ダイスボットのつくりかた

■概要

ここでは「どどんとふ」のダイスボットを自作してみたい人用に具体的な手順について記述しています。

■そもそもダイスボットって何?

「ダイス+ロボット」でダイスボット。
元々はチャットソフトでダイスを振ってくれるロボットの用なツールを指す言葉ですね。
どどんとふではそのネーミングを引き継いでゲーム用のダイステキストを処理する機能を「ダイスボット」と呼んでいます。

■ダイスボットの自作って?

どどんとふで遊ぼうと思ったゲームがダイスボットの一覧に無い。
そんな時にどうするか?

作者に頼むのもモチロンいいですね。

でも、超マイナールールだったり自作TRPGだったり、あるいは単純にプログラミングってのが楽しそうだったり。
色んな理由で自分で作りたくなる時ってのはあるものです。
そんなダイスボットの自作が出来ちゃうのがどどんとふの魅力の一つ。
オープンソースの素晴らしさというわけですね。

どどんとふのダイスボットは「Ruby」というプログラミング言語でソースコードを書くことで追加・変更することができます。
「うっはープログラミングとかサッパリだわー」という人は、流石にこの先は読まなくてもOKです。
「Ruby!いいね!俺大好きなんだよ!」なんて酔狂な方はもちろんウェルカム!
でも、どちらかというと「Rubyかー、この機会にちょっと勉強してみようかな?」という方向けに説明進めていきましょう。

■環境の用意

それではWindows環境向けにダイスボットを自作するための手順を説明していきましょう。
#Unix系OSの人なら当然Rubyはインストール済みと信じて説明は割愛。
ダイスボットの作成のためにはWindows用のRuby実行環境を作成する必要があります。
RubyInstallerから、「Ruby 1.9.X-pXXX」(Xは任意の数字)をダウンロードします。

 http://rubyinstaller.org/

あとは、インストーラーの指示に従ってインストール。
そしてインストールディレクトリの bin にパスを通します。(パスを通す、の意味が知らない人はネットで検索だ!)
Ruby1.9.3のデフォルトだと「C:\Program Files\Ruby193\bin」ですかね。 ruby.exe のある場所にパスを通すのがポイントです。
後はコマンドプロンプト開いて(コマンドプロンプト、の意味が分からない人はネットで(略))、
ruby -v
とコマンドを実行して、Rubyのバージョン情報が表示されればOKです。
準備万端。それではダイスボットを作っていきましょう!

■自作ダイスボットの作り方:その1 名前大事!

今回サンプルとして作るダイスボット用に架空のゲームシステムがあるとしましょう。

タイトル:仮ダイス
・判定方法
 6面ダイスをX個振るシステム。
 判定コマンドは
  KDx>=y(xはダイスロール数、yは目標値)


これをダイスボットで実装する方法を考えて行きましょう。

まずは diceBot ディレクトリを開きます。
どどんとふ なら src_bcdice/diceBot
B&C Dice2.0 なら src/diceBot
その中に _Template.rb という名前のファイルがあります。

テンプレート、TRPGでもよく使われる単語ですね。
要は「ひな型」って意味でして、それをもとに作れば1から作るよりも楽だよーってニュアンスですね。
ではでは、そのニュアンスを信じて _Template.rb ファイルを元に作ってみましょう。
_Template.rb ファイルをコピーして、今回のゲーム用のタイトルをファイル名に付けましょう。
タイトルが「仮ダイス」ですから

 KariDice.rb

ですかね。
このKariDiceという名前は「ダイスボットの型名」と呼んで他でも使いますので「ファイル名なんてなんでもいいや」とか思わずにしっかり決めてくださいね。
プログラミングでは命名が何より大事ですので。
この KariDice.rb を以降は編集していきます。

■自作ダイスボットの作り方:その2 まずは形から

KariDice.rb はまだ中身は _Template.rb ファイルのままですね。
この中身をチョイチョイっと書き換えて仮ダイスを実装していきましょう。
ファイルを開いて、

class Template < DiceBot

ここを、今回のダイスボットは名前が 「KariDice」なので

class KariDice < DiceBot

と書き換え。ファイル名の(~.rb)部分と同じにするわけですね。
続いて

  def gameName
    'ゲーム名'
  end
  
  def gameType
    "GameType"
  end


  def gameName
    '仮ダイス'
  end
  
  def gameType
    "KariDice"
  end
に変更。
ゲームの日本語名とファイル名と同じ英文字での型名を定義しています。
では書き間違いがないか、念のため KariDice.rb の置いてあるディレクトリで

 ruby -cw KariDice.rb

とコマンドを実行しましょう。

 Syntax OK

と表示されれば問題ありません。

エラーが表示される場合はそこに文法間違いがあるので確認しましょう。
もうこれだけでダイスボットとしての形は整ったことになります!

