Files
advent-of-code-2021/18-snailfish/code.scm
2021-12-18 23:37:33 +01:00

148 lines
4.0 KiB
Scheme

(import (srfi :13)) ; string extensions
; returns a new string where all occurrences of `find` have been replaced by `replace`
(define (string-substitute str find replace)
(let [(len-find (string-length find))]
(let loop [(str str)]
(let [(index (string-contains str find))]
(if index
(loop (string-replace str replace index (+ index len-find)))
str)))))
; turns a snailfish number into a regular scheme list
(define (snailfish-number->list str)
; this feels like cheating x)
(get-datum
(open-string-input-port
(string-substitute str "," " "))))
; loads all the snailfish numbers from the given text port, one by line
(define (load-numbers port)
(let loop [(numbers '())]
(if (eof-object? (peek-char port))
(reverse numbers)
(loop (cons (snailfish-number->list (get-line port)) numbers)))))
; updates literal from snailfish number at given index
(define (update-literal! number index updater)
(let [(i 0)]
(let rec [(p number)]
(if (null? p)
i
(if (number? (car p))
(if (= i index)
(begin
(set-car! p (updater (car p)))
(set! i #f)) ; kill recursion
(begin
(set! i (+ i 1))
(rec (cdr p))))
(begin
(rec (car p))
(when i
(rec (cdr p)))))))))
; change literal at given index to be given value
(define (set-literal! number index value)
(update-literal! number index (lambda (v) value)))
; increment literal at given index by given value
(define (add-literal! number index value)
(update-literal! number index (lambda (v) (+ v value))))
; change the car of the cdr of l
(define (set-cadr! l v)
(set-car! (cdr l) v))
; explode snailfish number if possible
; returns #t if any number has exploded
(define (explode! input)
(let [(index 0)]
(let look [(parent '()) (set-parent! #f) (depth 0) (pair input)]
(if (not (number? pair))
(if (>= depth 4)
; explode
(let [(left (car pair))
(right (cadr pair))]
(add-literal! input (- index 1) left)
(add-literal! input (+ index 2) right)
(set-parent! parent 0)
(set! index #f)) ; kill recursion
(begin
(look pair set-car! (+ depth 1) (car pair))
(when index
(look pair set-cadr! (+ depth 1) (cadr pair)))))
; pair is actually a number
(set! index (+ index 1))))
(not index)))
; split snailfish number if possible
; returns #t if any number has been split
(define (split! input)
(let [(index 0)]
(let look [(pair input)]
(if (number? pair)
(if (>= pair 10)
(begin
; split
(set-literal! input index (list (div pair 2) (- pair (div pair 2))))
(set! index #f) ; kill recursion
(reduce! input))
(set! index (+ index 1)))
(begin
(look (car pair))
(when index ; check recursion hasn't been killed
(look (cadr pair))))))
(not index)))
; reduce snailfish number according to the rules of the problem
(define (reduce! input)
(if (explode! input)
(reduce! input)
(if (split! input)
(reduce! input)
input)))
; adds two snailfish numbers
; input will be corrupted!
(define (add! a b)
(reduce! (list a b)))
; compute magnitude of snailfish number
(define (magnitude n)
(if (number? n)
n
(+ (* 3 (magnitude (car n)))
(* 2 (magnitude (cadr n))))))
; deep copy given list
(define (duplicate lst)
(if (pair? lst)
(cons (duplicate (car lst)) (duplicate (cdr lst)))
lst))
; finds the largest magnitude obtained by summing any two numbers from the given list
(define (largest-magnitude numbers)
(let [(largest 0)]
(for-each
(lambda (n1)
(for-each
(lambda (n2)
(let* [(sum (add! (duplicate n1) (duplicate n2)))
(mag (magnitude sum))]
(when (> mag largest)
(set! largest mag))))
(remove n1 numbers)))
numbers)
largest))
(call-with-input-file
"input"
(lambda (file)
(let [(numbers (load-numbers file))]
(let [(numbers (duplicate numbers))]
(printf "part 1:~% Magnitude of final sum: ~a~%"
(magnitude (fold-left add! (car numbers) (cdr numbers)))))
(printf "part 2:~% Largest magnitude: ~a~%"
(largest-magnitude numbers)))))