solved day 16
This commit is contained in:
parent
d6312a6865
commit
6d947be58c
3 changed files with 260 additions and 20 deletions
3
aoc.asd
3
aoc.asd
|
@ -13,9 +13,10 @@
|
||||||
#:plump
|
#:plump
|
||||||
#:lquery
|
#:lquery
|
||||||
#:3d-vectors
|
#:3d-vectors
|
||||||
#:array-operations
|
#:array-operationIs
|
||||||
#:lla
|
#:lla
|
||||||
#:queues.simple-queue
|
#:queues.simple-queue
|
||||||
|
#:queues.priority-queue
|
||||||
#:bt-semaphore ; threads higher level lib
|
#:bt-semaphore ; threads higher level lib
|
||||||
#:trivia ; community standard pattern matching
|
#:trivia ; community standard pattern matching
|
||||||
#:transducers
|
#:transducers
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
|
||||||
(defpackage :aoc/2024/16
|
(defpackage :aoc/2024/16
|
||||||
(:use :cl :aoc :alexandria :trivia :lla)
|
(:use :cl :aoc :alexandria :trivia :lla :queues)
|
||||||
(:export
|
(:export
|
||||||
|
#:parse-input
|
||||||
|
#:best-score
|
||||||
|
#:best-path
|
||||||
|
#:solve-part2
|
||||||
#:sample-data
|
#:sample-data
|
||||||
#:sample-data2
|
#:sample-data2
|
||||||
#:part1
|
#:part1
|
||||||
|
@ -10,26 +14,241 @@
|
||||||
|
|
||||||
(in-package :aoc/2024/16)
|
(in-package :aoc/2024/16)
|
||||||
|
|
||||||
|
|
||||||
(defun parse-line (line)
|
(defun parse-line (line)
|
||||||
line)
|
(coerce line 'list))
|
||||||
|
|
||||||
|
|
||||||
(defun parse-input (lines)
|
(defun parse-input (lines)
|
||||||
(mapcar #'parse-line lines))
|
(make-array
|
||||||
|
(list (length lines) (length (first lines)))
|
||||||
|
:initial-contents (mapcar #'parse-line lines)))
|
||||||
|
|
||||||
(defparameter input-text (test-input 2024 16))
|
(defparameter input-text (test-input 2024 16))
|
||||||
(defparameter input-data (parse-input input-text))
|
(defparameter input-data (parse-input input-text))
|
||||||
|
|
||||||
(defparameter sample-text (aoc:split-lines ""))
|
(defparameter sample-text (aoc:split-lines "###############
|
||||||
|
#.......#....E#
|
||||||
|
#.#.###.#.###.#
|
||||||
|
#.....#.#...#.#
|
||||||
|
#.###.#####.#.#
|
||||||
|
#.#.#.......#.#
|
||||||
|
#.#.#####.###.#
|
||||||
|
#...........#.#
|
||||||
|
###.#.#####.#.#
|
||||||
|
#...#.....#.#.#
|
||||||
|
#.#.#.###.#.#.#
|
||||||
|
#.....#...#.#.#
|
||||||
|
#.###.#.#.#.#.#
|
||||||
|
#S..#.....#...#
|
||||||
|
###############"))
|
||||||
|
|
||||||
|
(defstruct pos
|
||||||
|
(x 0 :type fixnum)
|
||||||
|
(y 0 :type fixnum))
|
||||||
|
|
||||||
|
(defun pos-move (pos dir)
|
||||||
|
(case dir
|
||||||
|
(#\^ (make-pos :x (pos-x pos) :y (1- (pos-y pos))))
|
||||||
|
(#\v (make-pos :x (pos-x pos) :y (1+ (pos-y pos))))
|
||||||
|
(#\< (make-pos :x (1- (pos-x pos)) :y (pos-y pos)))
|
||||||
|
(#\> (make-pos :x (1+ (pos-x pos)) :y (pos-y pos)))
|
||||||
|
))
|
||||||
|
|
||||||
|
(defun show-map (map &optional (dest t))
|
||||||
|
(let ((w (array-dimension map 1))
|
||||||
|
(h (array-dimension map 0)))
|
||||||
|
(format
|
||||||
|
dest "~{~A~}"
|
||||||
|
(loop
|
||||||
|
for row from 0 below h
|
||||||
|
collect (format nil "~A~%"
|
||||||
|
(coerce
|
||||||
|
(loop for col from 0 below w
|
||||||
|
collect (aref map row col))
|
||||||
|
'string))))))
|
||||||
|
|
||||||
|
(defun get-pos (map pos)
|
||||||
|
(aref map (pos-y pos) (pos-x pos)))
|
||||||
|
|
||||||
|
(defun set-pos (map pos tile)
|
||||||
|
(setf (aref map (pos-y pos) (pos-x pos)) tile))
|
||||||
|
|
||||||
|
(defun find-tile (map char)
|
||||||
|
(let ((w (array-dimension map 1))
|
||||||
|
(h (array-dimension map 0)))
|
||||||
|
(loop
|
||||||
|
for row from 0 below h
|
||||||
|
for col = (loop
|
||||||
|
for col from 0 below w
|
||||||
|
if (eq (aref map row col) char) return col)
|
||||||
|
if col
|
||||||
|
return (make-pos :x col :y row)
|
||||||
|
)))
|
||||||
|
|
||||||
|
(defun swap-pos (map p np)
|
||||||
|
"swaps content of map. returns np"
|
||||||
|
(let ((o (get-pos map p)))
|
||||||
|
(setf (aref map (pos-y p) (pos-x p)) (get-pos map np))
|
||||||
|
(setf (aref map (pos-y np) (pos-x np)) o)
|
||||||
|
np))
|
||||||
|
|
||||||
|
|
||||||
|
(defun wet-finger-distance (a b)
|
||||||
|
"estimate the distance"
|
||||||
|
(+
|
||||||
|
(abs (- (pos-x a) (pos-x b)))
|
||||||
|
(abs (- (pos-y a) (pos-y b)))
|
||||||
|
(and
|
||||||
|
(eq (pos-x a) (pos-x b))
|
||||||
|
(eq (pos-y a) (pos-y b))
|
||||||
|
1000)))
|
||||||
|
|
||||||
|
(defun rotate-left (dir)
|
||||||
|
(case dir
|
||||||
|
(#\^ #\<)
|
||||||
|
(#\v #\>)
|
||||||
|
(#\< #\v)
|
||||||
|
(#\> #\^)
|
||||||
|
))
|
||||||
|
(defun rotate-right (dir)
|
||||||
|
(case dir
|
||||||
|
(#\^ #\>)
|
||||||
|
(#\v #\<)
|
||||||
|
(#\< #\^)
|
||||||
|
(#\> #\v)
|
||||||
|
))
|
||||||
|
|
||||||
|
(defun valid-pos-p (map pos)
|
||||||
|
(not (eq #\# (get-pos map pos))))
|
||||||
|
|
||||||
|
(defstruct reindeer pos dir)
|
||||||
|
|
||||||
|
(defstruct state path score)
|
||||||
|
|
||||||
|
(defun next-moves (map reindeer)
|
||||||
|
"return conses of next reindeer and cost to get there"
|
||||||
|
(let ((d (reindeer-dir reindeer))
|
||||||
|
(p (reindeer-pos reindeer)))
|
||||||
|
(remove-if-not
|
||||||
|
#'(lambda (r) (valid-pos-p map (reindeer-pos (car r))))
|
||||||
|
(list
|
||||||
|
(cons (make-reindeer :pos (pos-move p d) :dir d) 1)
|
||||||
|
(cons (make-reindeer :pos p :dir (rotate-left d)) 1000)
|
||||||
|
(cons (make-reindeer :pos p :dir (rotate-right d)) 1000)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun distance-estimate-f (finish)
|
||||||
|
"estimate the distance for a reindeer from given finish"
|
||||||
|
(let ((a finish))
|
||||||
|
(lambda (reindeer)
|
||||||
|
(let ((b (reindeer-pos reindeer) ))
|
||||||
|
(+
|
||||||
|
(abs (- (pos-x a) (pos-x b)))
|
||||||
|
(abs (- (pos-y a) (pos-y b)))
|
||||||
|
(if (or
|
||||||
|
(eq (pos-x a) (pos-x b))
|
||||||
|
(eq (pos-y a) (pos-y b)))
|
||||||
|
0
|
||||||
|
1000))))))
|
||||||
|
|
||||||
|
(defun compare-states-f (finish)
|
||||||
|
(let ((dist-est (distance-estimate-f finish)))
|
||||||
|
(lambda (s1 s2)
|
||||||
|
(< (+ (state-score s1)
|
||||||
|
(funcall dist-est (car (state-path s1))))
|
||||||
|
(+ (state-score s2)
|
||||||
|
(funcall dist-est (car (state-path s2))))))))
|
||||||
|
|
||||||
|
(defun best-path (map)
|
||||||
|
(let* ((start (find-tile map #\S))
|
||||||
|
(finish (find-tile map #\E))
|
||||||
|
(state-comparison (compare-states-f finish))
|
||||||
|
(best (make-hash-table :test #'equalp))
|
||||||
|
(todo (make-queue :priority-queue :compare state-comparison)))
|
||||||
|
(qpush todo (make-state
|
||||||
|
:path (list (make-reindeer :pos start :dir #\>))
|
||||||
|
:score 0))
|
||||||
|
(loop
|
||||||
|
for s = (qpop todo)
|
||||||
|
until (or
|
||||||
|
(null s)
|
||||||
|
(equalp (reindeer-pos (first (state-path s))) finish))
|
||||||
|
finally (return s)
|
||||||
|
do (loop
|
||||||
|
for next in (next-moves map (first (state-path s)))
|
||||||
|
for score = (+ (state-score s) (cdr next))
|
||||||
|
if (> (gethash (car next) best 1000000000) score)
|
||||||
|
do (progn
|
||||||
|
(qpush todo
|
||||||
|
(make-state
|
||||||
|
:path (cons (car next) (state-path s))
|
||||||
|
:score score))
|
||||||
|
(setf (gethash (car next) best) score)))
|
||||||
|
|
||||||
|
)))
|
||||||
|
|
||||||
|
(defun best-score (map)
|
||||||
|
(state-score (best-path map)))
|
||||||
|
|
||||||
(defparameter sample-data
|
(defparameter sample-data
|
||||||
(parse-input sample-text))
|
(parse-input sample-text))
|
||||||
|
|
||||||
(defun part1 (data)
|
(defun part1 (data)
|
||||||
(length data))
|
(format nil "~A" (best-score data)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun best-paths (map)
|
||||||
|
(let* ((start (find-tile map #\S))
|
||||||
|
(finish (find-tile map #\E))
|
||||||
|
(state-comparison (compare-states-f finish))
|
||||||
|
(best (make-hash-table :test #'equalp))
|
||||||
|
(best-score 100000000)
|
||||||
|
(pos-on-best-path (make-hash-table :test #'equalp))
|
||||||
|
(todo (make-queue :priority-queue :compare state-comparison)))
|
||||||
|
(qpush todo (make-state
|
||||||
|
:path (list (make-reindeer :pos start :dir #\>))
|
||||||
|
:score 0))
|
||||||
|
(loop
|
||||||
|
for s = (qpop todo)
|
||||||
|
until (null s)
|
||||||
|
for path = (state-path s)
|
||||||
|
for score = (state-score s)
|
||||||
|
finally (return (loop for k being the hash-keys in pos-on-best-path collect k))
|
||||||
|
if (<= score best-score)
|
||||||
|
do (if (equalp finish (reindeer-pos (first (state-path s))))
|
||||||
|
(progn
|
||||||
|
(if (< score best-score)
|
||||||
|
(progn
|
||||||
|
(setf best-score score)
|
||||||
|
(clrhash pos-on-best-path)))
|
||||||
|
(loop
|
||||||
|
for p in path
|
||||||
|
do (incf (gethash (reindeer-pos p) pos-on-best-path 0))))
|
||||||
|
(loop
|
||||||
|
for next in (next-moves map (first path))
|
||||||
|
for next-score = (+ score (or (cdr next) 0))
|
||||||
|
if (>= (gethash (car next) best 1000000000) next-score)
|
||||||
|
do (progn
|
||||||
|
(qpush todo
|
||||||
|
(make-state
|
||||||
|
:path (cons (car next) (state-path s))
|
||||||
|
:score next-score))
|
||||||
|
(setf (gethash (car next) best) next-score))))
|
||||||
|
|
||||||
|
)))
|
||||||
|
|
||||||
|
(defun show-paths (map paths)
|
||||||
|
(let ((nmap (copy-array map)))
|
||||||
|
(loop
|
||||||
|
for p in paths
|
||||||
|
do (set-pos nmap p #\O))
|
||||||
|
(show-map nmap)))
|
||||||
|
|
||||||
|
(defun solve-part2 (map)
|
||||||
|
(length (best-paths map)))
|
||||||
|
|
||||||
(defun part2 (data)
|
(defun part2 (data)
|
||||||
(length data))
|
(format nil "~A" (solve-part2 data) ))
|
||||||
|
|
||||||
(defun solve-day ()
|
(defun solve-day ()
|
||||||
(format t "part1: ~A~%" (part1 input-data))
|
(format t "part1: ~A~%" (part1 input-data))
|
||||||
|
|
|
@ -7,21 +7,41 @@
|
||||||
;:parent suite-2024
|
;:parent suite-2024
|
||||||
)
|
)
|
||||||
|
|
||||||
(define-test test-foo
|
(defparameter test-maze2 (parse-input (aoc:split-lines "#################
|
||||||
|
#...#...#...#..E#
|
||||||
|
#.#.#.#.#.#.#.#.#
|
||||||
|
#.#.#.#...#...#.#
|
||||||
|
#.#.#.#.###.#.#.#
|
||||||
|
#...#.#.#.....#.#
|
||||||
|
#.#.#.#.#.#####.#
|
||||||
|
#.#...#.#.#.....#
|
||||||
|
#.#.#####.#.###.#
|
||||||
|
#.#.#.......#...#
|
||||||
|
#.#.###.#####.###
|
||||||
|
#.#.#...#.....#.#
|
||||||
|
#.#.#.#####.###.#
|
||||||
|
#.#.#.........#.#
|
||||||
|
#.#.#.#########.#
|
||||||
|
#S#.............#
|
||||||
|
#################")))
|
||||||
|
|
||||||
|
|
||||||
|
(define-test test-solve-maze2
|
||||||
:parent suite-2024-16
|
:parent suite-2024-16
|
||||||
|
(is = 11048 (best-score test-maze2))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
(define-test test-bar
|
(define-test test-part2-maze2
|
||||||
:parent suite-2024-16
|
:parent suite-2024-16
|
||||||
)
|
(is = 64 (solVe-part2 test-maze2)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(define-test+run test-part1
|
(define-test+run test-part1
|
||||||
:parent suite-2024-16
|
:parent suite-2024-16
|
||||||
(is equal nil (part1 sample-data)))
|
(is equal "7036" (part1 sample-data)))
|
||||||
|
|
||||||
(define-test+run test-part2
|
(define-test+run test-part2
|
||||||
:parent suite-2024-16
|
:parent suite-2024-16
|
||||||
(is equal nil (part2 sample-data)))
|
(is equal "45" (part2 sample-data)))
|
||||||
|
|
Loading…
Reference in a new issue