ochalog

RubyとMediaWikiとIRCが好き。

BCDice#rollにおいて3番目以降の引数を使用する処理

「どどんとふのダイスボット」ことBCDiceのダイスロール処理(BCDice#roll)には、多くの引数(7個!)がある。この処理を単純化して引数を減らしたかったため、その呼び出し方について調べた。

背景

  • BCDice#roll の引数、返り値ともに個数が多すぎる。
  • 引数が多い分、処理が複雑。
    • 引数を減らして処理を簡潔にできないか?

BCDice#roll の引数の意味

  1. dice_cnt [Integer]:ダイスの個数
  2. dice_max [Integer]:ダイスの面数
  3. dice_sort [Integer]:並べ替え。0:ソートしない、1:足し算ダイスでソート有、2:バラバラロール(Bコマンド)でソート有。3:1+2。
  4. dice_add [Integer]BCDice#roll 内での振り足しの閾値
  5. dice_ul [String]:成功判定の演算子
  6. dice_diff [Integer]:成功判定の目標値
  7. dice_re [Integer]:振り足しロールの閾値

(参考)BCDice#roll の返り値の意味

  1. total [Integer]:出目の合計
  2. dice_str [String]:出目を並べた文字列
  3. numberSpot1 [Integer]:1が出た個数
  4. cnt_max [Integer]:最高値の出目の個数
  5. n_max [Integer]:出目の最大値
  6. cnt_suc [Integer]:成功数
  7. rerollCount [Integer]:個数振り足しロールの振り足し数

目的

以上の背景から、「BCDice#roll をどのような場合に多くの引数で呼び出さなければならないか」を把握することを目的として、ソースコードを調査した。通常、ダイスロールでは 2d6 のようにダイスの個数と面数を指定するため、それ以外の要素(dice_sort 以降)を引数で指定する場合、つまり引数を3個以上とする場合を「多くの引数で呼び出している」と定義した。

調査方法

次のコミット時点におけるソースコードを調査の対象とした。

https://github.com/bcdice/BCDice/tree/f1da744aea852e1ef4e6d79ef329b428f4757385

src ディレクトリで以下のコマンドを実行した。その出力から手作業で、BCDice#roll を3引数以上で呼び出している部分を抽出した。

git grep -n -P '\broll\('

結果(要約)

  • 4番目以降の引数を使用する場合は非常に少ない。
  • 4番目の引数 dice_add を使用する(0以外にする)のは、上方無限ロールのみ。
  • ゲームシステム固有ダイスボットで3番目以降の引数を使用する場合、ほとんどは3番目の引数 dice_sort を使用するだけ。
    • ただし、以下のような問題が見つかった。
      • typo@sortTye
      • 論理演算子の使い方の誤り:@sortType && 1@sortType は整数であり常に真だから、&& 1 をとっても必ず真)

考察

  • ほとんどの場合、引数は dice_cntdice_maxdice_sort だけで十分。これだけが用意された単純なメソッドを用意することが、改善案として考えられる。
  • 4番目以降の引数を使用する場合が非常に少ないため、それらの場合の処理を BCDice#roll の外に出すと、処理をより単純化できると考えられる。上で述べた単純なメソッドを使用してさらに複雑な処理を行うメソッドを用意するべき。
    • 特に dice_add を使用するのは上方無限ロールだけなので、この部分は UpperRoll に移動できるはず。
  • dice_sort の指定ミスが散見されるので、見直すべき。

