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
class Layout
def self.from(raw, &logic)
seats = Hash.new
raw.split("\n").each.with_index do |row,y|
row.chars.each.with_index do |pos,x|
seats[[y,x]] = false if pos == ?L
end
end
self.new(seats, &logic)
end
attr_reader :seats, :y_max, :x_max
def initialize(seats, &logic)
@seats, @logic = seats, logic
@y_max = @seats.keys.map(&:first).max
@x_max = @seats.keys.map(&:last).max
end
def [](yx)
@seats[yx]
end
def each
return enum_for(__method__) unless block_given?
loop do
yield self
seats = tick(&@logic)
break if seats == @seats
@seats = seats
end
end
def tick
@seats.keys.map {|yx|
[yx, @logic[self, yx]]
}.to_h
end
def to_s
(0..@y_max).map {|y|
(0..@x_max).map {|x|
case @seats[[y,x]]
when true then ?#
when false then ?L
when nil then ?.
else fail
end
}.join
}.join("\n")
end
end
NEIGHBORS = (-1..1).flat_map {|y| (-1..1).map {|x| [y, x] }} - [[0, 0]]
day1 = ->(layout, yx) {
y, x = yx
occupied_neighbors = NEIGHBORS.filter_map {|dy,dx| layout[[y+dy, x+dx]] }
if !layout[yx] && occupied_neighbors.empty?
true
elsif layout[yx] && occupied_neighbors.size >= 4
false
else
layout[yx]
end
}
day2 = ->(layout, yx) {
y, x = yx
occupied_neighbors = NEIGHBORS.filter_map {|dy,dx|
(1..).lazy
.map {|i| [y+dy*i, x+dx*i] }
.find {|y,x|
!(0..layout.y_max).cover?(y) ||
!(0..layout.x_max).cover?(x) ||
layout.seats.has_key?([y, x])
}
}.filter_map {|yx| layout[yx] }
if !layout[yx] && occupied_neighbors.empty?
true
elsif layout[yx] && occupied_neighbors.size >= 5
false
else
layout[yx]
end
}
layout = Layout.from(ARGF.read, &day2)
layout.each.take_while(&:itself).last
puts layout.seats.count(&:last)