day 16 monstrocity calculated overnight
This commit is contained in:
parent
914abe5a1f
commit
3126cd48ab
|
@ -741,7 +741,8 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(visit-each-once-recursive-max-vented 'aa 30 *test-graph* *test-vertices-map* *test-shortest-paths*)
|
;; Calculating PART 1
|
||||||
|
;; (visit-each-once-recursive-max-vented 'aa 30 *test-graph* *test-vertices-map* *test-shortest-paths*)
|
||||||
;; well 1570 is not correct 1651
|
;; well 1570 is not correct 1651
|
||||||
;; what am i missing?
|
;; what am i missing?
|
||||||
;; i am opening starting Node with 0 flow
|
;; i am opening starting Node with 0 flow
|
||||||
|
@ -753,3 +754,277 @@
|
||||||
;; CC : #<VERTICLE-DATA CC with flow: 1; is opened NIL {1004C57E33}>
|
;; CC : #<VERTICLE-DATA CC with flow: 1; is opened NIL {1004C57E33}>
|
||||||
;; DD : #<VERTICLE-DATA DD with flow: 1; is opened NIL {1004C57EB3}>
|
;; DD : #<VERTICLE-DATA DD with flow: 1; is opened NIL {1004C57EB3}>
|
||||||
;; EE : #<VERTICLE-DATA EE with flow: 1; is opened T {1004C57F33}>
|
;; EE : #<VERTICLE-DATA EE with flow: 1; is opened T {1004C57F33}>
|
||||||
|
|
||||||
|
;;; PART 2
|
||||||
|
;; need to compare 2 cases:
|
||||||
|
;; me running solo for 30 minutes
|
||||||
|
;; or two persons - me and elephant running for 26 minutes
|
||||||
|
|
||||||
|
;; oh, shit. now i'd need coordination between recusive calls? for them to share the actual state.
|
||||||
|
;; or what? do i just on each step do twice decisions and not share state
|
||||||
|
|
||||||
|
;; just a separate function, for 2 actors, would call both and compare
|
||||||
|
;; but it takes 1 turn to open valve. is that a problem?
|
||||||
|
;; it can be! because no guarantee that both start opening at same moment.
|
||||||
|
;; lots of changes then. need to iterate one step at a time
|
||||||
|
;; so person can be "in state, i just came in, turning the gas", i'm tired. let's go outside
|
||||||
|
;; so it's still better to be recursive, since calling for all possibilities.
|
||||||
|
;; but need to do iteration one tick at a time. setting state for p1 and p2 (for the next iteration)
|
||||||
|
;; and only adding value of cur-node-1|2 when it actually becoming turned on
|
||||||
|
;; and additional filtering out of other player's node
|
||||||
|
;;
|
||||||
|
;;
|
||||||
|
;; so, also "moving to other room" is not instantaneous
|
||||||
|
;; let's do:
|
||||||
|
;; cur-node-p1
|
||||||
|
;; p1-state
|
||||||
|
;; cur-node-p2
|
||||||
|
;; p2-state
|
||||||
|
;;
|
||||||
|
;; p.-state is current state or next state and should be excluded
|
||||||
|
;;
|
||||||
|
;; exit condition - if no more next nodes AND both players done opening
|
||||||
|
;; so let's do also DONE state, which is set to player when there's no more next nodes
|
||||||
|
;; and exit condition - both in DONE
|
||||||
|
;;
|
||||||
|
;; check that before DONE we added last room of the player to total
|
||||||
|
;;
|
||||||
|
;; I want to see if I could use lists and sybols as states:
|
||||||
|
;; OPENING '(GOING n) DONE where n should be readable number of how many turns of going left.
|
||||||
|
;; on (GOING 0) set next OPENING
|
||||||
|
;; on OPENING - since it's 1 turn - we should add target node to tally and set new GOING or DONE
|
||||||
|
;;
|
||||||
|
;; let's see if there's such thing as DESTRUCTURING CASE
|
||||||
|
(let
|
||||||
|
;; ((x 'opening))
|
||||||
|
;; ((x '(going 0)))
|
||||||
|
((x '(going 5)))
|
||||||
|
(cond
|
||||||
|
((eq x 1) "one")
|
||||||
|
((eq 'opening x) "opening")
|
||||||
|
((equal x '(going 0))
|
||||||
|
(format nil "were here! ~a~%" (second x)))
|
||||||
|
((and (listp x)
|
||||||
|
(eq (first x) 'going))
|
||||||
|
(format nil "going, ~a turns left" (second x)))
|
||||||
|
(t "default")))
|
||||||
|
|
||||||
|
(let ((x '(going 5)))
|
||||||
|
(case x
|
||||||
|
(1 "one")
|
||||||
|
('opening "opening")
|
||||||
|
((list going b) (format nil "going, ~a turns left~%" (second x)))
|
||||||
|
((list going 0) (format nil "were here! ~a~%" (second x)))
|
||||||
|
(t "default")))
|
||||||
|
|
||||||
|
;; now what would I want to do baced on the state of the player?
|
||||||
|
;; set next state and do what? modify the state passing further
|
||||||
|
;; that's possible i think
|
||||||
|
;; ugh. ugh.
|
||||||
|
;; so, what - generate all next states for the player
|
||||||
|
;; and in addition i guess mutate the vertices-data-map for next call? ugh.
|
||||||
|
;;
|
||||||
|
;; if one player finished OPENING, then we for each of their possible next move create separate v-data-map
|
||||||
|
;;
|
||||||
|
;; what if we operate players sequentially, doing 2 player move ticks per 1 time tick.
|
||||||
|
;; that might be nice. then only need to update 1 player state.
|
||||||
|
;; could have "steping player state & node" as first two arguments, always switching them.
|
||||||
|
;; it would keep lots of logic same, as one player update.
|
||||||
|
;;
|
||||||
|
;; exit when both are done. that could be nice, yeah.
|
||||||
|
;; and if active player is done - just pass the turn to another.
|
||||||
|
;; when do I increment time? store "turn" increment it by 1 every recursion, and / 2 to deduct from total time
|
||||||
|
;; i guess it's ok
|
||||||
|
|
||||||
|
;; removing other player node from possible nodes
|
||||||
|
(remove 'bb '((aa 5) (bb 7) (cc 1)) :test (lambda (removing-name name-and-dist) (eq removing-name (first name-and-dist))))
|
||||||
|
(remove 'cc '((aa 5) (bb 7) (cc 1)) :test (lambda (removing-name name-and-dist) (eq removing-name (first name-and-dist))))
|
||||||
|
(remove 'ee '((aa 5) (bb 7) (cc 1)) :test (lambda (removing-name name-and-dist) (eq removing-name (first name-and-dist))))
|
||||||
|
|
||||||
|
(defparameter *max-so-far* 0)
|
||||||
|
|
||||||
|
(defun 2-persons-visit-each-once-recursive-max-vented (active-pl-node active-pl-state
|
||||||
|
inactive-pl-node inactive-pl-state
|
||||||
|
current-turn total-allotted-time
|
||||||
|
freed-to-end-gas-accum
|
||||||
|
graph vertices-data-map shortest-paths)
|
||||||
|
(let* ((time-left (- total-allotted-time (floor (/ current-turn 2))))
|
||||||
|
(not-opened-possible-nodes (get-possible-next-vs active-pl-node graph vertices-data-map shortest-paths time-left))
|
||||||
|
(possible-next-nodes (remove inactive-pl-node not-opened-possible-nodes ; remove other player target node from available
|
||||||
|
:test (lambda (removing-name name-and-dist)
|
||||||
|
(eq removing-name (first name-and-dist)))))
|
||||||
|
(cur-node-gas-per-turn (flow (gethash active-pl-node vertices-data-map)))
|
||||||
|
(for-open-current-total-release (max 0 ; in case we get to -1 time remaining
|
||||||
|
(* cur-node-gas-per-turn time-left)
|
||||||
|
; freed gas after opening and staying
|
||||||
|
))
|
||||||
|
|
||||||
|
(next-turn (1+ current-turn)))
|
||||||
|
;; (format t "Turn:~%
|
||||||
|
;; active pl: state ~a, node ~a
|
||||||
|
;; inactive pl: state ~a, node ~a
|
||||||
|
;; accum: ~a
|
||||||
|
;; possible next: ~a
|
||||||
|
;; current turn: ~a; time remaining ~a~%"
|
||||||
|
;; active-pl-state active-pl-node
|
||||||
|
;; inactive-pl-state inactive-pl-node
|
||||||
|
;; freed-to-end-gas-accum
|
||||||
|
;; possible-next-nodes
|
||||||
|
;; current-turn time-left)
|
||||||
|
(when (> freed-to-end-gas-accum *max-so-far*)
|
||||||
|
(setq *max-so-far* freed-to-end-gas-accum)
|
||||||
|
(format t "updating max so far to: ~a~%" *max-so-far*)
|
||||||
|
)
|
||||||
|
(cond
|
||||||
|
((and (eq active-pl-state 'DONE)
|
||||||
|
(eq inactive-pl-state 'DONE))
|
||||||
|
;; (print "Total DONE processing")
|
||||||
|
;; recursion EXIT condition - both are done,
|
||||||
|
;; before becoming DONE and yielding turn previous player
|
||||||
|
;; counted how much steam is added "until the end of allotted-time" and added to accum
|
||||||
|
freed-to-end-gas-accum)
|
||||||
|
((eq active-pl-state 'DONE)
|
||||||
|
;; (print "One player DONE")
|
||||||
|
;; active player is DONE but not another, tick the turn and yield the turn
|
||||||
|
(2-persons-visit-each-once-recursive-max-vented
|
||||||
|
inactive-pl-node inactive-pl-state
|
||||||
|
active-pl-node 'DONE ; keep staying DONE
|
||||||
|
next-turn total-allotted-time
|
||||||
|
freed-to-end-gas-accum
|
||||||
|
graph vertices-data-map shortest-paths))
|
||||||
|
((and (listp active-pl-state)
|
||||||
|
(equal active-pl-state '(going 1))) ; am i here off by one?
|
||||||
|
;; (print "Processing ARRIVAL")
|
||||||
|
;; active player came to target state, set to OPENING
|
||||||
|
(2-persons-visit-each-once-recursive-max-vented
|
||||||
|
inactive-pl-node inactive-pl-state
|
||||||
|
active-pl-node 'OPENING ; for one turn
|
||||||
|
next-turn total-allotted-time
|
||||||
|
freed-to-end-gas-accum
|
||||||
|
graph vertices-data-map shortest-paths))
|
||||||
|
((and (listp active-pl-state)
|
||||||
|
(listp inactive-pl-state)
|
||||||
|
(equal (first active-pl-state) 'going)
|
||||||
|
(equal (first inactive-pl-state) 'going)
|
||||||
|
(not (= 1 (second inactive-pl-state))))
|
||||||
|
;; both are GOING, can find amount to decrement, decrement both and add 2*n to turn
|
||||||
|
(let ((common-skip (1- (min (second active-pl-state) (second inactive-pl-state)))))
|
||||||
|
; for 5 and 3; min is 3, common diminish 2 - to get one of them to 1
|
||||||
|
|
||||||
|
(2-persons-visit-each-once-recursive-max-vented
|
||||||
|
active-pl-node `(going ,(- (second active-pl-state) common-skip))
|
||||||
|
inactive-pl-node `(going ,(- (second inactive-pl-state) common-skip))
|
||||||
|
(+ current-turn (* 2 common-skip)) total-allotted-time
|
||||||
|
freed-to-end-gas-accum
|
||||||
|
graph vertices-data-map shortest-paths)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
((eq active-pl-state 'OPENING)
|
||||||
|
;; (print "in OPENING processing")
|
||||||
|
;; active player is OPENING, so it's already done. add current steam to final tally accum
|
||||||
|
;; then select next state OR set to done
|
||||||
|
(let ((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 active-pl-node table))
|
||||||
|
t )
|
||||||
|
;; well, this is still done on every turn, lazy would be nice
|
||||||
|
table)))
|
||||||
|
(if (not possible-next-nodes)
|
||||||
|
;; set state to DONE
|
||||||
|
(2-persons-visit-each-once-recursive-max-vented
|
||||||
|
inactive-pl-node inactive-pl-state
|
||||||
|
active-pl-node 'DONE ; will exit recursion when both DONE
|
||||||
|
next-turn total-allotted-time
|
||||||
|
(+ freed-to-end-gas-accum for-open-current-total-release)
|
||||||
|
; updated accum
|
||||||
|
graph
|
||||||
|
for-open-current-vertices-map ; updated State map
|
||||||
|
shortest-paths)
|
||||||
|
;; call recursively for all possible states with the added accumulator
|
||||||
|
;; else - there are some possible nodes to visit
|
||||||
|
;; main loop - check if OPENING, GOING or what
|
||||||
|
(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
|
||||||
|
(2-persons-visit-each-once-recursive-max-vented
|
||||||
|
inactive-pl-node inactive-pl-state
|
||||||
|
next-node `(going ,dist)
|
||||||
|
; starting to GO to selected NODe
|
||||||
|
next-turn total-allotted-time
|
||||||
|
freed-to-end-gas-accum ; same accum
|
||||||
|
graph
|
||||||
|
vertices-data-map ; for 0 flow - NOT updated State map
|
||||||
|
shortest-paths)
|
||||||
|
;; if were in NONZERO FLOW node - add to accum
|
||||||
|
(2-persons-visit-each-once-recursive-max-vented
|
||||||
|
inactive-pl-node inactive-pl-state
|
||||||
|
next-node `(going ,dist)
|
||||||
|
; starting to GO to selected NODe
|
||||||
|
next-turn total-allotted-time
|
||||||
|
(+ freed-to-end-gas-accum for-open-current-total-release)
|
||||||
|
; updated accum
|
||||||
|
graph
|
||||||
|
for-open-current-vertices-map ; updated State map
|
||||||
|
shortest-paths)
|
||||||
|
)
|
||||||
|
maximize next-max into max-freed
|
||||||
|
finally (return max-freed)))))
|
||||||
|
((and (listp active-pl-state)
|
||||||
|
(eq (first active-pl-state)
|
||||||
|
'going))
|
||||||
|
;; (print "Processing GOING")
|
||||||
|
;; active player is still going
|
||||||
|
;; switch active player and increment turn
|
||||||
|
(2-persons-visit-each-once-recursive-max-vented
|
||||||
|
inactive-pl-node inactive-pl-state
|
||||||
|
active-pl-node `(going ,(1- (second active-pl-state)))
|
||||||
|
; one step less
|
||||||
|
next-turn total-allotted-time
|
||||||
|
freed-to-end-gas-accum
|
||||||
|
graph vertices-data-map shortest-paths)
|
||||||
|
|
||||||
|
)
|
||||||
|
(t "SHOULD NOT HAPPEN")
|
||||||
|
)))
|
||||||
|
|
||||||
|
;; to start up - set turn 0, allotted time 24. both players as OPENING 'aa starting turn
|
||||||
|
;; 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 'aa *test-graph* *test-vertices-map* *test-shortest-paths* 30)
|
||||||
|
|
||||||
|
|
||||||
|
;; FINAL CALCULATION
|
||||||
|
;; (2-persons-visit-each-once-recursive-max-vented 'aa 'opening 'aa 'opening
|
||||||
|
;; 0 26 0
|
||||||
|
;; *test-graph* *test-vertices-map* *test-shortest-paths*)
|
||||||
|
;; 1482 is less than 1707
|
||||||
|
;; not 1500 is less than 1707
|
||||||
|
;; now 1581 is still less than 1707
|
||||||
|
;; yep, off-by-one in OPENING -> GOING n. should be n-1
|
||||||
|
|
||||||
|
;; so, after 2 ticks, we have 26 - / 2 2 25 time remaining
|
||||||
|
|
||||||
|
;; now let's calculate for main task
|
||||||
|
|
||||||
|
;; Turn:
|
||||||
|
;; active pl: state (GOING 4), node DO
|
||||||
|
;; inactive pl: state (GOING 4), node UX
|
||||||
|
;; accum: 1336
|
||||||
|
;; possible next: ((KZ 3) (JM 5) (NO 3))
|
||||||
|
;; current turn: 37; time remaining 8
|
||||||
|
;;
|
||||||
|
;; what were you doing for 24-8 = 16 turns each? well, walking between rooms, yeah
|
||||||
|
;;
|
||||||
|
;; while it runs, let's add printing "max so far"?
|
||||||
|
|
Loading…
Reference in New Issue