From 914abe5a1fdd1c8e6d0d8b2957a4850201f16aa6 Mon Sep 17 00:00:00 2001 From: efim Date: Sat, 17 Dec 2022 15:32:11 +0000 Subject: [PATCH] day 16, well that was an ugly logical error --- day16-even-simpler-test.txt | 2 + day16-input.txt | 51 ++++++ day16-scratch-graph-utils.lisp | 300 ++++++++++++++++++++++++++++----- 3 files changed, 307 insertions(+), 46 deletions(-) create mode 100644 day16-even-simpler-test.txt create mode 100644 day16-input.txt diff --git a/day16-even-simpler-test.txt b/day16-even-simpler-test.txt new file mode 100644 index 0000000..c66be84 --- /dev/null +++ b/day16-even-simpler-test.txt @@ -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 diff --git a/day16-input.txt b/day16-input.txt new file mode 100644 index 0000000..dc9b065 --- /dev/null +++ b/day16-input.txt @@ -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 diff --git a/day16-scratch-graph-utils.lisp b/day16-scratch-graph-utils.lisp index d579d8f..5aa1707 100644 --- a/day16-scratch-graph-utils.lisp +++ b/day16-scratch-graph-utils.lisp @@ -183,7 +183,7 @@ (defclass verticle-data () ((flow :reader flow :initarg :flow) (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) (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) when (and (equal cur-node from-node) (not (= 0 (flow to-node-data))) - (is-opened-p to-node-data)) + (not (is-opened-p to-node-data))) collect (list to-node dist) )) @@ -346,7 +346,7 @@ (not (equal cur-node to-node)) (not (= 0 (flow to-node-data))) (> time-remaining dist) - (is-opened-p to-node-data)) + (not (is-opened-p to-node-data))) collect (list to-node dist))) (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))) (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))) (if (not possible-next-nodes) ;; ending condition, either all opened or no more time @@ -373,7 +373,7 @@ (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 = (+ cur-node-gas-per-turn + for max-with-open-current = (+ for-open-current-total-release (recursive-max-vented next-node (- (1- time-left) dist) ; spent 1 turn opening current @@ -434,10 +434,10 @@ ;; (sum2 (loop for y from 1 to 1000000000 sum y))) ;; (+ sum1 sum2))) -(timing - (lparallel:plet ((sum1 (loop for x from 1 to 100000000 sum x)) - (sum2 (loop for y from 1 to 100000000 sum y))) - (+ sum1 sum2))) +;; (timing +;; (lparallel:plet ((sum1 (loop for x from 1 to 100000000 sum x)) +;; (sum2 (loop for y from 1 to 100000000 sum y))) +;; (+ sum1 sum2))) ;; (lparallel:pmap) ;; 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))) (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))) (if (not possible-next-nodes) ;; ending condition, either all opened or no more time @@ -494,45 +494,65 @@ ;; ok, now let's also start all next nodes separately? (defun precursive-max-vented (cur-node time-left graph vertices-data-map shortest-paths) - (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)) nil ) - 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 ((max-with-open-current - (+ cur-node-gas-per-turn - (precursive-max-vented - next-node - (- (1- time-left) dist) ; spent 1 turn opening current - graph - for-open-current-vertices-map - shortest-paths))) - (max-not-opening-current - (precursive-max-vented - 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)))))) + (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 + (flet ((recursive-max-for-next (next-node dist) + (let ((max-with-open-current + (+ for-open-current-total-release + (precursive-max-vented + next-node + (- (1- time-left) dist) ; spent 1 turn opening current + graph + for-open-current-vertices-map + shortest-paths))) + (max-not-opening-current + (precursive-max-vented + 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*))) +;; 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*) + +;; (timing +;; (print (recursive-max-vented 'aa 1 *test-graph* *test-vertices-map* *test-shortest-paths*))) ;; (timing ;; (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: ;;; 5.249021 seconds of real time ;;; 5.233272 seconds of run time @@ -545,3 +565,191 @@ ;; well. there are discrepancies with answers. ;; 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 : # +;; BB : # +;; CC : # +;; DD : # +;; EE : #