#lang scribble/lp2 @(require scribble/manual aoc-racket/helper) @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. @chunk[ ] @isection{What's the sum of all the numbers in the document?} I've never liked JavaScript, and spending more time with Racket has only deepened my antipathy. So I apologize if this solution is terse. We need to parse the JSON file, extract the numbers, and add them. To parse the file we'll use the @iracket[read-json] function from Racket's @racketmodname[json] library. This function converts the JSON into a JS-expression (see @iracket[jsexpr?]), which is a recursively nested data structure. If we had a simple recursively nested list, we could just @iracket[flatten] it and filter for the numbers. We'll do something similar here — recursively flatten the JS-expression and pull out the numbers. If you're new to Racket, notice the @italic{recursive descent} pattern used in @racket[flatten-jsexpr] — it's a very common way of handling recursively structured data. @chunk[ (require racket rackunit json) (provide (all-defined-out)) (define (string->jsexpr str) (read-json (open-input-string str))) ] @chunk[ (define (flatten-jsexpr jsexpr) (flatten (let loop ([x jsexpr]) (cond [(list? x) (map loop x)] [(hash? x) (loop (flatten (hash->list x)))] [else x])))) (define (q1 input-str) (define json-items (flatten-jsexpr (string->jsexpr input-str))) (apply + (filter number? json-items)))] @section{What's the sum of all the numbers, if hash tables with value @racket{red} are ignored?} We'll just update our flattening function to skip over hash tables that have @racket{red} among the values. @chunk[ (define (flatten-jsexpr-2 jsexpr) (flatten (let loop ([x jsexpr]) (cond [(list? x) (map loop x)] [(hash? x) (if (member "red" (hash-values x)) empty (loop (flatten (hash->list x))))] [else x])))) (define (q2 input-str) (define json-items (flatten-jsexpr-2 (string->jsexpr input-str))) (apply + (filter number? json-items))) ] @section{Testing Day 12} @chunk[ (module+ test (define input-str (file->string "day12-input.txt")) (check-equal? (q1 input-str) 191164) (check-equal? (q2 input-str) 87842))]