(in-package :ca.mhcat.advent2022)

;; --- Day 10: Cathode-Ray Tube ---

;; You avoid the ropes, plunge into the river, and swim to
;; shore.

;; The Elves yell something about meeting back up with them
;; upriver, but the river is too loud to tell exactly what
;; they're saying. They finish crossing the bridge and
;; disappear from view.

;; Situations like this must be why the Elves prioritized
;; getting the communication system on your handheld device
;; working. You pull it out of your pack, but the amount of
;; water slowly draining from a big crack in its screen
;; tells you it probably won't be of much immediate use.

;; Unless, that is, you can design a replacement for the
;; device's video system! It seems to be some kind of
;; cathode-ray tube screen and simple CPU that are both
;; driven by a precise clock circuit. The clock circuit
;; ticks at a constant rate; each tick is called a cycle.

;; Start by figuring out the signal being sent by the CPU.
;; The CPU has a single register, X, which starts with the
;; value 1. It supports only two instructions:

;;     addx V takes two cycles to complete. After two
;;     cycles, the X register is increased by the value V.
;;     (V can be negative.)

;;     noop takes one cycle to complete. It has no other
;;     effect.

;; The CPU uses these instructions in a program (your puzzle
;; input) to, somehow, tell the screen what to draw.

;; Consider the following small program:

;; noop
;; addx 3
;; addx -5

(defparameter day10/smol-test-data
 '("noop"
   "addx 3"
   "addx -5"))

;; Execution of this program proceeds as follows:

;;     At the start of the first cycle, the noop instruction
;;     begins execution. During the first cycle, X is 1.
;;     After the first cycle, the noop instruction finishes
;;     execution, doing nothing.

;;     At the start of the second cycle, the addx 3
;;     instruction begins execution. During the second
;;     cycle, X is still 1.

;;     During the third cycle, X is still 1. After the third
;;     cycle, the addx 3 instruction finishes execution,
;;     setting X to 4.

;;     At the start of the fourth cycle, the addx -5
;;     instruction begins execution. During the fourth
;;     cycle, X is still 4.

;;     During the fifth cycle, X is still 4. After the fifth
;;     cycle, the addx -5 instruction finishes execution,
;;     setting X to -1.

;; Maybe you can learn something by looking at the value of
;; the X register throughout execution. For now, consider
;; the signal strength (the cycle number multiplied by the
;; value of the X register) during the 20th cycle and every
;; 40 cycles after that (that is, during the 20th, 60th,
;; 100th, 140th, 180th, and 220th cycles).

;; For example, consider this larger program:

;; addx 15
;; addx -11
;; addx 6
;; addx -3
;; addx 5
;; addx -1
;; addx -8
;; addx 13
;; addx 4
;; noop
;; addx -1
;; addx 5
;; addx -1
;; addx 5
;; addx -1
;; addx 5
;; addx -1
;; addx 5
;; addx -1
;; addx -35
;; addx 1
;; addx 24
;; addx -19
;; addx 1
;; addx 16
;; addx -11
;; noop
;; noop
;; addx 21
;; addx -15
;; noop
;; noop
;; addx -3
;; addx 9
;; addx 1
;; addx -3
;; addx 8
;; addx 1
;; addx 5
;; noop
;; noop
;; noop
;; noop
;; noop
;; addx -36
;; noop
;; addx 1
;; addx 7
;; noop
;; noop
;; noop
;; addx 2
;; addx 6
;; noop
;; noop
;; noop
;; noop
;; noop
;; addx 1
;; noop
;; noop
;; addx 7
;; addx 1
;; noop
;; addx -13
;; addx 13
;; addx 7
;; noop
;; addx 1
;; addx -33
;; noop
;; noop
;; noop
;; addx 2
;; noop
;; noop
;; noop
;; addx 8
;; noop
;; addx -1
;; addx 2
;; addx 1
;; noop
;; addx 17
;; addx -9
;; addx 1
;; addx 1
;; addx -3
;; addx 11
;; noop
;; noop
;; addx 1
;; noop
;; addx 1
;; noop
;; noop
;; addx -13
;; addx -19
;; addx 1
;; addx 3
;; addx 26
;; addx -30
;; addx 12
;; addx -1
;; addx 3
;; addx 1
;; noop
;; noop
;; noop
;; addx -9
;; addx 18
;; addx 1
;; addx 2
;; noop
;; noop
;; addx 9
;; noop
;; noop
;; noop
;; addx -1
;; addx 2
;; addx -37
;; addx 1
;; addx 3
;; noop
;; addx 15
;; addx -21
;; addx 22
;; addx -6
;; addx 1
;; noop
;; addx 2
;; addx 1
;; noop
;; addx -10
;; noop
;; noop
;; addx 20
;; addx 1
;; addx 2
;; addx 2
;; addx -6
;; addx -11
;; noop
;; noop
;; noop

