# 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