πŸŽ„ Advent of Code 2022 - Day 10 - Cathode Ray Tube

(ns p10
(:require [clojure.string :as str]
[clojure.edn :as edn]
[clojure.test :as test]
[nextjournal.clerk :as clerk]))

For today's puzzle we are given a set of instructions for how to manipulate a single register. The noop instruction just increments the clock, while the addx instruction will add the corresponding number to the register.

Data

First we read in the instructions in a parseable form

(def data-string (slurp "input/advent-2022-10.txt"))
"
noopβ†©οΈŽnoopβ†©οΈŽaddx 15β†©οΈŽaddx -10β†©οΈŽnoopβ†©οΈŽnoopβ†©οΈŽaddx 3β†©οΈŽnoopβ†©οΈŽnoopβ†©οΈŽaddx 7β†©οΈŽaddx 1β†©οΈŽaddx 4β†©οΈŽaddx 888 more elided"
(defn process [s]
(->> s
(str/split-lines)
(map (fn [s] (str "(:" s ")")))
(map edn/read-string)))
#object[p10$process 0x60754a42 "
p10$process@60754a42"
]
(def data (process data-string))
((:noop) (:noop) (:addx 15) (:addx -10) (:noop) (:noop) (:addx 3) (:noop) (:noop) (:addx 7) (:addx 1) (:addx 4) (:addx -1) (:addx 1) (:addx 5) (:addx 1) (:noop) (:noop) (:addx 5) (:addx -1) 126 more elided)
(def test-data (process "noop
addx 3
noop addx 3 addx -5"))
((:noop) (:addx 3) (:addx -5))
(def test-data-2 (process (slurp "input/advent-2022-10-test.txt")))
((: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) 271 more elided)

Logic

For the core logic of the puzzle, we'll try to implement a function cycles that will return a lazy-seq of the states of the system at every clock cycle.

(def init {:clock 0 :X 1})
{:X 1 :clock 0}
(defn noop [state] (update state :clock inc))
#object[p10$noop 0x6933f579 "
p10$noop@6933f579"
]
(defn addx [state dx] (-> state (update :clock inc) (update :X + dx)))
#object[p10$addx 0x47d41097 "
p10$addx@47d41097"
]
(defn cycles
"Given a set of instructions, produce a lazy sequence of states."
([insts] (lazy-seq (cons init (cons init (cycles (noop init) insts)))))
([state insts]
(if-let [[op dx] (first insts)]
(let [next (noop state)]
(case op
:noop (lazy-seq (cons next (cycles next (rest insts))))
:addx (let [future (addx next dx)]
(lazy-seq (cons next (cons future (cycles future (rest insts))))))))
;; empty)))
nil)))
#object[p10$cycles 0x4f56cd4f "
p10$cycles@4f56cd4f"
]
(defn signal-strength [{clock :clock x :X}] (* x clock))
#object[p10$signal_strength 0x53bb9bd "
p10$signal_strength@53bb9bd"
]

Having done this, now we're told to pull out the "interesting signals" which are the 20th clock cycle and every 40 after that.

(defn sum-of-interesting-signals [insts]
(transduce
(comp
(drop 20)
(take-nth 40)
(map signal-strength))
+
(cycles insts)))
#object[p10$sum_of_interesting_signals 0x73551c5e "
p10$sum_of_interesting_signals@73551c5e"
]
(test/deftest test-part-1
(test/is (= 13140 (sum-of-interesting-signals test-data-2))))
#object[p10$test_part_1 0x14e2fa0b "
p10$test_part_1@14e2fa0b"
]
(def ans1 (sum-of-interesting-signals data))
14420

Part 2

For part 2, we now also have a CRT type monitor in place, and we'll draw something on the screen if it so happens that our CRT cursor is within one space of the horizontal register from the previous section.

We'll use the clerk html viewer here to generate a <pre> element with the result.

(defn render [data]
(clerk/html
[:pre {:style {:font-face "monospace" :line-height "1.0em"}}
(->> (cycles data)
(drop 1)
(map
(fn [pos {x :X}] (if (<= (abs (- pos x)) 1) \# \ ))
(for [row (range 6) pos (range 40)] pos))
(partition 40)
(map (fn [cs] (apply str cs)))
(str/join "\n"))]))
#object[p10$render 0x6d27716d "
p10$render@6d27716d"
]

Let's look at the test

(render test-data-2)
##  ##  ##  ##  ##  ##  ##  ##  ##  ##  
###   ###   ###   ###   ###   ###   ### 
####    ####    ####    ####    ####    
#####     #####     #####     #####     
######      ######      ######      ####
#######       #######       #######     

Looks good, what about our puzzle input:

(render data)
###   ##  #    ###  ###  ####  ##  #  # 
#  # #  # #    #  # #  #    # #  # #  # 
#  # #    #    #  # ###    #  #  # #  # 
###  # ## #    ###  #  #  #   #### #  # 
# #  #  # #    # #  #  # #    #  # #  # 
#  #  ### #### #  # ###  #### #  #  ##  
(def ans2 "rglrbzau")
"
rglrbzau"

Main

(defn -test [& _]
(test/run-tests 'p10))
#object[p10$_test 0x3509cb25 "
p10$_test@3509cb25"
]
(defn -main [& _]
(println "Answer1: " ans1)
(println "Answer2: " ans2))
#object[p10$_main 0x6ee59636 "
p10$_main@6ee59636"
]