diff --git a/2023/day21.rb b/2023/day21.rb new file mode 100644 index 0000000..379d0e8 --- /dev/null +++ b/2023/day21.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true +# typed: true + +require 'set' + +require 'sorbet-runtime' +require 'sorbet-struct-comparable' + +extend T::Sig + +class Cell < T::Struct + extend T::Sig + include T::Struct::ActsAsComparable + + const :r, Integer + const :c, Integer + + class << self + extend T::Sig + + sig { params(r: Integer, c: Integer).returns(Cell) } + def at(r, c) = Cell::new(r: r, c: c) + end + + sig { returns(Cell) } + def north = Cell::at(r - 1, c) + + sig { returns(Cell) } + def south = Cell::at(r + 1, c) + + sig { returns(Cell) } + def west = Cell::at(r, c - 1) + + sig { returns(Cell) } + def east = Cell::at(r, c + 1) + + sig { returns(T::Array[Cell]) } + def neighbors = [north, south, west, east] +end + +class Garden + extend T::Sig + + sig { returns(Cell) } + attr_reader :start + + def initialize(text) + @grid = T.let({}, T::Hash[Cell, String]) + + text.strip.each_line.each_with_index do |line, r| + line.strip.each_char.each_with_index do |char, c| + @grid[Cell::at(r, c)] = char + @start = Cell::at(r, c) if char == ?S + end + end + end + + sig { params(cell: Cell).returns(T.nilable(String)) } + def char(cell) = @grid[cell] + + sig { params(cell: Cell).returns(T::Boolean) } + def is_plot?(cell) + sym = char(cell) or return false + sym != '#' + end +end + +class Gardener + extend T::Sig + + sig { params(garden: Garden).void } + def initialize(garden) + @garden = garden + end + + sig { params(starts: T::Set[Cell]).returns(T::Set[Cell]) } + def step_hypothetically(starts) + dests = Set::new + starts.each do |start| + dests.merge start.neighbors.select { |c| @garden.is_plot?(c) } + end + dests + end +end + +sig { params(path: String).returns(Integer) } +def part1(path) + text = File.read(path) + garden = Garden::new(text) + gardener = Gardener::new(garden) + + pos = Set[garden.start] + 64.times do + pos = gardener.step_hypothetically(pos) + end + pos.size +end + +puts part1 File.join(__dir__, 'input/test.txt') +puts part1 File.join(__dir__, 'input/day21.txt')