#!/usr/local/bin/chicken-csi -ss (import srfi-69 (chicken string) (chicken io)) (define (n-of-v n v) (if (= 0 n) '() (cons v (n-of-v (sub1 n) v)))) (define (parse-line l) (let* ((parts (string-split l)) (v (string->symbol (car parts))) (n (string->number (cadr parts)))) (n-of-v n v))) (define (move pt dir) (let ((x (car pt)) (y (cadr pt))) (cond ((eq? dir 'U) (list x (add1 y))) ((eq? dir 'D) (list x (sub1 y))) ((eq? dir 'L) (list (sub1 x) y)) ((eq? dir 'R) (list (add1 x) y)) (else (error "Bad dir " dir))))) (define (adjacent? a b) (let ((xa (car a)) (ya (cadr a)) (xb (car b)) (yb (cadr b))) (and (<= (abs (- xa xb)) 1) (<= (abs (- ya yb)) 1)))) ; -10 -> -1, 4 -> 1, 0 -> 0 (define (unitize v) (if (= v 0) 0 (/ v (abs v)))) ; idea: head has moved, t to follow, returns new t ; (0 2) (0 0) -> (0 1) ; (1 3) (0 1) -> (1 2) ; if one dim is the same -> just move 1 in other dim ; if both dims diff -> move 1 in both dims ; IOW, move 1 in all directions changed (define (follow h t) (if (adjacent? h t) t (let* ((xh (car h)) (yh (cadr h)) (xt (car t)) (yt (cadr t)) (dx (- xh xt)) (dy (- yh yt))) (list (+ xt (unitize dx)) (+ yt (unitize dy)))))) (define (move-all dir knots tail-locs) ; only the head moves according to dir, everything else follows (let ((new-head (move (car knots) dir))) (let loop ((nh new-head) (tails (cdr knots))) (if (null? tails) (begin (hash-table-set! tail-locs nh 'seen) (list nh)) (if (adjacent? nh (car tails)) ; omptimization: can stop -- nothing else will move (cons nh tails) (cons nh (loop (follow nh (car tails)) (cdr tails)))))))) ;; NOTE: at first I was purely functional with cons-ing the ;; the h2,t2 to a history and then pulling unique t2's later ;; but that took 10s and it takes .2s to use a hash table :shrug: (define (play-out dirs knots tail-locs) (let loop ((knots knots) (dirs dirs)) (if (null? dirs) 'done (let ((ks (move-all (car dirs) knots tail-locs))) ; (newline) ; (plot-state ks) ; (newline) (loop ks (cdr dirs)))))) (define (main args) (let* ((dirs (flatten (map parse-line (read-lines)))) (tail-locs-1 (make-hash-table)) (tail-locs-2 (make-hash-table))) (hash-table-set! tail-locs-1 '(0 0) 'start) (hash-table-set! tail-locs-2 '(0 0) 'start) (play-out dirs (n-of-v 2 '(0 0)) tail-locs-1) (play-out dirs (n-of-v 10 '(0 0)) tail-locs-2) (print (hash-table-size tail-locs-1)) (print (hash-table-size tail-locs-2)))) ;; helpful for debugging! (define (plot-state knots) (print knots) (let loopi ((i -20)) (if (<= i 20) (let loopj ((j -20)) (if (<= j 20) (begin (if (member (list j (* -1 i)) knots) (display "#") (display ".")) (loopj (add1 j))) (begin (newline) (loopi (add1 i)))))))) ;; For repl (define slines '("R 4" "U 4" "L 3" "D 1" "R 4" "D 1" "L 5" "R 2"))