$ cat 11a.rkt
#lang racket
(require srfi/25)
(provide
example-input
new-octopus-garden
problem-input
step
)
(define flash-threshold 9)
(define energy-floor 0)
(define (aoc-11a input)
(let ((octopuses (new-octopus-garden input)))
(let loop ((i 0) (flashes 0))
(if (= i 100)
flashes
(loop (+ i 1) (+ flashes (step octopuses)))))))
(define (flash-octopuses octopuses)
(define (flash)
(let ((flash-count 0))
(octopuses 'for-each-octopus
(lambda (r c v)
(when (and (not (octopuses 'flashed? r c))
(> v flash-threshold))
(octopuses 'flashed-set! r c #t)
(octopuses 'for-each-neighbor r c
(lambda (r c v)
(octopuses 'energy-set! r c (+ v 1))))
(set! flash-count (+ flash-count 1)))))
flash-count))
(let loop ((flash-count 0))
(let ((flashes (flash)))
(if (zero? flashes)
flash-count
(loop (+ flash-count flashes))))))
(define (increase-energy-levels octopuses)
(octopuses 'for-each-octopus
(lambda (r c v)
(octopuses 'energy-set! r c (+ v 1)))))
(define (new-octopus-garden input)
(define (read-input input)
(let* ((zero (char->integer #\0))
(ord (lambda (c) (- (char->integer c) zero)))
(digify (lambda (s) (map ord (string->list s))))
(data (map digify (string-split input)))
(rows (length data)))
(values
rows
(if (zero? rows) 0 (length (car data))) data)))
(let-values (((rows cols data) (read-input input)))
(define (all-valid-neighbors r c neighbors)
(define (valid-neighbor nbr)
(let ((nr (car nbr))
(nc (cdr nbr)))
(and (<= 0 nr) (< nr rows)
(<= 0 nc) (< nc cols)
(or (not (= nr r)) (not (= nc c))))))
(filter (lambda (nbr) (valid-neighbor nbr)) neighbors))
(define (all-neighbors r c)
(let r-loop ((delta-r -1) (neighbors '()))
(if (> delta-r 1)
neighbors
(let ((new-r (+ r delta-r)))
(let c-loop ((delta-c -1) (nbrs neighbors))
(if (> delta-c 1)
(r-loop (+ delta-r 1) nbrs)
(c-loop (+ delta-c 1)
(cons
(cons new-r (+ c delta-c))
nbrs))))))))
(let*
((ranges (shape 0 rows 0 cols))
(energy (apply array (cons ranges (flatten data))))
(flashed (make-array ranges #f))
(call-f
(lambda (f r c) (f r c (array-ref energy r c))))
(set-array-value
(lambda (array args)
(apply array-set! (cons array args))))
(ref-array-value
(lambda (array args)
(apply array-ref (cons array args))))
(error
(lambda args
(apply raise-user-error (cons 'octopus-garden args)))))
(lambda (cmd . args)
(case (list 'quote cmd)
(('energy-set!)
(set-array-value energy args))
(('flashed?)
(ref-array-value flashed args))
(('flashed-set!)
(set-array-value flashed args))
(('for-each-neighbor)
(let ((r (car args))
(c (cadr args))
(f (caddr args)))
(map (lambda (c) (call-f f (car c) (cdr c)))
(all-valid-neighbors r c (all-neighbors r c)))))
(('for-each-octopus)
(let ((f (car args)))
(do ((r 0 (+ r 1))) ((= r rows) (void))
(do ((c 0 (+ c 1))) ((= c cols) (void))
(call-f f r c)))))
(('population)
(* rows cols))
(else
(error "Unrecognized command ~a" cmd)))))))
(define (reset-flashed-octopuses octopuses)
(octopuses 'for-each-octopus
(lambda (r c v)
(when (octopuses 'flashed? r c)
(octopuses 'energy-set! r c energy-floor)
(octopuses 'flashed-set! r c #f)))))
(define (step octopuses)
(increase-energy-levels octopuses)
(let ((flashes (flash-octopuses octopuses)))
(unless (zero? flashes)
(reset-flashed-octopuses octopuses))
flashes))
(define problem-input
(string-join '(
"4871252763"
"8533428173"
"7182186813"
"2128441541"
"3722272272"
"8751683443"
"3135571153"
"5816321572"
"2651347271"
"7788154252"
)
"\n"))
(module+ main
(aoc-11a problem-input))
(define example-input
(string-join '(
"5483143223"
"2745854711"
"5264556173"
"6141336146"
"6357385478"
"4167524645"
"2176841721"
"6882881134"
"4846848554"
"5283751526"
)
"\n"))
(module+ test
(require rackunit)
(check-eq? (aoc-11a "") 0)
(check-eq? (aoc-11a "0") 10)
(check-eq? (aoc-11a "000\n000\n000") 90)
(check-eq? (aoc-11a example-input) 1656)
(check-eq? (aoc-11a problem-input) 1747))
$ raco test 11a.rkt
raco test: (submod "11a.rkt" test)
5 tests passed
$