[2018][ruby][18.1]
change wrrnsvqlqutxywtttkrkyuousmpkmzls
commit 5e05e814f3a2c3f3297ecc5f004bf4cb1e6a4092
author Alpha Chen <alpha.chen@gmail.com>
date
parent nzvpywkw
diff --git a/2018/ruby/day_18.rb b/2018/ruby/day_18.rb
new file mode 100644
index 0000000..136a5c9
--- /dev/null
+++ b/2018/ruby/day_18.rb
@@ -0,0 +1,233 @@
+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)
+  area = area.changes.lazy.drop(10).first
+  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