summaryrefslogtreecommitdiff
path: root/02
diff options
context:
space:
mode:
authorPrefetch2024-03-02 19:36:12 +0100
committerPrefetch2024-03-02 19:36:12 +0100
commit1fbb07c54523c7a576bfff1cb689e155dd55f15a (patch)
tree7aa9f92a7d99ae9203b538803b7efefd846b67e0 /02
parentaf589b238c1d51960d8af3b36041aca2bad7855b (diff)
Add first five days
Diffstat (limited to '02')
-rw-r--r--02/input.txt16
-rw-r--r--02/lib.scm74
-rw-r--r--02/main.scm20
-rw-r--r--02/test.scm38
4 files changed, 148 insertions, 0 deletions
diff --git a/02/input.txt b/02/input.txt
new file mode 100644
index 0000000..315a416
--- /dev/null
+++ b/02/input.txt
@@ -0,0 +1,16 @@
+62 1649 1731 76 51 1295 349 719 52 1984 2015 2171 981 1809 181 1715
+161 99 1506 1658 84 78 533 242 1685 86 107 1548 670 960 1641 610
+95 2420 2404 2293 542 2107 2198 121 109 209 2759 1373 1446 905 1837 111
+552 186 751 527 696 164 114 530 558 307 252 200 481 142 205 479
+581 1344 994 1413 120 112 656 1315 1249 193 1411 1280 110 103 74 1007
+2536 5252 159 179 4701 1264 1400 2313 4237 161 142 4336 1061 3987 2268 4669
+3270 1026 381 185 293 3520 1705 1610 3302 628 3420 524 3172 244 295 39
+4142 1835 4137 3821 3730 2094 468 141 150 3982 147 4271 1741 2039 4410 179
+1796 83 2039 1252 84 1641 2165 1218 1936 335 1807 2268 66 102 1977 2445
+96 65 201 275 257 282 233 60 57 200 216 134 72 105 81 212
+3218 5576 5616 5253 178 3317 6147 5973 2424 274 4878 234 200 4781 5372 276
+4171 2436 134 3705 3831 3952 2603 115 660 125 610 152 4517 587 1554 619
+2970 128 2877 1565 1001 167 254 2672 59 473 2086 181 1305 162 1663 2918
+271 348 229 278 981 1785 2290 516 473 2037 737 2291 2521 1494 1121 244
+2208 2236 1451 621 1937 1952 865 61 1934 49 1510 50 1767 59 194 1344
+94 2312 2397 333 1192 106 2713 2351 2650 2663 703 157 89 510 1824 125
diff --git a/02/lib.scm b/02/lib.scm
new file mode 100644
index 0000000..0274dad
--- /dev/null
+++ b/02/lib.scm
@@ -0,0 +1,74 @@
+(library (lib)
+ (export solve-part1 solve-part2)
+ (import (chezscheme))
+
+ ; Split list at first delimiter into `prefix' and `suffix'
+ ; Return value is a pair like `((p r e f i x) s u f f i x)'
+ (define (list-split-left delimiter? xs)
+ (let loop ((prefix '())
+ (suffix xs))
+ (if (null? suffix)
+ (cons prefix suffix)
+ (let ((x (car suffix)))
+ (if (delimiter? x)
+ ; Found first delimiter, so return immediately
+ (cons prefix (cdr suffix))
+ ; `x' isn't a delimiter, so append it to `prefix'
+ (loop (append prefix (list x)) (cdr suffix)))))))
+
+ ; Split list at given delimiter into list of sublists
+ (define (list-split delimiter? xs)
+ (let loop ((pieces '())
+ (rest xs))
+ (if (null? rest)
+ ; Fix order and remove all empty sublists from output
+ ; (which are caused by consecutive delimiters in `xs')
+ (reverse (remp null? pieces))
+ ; Extract next piece from `rest' and prepend it to `pieces'
+ (let ((next (list-split-left delimiter? rest)))
+ (loop (cons (car next) pieces) (cdr next))))))
+
+ ; Split string at whitespace into list of words
+ (define (string-split str)
+ (map list->string (list-split char-whitespace? (string->list str))))
+
+ ; Partially applied divisibility check
+ (define (check-divisor? x)
+ (lambda (y)
+ (and (not (= y 0)) ; Don't try dividing by zero
+ (not (= x y)) ; Disallow trivial solution
+ (= (mod x y) 0))))
+
+ ; Find minimum and maximum in row, return difference
+ (define (find-range row)
+ (- (apply max row) (apply min row)))
+
+ ; Find `x' and `y' where `y' is divisor of `x', return quotient
+ (define (find-quotient row)
+ ; Note: we don't check if we've reached the end of `xs',
+ ; because we're guaranteed success before that happens.
+ ; We're also guaranteed only one non-trivial solution.
+ (let loop ((xs row))
+ (let* ((x (car xs))
+ (y (find (check-divisor? x) row)))
+ ; Is this the (`x',`y') combination we're looking for,
+ ; i.e. have we found a valid divisor `y' for this `x'?
+ (if y
+ (div x y)
+ (loop (cdr xs))))))
+
+ ; Add up result of `proc' for each `row' of numbers in `lines'
+ (define (solve-puzzle proc lines)
+ (fold-left
+ (lambda (accum row)
+ (+ accum (proc (map string->number row))))
+ 0
+ (map string-split lines)))
+
+ (define (solve-part1 lines)
+ (solve-puzzle find-range lines))
+
+ (define (solve-part2 lines)
+ (solve-puzzle find-quotient lines))
+
+)
diff --git a/02/main.scm b/02/main.scm
new file mode 100644
index 0000000..746fa01
--- /dev/null
+++ b/02/main.scm
@@ -0,0 +1,20 @@
+(import (chezscheme))
+
+; Where the magic happens
+(import (lib))
+
+; Read my personal puzzle input
+(define input
+ (call-with-input-file "input.txt"
+ (lambda (port)
+ (let loop ((line (get-line port))
+ (result '()))
+ (if (eof-object? line)
+ (reverse result)
+ (loop (get-line port) (cons line result)))))))
+
+; Part 1 gives 44216 for me
+(printf "Part 1 solution: ~a\n" (solve-part1 input))
+
+; Part 2 gives 320 for me
+(printf "Part 2 solution: ~a\n" (solve-part2 input))
diff --git a/02/test.scm b/02/test.scm
new file mode 100644
index 0000000..10ee751
--- /dev/null
+++ b/02/test.scm
@@ -0,0 +1,38 @@
+(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))
+
+(define part1-example1
+ '("5 1 9 5"
+ "7 5 3"
+ "2 4 6 8"))
+(test-part1 "part 1 example 1"
+ part1-example1 18)
+
+(printf "Part 2 tests:\n")
+
+(define (test-part2 name input expected)
+ (run-test name solve-part2 input expected))
+
+(define part2-example1
+ '("5 9 2 8"
+ "9 4 7 3"
+ "3 8 6 5"))
+(test-part2 "part 2 example 1"
+ part2-example1 9)
+