とりあえずきれいごとな建前として「クラスをどう設計していくのかの一例を示してみたい」っていうあたりの要望がある、ってことにしといて。
ネタ的にものすごくニッチな気もするのだが。
書きたくなったんだからしゃ〜ないw
# っていうか「実装する気満々だったりする」ので単純に「ちょ〜どいいから記事にして備忘録にしちゃえ〜」って感じです B-p
まず、大雑把に「TRPGにおける成功判定(ダイスを使うもの)」について「最アバウトレイヤー」で考える。
・ダイスを振る
・成功したり失敗したりする:結果とか呼んでみる
うんこんなもんだ。
プログラムっぽく書くとこんな感じ。
結果 = ダイスを振る();
素晴らしい。
見もふたもない、とも言う。
さて。結果とダイスを振る、のどっちから考察してもいいんだけど…とりあえず結果から。
基本的には「成功」か「失敗」の2択なので。2択ならbooleanでいいんぢゃね? ってことで考えてみる。
この辺の考えの甘さとそれに伴う修正の仕方とかは後でやるから、気づいてたら一端失念してw
boolean = ダイスを振る();
さて、処理への考察。
一般的に「さいころを振って」「ある数値基準に、それより大きかったり小さかったりすると、成功したり失敗したりする」もんです。
boolean = ダイスを振る(振るダイスのタイプと数, 目標値);
そうだなぁとりあえず「D&D 4thのST判定」とか局所なところから。
1d20ふって(20面ダイスを1回振って)10以上なら成功、9以下なら失敗。
この場合要素としては「1d20」「10(以上なら成功)」なので。その辺をてけとうにパラメタっぽくすると…こう?
boolean = ダイスを振る("1d20", 10);
ここいらあたりまではぶっちゃけオブジェクトとかいらない。別にぺたんとしたfunctionで十分。
問題はここから。
「ダイスによる判定」は、実際のところそれはそれはバラエティに富んでたりする。
まずは「上方判定」と「下方判定」。さいころの目が「小さいほうがいい感じ」と「大きいほうがいい感じ」ってのがある。D&Dとかは上方だし(昔は上下ばらんばらんだったんだけどねぇ)。GURPSなんてのは典型的な下方。
さらに。さいころの目を足すのではなくて「個々のさいころと目標値を比較して、目標値を上回ったダイスの数で判定(シャドウラン/WoD)」とかっていうのもあります。
さらにさらに、ごく一部のシステム(具体的には央華封神)には「裏成功」というびみょ〜な成功もあります。
ついでに。結果も「成功と失敗」だけではなくて、一般的には「クリティカルとファンブル」ってのがあります。改心の一撃&痛恨の一撃とかまぁ色々表現がありますが。ないものもあります。
まずとりあえず「結果、ってのは2種類とは限らない」らしいことが骨身にしみてわかったので、一端boolean説を取り下げます。
毎度のごとく「結果、というよくわからんモヤモヤしたもの」に戻します。
この辺のフットワークのよさとかケツの軽さとかは重要なのでチェック。試験に出るよ。
結果 = ダイスを振る();
先に結果のほうから再考察。
色々なシステムを大まかに統合して、「結果」というカテゴリに入る状態や情報は
・成功
・失敗
・クリティカル
・ファンブル
・裏成功かどうか
・成功数
・失敗数(ボッチ数)
あたり。数を聞くものを除いて、基本的には「個々にis関数を作って問い合わせる」のが楽なので。
極めて大まかに、この子をクラスにします。
class 結果 { public abstract boolean is成功(); public abstract boolean is失敗(); public abstract boolean isクリティカル(); public abstract boolean isファンブル(); public abstract boolean is裏成功かどうか(); public abstract int get成功数(); public abstract int get失敗数(); }
となるわけですな。結果の状態が増えたら適当に増やしといて、っていう気軽さがオブジェクトの身上。
なので、一端、戻り値である「結果」については考察終了。
次に「ダイスを振る」ほう。
結局のところ「ある目標値に対してダイスを振ってどうだったか」を聞くので、本質的な部分である
結果 = ダイスを振る(振るダイスのタイプと数, 目標値);
の部分には差異がない。ここ重要。
差異がないからんじゃってんで、割とすぐに思いつくのはこーゆーインタフェース。
結果 = ダイスを振る(判定種別, 振るダイスのタイプと数, 目標値);
い〜んだけどっていうかぜんぜんよくないんだけど。
上述の実装は、やらせてみると、大体こんな感じ。
結果 function ダイスを振る(判定種別, 振るダイスのタイプと数, 目標値) { switch(判定種別) { case 'D&DのST': 処理; 処理; 処理; break; case 'ソードワールド': 処理; 処理; 処理; break; case 'シャドウラン': 処理; 処理; 処理; break; } - 以下略 -
で、保守性の観点などから、はっきりいって「ンベ B-p」な感じです。
これをみて「きしょい」って思える程度の感性は、もっていて損ないと思う。
こゆ時に、GoFの「工場(Factory)」をつかうであるざんす。
んと…イメージを大まかに書くと、こう。
ダイス処理インスタンス = 工場::作成依頼(システム名); 結果 = ダイス処理インスタンス->処理よろ〜(振るダイスのタイプと数、目標値);
ずぼらな人的には、こう。
結果 = 工場::作成依頼(システム名)->処理よろ〜(振るダイスのタイプと数、目標値);
これをやると「システムが増えたとき」に有利なほかに「とあるシステム(用のダイス処理)への修正」が「ほかのシステム(用のダイス処理)に影響しない」ので、その辺がなんていうか「気軽」です。
まぁ、こうやっておいちゃんはクラスを切っていくわけですね。
「上から下に」落とすんじゃなくて、もっとこう…なんていうか…あぢゃいるでいんたらくてぶな感じ?(いってみたかっただけ)
こまめに作って、ちょこちょこと修正しながら、時々は大本に立ち戻りながら、ゆっくりと「荒削りから仕上げに向けて」進めていく感じ。
このほうが楽なんじゃないかなぁ、って思う、おいちゃんからの一案でした。