diff --git a/day1/input.txt b/day1-input.txt similarity index 100% rename from day1/input.txt rename to day1-input.txt diff --git a/day1/main.scrbl b/day1.scrbl similarity index 97% rename from day1/main.scrbl rename to day1.scrbl index a8cae2d..4286d63 100644 --- a/day1/main.scrbl +++ b/day1.scrbl @@ -3,7 +3,7 @@ @aoc-title[1] -Our @link-rp["day1/input.txt"]{input} is a string of parentheses that controls an elevator. A left parenthesis @litchar{(} means go up one floor, and a right parenthesis @litchar{)} means go down. +Our @link-rp["day1-input.txt"]{input} is a string of parentheses that controls an elevator. A left parenthesis @litchar{(} means go up one floor, and a right parenthesis @litchar{)} means go down. @chunk[ @@ -110,7 +110,7 @@ The two are similar. The choice comes down to readability and efficiency — me @chunk[ (module+ test - (define input-str (file->string "input.txt")) + (define input-str (file->string "day1-input.txt")) (check-equal? (q1 input-str) 74) (check-equal? (q1-alt input-str) 74) (check-equal? (q2 input-str) 1795) diff --git a/day2/input.txt b/day2-input.txt similarity index 100% rename from day2/input.txt rename to day2-input.txt diff --git a/day2/main.scrbl b/day2.scrbl similarity index 92% rename from day2/main.scrbl rename to day2.scrbl index 1fa2a94..4f43b2b 100644 --- a/day2/main.scrbl +++ b/day2.scrbl @@ -3,7 +3,7 @@ @aoc-title[2] -Our @link-rp["day2/input.txt"]{input} is a list of strings that represent dimensions of rectangular boxes. +Our @link-rp["day2-input.txt"]{input} is a list of strings that represent dimensions of rectangular boxes. @chunk[ @@ -59,6 +59,6 @@ We take the same approach, with a new @racket[box->ribbon] function. @chunk[ (module+ test - (define input-str (file->string "input.txt")) + (define input-str (file->string "day2-input.txt")) (check-equal? (q1 input-str) 1586300) (check-equal? (q2 input-str) 3737498))] \ No newline at end of file diff --git a/day2/daylang.rkt b/day2/daylang.rkt deleted file mode 100644 index 04956c7..0000000 --- a/day2/daylang.rkt +++ /dev/null @@ -1,43 +0,0 @@ -#lang racket -(require rackunit) -(provide read-syntax) - -(define (get-dimensions str) - (map string->number (regexp-match* #px"\\d+" str))) - -(define (get-faces dimensions) - (define half-faces - (for*/list ([first-pos (in-range (sub1 (length dimensions)))] - [second-pos (in-range (add1 first-pos) (length dimensions))]) - (list (list-ref dimensions first-pos) (list-ref dimensions second-pos)))) - (append* (make-list 2 half-faces))) - -(define (str->paper str) - (define dimensions (get-dimensions str)) - (define faces (get-faces dimensions)) - (define areas (map (curry apply *) faces)) - (define smallest-area (apply min areas)) - (apply + smallest-area areas)) - -(define (str->ribbon str) - (define dimensions (get-dimensions str)) - (define faces (get-faces dimensions)) - (define perimeters (map (λ(face) (* 2 (apply + face))) faces)) - (define smallest-perimeter (apply min perimeters)) - (define volume (apply * dimensions)) - (+ smallest-perimeter volume)) - -(check-equal? (str->paper "2x3x4") 58) -(check-equal? (str->paper "1x1x10") 43) - -(check-equal? (str->ribbon "2x3x4") 34) -(check-equal? (str->ribbon "1x1x10") 14) - -(define (read-syntax source-path-string in-port) - (with-syntax ([source-str (string-trim (port->string in-port))] - [str->paper str->paper] - [str->ribbon str->ribbon]) - #'(module _ racket - (define package-strs (string-split 'source-str)) - (apply + (map str->paper package-strs)) - (apply + (map str->ribbon package-strs))))) \ No newline at end of file diff --git a/day3/input.txt b/day3-input.txt similarity index 100% rename from day3/input.txt rename to day3-input.txt diff --git a/day3/main.scrbl b/day3.scrbl similarity index 97% rename from day3/main.scrbl rename to day3.scrbl index cd09cac..e489d8a 100644 --- a/day3/main.scrbl +++ b/day3.scrbl @@ -3,7 +3,7 @@ @aoc-title[3] -Our @link-rp["day3/input.txt"]{input} is a string made of the characters @litchar{^v<>} that represent north, south, west, and east. Taken together, the string represents a path through an indefinitely large grid. +Our @link-rp["day3-input.txt"]{input} is a string made of the characters @litchar{^v<>} that represent north, south, west, and east. Taken together, the string represents a path through an indefinitely large grid. In essence, this a two-dimensional version of the elevator problem in @secref["day-1"]. @@ -98,7 +98,7 @@ The solution works the same as before — the only new task is to split the inpu @chunk[ (module+ test - (define input-str (file->string "input.txt")) + (define input-str (file->string "day3-input.txt")) (check-equal? (q1 input-str) 2565) (check-equal? (q1-complex input-str) 2565) (check-equal? (q2 input-str) 2639))] diff --git a/day3/daylang.rkt b/day3/daylang.rkt deleted file mode 100644 index b99a3ee..0000000 --- a/day3/daylang.rkt +++ /dev/null @@ -1,47 +0,0 @@ -#lang racket -(require rackunit) -(provide read-syntax) - -(define (str->visits str) - (define start '(0 0)) - (define moves (map (λ(move) (case move - [("^") '(0 1)] - [("v") '(0 -1)] - [("<") '(-1 0)] - [(">") '(1 0)])) - (regexp-match* #rx"." str))) - (reverse (for/fold ([visit-acc (list start)]) - ([move (in-list moves)]) - (cons (map + move (car visit-acc)) visit-acc)))) - -(define (str->unique-visits str) - (define visits (str->visits str)) - (length (remove-duplicates visits))) - -(define (str->robosanta str) - (define-values (reversed-santa-path reversed-robo-path) - (for/fold ([santa-acc empty][robo-acc empty]) - ([c (in-string str)][pos (in-naturals)]) - (if (even? pos) - (values (cons c santa-acc) robo-acc) - (values santa-acc (cons c robo-acc))))) - (define santa-str (string-append* (map ~a (reverse reversed-santa-path)))) - (define robo-str (string-append* (map ~a (reverse reversed-robo-path)))) - (length (remove-duplicates (append (str->visits santa-str) (str->visits robo-str))))) - - -(check-equal? (str->unique-visits ">") 2) -(check-equal? (str->unique-visits "^>v<") 4) - -(check-equal? (str->robosanta "^v") 3) -(check-equal? (str->robosanta "^>v<") 3) -(check-equal? (str->robosanta "^v^v^v^v^v") 11) - - -(define (read-syntax source-path-string in-port) - (with-syntax ([source-str (string-trim (port->string in-port))] - [str->unique-visits str->unique-visits] - [str->robosanta str->robosanta]) - #'(module _ racket - (str->unique-visits source-str) - (str->robosanta source-str)))) \ No newline at end of file diff --git a/day4-input.txt b/day4-input.txt new file mode 100644 index 0000000..fe1c591 --- /dev/null +++ b/day4-input.txt @@ -0,0 +1 @@ +iwrupvqb \ No newline at end of file diff --git a/day4.scrbl b/day4.scrbl new file mode 100644 index 0000000..cd363a3 --- /dev/null +++ b/day4.scrbl @@ -0,0 +1,51 @@ +#lang scribble/lp2 +@(require scribble/manual aoc-racket/helper) +@(require (for-label openssl/md5)) + +@aoc-title[4] + +Our @link-rp["day4-input.txt"]{input} is a string of eight characters that represents part of a key for making an MD5 hash. + +@chunk[ + + + + ] + +@section{What is the lowest-numbered MD5 hash starting with five zeroes?} + +We're asked to create an MD5 hash from an input key that consists of our eight-character input joined to a decimal number. The puzzle asks us to find the lowest decimal number that, when joined to our input, produces an MD5 hash that starts with five zeroes. + +Whether or not you already know what an MD5 hash is, you can search the Racket docs and will soon find the @racketmodname[openssl/md5] module and the @racket[md5] function. Then, this puzzle is easy: starting at @racket[0], make new input keys with each integer, and stop when we find one that results in the MD5 hash we want. (The approach is similar to the second part of @secref["day-1"].) + + +@chunk[ + (require racket rackunit openssl/md5) + ] + +@chunk[ + (define (q1 str) + (for/or ([i (in-naturals)]) + (define md5-key (string-append str (~a i))) + (define md5-hash (md5 (open-input-string md5-key))) + (and (string-prefix? md5-hash "00000") i))) + ] + +@section{How about six zeroes?} + +Exactly the same, except we test for a string of six zeroes. It is likely, however, to take quite a bit longer to run, as the sixth zero essentially makes the criterion 10 times more stringent. + +@chunk[ + (define (q2 str) + (for/or ([i (in-naturals)]) + (define md5-key (string-append str (~a i))) + (define md5-hash (md5 (open-input-string md5-key))) + (and (string-prefix? md5-hash "000000") i)))] + +@section{Testing our input} + +@chunk[ + (module+ test + (define input-str (file->string "day4-input.txt")) + (check-equal? (q1 input-str) 346386) + (check-equal? (q2 input-str) 9958218))] diff --git a/helper.rkt b/helper.rkt index 173faad..d5deb00 100644 --- a/helper.rkt +++ b/helper.rkt @@ -14,5 +14,5 @@ (with-syntax ([rp-name (generate-temporary)]) #'(begin (require racket/runtime-path) - (define-runtime-path rp-name (expand-user-path where)) + (define-runtime-path rp-name where) @(link (path->string rp-name) text-args ...)))])) \ No newline at end of file diff --git a/start.scrbl b/start.scrbl index a6d2d48..0105b9d 100644 --- a/start.scrbl +++ b/start.scrbl @@ -9,6 +9,7 @@ @local-table-of-contents[] -@include-section[(submod "day1/main.scrbl" doc)] -@include-section[(submod "day2/main.scrbl" doc)] -@include-section[(submod "day3/main.scrbl" doc)] +@include-section[(submod "day1.scrbl" doc)] +@include-section[(submod "day2.scrbl" doc)] +@include-section[(submod "day3.scrbl" doc)] +@include-section[(submod "day4.scrbl" doc)] \ No newline at end of file