-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path21-2.rb
executable file
·110 lines (91 loc) · 2.5 KB
/
21-2.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env ruby
class Game
attr_accessor :num_games
attr_accessor :wins
def initialize(p1, p2)
# @num_games and @wins are hashes of [position1, score1,
# position2, score2] -> number of games
# @num_games are still active, @wins have ended
@num_games = Hash.new 0
@num_games[[p1 - 1, 0, p2 - 1, 0]] = 1
@wins = Hash.new 0
possible_rolls = [1, 2, 3].product([1, 2, 3]).product([1, 2, 3]).map(&:flatten).map(&:sum).sort
num_possibilities = possible_rolls.uniq.map { |p| possible_rolls.count p }
@rolls = possible_rolls.uniq.zip num_possibilities
end
def remove_wins!
ended = []
@num_games.each do |state, num|
_, score1, _, score2 = state
if score1 >= 21 or score2 >= 21
@wins[state] += num
ended.append state
end
end
ended.each do |state|
@num_games.delete state
end
end
def go1!
new_num_games = Hash.new 0
@num_games.each do |state, num|
pos1, score1, pos2, score2 = state
@rolls.each do |roll, ways|
new_pos1 = (pos1 + roll) % 10
new_state = [new_pos1, score1 + new_pos1 + 1, pos2, score2]
new_num_games[new_state] += num * ways
end
end
@num_games = new_num_games
end
def go2!
new_num_games = Hash.new 0
@num_games.each do |state, num|
pos1, score1, pos2, score2 = state
@rolls.each do |roll, ways|
new_pos2 = (pos2 + roll) % 10
new_state = [pos1, score1, new_pos2, score2 + new_pos2 + 1]
new_num_games[new_state] += num * ways
end
end
@num_games = new_num_games
end
def num_wins(player)
win_score_index = 1
win_score_index = 3 if player == 1
@wins.select { |state, _| state[win_score_index] >= 21 }.values.sum
end
def score
[num_wins(0), num_wins(1)].max
end
def play!
until @num_games.empty?
go1!
remove_wins!
go2!
remove_wins!
end
end
def to_s
s = "<#{self.class}:\n"
@num_games.each do |state, num|
pos1, score1, pos2, score2 = state
s += "P1 at #{pos1 + 1}, score #{score1}, P2 at #{pos2 + 1}, score #{score2}: #{num} games.\n"
end
s += '>'
s
end
def inspect
to_s
end
end
format = /^Player (?<player>[[:digit:]]) starting position: (?<pos>[[:digit:]]+)$/
lines = File.read('21.input').lines.map(&:strip)
players = [0, 0]
2.times do
m = format.match(lines.shift)
players[m['player'].to_i - 1] = m['pos'].to_i
end
game = Game.new(players[0], players[1])
game.play!
print game.score, "\n"