input = DATA .readlines(chomp: true).flat_map.with_index {|row, y| row.chars.map.with_index {|i, x| [[y,x], i] } }.to_h freqs = input.values.uniq - %w[.] antennae = freqs.to_h {|freq| [freq, input.select { _2 == freq }.keys] } pp antennae.values.flat_map {|yxs| yxs.combination(2).flat_map {|(ay,ax),(by,bx)| dy = by - ay dx = bx - ax [[ay-dy, ax-dx], [by+dy, bx+dx]] }.select {|yx| input.has_key?(yx) } }.uniq.length def antinodes(input, a, b) return enum_for(__method__, input, a, b) unless block_given? max_y, max_x = input.keys.transpose.map(&:max) r = Rational(*b.zip(a).map { _1 - _2 }) delta = r.numerator, r.denominator yx = a loop do break unless input.has_key?(yx) yield yx yx = yx.zip(delta).map { _1 - _2 } end yx = b loop do break unless input.has_key?(yx) yield yx yx = yx.zip(delta).map { _1 + _2 } end end pp antennae.values.flat_map {|yxs| yxs.combination(2).flat_map {|a,b| antinodes(input, a, b).to_a } }.uniq.length __END__ ............ ........0... .....0...... .......0.... ....0....... ......A..... ............ ............ ........A... .........A.. ............ ............