יום 12 - Subterranean Sustainability

טוב רשמית אני בדיליי אבל אין מה לעשות חודש שלם לפתור חידות יום אחרי יום זה קשה. בכל מקרה זה כיף אז ממשיכים גם אם קצת אחרי כולם.

קישור לחידה המקורית:
https://adventofcode.com/2018/day/12

פיתרון בשפת רובי:

class Garden
  # initial state: #..#.#..##......###...###
  attr_accessor :gen
  def initialize(initial_state)
    @ruleset = {}
    @flowers = {}
    @flowers.default = '.'
    @gen = 0

    @flowers[-2] = Flower.new(-2, @ruleset, false)
    @flowers[-1] = Flower.new(-1, @ruleset, false)
    initial_state.each_char.each_with_index do |char, index|
      @flowers[index] = Flower.new(index, @ruleset, char == '#')
    end
    last = initial_state.length

    @flowers[last] = Flower.new(last, @ruleset, false)
    @flowers[last + 1] = Flower.new(last + 1, @ruleset, false)
  end

  def sum_indexes_with_plants
    @flowers.select {|idx, plant| plant.state }.keys.reduce(0, :+)
  end

  # rule: ..##. => .
  def add_rule(rule)
    key, result = rule.split(/\s*=>\s*/)
    @ruleset[key] = result == '#' ? true : false
  end

  def set_neighbors
    @flowers.each do |index, flower|
      flower.l2 = @flowers[index - 2]
      flower.l1 = @flowers[index - 1]
      flower.r1 = @flowers[index + 1]
      flower.r2 = @flowers[index + 2]
    end
  end

  def nextgen
    @flowers.each { |i, f| f.update }
    @flowers.each { |i, f| f.finish_update }

    first, last = @flowers.keys.minmax
    if @flowers[first].state || @flowers[first + 1].state
      @flowers[first - 2] = Flower.new(@flowers[first].id - 2, @ruleset, false)
      @flowers[first - 1] = Flower.new(@flowers[first].id - 1, @ruleset, false)
    end

    if @flowers[last].state || @flowers[last - 1].state
      @flowers[last + 1] = Flower.new(@flowers[last].id + 1, @ruleset, false)
      @flowers[last + 2] = Flower.new(@flowers[last].id + 2, @ruleset, false)
    end

    set_neighbors
    @gen += 1
  end

  def to_s
    minkey, maxkey = @flowers.keys.minmax
    "%2d %s [%d,%d]" % [@gen, @flowers.sort_by {|k, v| k }.map {|i, f| f.to_s }.join(''), minkey, maxkey ]
  end
end

class Flower
  attr_accessor :state, :l2, :l1, :r1, :r2, :next_state, :ruleset, :id

  def initialize(id, ruleset, state)
    @ruleset = ruleset
    self.state = state
    @id = id
  end

  def to_s
    state ? '#' : '.'
  end

  def update
    key = "#{l2}#{l1}#{self}#{r1}#{r2}".rjust(5, '.')
    if ruleset.key?(key)
      self.next_state = ruleset[key]
    else
      self.next_state = false
    end
  end

  def finish_update
    self.state = next_state
  end
end

garden = nil
ARGF.each_line do |line|
  line = line.chomp
  if line =~ /initial state:/
    garden = Garden.new(line.match(/initial state: ([#.]+)$/)[1])
  elsif line =~ /#/
    garden.add_rule(line)
  end  
end

garden.set_neighbors

200.times do |i|
  prev_result = garden.sum_indexes_with_plants
  garden.nextgen
  diff = garden.sum_indexes_with_plants - prev_result
  # Turns out the diff between numbers is the same after ~100 rounds
  # for me it was 73
  puts "#{garden.gen} - #{garden.sum_indexes_with_plants} [#{diff}]"
end

puts (50_000_000_000 - 200) * 73 + garden.sum_indexes_with_plants