day 20

https://adventofcode.com/2020/day/20

The task is to assemble an image from different 10x10 tiles. The tiles themselves can be flipped and rotated.

transformations

I didn’t want to calculate the image transformations myself, so I used java’s java.awt.geom.AffineTransform. I created a 4x4 testimage with 16 different shades of red to calculate a hash of the overall image to check, which transformation yield the same result.

all 18 possible transformations

  • Clockwise180
  • Clockwise180,FlipHorizontal
  • Clockwise180,FlipVertical
  • Clockwise270
  • Clockwise270,FlipHorizontal
  • Clockwise270,FlipVertical
  • Clockwise90
  • Clockwise90,FlipHorizontal
  • Clockwise90,FlipVertical
  • FlipHorizontal
  • FlipHorizontal,Clockwise180
  • FlipHorizontal,Clockwise270
  • FlipHorizontal,Clockwise90
  • FlipVertical
  • FlipVertical,Clockwise180
  • FlipVertical,Clockwise270
  • FlipVertical,Clockwise90
  • NoOp

examples of different transformations yielding the same image

imagetransformationshash
NoOp0153045607590105120135150165180195210225
Clockwise270, FlipVertical0601201801575135195309015021045105165225
FlipVertical, Clockwise900601201801575135195309015021045105165225
Clockwise180, FlipVertical1801952102251201351501656075901050153045
FlipHorizontal1801952102251201351501656075901050153045

There are only 8 distinct transformations

imagetransformationshash
NoOp0153045607590105120135150165180195210225
Clockwise904510516522530901502101575135195060120180
Clockwise1802252101951801651501351201059075604530150
Clockwise2701801206001951357515210150903022516510545
Clockwise90, FlipVertical2251651054521015090301951357515180120600
FlipHorizontal1801952102251201351501656075901050153045
FlipVertical4530150105907560165150135120225210195180
FlipVertical, Clockwise900601201801575135195309015021045105165225

image transformations

take this tile from the example input

Tile 1951:
#.##...##.
#.####...#
.....#..##
#...######
.##.#....#
.###.#####
###.##.##.
.###....#.
..#.#..#.#
#...##.#..

tiles as images

imagetransformationsimagetransformations
NoOpClockwise90, FlipVertical
Clockwise90FlipHorizontal
Clockwise180FlipVertical
Clockwise270FlipVertical, Clockwise90

calculating edges

To find the matching edges I decided to encode the edges of each tile (and its transformed representations) as hashes. For that we take the top, left, bottom and right pixels and convert them into binary representation.

#.##...##. -->
1011000110 = 710

So the top edge of tile 1951 from above is represented as 710.

matching edges

To find a matching pair the directions of the matches matter.

match-direction: vertical

id1 (top)
id2 (bottom)

match-direction: horizontal

id1 (left) | id2 (right)

matching tiles

This are all the matching edges.

