You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

#### 166 lines 3.9 KiB Raw Permalink Blame History

 ```require "advent" ``` ```tiles = input(2020, 20).split "\n\n" ``` ```tiles.pop ``` ```thash = {} of Int32 => Array(Array(Char)) ``` ```tiles = tiles.map do |t| ``` ``` tl = t.lines ``` ``` tid = tl[0].match(/Tile (\d+):/).not_nil![1].to_i32 ``` ``` tls = tl[1..] ``` ``` thash[tid] = tls.map &.chars ``` ```end ``` ``` ``` ```class Array(T) ``` ``` def matches?(other) ``` ``` zip_with(other) { |l,r| l == r }.all? ``` ``` end ``` ``` ``` ``` def rotate ``` ``` reverse.transpose ``` ``` end ``` ```end ``` ``` ``` ```def check(side_a, side_b) ``` ``` return :normal if side_a.matches?(side_b) ``` ``` return :flip if side_a.matches?(side_b.reverse) ``` ``` return nil ``` ```end ``` ``` ``` ```def check_all(side_a, other, other_t) ``` ``` check(side_a, other.first) || check(side_a, other.last) || check(side_a, other_t.first) || check(side_a, other_t.last) ``` ```end ``` ``` ``` ```MONSTER = [ {0, 1}, {1, 0}, {4, 0}, {5, 1}, {6, 1}, {7, 0}, {10, 0}, {11,1}, {12, 1}, {13, 0}, {16, 0}, ``` ``` {17, 1}, {18, 1}, {18, 2}, {19, 1} ] ``` ``` ``` ```def stitch(m, thash, corner) ``` ``` image = Array(Array(Char)).new(12*8) do |y| ``` ``` Array(Char).new(12*8) do |x| ``` ``` '.' ``` ``` end ``` ``` end ``` ``` ``` ``` tile = thash[corner] ``` ``` tile_id = corner ``` ``` tile.reverse! if m[corner].has_key? :top ``` ``` tile.each &.reverse! if m[corner].has_key? :left ``` ``` ``` ``` 12.times do |row| ``` ``` tile = tile.not_nil! ``` ``` ``` ``` row_tile = tile ``` ``` row_id = tile_id ``` ``` ``` ``` 12.times do |col| ``` ``` row_tile = row_tile.not_nil! ``` ``` ``` ``` (0..7).each do |y| ``` ``` (0..7).each do |x| ``` ``` image[col*8+y][row*8+x] = row_tile[y+1][x+1] ``` ``` end ``` ``` end ``` ``` ``` ``` matches = nil ``` ``` thash.each do |other_id, other_tile| ``` ``` next if matches ``` ``` next if other_id == row_id ``` ``` 4.times do ``` ``` if row_tile.last.matches? other_tile.first ``` ``` row_id = other_id ``` ``` matches = other_tile ``` ``` elsif row_tile.last.matches? other_tile.first.reverse ``` ``` row_id = other_id ``` ``` matches = other_tile.map &.reverse ``` ``` end ``` ``` other_tile = other_tile.rotate ``` ``` end ``` ``` end ``` ``` ``` ``` row_tile = matches ``` ``` end ``` ``` ``` ``` rot = tile.rotate ``` ``` matches = nil ``` ``` thash.each do |other_id, other_tile| ``` ``` next if matches ``` ``` next if other_id == tile_id ``` ``` ``` ``` 4.times do ``` ``` if rot.last.matches? other_tile.first ``` ``` tile_id = other_id ``` ``` matches = other_tile.rotate.rotate.rotate ``` ``` elsif rot.last.matches? other_tile.first.reverse ``` ``` tile_id = other_id ``` ``` matches = other_tile.map(&.reverse).rotate.rotate.rotate ``` ``` end ``` ``` other_tile = other_tile.rotate ``` ``` end ``` ``` end ``` ``` tile = matches ``` ``` end ``` ``` image ``` ```end ``` ``` ``` ```def find_dragons(stitch) ``` ``` dragons = [] of {Int32,Int32} ``` ``` stitch.each_with_index do |row, y| ``` ``` row.each_with_index do |c, x| ``` ``` is_dragon = MONSTER.all? do |dx, dy| ``` ``` (stitch[y+dy]?.try &.[x+dx]?) == '#' ``` ``` end ``` ``` dragons << {x,y} if is_dragon ``` ``` end ``` ``` end ``` ``` dragons ``` ```end ``` ``` ``` ```def find_all_dragons(stitch) ``` ``` dragons = [] of {Int32,Int32} ``` ``` 4.times do ``` ``` dragons.concat find_dragons(stitch) ``` ``` dragons.concat find_dragons(stitch.reverse) ``` ``` stitch = stitch.rotate ``` ``` end ``` ``` dragons ``` ```end ``` ``` ``` ```def match(thash, tile) ``` ``` matches = {} of Symbol => {Int32, Symbol} ``` ``` cs = thash[tile] ``` ``` tcs = cs.transpose ``` ``` ``` ``` thash.each do |t, ocs| ``` ``` next if t == tile ``` ``` tocs = ocs.transpose ``` ``` top = check_all(cs.first, ocs, tocs) ``` ``` bottom = check_all(cs.last, ocs, tocs) ``` ``` left = check_all(tcs.first, ocs, tocs) ``` ``` right = check_all(tcs.last, ocs, tocs) ``` ``` ``` ``` matches[:top] = {t, top} if top ``` ``` matches[:left] = {t, left} if left ``` ``` matches[:bottom] = {t, bottom} if bottom ``` ``` matches[:right] = {t, right} if right ``` ``` end ``` ``` matches ``` ```end ``` ``` ``` ```def part1(input) ``` ``` corners = input.select do |t, i| ``` ``` match(input, t).size == 2 ``` ``` end ``` ``` corners.keys.map(&.to_i64).product ``` ```end ``` ``` ``` ```def part2(input) ``` ``` matches = {} of Int32 => Hash(Symbol, {Int32, Symbol}) ``` ``` corners = input.select do |t, i| ``` ``` matches[t] = match(input, t) ``` ``` match(input, t).size == 2 ``` ``` end ``` ``` stitched = stitch(matches, input, corners.first[0]) ``` ``` dragons = find_all_dragons(stitched) ``` ``` stitched.sum(&.count(&.==('#'))) - (dragons.size * MONSTER.size) ``` ```end ``` ``` ``` ```puts part1(thash.clone) ``` ```puts part2(thash.clone) ``` ``` ```