Caleb Weeks

Posted on

# Advent of Code #3 (in Crystal)

The nature of Advent of Code problems coming in two parts means that you kind of have to take a gamble on a good data structure for the first part, and just hope it works out for the second without too much refactoring.

Well, I decided to store the numbers and symbols in two separate hashes with keys of `{x, y}` positions. (For the numbers, this position represented the position of the first digit). For the first part, this allowed a simple hash lookup to figure out if there was an adjacent symbol.

For the second part, it was almost as simple as looking up in the other direction, but since I did not store the position of every digit (just the first digit in the number), there was a bit of extra math to do. I ended up just looking a little further to the left, making sure that the number was actually long enough to be adjacent to the gear.

I solved this one live here, but didn't finish part 2 on the stream. Ironically, I ended up finishing it just 5 minutes after the stream ended. By the way, I would love to get to 20 subscribers by the end of the year. Could you help me get there? Thanks!

Here's my code:

``````input = File.read("input").strip

alias Position = Tuple(Int32, Int32)
alias PositionMap = Hash(Position, String)

numbers = input
.split("\n")
.map_with_index { |x, i| {x, i} }
.reduce(PositionMap.new) do |number_map, (line, line_number)|
line_numbers = line.scan(/\d+/).reduce(PositionMap.new) do |line_number_map, number|
line_number_map.merge({ {number.begin, line_number} => number[0] })
end
number_map.merge(line_numbers)
end

symbols = input
.split("\n")
.map_with_index { |x, i| {x, i} }
.reduce(PositionMap.new) do |symbol_map, (line, line_number)|
line_symbols = line.scan(/[^(\d|.)]/).reduce(PositionMap.new) do |line_symbol_map, symbol|
line_symbol_map.merge({ {symbol.begin, line_number} => symbol[0] })
end
symbol_map.merge(line_symbols)
end

part1 = numbers.select do |(x, y), number|
((y - 1)..(y + 1)).each do |y|
((x - 1)..(x + number.size)).each do |x|
if symbols.has_key?({x, y})
end
end
end
end.values.map(&.to_i).sum

puts part1

part2 = symbols.reduce(0) do |sum, ((x, y), symbol)|
if symbol == "*"
((y - 1)..(y + 1)).each do |j|
((x - 3)..(x + 1)).each do |i|
number = numbers[{i, j}]?
if !number.nil? && number.size + i >= x