master-blaster
Matthew Butterick 9 years ago
parent 9e9f30b27c
commit 3569b6bccb

@ -5,20 +5,24 @@
@author[(author+email "Matthew Butterick" "mb@mbtype.com")] @author[(author+email "Matthew Butterick" "mb@mbtype.com")]
@defmodule[aoc-racket] @defmodule[aoc-racket]
@italic{Dedicated to curious characters everywhere, especially those learning Racket.}
@local-table-of-contents[] @local-table-of-contents[]
@include-section[(submod "day1.scrbl" doc)] @include-section[(submod "day1.rkt" doc)]
@include-section[(submod "day2.scrbl" doc)] @include-section[(submod "day2.rkt" doc)]
@include-section[(submod "day3.scrbl" doc)] @include-section[(submod "day3.rkt" doc)]
@include-section[(submod "day4.scrbl" doc)] @include-section[(submod "day4.rkt" doc)]
@include-section[(submod "day5.scrbl" doc)] @include-section[(submod "day5.rkt" doc)]
@include-section[(submod "day6.scrbl" doc)] @include-section[(submod "day6.rkt" doc)]
@include-section[(submod "day7.scrbl" doc)] @include-section[(submod "day7.rkt" doc)]
@include-section[(submod "day8.scrbl" doc)] @include-section[(submod "day8.rkt" doc)]
@include-section[(submod "day9.scrbl" doc)] @include-section[(submod "day9.rkt" doc)]
@include-section[(submod "day10.scrbl" doc)] @include-section[(submod "day10.rkt" doc)]
@include-section[(submod "day11.scrbl" doc)] @include-section[(submod "day11.rkt" doc)]
@include-section[(submod "day12.scrbl" doc)] @include-section[(submod "day12.rkt" doc)]
@include-section[(submod "day13.scrbl" doc)] @include-section[(submod "day13.rkt" doc)]
@index-section[]

@ -3,6 +3,8 @@
@aoc-title[1] @aoc-title[1]
@defmodule[aoc-racket/day1]
@link["http://adventofcode.com/day/1"]{The puzzle}. 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. @link["http://adventofcode.com/day/1"]{The puzzle}. 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> @chunk[<day1>
@ -22,6 +24,7 @@ The building has an indefinite number of floors in both directions. So the ultim
@chunk[<day1-setup> @chunk[<day1-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
(define up-char #\() (define up-char #\()
(define down-char #\)) (define down-char #\))
@ -35,11 +38,7 @@ The building has an indefinite number of floors in both directions. So the ultim
@chunk[<day1-q1> @chunk[<day1-q1>
(define (q1 str) (define (q1 str)
(displayln (format "ups = ~a" (get-ups str))) (get-destination str))]
(displayln (format "downs = ~a" (get-downs str)))
(define destination (get-destination str))
(displayln (format "destination = ~a" destination))
destination)]
@subsection{Alternate approach: numerical conversion} @subsection{Alternate approach: numerical conversion}
@ -53,9 +52,7 @@ Rather than counting matches with @racket[regexp-match*], we could also convert
-1))) -1)))
(define (q1-alt str) (define (q1-alt str)
(define destination (apply + (elevator-string->ints str))) (apply + (elevator-string->ints str)))]
(displayln (format "destination = ~a" destination))
destination)]
@section[#:tag "q2"]{At what point does the elevator enter the basement?} @section[#:tag "q2"]{At what point does the elevator enter the basement?}
@ -77,15 +74,13 @@ We could characterize this as a problem of tracking @italic{cumulative values} o
#:break (in-basement? movements-so-far)) #:break (in-basement? movements-so-far))
(cons (get-destination (~a c)) movements-so-far))) (cons (get-destination (~a c)) movements-so-far)))
(define basement-position (length relative-movements)) (length relative-movements))]
(displayln (format "basement entered at position = ~a" basement-position))
basement-position)]
@subsection{Alternate approaches: @tt{for/first} or @tt{for/or}} @subsection{Alternate approaches: @tt{for/first} or @tt{for/or}}
When you need to stop a loop the first time a condition occurs, you can also consider @racket[for/first] or @racket[for/or]. The difference is that @racket[for/first] ends after the first evaluation of the body, but @racket[for/or] evaluates the body every time, and ends the first time the body is not @racket[#f]. When you need to stop a loop the first time a condition occurs, you can also consider @racket[for/first] or @racket[for/or]. The difference is that @racket[for/first] ends after the first evaluation of the body, but @racket[for/or] evaluates the body every time, and ends the first time the body is not @racket[#f].
The two are similar. The choice comes down to readability and efficiency  meaning, if each iteration of the loop is expensive, you probably will want to cache intermediate values, which means you might as well use @racket[for/fold]. The two are similar. The choice comes down to readability and efficiency  meaning, if each iteration of the loop is expensive, you'll probably want to cache intermediate values, which means you might as well use @racket[for/fold].
@chunk[<day1-q2-alt> @chunk[<day1-q2-alt>
(define (q2-for/first str) (define (q2-for/first str)
@ -94,7 +89,6 @@ The two are similar. The choice comes down to readability and efficiency — me
(for/first ([idx (in-range (length ints))] (for/first ([idx (in-range (length ints))]
#:when (negative? (apply + (take ints idx)))) #:when (negative? (apply + (take ints idx))))
idx))) idx)))
(displayln (format "basement entered at position = ~a" basement-position))
basement-position) basement-position)
(define (q2-for/or str) (define (q2-for/or str)
@ -102,7 +96,6 @@ The two are similar. The choice comes down to readability and efficiency — me
(let ([ints (elevator-string->ints str)]) (let ([ints (elevator-string->ints str)])
(for/or ([idx (in-range (length ints))]) (for/or ([idx (in-range (length ints))])
(and (negative? (apply + (take ints idx))) idx)))) (and (negative? (apply + (take ints idx))) idx))))
(displayln (format "basement entered at position = ~a" basement-position))
basement-position)] basement-position)]

