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-08)
- 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-08
(:use :common-lisp
:parseq
:fiveam)
(:export :problem-a
:problem-b))
(in-package :aoc-2021-08)
(defun read-input (file)
(with-open-file (in file)
(loop
for line = (read-line in nil)
while line
collect (cl-ppcre:split " \\| " line))))
(defparameter *input*
(read-input "input/08.txt"))
(defun count-1-4-7-8 (lines)
(loop
for (nil output) in lines
sum (loop
for display in (cl-ppcre:split " " output)
for n = (length display)
count (or (= n 2) (= n 3) (= n 4) (= n 7)))))
(defun problem-a () (format t "Problem 08 A: ~a~%" (count-1-4-7-8 *input*)))
Ok, after reading it over a couple times I finally grok it. But that doesn’t help me code it any quicker.
The official designations for each segment are from this table:
Ok, taking a moment to think, there has got to be a better way. So
here’s my thought, each pattern can be turned into a 7-bit number. The
order doesn’t actually matter. Using this, 1, 4, 7, and 8 can be
readily identified using part 1, logcount
can be used to count the
number of bits and give me the same result as I had before.
(defun pattern-to-number (pattern)
(loop for ch across pattern
sum (expt 2 (- (char-code ch) (char-code #\a)))))
(defun translate (numbers output)
(loop
for d in output
with result = 0
finally (return result)
do
(setf result (* 10 result))
(incf result (position d numbers))))
(defun decode-line (codes output)
(let ((codes (mapcar #'pattern-to-number codes))
(output (mapcar #'pattern-to-number output))
(numbers (make-array 10)))
;; set 1
(setf (aref numbers 1)
(find-if (lambda (n) (= (logcount n) 2)) codes))
(setf codes (remove (aref numbers 1) codes))
;; set 4
(setf (aref numbers 4)
(find-if (lambda (n) (= (logcount n) 4)) codes))
(setf codes (remove (aref numbers 4) codes))
;; set 7
(setf (aref numbers 7)
(find-if (lambda (n) (= (logcount n) 3)) codes))
(setf codes (remove (aref numbers 7) codes))
;; set 8
(setf (aref numbers 8)
(find-if (lambda (n) (= (logcount n) 7)) codes))
(setf codes (remove (aref numbers 8) codes))
;; 9 ^ (7 | 4) will have one bit
(setf (aref numbers 9)
(find-if (lambda (n)
(= (logcount (logxor n (logior (aref numbers 7) (aref numbers 4)))) 1))
codes))
(setf codes (remove (aref numbers 9) codes))
;; 3 & 1 = 1
(setf (aref numbers 3)
(find-if (lambda (n)
(and (= (logcount n) 5)
(= (aref numbers 1) (logand (aref numbers 1) n))))
codes))
(setf codes (remove (aref numbers 3) codes))
;; 6 | 1 = 8
(setf (aref numbers 6)
(find-if (lambda (n)
(= (aref numbers 8) (logior (aref numbers 1) n)))
codes))
(setf codes (remove (aref numbers 6) codes))
;; 0 is the last 6-bit value
(setf (aref numbers 0)
(find-if (lambda (n)
(= 6 (logcount n)))
codes))
(setf codes (remove (aref numbers 0) codes))
;; 6 or 5 = 6
(setf (aref numbers 5)
(find-if (lambda (n)
(= (aref numbers 6) (logior (aref numbers 6) n)))
codes))
(setf codes (remove (aref numbers 5) codes))
;; 2 is last
(setf (aref numbers 2) (first codes))
(translate numbers output)))
(defun decode-all-lines (lines)
(loop
for (codes output) in lines
sum (decode-line (cl-ppcre:split " " codes) (cl-ppcre:split " " output))))
(defun problem-b () (format t "Problem 08 B: ~a~%" (decode-all-lines *input*)))
<<read-input>>
<<input>>
<<part-1>>
<<decoder-ring>>
<<initialize>>
<<structs>>
<<functions>>
<<input>>
<<problem-a>>
<<problem-b>>
(problem-a)
(problem-b)
Problem 08 A: 421 Problem 08 B: 986163
(def-suite aoc.2021.08)
(in-suite aoc.2021.08)
(defparameter *small-test*
'("acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab"
"cdfeb fcadb cdfeb cdbaf"))
(defparameter *big-test*
(read-input "test/08.txt"))
(run! 'aoc.2021.08)
Simple runner.
with AOC2021.Day08;
procedure Day08 is
begin
AOC2021.Day08.Run;
end Day08;
Specification for solution.
package AOC2021.Day08 is
procedure Run;
end AOC2021.Day08;
with GNAT.Regpat; use GNAT.Regpat;
with Text_IO; use Text_IO;
Actual implementation body.
<<ada-packages>>
package body AOC2021.Day08 is
-- represents the signals as a bolean array
type Encoded is array (Character range 'a'..'g') of Boolean
with Default_Component_Value => False;
Empty : constant Encoded := (others => False);
type Code_Array is array (1..10) of Encoded;
type Display_Array is array (1..4) of Encoded;
-- Straight forward procedure to convert a string of [a-g]+ into a
-- boolean array.
procedure Encode (Code : in out Encoded; Input : String) is
begin
for C of Input loop
Code(C) := True;
end loop;
end Encode;
procedure Parse_Line (Line : String; Codes : out Code_Array; Display : out Display_Array; Count : in out Integer) is
Pattern : constant String :=
"([a-g]+) ([a-g]+) ([a-g]+) ([a-g]+) ([a-g]+) ([a-g]+) ([a-g]+) ([a-g]+) ([a-g]+) ([a-g]+) \| ([a-g]+) ([a-g]+) ([a-g]+) ([a-g]+)";
Re : constant Pattern_Matcher := Compile(Pattern);
Matches : Match_Array (0..Paren_Count(Re));
begin
Match(Re, Line, Matches);
for I in 1..10 loop
Encode(Codes (I), Line(Matches(I).First..Matches(I).Last));
end loop;
for I in 1..4 loop
if 1 = (Matches (I+10).Last - Matches (I+10).First)
or 2 = (Matches (I+10).Last - Matches (I+10).First)
or 3 = (Matches (I+10).Last - Matches (I+10).First)
or 6 = (Matches (I+10).Last - Matches (I+10).First)
then
Count := Count + 1;
end if;
Encode(Display (I), Line(Matches(I+10).First..Matches(I+10).Last));
end loop;
end Parse_Line;
-- I couldn't find a "popcount" function in the Ada spec so here it is
function Popcount (Code : Encoded) return Natural is
Count : Natural := 0;
begin
for B of Code loop
Count := Count + (if B then 1 else 0);
end loop;
return Count;
end Popcount;
procedure Decode_Display (Codes : in out Code_Array; Display : Display_Array; Sum : in out Integer) is
Decoder_Ring : array (0..9) of Encoded;
Result : Natural := 0;
begin
Decoder_Ring (8) := (others => True);
-- Remove "8"
for I in Codes'Range loop
if Codes(I) = Decoder_Ring (8)
then
Codes(I) := Empty;
exit;
end if;
end loop;
-- Find "1"
for I in Codes'Range loop
if 2 = Popcount (Codes(I))
then
Decoder_Ring (1) := Codes(I);
Codes(I) := Empty;
exit;
end if;
end loop;
-- Find "7"
for I in Codes'Range loop
if 3 = Popcount (Codes(I))
then
Decoder_Ring (7) := Codes(I);
Codes(I) := Empty;
exit;
end if;
end loop;
-- Find "4"
for I in Codes'Range loop
if 4 = Popcount (Codes(I))
then
Decoder_Ring (4) := Codes(I);
Codes(I) := Empty;
exit;
end if;
end loop;
-- Find "9": 1 = Popcount (9 xor (7 or 4))
for I in Codes'Range loop
if 1 = Popcount (Codes(I) xor (Decoder_Ring(7) or Decoder_Ring(4)))
and Popcount (Codes(I)) = 6
then
Decoder_Ring (9) := Codes(I);
Codes(I) := Empty;
exit;
end if;
end loop;
-- Find "0": Popcount 0 = 6
for I in Codes'Range loop
if Popcount (Codes(I)) = 6
and Decoder_Ring (1) = (Decoder_Ring (1) and Codes (I))
then
Decoder_Ring (0) := Codes (I);
Codes (I) := Empty;
end if;
end loop;
-- Find "6": 8 = 6 or 1
for I in Codes'Range loop
if Popcount (Codes(I)) = 6
then
Decoder_Ring (6) := Codes(I);
Codes(I) := Empty;
exit;
end if;
end loop;
-- Find "5": 6 = 5 or 6
for I in Codes'Range loop
if Decoder_Ring (9) = (Codes(I) or Decoder_Ring (1))
then
Decoder_Ring (5) := Codes(I);
Codes(I) := Empty;
exit;
end if;
end loop;
-- Find "3": 3 or 1 = 3 & Popcount 3 = 5
for I in Codes'Range loop
if Codes(I) = (Codes(I) or Decoder_Ring (1))
and Popcount (Codes(I)) = 5
then
Decoder_Ring (3) := Codes(I);
Codes(I) := Empty;
exit;
end if;
end loop;
-- Find "2": 2 or 3 = 2 or 1
for I in Codes'Range loop
if Codes(I) /= Empty
then
Decoder_Ring (2) := Codes(I);
Codes(I) := Empty;
exit;
end if;
end loop;
for Code of Display loop
Result := Result * 10;
for I in Decoder_Ring'Range loop
if Code = Decoder_Ring (I)
then
Result := Result + I;
exit;
end if;
end loop;
end loop;
Sum := Sum + Result;
end Decode_Display;
procedure Solve (Filename : String; Count : in out Integer; Sum : in out Integer) is
Input_File : File_Type;
begin
Open (Input_File, In_File, Filename);
while not End_Of_File (Input_File) loop
declare
Line : String := Get_Line (Input_File);
Codes : Code_Array;
Display : Display_Array;
begin
Parse_Line (Line, Codes, Display, Count);
Decode_Display (Codes, Display, Sum);
end;
end loop;
Close (Input_File);
end Solve;
procedure Run is
Count : Integer := 0;
Sum : Integer := 0;
begin
Solve ("../input/08.txt", Count, Sum);
Put_Line("Advent of Code 2021 - Day 08");
Put_Line("The result for Part 1 is " & Integer'Image(Count));
Put_Line("The result for Part 2 is " & Integer'Image(Sum));
end Run;
end AOC2021.Day08;
In order to run this you have to “tangle” the code first using C-c
C-v C-t
.
cd ada
gnatmake day08
./day08