summaryrefslogtreecommitdiff
path: root/03/lib.scm
diff options
context:
space:
mode:
authorPrefetch2024-03-02 19:36:12 +0100
committerPrefetch2024-03-02 19:36:12 +0100
commit1fbb07c54523c7a576bfff1cb689e155dd55f15a (patch)
tree7aa9f92a7d99ae9203b538803b7efefd846b67e0 /03/lib.scm
parentaf589b238c1d51960d8af3b36041aca2bad7855b (diff)
Add first five days
Diffstat (limited to '03/lib.scm')
-rw-r--r--03/lib.scm99
1 files changed, 99 insertions, 0 deletions
diff --git a/03/lib.scm b/03/lib.scm
new file mode 100644
index 0000000..6e1e4d1
--- /dev/null
+++ b/03/lib.scm
@@ -0,0 +1,99 @@
+(library (lib)
+ (export solve-part1 solve-part2)
+ (import (chezscheme))
+
+ ; Is this direction up or down?
+ (define (vertical? dir)
+ (= 0 (vector-ref dir 0)))
+
+ ; Rotate stepping direction counterclockwise 90 degrees
+ (define (rotate-ccw dir)
+ (cond
+ ((equal? dir '#(1 0))
+ '#(0 1))
+ ((equal? dir '#(0 1))
+ '#(-1 0))
+ ((equal? dir '#(-1 0))
+ '#(0 -1))
+ ((equal? dir '#(0 -1))
+ '#(1 0))))
+
+ ; Follow the spiral to find (x,y) `pos' of the `address'th square.
+ ; The idea is to take `steps' steps in the current direction `dir',
+ ; rotate `dir', take `steps' steps again, rotate, increment `steps',
+ ; then start over, and so on. So the sequence starts like this:
+ ; 1 step right, 1 step up,
+ ; 2 steps left, 2 steps down,
+ ; 3 steps right, 3 steps up,
+ ; 4 steps left, 4 steps down,
+ ; etc.
+ ; Note: for each value of `steps', the last stage is always up/down.
+ (define (get-position address)
+ (let loop ((pos '#(0 0))
+ (dir '#(1 0))
+ (steps 1)
+ (count 0)
+ (total 1))
+ (if (= total address)
+ pos
+ (if (= count (- steps 1))
+ ; Yes, this is the last step before we need to turn
+ (loop
+ ; Take the step, i.e. add direction to position
+ (vector-map + pos dir)
+ ; Rotate counterclockwise after this step
+ (rotate-ccw dir)
+ ; If this step is vertical, we need to increment
+ (+ steps (if (vertical? dir) 1 0))
+ ; Reset step counter for the current direction
+ 0
+ ; Keep track of total steps since square one
+ (+ total 1))
+ ; No, this is an ordinary step, no rotation needed
+ (loop
+ (vector-map + pos dir)
+ dir
+ steps
+ (+ count 1)
+ (+ total 1))))))
+
+ ; Given a position, return all eight positions adjacent to it
+ (define (get-adjacent pos)
+ (map
+ (lambda (dir) (vector-map + pos dir))
+ '(#(1 0) #(1 1) #(0 1) #(-1 1) #(-1 0) #(-1 -1) #(0 -1) #(1 -1))))
+
+ ; Given a position, sum the values of all initialized adjacent squares
+ (define (sum-adjacent pos memory)
+ (fold-left
+ (lambda (sum adj)
+ ; Try to retrieve `pair' from memory
+ (let ((pair (assoc adj memory)))
+ (+ sum
+ ; If position `(car pair)' has been written,
+ ; add its value `(cdr pair) to the sum total.
+ (if pair (cdr pair) 0))))
+ 0
+ (get-adjacent pos)))
+
+ (define (solve-part1 target)
+ (let* ((xy (get-position target))
+ (x (vector-ref xy 0))
+ (y (vector-ref xy 1)))
+ (+ (abs x) (abs y))))
+
+ (define (solve-part2 target)
+ (let loop ((address 2)
+ (memory '((#(0 0) . 1))))
+ (let ((value (cdar memory)))
+ ; Has the most recently written `value' crossed the threshold?
+ (if (> value target)
+ value
+ ; If not, move on, writing a new `val' to the next `pos'
+ (let* ((pos (get-position address))
+ (val (sum-adjacent pos memory)))
+ (loop
+ (+ address 1)
+ (cons (cons pos val) memory)))))))
+
+)