diff --git a/2023/ruby/day_03.rb b/2023/ruby/day_03.rb index 393a840..08de043 100644 --- a/2023/ruby/day_03.rb +++ b/2023/ruby/day_03.rb @@ -1,69 +1,55 @@ +require "strscan" + Part = Data.define(:number) do def eql?(other) self.object_id == other.object_id end end -CurrentNumber = Data.define(:coords, :acc) -cur_num = nil -nums = {} -syms = {} -ARGF.readlines(chomp: true).each.with_index do |row, y| - row.chars.each.with_index do |c, x| - case c - when /\d/ - if cur_num - cur_num.coords << [y,x] - cur_num.acc << c +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 - cur_num = CurrentNumber.new([[y,x]], c) - end - else - if cur_num - part = Part.new(cur_num.acc.to_i) - nums.merge!(cur_num.coords.to_h { [_1, part] }) - cur_num = nil - end - - if c != ?. - syms[[y,x]] = c - end + fail end end - - if cur_num - part = Part.new(cur_num.acc.to_i) - nums.merge!(cur_num.coords.to_h { [_1, part] }) - cur_num = nil - 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 - .flat_map {|(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) } - .uniq - } - .sum(&:number) +p syms.keys.flat_map { neighboring_parts.(_1) }.uniq.sum(&:number) +# part two p syms .select { _2 == ?* } - .filter_map {|(y,x), _| - dy = (-1..1).to_a - dx = (-1..1).to_a - parts = dy.product(dx) - .map {|dy,dx| [y+dy,x+dx] } - .filter_map { nums.fetch(_1, nil) } - .uniq - - if parts.length == 2 - parts - else - nil - end + .filter_map {|coord, _| + parts = neighboring_parts.(coord).uniq + parts.length == 2 ? parts : nil } .sum { _1.map(&:number).inject(:*) }