結果(詳細)

  • bcdiceCore.rb:1083: dice_dat = roll(dice_cnt, dice_max, (@diceBot.sortType & 2), 0, signOfInequality, diff)
    • バラバラロール
  • dice/AddDice.rb:298: return @bcdice.roll(dice_wk, dice_max, sortType)
    • 加算ロール
  • dice/RerollDice.rb:61: @bcdice.roll(x, n, (@diceBot.sortType & 2), 0, signOfInequality, diff, rerollNumber)
    • 個数振り足しロール
  • dice/UpperDice.rb:145: @bcdice.roll(diceCount, diceMax, (@diceBot.sortType & 2), @upper, @signOfInequality, diceDiff)
    • 上方無限ロール
  • diceBot/Airgetlamh.rb:81: dice, diceText = roll(rollCount, 10, @sortType)
  • diceBot/Alsetto.rb:77: dice, diceText = roll(rollCount, 6, @sortType)
  • diceBot/Avandner.rb:65: dice, diceText = roll(rollCount, 10, @sortType)
    • 並び替え
  • diceBot/BeastBindTrinity.rb:200: _, dice_str, = roll(dice_tc, 6, (sortType & 1))
    • ダイス数修正、並べ替えせずに出力
  • diceBot/BlindMythos.rb:140: _, diceText, = roll(diceCount, 6, isSort)
    • 並び替え
  • diceBot/DoubleCross.rb:156: dice_dat = roll(dice_cnt, dice_max, (sortType & 2), 0, "", 0, critical)
    • クリティカル(成功判定)
  • diceBot/DoubleCross.rb:182: dice_dat = roll(dice_cnt, dice_max, (sortType & 2), 0, "", 0, critical)
    • クリティカル(成功判定)
  • diceBot/EmbryoMachine.rb:82: dice_now, dice_str, = roll(2, 10, (sortType & 1))
  • diceBot/GehennaAn.rb:56: diceValue, diceText, = roll(diceCount, 6, (sortType & 1))
  • diceBot/HatsuneMiku.rb:84: _, diceText, = roll(diceCount, 6, isSort)
    • 並び替え
  • diceBot/Illusio.rb:53: dice, diceText = roll(diceCount, 6, @sortTye)
    • 並び替え?(typoしている?)
  • diceBot/LiveraDoll.rb:81: dice, diceText = roll(diceCount, 6, @sortType)
  • diceBot/MeikyuDays.rb:90: _, dice_str, = roll(dice_c, 6, (sortType & 1))
  • diceBot/MeikyuKingdom.rb:178: _, dice_str, = roll(diceCount, 6, (sortType & 1))
  • diceBot/MonotoneMusium.rb:64: total, dice_str, = roll(2, 6, @sortType && 1)
  • diceBot/MonotoneMusium_Korean.rb:64: total, dice_str, = roll(2, 6, @sortType && 1)
  • diceBot/Nechronica.rb:100: _, dice_str, n1, cnt_max, n_max = roll(dice_n, 10, 1)
  • diceBot/Nechronica_Korean.rb:100: _, dice_str, n1, cnt_max, n_max = roll(dice_n, 10, 1)
  • diceBot/NightWizard.rb:127: dice_n, dice_str, = roll(2, 6, 0)
  • diceBot/NightWizard.rb:173: dice_n, dice_str, = roll(2, 6, 0)
    • 並び替え
  • diceBot/NinjaSlayer.rb:110: dice = roll(m[1], 6, 0, 0, '>=', 2)
  • diceBot/NinjaSlayer.rb:130: dice = roll(m[1], 6, 0, 0, '>=', 3)
  • diceBot/NinjaSlayer.rb:149: dice = roll(m[1], 6, 0, 0, '>=', 4)
  • diceBot/NinjaSlayer.rb:168: dice = roll(m[1], 6, 0, 0, '>=', 5)
  • diceBot/NinjaSlayer.rb:187: dice = roll(m[1], 6, 0, 0, '>=', 6)
  • diceBot/NinjaSlayer.rb:206: dice = roll(m[1], 6, 0, 0, '>=', 4)
  • diceBot/NinjaSlayer.rb:226: dice = roll(m[1], 6, 0, 0, '>=', 2)
  • diceBot/NinjaSlayer.rb:245: dice = roll(m[1], 6, 0, 0, '>=', 3)
  • diceBot/NinjaSlayer.rb:264: dice = roll(m[1], 6, 0, 0, '>=', 4)
  • diceBot/NinjaSlayer.rb:283: dice = roll(m[1], 6, 0, 0, '>=', 5)
  • diceBot/NinjaSlayer.rb:302: dice = roll(m[1], 6, 0, 0, '>=', 6)
  • diceBot/NinjaSlayer.rb:321: dice = roll(m[1], 6, 0, 0, '>=', 4)
    • 成功判定
  • diceBot/OrgaRain.rb:49: dice, diceText = roll(diceCount, 10, @sortType)
    • 並び替え
  • diceBot/Postman.rb:87: dice, diceText = roll(diceCount, 6, @sortTye)
  • diceBot/Raisondetre.rb:79: dice, diceText = roll(rollCount, 10, @sortTye)
  • diceBot/Raisondetre.rb:133: dice, diceText = roll(rollCount, 10, @sortTye)
    • 並び替え?(typoしている?)
  • diceBot/SRS.rb:56: total, dice_str, = roll(2, 6, @sortType && 1)
    • 並び替えだが、値は常に true となる
  • diceBot/Strave.rb:79: dice, diceText = roll(diceCount, 10, @sortType)
  • diceBot/Torg.rb:102: dummy = roll(1, 20, 0)
  • diceBot/TunnelsAndTrolls.rb:165: rollTotal, rollDiceResultText, roll_cnt1, rollDiceMaxCount, roll_n_max, roll_cnt_suc, roll_cnt_re = roll(dice_wk, 6, (sortType & 1))
    • 並び替え
  • diceBot/TwilightGunsmoke.rb:76: total, dice_str, = roll(2, 6, @sortType && 1)
    • 並び替えだが、値は常に true となる