Skip to content

Commit

Permalink
Combine recursion stack variables into a single struct
Browse files Browse the repository at this point in the history
  • Loading branch information
neilslater committed Oct 10, 2021
1 parent 82fbdb7 commit 5f1d0cc
Showing 1 changed file with 33 additions and 25 deletions.
58 changes: 33 additions & 25 deletions lib/games_dice/complex_die_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,70 @@ module GamesDice
# @!visibility private
# Private extension methods for GamesDice::ComplexDie
module ComplexDieHelpers
RecurseStack = Struct.new(:depth, :roll_reason, :subtracting, :probabilities, :prior_probability) do
def initialize
self.depth = 0
self.roll_reason = :basic
self.subtracting = false
self.probabilities = {}
self.prior_probability = 1.0
end
end

private

def recursive_probabilities(probabilities = {}, prior_probability = 1.0, depth = 0, prior_result = nil, rerolls_left = nil, roll_reason = :basic, subtracting = false)
each_probability = prior_probability / @basic_die.sides
depth += 1
if depth >= 20 || each_probability < 1.0e-16
def recursive_probabilities(stack = RecurseStack.new, prior_result = nil, rerolls_left = nil)
stack.prior_probability = stack.prior_probability / @basic_die.sides
stack.depth += 1
if stack.depth >= 20 || stack.prior_probability < 1.0e-16
@probabilities_complete = false
stop_recursing = true
end

@basic_die.each_value do |v|
recurse_probs_for_value(v, roll_reason, probabilities, each_probability, depth, prior_result, rerolls_left,
subtracting, stop_recursing)
recurse_probs_for_value(v, stack, prior_result, rerolls_left, stop_recursing)
end
probabilities
stack.probabilities
end

def recurse_probs_for_value(v, roll_reason, probabilities, each_probability, depth, prior_result, rerolls_left, subtracting, stop_recursing)
def recurse_probs_for_value(v, stack, prior_result, rerolls_left, stop_recursing)
# calculate value, recurse if there is a reroll
result_so_far, rerolls_remaining = calc_result_so_far(prior_result, rerolls_left, v, roll_reason)
result_so_far, rerolls_remaining = calc_result_so_far(prior_result, rerolls_left, v, stack)

# Find which rule, if any, is being triggered
rule_idx = find_matching_reroll_rule(v, result_so_far.rolls.length, rerolls_remaining)

if rule_idx && !stop_recursing
recurse_probs_with_rule(probabilities, each_probability, depth, result_so_far, rerolls_remaining, rule_idx,
subtracting)
recurse_probs_with_rule(stack, result_so_far, rerolls_remaining, rule_idx)
else
t = result_so_far.total
probabilities[t] ||= 0.0
probabilities[t] += each_probability
stack.probabilities[t] ||= 0.0
stack.probabilities[t] += stack.prior_probability
end
end

def recurse_probs_with_rule(probabilities, each_probability, depth, result_so_far, rerolls_remaining, rule_idx, subtracting)
def recurse_probs_with_rule(stack, result_so_far, rerolls_remaining, rule_idx)
rule = @rerolls[rule_idx]
rerolls_remaining[rule_idx] -= 1
is_subtracting = true if subtracting || rule.type == :reroll_subtract

# Apply the rule (note reversal for additions, after a subtract)
if subtracting && rule.type == :reroll_add
recursive_probabilities probabilities, each_probability, depth, result_so_far, rerolls_remaining,
:reroll_subtract, is_subtracting
else
recursive_probabilities probabilities, each_probability, depth, result_so_far, rerolls_remaining, rule.type,
is_subtracting
end
next_stack = stack.clone
next_stack.subtracting = true if stack.subtracting || rule.type == :reroll_subtract
next_stack.roll_reason = if stack.subtracting && rule.type == :reroll_add
:reroll_subtract
else
rule.type
end
recursive_probabilities next_stack, result_so_far, rerolls_remaining
end

def calc_result_so_far(prior_result, rerolls_left, v, roll_reason)
def calc_result_so_far(prior_result, rerolls_left, v, stack)
if prior_result
result_so_far = prior_result.clone
result_so_far.add_roll(v, roll_reason)
result_so_far.add_roll(v, stack.roll_reason)
rerolls_remaining = rerolls_left.clone
else
result_so_far = GamesDice::DieResult.new(v, roll_reason)
result_so_far = GamesDice::DieResult.new(v, stack.roll_reason)
rerolls_remaining = @rerolls.map(&:limit)
end
[result_so_far, rerolls_remaining]
Expand Down

0 comments on commit 5f1d0cc

Please sign in to comment.