tile1 idtransformation tile1tile2 idtransformation tile2match directionhash of matching edge
1171NoOp1489Clockwise180horizontal288
1171FlipVertical1489FlipHorizontalhorizontal18
1171Clockwise901489Clockwise270vertical391
1171FlipVertical, Clockwise901489Clockwise90, FlipVerticalvertical902
1171Clockwise902473NoOphorizontal966
1171Clockwise90, FlipVertical2473FlipVerticalhorizontal399
1171Clockwise1802473Clockwise90vertical96
1171FlipVertical2473FlipVertical, Clockwise90vertical24
1427Clockwise901489Clockwise90horizontal948
1427Clockwise90, FlipVertical1489Clockwise90, FlipVerticalhorizontal183
1427Clockwise1801489Clockwise180vertical300
1427FlipVertical1489FlipVerticalvertical210
1427FlipVertical, Clockwise902311FlipVertical, Clockwise90horizontal210
1427Clockwise2702311Clockwise270horizontal300
1427FlipHorizontal2311FlipHorizontalvertical183
1427NoOp2311NoOpvertical948
1427NoOp2473Clockwise90horizontal234
1427FlipVertical2473Clockwise90, FlipVerticalhorizontal348
1427Clockwise902473Clockwise180vertical9
1427FlipVertical, Clockwise902473FlipVerticalvertical576
1427FlipHorizontal2729FlipHorizontalhorizontal576
1427Clockwise1802729Clockwise180horizontal9
1427Clockwise90, FlipVertical2729Clockwise90, FlipVerticalvertical348
1427Clockwise2702729Clockwise270vertical234
1489NoOp1171Clockwise180horizontal18
1489FlipVertical1171FlipHorizontalhorizontal288
1489Clockwise901171Clockwise270vertical689
1489FlipVertical, Clockwise901171Clockwise90, FlipVerticalvertical565
1489FlipVertical, Clockwise901427FlipVertical, Clockwise90horizontal948
1489Clockwise2701427Clockwise270horizontal183
1489FlipHorizontal1427FlipHorizontalvertical43
1489NoOp1427NoOpvertical848
1489FlipHorizontal2971FlipHorizontalhorizontal565
1489Clockwise1802971Clockwise180horizontal689
1489Clockwise90, FlipVertical2971Clockwise90, FlipVerticalvertical288
1489Clockwise2702971Clockwise270vertical18
1951NoOp2311NoOphorizontal498
1951FlipVertical2311FlipVerticalhorizontal318
1951Clockwise902311Clockwise90vertical587
1951FlipVertical, Clockwise902311FlipVertical, Clockwise90vertical841
1951Clockwise902729Clockwise90horizontal710
1951Clockwise90, FlipVertical2729Clockwise90, FlipVerticalhorizontal397
1951Clockwise1802729Clockwise180vertical177
1951FlipVertical2729FlipVerticalvertical564
2311Clockwise901427Clockwise90horizontal210
2311Clockwise90, FlipVertical1427Clockwise90, FlipVerticalhorizontal300
2311Clockwise1801427Clockwise180vertical924
2311FlipVertical1427FlipVerticalvertical231
2311FlipHorizontal1951FlipHorizontalhorizontal498
2311Clockwise1801951Clockwise180horizontal318
2311Clockwise90, FlipVertical1951Clockwise90, FlipVerticalvertical616
2311Clockwise2701951Clockwise270vertical89
2311NoOp3079FlipVerticalhorizontal89
2311FlipVertical3079NoOphorizontal616
2311Clockwise903079FlipVertical, Clockwise90vertical318
2311FlipVertical, Clockwise903079Clockwise90vertical498
2473FlipHorizontal1171FlipVertical, Clockwise90horizontal966
2473Clockwise1801171Clockwise270horizontal399
2473Clockwise90, FlipVertical1171FlipHorizontalvertical184
2473Clockwise2701171NoOpvertical116
2473FlipVertical, Clockwise901427FlipHorizontalhorizontal234
2473Clockwise2701427Clockwise180horizontal348
2473FlipHorizontal1427Clockwise90, FlipVerticalvertical481
2473NoOp1427Clockwise270vertical542
2473NoOp3079Clockwise90, FlipVerticalhorizontal116
2473FlipVertical3079Clockwise90horizontal184
2473Clockwise903079FlipVerticalvertical399
2473FlipVertical, Clockwise903079Clockwise180vertical966
2729NoOp1427NoOphorizontal576
2729FlipVertical1427FlipVerticalhorizontal9
2729Clockwise901427Clockwise90vertical962
2729FlipVertical, Clockwise901427FlipVertical, Clockwise90vertical271
2729FlipVertical, Clockwise901951FlipVertical, Clockwise90horizontal710
2729Clockwise2701951Clockwise270horizontal397
2729FlipHorizontal1951FlipHorizontalvertical680
2729NoOp1951NoOpvertical85
2729Clockwise902971Clockwise90horizontal85
2729Clockwise90, FlipVertical2971Clockwise90, FlipVerticalhorizontal680
2729Clockwise1802971Clockwise180vertical397
2729FlipVertical2971FlipVerticalvertical710
2971NoOp1489NoOphorizontal565
2971FlipVertical1489FlipVerticalhorizontal689
2971Clockwise901489Clockwise90vertical78
2971FlipVertical, Clockwise901489FlipVertical, Clockwise90vertical456
2971FlipVertical, Clockwise902729FlipVertical, Clockwise90horizontal85
2971Clockwise2702729Clockwise270horizontal680
2971FlipHorizontal2729FlipHorizontalvertical532
2971NoOp2729NoOpvertical161
3079FlipHorizontal2311Clockwise180horizontal616
3079Clockwise1802311FlipHorizontalhorizontal89
3079Clockwise90, FlipVertical2311Clockwise270vertical66
3079Clockwise2702311Clockwise90, FlipVerticalvertical264
3079FlipVertical, Clockwise902473Clockwise180horizontal184
3079Clockwise2702473FlipHorizontalhorizontal116
3079FlipHorizontal2473Clockwise270vertical501
3079NoOp2473Clockwise90, FlipVerticalvertical702

