|
|
|
require "minitest"
|
|
|
|
require "minitest/pride"
|
|
|
|
|
|
|
|
class Area
|
|
|
|
def self.parse(input)
|
|
|
|
acres = input.chomp.lines.map {|row| row.chomp.chars.to_a }
|
|
|
|
new(acres)
|
|
|
|
end
|
|
|
|
|
|
|
|
attr_reader :acres
|
|
|
|
|
|
|
|
def initialize(acres)
|
|
|
|
@acres = acres
|
|
|
|
end
|
|
|
|
|
|
|
|
def changes
|
|
|
|
return enum_for(__method__) unless block_given?
|
|
|
|
|
|
|
|
loop do
|
|
|
|
yield self
|
|
|
|
|
|
|
|
acres = @acres.map.with_index {|row, y|
|
|
|
|
row.map.with_index {|acre, x|
|
|
|
|
adjacent = self.adjacent(x, y)
|
|
|
|
case self[x, y]
|
|
|
|
when ?.
|
|
|
|
(adjacent.count(?|) >= 3) ? ?| : ?.
|
|
|
|
when ?|
|
|
|
|
(adjacent.count(?#) >= 3) ? ?# : ?|
|
|
|
|
when ?#
|
|
|
|
(adjacent.count(?#) >= 1 && adjacent.count(?|) >= 1) ? ?# : ?.
|
|
|
|
else
|
|
|
|
fail "Unexpected acre: #{self[x, y]}"
|
|
|
|
end
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@acres = acres
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
@acres.map {|row| row.join }.join(?\n)
|
|
|
|
end
|
|
|
|
|
|
|
|
def [](x, y)
|
|
|
|
@acres.fetch(y).fetch(x)
|
|
|
|
end
|
|
|
|
|
|
|
|
def adjacent(x, y)
|
|
|
|
(-1..1).flat_map {|dx| (-1..1).map {|dy| [dx, dy] } }
|
|
|
|
.reject {|dx,dy| dx == 0 && dy == 0 }
|
|
|
|
.map {|dx, dy| [x+dx, y+dy] }
|
|
|
|
.reject {|x, y| x < 0 || y < 0 }
|
|
|
|
.map {|x, y|
|
|
|
|
@acres.fetch(y) { [] }.fetch(x) { nil }
|
|
|
|
}.compact
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class TestArea < Minitest::Test
|
|
|
|
def test_area
|
|
|
|
area = Area.parse(<<~AREA)
|
|
|
|
.#.#...|#.
|
|
|
|
.....#|##|
|
|
|
|
.|..|...#.
|
|
|
|
..|#.....#
|
|
|
|
#.#|||#|#|
|
|
|
|
...#.||...
|
|
|
|
.|....|...
|
|
|
|
||...#|.#|
|
|
|
|
|.||||..|.
|
|
|
|
...#.|..|.
|
|
|
|
AREA
|
|
|
|
changes = area.changes
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
.#.#...|#.
|
|
|
|
.....#|##|
|
|
|
|
.|..|...#.
|
|
|
|
..|#.....#
|
|
|
|
#.#|||#|#|
|
|
|
|
...#.||...
|
|
|
|
.|....|...
|
|
|
|
||...#|.#|
|
|
|
|
|.||||..|.
|
|
|
|
...#.|..|.
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
.......##.
|
|
|
|
......|###
|
|
|
|
.|..|...#.
|
|
|
|
..|#||...#
|
|
|
|
..##||.|#|
|
|
|
|
...#||||..
|
|
|
|
||...|||..
|
|
|
|
|||||.||.|
|
|
|
|
||||||||||
|
|
|
|
....||..|.
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
.......#..
|
|
|
|
......|#..
|
|
|
|
.|.|||....
|
|
|
|
..##|||..#
|
|
|
|
..###|||#|
|
|
|
|
...#|||||.
|
|
|
|
|||||||||.
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
.|||||||||
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
.......#..
|
|
|
|
....|||#..
|
|
|
|
.|.||||...
|
|
|
|
..###|||.#
|
|
|
|
...##|||#|
|
|
|
|
.||##|||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
.....|.#..
|
|
|
|
...||||#..
|
|
|
|
.|.#||||..
|
|
|
|
..###||||#
|
|
|
|
...###||#|
|
|
|
|
|||##|||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
....|||#..
|
|
|
|
...||||#..
|
|
|
|
.|.##||||.
|
|
|
|
..####|||#
|
|
|
|
.|.###||#|
|
|
|
|
|||###||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
...||||#..
|
|
|
|
...||||#..
|
|
|
|
.|.###|||.
|
|
|
|
..#.##|||#
|
|
|
|
|||#.##|#|
|
|
|
|
|||###||||
|
|
|
|
||||#|||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
...||||#..
|
|
|
|
..||#|##..
|
|
|
|
.|.####||.
|
|
|
|
||#..##||#
|
|
|
|
||##.##|#|
|
|
|
|
|||####|||
|
|
|
|
|||###||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
..||||##..
|
|
|
|
..|#####..
|
|
|
|
|||#####|.
|
|
|
|
||#...##|#
|
|
|
|
||##..###|
|
|
|
|
||##.###||
|
|
|
|
|||####|||
|
|
|
|
||||#|||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
..||###...
|
|
|
|
.||#####..
|
|
|
|
||##...##.
|
|
|
|
||#....###
|
|
|
|
|##....##|
|
|
|
|
||##..###|
|
|
|
|
||######||
|
|
|
|
|||###||||
|
|
|
|
||||||||||
|
|
|
|
||||||||||
|
|
|
|
AREA
|
|
|
|
|
|
|
|
assert_equal <<~AREA.chomp, changes.next.to_s
|
|
|
|
.||##.....
|
|
|
|
||###.....
|
|
|
|
||##......
|
|
|
|
|##.....##
|
|
|
|
|##.....##
|
|
|
|
|##....##|
|
|
|
|
||##.####|
|
|
|
|
||#####|||
|
|
|
|
||||#|||||
|
|
|
|
||||||||||
|
|
|
|
AREA
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def solve(input)
|
|
|
|
area = Area.parse(input)
|
|
|
|
changes = area.changes
|
|
|
|
|
|
|
|
# Part One
|
|
|
|
# 11.times do |i|
|
|
|
|
1000000001.times do |i|
|
|
|
|
changes.next
|
|
|
|
|
|
|
|
wooded = area.acres.flat_map(&:itself).count(?|)
|
|
|
|
lumberyards = area.acres.flat_map(&:itself).count(?#)
|
|
|
|
|
|
|
|
puts "#{i}: #{wooded * lumberyards}"
|
|
|
|
end
|
|
|
|
|
|
|
|
wooded = area.acres.flat_map(&:itself).count(?|)
|
|
|
|
lumberyards = area.acres.flat_map(&:itself).count(?#)
|
|
|
|
wooded * lumberyards
|
|
|
|
end
|
|
|
|
|
|
|
|
if __FILE__ == $0
|
|
|
|
require "minitest/autorun" and exit if ENV["TEST"]
|
|
|
|
|
|
|
|
puts solve(ARGF.read)
|
|
|
|
end
|
|
|
|
|
|
|
|
__END__
|
|
|
|
Here's the cycle, 28 minutes long:
|
|
|
|
|
|
|
|
1000000000 % 28 = 552 % 28
|
|
|
|
552 % 28 = 20
|
|
|
|
|
|
|
|
540: 223468
|
|
|
|
541: 227744
|
|
|
|
542: 226338
|
|
|
|
543: 221697
|
|
|
|
544: 214775
|
|
|
|
545: 206150
|
|
|
|
546: 200326
|
|
|
|
547: 197380
|
|
|
|
548: 177364
|
|
|
|
549: 177834
|
|
|
|
550: 176960
|
|
|
|
551: 176490
|
|
|
|
552: 174584
|
|
|
|
553: 177683
|
|
|
|
554: 176808
|
|
|
|
555: 176715
|
|
|
|
556: 177784
|
|
|
|
557: 182016
|
|
|
|
558: 182479
|
|
|
|
559: 188256
|
|
|
|
560: 194892
|
|
|
|
561: 199864
|
|
|
|
562: 206720
|
|
|
|
563: 210330
|
|
|
|
564: 212102
|
|
|
|
565: 212310
|
|
|
|
566: 215306
|
|
|
|
567: 217260
|