Created
June 19, 2018 17:16
-
-
Save jamie/fc0005d256c36e84bdac1a8a1edd80d6 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
class Deck | |
attr_accessor :needs_shuffle | |
def initialize(cards = [%w(x2 miss), %w(2 -2), %w(1 -1) * 5, %w(0) * 6].flatten) | |
@orig_cards = cards | |
shuffle! | |
end | |
def add(cards) | |
@orig_cards += cards | |
self | |
end | |
def remove(cards) | |
cards.each do |card| | |
index = @orig_cards.index(card) | |
@orig_cards.delete_at(index) | |
end | |
self | |
end | |
def replace(removed, added) | |
remove(removed) | |
add(added) | |
end | |
def cards | |
@orig_cards.sort | |
end | |
def shuffle! | |
@cards = @orig_cards.shuffle | |
@needs_shuffle = false | |
end | |
def draw_normal | |
draw = [] | |
loop do | |
draw << @cards.shift | |
@needs_shuffle = true if ['x2', 'miss'].include? draw.last | |
break if draw.last !~ /r/ | |
end | |
filter(draw) | |
end | |
def draw_advantage | |
draw = [] | |
draw << @cards.shift | |
draw << @cards.shift | |
# If all rolling, keep drawing until no rolling | |
draw << @cards.shift while draw.all?{|c| c =~ /r/} | |
@needs_shuffle = true if draw.include?('x2') || draw.include?('miss') | |
# If any rolling, return whole stack | |
return filter(draw) if draw.any?{|c| c =~ /r/} | |
# If miss, use other | |
return filter([draw.first]) if draw.last == "miss" | |
return filter([draw.last]) if draw.first == "miss" | |
# If double, use that TODO: someknow pass a cutoff where +2 or +3 might be better on low attack | |
return ["x2"] if draw.include? "x2" | |
# If all numeric, return largest | |
if draw.all?{|c| c =~ /^-?\d$/} | |
return [draw.map(&:to_i).sort.last] | |
end | |
# TODO: +1 <thing> beats +1, <thing> vs <thing> is ambiguous | |
fail "Ambiguous draw: #{draw.inspect}" | |
end | |
def filter(draw) | |
return ['miss'] if draw.include? "miss" | |
draw.sort.map{|card| card == "x2" ? card : card.gsub(/^[r]/,'').to_i } | |
end | |
def end_turn | |
shuffle! if needs_shuffle | |
end | |
end | |
TRIALS = 1_000_000 | |
def sim(caption, cards, operation) | |
deck = Deck.new(cards) | |
histogram = Hash.new{|h,k| 0} | |
TRIALS.times do | |
dmg = ATTACK | |
draw = operation.call(deck) | |
ddraw = draw.dup # Useful for debugging | |
if draw.include? "miss" | |
dmg = 0 | |
else | |
while card = draw.shift do | |
case card | |
when Integer; dmg += card | |
when 'x2' ; dmg *= 2 | |
else ; fail "Unknown card: #{card}" | |
end | |
end | |
end | |
histogram[dmg] += 1 | |
deck.end_turn | |
end | |
puts "#{caption}: (#{cards.size}) #{cards.join(' ')}" | |
average = histogram.map{|k,v|k*v}.inject(&:+) / TRIALS.to_f | |
puts ">>> expected: %0.2f (+%0.2f)" % [average, average-ATTACK] | |
histogram.keys.sort.each do |k| | |
freq = (100.0 * histogram[k] / TRIALS) | |
puts "[%2d]: %5.2f %% %s" % [k, freq, '#' * freq] | |
end | |
puts | |
end | |
ATTACK = 3 | |
draw_normal = ->(d){d.draw_normal} | |
draw_advantage = ->(d){d.draw_advantage} | |
draw_mixed = ->(d){rand(2).zero? ? d.draw_normal : d.draw_advantage} | |
default = Deck.new.cards | |
sim 'default', default, draw_normal | |
sim 'default w/ advantage', default, draw_advantage | |
# sim 'default 50% advantage', default, draw_mixed | |
sim 'two 1s', Deck.new.add(%w(1 1)).cards, draw_advantage | |
sim 'two 1s', Deck.new.add(%w(r1 r1)).cards, draw_advantage | |
# sunkeeper3a = Deck.new.add(%w(1 1)).remove(%w(-1 -1 -1 -1)).cards | |
# sunkeeper3b = Deck.new.add(%w(1 1)).remove(%w(-1 -1 0 0 0 0)).cards | |
# sunkeeper3c = Deck.new.add(%w()).remove(%w(-1 -1 -1 -1 0 0 0 0)).cards | |
# sunkeeper6 = Deck.new.add(%w(1 1 2)).remove(%w(-2 -1 -1 -1 -1 0 0 0 0)).cards | |
# sim 'SK3 A', sunkeeper3a, draw_mixed | |
# sim 'SK3 B', sunkeeper3b, draw_mixed | |
# sim 'SK3 C', sunkeeper3c, draw_mixed | |
# sim 'SK6', sunkeeper6, draw_mixed | |
ATTACK = 2 | |
soothsinger = Deck.new.remove(%w(-1 -1 -1 -1 -2)).replace(%w(1 1 1 1), %w(4 4)).replace(%w(0 0 0 0 0 0), %w(1 1 2 2 2 3)).replace(%w(-1), %w(0)).add(%w(r1 r1 r1 r0 r0 r0 r0)).cards | |
soothsinger_slim = Deck.new.remove(%w(-1 -1 -1 -1 -2)).replace(%w(1 1 1 1), %w(4 4)).replace(%w(0 0 0 0 0 0), %w(1 1 2 2 2 3)).replace(%w(-1), %w(0)).cards | |
sim 'SSMax', soothsinger, draw_normal | |
sim 'SSSlim', soothsinger_slim, draw_normal |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment