day 16, well that was an ugly logical error

This commit is contained in:
efim 2022-12-17 15:32:11 +00:00
parent 293250c20b
commit 914abe5a1f
3 changed files with 307 additions and 46 deletions

View File

@ -0,0 +1,2 @@
Valve AA has flow rate=0; tunnels lead to valves BB
Valve BB has flow rate=1; tunnels lead to valves AA

51
day16-input.txt Normal file
View File

@ -0,0 +1,51 @@
Valve NV has flow rate=5; tunnels lead to valves ZV, CG, YB, HX, OY
Valve NU has flow rate=6; tunnels lead to valves DA, MA, OA, DK
Valve VU has flow rate=0; tunnels lead to valves PS, FX
Valve JW has flow rate=0; tunnels lead to valves AA, MD
Valve RI has flow rate=0; tunnels lead to valves OY, DG
Valve DG has flow rate=9; tunnels lead to valves TG, RI, DF, EV, KW
Valve PH has flow rate=7; tunnels lead to valves KW, OW, LT, LZ
Valve KZ has flow rate=12; tunnels lead to valves ET, QV, CK, MS
Valve IX has flow rate=0; tunnels lead to valves TS, DO
Valve MS has flow rate=0; tunnels lead to valves LZ, KZ
Valve IL has flow rate=0; tunnels lead to valves DO, ET
Valve EJ has flow rate=20; tunnels lead to valves AV, JY
Valve DK has flow rate=0; tunnels lead to valves NU, CG
Valve YB has flow rate=0; tunnels lead to valves NV, PS
Valve OA has flow rate=0; tunnels lead to valves YA, NU
Valve DA has flow rate=0; tunnels lead to valves NU, RG
Valve KO has flow rate=0; tunnels lead to valves AA, TG
Valve RG has flow rate=4; tunnels lead to valves DF, DA, ZV, MD, LB
Valve MA has flow rate=0; tunnels lead to valves AA, NU
Valve OW has flow rate=0; tunnels lead to valves DO, PH
Valve KW has flow rate=0; tunnels lead to valves DG, PH
Valve DO has flow rate=14; tunnels lead to valves IX, IL, CZ, OW
Valve DF has flow rate=0; tunnels lead to valves RG, DG
Valve TG has flow rate=0; tunnels lead to valves DG, KO
Valve LB has flow rate=0; tunnels lead to valves RG, FX
Valve HX has flow rate=0; tunnels lead to valves AA, NV
Valve GB has flow rate=0; tunnels lead to valves AV, XK
Valve CG has flow rate=0; tunnels lead to valves DK, NV
Valve LT has flow rate=0; tunnels lead to valves AO, PH
Valve FX has flow rate=23; tunnels lead to valves LB, HY, VU
Valve ET has flow rate=0; tunnels lead to valves IL, KZ
Valve CK has flow rate=0; tunnels lead to valves UX, KZ
Valve LZ has flow rate=0; tunnels lead to valves PH, MS
Valve YA has flow rate=17; tunnels lead to valves JY, OA
Valve TS has flow rate=0; tunnels lead to valves NO, IX
Valve NO has flow rate=8; tunnel leads to valve TS
Valve XK has flow rate=24; tunnel leads to valve GB
Valve PS has flow rate=18; tunnels lead to valves EV, VU, YB
Valve AA has flow rate=0; tunnels lead to valves JW, HX, MA, KO
Valve MD has flow rate=0; tunnels lead to valves JW, RG
Valve JM has flow rate=19; tunnels lead to valves QV, HY, AO
Valve AV has flow rate=0; tunnels lead to valves EJ, GB
Valve AO has flow rate=0; tunnels lead to valves JM, LT
Valve JY has flow rate=0; tunnels lead to valves YA, EJ
Valve OY has flow rate=0; tunnels lead to valves NV, RI
Valve UX has flow rate=13; tunnels lead to valves CZ, CK
Valve HY has flow rate=0; tunnels lead to valves JM, FX
Valve EV has flow rate=0; tunnels lead to valves PS, DG
Valve CZ has flow rate=0; tunnels lead to valves UX, DO
Valve ZV has flow rate=0; tunnels lead to valves NV, RG
Valve QV has flow rate=0; tunnels lead to valves JM, KZ

View File

@ -183,7 +183,7 @@
(defclass verticle-data () (defclass verticle-data ()
((flow :reader flow :initarg :flow) ((flow :reader flow :initarg :flow)
(name :reader name :initarg :name) (name :reader name :initarg :name)
(is-opened-p :accessor is-opened-p :initform t))) (is-opened-p :accessor is-opened-p :initform nil)))
(defmethod print-object ((obj verticle-data) stream) (defmethod print-object ((obj verticle-data) stream)
(with-slots (name flow is-opened-p) obj (with-slots (name flow is-opened-p) obj
@ -325,7 +325,7 @@
;; do (format t "from ~a to ~a dist: ~a. ~a~%" from-node to-node dist to-node-data) ;; do (format t "from ~a to ~a dist: ~a. ~a~%" from-node to-node dist to-node-data)
when (and (equal cur-node from-node) when (and (equal cur-node from-node)
(not (= 0 (flow to-node-data))) (not (= 0 (flow to-node-data)))
(is-opened-p to-node-data)) (not (is-opened-p to-node-data)))
collect (list to-node dist) collect (list to-node dist)
)) ))
@ -346,7 +346,7 @@
(not (equal cur-node to-node)) (not (equal cur-node to-node))
(not (= 0 (flow to-node-data))) (not (= 0 (flow to-node-data)))
(> time-remaining dist) (> time-remaining dist)
(is-opened-p to-node-data)) (not (is-opened-p to-node-data)))
collect (list to-node dist))) collect (list to-node dist)))
(get-possible-next-vs 'aa *test-graph* *test-vertices-map* *test-shortest-paths* 30) (get-possible-next-vs 'aa *test-graph* *test-vertices-map* *test-shortest-paths* 30)
@ -364,7 +364,7 @@
)) ))
(for-open-current-vertices-map (let ((table (alexandria:copy-hash-table vertices-data-map))) (for-open-current-vertices-map (let ((table (alexandria:copy-hash-table vertices-data-map)))
(maphash (lambda (k v) (setf (gethash k table) (copy-instance v))) table) (maphash (lambda (k v) (setf (gethash k table) (copy-instance v))) table)
(setf (is-opened-p (gethash cur-node table)) nil ) (setf (is-opened-p (gethash cur-node table)) t )
table))) table)))
(if (not possible-next-nodes) (if (not possible-next-nodes)
;; ending condition, either all opened or no more time ;; ending condition, either all opened or no more time
@ -373,7 +373,7 @@
(progn (progn
;; (format t "in ~a. left steps ~a~%" cur-node time-left) ;; (format t "in ~a. left steps ~a~%" cur-node time-left)
(loop for (next-node dist) in possible-next-nodes (loop for (next-node dist) in possible-next-nodes
for max-with-open-current = (+ cur-node-gas-per-turn for max-with-open-current = (+ for-open-current-total-release
(recursive-max-vented (recursive-max-vented
next-node next-node
(- (1- time-left) dist) ; spent 1 turn opening current (- (1- time-left) dist) ; spent 1 turn opening current
@ -434,10 +434,10 @@
;; (sum2 (loop for y from 1 to 1000000000 sum y))) ;; (sum2 (loop for y from 1 to 1000000000 sum y)))
;; (+ sum1 sum2))) ;; (+ sum1 sum2)))
(timing ;; (timing
(lparallel:plet ((sum1 (loop for x from 1 to 100000000 sum x)) ;; (lparallel:plet ((sum1 (loop for x from 1 to 100000000 sum x))
(sum2 (loop for y from 1 to 100000000 sum y))) ;; (sum2 (loop for y from 1 to 100000000 sum y)))
(+ sum1 sum2))) ;; (+ sum1 sum2)))
;; (lparallel:pmap) ;; (lparallel:pmap)
;; the problem is in function that returns for each node maximum of 2 recursive calls ;; the problem is in function that returns for each node maximum of 2 recursive calls
@ -454,7 +454,7 @@
)) ))
(for-open-current-vertices-map (let ((table (alexandria:copy-hash-table vertices-data-map))) (for-open-current-vertices-map (let ((table (alexandria:copy-hash-table vertices-data-map)))
(maphash (lambda (k v) (setf (gethash k table) (copy-instance v))) table) (maphash (lambda (k v) (setf (gethash k table) (copy-instance v))) table)
(setf (is-opened-p (gethash cur-node table)) nil ) (setf (is-opened-p (gethash cur-node table)) t )
table))) table)))
(if (not possible-next-nodes) (if (not possible-next-nodes)
;; ending condition, either all opened or no more time ;; ending condition, either all opened or no more time
@ -494,6 +494,9 @@
;; ok, now let's also start all next nodes separately? ;; ok, now let's also start all next nodes separately?
(defun precursive-max-vented (cur-node time-left graph vertices-data-map shortest-paths) (defun precursive-max-vented (cur-node time-left graph vertices-data-map shortest-paths)
(progn
(format t "starting iteration for ~a, left ~a" cur-node time-left)
(print-hashmap vertices-data-map)
(let* ((possible-next-nodes (get-possible-next-vs cur-node graph vertices-data-map shortest-paths time-left)) (let* ((possible-next-nodes (get-possible-next-vs cur-node graph vertices-data-map shortest-paths time-left))
(cur-node-gas-per-turn (flow (gethash cur-node vertices-data-map))) (cur-node-gas-per-turn (flow (gethash cur-node vertices-data-map)))
(for-open-current-total-release (max 0 ; in case we get to -1 time remaining (for-open-current-total-release (max 0 ; in case we get to -1 time remaining
@ -502,7 +505,7 @@
)) ))
(for-open-current-vertices-map (let ((table (alexandria:copy-hash-table vertices-data-map))) (for-open-current-vertices-map (let ((table (alexandria:copy-hash-table vertices-data-map)))
(maphash (lambda (k v) (setf (gethash k table) (copy-instance v))) table) (maphash (lambda (k v) (setf (gethash k table) (copy-instance v))) table)
(setf (is-opened-p (gethash cur-node table)) nil ) (setf (is-opened-p (gethash cur-node table)) t )
table))) table)))
(if (not possible-next-nodes) (if (not possible-next-nodes)
;; ending condition, either all opened or no more time ;; ending condition, either all opened or no more time
@ -510,7 +513,7 @@
;; else - there are some possible nodes to visit ;; else - there are some possible nodes to visit
(flet ((recursive-max-for-next (next-node dist) (flet ((recursive-max-for-next (next-node dist)
(let ((max-with-open-current (let ((max-with-open-current
(+ cur-node-gas-per-turn (+ for-open-current-total-release
(precursive-max-vented (precursive-max-vented
next-node next-node
(- (1- time-left) dist) ; spent 1 turn opening current (- (1- time-left) dist) ; spent 1 turn opening current
@ -527,12 +530,29 @@
(max max-not-opening-current max-with-open-current)))) (max max-not-opening-current max-with-open-current))))
(apply #'max (lparallel:pmapcar (lambda (next-and-dist-list) (apply #'max (lparallel:pmapcar (lambda (next-and-dist-list)
(recursive-max-for-next (first next-and-dist-list) (second next-and-dist-list))) (recursive-max-for-next (first next-and-dist-list) (second next-and-dist-list)))
possible-next-nodes)))))) possible-next-nodes)))))))
(timing ;; HERE INIT
(print (recursive-max-vented 'aa 14 *test-graph* *test-vertices-map* *test-shortest-paths*))) (defparameter *test-graph* (graph-utils:make-graph))
(defparameter *test-vertices-map* (make-hash-table))
;; (read-file-data "day16-test.txt" *test-graph* *test-vertices-map*)
;; (read-file-data "day16-simpler-test.txt" *test-graph* *test-vertices-map*)
(read-file-data "day16-even-simpler-test.txt" *test-graph* *test-vertices-map*)
(defparameter *test-shortest-paths* (graph-utils:all-pairs-shortest-paths *test-graph*))
(print-hashmap *test-shortest-paths*)
;; (timing
;; (print (recursive-max-vented 'aa 1 *test-graph* *test-vertices-map* *test-shortest-paths*)))
;; (timing ;; (timing
;; (print (precursive-max-vented 'aa 14 *test-graph* *test-vertices-map* *test-shortest-paths*))) ;; (print (precursive-max-vented 'aa 14 *test-graph* *test-vertices-map* *test-shortest-paths*)))
603 ;;; Computation took:
;;; 21.654093 seconds of real time
;;; 21.583296 seconds of run time
603 ;;; Computation took:
;;; 41.297173 seconds of real time
;;; 294.63492 seconds of run time
;; old bugged, where only added 1 minute of opened valve
111 ;;; Computation took: 111 ;;; Computation took:
;;; 5.249021 seconds of real time ;;; 5.249021 seconds of real time
;;; 5.233272 seconds of run time ;;; 5.233272 seconds of run time
@ -545,3 +565,191 @@
;; well. there are discrepancies with answers. ;; well. there are discrepancies with answers.
;; so, let's make an even simpler case? ;; so, let's make an even simpler case?
;;; let's batch more. only initial decision into parallel?
(defun initial-coice-paralleled-max-vented (cur-node time-left graph vertices-data-map shortest-paths initial-time)
(let* ((possible-next-nodes (get-possible-next-vs cur-node graph vertices-data-map shortest-paths time-left))
(cur-node-gas-per-turn (flow (gethash cur-node vertices-data-map)))
(for-open-current-total-release (max 0 ; in case we get to -1 time remaining
(* cur-node-gas-per-turn
(1- time-left)) ; freed gas after opening and staying
))
(for-open-current-vertices-map (let ((table (alexandria:copy-hash-table vertices-data-map)))
(maphash (lambda (k v) (setf (gethash k table) (copy-instance v))) table)
(setf (is-opened-p (gethash cur-node table)) t )
table)))
(if (not possible-next-nodes)
;; ending condition, either all opened or no more time
for-open-current-total-release
;; else - there are some possible nodes to visit
(flet ((recursive-max-for-next (next-node dist)
(let* ((recursive-function (if (< (- initial-time time-left ) 2)
#'precursive-max-vented
#'recursive-max-vented))
(max-with-open-current
(+ for-open-current-total-release
(funcall recursive-function
next-node
(- (1- time-left) dist) ; spent 1 turn opening current
graph
for-open-current-vertices-map
shortest-paths)))
(max-not-opening-current
(funcall recursive-function
next-node
(- time-left dist) ; going there directly
graph
vertices-data-map
shortest-paths)))
(max max-not-opening-current max-with-open-current))))
(apply #'max (lparallel:pmapcar (lambda (next-and-dist-list)
(recursive-max-for-next (first next-and-dist-list) (second next-and-dist-list)))
possible-next-nodes))))))
;; (timing
;; (print (recursive-max-vented 'aa 14 *test-graph* *test-vertices-map* *test-shortest-paths*)))
;; (timing
;; (print (precursive-max-vented 'aa 14 *test-graph* *test-vertices-map* *test-shortest-paths*)))
;; (timing
;; (print (initial-coice-paralleled-max-vented 'aa 14 *test-graph* *test-vertices-map* *test-shortest-paths* 14)))
603 ;;; Computation took:
;;; 21.654093 seconds of real time
;;; 21.583296 seconds of run time
603 ;;; Computation took:
;;; 41.297173 seconds of real time
;;; 294.63492 seconds of run time
603 ;;; Computation took:
;;; 42.40918 seconds of real time
;;; 293.1057 seconds of run time
;; so it's for some reason still not ok?
;; oh, that's because initial choice is very limited.
;; only into 3 paths
;; (timing
;; (print (initial-coice-paralleled-max-vented 'aa 30 *test-graph* *test-vertices-map* *test-shortest-paths* 30)))
;;; OK, so my problem is a BUG
;; let's focus on single threaded solution
;; HERE INIT
(defparameter *test-graph* (graph-utils:make-graph))
(defparameter *test-vertices-map* (make-hash-table))
;; (read-file-data "day16-test.txt" *test-graph* *test-vertices-map*)
(read-file-data "day16-simpler-test.txt" *test-graph* *test-vertices-map*)
;; (read-file-data "day16-even-simpler-test.txt" *test-graph* *test-vertices-map*)
(defparameter *test-shortest-paths* (graph-utils:all-pairs-shortest-paths *test-graph*))
(print-hashmap *test-shortest-paths*)
;; (recursive-max-vented 'aa 7 *test-graph* *test-vertices-map* *test-shortest-paths*)
(defun recursive-max-vented (cur-node time-left graph vertices-data-map shortest-paths)
(progn
(format t "starting iteration for ~a, left ~a~%" cur-node time-left)
(print-hashmap vertices-data-map)
(let* ((possible-next-nodes (get-possible-next-vs cur-node graph vertices-data-map shortest-paths time-left))
(cur-node-gas-per-turn (flow (gethash cur-node vertices-data-map)))
(for-open-current-total-release (max 0 ; in case we get to -1 time remaining
(* cur-node-gas-per-turn
(1- time-left)) ; freed gas after opening and staying
))
(for-open-current-vertices-map (let ((table (alexandria:copy-hash-table vertices-data-map)))
(maphash (lambda (k v) (setf (gethash k table) (copy-instance v))) table)
(setf (is-opened-p (gethash cur-node table)) t )
table)))
(if (not possible-next-nodes)
;; ending condition, either all opened or no more time
for-open-current-total-release
;; else - there are some possible nodes to visit
(progn
;; (format t "in ~a. left steps ~a~%" cur-node time-left)
(loop for (next-node dist) in possible-next-nodes
for max-with-open-current = (+ for-open-current-total-release
(recursive-max-vented
next-node
(- (1- time-left) dist) ; spent 1 turn opening current
graph
for-open-current-vertices-map
shortest-paths))
for max-not-opening-current = (recursive-max-vented
next-node
(- time-left dist) ; going there directly
graph
vertices-data-map
shortest-paths)
maximize max-with-open-current into max-freed
maximize max-not-opening-current into max-freed
finally (return max-freed)))))))
;; well. here's my problem - i'm not marking Nodes I've already visited.
;; since I'm already working with a closure, i only need to visit each "actual" node once. and it makes no sense skipping them.
(defun visit-each-once-recursive-max-vented (cur-node time-left graph vertices-data-map shortest-paths)
;; (format t "wtf")
(let* ((possible-next-nodes (get-possible-next-vs cur-node graph vertices-data-map shortest-paths time-left))
(cur-node-gas-per-turn (flow (gethash cur-node vertices-data-map)))
(for-open-current-total-release (max 0 ; in case we get to -1 time remaining
(* cur-node-gas-per-turn
(1- time-left)) ; freed gas after opening and staying
))
(for-open-current-vertices-map (let ((table (alexandria:copy-hash-table vertices-data-map)))
(maphash (lambda (k v) (setf (gethash k table) (copy-instance v))) table)
(setf (is-opened-p (gethash cur-node table)) t )
table)))
;; (format t ">> starting iteration for ~a, left ~a~%; cur-gas ~a~%" cur-node time-left cur-node-gas-per-turn)
;; (print-hashmap vertices-data-map)
(if (not possible-next-nodes)
;; ending condition, either all opened or no more time
for-open-current-total-release
;; else - there are some possible nodes to visit
(progn
;; (format t "in ~a. left steps ~a~%" cur-node time-left)
(loop for (next-node dist) in possible-next-nodes
for next-max = (if (= 0 cur-node-gas-per-turn)
;; if we for some reason (for example on start) get to node with 0 flow
(visit-each-once-recursive-max-vented
next-node
(- time-left dist)
; going there directly
graph
vertices-data-map
shortest-paths)
(+ for-open-current-total-release
(visit-each-once-recursive-max-vented
next-node
(- (1- time-left)
dist)
; spent 1 turn opening current
graph
for-open-current-vertices-map
shortest-paths)))
maximize next-max into max-freed
finally (return max-freed))))))
;; HERE INIT
(defparameter *test-graph* (graph-utils:make-graph))
(defparameter *test-vertices-map* (make-hash-table))
(read-file-data "day16-input.txt" *test-graph* *test-vertices-map*)
;; (read-file-data "day16-test.txt" *test-graph* *test-vertices-map*)
;; (read-file-data "day16-simpler-test.txt" *test-graph* *test-vertices-map*)
;; (read-file-data "day16-even-simpler-test.txt" *test-graph* *test-vertices-map*)
(defparameter *test-shortest-paths* (graph-utils:all-pairs-shortest-paths *test-graph*))
(print-hashmap *test-shortest-paths*)
(get-possible-next-vs 'ee *test-graph* *test-vertices-map* *test-shortest-paths* 30)
(visit-each-once-recursive-max-vented 'aa 30 *test-graph* *test-vertices-map* *test-shortest-paths*)
;; well 1570 is not correct 1651
;; what am i missing?
;; i am opening starting Node with 0 flow
;; it seems that this call should be impossible:
;; starting iteration for CC, left 0
;; AA : #<VERTICLE-DATA AA with flow: 0; is opened NIL {1004C57D33}>
;; BB : #<VERTICLE-DATA BB with flow: 1; is opened T {1004C57DB3}>
;; CC : #<VERTICLE-DATA CC with flow: 1; is opened NIL {1004C57E33}>
;; DD : #<VERTICLE-DATA DD with flow: 1; is opened NIL {1004C57EB3}>
;; EE : #<VERTICLE-DATA EE with flow: 1; is opened T {1004C57F33}>