167 lines
3.9 KiB
Crystal
167 lines
3.9 KiB
Crystal
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)
|