Advent of Code #2 (in JavaScript & Haskell)

Today’s Advent of Code puzzle continues the theme of calculating a single value from a list of input, except this time, the input is text. Again, I solved the problem initially in Excel (where the hardest part was figuring out how to split a string by …


This content originally appeared on DEV Community and was authored by Caleb Weeks

Today's Advent of Code puzzle continues the theme of calculating a single value from a list of input, except this time, the input is text. Again, I solved the problem initially in Excel (where the hardest part was figuring out how to split a string by a delimiter...). Here is my attempt in Haskell and JavaScript.

Part One

Given a list of course instructions as seen below, we need to find the final destination of a submarine by adding up the horizontal and depth values and multiplying the two sums. A forward instruction adds horizontal position while up and down decrease and increase the depth, respectively.

course = ["forward 5", "down 5", "forward 8", "up 3", "down 8", "forward 2"]

The first thing to do is parse out the numbers. I decided to use pattern matching to do this:

parseInstruction :: String -> (Int, Int)
parseInstruction ('f':'o':'r':'w':'a':'r':'d':x) = (read x, 0)
parseInstruction ('d':'o':'w':'n':x) = (0, read x)
parseInstruction ('u':'p':x) = (0, negate (read x))
parseInstruction _ = (0, 0)

This will give us a tuple of horizontal and depth positions, so we just need to add them all up. Here is a helper function to add two tuples together:

sumTuples :: (Num a, Num b) => (a, b) -> (a, b) -> (a, b)
sumTuples (a1, b1) (a2, b2) = (a1 + a2, b1 + b2)

After folding over the original course instructions with our tuple summing helper function following the instruction parser, we just multiply the final two values in the tuple together. A cool trick to do this is to uncurry the multiplication operator, which will simply pass both values of the tuple to the operator.

answer = uncurry (*) (foldl (
  \acc instruction -> sumTuples acc (parseInstruction instruction)
) (0, 0) course)

This approach can be copied almost identically in JavaScript. A switch/case block is used instead of pattern matching for the parseInstruction function, and the final multiplication of the two values is chained in another reduce.

const parseInstruction = (instruction) => {
  const [direction, valueStr] = instruction.split(" ");
  const value = parseInt(valueStr);
  switch (direction) {
    case "forward":
      return [value, 0];
    case "down":
      return [0, value];
    case "up":
      return [0, -value];
  }
};

const sumTuples = ([a1, b1], [a2, b2]) => [a1 + a2, b1 + b2];

const answer = course
  .reduce(
    (acc, instruction) => sumTuples(acc, parseInstruction(instruction)),
    [0, 0]
  )
  .reduce((acc, x) => acc * x, 1);

Part Two

The second part of the puzzle revises the meaning of the instructions such that up and down actually refer to the aim of the submarine, and the depth is actually calculated by multiplying the forward value by the current aim value. This requires keeping track of an additional accumulator value during the fold. The instruction parsing function stays the same, but we'll replace the sumTuples function with an accumulator function that takes care of the folding procedure:

accumulator :: (Int, Int, Int) -> String -> (Int, Int, Int)
accumulator (horizontal, aim, depth) instruction = 
  (\(h, a) -> (horizontal + h, aim + a, depth + (h * (aim + a)))) 
  (parseInstruction instruction)

Horizontal and aim are accumulated as normal, but the depth is calculated as the current aim multiplied by the horizontal value from the instruction. We'll also need to manually pick out the depth and horizontal values from the triple to get the final product:

answer = (\(horizontal, aim, depth) -> horizontal * depth)
(foldl accumulator (0, 0, 0) course)

The same changes can be made in JavaScript, but we'll also have to swap out the chained reduce hack for an intermediary variable assignment since we can't have inline lambdas. We could define a function and compose it with the reduce, but it wouldn't save much.

const accumulator = ([horizontal, aim, depth], instruction) => {
  const [h, a] = parseInstruction(instruction);
  return [horizontal + h, aim + a, depth + h * (aim + a)];
};

const [horizontal, aim, depth] = course.reduce(accumulator, [0, 0, 0]);

const answer = horizontal * depth;

This problem had a lot of similarities to yesterday's problem, so fortunately, I didn't take quite as long coming up with these solutions. How would you implement a solution to these problems in Haskell or JavaScript? I'm particularly interested in better alternatives to the pattern matching hack for parsing the instructions in Haskell.


This content originally appeared on DEV Community and was authored by Caleb Weeks


Print Share Comment Cite Upload Translate Updates
APA

Caleb Weeks | Sciencx (2021-12-03T03:25:52+00:00) Advent of Code #2 (in JavaScript & Haskell). Retrieved from https://www.scien.cx/2021/12/03/advent-of-code-2-in-javascript-haskell/

MLA
" » Advent of Code #2 (in JavaScript & Haskell)." Caleb Weeks | Sciencx - Friday December 3, 2021, https://www.scien.cx/2021/12/03/advent-of-code-2-in-javascript-haskell/
HARVARD
Caleb Weeks | Sciencx Friday December 3, 2021 » Advent of Code #2 (in JavaScript & Haskell)., viewed ,<https://www.scien.cx/2021/12/03/advent-of-code-2-in-javascript-haskell/>
VANCOUVER
Caleb Weeks | Sciencx - » Advent of Code #2 (in JavaScript & Haskell). [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/12/03/advent-of-code-2-in-javascript-haskell/
CHICAGO
" » Advent of Code #2 (in JavaScript & Haskell)." Caleb Weeks | Sciencx - Accessed . https://www.scien.cx/2021/12/03/advent-of-code-2-in-javascript-haskell/
IEEE
" » Advent of Code #2 (in JavaScript & Haskell)." Caleb Weeks | Sciencx [Online]. Available: https://www.scien.cx/2021/12/03/advent-of-code-2-in-javascript-haskell/. [Accessed: ]
rf:citation
» Advent of Code #2 (in JavaScript & Haskell) | Caleb Weeks | Sciencx | https://www.scien.cx/2021/12/03/advent-of-code-2-in-javascript-haskell/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.