summaryrefslogtreecommitdiff
path: root/03
diff options
context:
space:
mode:
Diffstat (limited to '03')
-rw-r--r--03/lib.scm99
-rw-r--r--03/main.scm13
-rw-r--r--03/test.scm42
3 files changed, 154 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)))))))
+
+)
diff --git a/03/main.scm b/03/main.scm
new file mode 100644
index 0000000..561fcd7
--- /dev/null
+++ b/03/main.scm
@@ -0,0 +1,13 @@
+(import (chezscheme))
+
+; Where the magic happens
+(import (lib))
+
+; My personal puzzle input
+(define input 289326)
+
+; Part 1 gives 419 for me
+(printf "Part 1 solution: ~a\n" (solve-part1 input))
+
+; Part 2 gives 295229 for me
+(printf "Part 2 solution: ~a\n" (solve-part2 input))
diff --git a/03/test.scm b/03/test.scm
new file mode 100644
index 0000000..0f7817c
--- /dev/null
+++ b/03/test.scm
@@ -0,0 +1,42 @@
+(import (chezscheme))
+
+; Where the magic happens
+(import (lib))
+
+; My quick-and-dirty unit testing framework (copied for each day)
+(define (run-test name proc input expected)
+ (let ((result (proc input)))
+ (if (= result expected)
+ (printf "\x1b;[32;1mPASS\x1b;[0m: ~a\n"
+ name)
+ (printf "\x1b;[31;1mFAIL\x1b;[0m: ~a: got ~a, expected ~a\n"
+ name result expected))))
+
+(printf "Part 1 tests:\n")
+
+(define (test-part1 name input expected)
+ (run-test name solve-part1 input expected))
+
+(test-part1 "part 1 example 1"
+ 1 0)
+(test-part1 "part 1 example 2"
+ 12 3)
+(test-part1 "part 1 example 3"
+ 23 2)
+(test-part1 "part 1 example 4"
+ 1024 31)
+
+(printf "Part 2 tests:\n")
+
+(define (test-part2 name input expected)
+ (run-test name solve-part2 input expected))
+
+(test-part2 "part 2 example 1"
+ 1 2)
+(test-part2 "part 2 example 2"
+ 23 25)
+(test-part2 "part 2 example 3"
+ 133 142)
+(test-part2 "part 2 example 4"
+ 747 806)
+