試しにどどんとふで実際に動かしてみましょう。
どどんとふサーバ上の src_bcdice/diceBot に KariDice.rb を置いてログイン。
ダイスボット一覧の一番下に「仮ダイス」が表示されましたね。
いい感じです!

■自作ダイスボットの作り方:その3 テストファースト!

では、今回実装する予定の判定コマンドを実装していきましょう。
定義はこうでしたね。


・判定方法
 6面ダイスをX個振るシステム。
 判定コマンドは
  KDx>=y(xはダイスロール数、yは目標値)


これを実装するには、

  setPrefixes([])


  def rollDiceCommand(command)
    ''
  end
の2つのメソッドを書き換えることになります。
引数の command にダイスボットとして入力した文字列が渡されますので、そこからロールの判定を行い、結果の文字列を戻り値に渡せばOKです。
では早速実装!
…とその前に、動作テストの用意をしましょう。
どどんとふのダイスボットは作成時に動作検証をしやすいように動作テスト用の仕組みが用意されています(ユニットテストと呼ばれるものですね)。
これを活用すればダイスボットが正しく動いているかをどどんとふ上で確認しなくてもよいのでとっても楽です。
どどんとふ なら src_bcdice
B&C Dice2.0 なら src に移動。
そこにある test.rb がテスト用のスクリプト。 そして test/data/ 配下にあるテキストファイルが全てテスト用のデータです。

ここでは例として Cthulhu.txt を開きましょう。


============================
input:
1D100<=10
output:
Cthulhu : (1D100<=10) > 98 > 致命的失敗
rand:98/100
============================
input:
1D100<=10
output:
Cthulhu : (1D100<=10) > 45 > 失敗
rand:45/100
============================


といった記述が並んでいますね。
テストデータの書式は


input: (テストしたいデータ)
output:
(ダイスボットの型名) : (出力される文字列)
rand:(テストしたいダイスの出目)


となります。
また、複数テストデータを記述するときは、データの間を
============================
で区切る必要があります。

output の「(ダイスボットの型名):」はダイスボットの内部で自動的に生成されるので常に必要です。
実際に表示される文字を意識したいので、省略はできなくなっています。
rand の記述は 1/6,2/6 のように (出目)/(ロールするダイス) の形で記述します。
これを使えば3D6で1,1,1を出すようなパターンでも 1/6,1/6,1/6 と記述してしまえばすぐ検証できるわけですね。便利!

今回のテストとして新しいテストデータとして KariDice.txt ファイルを作成しましょう。
.rb と同じ名前でテストデータの .txt を作成するわけですね。


input:
KD3>=10
output:
KariDice : (KD3>=10) > 10[2,5,3] > 成功
rand:2/6,5/6,3/6
============================
input:
KD3>=10
output:
KariDice : (KD3>=10) > 9[1,5,3] > 失敗
rand:1/6,5/6,3/6


そしてテストの実行。

ruby test.rb KariDice

とコマンドを実行。


ruby test.rb KariDice
XX

[Failures]
Game type: KariDice
Index: 1
Input:
  KD3>=10
Expected:
  KariDice : (KD3>=10) > 10[2,5,3] > 成功
Result:
  ダイス残り:2/6, 5/6, 3/6
Rands: 2/6, 5/6, 3/6
===========================
Game type: KariDice
Index: 2
Input:
  KD3>=10
Expected:
  KariDice : (KD3>=10) > 9[1,5,3] > 失敗
Result:
  ダイス残り:1/6, 5/6, 3/6
Rands: 1/6, 5/6, 3/6


と出力されます。

これは「xx」とxが2つなので「テスト2件に失敗」を示します。成功なら「..」となって、「OK」が表示されます。
ここでは判定成功時と失敗時の2件の動作を確認して、両方に失敗したということですね。
早く「OK」が表示されるのが楽しみですね!

以後はソースコードを変更したらテストを実行して動作を確認、
新しい実装をする時には先にですとデータを作って、動作確認、を繰り返すことになります。
テスト→実装→テスト→実装…の順ですね。

ちなみ、こうやって先にテストを作ってからプログラムを作成する方法をテストファーストって呼んだりします。
是非実績していきましょう!

■自作ダイスボットの作り方:その4 そして実装

準備もできたので、ダイスボットの実装を。

まずはコマンドの定義を

  setPrefixes(['KD\d+>=\d+'])
