diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 41afb5dfd..ff104b7f6 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -222,7 +222,6 @@ Style/GlobalVars: - src/test/others/testCard.rb - src/irc/ircBot.rb - src/dice/AddDice.rb - - src/dice/RerollDice.rb - src/diceBot/RecordOfSteam.rb - src/diceBot/TokumeiTenkousei.rb - src/diceBot/BattleTech.rb diff --git a/src/dice/RerollDice.rb b/src/dice/RerollDice.rb index 8caf262e8..9273ac293 100644 --- a/src/dice/RerollDice.rb +++ b/src/dice/RerollDice.rb @@ -4,6 +4,24 @@ require "utils/format" # 個数振り足しダイス +# +# ダイスを振り、条件を満たした出目の個数だけダイスを振り足す。振り足しがなくなるまでこれを繰り返す。 +# 成功条件を満たす出目の個数を調べ、成功数を表示する。 +# +# 例 +# 2R6+1R10[>3]>=5 +# 2R6+1R10>=5@>3 +# +# 振り足し条件は角カッコかコマンド末尾の @ で指定する。 +# [>4] の場合、4より大きい出目が出たら振り足す。 +# [4] のように数値のみ指定されている場合、成功条件の比較演算子を流用する。 +# 上記の例の時、出目が +# "2R6" -> [5,6] [5,4] [1,3] +# "1R10" -> [9] [1] +# だとすると、 >=5 に該当するダイスは5つなので成功数5となる。 +# +# 成功条件が書かれていない場合、成功数0として扱う。 +# 振り足し条件が数値のみ指定されている場合、比較演算子は >= が指定されたとして振舞う。 class RerollDice def initialize(bcdice, diceBot) @bcdice = bcdice @@ -12,43 +30,21 @@ def initialize(bcdice, diceBot) end def rollDice(string) - output = rollDiceCatched(string) - - return "#{@nick_e}: #{output}" - end - - def rollDiceCatched(string) - debug('RerollDice.rollDice string', string) - string = string.strip - - m = /^S?(\d+R\d+(?:\+\d+R\d+)*)(?:\[(\d+)\])?(?:([<>=]+)(\d+))?(?:@(\d+))?$/.match(string) - unless m - debug("is invaild rdice", string) - return '1' - end - - notation = m[1] - cmp_op = Normalize.comparison_operator(m[3]) - target_number = cmp_op ? m[4].to_i : nil - unless cmp_op - cmp_op, target_number = target_from_default() + unless parse(string) + return nil end - reroll_cmp_op = cmp_op || :>= - reroll_threshold = decide_reroll_threthold(m[2] || m[5], target_number) - - unless reroll_threshold - return "#{string} > #{msg_invalid_reroll_number}" + unless @reroll_threshold + return msg_invalid_reroll_number(string) end - dice_queue = [] - notation.split("+").each do |xRn| + dice_queue = @notation.split("+").map do |xRn| x, n = xRn.split("R").map(&:to_i) - unless valid_reroll_rule?(n, cmp_op, target_number) - return "#{string} > #{msg_invalid_reroll_number}" - end + [x, n, 0] + end - dice_queue.push([x, n, 0]) + unless dice_queue.all? { |d| valid_reroll_rule?(d[1], @reroll_cmp_op, @reroll_threshold) } + return msg_invalid_reroll_number(string) end success_count = 0 @@ -66,8 +62,8 @@ def rollDiceCatched(string) dice_total_count += x dice_list = roll_(x, n) - success_count += dice_list.count() { |val| val.send(cmp_op, target_number) } if cmp_op - reroll_count = dice_list.count() { |val| val.send(reroll_cmp_op, reroll_threshold) } + success_count += dice_list.count() { |val| compare(val, @cmp_op, @target_number) } if @cmp_op + reroll_count = dice_list.count() { |val| compare(val, @reroll_cmp_op, @reroll_threshold) } dice_str_list.push(dice_list.join(",")) @@ -80,21 +76,41 @@ def rollDiceCatched(string) end end - cmp_op_text = Format.comparison_operator(cmp_op) grich_text = @diceBot.getGrichText(one_count, dice_total_count, success_count) sequence = [ - "(#{notation}[#{reroll_threshold}]#{cmp_op_text}#{target_number})", + expr(), dice_str_list.join(" + "), "成功数#{success_count}", trim_prefix(" > ", grich_text), ].compact - return sequence.join(" > ") + return "#{@nick_e}: #{sequence.join(' > ')}" end private + # @param command [String] + # @return [Boolean] + def parse(command) + m = /^S?(\d+R\d+(?:\+\d+R\d+)*)(?:\[([<>=]+)?(\d+)\])?(?:([<>=]+)(\d+))?(?:@([<>=]+)?(\d+))?$/.match(command) + unless m + return false + end + + @notation = m[1] + @cmp_op = Normalize.comparison_operator(m[4]) + @target_number = @cmp_op ? m[5].to_i : nil + unless @cmp_op + @cmp_op, @target_number = target_from_default() + end + + @reroll_cmp_op = decide_reroll_cmp_op(m) + @reroll_threshold = decide_reroll_threthold(m[3] || m[7], @target_number) + + return true + end + # @return [Array<(Symbol, Integer)>] # @return [Array<(nil, nil)>] def target_from_default @@ -108,6 +124,21 @@ def target_from_default return cmp_op, target_number end + # @param m [MatchData] + # @return [Symbol] + def decide_reroll_cmp_op(m) + op = + if m[2] && m[3] + m[2] + elsif m[6] && m[7] + m[6] + else + m[4] + end + + Normalize.comparison_operator(op) || :>= + end + # @param captured_threthold [String, nil] # @param target_number [Integer, nil] # @return [Integer] @@ -122,8 +153,18 @@ def decide_reroll_threthold(captured_threthold, target_number) end end - def msg_invalid_reroll_number() - "条件が間違っています。2R6>=5 あるいは 2R6[5] のように振り足し目標値を指定してください。" + # @return [String] + def expr() + reroll_cmp_op_text = @cmp_op != @reroll_cmp_op ? Format.comparison_operator(@reroll_cmp_op) : nil + cmp_op_text = Format.comparison_operator(@cmp_op) + + "(#{@notation}[#{reroll_cmp_op_text}#{@reroll_threshold}]#{cmp_op_text}#{@target_number})" + end + + # @param command [String] + # @return [String] + def msg_invalid_reroll_number(command) + "#{@nick_e}: #{command} > 条件が間違っています。2R6>=5 あるいは 2R6[5] のように振り足し目標値を指定してください。" end # @param sides [Integer] @@ -147,20 +188,41 @@ def valid_reroll_rule?(sides, cmp_op, reroll_threshold) # 振り足しロール end end + # @param times [Integer] + # @param sides [Integer] + # @return [Array] def roll_(times, sides) _, dice_list, = @bcdice.roll(times, sides, (@diceBot.sortType & 2)) dice_list.split(",").map(&:to_i) end + # @param prefix [String] + # @param string [String] + # @param [String, nil] def trim_prefix(prefix, string) if string.start_with?(prefix) string = string[prefix.size..-1] end - if string.size.zero? + if string.empty? nil else string end end + + # 整数を比較する + # Ruby 1.8のケア用 + # + # @param lhs [Integer] + # @param op [Symbol] + # @param rhs [Integer] + # @return [Boolean] + def compare(lhs, op, rhs) + if op == :'!=' + lhs != rhs + else + lhs.send(op, rhs) + end + end end diff --git a/src/test/data/None.txt b/src/test/data/None.txt index 2ac7b73dd..96cbe7996 100644 --- a/src/test/data/None.txt +++ b/src/test/data/None.txt @@ -748,6 +748,36 @@ DiceBot : (2R4+2R6[4]>=4) > 4,3 + 3,5 + 1 + 2 > 成功数2 rand:4/4,3/4,3/6,5/6,1/4,2/6 ============================ input: +2R4+2R6[>4]>=4 +output: +DiceBot : (2R4+2R6[>4]>=4) > 4,3 + 3,5 + 2 > 成功数2 +rand:4/4,3/4,3/6,5/6,2/6 +============================ +input: +2R4+2R6>=4@>4 +output: +DiceBot : (2R4+2R6[>4]>=4) > 4,3 + 3,5 + 2 > 成功数2 +rand:4/4,3/4,3/6,5/6,2/6 +============================ +input: +2R4+2R6[<=2]>=4 +output: +DiceBot : (2R4+2R6[<=2]>=4) > 4,2 + 3,5 + 2 + 4 > 成功数3 +rand:4/4,2/4,3/6,5/6,2/4,4/4 +============================ +input: +2R4+2R6>=4@<=2 +output: +DiceBot : (2R4+2R6[<=2]>=4) > 4,2 + 3,5 + 2 + 4 > 成功数3 +rand:4/4,2/4,3/6,5/6,2/4,4/4 +============================ +input: +2R4+2R6[<>4]>=4 +output: +DiceBot : (2R4+2R6[<>4]>=4) > 4,2 + 3,5 + 4 + 4,4 > 成功数5 +rand:4/4,2/4,3/6,5/6,4/4,4/6,4/6 +============================ +input: 134 数値だけには反応しない output: rand: @@ -1006,13 +1036,13 @@ rand: input: 2R6[3] output: -DiceBot : (2R6[3]) > 3,2 + 1 > 成功数0 +DiceBot : (2R6[>=3]) > 3,2 + 1 > 成功数0 rand:3/6,2/6,1/6 ============================ input: S2R6[3] output: -DiceBot : (2R6[3]) > 3,2 + 1 > 成功数0###secret dice### +DiceBot : (2R6[>=3]) > 3,2 + 1 > 成功数0###secret dice### rand:3/6,2/6,1/6 ============================ input: