Project

General

Profile

Bug #17427 ยป aoc22.rb

amcaplan (Ariel Caplan), 12/22/2020 09:31 PM

 
require 'set'

class Deck
def initialize(stack=[])
@stack = stack
end

def take
stack.shift
end

def add(*cards)
stack.concat(cards)
end

def max
stack.max
end

def play_against!(other_deck, recursing: false)
# smallest_cards = [*stack, *other_deck.stack].sort.first(2)
# if smallest_cards.sum > (stack.size + other_deck.stack.size - 2)
# $counter += 1
# puts "counter1: #{$counter}" if $counter % 1000 == 0
# presumptive_winner = [max, other_deck.max].max == max ? self : other_deck
# presumptive_winner.win!
# return nil
# end
states = Set.new
decks = [self, other_deck]
until decks.any?(&:empty?) || decks.any?(&:winner?)
states << [stack, other_deck.stack]
if states.map(&:hash).uniq.size < states.size
states.each { |state|
p state
}
raise states.inspect
end
decks.inject(:compete!)
if states.include?([stack, other_deck.stack])
win!
return nil
end
end
decks.find { |deck| !deck.empty? }.win!
nil
end

def compete!(other_deck)
mine = take
opposing = other_deck.take
if mine > opposing
add(mine, opposing)
else
other_deck.add(opposing, mine)
end
end

def win!
@winner = true
end

def winner?
!!winner
end

def score
stack.reverse.map.with_index.sum(0) { |card, index|
card * (index + 1)
}
end

def empty?
stack.empty?
end

protected
attr_reader :stack, :winner
end

class RecursiveDeck < Deck
def compete!(other_deck)
if recurse? && other_deck.recurse?
mine = take
opposing = other_deck.take
my_subdeck = recursive_duplicate(mine)
other_subdeck = other_deck.recursive_duplicate(opposing)
my_subdeck.play_against!(other_subdeck, recursing: true)
if my_subdeck.winner?
add(mine, opposing)
else
other_deck.add(opposing, mine)
end
else
super
end
end

protected

def recurse?
stack.first < stack.size
end

def recursive_duplicate(num)
self.class.new(stack.slice(0, num))
end
end

input_decks = <<~INPUTS
Player 1:
12
48
26
22
44
16
31
19
30
10
40
47
21
27
2
46
9
15
23
6
50
28
5
42
34

Player 2:
14
45
4
24
1
7
36
29
38
33
3
13
11
17
39
43
8
41
32
37
35
49
20
18
25
INPUTS

decks = []
input_decks.each_line do |line|
if line =~ /Player/
decks << RecursiveDeck.new
elsif line =~ /\d+/
decks.last.add(line.to_i)
end
end
decks.inject(:play_against!)
p decks
puts decks.map(&:score)
    (1-1/1)