From 1fbb07c54523c7a576bfff1cb689e155dd55f15a Mon Sep 17 00:00:00 2001 From: Prefetch Date: Sat, 2 Mar 2024 19:36:12 +0100 Subject: Add first five days --- 02/input.txt | 16 +++++++++++++ 02/lib.scm | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 02/main.scm | 20 ++++++++++++++++ 02/test.scm | 38 +++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 02/input.txt create mode 100644 02/lib.scm create mode 100644 02/main.scm create mode 100644 02/test.scm (limited to '02') 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) + -- cgit v1.2.3