132 lines
2.6 KiB
Ruby
Executable File
132 lines
2.6 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
# frozen_string_literal: true
|
|
|
|
require "pathname"
|
|
require "set"
|
|
|
|
class Grid
|
|
NEIGHBOR = {
|
|
n: [0, -1],
|
|
s: [0, 1],
|
|
e: [1, 0],
|
|
w: [-1, 0],
|
|
ne: [1, -1],
|
|
se: [1, 1],
|
|
nw: [-1, -1],
|
|
sw: [-1, 1],
|
|
}.map(&:freeze).to_h.freeze
|
|
|
|
def add_row(row)
|
|
@area ||= []
|
|
row = row.map { |x| x == "#" ? 1 : 0 }
|
|
@width ||= row.size
|
|
@area.push(row[0..@height])
|
|
end
|
|
|
|
def to_s = @area.map(&:to_s).join("\n")
|
|
def row_count = @area.size
|
|
def col_count = @width
|
|
|
|
def dup
|
|
grid = self.class.new
|
|
grid.instance_variable_set(:@area, @area.map(&:dup).dup)
|
|
grid.instance_variable_set(:@width, @width.dup)
|
|
grid.instance_variable_set(:@sticky, @sticky&.dup)
|
|
grid
|
|
end
|
|
|
|
def []=(x, y, val)
|
|
return if @sticky&.include?([x, y])
|
|
@area[y][x] = val
|
|
end
|
|
|
|
def [](x, y)
|
|
@area[y][x]
|
|
end
|
|
|
|
def make_sticky(x, y, val = 1)
|
|
@sticky ||= Set.new
|
|
self[x, y] = val
|
|
@sticky.add([x, y])
|
|
end
|
|
|
|
def count_on
|
|
@area.sum { |r| r.sum }
|
|
end
|
|
|
|
def advance(steps)
|
|
steps.times do |step|
|
|
change_count = advance_step()
|
|
return step if change_count.zero?
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def advance_step
|
|
changes = []
|
|
|
|
@area.each_with_index do |row, y|
|
|
row.each_with_index do |_, x|
|
|
nb_on_count = neighbors_that_are_on(x, y)
|
|
|
|
if self[x, y] == 1 && (nb_on_count < 2 || nb_on_count > 3)
|
|
changes.push([x, y, 0])
|
|
elsif self[x, y] == 0 && nb_on_count == 3
|
|
changes.push([x, y, 1])
|
|
end
|
|
end
|
|
end
|
|
|
|
changes.each do |(x, y, val)|
|
|
self[x, y] = val
|
|
end
|
|
|
|
changes.size
|
|
end
|
|
|
|
def neighbors_that_are_on(x, y)
|
|
NEIGHBOR.keys.sum { |d| neighbor_on?(x, y, d) ? 1 : 0 }
|
|
end
|
|
|
|
def neighbor_on?(x, y, direction)
|
|
nb_x, nb_y = NEIGHBOR[direction].then { |(nx, ny)| [x + nx, y + ny] }
|
|
return false if nb_x < 0 || nb_y < 0
|
|
|
|
neighbor = @area.dig(nb_y, nb_x)
|
|
return false unless neighbor
|
|
|
|
neighbor == 1
|
|
end
|
|
end
|
|
|
|
def parse_file(file)
|
|
file.each_line.each_with_object(Grid.new) do |line, grid|
|
|
grid.add_row(line.chomp.chars)
|
|
end
|
|
end
|
|
|
|
def main(file_paths)
|
|
file_paths.each do |file_path|
|
|
puts "File: #{file_path}"
|
|
|
|
grid1 = parse_file(file_path)
|
|
grid2 = grid1.dup
|
|
|
|
grid1.advance(100)
|
|
solution1 = grid1.count_on
|
|
puts " Solution 1: #{solution1}"
|
|
|
|
grid2.make_sticky(0, 0)
|
|
grid2.make_sticky(0, grid2.col_count - 1)
|
|
grid2.make_sticky(grid2.row_count - 1, 0)
|
|
grid2.make_sticky(grid2.row_count - 1, grid2.col_count - 1)
|
|
grid2.advance(100)
|
|
|
|
solution2 = grid2.count_on
|
|
puts " Solution 2: #{solution2}"
|
|
end
|
|
end
|
|
|
|
main(ARGV.map { |x| Pathname(x) })
|