diff --git a/8.rkt b/8.rkt index 93e4cec..cb011cd 100644 --- a/8.rkt +++ b/8.rkt @@ -26,17 +26,39 @@ (cdr (execute-program input))) (define (part2 input) - (let/ec exit - (for ([ins (in-vector input)] [i (in-naturals)] - #:unless (string=? "acc" (insn-type ins))) - (define new-insn - (insn (match (insn-type ins) ["jmp" "nop"] ["nop" "jmp"]) - (insn-arg ins))) - (define new-prg (vector-copy input)) - (vector-set! new-prg i new-insn) - (match (execute-program new-prg) - [(cons 'loop _) (void)] - [(cons 'done acc) (exit acc)])))) + (define target-pc (vector-length input)) + ;; create naive CFG (as list of edges) + (define model/list + (for/list ([i (in-naturals)] [ins (in-vector input)]) + (match-define (insn type arg) ins) + (match type + [(or "nop" "acc") (list i (add1 i))] + ["jmp" (list i (+ i arg))]))) + ;; detect connected components of unweighted CFG + (define ccs (map list->set (cc (undirected-graph model/list)))) + ;; find which components we need to connect + (define to-cc (for/first ([cc (in-list ccs)] #:when (set-member? cc target-pc)) cc)) + ;; execute the program, but bail as soon as there's a way to to-cc + (define (execute/graph G [v 0] [acc 0] [changed? #f]) + (cond + ;; done! + [(= target-pc v) acc] + ;; not done, yet.... + [else + ;; fetch instruction + (match-define (insn type arg) (vector-ref input v)) + ;; determine the "alternate target" if this instruction were mutated + (define alt-target (match type + ["nop" (+ v arg)] + ["jmp" (add1 v)] + ["acc" #f])) + ;; calculate new accumulator + (define new-acc (if (string=? type "acc") (+ acc arg) acc)) + ;; if we haven't mutated an instruction yet, and alt-target goes into to-cc, use it + (if (and (not changed?) alt-target (set-member? to-cc alt-target)) + (execute/graph G alt-target new-acc #t) + (execute/graph G (first (get-neighbors G v)) new-acc changed?))])) + (execute/graph (directed-graph model/list))) (module+ main (define input