@ -3,6 +3,8 @@
@aoc-title[10] @aoc-title[10]
@defmodule[aoc-racket/day10]
@link["http://adventofcode.com/day/10"]{The puzzle}. Our @link-rp["day10-input.txt"]{input} is a short numeric key. @link["http://adventofcode.com/day/10"]{The puzzle}. Our @link-rp["day10-input.txt"]{input} is a short numeric key.
@chunk[<day10> @chunk[<day10>
@ -21,6 +23,7 @@ The second part of the puzzle is just going to change the number of iterations.
@chunk[<day10-setup> @chunk[<day10-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
(define (look-and-say iterations input-key) (define (look-and-say iterations input-key)
(for/fold ([start input-key]) (for/fold ([start input-key])

@ -3,6 +3,8 @@
@aoc-title[11] @aoc-title[11]
@defmodule[aoc-racket/day11]
@link["http://adventofcode.com/day/11"]{The puzzle}. Our @link-rp["day11-input.txt"]{input} is a short alphabetic key that represents a password. @link["http://adventofcode.com/day/11"]{The puzzle}. Our @link-rp["day11-input.txt"]{input} is a short alphabetic key that represents a password.
@chunk[<day11> @chunk[<day11>
@ -34,6 +36,7 @@ The @racket[three-consecutive-letters?] test works by converting the letters to
@chunk[<day11-setup> @chunk[<day11-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
(define (increment-password password) (define (increment-password password)
(define (increment-letter c) (define (increment-letter c)

@ -3,6 +3,8 @@
@aoc-title[12] @aoc-title[12]
@defmodule[aoc-racket/day12]
@link["http://adventofcode.com/day/12"]{The puzzle}. Our @link-rp["day12-input.txt"]{input} is, unfortunately, a @link["http://json.org/"]{JSON} file. @link["http://adventofcode.com/day/12"]{The puzzle}. Our @link-rp["day12-input.txt"]{input} is, unfortunately, a @link["http://json.org/"]{JSON} file.
@chunk[<day12> @chunk[<day12>
@ -24,6 +26,7 @@ If you're new to Racket, notice the @italic{recursive descent} pattern used in @
@chunk[<day12-setup> @chunk[<day12-setup>
(require racket rackunit json) (require racket rackunit json)
(provide (all-defined-out))
(define (string->jsexpr str) (define (string->jsexpr str)
(read-json (open-input-string str))) (read-json (open-input-string str)))

@ -3,6 +3,8 @@
@aoc-title[13] @aoc-title[13]
@defmodule[aoc-racket/day13]
@link["http://adventofcode.com/day/13"]{The puzzle}. Our @link-rp["day13-input.txt"]{input} is a list of descriptions of ``happiness units'' that would be gained or lost among eight people sitting next to each other at a dinner table. @link["http://adventofcode.com/day/13"]{The puzzle}. Our @link-rp["day13-input.txt"]{input} is a list of descriptions of ``happiness units'' that would be gained or lost among eight people sitting next to each other at a dinner table.
@chunk[<day13> @chunk[<day13>
@ -23,6 +25,7 @@ Those wrinkles noted, we'll proceed as we did in @secref{Day_9}. We'll parse the
@chunk[<day13-setup> @chunk[<day13-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
(define happiness-scores (make-hash)) (define happiness-scores (make-hash))

@ -3,6 +3,8 @@
@aoc-title[2] @aoc-title[2]
@defmodule[aoc-racket/day2]
@link["http://adventofcode.com/day/2"]{The puzzle}. Our @link-rp["day2-input.txt"]{input} is a list of strings that represent dimensions of rectangular boxes. @link["http://adventofcode.com/day/2"]{The puzzle}. Our @link-rp["day2-input.txt"]{input} is a list of strings that represent dimensions of rectangular boxes.
@chunk[<day2> @chunk[<day2>
@ -22,6 +24,7 @@ Then we have a traditional setup for the devastating one-two punch of @racket[ma
@chunk[<day2-setup> @chunk[<day2-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
(define (string->boxes str) (define (string->boxes str)
(for/list ([ln (in-list (string-split str "\n"))]) (for/list ([ln (in-list (string-split str "\n"))])
(map string->number (string-split ln "x"))))] (map string->number (string-split ln "x"))))]

@ -3,6 +3,8 @@
@aoc-title[3] @aoc-title[3]
@defmodule[aoc-racket/day3]
@link["http://adventofcode.com/day/3"]{The puzzle}. 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. @link["http://adventofcode.com/day/3"]{The puzzle}. 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}. In essence, this a two-dimensional version of the elevator problem in @secref{Day_1}.
@ -25,6 +27,7 @@ Once the whole cell path is computed, the answer is found by removing duplicate
@chunk[<day3-setup> @chunk[<day3-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
] ]
@chunk[<day3-q1> @chunk[<day3-q1>

@ -4,6 +4,8 @@
@aoc-title[4] @aoc-title[4]
@defmodule[aoc-racket/day4]
@link["http://adventofcode.com/day/4"]{The puzzle}. Our @link-rp["day4-input.txt"]{input} is a string of eight characters that represents part of a key for making an MD5 hash. @link["http://adventofcode.com/day/4"]{The puzzle}. 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> @chunk[<day4>
@ -21,6 +23,7 @@ Whether or not you already know what an MD5 hash is, you can search the Racket d
@chunk[<day4-setup> @chunk[<day4-setup>
(require racket rackunit openssl/md5) (require racket rackunit openssl/md5)
(provide (all-defined-out))
] ]
@chunk[<day4-q1> @chunk[<day4-q1>

@ -3,6 +3,8 @@
@aoc-title[5] @aoc-title[5]
@defmodule[aoc-racket/day5]
@link["http://adventofcode.com/day/5"]{The puzzle}. Our @link-rp["day5-input.txt"]{input} is a list of random-looking but not really random text strings. @link["http://adventofcode.com/day/5"]{The puzzle}. Our @link-rp["day5-input.txt"]{input} is a list of random-looking but not really random text strings.
@chunk[<day5> @chunk[<day5>
@ -26,6 +28,7 @@ This is a job for @racket[regexp-match]. There's nothing tricky here (except for
@chunk[<day5-setup> @chunk[<day5-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
] ]
@chunk[<day5-q1> @chunk[<day5-q1>

@ -3,6 +3,8 @@
@aoc-title[6] @aoc-title[6]
@defmodule[aoc-racket/day6]
@link["http://adventofcode.com/day/6"]{The puzzle}. Our @link-rp["day6-input.txt"]{input} is a list of instructions for turning on (or off) the bulbs in a @racket[(* 1000 1000)] grid of lights. @link["http://adventofcode.com/day/6"]{The puzzle}. Our @link-rp["day6-input.txt"]{input} is a list of instructions for turning on (or off) the bulbs in a @racket[(* 1000 1000)] grid of lights.
@chunk[<day6> @chunk[<day6>
@ -47,6 +49,7 @@ We'll define our functions for setting and counting the lights separately, since
@chunk[<day6-setup> @chunk[<day6-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
(define (set-lights lights arglist) (define (set-lights lights arglist)
(match-define (list bulb-func x1 y1 x2 y2) arglist) (match-define (list bulb-func x1 y1 x2 y2) arglist)

@ -3,6 +3,8 @@
@aoc-title[7] @aoc-title[7]
@defmodule[aoc-racket/day7]
@link["http://adventofcode.com/day/7"]{The puzzle}. Our @link-rp["day7-input.txt"]{input} describes an electrical circuit, with each line of the file describing the signal provided to a particular wire. @link["http://adventofcode.com/day/7"]{The puzzle}. Our @link-rp["day7-input.txt"]{input} describes an electrical circuit, with each line of the file describing the signal provided to a particular wire.
@chunk[<day7> @chunk[<day7>
@ -45,6 +47,7 @@ One gotcha when using syntax transformers is that identifiers introduced by a tr
@chunk[<day7-setup> @chunk[<day7-setup>
(require racket rackunit (require racket rackunit
(for-syntax racket/base racket/file racket/string)) (for-syntax racket/base racket/file racket/string))
(provide (all-defined-out))
(define-syntax (convert-input-to-wire-functions stx) (define-syntax (convert-input-to-wire-functions stx)
(syntax-case stx () (syntax-case stx ()

@ -3,6 +3,8 @@
@aoc-title[8] @aoc-title[8]
@defmodule[aoc-racket/day8]
@link["http://adventofcode.com/day/8"]{The puzzle}. Our @link-rp["day8-input.txt"]{input} consists of a list of seemingly random strings within quotation marks. @link["http://adventofcode.com/day/8"]{The puzzle}. Our @link-rp["day8-input.txt"]{input} consists of a list of seemingly random strings within quotation marks.
@chunk[<day8> @chunk[<day8>
@ -19,6 +21,7 @@ The literal length of the string is trivial — use @racket[string-length]. The
@chunk[<day8-setup> @chunk[<day8-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
] ]
@chunk[<day8-q1> @chunk[<day8-q1>

@ -3,6 +3,8 @@
@aoc-title[9] @aoc-title[9]
@defmodule[aoc-racket/day9]
@link["http://adventofcode.com/day/9"]{The puzzle}. Our @link-rp["day9-input.txt"]{input} consists of a list of distances between fictional cities, e.g., @italic{AlphaCentauri to Straylight = 133}. @link["http://adventofcode.com/day/9"]{The puzzle}. Our @link-rp["day9-input.txt"]{input} consists of a list of distances between fictional cities, e.g., @italic{AlphaCentauri to Straylight = 133}.
@chunk[<day9> @chunk[<day9>
@ -23,6 +25,7 @@ In the second part, we'll loop through every possible path between the cities wi
@chunk[<day9-setup> @chunk[<day9-setup>
(require racket rackunit) (require racket rackunit)
(provide (all-defined-out))
(define distances (make-hash)) (define distances (make-hash))

@ -0,0 +1,3 @@
#lang racket/base
;; nothing at the top level