If you have a lisp installation, emacs, org-mode, and org-babel support for lisp installed you can run this by:
- Starting slime (
M-x slime
) - Typing
C-c C-c
in the block initialize. - In the repl type
(in-package :aoc-2020-11)
- Typing
C-c C-c
in the block answers
(unless (find-package :cl-ppcre)
(ql:quickload "cl-ppcre"))
(unless (find-package :iterate)
(ql:quickload "iterate"))
(unless (find-package :parseq)
(ql:quickload "parseq"))
(unless (find-package :fiveam)
(ql:quickload "fiveam"))
(unless (find-package :series)
(ql:quickload "series"))
(unless (find-package :cl-permutation)
(ql:quickload "cl-permutation"))
(unless (find-package :bordeaux-threads)
(ql:quickload "bordeaux-threads"))
<<packages>>
(defpackage :aoc-2020-11
(:use :common-lisp
:iterate
:parseq
:fiveam)
(:export :problem-a
:problem-b))
(in-package :aoc-2020-11)
(defun read-input (file)
(iter (for line in-file file using #'read-line)
(collect line)))
(defparameter *input*
(read-input "input/11.txt"))
A Game of Life styled problem. The rules:
- If a seat is empty and count of adjacent occupied seats = 0, it becomes occupied.
- If the seat is occupied and 4 or more adjacent seats are occupied it becomes empty.
- Otherwise no change.
(defun to-grid (lines)
(let ((grid (make-hash-table)))
(loop for l in lines
for j from 0
do (loop for c across l
for i from 0
do (setf (gethash (complex i j) grid) c)))
grid))
(defun print-grid (grid)
(loop for j from 0
while (gethash j grid)
do (loop for i from 0
while (gethash (complex i j) grid)
do (format t "~A" (gethash (complex i j) grid)))
(format t "~%")))
I’ll use a double buffer approach. Create a new grid with all the updated cells, then replace the old one. Repeat.
(defun next-empty (location grid)
(loop for i in '(#C(1 1) #C(1 0)
#C(0 1) #C(0 -1)
#C(-1 1) #C(-1 0)
#C(-1 -1) #C(1 -1))
count (char= #\# (gethash (+ location i) grid #\.)) into tally
finally (return (if (zerop tally) #\# #\L))))
(defun next-occupied (location grid)
(loop for i in '(#C(1 1) #C(1 0)
#C(0 1) #C(0 -1)
#C(-1 1) #C(-1 0)
#C(-1 -1) #C(1 -1))
count (char= #\# (gethash (+ location i) grid #\.)) into tally
finally (return (if (<= 4 tally) #\L #\#))))
(defun next-grid (grid)
(let ((next (make-hash-table)))
(loop for k being each hash-key of grid using (hash-value v)
do (case v
(#\. (setf (gethash k next) v))
(#\L (setf (gethash k next) (next-empty k grid)))
(#\# (setf (gethash k next) (next-occupied k grid))))
count (char/= v (gethash k next)) into changed
finally (return (values next changed)))))
(defun count-occupied (grid)
(loop for v being the hash-values of grid
count (char= #\# v)))
(defun find-stable-point (grid)
(loop
for i from 1
do (multiple-value-bind (next changed)
(next-grid grid)
(setf grid next)
(when (zerop changed)
(return (count-occupied grid))))))
(defun problem-a () (format t "Problem 11 A: ~a~%" (find-stable-point (to-grid *input*))))
Ok, so it’s basically the same idea, however our rules change a bit. They see further than just the adjacent spaces. Change the tally to look as far in the direction as is visible, find the first seat, and use its state to determine the counts.
(defun first-chair-in-direction (location direction grid)
(loop for i from 1
for pos = (+ location (* i direction))
for c = (gethash pos grid)
while (and c
(char= #\. c))
finally (return (or c #\.))))
(defun next-empty-visibility (location grid)
(loop for i in '(#C(1 1) #C(1 0)
#C(0 1) #C(0 -1)
#C(-1 1) #C(-1 0)
#C(-1 -1) #C(1 -1))
count (char= #\# (first-chair-in-direction location i grid)) into tally
finally (return (if (zerop tally) #\# #\L))))
(defun next-occupied-visibility (location grid)
(loop for i in '(#C(1 1) #C(1 0)
#C(0 1) #C(0 -1)
#C(-1 1) #C(-1 0)
#C(-1 -1) #C(1 -1))
count (char= #\# (first-chair-in-direction location i grid))into tally
finally (return (if (<= 5 tally) #\L #\#))))
(defun next-grid-visibility (grid)
(let ((next (make-hash-table)))
(loop for k being each hash-key of grid using (hash-value v)
do (case v
(#\. (setf (gethash k next) v))
(#\L (setf (gethash k next) (next-empty-visibility k grid)))
(#\# (setf (gethash k next) (next-occupied-visibility k grid))))
count (char/= v (gethash k next)) into changed
finally (return (values next changed)))))
No regrets about the copy/paste. I could make the next-grid
function
take keyword parameters to determin what to call for each input, but
this got it done.
Dummy me, though, forgot to update the 4 to a 5 for
next-occupied
. Reading comprehension fail there on my part.
(defun find-stable-point-visibility (grid)
(loop
for i from 1
do (multiple-value-bind (next changed)
(next-grid-visibility grid)
(setf grid next)
(when (zerop changed)
(return (count-occupied grid))))))
(defun problem-b () (format t "Problem 11 B: ~a~%" (find-stable-point-visibility (to-grid *input*))))
<<read-input>>
<<input>>
<<to-grid>>
<<initialize>>
<<structs>>
<<functions>>
<<input>>
<<problem-a>>
<<problem-b>>
(problem-a)
(problem-b)
Problem 11 A: 2448 Problem 11 B: 2234
(def-suite aoc.2020.11)
(in-suite aoc.2020.11)
(run! 'aoc.2020.11)
Simple runner.
with AOC2020.Day11;
procedure Day11 is
begin
AOC2020.Day11.Run;
end Day11;
Specification for solution.
package AOC2020.Day11 is
procedure Run;
end AOC2020.Day11;
with Text_IO; use Text_IO;
with Ada.Text_IO.Unbounded_IO;
use Ada.Text_IO.Unbounded_IO;
procedure Read_File (Grid : out Cell_Array) is
Fin : File_Type;
Line : String (1..99);
Length : Natural;
Y : Height_Range := 1;
begin
for Row in Height_Range'Range loop
Grid := (Width_Range => (others => Floor));
end loop;
Open (Fin, In_File, "../input/11.txt");
while not End_Of_File (Fin) loop
Get_Line(Fin, Line, Length);
Put_Line(Length'Image);
for X in 1..98 loop
case Line(X) is
when '.' => Grid (X, Y) := Floor;
when '#' => Grid (X, Y) := Occupied;
when 'L' => Grid (X, Y) := Empty;
when others => null;
end case;
end loop;
end loop;
Close (Fin);
end Read_File;
I’m going to create a type to represent the cell types, in good Ada fashion. The grid will be represented by a 2d array of cells. I’m going to cheat a bit and hardcode the sizes. I know it’s a 98x97 (w x h) grid. I’ll pad it with 2 on either side.
type Cell_Type is (Empty, Occupied, Floor);
subtype Height_Range is Integer range 0..98;
subtype Width_Range is Integer range 0..99;
type Cell_Array is array (Width_Range, Height_Range) of Cell_Type;
Actual implementation body.
<<ada-packages>>
package body AOC2020.Day11 is
<<types-and-generics>>
<<ada-read-input>>
procedure Run is
Grid : Cell_Array;
begin
Read_File (Grid);
Put_Line ("Advent of Code 2020 - Day 11");
Put_Line ("The result for Part 1 is " & Integer'Image(0));
Put_Line ("The result for Part 2 is " & Integer'Image(0));
end Run;
end AOC2020.Day11;
In order to run this you have to “tangle” the code first using C-c
C-v C-t
.
cd ada
gnatmake day11
./day11