day 20, part 2

master
Matthew Butterick 4 years ago
parent 182bd889bf
commit 87e6fbadcb

@ -1,5 +1,5 @@
#lang br #lang br
(require racket/file rackunit) (require racket/file rackunit racket/dict)
(define edge-flipped (define edge-flipped
(let () (let ()
@ -13,7 +13,7 @@
(arithmetic-shift 1 i))) (arithmetic-shift 1 i)))
(λ (x) (hash-ref! cache x (λ () (edge-flipper x)))))) (λ (x) (hash-ref! cache x (λ () (edge-flipper x))))))
(struct tile (num edges) #:transparent) (struct tile (num edges css) #:transparent #:mutable)
(define (tilestr->edgevals tilestr) (define (tilestr->edgevals tilestr)
(define vec (list->vector (string->list (string-replace tilestr "\n" "")))) (define vec (list->vector (string->list (string-replace tilestr "\n" ""))))
@ -27,15 +27,17 @@
#:when (char=? (vector-ref vec idx) #\#)) #:when (char=? (vector-ref vec idx) #\#))
(arithmetic-shift 1 i)))) (arithmetic-shift 1 i))))
(define tilestrs (make-hash))
(define tiles (define tiles
(let* ([recs (string-split (file->string "20.rktd") "Tile")] (let* ([recs (string-split (file->string "20.rktd") "Tile")]
[recs (map string-trim recs)]) [recs (map string-trim recs)])
(for/list ([rec recs]) (for/list ([rec recs])
(match-define (list numstr tilestr) (string-split rec ":")) (match-define (list numstr tilestr) (string-split rec ":"))
(tile (string->number numstr) (define num (string->number numstr))
(tilestr->edgevals tilestr))))) (tile num (tilestr->edgevals tilestr) (map string->list (string-split tilestr "\n"))))))
(define (matching-edges tile) (define (matching-tiles tile [tiles tiles])
(for/list ([edge (in-list (tile-edges tile))]) (for/list ([edge (in-list (tile-edges tile))])
(for/first ([other-tile (in-list (remove tile tiles))] (for/first ([other-tile (in-list (remove tile tiles))]
#:when #:when
@ -43,7 +45,131 @@
(or (eq? edge other-edge) (eq? edge (edge-flipped other-edge))))) (or (eq? edge other-edge) (eq? edge (edge-flipped other-edge)))))
other-tile))) other-tile)))
(define (corner-tile? tile) (define (matching-edge-count tile count)
(= 2 (length (filter values (matching-edges tile))))) (= count (length (filter values (matching-tiles tile)))))
(define (edge-tile? tile) (matching-edge-count tile 3))
(define (corner-tile? tile) (matching-edge-count tile 2))
(check-equal? (apply * (map tile-num (filter corner-tile? tiles))) 15405893262491) (check-equal? (apply * (map tile-num (filter corner-tile? tiles))) 15405893262491)
(define corners (filter corner-tile? tiles))
(define first-corner (car corners))
(require racket/set)
(define tileset (apply mutable-set (map tile-num tiles)))
(define tile-grid (make-hasheqv))
(define (flip-css css) (map reverse css))
(define (flip t)
(tile (tile-num t) (tile-edges t) (flip-css (tile-css t))))
(define (rotate-css css)
(map reverse (apply map list css)))
(define (rotate t)
(tile (tile-num t) (tile-edges t) (rotate-css (tile-css t))))
#;(define (place-line tile coord orientation)
(hash-set! tile-grid coord tile)
(set-remove! tileset (tile-num tile))
(for ([(edge idx) (in-indexed (tile-edges tile))]
[dir '(+i 1 -i -1)]
[matching-tile (matching-tiles tile)]
#:when (and matching-tile (= dir orientation)))
;; match polarity
(unless (memq edge (tile-edges matching-tile))
(flip! matching-tile))
;; match rotation
(let loop ()
(rotate! matching-tile)
(unless (eq? (list-ref (tile-edges tile) idx)
(list-ref (tile-edges matching-tile) idx))
(loop)))
;; rotate 180
(rotate! matching-tile)
(rotate! matching-tile)
;; invert polarity
(flip! matching-tile)
(place-line matching-tile (+ coord dir) orientation)))
(define (top tile) (first (tile-css tile)))
(define (bottom tile) (last (tile-css tile)))
(define (left tile) (map first (tile-css tile)))
(define (right tile) (map last (tile-css tile)))
(define (joiner tile left-proc right-proc)
(for*/first ([other-tile tiles]
#:unless (eq? (tile-num tile) (tile-num other-tile))
[flip-proc (list values flip)]
[rotate-proc (list values rotate (compose1 rotate rotate) (compose1 rotate rotate rotate))]
[adjusted-tile (in-value (rotate-proc (flip-proc other-tile)))]
#:when (equal? (left-proc tile) (right-proc adjusted-tile)))
adjusted-tile))
(define (insert-joiners! coord)
(define tile (hash-ref tile-grid coord))
(for ([edge-proc (list top left bottom right)]
[opp-edge-proc (list bottom right top left)]
[dir (list +i -1 -i 1)])
(match (joiner tile edge-proc opp-edge-proc)
[#false (void)]
[joiner (hash-set! tile-grid (+ coord dir) joiner)])))
(hash-set! tile-grid 0 first-corner)
(for* ([imag (in-range 12)]
[real (in-range 0 -12 -1)])
(insert-joiners! (+ real (* imag +i))))
(define (trim-edge css)
(drop-right (cdr (map (λ (cs) (drop-right (cdr cs) 1)) css)) 1))
(define actual-image
(append*
(for/list ([imag (in-range 11 -1 -1)])
(apply map append
(for/list ([real (in-range -11 1)])
(trim-edge (tile-css (hash-ref tile-grid (+ real (* imag +i))))))))))
#;(for-each (λ (x) (displayln (list->string x))) actual-image)
(define row-width (length (car actual-image)))
(define (css->vec css) (list->vector (apply append css)))
(define sea-monster-offsets (list 0
(- row-width 18)
(- row-width 13)
(- row-width 12)
(- row-width 7)
(- row-width 6)
(- row-width 1)
row-width
(+ row-width 1)
(- (* 2 row-width) 2)
(- (* 2 row-width) 5)
(- (* 2 row-width) 8)
(- (* 2 row-width) 11)
(- (* 2 row-width) 14)
(- (* 2 row-width) 17)))
(define (sea-monster-at? vec idx)
(for/and ([offset (in-list sea-monster-offsets)])
(define pos (+ idx offset))
(and
(< pos (vector-length vec))
(char=? (vector-ref vec pos) #\#))))
(define vec (css->vec (rotate-css (rotate-css (rotate-css actual-image)))))
(define sea-monster-idxs
(for/list ([idx (in-range (vector-length vec))]
#:when (sea-monster-at? vec idx))
idx))
(define sea-monster-parts
(set->list
(for*/set ([idx sea-monster-idxs]
[offset sea-monster-offsets])
(+ idx offset))))
(check-equal? (- (count (λ (c) (char=? #\# c)) (apply append actual-image)) (length sea-monster-parts)) 2133)