-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path4_b.rb
161 lines (134 loc) · 3.18 KB
/
4_b.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# typed: true
# frozen_string_literal: true
class Level
class << self
def run(input)
bingo = Bingo.from_raw(input)
winner = bingo.last_winner!
puts "Score: #{T.must(winner).unmarked_sum * T.must(bingo.current_draw)}"
end
end
end
class BoardNumber
extend T::Sig
sig { returns(Integer) }
attr_accessor :number
sig { returns(T::Boolean) }
attr_accessor :marked
sig { params(number: Integer).void }
def initialize(number)
@number = number
@marked = false
end
sig { params(draw: Integer).void }
def mark!(draw)
@marked = true if draw == @number
end
end
class Board
extend T::Sig
sig { returns(T::Array[T::Array[BoardNumber]]) }
attr_accessor :numbers
class << self
extend T::Sig
sig { params(board_lines: T::Array[T::Array[String]]).returns(T::Array[Board]) }
def from_lines(board_lines)
board_lines.map do |lines|
Board.new(
lines.map do |line|
line.split.map do |number|
BoardNumber.new(number.to_i)
end
end
)
end
end
end
sig { params(numbers: T::Array[T::Array[BoardNumber]]).void }
def initialize(numbers = [])
@numbers = T.must(numbers)
end
sig { params(draw: Integer).void }
def mark!(draw)
T.must(numbers).each do |row|
T.must(row).each do |number|
number.mark!(draw)
end
end
end
sig { returns(T::Boolean) }
def winner?
[numbers, T.must(numbers).transpose].each do |set|
return true if T.let(set, T::Array[T::Array[BoardNumber]]).any? { |row| row.all?(&:marked) }
end
false
end
sig { returns(Integer) }
def unmarked_sum
numbers.flatten.reject(&:marked).map(&:number).sum
end
end
class Bingo
extend T::Sig
sig { returns(Array) }
attr_accessor :boards
sig { returns(Array) }
attr_accessor :draw
sig { returns(Integer) }
attr_accessor :draw_idx
class << self
extend T::Sig
sig { params(data_string: String).returns(Bingo) }
def from_raw(data_string)
bingo = Bingo.new
lines = data_string.split("\n")
bingo.draw = T.must(lines.shift).split(',').map(&:to_i)
board_lines = []
while lines.any?
lines.shift
board_lines << lines.shift(5)
end
bingo.boards = Board.from_lines(board_lines)
bingo
end
end
sig { params(draw: Array, boards: Array).void }
def initialize(draw: [], boards: [])
@draw_idx = 0
@draw = draw
@boards = boards
end
sig { returns(T.nilable(Board)) }
def draw_all!
while draw[draw_idx]
winner = draw!
return winner if winner
@draw_idx += 1
end
end
sig { returns(T.nilable(Board)) }
def last_winner!
losers = @boards
while draw[draw_idx]
winners = losers.select do |board|
board.mark!(draw[draw_idx])
board.winner?
end
losers -= winners
return winners.first if losers.empty?
@draw_idx += 1
end
end
sig { returns(T.nilable(Board)) }
def draw!
winners = @boards.select do |board|
board.mark!(draw[draw_idx])
board.winner?
end
winners.first
end
sig { returns(T.nilable(Integer)) }
def current_draw
draw[draw_idx]
end
end