-
Notifications
You must be signed in to change notification settings - Fork 189
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #217 from bcdice/refactor_reroll_dice
Rerollダイスのリファクタリングと振り足し条件指定の仕様変更
- Loading branch information
Showing
3 changed files
with
225 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,143 +1,234 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
require "utils/normalize" | ||
require "utils/format" | ||
|
||
# 個数振り足しダイス | ||
# | ||
# ダイスを振り、条件を満たした出目の個数だけダイスを振り足す。振り足しがなくなるまでこれを繰り返す。 | ||
# 成功条件を満たす出目の個数を調べ、成功数を表示する。 | ||
# | ||
# 例 | ||
# 2R6+1R10[>3]>=5 | ||
# 2R6+1R10>=5@>3 | ||
# | ||
# 振り足し条件は角カッコかコマンド末尾の @ で指定する。 | ||
# [>3] の場合、3より大きい出目が出たら振り足す。 | ||
# [3] のように数値のみ指定されている場合、成功条件の比較演算子を流用する。 | ||
# 上記の例の時、出目が | ||
# "2R6" -> [5,6] [5,4] [1,3] | ||
# "1R10" -> [9] [1] | ||
# だとすると、 >=5 に該当するダイスは5つなので成功数5となる。 | ||
# | ||
# 成功条件が書かれていない場合、成功数0として扱う。 | ||
# 振り足し条件が数値のみ指定されている場合、比較演算子は >= が指定されたとして振舞う。 | ||
class RerollDice | ||
def initialize(bcdice, diceBot) | ||
@bcdice = bcdice | ||
@diceBot = diceBot | ||
@nick_e = @bcdice.nick_e | ||
end | ||
|
||
#################### 個数振り足しダイス ######################## | ||
def rollDice(string) | ||
output = '' | ||
|
||
begin | ||
output = rollDiceCatched(string) | ||
rescue StandardError => e | ||
output = "#{string} > " + e.to_s | ||
unless parse(string) | ||
return nil | ||
end | ||
|
||
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' | ||
unless @reroll_threshold | ||
return msg_invalid_reroll_number(string) | ||
end | ||
|
||
string, braceThreshold, operator, conditionValue, atmarkThreshold = m.captures | ||
|
||
signOfInequality, diff = getCondition(operator, conditionValue) | ||
rerollNumber = getRerollNumber(braceThreshold, atmarkThreshold, diff) | ||
debug('rerollNumber', rerollNumber) | ||
|
||
debug("diff", diff) | ||
|
||
diceQueue = [] | ||
string.split("+").each do |xRn| | ||
x, n = xRn.split("R").map { |s| s.to_i } | ||
checkReRollRule(n, signOfInequality, diff) | ||
dice_queue = @notation.split("+").map do |xRn| | ||
x, n = xRn.split("R").map(&:to_i) | ||
[x, n, 0] | ||
end | ||
|
||
diceQueue.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 | ||
|
||
successCount = 0 | ||
diceStrList = [] | ||
success_count = 0 | ||
dice_str_list = [] | ||
dice_cnt_total = 0 | ||
numberSpot1Total = 0 | ||
loopCount = 0 | ||
one_count = 0 | ||
loop_count = 0 | ||
|
||
while !diceQueue.empty? && @diceBot.should_reroll?(loopCount) | ||
dice_total_count = 0 | ||
|
||
while !dice_queue.empty? && @diceBot.should_reroll?(loop_count) | ||
# xRn | ||
x, n, depth = diceQueue.shift | ||
loopCount += 1 | ||
x, n, depth = dice_queue.shift | ||
loop_count += 1 | ||
dice_total_count += x | ||
|
||
total, dice_str, numberSpot1, cnt_max, n_max, success, rerollCount = | ||
@bcdice.roll(x, n, (@diceBot.sortType & 2), 0, signOfInequality, diff, rerollNumber) | ||
debug('bcdice.roll : total, dice_str, numberSpot1, cnt_max, n_max, success, rerollCount', | ||
total, dice_str, numberSpot1, cnt_max, n_max, success, rerollCount) | ||
dice_list = roll_(x, n) | ||
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) } | ||
|
||
successCount += success | ||
diceStrList.push(dice_str) | ||
dice_cnt_total += x | ||
dice_str_list.push(dice_list.join(",")) | ||
|
||
if depth.zero? | ||
numberSpot1Total += numberSpot1 | ||
one_count += dice_list.count(1) | ||
end | ||
|
||
if rerollCount > 0 | ||
diceQueue.push([rerollCount, n, depth + 1]) | ||
if reroll_count > 0 | ||
dice_queue.push([reroll_count, n, depth + 1]) | ||
end | ||
end | ||
|
||
output = "#{diceStrList.join(' + ')} > 成功数#{successCount}" | ||
string += "[#{rerollNumber}]#{signOfInequality}#{diff}" | ||
grich_text = @diceBot.getGrichText(one_count, dice_total_count, success_count) | ||
|
||
sequence = [ | ||
expr(), | ||
dice_str_list.join(" + "), | ||
"成功数#{success_count}", | ||
trim_prefix(" > ", grich_text), | ||
].compact | ||
|
||
debug("string", string) | ||
output += @diceBot.getGrichText(numberSpot1Total, dice_cnt_total, successCount) | ||
return "#{@nick_e}: #{sequence.join(' > ')}" | ||
end | ||
|
||
output = "(#{string}) > #{output}" | ||
private | ||
|
||
if output.length > $SEND_STR_MAX # 長すぎたときの救済 | ||
output = "(#{string}) > ... > 回転数#{round} > 成功数#{successCount}" | ||
# @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 | ||
|
||
return output | ||
@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_threshold(m[3] || m[7], @target_number) | ||
|
||
return true | ||
end | ||
|
||
def getCondition(operator, conditionValue) | ||
if operator && conditionValue | ||
operator = @bcdice.marshalSignOfInequality(operator) | ||
conditionValue = conditionValue.to_i | ||
elsif (m = /([<>=]+)(\d+)/.match(@diceBot.defaultSuccessTarget)) | ||
operator = @bcdice.marshalSignOfInequality(m[1]) | ||
conditionValue = m[2].to_i | ||
# @return [Array<(Symbol, Integer)>] | ||
# @return [Array<(nil, nil)>] | ||
def target_from_default | ||
m = /^([<>=]+)(\d+)$/.match(@diceBot.defaultSuccessTarget) | ||
unless m | ||
return nil, nil | ||
end | ||
|
||
return operator, conditionValue | ||
cmp_op = Normalize.comparison_operator(m[1]) | ||
target_number = cmp_op ? m[2].to_i : nil | ||
return cmp_op, target_number | ||
end | ||
|
||
def getRerollNumber(braceThreshold, atmarkThreshold, conditionValue) | ||
if braceThreshold | ||
braceThreshold.to_i | ||
elsif atmarkThreshold | ||
atmarkThreshold.to_i | ||
# @param m [MatchData] | ||
# @return [Symbol] | ||
def decide_reroll_cmp_op(m) | ||
bracket_op = m[2] | ||
bracket_number = m[3] | ||
at_op = m[6] | ||
at_number = m[7] | ||
cmp_op = m[4] | ||
|
||
op = | ||
if bracket_op && bracket_number | ||
bracket_op | ||
elsif at_op && at_number | ||
at_op | ||
else | ||
cmp_op | ||
end | ||
|
||
Normalize.comparison_operator(op) || :>= | ||
end | ||
|
||
# @param captured_threshold [String, nil] | ||
# @param target_number [Integer, nil] | ||
# @return [Integer] | ||
# @return [nil] | ||
def decide_reroll_threshold(captured_threshold, target_number) | ||
if captured_threshold | ||
captured_threshold.to_i | ||
elsif @diceBot.rerollNumber != 0 | ||
@diceBot.rerollNumber | ||
elsif conditionValue | ||
conditionValue.to_i | ||
else | ||
raiseErroForJudgeRule() | ||
target_number | ||
end | ||
end | ||
|
||
def raiseErroForJudgeRule() | ||
raise "条件が間違っています。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 | ||
|
||
def checkReRollRule(dice_max, signOfInequality, diff) # 振り足しロールの条件確認 | ||
valid = true | ||
|
||
case signOfInequality | ||
when '<=' | ||
valid = false if diff >= dice_max | ||
when '>=' | ||
valid = false if diff <= 1 | ||
when '<>' | ||
valid = false if (diff > dice_max) || (diff < 1) | ||
when '<' | ||
valid = false if diff > dice_max | ||
when '>' | ||
valid = false if diff < 1 | ||
# @param command [String] | ||
# @return [String] | ||
def msg_invalid_reroll_number(command) | ||
"#{@nick_e}: #{command} > 条件が間違っています。2R6>=5 あるいは 2R6[5] のように振り足し目標値を指定してください。" | ||
end | ||
|
||
# @param sides [Integer] | ||
# @param cmp_op [Symbol] | ||
# @param reroll_threshold [Integer] | ||
# @return [Boolean] | ||
def valid_reroll_rule?(sides, cmp_op, reroll_threshold) # 振り足しロールの条件確認 | ||
case cmp_op | ||
when :<= | ||
reroll_threshold < sides | ||
when :< | ||
reroll_threshold <= sides | ||
when :>= | ||
reroll_threshold > 1 | ||
when :> | ||
reroll_threshold >= 1 | ||
when :'!=' | ||
(1..sides).include?(reroll_threshold) | ||
else | ||
true | ||
end | ||
end | ||
|
||
unless valid | ||
raiseErroForJudgeRule() | ||
# @param times [Integer] | ||
# @param sides [Integer] | ||
# @return [Array<Integer>] | ||
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.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 |
Oops, something went wrong.