Project

General

Profile

Bug #17427 ยป aoc22.rb

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

 
1
require 'set'
2

    
3
class Deck
4
  def initialize(stack=[])
5
    @stack = stack
6
  end
7

    
8
  def take
9
    stack.shift
10
  end
11

    
12
  def add(*cards)
13
    stack.concat(cards)
14
  end
15

    
16
  def max
17
    stack.max
18
  end
19

    
20
  def play_against!(other_deck, recursing: false)
21
    # smallest_cards = [*stack, *other_deck.stack].sort.first(2)
22
    # if smallest_cards.sum > (stack.size + other_deck.stack.size - 2)
23
      # $counter += 1
24
      # puts "counter1: #{$counter}" if $counter % 1000 == 0
25
      # presumptive_winner = [max, other_deck.max].max == max ? self : other_deck
26
      # presumptive_winner.win!
27
      # return nil
28
    # end
29
    states = Set.new
30
    decks = [self, other_deck]
31
    until decks.any?(&:empty?) || decks.any?(&:winner?)
32
      states << [stack, other_deck.stack]
33
      if states.map(&:hash).uniq.size < states.size
34
        states.each { |state|
35
          p state
36
        }
37
        raise states.inspect 
38
      end
39
      decks.inject(:compete!)
40
      if states.include?([stack, other_deck.stack])
41
        win!
42
        return nil
43
      end
44
    end
45
    decks.find { |deck| !deck.empty? }.win!
46
    nil
47
  end
48

    
49
  def compete!(other_deck)
50
    mine = take
51
    opposing = other_deck.take
52
    if mine > opposing
53
      add(mine, opposing)
54
    else
55
      other_deck.add(opposing, mine)
56
    end
57
  end
58

    
59
  def win!
60
    @winner = true
61
  end
62

    
63
  def winner?
64
    !!winner
65
  end
66

    
67
  def score
68
    stack.reverse.map.with_index.sum(0) { |card, index|
69
      card * (index + 1)
70
    }
71
  end
72

    
73
  def empty?
74
    stack.empty?
75
  end
76

    
77
  protected
78
  attr_reader :stack, :winner
79
end
80

    
81
class RecursiveDeck < Deck
82
  def compete!(other_deck)
83
    if recurse? && other_deck.recurse?
84
      mine = take
85
      opposing = other_deck.take
86
      my_subdeck = recursive_duplicate(mine)
87
      other_subdeck = other_deck.recursive_duplicate(opposing)
88
      my_subdeck.play_against!(other_subdeck, recursing: true)
89
      if my_subdeck.winner?
90
        add(mine, opposing)
91
      else
92
        other_deck.add(opposing, mine)
93
      end
94
    else
95
      super
96
    end
97
  end
98

    
99
  protected
100

    
101
  def recurse?
102
    stack.first < stack.size
103
  end
104

    
105
  def recursive_duplicate(num)
106
    self.class.new(stack.slice(0, num))
107
  end
108
end
109

    
110
input_decks = <<~INPUTS
111
Player 1:
112
12
113
48
114
26
115
22
116
44
117
16
118
31
119
19
120
30
121
10
122
40
123
47
124
21
125
27
126
2
127
46
128
9
129
15
130
23
131
6
132
50
133
28
134
5
135
42
136
34
137

    
138
Player 2:
139
14
140
45
141
4
142
24
143
1
144
7
145
36
146
29
147
38
148
33
149
3
150
13
151
11
152
17
153
39
154
43
155
8
156
41
157
32
158
37
159
35
160
49
161
20
162
18
163
25
164
INPUTS
165

    
166
decks = []
167
input_decks.each_line do |line|
168
  if line =~ /Player/
169
    decks << RecursiveDeck.new
170
  elsif line =~ /\d+/
171
    decks.last.add(line.to_i)
172
  end
173
end
174
decks.inject(:play_against!)
175
p decks
176
puts decks.map(&:score)