Skip to content

Day 03: Gear Ratios

For part 1, I find all numbers by regex and see if they're adjacent to a symbol by looking up their dilated subrange in a matrix. At the same time, I plot those numbers on a second matrix representation. In part 2 I can then lookup the numbers adjacent to the gears. I would like to be able to make this solution more elegant. Part 2 would go wrong if any gear has identical numbers on either side, I was lucky that didn't occur in my input.

file: src/Day03.jl
module Day03

using .Iterators: flatmap

struct Number
  line::Int
  pos::UnitRange{Int}
  value::Int
end

function find_numbers(inp)
  int_re = r"[1-9][0-9]*"
  numbers_from_line((line, str)) =
    findall(int_re, str) .|> (pos -> Number(line, pos, parse(Int, str[pos])))
  flatmap(numbers_from_line, enumerate(inp))
end

function dilation(n::Number, size)
  CartesianIndices((
    max(n.pos.start - 1, 1):min(n.pos.stop + 1, size[1]),
    max(n.line - 1, 1):min(n.line + 1, size[2])
  ))
end

function main(io::IO)
  input = collect(readlines(io))
  inp_mat = input .|> (line -> [line...]) |> (vv -> reduce(hcat, vv))

  number_mat = zeros(Int, size(inp_mat)...)
  symbol_char(c) = ((c < '0') | (c > '9')) & (c != '.')
  part_sum = 0
  for n in find_numbers(input)
    number_mat[n.pos, n.line] .= n.value
    if any(symbol_char.(inp_mat[dilation(n, size(inp_mat))]))
      part_sum += n.value
    end
  end
  println("Part 1: ", part_sum)

  star(p) = CartesianIndices((p[1]-1:p[1]+1, p[2]-1:p[2]+1))
  gear_sum = 0
  for g in findall(c -> c == '*', inp_mat)
    nums = setdiff!(Set(number_mat[star(g)]), [0])
    if length(nums) == 2
      gear_sum += prod(nums)
    end
  end
  println("Part 2: ", gear_sum)
end

end
output day 3
Part 1: 553825
Part 2: 93994191