master-blaster
Matthew Butterick 9 years ago
parent 31a9f73acb
commit adf41e9eaf

@ -18,3 +18,4 @@
@include-section[(submod "day7.scrbl" doc)]
@include-section[(submod "day8.scrbl" doc)]
@include-section[(submod "day9.scrbl" doc)]
@include-section[(submod "day10.scrbl" doc)]

@ -0,0 +1 @@
1321131112

@ -0,0 +1,63 @@
#lang scribble/lp2
@(require scribble/manual aoc-racket/helper)
@aoc-title[10]
@link["http://adventofcode.com/day/10"]{The puzzle}. Our @link-rp["day10-input.txt"]{input} is a short numeric key.
@chunk[<day10>
<day10-setup>
<day10-q1>
<day10-q2>
<day10-test>]
@section{What's the length of the sequence after 40 iterations?}
The puzzle asks us to compute the @italic{look and say} sequence invented by mathematician John Conway. Each iteration of the sequence is the description of the last step if you said it in numbers. So @racket[1] becomes ``one 1'', written @racket[11]; @racket[11] becomes ``two ones'', or @racket[21], then @racket[1211], @racket[111221], and so on.
As in @secref{Day_1}, this puzzle relies on cumulative state, so we'll loop using @racket[for/fold]. To generate the new string for each pass of the loop, we'll use @racket[regexp-match*] to find every contiguous run of digits. Each digit run will be converted into a list with the number of digits and then digit. Then all these lists of numbers are concatenated into a new string, and the process repeats.
The second part of the puzzle is just going to change the number of iterations. So we'll make one function that we can use for both parts.
@chunk[<day10-setup>
(require racket rackunit)
(define (look-and-say iterations input-key)
(for/fold ([start input-key])
([i (in-range iterations)])
(define digit-runs (regexp-match* #px"(\\d)\\1*" start))
(string-append*
(map ~a
(append-map (λ(digit-run)
(list (string-length digit-run)
(substring digit-run 0 1)))
digit-runs)))))
]
@chunk[<day10-q1>
(define (q1 input-key)
(string-length (look-and-say 40 input-key)))]
@section{After 50 iterations?}
We just run the same @racket[look-and-say] function 50 times rather than 40.
@chunk[<day10-q2>
(define (q2 input-key)
(string-length (look-and-say 50 input-key))) ]
@section{Testing Day 10}
@chunk[<day10-test>
(module+ test
(define input-key (file->string "day10-input.txt"))
(check-equal? (q1 input-key) 492982)
(check-equal? (q2 input-key) 6989950))]

@ -38,7 +38,7 @@ becomes
(@racket[wire-value-cache] is just a performance enhancement, so that wire values don't have to be computed multiple times.)
One gotcha when using syntax transformers is that newly defined identifiers can mask others. For instance, one of the wires in our input is named @tt{if}. When our syntax transformer defines the @tt{if} function, it will override the usual meaning of @racket[if]. There are plenty of elegant ways to prevent these name collisions. (The most important of which is called @italic{syntax hygiene}, and permeates the design of Racket's syntax-transformation system.) But because this is a puzzle, we'll take the cheap way out: we won't use @racket[if] elsewhere in our code, and instead use @racket[cond].
One gotcha when using syntax transformers is that identifiers introduced by a transformer can silently override others (in the same way that identifiers defined inside a @racket[let] will override those with the same name outside the @racket[let]). For instance, one of the wires in our input is named @tt{if}. When our syntax transformer defines the @tt{if} function, it will override the usual meaning of @racket[if]. There are plenty of elegant ways to prevent these name collisions. (The most important of which is called @italic{syntax hygiene}, and permeates the design of Racket's syntax-transformation system.) But because this is a puzzle, we'll take the cheap way out: we won't use @racket[if] elsewhere in our code, and instead use @racket[cond].
@chunk[<day7-setup>
(require racket rackunit