/
day08.lisp
81 lines (68 loc) · 2.6 KB
/
day08.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
(mgl-pax:define-package :aoc.2020.08
(:nicknames :2020.08)
(:use :cl :aoc.util :mgl-pax)
(:import-from :alexandria #:make-keyword)
(:import-from :screamer #:make-variable))
(in-package :2020.08)
(defsummary (:title "Handheld Halt")
"**Part 1** - Find the bootloader error"
(final-acc-value function)
"**Part 2** - Fix the bootloader error"
(apply-patch function))
(defvar *mutate* nil)
(defvar *mutations* (make-variable))
(defvar *visited* '())
(defun build-data (&optional input)
(coerce (read-day-input #'parse-instruction :input input) 'vector))
(defun parse-instruction (instruction)
(destructuring-bind (opcode arg) (cl-ppcre:split " " instruction)
(list (make-keyword (string-upcase opcode)) (parse-integer arg))))
(defstruct cpu
(pc 0 :type fixnum)
(acc 0 :type fixnum))
(defstruct exit
(cause nil :type keyword)
(value nil :type fixnum))
(defun done? (pc destination)
(or (member pc *visited*)
(= pc destination)))
(screamer::defun step-cpu (cpu opcode arg)
(when (member (cpu-pc cpu) *visited*)
(screamer:fail))
(screamer:local
(setf *visited* (cons (cpu-pc cpu) *visited*))
(let ((opcode
(cond ((and *mutate* (not (eql opcode :acc)))
(screamer:either :jmp :nop))
(t opcode))))
(ecase opcode
(:nop (incf (cpu-pc cpu)))
(:acc (setf (cpu-acc cpu) (+ (cpu-acc cpu) arg)
(cpu-pc cpu) (1+ (cpu-pc cpu))))
(:jmp (setf (cpu-pc cpu) (+ (cpu-pc cpu) arg)))))))
(screamer::defun final-acc-value (code)
(screamer:assert! (screamer:=v *mutations* (screamer:an-integer-betweenv 0 1)))
(loop with cpu = (make-cpu)
with destination = (length code)
for pc = (cpu-pc cpu)
until (done? pc destination)
for (opcode arg) = (aref code pc)
do (step-cpu cpu opcode arg)
finally (return (values (cpu-acc cpu) pc))))
(defun part-1 (&optional (data (build-data)))
(screamer:one-value (final-acc-value data)))
(defun apply-patch (code address)
(let ((patched (map 'vector #'copy-list code)))
(symbol-macrolet ((patch-point (first (aref patched address))))
(when (eql patch-point :acc)
(return-from apply-patch :skip))
(setf patch-point (if (eql patch-point :nop) :jmp :nop)))
patched))
(defun part-2 (&optional (data (build-data)))
(loop for i = 0 then (1+ i)
for patched = (apply-patch data i)
unless (eql patched :skip)
do (multiple-value-bind (acc pc)
(screamer:one-value (final-acc-value patched))
(when (= pc (length data))
(return acc)))))