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-2021-09)
- 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 :lparallel)
(ql:quickload "lparallel"))
(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-2021-09
(:use :common-lisp
:parseq
:fiveam)
(:export :problem-a
:problem-b))
(in-package :aoc-2021-09)
(defun read-input (file)
(with-open-file (in file)
(loop
for line = (read-line in nil)
while line
collect line)))
(defparameter *input*
(read-input "input/09.txt"))
(defun make-grid (lines)
(loop
with grid = (make-hash-table)
for line in lines
for i from 0
finally (return grid)
do (loop
for c across line
for j from 0
do (setf (gethash (complex i j) grid) (- (char-code c) (char-code #\0))))))
(defun score-grid (grid)
(loop
with score = 0
for pos being the hash-keys of grid using (hash-value v)
finally (return score)
if (loop
for offset in '(#C(0 1) #C(0 -1) #C(1 0) #C(-1 0))
always (< v (gethash (+ offset pos) grid 10)))
do (incf score (1+ v))))
(defun problem-a () (format t "Problem 09 A: ~a~%" (score-grid (make-grid *input*))))
Now instead of finding the low point, we need to find all “basins” basins are unique areas, below 9, that all flow to a common low point.
If I understand the example correctly, basins are always surrounded by 9s. What I’ll do is find each low point, and search outward.
(defun low-point-p (grid pos)
(loop
with v = (gethash pos grid)
for offset in '(#C(0 1) #C(0 -1) #C(1 0) #C(-1 0))
always (< v (gethash (+ offset pos) grid 10))))
(defun neighbors (pos)
(loop for offset in '(#C(0 1) #C(0 -1) #C(1 0) #C(-1 0))
collect (+ pos offset)))
(defun get-basin-size (grid pos)
(loop
with visited = (list pos)
with basin = (list pos)
with next = (neighbors pos)
finally (return (length basin))
for current = (pop next)
for height = (gethash current grid 9)
do (push current visited)
if (< height 9)
do
(push current basin)
(let ((neighbors (neighbors current)))
(loop for n in neighbors
unless (member n visited)
do (pushnew n next)))
while next))
(defun find-basin-sizes (grid)
(loop
with basins = nil
for pos being the hash-keys of grid
if (low-point-p grid pos)
do (push (get-basin-size grid pos) basins)
finally (return (subseq (sort basins #'>) 0 3))))
(defun problem-b () (format t "Problem 09 B: ~a~%" (apply #'* (find-basin-sizes (make-grid *input*)))))
<<read-input>>
<<input>>
<<chars-to-grid>>
<<basins>>
<<initialize>>
<<structs>>
<<functions>>
<<input>>
<<problem-a>>
<<problem-b>>
(problem-a)
(problem-b)
Problem 09 A: 417 Problem 09 B: 1148965
(def-suite aoc.2021.09)
(in-suite aoc.2021.09)
(run! 'aoc.2021.09)
Simple runner.
with AOC2021.Day09;
procedure Day09 is
begin
AOC2021.Day09.Run;
end Day09;
Specification for solution.
package AOC2021.Day09 is
procedure Run;
end AOC2021.Day09;
with Text_IO; use Text_IO;
with Ada.Containers.Vectors;
with Ada.Containers; use Ada.Containers;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO;
package String_Vectors is new Ada.Containers.Vectors
(Element_Type => Unbounded_String, Index_Type => Positive);
use String_Vectors;
type Height_Type is new Integer range 0..9;
type Height_Map_Array is array(Natural range <>, Natural range <>) of Height_Type;
<<ada-packages>>
package body AOC2021.Day09 is
<<types-and-generics>>
function Is_Lowest_Point(I, J: Natural; Height_Map: Height_Map_Array) return Boolean is
(for all X in -1..1 => X = 0 or
(Height_Map(I,J) < Height_Map(I + X, J)
and Height_Map(I,J) < Height_Map(I, J + X)));
function Part_1(Height_Map: Height_Map_Array) return Integer is
Count : Integer := 0;
begin
for I in 1..(Height_Map'Last(1)-1) loop
for J in 1..(Height_Map'Last(2)-1) loop
if Is_Lowest_Point(I, J, Height_Map) then
Count := Count + 1 + Integer(Height_Map(I,J));
end if;
end loop;
end loop;
return Count;
end Part_1;
function Basin_Crawl (I, J: Natural; Height_Map: in out Height_Map_Array) return Integer is
Total : Integer := 0;
begin
if Height_Map(I, J) = 9
then
return 0;
end if;
Height_Map(I, J) := 9;
Total := Total + Basin_Crawl(I + 1, J, Height_Map);
Total := Total + Basin_Crawl(I - 1, J, Height_Map);
Total := Total + Basin_Crawl(I, J + 1, Height_Map);
Total := Total + Basin_Crawl(I, J - 1, Height_Map);
return Total + 1;
end Basin_Crawl;
function Part_2(Height_Map: in out Height_Map_Array) return Integer is
Biggest : array (1..3) of Integer := (others => 0);
Temp : Integer := 0;
Current_Basin : Integer := 0;
begin
for I in 1..(Height_Map'Last(1)-1) loop
for J in 1..(Height_Map'Last(2)-1) loop
if Is_Lowest_Point(I, J, Height_Map) then
Current_Basin := Basin_Crawl(I, J, Height_Map);
for I in 1..3 loop
if Biggest(I) < Current_Basin then
Temp := Biggest(I);
Biggest(I) := Current_Basin;
Current_Basin := Temp;
end if;
end loop;
end if;
end loop;
end loop;
return Biggest(1) * Biggest(2) * Biggest(3);
end Part_2;
function Make_Map (Lines: String_Vectors.Vector) return Height_Map_Array is
Height_Map : Height_Map_Array(0..(Natural(Lines.Length)+1),
0..(Length(Lines(1)) + 1))
:= (others => (others => 9));
begin
for I in 1..Natural(Lines.Length) loop
declare
Line : Unbounded_String := Lines(I);
begin
for J in 1..Length(Lines(1)) loop
Height_Map(I, J) := Height_Type'Value(Slice(Line,J,J));
end loop;
end;
end loop;
return Height_Map;
end Make_Map;
procedure Read_File (Filename: String; Lines: out String_Vectors.Vector) is
File : File_Type;
Line : Unbounded_String;
begin
Open (File, In_File, Filename);
while not End_Of_File (File) loop
Get_Line(File, Line);
Lines.Append(Line);
end loop;
Close (File);
null;
end Read_File;
procedure Run is
Lines: String_Vectors.Vector;
P1 : Integer := 0;
P2 : Integer := 0;
begin
Read_File ("../input/09.txt", Lines);
declare
Height_Map : Height_Map_Array := Make_Map(Lines);
begin
P1 := Part_1(Height_Map);
P2 := Part_2(Height_Map);
end;
Put_Line("Advent of Code 2021 - Day 09");
Put_Line("The result for Part 1 is " & Integer'Image(P1));
Put_Line("The result for Part 2 is " & Integer'Image(p2));
end Run;
end AOC2021.Day09;
In order to run this you have to “tangle” the code first using C-c
C-v C-t
.
cd ada
gnatmake day09
./day09
Advent of Code 2021 - Day 09 The result for Part 1 is 417 The result for Part 2 is 1148965