diff --git a/aoc-racket.scrbl b/aoc-racket.scrbl index eab9f33..25987ed 100644 --- a/aoc-racket.scrbl +++ b/aoc-racket.scrbl @@ -18,4 +18,5 @@ @include-section[(submod "day7.scrbl" doc)] @include-section[(submod "day8.scrbl" doc)] @include-section[(submod "day9.scrbl" doc)] -@include-section[(submod "day10.scrbl" doc)] \ No newline at end of file +@include-section[(submod "day10.scrbl" doc)] +@include-section[(submod "day11.scrbl" doc)] \ No newline at end of file diff --git a/day11-input.txt b/day11-input.txt new file mode 100644 index 0000000..21b8e33 --- /dev/null +++ b/day11-input.txt @@ -0,0 +1 @@ +hxbxwxba \ No newline at end of file diff --git a/day11.scrbl b/day11.scrbl new file mode 100644 index 0000000..08642a7 --- /dev/null +++ b/day11.scrbl @@ -0,0 +1,99 @@ +#lang scribble/lp2 +@(require scribble/manual aoc-racket/helper) + +@aoc-title[11] + +@link["http://adventofcode.com/day/1q"]{The puzzle}. Our @link-rp["day11-input.txt"]{input} is a short alphabetic key that represents a password. + +@chunk[ + + + + ] + +@section{What's the next password that meets the criteria?} + +Though the password is alphabetic, we can increment it as we would a numerical password, by changing the rightmost letter to the next letter (for instance @litchar{x} to @litchar{y}, @litchar{y} to @litchar{z}). When we reach @litchar{z}, we roll over to @litchar{a}, and ``carry over'' the surplus by incrementing the letter to the left. + +Furthermore, like @secref{Day_5}, the puzzle provides certain criteria that must be met: + +@itemlist[ + @item{The password must have a sequence of three consecutive letters (like @litchar{bcd}).} + + @item{The password may not contain @litchar{i}, @litchar{o}, or @litchar{l}.} + + @item{The password must contain two different, non-overlapping pairs of letters.} + ] + +As in @secref{Day_5}, we'll use @racket[regexp-match] to implement tests for these conditions. We'll also use @racket[regexp-replace*] to build the function that increments a password alphabetically. Then it's a simple matter of looking at passwords until we find one that works. + +The @racket[increment-password] function works by using the observation that if the password ends in any number of @litchar{z}s, you have to roll them over to @litchar{a} and increment the letter to the left. Otherwise, you can just increment the last letter — which is actually the same rule, but with zero @litchar{z}s. This logic can all be captured in one regular expression — @racket[#rx"^(.*?)(.)(z*)$"]. + +The @racket[three-consecutive-letters?] test works by converting the letters to numbers and creating a list of the differences betweeen adjacent values. Any three consecutive letters will differ by value of @racket[1]. So if the list of differences contains the subsequence @racket['(1 1)], then the string has three consecutive letters. + + +@chunk[ + (require racket rackunit) + + (define (increment-password password) + (define (increment-letter c) + ((compose1 ~a integer->char add1 char->integer car string->list) c)) + + (match-define (list _ prefix letter-to-increment trailing-zs) + (regexp-match #rx"^(.*?)(.)(z*)$" password)) + + (string-append* (list prefix (increment-letter letter-to-increment) + (regexp-replace* #rx"z" trailing-zs "a")))) + + (define (three-consecutive-letters? str) + (define ints (map char->integer (string->list str))) + (let loop ([differences (map - (cdr ints) (drop-right ints 1))]) + (if (empty? differences) + #f + (or (list-prefix? '(1 1) differences) (loop (cdr differences)))))) + + (define (no-iol? str) + (not (regexp-match #rx"[iol]" str))) + + (define (two-nonoverlapping-doubles? str) + (regexp-match #px"(\\w)\\1.*?(\\w)\\2" str)) + + (define (valid? password) + (and (three-consecutive-letters? password) + (no-iol? password) + (two-nonoverlapping-doubles? password))) + + (define (find-next-valid-password starting-password) + (define candidate-pw (increment-password starting-password)) + (if (valid? candidate-pw) + candidate-pw + (find-next-valid-password candidate-pw))) + + ] + +@chunk[ + + (define (q1 input-key) + (find-next-valid-password input-key))] + + + +@section{What's the next valid password after that?} + +We take the answer to question 1 and use it as input to the same function. + +@chunk[ + + (define (q2 input-key) + (find-next-valid-password (q1 input-key))) ] + + +@section{Testing Day 10} + +@chunk[ + (module+ test + (define input-key (file->string "day11-input.txt")) + (check-equal? (q1 input-key) "hxbxxyzz") + (check-equal? (q2 input-key) "hxcaabcc"))] + + diff --git a/day7.scrbl b/day7.scrbl index cda6ba8..337c562 100644 --- a/day7.scrbl +++ b/day7.scrbl @@ -18,19 +18,21 @@ The first question we should ask is — how do we model a wire? We're told that In other languages, creating functions from text strings would be a difficult trick. But this facility is built into Racket with @racket[define-syntax]. Essentially our program will run in two phases: in the syntax-transformation phase, we'll read in the list of wire descriptions and expand them into code that represents functions. In the second phase, the program — including our new functions, created via syntax transformation — will compile & run as usual. -The @racket[convert-input-to-wire-functions] syntax transformer takes the input strings and converts them into syntax that looks more like Racket code, so that a line like +The @racket[convert-input-to-wire-functions] transformer takes the input strings and first converts each into a @italic{datum} — that is, a fragment of Racket code. So an input string like this: @racket["bn RSHIFT 2 -> bo"] -becomes +becomes a datum like this: @racket[(wire bn RSHIFT 2 -> bo)] -Then the @racket[wire] transformer moves the arguments around to define functions, by matching the three definition patterns that appear in the input. Thus, syntax like +Next, this transformer converts the datums into @italic{syntax}, a process that adds contextual information (for instance, the meanings of identifiers) so the code can be evaluated. + +Then the @racket[wire] transformer moves the arguments around to define functions, by matching the three definition patterns that appear in the input. Thus, syntax like this: @racket[(wire bn RSHIFT 2 -> bo)] -becomes +becomes: @racket[(define (bo) (RSHIFT (evaluate-arg bn) (evaluate-arg 2)))]