94 lines
3.2 KiB
Common Lisp
94 lines
3.2 KiB
Common Lisp
(load (merge-pathnames "../common.lisp" *load-truename*))
|
|
|
|
(defconstant tile-floor #\.)
|
|
(defconstant tile-empty #\L)
|
|
(defconstant tile-taken #\#)
|
|
|
|
(defun parse-input (input)
|
|
(make-array (list (length input) (length (car input)))
|
|
:initial-contents input))
|
|
|
|
(defconstant test-data
|
|
(parse-input '("L.LL.LL.LL"
|
|
"LLLLLLL.LL"
|
|
"L.L.L..L.."
|
|
"LLLL.LL.LL"
|
|
"L.LL.LL.LL"
|
|
"L.LLLLL.LL"
|
|
"..L.L....."
|
|
"LLLLLLLLLL"
|
|
"L.LLLLLL.L"
|
|
"L.LLLLL.LL")))
|
|
|
|
(defun safe-at (state x y max-x max-y &key (default tile-floor))
|
|
(if (and (>= x 0) (>= y 0) (< x max-x) (< y max-y))
|
|
(aref state x y)
|
|
default))
|
|
|
|
(defun count-neighbours (state x y neighbour)
|
|
(destructuring-bind (width height) (array-dimensions state)
|
|
(loop for i in '(-1 0 1)
|
|
sum (loop for j in '(-1 0 1)
|
|
count (and (not (and (zerop i) (zerop j)))
|
|
(eq tile-taken (funcall neighbour state x y i j width height)))))))
|
|
|
|
(defun neighbour-at (state x y i j max-x max-y)
|
|
(safe-at state (+ x i) (+ y j) max-x max-y))
|
|
|
|
(defun closest-in-direction (state x y dir-x dir-y max-x max-y)
|
|
(let* ((next-x (+ x dir-x)) (next-y (+ y dir-y))
|
|
(next (safe-at state next-x next-y max-x max-y :default NIL)))
|
|
(if (eq next tile-floor)
|
|
(closest-in-direction state next-x next-y dir-x dir-y max-x max-y)
|
|
next)))
|
|
|
|
(defun next-cell-state-changed (state x y neighbour limit)
|
|
(let ((current (aref state x y)))
|
|
(when (not (eq current tile-floor))
|
|
(let ((neighbours (count-neighbours state x y neighbour)))
|
|
(or (and (char= current tile-empty) (zerop neighbours))
|
|
(and (char= current tile-taken) (>= neighbours limit)))))))
|
|
|
|
(defun next-state-delta (state max-x max-y neighbour limit)
|
|
(loop for i from 0 to (1- max-x)
|
|
append (loop for j from 0 to (1- max-y)
|
|
when (next-cell-state-changed state i j neighbour limit)
|
|
collect (list i j))))
|
|
|
|
(defun flip-state (state x y)
|
|
(setf (aref state x y)
|
|
(case (aref state x y)
|
|
(#\. tile-floor)
|
|
(#\L tile-taken)
|
|
(#\# tile-empty))))
|
|
|
|
(defun next-state (state delta)
|
|
(loop for pos in delta do
|
|
(flip-state state (car pos) (cadr pos))))
|
|
|
|
(defun until-stable (state neighbour limit)
|
|
(destructuring-bind (width height) (array-dimensions state)
|
|
(loop as delta = (next-state-delta state width height neighbour limit)
|
|
until (null delta)
|
|
do (next-state state delta)
|
|
finally (return state))))
|
|
|
|
(defun count-taken (state)
|
|
(destructuring-bind (width height) (array-dimensions state)
|
|
(loop for x from 0 to (1- width)
|
|
sum (loop for y from 0 to (1- height)
|
|
count (char= tile-taken (safe-at state x y width height))))))
|
|
|
|
(defun solution (input count limit)
|
|
(count-taken (until-stable input count limit)))
|
|
|
|
(defun part1 (input)
|
|
(solution input #'neighbour-at 4))
|
|
|
|
(defun part2 (input)
|
|
(solution input #'closest-in-direction 5))
|
|
|
|
(let ((input (read-data)))
|
|
(format t "Part 1: ~A~%" (part1 (parse-input input)))
|
|
(format t "Part 2: ~A~%" (part2 (parse-input input))))
|