(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)))))