(defparameter day10/big-test-data
 '("addx 15"
   "addx -11"
   "addx 6"
   "addx -3"
   "addx 5"
   "addx -1"
   "addx -8"
   "addx 13"
   "addx 4"
   "noop"
   "addx -1"
   "addx 5"
   "addx -1"
   "addx 5"
   "addx -1"
   "addx 5"
   "addx -1"
   "addx 5"
   "addx -1"
   "addx -35"
   "addx 1"
   "addx 24"
   "addx -19"
   "addx 1"
   "addx 16"
   "addx -11"
   "noop"
   "noop"
   "addx 21"
   "addx -15"
   "noop"
   "noop"
   "addx -3"
   "addx 9"
   "addx 1"
   "addx -3"
   "addx 8"
   "addx 1"
   "addx 5"
   "noop"
   "noop"
   "noop"
   "noop"
   "noop"
   "addx -36"
   "noop"
   "addx 1"
   "addx 7"
   "noop"
   "noop"
   "noop"
   "addx 2"
   "addx 6"
   "noop"
   "noop"
   "noop"
   "noop"
   "noop"
   "addx 1"
   "noop"
   "noop"
   "addx 7"
   "addx 1"
   "noop"
   "addx -13"
   "addx 13"
   "addx 7"
   "noop"
   "addx 1"
   "addx -33"
   "noop"
   "noop"
   "noop"
   "addx 2"
   "noop"
   "noop"
   "noop"
   "addx 8"
   "noop"
   "addx -1"
   "addx 2"
   "addx 1"
   "noop"
   "addx 17"
   "addx -9"
   "addx 1"
   "addx 1"
   "addx -3"
   "addx 11"
   "noop"
   "noop"
   "addx 1"
   "noop"
   "addx 1"
   "noop"
   "noop"
   "addx -13"
   "addx -19"
   "addx 1"
   "addx 3"
   "addx 26"
   "addx -30"
   "addx 12"
   "addx -1"
   "addx 3"
   "addx 1"
   "noop"
   "noop"
   "noop"
   "addx -9"
   "addx 18"
   "addx 1"
   "addx 2"
   "noop"
   "noop"
   "addx 9"
   "noop"
   "noop"
   "noop"
   "addx -1"
   "addx 2"
   "addx -37"
   "addx 1"
   "addx 3"
   "noop"
   "addx 15"
   "addx -21"
   "addx 22"
   "addx -6"
   "addx 1"
   "noop"
   "addx 2"
   "addx 1"
   "noop"
   "addx -10"
   "noop"
   "noop"
   "addx 20"
   "addx 1"
   "addx 2"
   "addx 2"
   "addx -6"
   "addx -11"
   "noop"
   "noop"
   "noop"))

;; The interesting signal strengths can be determined as follows:

;;     During the 20th cycle, register X has the value 21,
;;     so the signal strength is 20 * 21 = 420. (The 20th
;;     cycle occurs in the middle of the second addx -1, so
;;     the value of register X is the starting value, 1,
;;     plus all of the other addx values up to that point: 1
;;     + 15 - 11 + 6 - 3 + 5 - 1 - 8 + 13 + 4 = 21.)

;;     During the 60th cycle, register X has the value 19,
;;     so the signal strength is 60 * 19 = 1140.

;;     During the 100th cycle, register X has the value 18,
;;     so the signal strength is 100 * 18 = 1800.

;;     During the 140th cycle, register X has the value 21,
;;     so the signal strength is 140 * 21 = 2940.

