148 lines
4.0 KiB
Scheme
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)))))
|