summaryrefslogtreecommitdiff
path: root/09/lib.scm
blob: 5eac027a8b02d01ca32bb65a027616b2ead19532 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
(library (lib)
  (export solve-part1 solve-part2)
  (import (chezscheme))

  ; Global state... a sin, but very convenient in this case!
  (define chars '())     ; Characters still to be parsed
  (define part1-total 0) ; Total score of groups in input
  (define part2-total 0) ; Total non-escaped garbage chars

  ; Is the next character in the stream equal to `c'?
  (define (peek? c)
    (char=? c (car chars)))

  ; Blindly accept character and pop it. We do all detection
  ; via `peek?', so we don't care what gets discarded here.
  (define (next)
    (set! chars (cdr chars)))

  ; Parse a "group". We can assume all input is well-formed,
  ; which really helps keep this code as simple as possible.
  (define (parse-group depth)
    (next) ; Accept initial #\{
    (let loop ()
      ; Expect start of "group" or "garbage"
      (cond
        ((peek? #\{)
         (parse-group (+ depth 1)))
        ((peek? #\<)
         (parse-garbage)))
      ; Expect comma or end of current "group"
      (cond
        ((peek? #\,)
         (next)  ; Accept #\,
         (loop)) ; Parse next "group"/"garbage"
        ((peek? #\})
         ; Add current group's nesting depth to total score
         (set! part1-total (+ part1-total depth))
         (next))))) ; Accept #\} and return

  ; Parse "garbage": everything until next non-escaped #\>.
  (define (parse-garbage)
    (let loop ()
      (next) ; Accept char (#\< at first)
      (cond
        ((peek? #\>)
          (next)) ; Accept #\> and return
        ((peek? #\!)
          (next)  ; Accept #\!
          (loop)) ; Continue to escaped char
        (else
          ; Add this character to the total count
          (set! part2-total (+ part2-total 1))
          (loop)))))

  ; Reset global parser state and parse the input string
  (define (solve-puzzle str)
    (set! chars (string->list str))
    (set! part1-total 0)
    (set! part2-total 0)
    (parse-group 1))

  (define (solve-part1 str)
    (solve-puzzle str)
    part1-total)

  (define (solve-part2 str)
    (solve-puzzle str)
    part2-total)

)