finding the corner pieces

If you count the number of matching tiles for each tile, you can determine their place in the picture.

The tiles with only two matching tiles must be the corners of the picture. Thankfully, Erik designed the puzzle in a way that this property holds.

tile idmatching tilesnumber of matches
11711489,24732
14271489,2311,2473,27294
14891171,1427,29713
19512311,27292
23111427,1951,30793
24731171,1427,30793
27291427,1951,29713
29711489,27292
30792311,24732

So, our corners are these tiles.

tile idmatching tilesnumber of matches
11711489,24732
19512311,27292
29711489,27292
30792311,24732

assembling the picture

Assembling the picture turns out to be quite complex. First, we have to pick one of the four corner pieces and assume, this is our top-left tile.

Let’s pick the aforementioned corner-tile 1951 as our top-left cornerpiece.

We now have to figure out how to transform it in a way, such that the matching edges are on the inside - or, we flip it around and look for the edges that face outwards.

finding the outer edges

tile idsnumber of tiles with that edgeedge hash
1427, 14892948
19511177
19511564
19511587
19511841
1951, 23112318
1951, 27292397
1951, 23112498
1951, 27292710
23111231

We now have to pick those transformations where the top and left edge have one of those hashes (177, 564, 587, 841)

finding correct transformations of 1951 to make it a cornerpiece with the correct edges facing outwards

Here are the edge-configurations of every possible transformation of tile 1951.

transformationtopleftbottomright
Clockwise180177318397587
Clockwise90, FlipVertical318177587397
FlipHorizontal397498177841
Clockwise270498397841177
FlipVertical564587710318
Clockwise90587564318710
NoOp710841564498
FlipVertical, Clockwise90841710498564

We now know, that there are only two valid transformations of tile 1951: FlipVertical and Clockwise90.

With this information we can orient the neighbors of 1951 properly.

orienting the neighbors

We know from above that 1951 only has two possible neighbors.

tile idmatching tiles
19512311,2729

Let’s expand that relationship further.

tile1 idtransformation tile1tile2 idtransformation tile2match directionhash of matching edge
1951FlipVertical2311FlipVerticalhorizontal318
1951FlipVertical2729FlipVerticalvertical564
1951Clockwise902729Clockwise90horizontal710
1951Clockwise902311Clockwise90vertical587

We have two possibilities to arange these pieces

y\x01
01951 FlipVertical 2311 FlipVertical
1 2729 FlipVertical
y\x01
01951 Clockwise90 2729 Clockwise90
1 2311 Clockwise90

more constraints

We know that the piece at (1,0) has to have its top edge outwards-facing.

tile idsnumber of tiles with that edgeedge hash
23111231
23111924
27291271
27291962

Here are the tile-configurations of 2311 and 2729 after applying the possible transformations (FlipVertical and Clockwise90)

idtransformationtopleftbottomright
2311FlipVertical231318210616
2311Clockwise90318231616210
2729FlipVertical710962859
2729Clockwise90962710985

Let’s examine 2311:

  • if we put 2311 at (1,0) we have to apply FlipVertical. The the top edge has to be facing outwards, which it does (hash 231).
  • if we put 2311 at (0,1) we have to apply Clockwise90. The the left edge has to be facing outwards, which it does (hash 231).

Let’s examine 2729:

  • if we put 2729 at (1,0) we have to apply Clockwise90. The the top edge has to be facing outwards, which it does (hash 962).
  • if we put 2729 at (0,1) we have to apply FlipVertical. The the left edge has to be facing outwards, which it does (hash 962).

This doesn’t help us either, since these configurations are still valid.

finding tiles with two neighbors

We want to place the tile (1,1). It has to satisfy the following condition

   tile(1,1).top  == tile(1,0).bottom
