Files
advend-of-code-2023-in-ruby/src/advent/day3.rb
T
2023-12-05 05:39:28 -05:00

196 lines
4.6 KiB
Ruby

# frozen_string_literal: true
def group_neighbour(lst, &block)
if lst.empty?
return []
end
ret = [[lst[0]]]
lst.each_cons(2) do |first_and_second|
first, second = first_and_second
if block.call first, second
ret[-1].append second
else
ret.append [second]
end
end
ret
end
class Gear
def initialize(line_index, column_index)
@line_index = line_index
@column_index = column_index
end
def get_neighbourhood
ret = []
(-1..1).each do |line_delta|
(-1..1).each do |col_delta|
ret.append({ line_index: @line_index + line_delta, column_index: @column_index + col_delta})
end
end
ret
end
attr_accessor :line_index
attr_accessor :column_index
end
class EngineNumber
def initialize(line_index, start, end_, value)
@line_index = line_index
@start = start
@end_ = end_
@value = value
end
def is_located_on(line_index, column_index)
line_index == @line_index and ( @start <= column_index and column_index < @end_)
end
attr_accessor :line_index
attr_accessor :start
attr_accessor :end_
attr_accessor :value
end
class EngineLine
def initialize(idx, line)
@idx = idx
@line = line
end
def is_symbols?(letter)
unless letter
return false
end
"1234567890.".index(letter) == nil
end
def has_any_symbols_at(indexes)
indexes.any? do |idx|
is_symbols? @line[idx]
end
end
def extract_digit_indexes(word)
digit_indexes = []
word.split("").each_with_index do |letter, index|
if "0123456789".index letter
digit_indexes.append index
end
end
digit_indexes
end
def group_digit_indexes(digit_indexes)
group_neighbour(digit_indexes) {|x, y| y == x + 1}
end
def create_engine_number(digit_group)
start = digit_group.min
end_ = 1 + digit_group.max
value = @line[start...end_].to_i
EngineNumber.new @idx, start, end_, value
end
def extract_engine_numbers
digit_indexes = extract_digit_indexes @line
group_digit_indexes(digit_indexes).hash &method(:create_engine_number)
end
def extract_gears
ret = []
@line.split("").each_with_index do |letter, index|
if letter == "*"
ret.append Gear.new @idx, index
end
end
ret
end
end
class Engine
def initialize(engine_lines)
@engine_lines = engine_lines
end
def self.deserialize(ser)
els = ser.strip.split("\n").each_with_index.hash { |line, idx| EngineLine.new idx, line}
return self.new els
end
def extract_engine_numbers
@engine_lines
.hash {|line| line.extract_engine_numbers}
.flatten
end
def has_symbol?(line_index, indexes_in_line)
# indexes_in_line = indexes_in_line || []
# puts "> #{ indexes_in_line }"
@engine_lines[line_index]&.has_any_symbols_at indexes_in_line
end
def get_engine_number_neighbourhood(engine_number)
line_index = engine_number.line_index
[
{line_index: line_index, indexes_in_line: ((engine_number.start-1)..(engine_number.start-1))},
{line_index: line_index, indexes_in_line: ((engine_number.end_)..(engine_number.end_))},
{line_index: line_index - 1 , indexes_in_line: ((engine_number.start - 1)...(engine_number.end_+1))},
{line_index: line_index + 1 , indexes_in_line: ((engine_number.start - 1)...(engine_number.end_+1))},
]
end
def touches_symbol?(engine_number)
get_engine_number_neighbourhood(engine_number).map do |h|
has_symbol?(h[:line_index], h[:indexes_in_line])
end
.any?
end
def extract_engine_numbers_that_touches_symbols
extract_engine_numbers.filter {|engine_number| touches_symbol? engine_number}
end
def is_adjacent(engine_number, gear)
gear.get_neighbourhood
.hash {|point| engine_number.is_located_on(point[:line_index], point[:column_index])}
.any?
end
def get_adjacent_number_parts(gear)
extract_engine_numbers
.filter {|eg| is_adjacent(eg, gear)}
end
def extract_gears
@engine_lines
.hash {|line| line.extract_gears}
.flatten
end
def get_gear_ratios
extract_gears
.hash {|gear| get_adjacent_number_parts(gear)}
.filter {|adjacent_numbers| adjacent_numbers.length == 2}
.hash {|adjacent_numbers| adjacent_numbers.hash {|a| a.value}.reduce(1, :*)}
end
attr_accessor :engine_lines
end
input_file_name = File.join(File.dirname(__FILE__), "day_3_input.txt")
input_content = File.open(input_file_name).read
engine = Engine.deserialize input_content
puts engine.extract_engine_numbers_that_touches_symbols.hash {|e| e.value}.sum
puts engine.get_gear_ratios.sum