Skip to content

Latest commit

 

History

History
274 lines (264 loc) · 8.61 KB

2020.12.org

File metadata and controls

274 lines (264 loc) · 8.61 KB

Day 12

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-12)
  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-12
  (:use :common-lisp
        :iterate
        :parseq
        :fiveam)
  (:export :problem-a
           :problem-b))
(in-package :aoc-2020-12)

Input

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

Part 1

Calculate the Manhattan distance from the starting point to where we end up after following series of directions. Only L and R directions cause us to rotate.

(defun follow-directions (directions)
  (loop for l in directions
     with pos = #C(0 0)
     with dir = #C(1 0)
     for command = (char l 0)
     for distance = (parse-integer (subseq l 1))
     finally (return (+ (abs (realpart pos)) (abs (imagpart pos))))
     do (case command 
          (#\F (incf pos (* distance dir)))
          (#\R (setf dir (/ dir (expt #C(0 1) (/ distance 90)))))
          (#\L (setf dir (* dir (expt #C(0 1) (/ distance 90)))))
          (#\N (incf pos (* distance #C(0 1))))
          (#\S (incf pos (* distance #C(0 -1))))
          (#\E (incf pos (* distance #C(1 0))))
          (#\W (incf pos (* distance #C(-1 0)))))))
(defun problem-a () (format t "Problem 12 A: ~a~%" (follow-directions *input*)))

Part 2

Slightly more complicated for this part. The N/S/E/W/L/R all move the waypoint. F now moves the ship to the waypoint a number of times. The waypoint itself is always kept a constant position away from the ship.

I’m going to continue using complex numbers because this makes it easy. Fundamentally, N/S/E/W don’t change except that instead of changing pos, the waypoint is altered.

L and R, however, are different. It’s now rotating around the ship. Which is not too bad. Instead of adjusting a degrees we subtract ship from waypoint, to place it at the origin, multiply like before, then add the ship position back to restore it.

A thought, the waypoint can be maintained as simply a vector from the origin. It’s always relative to the ship, so we can simplify rotation and skipping having to adjust it after moving the ship.

(defun follow-waypoint (directions)
  (loop for l in directions
     with pos = #C(0 0)
     with waypoint = #C(10 1)
     for command = (char l 0)
     for distance = (parse-integer (subseq l 1))
     finally (return (+ (abs (realpart pos)) (abs (imagpart pos))))
     do (case command 
          (#\F (incf pos (* distance waypoint)))
          (#\R (setf waypoint (/ waypoint (expt #C(0 1) (/ distance 90)))))
          (#\L (setf waypoint (* waypoint (expt #C(0 1) (/ distance 90)))))
          (#\N (incf waypoint (* distance #C(0 1))))
          (#\S (incf waypoint (* distance #C(0 -1))))
          (#\E (incf waypoint (* distance #C(1 0))))
          (#\W (incf waypoint (* distance #C(-1 0)))))))
(defun problem-b () (format t "Problem 12 B: ~a~%" (follow-waypoint *input*)))

Putting it all together

<<read-input>>
<<input>>
<<follow-directions>>
<<follow-waypoint>>
<<initialize>>
<<structs>>
<<functions>>
<<input>>
<<problem-a>>
<<problem-b>>
(problem-a)
(problem-b)

Answer

Problem 12 A: 362
Problem 12 B: 29895

Test Cases

(def-suite aoc.2020.12)
(in-suite aoc.2020.12)
(defparameter *test-input* '("F10" "N3" "F7" "R90" "F11"))
(test move-ship
  (is (= 25 (follow-directions *test-input*))))
(test move-with-waypoint
  (is (= 286 (follow-waypoint *test-input*))))
(run! 'aoc.2020.12)

Test Results

Running test suite AOC.2020.12
 Running test MOVE-SHIP .
 Running test MOVE-WITH-WAYPOINT .
 Did 2 checks.
    Pass: 2 (100%)
    Skip: 0 ( 0%)
    Fail: 0 ( 0%)

Thoughts

Back In My Day

On Reddit this challenge was presented. The L and R commands now rotate by radians. I changed two lines from the follow-waypoints version.

(defun follow-radians (directions)
  (loop for l in directions
     with pos = #C(0 0)
     with waypoint = #C(10 1)
     for command = (char l 0)
     for distance = (parse-integer (subseq l 1))
     finally (return (+ (abs (realpart pos)) (abs (imagpart pos))))
     do (case command 
          (#\F (incf pos (* distance waypoint)))
          (#\R (setf waypoint (/ waypoint (cis distance))))
          (#\L (setf waypoint (* waypoint (cis distance))))
          (#\N (incf waypoint (* distance #C(0 1))))
          (#\S (incf waypoint (* distance #C(0 -1))))
          (#\E (incf waypoint (* distance #C(1 0))))
          (#\W (incf waypoint (* distance #C(-1 0)))))))
(format t "~$~%" (follow-radians *test-input*))
(format t "~$~%" (follow-radians *input*))

Ada

Runner

Simple runner.

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

Specification

Specification for solution.

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

Packages

with GNAT.Regpat; use GNAT.Regpat;
with Text_IO; use Text_IO;

Types and generics

Implementation

Actual implementation body.

<<ada-packages>>
package body AOC2020.Day12 is
   <<types-and-generics>>
   -- Used as an example of matching regular expressions
   procedure Parse_Line (Line : Unbounded_String; P : out Password) is
      Pattern : constant String := "(\d+)-(\d+) ([a-z]): ([a-z]+)";
      Re : constant Pattern_Matcher := Compile(Pattern);
      Matches : Match_Array (0..4);
      Pass : Unbounded_String;
      P0, P1 : Positive;
      C : Character;
   begin
      Match(Re, To_String(Line), Matches);
      P0 := Integer'Value(Slice(Line, Matches(1).First, Matches(1).Last));
      P1 := Integer'Value(Slice(Line, Matches(2).First, Matches(2).Last));
      C := Element(Line, Matches(3).First);
      Pass := To_Unbounded_String(Slice(Line, Matches(4).First, Matches(4).Last));
      P := (Min_Or_Pos => P0,
            Max_Or_Pos => P1,
            C => C,
            P => Pass);
   end Parse_Line;
   procedure Run is
   begin
      Put_Line("Advent of Code 2020 - Day 12");
      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.Day12;

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 day12
./day12