You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
advent-of-code/2023/ruby/day_03.rb

56 lines
1.1 KiB

require "strscan"
Part = Data.define(:number) do
def eql?(other)
self.object_id == other.object_id
end
end
def parse(input)
ss = StringScanner.new(input)
y = x = 0
schematic = {}
until ss.eos?
case
when ss.scan(/\./)
x += 1
when num = ss.scan(/\d+/)
coords = (0...num.length).map {|dx| [y, x+dx] }
part = Part.new(num.to_i)
schematic.merge!(coords.to_h { [_1, part] })
x += num.length
when ss.scan(/\n/)
y += 1
x = 0
when sym = ss.scan(/./)
schematic[[y,x]] = sym
x += 1
else
fail
end
end
schematic
end
schematic = parse(ARGF.read)
syms, nums = schematic.partition { String === _2 }.map(&:to_h)
neighboring_parts = ->((y,x)) {
dy = (-1..1).to_a
dx = (-1..1).to_a
dy.product(dx)
.map {|dy,dx| [y+dy,x+dx] }
.filter_map { nums.fetch(_1, nil) }
}
# part one
p syms.keys.flat_map { neighboring_parts.(_1) }.uniq.sum(&:number)
# part two
p syms
.select { _2 == ?* }
.filter_map {|coord, _|
parts = neighboring_parts.(coord).uniq
parts.length == 2 ? parts : nil
}
.sum { _1.map(&:number).inject(:*) }