;;     During the 180th cycle, register X has the value 16,
;;     so the signal strength is 180 * 16 = 2880.

;;     During the 220th cycle, register X has the value 18,
;;     so the signal strength is 220 * 18 = 3960.

;; The sum of these signal strengths is 13140.

;; Find the signal strength during the 20th, 60th, 100th,
;; 140th, 180th, and 220th cycles. What is the sum of these
;; six signal strengths?

(defclass day10/state-machine ()
 ((x-reg :initform 1)
  (clock :initform 0)
  (state-log :initform nil :reader state-log)))

(defgeneric day10/exec-instruction (state-machine instruction))

(defmethod day10/exec-instruction ((state-machine day10/state-machine) (instruction list))
 (destructuring-bind (oper &optional opnd) instruction
   (with-slots (x-reg clock state-log) state-machine
     (let ((old-x-reg x-reg))
       (when (eq 'addx oper)
         (push (list (incf clock) old-x-reg) state-log)
         (incf x-reg opnd))
       (push (list (incf clock) old-x-reg) state-log)))))

(defun day10/run-sim (lst)
 (loop for line in (day10/parse-data lst)
       with sm = (make-instance 'day10/state-machine)
       do (day10/exec-instruction sm line)
          finally (return (state-log sm))))

(defun day10/compute-part1 (lst)
 (let ((state-log (day10/run-sim lst)))
   (apply #'+ (mapcar (lambda (target-clock)
                        (apply #'* (find target-clock state-log :key #'car)))
                      (list 20 60 100 140 180 220)))))

(defun day10/parse-data (lines)
 (mapcar (lambda (line)
           (with-input-from-string (s line)
             (let* ((oper (read s))
                    (opnd (case oper (addx (read s)))))
               (list oper opnd))))
         lines))

(defun day10/part1 ()
 (day10/compute-part1
  (load-lines "day10.txt")))

;; --- Part Two ---

;; It seems like the X register controls the horizontal
;; position of a sprite. Specifically, the sprite is 3
;; pixels wide, and the X register sets the horizontal
;; position of the middle of that sprite. (In this system,
;; there is no such thing as "vertical position": if the
;; sprite's horizontal position puts its pixels where the
;; CRT is currently drawing, then those pixels will be
;; drawn.)

;; You count the pixels on the CRT: 40 wide and 6 high. This
;; CRT screen draws the top row of pixels left-to-right,
;; then the row below that, and so on. The left-most pixel
;; in each row is in position 0, and the right-most pixel in
;; each row is in position 39.

;; Like the CPU, the CRT is tied closely to the clock
;; circuit: the CRT draws a single pixel during each cycle.
;; Representing each pixel of the screen as a #, here are
;; the cycles during which the first and last pixel in each
;; row are drawn:

;; Cycle   1 -> ######################################## <- Cycle  40
;; Cycle  41 -> ######################################## <- Cycle  80
;; Cycle  81 -> ######################################## <- Cycle 120
;; Cycle 121 -> ######################################## <- Cycle 160
;; Cycle 161 -> ######################################## <- Cycle 200
;; Cycle 201 -> ######################################## <- Cycle 240

;; So, by carefully timing the CPU instructions and the CRT
;; drawing operations, you should be able to determine
;; whether the sprite is visible the instant each pixel is
;; drawn. If the sprite is positioned such that one of its
;; three pixels is the pixel currently being drawn, the
;; screen produces a lit pixel (#); otherwise, the screen
;; leaves the pixel dark (.).

;; The first few pixels from the larger example above are
;; drawn as follows:

;; Sprite position: ###.....................................

;; Start cycle   1: begin executing addx 15
;; During cycle  1: CRT draws pixel in position 0
;; Current CRT row: #

;; During cycle  2: CRT draws pixel in position 1
;; Current CRT row: ##
;; End of cycle  2: finish executing addx 15 (Register X is now 16)
;; Sprite position: ...............###......................

;; Start cycle   3: begin executing addx -11
;; During cycle  3: CRT draws pixel in position 2
;; Current CRT row: ##.

;; During cycle  4: CRT draws pixel in position 3
;; Current CRT row: ##..
;; End of cycle  4: finish executing addx -11 (Register X is now 5)
;; Sprite position: ....###.................................

;; Start cycle   5: begin executing addx 6
;; During cycle  5: CRT draws pixel in position 4
;; Current CRT row: ##..#

;; During cycle  6: CRT draws pixel in position 5
;; Current CRT row: ##..##
;; End of cycle  6: finish executing addx 6 (Register X is now 11)
;; Sprite position: ..........###...........................

;; Start cycle   7: begin executing addx -3
;; During cycle  7: CRT draws pixel in position 6
;; Current CRT row: ##..##.

;; During cycle  8: CRT draws pixel in position 7
;; Current CRT row: ##..##..
;; End of cycle  8: finish executing addx -3 (Register X is now 8)
;; Sprite position: .......###..............................

;; Start cycle   9: begin executing addx 5
;; During cycle  9: CRT draws pixel in position 8
;; Current CRT row: ##..##..#

;; During cycle 10: CRT draws pixel in position 9
;; Current CRT row: ##..##..##
;; End of cycle 10: finish executing addx 5 (Register X is now 13)
;; Sprite position: ............###.........................

;; Start cycle  11: begin executing addx -1
;; During cycle 11: CRT draws pixel in position 10
;; Current CRT row: ##..##..##.

;; During cycle 12: CRT draws pixel in position 11
;; Current CRT row: ##..##..##..
;; End of cycle 12: finish executing addx -1 (Register X is now 12)
;; Sprite position: ...........###..........................

;; Start cycle  13: begin executing addx -8
;; During cycle 13: CRT draws pixel in position 12
;; Current CRT row: ##..##..##..#

;; During cycle 14: CRT draws pixel in position 13
;; Current CRT row: ##..##..##..##
;; End of cycle 14: finish executing addx -8 (Register X is now 4)
;; Sprite position: ...###..................................

;; Start cycle  15: begin executing addx 13
;; During cycle 15: CRT draws pixel in position 14
;; Current CRT row: ##..##..##..##.

;; During cycle 16: CRT draws pixel in position 15
;; Current CRT row: ##..##..##..##..
;; End of cycle 16: finish executing addx 13 (Register X is now 17)
;; Sprite position: ................###.....................

;; Start cycle  17: begin executing addx 4
;; During cycle 17: CRT draws pixel in position 16
;; Current CRT row: ##..##..##..##..#

;; During cycle 18: CRT draws pixel in position 17
;; Current CRT row: ##..##..##..##..##
;; End of cycle 18: finish executing addx 4 (Register X is now 21)
;; Sprite position: ....................###.................

;; Start cycle  19: begin executing noop
;; During cycle 19: CRT draws pixel in position 18
;; Current CRT row: ##..##..##..##..##.
;; End of cycle 19: finish executing noop

;; Start cycle  20: begin executing addx -1
;; During cycle 20: CRT draws pixel in position 19
;; Current CRT row: ##..##..##..##..##..

;; During cycle 21: CRT draws pixel in position 20
;; Current CRT row: ##..##..##..##..##..#
;; End of cycle 21: finish executing addx -1 (Register X is now 20)
;; Sprite position: ...................###..................

;; Allowing the program to run to completion causes the CRT
;; to produce the following image:

;; ##..##..##..##..##..##..##..##..##..##..
;; ###...###...###...###...###...###...###.
;; ####....####....####....####....####....
;; #####.....#####.....#####.....#####.....
;; ######......######......######......####
;; #######.......#######.......#######.....

;; Render the image given by your program. What eight
;; capital letters appear on your CRT?

(defun day10/render-image (log)
 (coerce
  (loop for entry in log
        for idx from 0 below (* 40 6)
        when (and (zerop (mod idx 40)) (plusp idx))
          collect #\newline
        if (find (mod idx 40) (list entry (1+ entry) (1- entry)))
          collect #\*
        else
          collect #\space)
  'string))

(defun day10/compute-part2 (lst)
 (let ((state-log (day10/run-sim lst)))
     (format t "~A~%" (day10/render-image
                       (reverse
                        (mapcar #'cadr state-log))))))

(defun day10/part2 ()
 (day10/compute-part2
  (load-lines "day10.txt")))