Skip to content

Latest commit

 

History

History
316 lines (308 loc) · 9.78 KB

2020.11.org

File metadata and controls

316 lines (308 loc) · 9.78 KB

Day 11

Executing this code

If you have a lisp installation, emacs, org-mode, and org-babel support for lisp installed you can run this by:

  1. Starting slime (M-x slime)
  2. Typing C-c C-c in the block initialize.
  3. In the repl type (in-package :aoc-2020-11)
  4. Typing C-c C-c in the block answers

Initial stuffs

Packages to load

(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"))

Create package for this day

<<packages>>
(defpackage :aoc-2020-11
  (:use :common-lisp
        :iterate
        :parseq
        :fiveam)
  (:export :problem-a
           :problem-b))
(in-package :aoc-2020-11)

Input

(defun read-input (file)
  (iter (for line in-file file using #'read-line)
        (collect line)))
(defparameter *input*
  (read-input "input/11.txt"))

Part 1

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*))))

Part 2

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*))))

Putting it all together

<<read-input>>
<<input>>
<<to-grid>>
<<initialize>>
<<structs>>
<<functions>>
<<input>>
<<problem-a>>
<<problem-b>>
(problem-a)
(problem-b)

Answer

Problem 11 A: 2448
Problem 11 B: 2234

Test Cases

(def-suite aoc.2020.11)
(in-suite aoc.2020.11)

(run! 'aoc.2020.11)

Test Results

Thoughts

Ada

Runner

Simple runner.

with AOC2020.Day11;
procedure Day11 is
begin
  AOC2020.Day11.Run;
end Day11;

Specification

Specification for solution.

package AOC2020.Day11 is
   procedure Run;
end AOC2020.Day11;

Packages

with Text_IO; use Text_IO;
with Ada.Text_IO.Unbounded_IO;
use Ada.Text_IO.Unbounded_IO;

Read input

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;

Types and generics

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;

Implementation

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;

Run the program

In order to run this you have to “tangle” the code first using C-c C-v C-t.

cd ada
gnatmake day11
./day11