musifter

AoC day 20, Smalltalk

Jan 3rd, 2021 (edited)
1,129
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/local/bin/gst -q
  2.  
  3. Integer extend [
  4.     bitAppend: bool [
  5.         ^(self bitShift: 1) bitOr: bool asCBooleanValue.
  6.     ]
  7. ]
  8.  
  9. Object subclass: Map [
  10.     | tiles edges corners grid dim |
  11.  
  12.     Map class >> new [
  13.         | bits val |
  14.         Rev_map := Array new: 1023.
  15.         (1 to: 1023) do: [ :i |
  16.             bits := i.
  17.             val  := 0.
  18.             10 timesRepeat: [
  19.                 val  := val bitAppend: ((bits bitAnd: 1) ~= 0).
  20.                 bits := bits bitShift: -1.
  21.             ].
  22.             Rev_map at: i put: val.
  23.         ].
  24.  
  25.         ^(super new) init.
  26.     ]
  27.  
  28.     init [
  29.         tiles := LookupTable new.
  30.         edges := LookupTable new.
  31.         ^self
  32.     ]
  33.  
  34.     Map class >> revEdge: edge [
  35.         ^Rev_map at: edge
  36.     ]
  37.  
  38.     addTile: tile [
  39.         tiles at: (tile getId) put: tile.
  40.  
  41.         tile getEdges do: [ :edgeId |
  42.             (edges at: edgeId ifAbsentPut: [OrderedCollection new]) add: tile getId.
  43.         ].
  44.     ]
  45.  
  46.     getTile: tileId [ ^(tiles at: tileId) ]
  47.     getDim          [ ^dim ]
  48.  
  49.     isInternal: edge [
  50.         ^((edges at: edge) size = 2)
  51.     ]
  52.  
  53.     findCorners [
  54.         ^corners := tiles select: [ :tile |
  55.             ((tile getEdges select: [ :e | self isInternal: e ]) size = 4).
  56.         ]
  57.     ]
  58.  
  59.     solveMap [
  60.         | corner corn_edges intern rots |
  61.         dim := tiles size sqrt asInteger.
  62.         grid := (1 to: dim) collect: [ :i | Array new: dim ].
  63.  
  64.         corner := corners anyOne.
  65.         corn_edges := corner getEdges.
  66.         intern := (1 to: 4) select: [ :i | self isInternal: (corn_edges at: i) ].
  67.  
  68.         rots := ((intern at: 2) = 4) ifTrue:  [ ((intern at: 1) = 1) ifTrue: [1] ifFalse: [0] ]
  69.                                      ifFalse: [ intern at: 2 ].
  70.  
  71.         rots timesRepeat: [ corner rotTile ].
  72.         (grid at: 1) at: 1 put: corner.
  73.  
  74.         (2 to: dim) do: [ :x |
  75.            | prev edgeId tile |
  76.             prev := (grid at: 1) at: (x - 1).
  77.  
  78.             edgeId := Map revEdge: (prev getEdges at: 4).
  79.             tile := tiles at: ((edges at: edgeId) detect: [ :t | t ~= prev getId ]).
  80.             tile translate: edgeId to: 2.
  81.             (grid at: 1) at: x put: tile.
  82.         ].
  83.  
  84.         (2 to: dim) do: [ :y |
  85.             (1 to: dim) do: [ :x |
  86.                | prev edgeId tile |
  87.                 prev := (grid at: y - 1) at: x.
  88.                 edgeId := Map revEdge: (prev getEdges at: 3).
  89.  
  90.                 tile := tiles at: ((edges at: edgeId) detect: [ :t | t ~= prev getId ]).
  91.                 tile translate: edgeId to: 1.
  92.                 (grid at: y) at: x put: tile.
  93.             ].
  94.         ].
  95.     ]
  96.  
  97.     stitch [
  98.         | mosaic my line |
  99.         mosaic := Array new: dim * 8.
  100.         my := 1.
  101.  
  102.         (1 to: dim) do: [ :gy |
  103.             (1 to: 8) do: [ :y |
  104.                 line := ''.
  105.                 (1 to: dim) do: [ :gx |
  106.                     line := (line, ((((grid at: gy) at: gx) getTile) at: y) asString).
  107.                 ].
  108.  
  109.                 mosaic at: my put: line.
  110.                 my := my + 1.
  111.             ]
  112.         ].
  113.  
  114.         ^mosaic
  115.     ]
  116. ]
  117.  
  118. Object subclass: Tile [
  119.     | id edges tile |
  120.     Tile class >> withId: idNum tile: strArray [
  121.         ^(super new) init: idNum tile: strArray.
  122.     ]
  123.  
  124.     init: tileNum tile: tileStr [
  125.         | top left bot right |
  126.         id    := tileNum.
  127.         edges := Array new: 8.
  128.         tile  := Array new: 8.
  129.         top   := 0.
  130.         left  := 0.
  131.         bot   := 0.
  132.         right := 0.
  133.  
  134.         (1 to: 10) do: [ :i |
  135.             top   := top   bitAppend: (((tileStr at: 1) at: i) = $#).
  136.             left  := left  bitAppend: (((tileStr at: 11 - i) at: 1) = $#).
  137.             bot   := bot   bitAppend: (((tileStr at: 10) at: 11 - i) = $#).
  138.             right := right bitAppend: (((tileStr at: i) at: 10) = $#).
  139.         ].
  140.  
  141.         edges at: 1 put: top; at: 2 put: left; at: 3 put: bot; at: 4 put: right;
  142.               at: 5 put: (Map revEdge: left);
  143.               at: 6 put: (Map revEdge: top);
  144.               at: 7 put: (Map revEdge: right);
  145.               at: 8 put: (Map revEdge: bot).
  146.  
  147.         (2 to: 9) do: [ :i |
  148.             tile at: (i - 1) put: ((tileStr at: i) readStream copyFrom: 1 to: 8).
  149.         ].
  150.  
  151.         ^self
  152.     ]
  153.  
  154.     getId       [ ^id ]
  155.     getEdges    [ ^edges ]
  156.     getTile     [ ^tile ]
  157.  
  158.     flipTile [
  159.         | flip f_edges |
  160.         flip := (1 to: 8) collect: [ :i | Array new: 8 ].
  161.  
  162.         f_edges := Array new: 8.
  163.  
  164.         (1 to: 8) do: [ :y |
  165.             (1 to: 8) do: [ :x |
  166.                 (flip at: y) at: x put: ((tile at: x) at: y).
  167.             ]
  168.         ].
  169.  
  170.         (1 to: 4) do: [ :i |
  171.             f_edges at: i put: (edges at: (i + 4)).
  172.             f_edges at: (i + 4) put: (edges at: i).
  173.         ].
  174.  
  175.         tile := flip.
  176.         edges := f_edges.
  177.     ]
  178.  
  179.     rotTile [
  180.         | rot r_edges |
  181.         rot := (1 to: 8) collect: [ :i | Array new: 8 ].
  182.  
  183.         r_edges := Array new: 8.
  184.  
  185.         (1 to: 8) do: [ :y |
  186.             (1 to: 8) do: [ :x |
  187.                 (rot at: y) at: x put: ((tile at: (9 - x)) at: y).
  188.             ]
  189.         ].
  190.  
  191.         (1 to: 3) do: [ :i |
  192.             r_edges at: i put: (edges at: (i + 1)).
  193.             r_edges at: (i + 5) put: (edges at: (i + 4)).
  194.         ].
  195.         r_edges at: 4 put: (edges at: 1).
  196.         r_edges at: 5 put: (edges at: 8).
  197.  
  198.         tile := rot.
  199.         edges := r_edges.
  200.     ]
  201.  
  202.     translate: edgeId to: side [
  203.         | idx |
  204.         idx := (1 to: 8) detect: [ :i | (edges at: i) = edgeId ].
  205.         (idx > 4) ifTrue: [ self flipTile. idx := idx - 4 ].
  206.         ((idx - side) \\ 4) timesRepeat: [ self rotTile ].
  207.     ]
  208. ]
  209.  
  210. "
  211. |  Mainline
  212. "
  213. map := Map new.
  214.  
  215. inStream := stdin lines contents readStream.
  216. [ (line := inStream next) notNil ] whileTrue: [
  217.     id := line substrings second asNumber.  " read header "
  218.  
  219.     inTile := OrderedCollection new.
  220.     10 timesRepeat: [ inTile addLast: inStream next ].  " read tile "
  221.  
  222.     map addTile: (Tile withId: id tile: inTile).
  223.  
  224.     inStream next.  " skip blank line "
  225. ].
  226.  
  227. stdout nextPutAll: 'Part 1: ', (map findCorners inject: 1 into: [:a :b | a * b getId]) asString; nl.
  228.  
  229. " Put the map together "
  230. map solveMap.
  231.  
  232. " Get the mosaic in four orientations: as made, flipped diagonally, and lines reversed "
  233. mosaic := Array new: 4.
  234. mosaic at: 1 put: map stitch.
  235.  
  236. dim := (map getDim * 8).
  237.  
  238. " Flip diagonally "
  239. mosaic at: 2 put: ((1 to: dim) collect: [ :i | String new: dim ]).
  240. (1 to: dim) do: [ :y |
  241.     (1 to: dim) do: [ :x |
  242.         ((mosaic at: 2) at: y) at: x put: (((mosaic at: 1) at: x) at: y).
  243.     ].
  244. ].
  245.  
  246. " Reverse lines of the above two "
  247. (1 to: 2) do: [ :i |
  248.     mosaic at: (i + 2) put: ((mosaic at: i) collect: [ :line | line reverse ]).
  249. ].
  250.  
  251. "
  252. | The four remaining orientations are vertical flips of these four, so we can use
  253. | the middle line of the monster to judge which matches best.
  254. "
  255. mons_mid := '#....##....##....###' asRegex.
  256.  
  257. best_map := nil.
  258. best_match := nil.
  259.  
  260. (1 to: 4) do: [ :map |
  261.     mat := (2 to: dim - 1) select: [:i | (((mosaic at: map) at: i) =~ mons_mid) matched].
  262.     (mat size > best_match size) ifTrue: [
  263.         best_match := mat.
  264.         best_map := mosaic at: map.
  265.     ].
  266. ].
  267.  
  268. " Scan map for monsters both up-side-up and up-side-down "
  269. up_count := 0.
  270. down_count := 0.
  271.  
  272. best_match do: [ :i |
  273.     stream := (best_map at: i) readStream.
  274.     pos := 0.
  275.     [ (mat := (stream upToEnd =~ mons_mid)) matched ] whileTrue: [
  276.         pos := pos + mat from.
  277.  
  278.         (((best_map at: i - 1) at: pos + 18) = $#) ifTrue: [
  279.             below := (best_map at: i + 1) copyFrom: pos to: pos + 16.
  280.             ((below =~ '^.#..#..#..#..#..#') matched) ifTrue: [
  281.                 up_count := up_count + 1.
  282.             ].
  283.         ].
  284.  
  285.         (((best_map at: i + 1) at: pos + 18) = $#) ifTrue: [
  286.             above:= (best_map at: i - 1) copyFrom: pos to: pos + 16.
  287.             ((above =~ '^.#..#..#..#..#..#') matched) ifTrue: [
  288.                 down_count := down_count + 1.
  289.             ].
  290.         ].
  291.  
  292.         stream position: pos.
  293.     ].
  294. ].
  295.  
  296. " Count waves in stitched image "
  297. waves := (best_map collect: [ :line | (line select: [ :c | c = $# ]) size ])
  298.                 inject: 0 into: [ :a :b | a + b ].
  299.  
  300. stdout nextPutAll: 'Part 2: ', (waves - ((up_count max: down_count) * 15)) asString; nl.
Add Comment
Please, Sign In to add comment