garden = DATA.readlines(chomp: true).flat_map.with_index {|row, y| row.chars.map.with_index {|id, x| [[y,x], id] } }.to_h DELTAS = [[-1, 0], [1, 0], [0, -1], [0, 1]] queue = Set[*garden.keys] plots = [Set[queue.first]] until queue.empty? yx = plots.last.select { queue.include?(_1) }.first if yx.nil? plots << Set[queue.first] if queue.first next end queue.delete(yx) id = garden.fetch(yx) neighbors = DELTAS .map {|d| yx.zip(d).map { _1 + _2 }} .select { garden.fetch(_1, nil) == id } if neighbors.empty? plots << Set[queue.first] if queue.first else plots.last.merge(neighbors) end end perimeter = ->(plot) { plot.flat_map {|yx| DELTAS .map {|delta| yx.zip(delta).map { _1 + _2 }} .reject { garden.fetch(_1, nil) == garden[plot.first] } } } pp plots .map { [_1.size, perimeter[_1]] } .sum { _1 * _2.length } sides = ->(plot) { id = garden.fetch(plot.first) rows = plot.group_by(&:first).values.flat_map {|row| [-1, 1].flat_map {|dy| row .map {|y,x| [y+dy, x] } .reject { garden.fetch(_1, nil) == id } .map(&:last) .sort .chunk_while { _1 + 1 == _2 } } } cols = plot.group_by(&:last).values.flat_map {|row| [-1, 1].flat_map {|dx| row .map {|y,x| [y, x+dx] } .reject { garden.fetch(_1, nil) == id } .map(&:first) .sort .chunk_while { _1 + 1 == _2 } } } rows + cols } pp plots .map { [garden.fetch(_1.first), _1.size, sides[_1]] } .sum { _2 * _3.sum(&:count) } __END__ RRRRIICCFF RRRRIICCCF VVRRRCCFFF VVRCCCJFFF VVVVCJJCFE VVIVCCJJEE VVIIICJJEE MIIIIIJJEE MIIISIJEEE MMMISSJEEE