Advent-of-Code/day10.lisp

317 lines
10 KiB
Common Lisp

;; https://adventofcode.com/2022/day/10
;;
;; cathode ray tube. hm.
;; so, starting from 20th cycle, step by 40 and calculate the value in the Register
;; to get desired value multiply the cicle number by value.
;; and for result - sum them all
;; cool.
;;
;; main part: we need Register value "during" the 20th, etc cycle
;; so it's "end the end of 19th"
;; at 0 we have value 1
;; then each operation does some amount of ticks.
;; well,
;; i guess i could write (tick) function that increases the timer, and if timer of required time - adds current value to result
;; let's finally use CLOS?
(defclass machine ()
((clock :reader clock :initform 0)
(register :reader register :initform 1)
(accumulated-signal :reader accumulated-signal :initform 0)
(next-special-clock :reader next-special-clock :initform 20)))
(defmethod print-object ((obj machine) stream)
(print-unreadable-object (obj stream :type t)
(with-accessors ((clock clock )
(register register )
(accumulated-signal accumulated-signal)
(next-special-clock next-special-clock))
obj
(format stream "clock: ~a, register: ~a, accum: ~a; next-special: ~a" clock register accumulated-signal next-special-clock))))
(defparameter *test-machine* (make-instance 'machine))
(setf (clock *test-machine*) 7)
(setf (slot-value *test-machine* 'clock) 9)
;; can the method be private in the class?
;; so that clock would be incremented by (tick)
;; and (tick) only called from the (run-command) ?
;; and also for register to also only have reader?
;; can I disallow setting values to register?
;; they are still writeable by (slot-value) link
;; it seems that if I don't need polymorphism, I don't need DEFGENERIC
;; and can just define some functions
;; and classes don't change their scopes in any way, and don't really encapsulate their state, as slots are writeable
(defgeneric tick (obj)
(:method ((obj machine))
(incf (slot-value obj 'clock))
(when (= (next-special-clock obj) (clock obj))
(incf (slot-value obj 'accumulated-signal) (* (clock obj) (register obj)))
(update-special-clock obj))))
(tick *test-machine*)
(defparameter *test-machine* (make-instance 'machine))
(progn
(tick *test-machine*)
(print *test-machine*))
(next-special-clock *test-machine*)
(defgeneric update-special-clock (obj)
(:method ((obj machine))
(incf (slot-value obj 'next-special-clock) 40)))
(update-special-clock *test-machine*)
;; now that i'm thinking about modifying accumulated signal on tick,
;; i'm thinking about storing "next-special-clock" starting with 20, and rewriting by x + 40
;; and comparing with (1- next-special-clock) for value during that special time
;; i need to slow down. how would commands look:
;; addx 4 (tick) (tick) (set-new-value)
;; noop (tick)
;; so. if clock is at 19, meaning we endedn 19th cycle. and we start 20th which is noop
;; the tick should increase the clock
;; check if it's special. if it's 20 then add current Register * 20 and add to sum
;; now tick seems to work.
;; let's implement noop and addx
(defgeneric noop (mach)
(:method ((mach machine))
(tick mach)))
(defgeneric addx (mach num)
(:method ((mach machine) num)
(tick mach)
(tick mach)
(incf (slot-value mach 'register) num )))
(defparameter *test-machine* (make-instance 'machine))
(print *test-machine*)
(addx *test-machine* 4)
;; and all i need now is to simulate the input file
(require 'cl-ppcre)
(defun parse-command-line (line machine)
(let* ((the-split (cl-ppcre:split " " line))
(command (intern (string-upcase (first the-split)))))
(case command
('addx (addx machine (parse-integer (second the-split))))
('noop (noop machine))
(t "hello"))))
(defparameter *test-machine* (make-instance 'machine))
(print *test-machine*)
(parse-command-line "addx 31" *test-machine*)
(parse-command-line "noop" *test-machine*)
(intern "addx")
(intern "ADDX")
(string-upcase "addx")
;; now it's time to simiulate the file evaluation
(let ((my-machine (make-instance 'machine)))
(with-open-file (in "day10-test.txt")
(loop
for line = (read-line in nil nil)
while line
do (parse-command-line line my-machine))
(accumulated-signal my-machine)))
(let ((my-machine (make-instance 'machine)))
(with-open-file (in "day10-input.txt")
(loop
for line = (read-line in nil nil)
while line
do (parse-command-line line my-machine))
(accumulated-signal my-machine)))
;;; PART 2
;; suppose would want to implement the screen as a class as well?
;; it would do what?
;; it should have logic on top of the machine?
;; maybe run it's code after machine tick?
;; maybe extending machine
;; afther cycle that starts at 0 and ends at 1 first pixel finishes
;; after cycle that starts at 39 and ends at 40 last pixes finishes
;; so, yeah. if i have crt extend machine, it could have it's own special cycles
;; and tick could be extended to run after, and also produce pixel \ string \ char
;; & newline when needed - into the string buffer of crt?
(print "hello/nman")
;; i guess i could just print to terminal
;; and improvement could be configuring the output stream
(format t "hello")
(terpri)
(defclass crt (machine)
((next-special-clock :reader next-special-clock :initform 40)))
(defparameter *test-crt* (make-instance 'crt))
(update-special-clock *test-crt*)
(tick *test-crt*)
;; now on each tick i want to print a char.
;; what is the logic here?
;; on each tick print # if current clock is within +-1 of register value
;; and . otherwise
;; on the special-clock to (terpri)
;; simplified printing
(defmethod tick :before ((obj crt))
(format t "@")
(when (= (clock obj) (1- (next-special-clock obj)))
(terpri)))
(defparameter *test-crt* (make-instance 'crt))
(next-special-clock *test-crt*)
(tick *test-crt*)
(noop *test-crt*)
;; so my problem was the crt method runs "before"
;; and never reaches clock == next-special-clock
;; because when tick runs on 39 it increases clock to 40 and also increases next-sp to 80
;; but i suppose doing new line on 39 is what i want
(defun cur-pixel-in-sprite (pixel-index sprite-center)
(and (>= pixel-index (1- sprite-center))
(<= pixel-index (1+ sprite-center))))
(cur-pixel-in-sprite 0 0)
(cur-pixel-in-sprite 0 1)
(cur-pixel-in-sprite 0 2)
(defmethod tick :before ((obj crt))
(let ((pixel (if (cur-pixel-in-sprite (clock obj) (register obj))
"#"
".")))
(format t pixel))
(when (= (clock obj) (1- (next-special-clock obj)))
(incf (slot-value obj 'register) 40)
(terpri)))
(defparameter *test-crt* (make-instance 'crt))
(noop *test-crt*)
(let ((my-machine (make-instance 'crt)))
(with-open-file (in "day10-test.txt")
(loop
for line = (read-line in nil nil)
while line
do (parse-command-line line my-machine))
(accumulated-signal my-machine)))
(let ((my-machine (make-instance 'crt)))
(with-open-file (in "day10-input.txt")
(loop
for line = (read-line in nil nil)
while line
do (parse-command-line line my-machine))
(accumulated-signal my-machine)))
;;; Cleaning up:
;;; part 1
;; state of the CPU
(defclass machine ()
((clock :reader clock :initform 0)
(register :reader register :initform 1)
(accumulated-signal :reader accumulated-signal :initform 0)
(next-special-clock :reader next-special-clock :initform 20)))
(defmethod print-object ((obj machine) stream)
(print-unreadable-object (obj stream :type t)
(with-accessors ((clock clock )
(register register )
(accumulated-signal accumulated-signal)
(next-special-clock next-special-clock))
obj
(format stream "clock: ~a, register: ~a, accum: ~a; next-special: ~a" clock register accumulated-signal next-special-clock))))
;; passing of sinlge step
(defgeneric tick (obj)
(:method ((obj machine))
(incf (slot-value obj 'clock))
(when (= (next-special-clock obj) (clock obj))
(incf (slot-value obj 'accumulated-signal) (* (clock obj) (register obj)))
(update-special-clock obj))))
(defgeneric update-special-clock (obj)
(:method ((obj machine))
(incf (slot-value obj 'next-special-clock) 40)))
;; actual cpu commands in terms of ticks and register changes:
(defgeneric noop (mach)
(:method ((mach machine))
(tick mach)))
(defgeneric addx (mach num)
(:method ((mach machine) num)
(tick mach)
(tick mach)
(incf (slot-value mach 'register) num )))
(require 'cl-ppcre)
(defun parse-command-line (line machine)
(let* ((the-split (cl-ppcre:split " " line))
(command (intern (string-upcase (first the-split)))))
(case command
('addx (addx machine (parse-integer (second the-split))))
('noop (noop machine))
(t "hello"))))
;; now it's time to simiulate the file evaluation
(let ((my-machine (make-instance 'machine)))
(with-open-file (in "day10-test.txt")
(loop
for line = (read-line in nil nil)
while line
do (parse-command-line line my-machine))
(accumulated-signal my-machine)))
(let ((my-machine (make-instance 'machine)))
(with-open-file (in "day10-input.txt")
(loop
for line = (read-line in nil nil)
while line
do (parse-command-line line my-machine))
(accumulated-signal my-machine)))
;;; PART 2
(defclass crt (machine)
((next-special-clock :reader next-special-clock :initform 40)))
(defun cur-pixel-in-sprite (pixel-index sprite-center)
(and (>= pixel-index (1- sprite-center))
(<= pixel-index (1+ sprite-center))))
(defmethod tick :before ((obj crt))
(let ((pixel (if (cur-pixel-in-sprite (clock obj) (register obj))
"#"
".")))
(format t pixel))
(when (= (clock obj) (1- (next-special-clock obj)))
(incf (slot-value obj 'register) 40)
(terpri)))
;; and same code reading in commands, but with CRT results in printing
(let ((my-machine (make-instance 'crt)))
(with-open-file (in "day10-test.txt")
(loop
for line = (read-line in nil nil)
while line
do (parse-command-line line my-machine))
(accumulated-signal my-machine)))
(let ((my-machine (make-instance 'crt)))
(with-open-file (in "day10-input.txt")
(loop
for line = (read-line in nil nil)
while line
do (parse-command-line line my-machine))
(accumulated-signal my-machine)))