From 4814b6de4b0b5ea29ec7f4948a7f057a3caf3ab4 Mon Sep 17 00:00:00 2001 From: SAKATA Sinji Date: Thu, 11 Jun 2020 20:31:04 +0900 Subject: [PATCH] Refactor ChaosFlare --- src/diceBot/ChaosFlare.rb | 201 +++++++++++++++++------------------ src/test/data/ChaosFlare.txt | 64 +++++++++-- src/utils/command_parser.rb | 47 +++++++- 3 files changed, 197 insertions(+), 115 deletions(-) diff --git a/src/diceBot/ChaosFlare.rb b/src/diceBot/ChaosFlare.rb index 2a970af4a..2a29c0ba0 100644 --- a/src/diceBot/ChaosFlare.rb +++ b/src/diceBot/ChaosFlare.rb @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # frozen_string_literal: true +require 'utils/command_parser' + class ChaosFlare < DiceBot # ゲームシステムの識別子 ID = 'Chaos Flare' @@ -12,17 +14,30 @@ class ChaosFlare < DiceBot # ダイスボットの使い方 HELP_MESSAGE = <=目標値] -  (例1) CF (2d6で普通に判定) -  (例2) CF+10@10 (+10の修正値で、クリティカル値10で判定) -  (例3) CF+10#3 (+10の修正値で、ファンブル値3で判定) -  (例4) CF+10>=10 (+10の修正値で、目標値を指定。差分値が出ます) -  (例5) 3CF (ダイス3つで判定) -  (例6) 3CF+10@10#3>=10 (ダイス3つ、修正値10、クリティカル値10、ファンブル値3、目標値10で判定) -  (例7) CF+5-3#3+3>=10 (修正値は計算できます。修正値は、ファンブル値の後、目標値の前の場所にも書けます) +判定 +CF + 書式: [ダイスの数]CF[修正値][@クリティカル値][#ファンブル値][>=目標値] + CF以外は全て省略可能 + 例: + - CF 2D6,クリティカル値12,ファンブル値2で判定 + - CF+10@10 修正値+10,クリティカル値10で判定 + - CF+10#3 修正値+10,ファンブル値3で判定 + - CF+10>=10 目標値を指定した場合、差分値も出力する + - 3CF+10@10#3>=10 3D6での判定 + - CF@9#3+8>=10 + +2D6 + ファンブル値2で判定する。クリティカルの判定は行われない。 + 目標値が設定された場合、差分値を出力する。 + - 2D6+4>=10 各種表 -  FT:因縁表 + FT: 因縁表 + FTx: 数値を指定すると因果表の値を出力する + - FT -> 11から66の間でランダム決定 + - FT23 -> 23の項目を出力 + - FT0 + - FT7 INFO_MESSAGE_TEXT setPrefixes(['\d*CF.*', 'FT\d*']) @@ -64,123 +79,100 @@ def check_2D6(total, dice_total, _dice_list, cmp_op, target) return output end - # コマンドを分岐する場所。 def rollDiceCommand(command) - case command - when /FT\d*/i - return getFate(command) + if command.start_with? "FT" + roll_fate_table(command) + else + cf_roll(command) end - - return getRollResult(command) end - # 因縁表を振る場所。 - def getFate(command) - debug("getFate", "begin") - matched = /FT(\d*)/i.match(command) - debug("matched", matched) - - if matched[1] == "" - #ランダムに振る処理。 - first_die = roll(1,6)[0] - first_index = first_die - second_die = roll(1,6)[0] - second_index = ((second_die) / 2).ceil - 1 - return "(#{first_die},#{second_die}) → #{FATE_TABLE[first_index][second_index]}" + private + + # 因縁表 + def roll_fate_table(command) + m = /^FT(\d+)?/.match(command) + if m[1] + num = m[1].to_i + if [0, 7].include?(num) + return "因果表(#{num}) > #{FATE_TABLE[num][0]}" + end + + dice1 = num / 10 + dice2 = num % 10 + if !(1..6).cover?(dice1) || !(1..6).cover?(dice2) + return nil + end else - #出目を指定して因縁表を振る処理の場所です。気力のある方お願いします。腐れ縁と任意はすでにtableに入っています。 - return "" + dice1, = roll(1, 6) + dice2, = roll(1, 6) end + + index1 = dice1 + index2 = (dice2 / 2) - 1 + return "因果表(#{dice1}#{dice2}) > #{FATE_TABLE[index1][index2]}" end - #カオスフレア用の判定を処理する場所。べた書きです。 - def getRollResult(command) - #まずはコマンドが合っているか。 - roll_regex = /(?:(\d+))?CF((?:[+-]\d+)*)(?:@(\d+))?(?:#(\d+))?((?:[+-]\d+)*)(?:>=(\d+))?/i - matched = roll_regex.match(command) - debug("match", matched) - unless matched - return nil - end + # カオスフレア専用コマンド + # @param command [String] + # @return [String, nil] + def cf_roll(command) + parser = CommandParser.new(/\d*CF/) - #指定された各種数字を取得。 - dice_num = 2 - if matched[1] != nil - dice_num = matched[1].to_i + @cmd = parser.parse(command) + unless @cmd + return nil end - critical = 12 - if matched[3] != nil - critical = matched[3].to_i - end + times = @cmd.command == "CF" ? 2 : @cmd.command.to_i + critical = @cmd.critical || 12 + fumble = @cmd.fumble || 2 + @cmd.dollar = nil - fumble = 2 - if matched[4] != nil - fumble = matched[4].to_i - end - debug("fumble", fumble) - - #素のダイスで振る。 - dice_result = roll(dice_num, 6) - debug("result", dice_result) - - dice_sum = dice_result[0] - debug("sum", dice_sum) - - #クリティカルなら30に、ファンブルなら-20に。 - critical_flag = false - fumble_flag = false - if dice_sum >= critical - dice_sum = 30 - critical_flag = true - elsif dice_sum <= fumble - dice_sum = -20 - fumble_flag = true + if times < 0 || ![:>=, nil].include?(@cmd.cmp_op) + return nil end - #修正値を計算して出目に加える。evalで手抜きです。 - adjust = 0 - if matched[2] != "" - adjust += eval(matched[2]) - end - if matched[5] != "" - adjust += eval(matched[5]) - end + dice_total, dice_list_text = roll(times, 6) - dice_sum = dice_sum + adjust - debug("sum", dice_sum) + is_critical = dice_total >= critical + is_fumble = dice_total <= fumble - #必要なら差分値を出す。 - diff = 0 - if matched[6] != nil - diff = dice_sum - if dice_sum < 0 - diff = 0 + total = + if is_critical + 30 + elsif is_fumble + -20 + else + dice_total end - diff -= matched[6].to_i - end - #結果の文字列を作る。 - result_string = "(#{dice_result[1]}) → #{dice_sum}" - if dice_sum < 0 - result_string += "(0)" - end - if critical_flag - result_string += " (クリティカル)" - end - if fumble_flag - result_string += " (ファンブル)" - end - if matched[6] != nil - result_string += " [差分値:#{diff}]" - end + total += @cmd.modify_number + + sequence = [ + "(#{@cmd.to_s(:tail_suffix => true)})", + "#{dice_total}[#{dice_list_text}]", + total.to_s, + ("0" if total < 0), + ("クリティカル" if is_critical), + ("ファンブル" if is_fumble), + ("差分値 #{difference(total)}" if @cmd.target_number), + ].compact - #お疲れ様でした。 - return result_string + return sequence.join(" > ") end + # @param total [Integer] 合計値 + # @return [Integer] 差分値 + def difference(total) + if total < 0 + -@cmd.target_number + else + total - @cmd.target_number + end + end - #表を振るのに使う定数的なやつ。 + # 表を振るのに使う定数的なやつ。 FATE_TABLE = [ ["腐れ縁"], ["純愛", "親近感", "庇護"], @@ -191,5 +183,4 @@ def getRollResult(command) ["恐怖", "執着", "利用"], ["任意"] ].freeze - end diff --git a/src/test/data/ChaosFlare.txt b/src/test/data/ChaosFlare.txt index cf1af83dc..081be9f0c 100644 --- a/src/test/data/ChaosFlare.txt +++ b/src/test/data/ChaosFlare.txt @@ -241,42 +241,90 @@ rand:4/6,5/6 input: FT output: -Chaos Flare : (4,5) → 期待 +Chaos Flare : 因果表(45) > 期待 rand:4/6,5/6 ============================ input: -3CF+10+5-10@10#3+3>=10 +FT45 output: -Chaos Flare : (4,5,1) → 38 (クリティカル) [差分値:28] +Chaos Flare : 因果表(45) > 期待 +rand: +============================ +input: +SFT +output: +Chaos Flare : 因果表(45) > 期待###secret dice### +rand:4/6,5/6 +============================ +input: +FT0 +output: +Chaos Flare : 因果表(0) > 腐れ縁 +rand: +============================ +input: +FT7 +output: +Chaos Flare : 因果表(7) > 任意 +rand: +============================ +input: +3CF+10+5-10@10#3>=10 +output: +Chaos Flare : (3CF+5@10#3>=10) > 10[4,5,1] > 35 > クリティカル > 差分値 25 +rand:4/6,5/6,1/6 +============================ +input: +3CF@10#3+10+5-10>=10 +output: +Chaos Flare : (3CF+5@10#3>=10) > 10[4,5,1] > 35 > クリティカル > 差分値 25 +rand:4/6,5/6,1/6 +============================ +input: +S3CF+10+5-10@10#3>=10 +output: +Chaos Flare : (3CF+5@10#3>=10) > 10[4,5,1] > 35 > クリティカル > 差分値 25###secret dice### rand:4/6,5/6,1/6 ============================ input: CF output: -Chaos Flare : (4,5) → 9 +Chaos Flare : (CF) > 9[4,5] > 9 +rand:4/6,5/6 +============================ +input: +SCF +output: +Chaos Flare : (CF) > 9[4,5] > 9###secret dice### rand:4/6,5/6 ============================ input: 3CF output: -Chaos Flare : (4,5,1) → 10 +Chaos Flare : (3CF) > 10[4,5,1] > 10 rand:4/6,5/6,1/6 ============================ input: CF@9 output: -Chaos Flare : (4,5) → 30 (クリティカル) +Chaos Flare : (CF@9) > 9[4,5] > 30 > クリティカル rand:4/6,5/6 ============================ input: CF#5 output: -Chaos Flare : (1,3) → -20(0) (ファンブル) +Chaos Flare : (CF#5) > 4[1,3] > -20 > 0 > ファンブル rand:1/6,3/6 ============================ input: CF+10@10>=10 output: -Chaos Flare : (4,5) → 19 [差分値:9] +Chaos Flare : (CF+10@10>=10) > 9[4,5] > 19 > 差分値 9 +rand:4/6,5/6 +============================ +input: +CF-10@10>=10 +output: +Chaos Flare : (CF-10@10>=10) > 9[4,5] > -1 > 0 > 差分値 -10 rand:4/6,5/6 diff --git a/src/utils/command_parser.rb b/src/utils/command_parser.rb index a53a1ddbc..382f45692 100644 --- a/src/utils/command_parser.rb +++ b/src/utils/command_parser.rb @@ -1,5 +1,6 @@ require "utils/ArithmeticEvaluator" require "utils/normalize" +require "utils/modifier_formatter" class CommandParser < ArithmeticEvaluator def initialize(*literals) @@ -7,6 +8,49 @@ def initialize(*literals) @round_type = :omit end + # @!attribute [rw] command + # @return [String] + # @!attribute [rw] critical + # @return [Integer, nil] + # @!attribute [rw] fumble + # @return [Integer, nil] + # @!attribute [rw] dollar + # @return [Integer, nil] + # @!attribute [rw] modify_number + # @return [Integer] + # @!attribute [rw] cmp_op + # @return [Symbol, nil] + # @!attribute [rw] target_number + # @return [Integer, nil] + class Parsed + attr_accessor :command, :critical, :fumble, :dollar, :modify_number, :cmp_op, :target_number + + include ModifierFormatter + + def initialize + @critical = nil + @fumble = nil + @dollar = nil + end + + def to_s(tail_suffix: false) + c = @critical ? "@#{@critical}" : nil + f = @fumble ? "##{@fumble}" : nil + d = @dollar ? "$#{@dollar}" : nil + m = format_modifier(@modify_number) + + if tail_suffix + [@command, m, c, f, d, @cmp_op, @target_number].join() + else + [@command, c, f, d, m, @cmp_op, @target_number].join() + end + end + end + + # @param expr [String] + # @param rount_type [Symbol] + # @return [CommandParser::Parsed] + # @return [nil] def parse(expr, round_type = :omit) @tokens = tokenize(expr) @idx = 0 @@ -30,10 +74,9 @@ def parse(expr, round_type = :omit) return @parsed end - Parsed = Struct.new(:command, :critical, :fumble, :dollar, :modify_number, :cmp_op, :target_number) - private + # @return [Array] def tokenize(expr) expr.gsub(%r{[\(\)\+\-*/@#\$]|[<>!=]+}) { |e| " #{e} " }.split(' ') end