summaryrefslogtreecommitdiff
path: root/09/lib.scm
diff options
context:
space:
mode:
authorPrefetch2024-03-26 21:41:32 +0100
committerPrefetch2024-03-26 21:41:32 +0100
commit757b98fb30e75d7698a0986184d0303224db156c (patch)
treeb0f01bf59bb6fc7d26b55f6edd611393f8f8bf62 /09/lib.scm
parent1fbb07c54523c7a576bfff1cb689e155dd55f15a (diff)
Publish days 6-10
Diffstat (limited to '09/lib.scm')
-rw-r--r--09/lib.scm70
1 files changed, 70 insertions, 0 deletions
diff --git a/09/lib.scm b/09/lib.scm
new file mode 100644
index 0000000..5eac027
--- /dev/null
+++ b/09/lib.scm
@@ -0,0 +1,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)
+
+)