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|
adjacent_to_symbol = false
((y - 1)..(y + 1)).each do |y|
((x - 1)..(x + number.size)).each do |x|
if symbols.has_key?({x, y})
adjacent_to_symbol = true
end
end
end
adjacent_to_symbol
end.values.map(&.to_i).sum
puts part1
part2 = symbols.reduce(0) do |sum, ((x, y), symbol)|
adjacent_numbers = [] of Int32
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
adjacent_numbers.push(number.to_i)
end
end
end
sum += adjacent_numbers.product if adjacent_numbers.size == 2
end
sum
end
puts part2
Top comments (0)