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

@@ -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 : #<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}>