ここに書いて、どどんとふにコマンドを教えてやる必要があります。忘れがちなので注意!
ポイントは、文字列を””ではなく’’で囲むことですね。
こうしないと¥dのdが¥文字でエスケープされてしまい、「¥d」という文字にならないのですね。
正規表現相当の文字をここで文字列として定義してRubyとActionScript両方で使っているので、扱いにクセがあります。
分かりにくい場合は、今後の拡張性も考えて
setPrefixes(['KD.*'])
みたいに手を抜いた記述にしてしまうのも手です。
とりあえず動かしてみたいですしね。
次にコマンドの判定部分を実装。

  def rollDiceCommand(command)
    debug("rollDiceCommand Begin")
    
    return '' unless( /^KD(\d+)>=(\d+)$/ =~ command )
    
    debug("command", command)
    
    diceCount = $1.to_i
    target = $2.to_i
    
    total, diceText, = roll(diceCount, 6)
    result = (total >= target ? "成功" : "失敗")
    
    text = "(#{command}) > #{total}[#{diceText}] > #{result}"
    debug("rollDiceCommand result")
    
    return text
  end
これで成否判定は実装完了。
rollメソッドはDiceBotクラスで実装済みのメソッドで、
合計, ダイスの文字列, [その他のデータ] = roll(ダイス数, ダイス種別)
みたいに使います。今回は[その他のデータ]は使わないので省略して
 合計, ダイスの文字列, =
となっています。 = の直前に「,」があるのがポイントです。
この書き方で[その他のデータ]は無視するよーって宣言しているわけですね。
あと、

 debug("rollDiceCommand Begin")
と書いている箇所はデバッグ用の文字列です。
さっきテストを実行したときに


Index: 1


みたいな表示がありましたが、ここにあるIndexというのはテストの通番でして、

 ruby test.rb KariDice 1

のようにテスト時にダイスボット名と共にインデックス番号を指定すると、そのテスト1件だけを実行してくれます。
特定のテストを詳細に検証する時に使うわけですが、その際にはデバッグ文が出力されるようになります。

たとえば、

 debug("command", command)

と書くと

 command : "KD3>=10"

みたいにデバッグ文が出力されるので読みやすく検証に便利です。
つまりテストでは

 ruby test.rb (ダイスボット名)

で全体を確認しつつ、失敗パターンをピックアップ確認したいときは

 ruby test.rb (ダイスボット名) (インデックス番号)

で実行。これがテストの王道ということですね。

あ、ちなみに

 ruby test.rb

だけなら全ダイスボットのテストが実行されます。
最後の最後に試しておくと、他に影響与えてないことが確認できて安心です。

さてさて、では先ほど提示した内容を実装してテストしてみましょう。


ruby test.rb KariDice
..
OK.


「OK」が表示されました!やったね!!


■自作ではなく、拡張する場合の注意点

ダイスボットを自作するのではなく、既存のダイスボットを拡張する、という方も多いでしょう。
その場合も、基本的には同じ手順で大丈夫です。
ただ、1点だけ注意点となるのが「後方互換性」について。

「どどんとふ」では過去のバージョンとの互換性を何よりも重視しています。
オンセ用に作った自キャラ用のダイスボットのコマンドがある日急に使えなくなる、というのは最も避けたい事態ですね。
なので、コマンドや機能を拡張する場合には既存の機能を変更しないのが大事です。
つまり「後方互換性」ですね。

もちろん、オリジナルのダイスボットを作る場合や、個人的に拡張する場合には自由に作ってOKですが、
完成したら公式にも送ろう…なんて素晴らしいことを考えている方は注意していただければと思います。


■終わりに

ここまでできれば後は同じことを繰り返してダイスボットの実装ができます。

まとめると

 テストファーストでテスト作成し失敗することを確認。
 →コードを実装、テスト。
 →うまく動かなかったらインデックス指定しデバッグ表示で内容確認。
 →OKが表示されたら祝杯!

こんな感じですね。

テストデータの作成にはパターンの網羅や以上・以下の閾値確認など色々ポイントがあるんですが、
それ書き出すとまた長文になりそうなので各自で工夫してみてくださいね。

ではでは!




■追記:テスト方式の変更について。

B&CVer2.02.27、どどんとふVer.1.46.07 以降ではテストの方式に変更があります。
ここでは、過去のバージョンでテストを行っていた人用に差分について説明します。

◆テストデータの設置場所
・これまで:
test/testData.txt に各ゲーム種別のテストを追加。
・これから:
data/(各ゲーム種別名).txt に各ゲームのテストを追加。
→ テストデータをゲーム毎に分割。

◆テストの記述方法
・これまで:
============================
input:1D100<=10	Cthulhu
output:
Cthulhu : (1D100<=10) > 45 > 失敗
rand:45/100
============================
・これから:
============================
input:
1D100<=10
output:
Cthulhu : (1D100<=10) > 45 > 失敗
rand:45/100
============================
→ テストデータにゲームの記述は不要に。
 inputはoutputと同様に改行後に記述に。

◆テストの実行方法
 全テスト実施、ゲーム毎の一括実施は同様。異なるのは特定のテストだけを実施する場合。
・これまで:
ruby test.rb 1
・これから:
ruby test.rb Cthulhu 1
→ゲーム種別を明示したうえで、実行するテスト番号を追記する必要があります。

以上!