You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
advent-of-code/2018/ruby/day_18.rb

281 lines
4.9 KiB

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