&& tile(1,1).left == tile(0,1).right
idtransformationxytopleftbottomright
2311FlipVertical10231318210616
2729FlipVertical01710962859
2729Clockwise9010962710985
2311Clockwise9001318231616210

This means we’re looking for a tile t where

t.top == 210 && t.left == 9 ||
t.top == 9   && t.left == 210
idtransformationtopleftbottomright
1427Clockwise909210348948
1427FlipVertical2109948348

We have still have two possibilities to arange these pieces.

y\x01
01951 FlipVertical 2311 FlipVertical
1 2729 FlipVertical 1427 FlipVertical
y\x01
01951 Clockwise90 2729 Clockwise90
1 2311 Clockwise90 1427 Clockwise90

finding the top right corner piece

We can now look for the top right corner piece with one added constraint. Its left edge has to match the right edge of (1,0). The transformation has to orient the tile in a way that the top and right faces point outwards.

idtransformationtopleftbottomright
2971Clockwise907885689161
3079NoOp702616184264

Still two possibilities to arrange our tiles.

y\x012
01951 FlipVertical 2311 FlipVertical 3079 NoOp
1 2729 FlipVertical 2729 FlipVertical
y\x012
01951 Clockwise90 2729 Clockwise90 2971 Clockwise90
1 2311 Clockwise90 1427 Clockwise90

finding the bottom left corner piece

We can now look for the bottom left corner piece with one added constraint. Its top edge has to match the bottom edge of (0,1). The transformation has to orient the tile in a way that the bottom and left faces point outwards.

idtransformationtopleftbottomright
2971FlipVertical8578161689
3079FlipVertical, Clockwise90616702264184
y\x01
01951 FlipVertical 2311 FlipVertical
1
2
y\x01
01951 Clockwise90 2729 Clockwise90
1
2

mirrored symmetry

We see that it’s going to continue to find two possibilities, since the whole thing can be mirrored. For completing the example we just pick the first configuration. For that we place 2971 at (2,0) and 3079 at (0,2).

y\x012
01951 FlipVertical2311 FlipVertical3079 NoOp
12729 FlipVertical1427 FlipVertical
22971 FlipVertical
y\x012
0
1
2

continue to fill the missing pieces

(1,2)

idtransformationtopleftbottomright
1489FlipVertical948689848288
y\x012
0
1
2

(2,1)

idtransformationtopleftbottomright
2473Clockwise90, FlipVertical184348399481
y\x012
0
1
2

(2,2)

Yay - our last cornerpiece

idtransformationtopleftbottomright
1171FlipHorizontal39928896902

the last piece

y\x012
0
1
2

generalizing the rules

  • pick one of the cornerpieces for (0,0) and orient it
    • top and left edges must face outwards
    • if there are more than one possible transformation, pick one of them
  val filterFn: ((Int, Edge, List[Transformation], BufferedImage)) => Boolean = { row =>
    (useTopTileFilter, useLeftTileFilter) match {
      case (true, true)   => topTileFilter(row) && leftTileFilter(row)
      case (true, false)  => topTileFilter(row) && leftOuterFilter(row)
      case (false, true)  => leftTileFilter(row) && topOuterFilter(row)
      case (false, false) => topOuterFilter(row) && leftOuterFilter(row)
    }
  }

finding Nessi

the final assembly

assembled image with bordersassembled image with highlighted borderfinal version with borders removed

nessi

We have to find this pattern in the final image, but (of course) we don’t know how the image has been transformed.

                  #
#    ##    ##    ###
 #  #  #  #  #  #

We transform the image in all 8 ways and search for the pattern in each image.

transformationsimage
NoOp
Clockwise90
Clockwise180
Clockwise270
FlipHorizontal
FlipVertical
FlipVerticalClockwise90
Clockwise90FlipVertical

The final image

We need to count the number of water-pixels that are not occupied by a sea monster. I don’t know if the sea monsters would overlap, so I selected all sea-monster pixels and diffed it with all water-pixels.

sea-monster sightingssea-monster pixelswater pixelsdiff
230303273 (our answer)

Solution for part 2

The solution for the random seed of my user looked like this

sea-monster sightingssea-monster pixelswater pixelsdiff
1928519141629 (our answer)

Turned out there were no overlapping sea monsters ;-)

Great fun, but quite complex to solve. I could have done it a lot simpler, but I enjoyed the programming with graphics.

$$ $$