finish day4, reorg directory

master-blaster
Matthew Butterick 8 years ago
parent 339b2c7d1d
commit 8539c8f9af

@ -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[<day1>
<setup>
@ -110,7 +110,7 @@ The two are similar. The choice comes down to readability and efficiency — me
@chunk[<test>
(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)

@ -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[<day2>
<setup>
@ -59,6 +59,6 @@ We take the same approach, with a new @racket[box->ribbon] function.
@chunk[<test>
(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))]

@ -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)))))

@ -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[<test>
(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))]

@ -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))))

@ -0,0 +1 @@
iwrupvqb

@ -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[<day4>
<setup>
<q1>
<q2>
<test>]
@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[<setup>
(require racket rackunit openssl/md5)
]
@chunk[<q1>
(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[<q2>
(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[<test>
(module+ test
(define input-str (file->string "day4-input.txt"))
(check-equal? (q1 input-str) 346386)
(check-equal? (q2 input-str) 9958218))]

@ -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 ...)))]))

@ -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)]