From 95a6798f7fa2b80901f6f68a60b4ffda5e279c46 Mon Sep 17 00:00:00 2001 From: Alpha Chen Date: Sun, 10 Dec 2023 19:40:29 -0800 Subject: [PATCH] [2023][ruby][10.x] --- 2023/ruby/day_10.rb | 104 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 2023/ruby/day_10.rb diff --git a/2023/ruby/day_10.rb b/2023/ruby/day_10.rb new file mode 100644 index 0000000..50c25c3 --- /dev/null +++ b/2023/ruby/day_10.rb @@ -0,0 +1,104 @@ +require "forwardable" + +DIR_DELTAS = { + N: [-1, 0], + W: [ 0, -1], + E: [ 0, 1], + S: [ 1, 0], +} +TILE_NEIGHBOR_DELTAS = { + ?| => "NS", + ?- => "EW", + ?L => "NE", + ?J => "NW", + ?7 => "SW", + ?F => "SE", + ?S => "NEWS", +}.transform_values {|dirs| dirs.chars.map { DIR_DELTAS.fetch(_1.to_sym) }} + +Tiles = Data.define(:tiles) do + extend Forwardable + def_delegator :tiles, :key + def_delegator :tiles, :fetch + def_delegator :tiles, :size + + def neighbors(coord) + TILE_NEIGHBOR_DELTAS.fetch(self.fetch(coord)) + .map {|delta| coord.zip(delta).map { _1 + _2 }} + .select { tiles.has_key?(_1) } + end + + def to_s + coords = tiles.keys + y_range, x_range = *extents + y_range.map {|y| + x_range.map {|x| tiles.fetch([y,x], ?.) }.join + }.join("\n") + end + + def extents + y_range = Range.new(*tiles.keys.map(&:first).minmax) + x_range = Range.new(*tiles.keys.map(&:last).minmax) + [y_range, x_range] + end +end + +tiles = ARGF.readlines(chomp: true).each.with_index.inject({}) {|tiles, (row, y)| + tiles.merge( + row.chars.each.with_index + .reject {|tile,_| tile == ?. } + .to_h {|tile,x| [[y,x], tile] } + ) +} +tiles = Tiles.new(tiles) + +start = tiles.key(?S) +seed = tiles.neighbors(start) + .find {|candidate| + tiles.neighbors(candidate).any? { tiles.fetch(_1) == ?S } + } + +loop_ = [seed] +until loop_.last == start + cur = loop_.last + loop_ << tiles.neighbors(cur) + .reject { _1 == loop_[-2] } + .first +end + +# fix the start tile +start_dirs = [seed, loop_[-2]].map {|coord| start.zip(coord).map { _2 - _1 }} +tile = TILE_NEIGHBOR_DELTAS.find { _2.sort == start_dirs.sort }[0] +tiles.tiles[start] = tile + +loop_ = Tiles.new(loop_.to_h { [_1, tiles.fetch(_1)] }) +# puts loop_ + +# part one +p loop_.size / 2 + +# part two +y_range, x_range = *loop_.extents +p y_range.map {|y| + x_range.chunk_while {|x_a, x_b| + a = loop_.fetch([y, x_a], nil) + b = loop_.fetch([y, x_b], nil) + + (a.nil? && b.nil?) || (a && TILE_NEIGHBOR_DELTAS.fetch(a).include?([0,1])) + }.select {|chunk| + # keep empty tiles + end_tiles = chunk.values_at(0, -1).map {|x| loop_.fetch([y,x], nil) } + next true if end_tiles.all?(&:nil?) + + # only keep pipes that cross the horizontal axis + deltas = end_tiles.flat_map {|tile| TILE_NEIGHBOR_DELTAS.fetch(tile) } + [[-1,0], [1,0]].all? {|dy| deltas.include?(dy) } + }.inject([false, 0]) {|(enclosed, count), chunk| + start_tile = loop_.fetch([y,chunk[0]], nil) + if start_tile.nil? + [enclosed, count + (enclosed ? chunk.size : 0)] + else + [!enclosed, count] + end + } +}.sum(&:last)