OOP in Ruby; I enjoyed writing this!
require 'benchmark' class Seat def initialize(mark) self.mark = mark self.next_mark = mark end def valid? mark != 'X' end def empty? mark == 'L' end def occupied? mark == '#' end def floor? mark == '.' end def invalid? mark == 'X' end def seat? occupied? || empty? end def to_s mark end def prepare_to_dance! if occupied? self.next_mark = 'L' elsif empty? self.next_mark = '#' end self end def will_it_flip? self.next_mark != mark end def do_the_shuffle! self.mark = next_mark end private attr_accessor :mark, :next_mark end class SeatingGrid def self.from_lines(grid) height = grid.length width = grid[0].chomp.length gridline = grid.map(&:chomp).join('') SeatingGrid.new(gridline, width: width, height: height) end def initialize(gridline, width:, height:) self.seats = gridline.chars.map { |x| Seat.new(x) } self.width = width self.height = height self._adjacency = {} self._visually_adjacent = {} self._visually_at = {} end def at(x, y) return Seat.new('X') if x < 0 || x >= width || y < 0 || y >= height self[y * width + x] end def visual_at(x, y, direction:) memoized = _visually_at["#{x}.#{y}"] return memoized if memoized seat = at(x, y) if seat.seat? || seat.invalid? _visually_at["#{x}.#{y}"] = seat return seat end visual_at( x + direction.first, y + direction.last, direction: direction ) end def [](i) seats[i] end def adjacent(x, y) memoized = _adjacency["#{x}.#{y}"] return memoized if memoized _adjacency["#{x}.#{y}"] = [ at(x - 1, y - 1), # top left at(x , y - 1), # top at(x + 1, y - 1), # top right at(x + 1, y ), # right at(x + 1, y + 1), # bottom right at(x , y + 1), # bottom at(x - 1, y + 1), # bottom left at(x - 1, y ), # left ].select(&:seat?) end def visual_adjacent(x, y) memoized = _visually_adjacent["#{x}.#{y}"] return memoized if memoized _visually_adjacent["#{x}.#{y}"] = [ visual_at(x - 1, y - 1, direction: [-1, -1]), # top left visual_at(x , y - 1, direction: [ 0, -1]), # top visual_at(x + 1, y - 1, direction: [ 1, -1]), # top right visual_at(x + 1, y , direction: [ 1, 0]), # right visual_at(x + 1, y + 1, direction: [ 1, 1]), # bottom right visual_at(x , y + 1, direction: [ 0, 1]), # bottom visual_at(x - 1, y + 1, direction: [-1, 1]), # bottom left visual_at(x - 1, y , direction: [-1, 0]), # left ].select(&:seat?) end def prepare_to_dance!(x, y) at(x, y).prepare_to_dance! end def each (0..width).each do |x| (0..height).each do |y| seat = at(x, y) yield x, y, seat if seat.seat? end end end def visualize gridline .chars .each_slice(width) .map(&:join) .join("\n") end def occupancy seats.count(&:occupied?) end def dance! seats.each(&:do_the_shuffle!) end private attr_accessor :seats, :width, :height attr_accessor :_adjacency, :_visually_adjacent, :_visually_at end def simulate(grid, adjacency:, packed_when:, verbose: false) rules = {} rules[-> (seat) { seat.empty? }] = -> (adjacents) { !(adjacents.any? { |a| a.occupied? }) } rules[-> (seat) { seat.occupied? }] = -> (adjacents) { !(adjacents.count { |a| a.occupied? } < packed_when) } iteration = 0 loop do changes = 0 iteration += 1 grid.each do |x, y, seat| next if seat.floor? rule = rules.find { |k, _| k.(seat) } next unless rule adjacents = adjacency.(grid, x, y) next unless rule.last.(adjacents) changed = grid.prepare_to_dance!(x, y).will_it_flip? changes += changed ? 1 : 0 end grid.dance! if verbose puts "[#{iteration}] #{changes} changes" puts puts grid.visualize puts "" puts "" end if changes.zero? puts "==============================" if verbose puts "Seats taken: #{grid.occupancy}" break end end end source = 'input.txt' Benchmark.bmbm do |x| x.report('part 1') do grid = SeatingGrid.from_lines(File.readlines(source)) simulate( grid, adjacency: -> (grid, x, y) { grid.adjacent(x, y) }, packed_when: 4, ) end x.report('part 2') do grid = SeatingGrid.from_lines(File.readlines(source)) simulate( grid, adjacency: -> (grid, x, y) { grid.visual_adjacent(x, y) }, packed_when: 5 ) end end
Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink.
Hide child comments as well
Confirm
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
OOP in Ruby; I enjoyed writing this!