DEV Community 👩‍💻👨‍💻

Cover image for Supply Stacks
Robert Mion
Robert Mion

Posted on

Supply Stacks

Advent of Code 2022 Day 5

Try the Simulator using your puzzle input!

Crate mover simulator

Part 1

  1. Save for later
  2. Manually re-creating the stack of crates
  3. Programming the crane
  4. Loop and adjust everything by 1
  5. My algorithm in JavaScript

Save for later

  • I intend to build a simulator for today's puzzle
  • To do that, I'll need to programmatically build the stack of crates
  • I'm excited to build that algorithm
  • But I'm more excited to attempt solving the puzzle for my own input

Manually re-creating the stack of crates

To programmatically represent the example input:

    [D]    
[N] [C]    
[Z] [M] [P]
 1   2   3 
Enter fullscreen mode Exit fullscreen mode

I will use a nested list data structure:

[
  ['Z', 'N'],
  ['M', 'C', 'D'],
  ['P']
]
Enter fullscreen mode Exit fullscreen mode

Programming the crane

After the crates are rearranged, the desired crates will be at the top of each stack.

There are four ways to adjust the amount of items in a list at either end:

  • Insert at the end
  • Insert at the beginning
  • Remove from the end
  • Remove from the beginning

It seems like this puzzle calls for these two:

  • Remove from the end
  • Insert at the end

In JavaScript, those are represented as:

  • pop()
  • push()

A command like move 3 would perform the following operations:

pop()
push()
pop()
push()
pop()
push()
Enter fullscreen mode Exit fullscreen mode

Loop and adjust everything by 1

Each command references stacks using 1-based indices:

move 3 from 1 to 3
Enter fullscreen mode Exit fullscreen mode

In pseudocode, I need to:

Do three times
  pop() from stack 0
  push() to stack 2
Enter fullscreen mode Exit fullscreen mode

My algorithm in JavaScript

// stacks = [ [...], [...], [...] ]

input
  .split('\n\n')[1]
  .split('\n')
  .forEach(cmd => {
    let [times, from, to] = [
      ...cmd.matchAll(/\d+/g)
    ].map(match => +match[0])
  for (let i = times; i > 0; i--) {
    let crate = stacks[from - 1].pop()
    stacks[to - 1].push(crate)
  }
})

return stacks.map(s => s[s.length - 1]).join('')
Enter fullscreen mode Exit fullscreen mode

Part 2

  1. Method swaps
  2. My algorithm in JavaScript

Method swaps

  • pop() for splice()
  • push() for concat()

pop() for splice()

  • pop() removes the last element
  • splice() removes several elements

push() for concat()

  • push() adds one element
  • concat() combines multiple arrays into a single array

Using this example again:

move 3 from 1 to 3
Enter fullscreen mode Exit fullscreen mode

In pseudocode, I need to:

splice(3 from the end) from stack 0
concat(stack 2 with 3 from the end)
Enter fullscreen mode Exit fullscreen mode

My algorithm in JavaScript

// stacks = [ [...], [...], [...] ]

input
  .split('\n\n')[1]
  .split('\n')
  .forEach(cmd => {
    let [times, from, to] = [
      ...cmd.matchAll(/\d+/g)
    ].map(match => +match[0])
    let crates = stacks[from - 1].splice(-times)
    stacks[to - 1] = stacks[to - 1].concat(crates)
  })

return stacks.map(s => s[s.length - 1]).join('')
Enter fullscreen mode Exit fullscreen mode

Saved for later

I want to programmatically build the stack of crates.

  1. Analyzing the input
  2. My algorithm in pseudocode
  3. My algorithm in JavaScript

Analyzing the input

    [D]    
[N] [C]    
[Z] [M] [P]
 1   2   3 
Enter fullscreen mode Exit fullscreen mode
  • The letters appear at indices: 1, 5, 9
  • The rows at the bottom are the crates earliest in the stack
  • The first row is useless for my purposes

My algorithm in pseudocode

Split the multi-line string into an array of strings
Reverse the array to put rows in their insertion order
Remove the first row containing the column numbers
For each row
  Start at the second character (index 1)
  Skip 4 characters each iteration
  Go until moving passed the end of the string
  If there's no nested array corresponding to the current column
    Create an empty one
  If the current character is anything other than a string with a single space
    Add the character to the appropriate nested array 
Enter fullscreen mode Exit fullscreen mode

My algorithm in JavaScript

let stacks = []
input
  .split('\n\n')[0]
  .split('\n')
  .reverse()
  .slice(1)
  .forEach(line => {
    for (let i = 1; i < line.length; i += 4) {
      if (typeof stacks[(i - 1) / 4] !== 'object') {
        stacks.push([])
      }
      if (line[i] !== ' ') {
        stacks[(i - 1) / 4].push(line[i])
      }
    }
  })
Enter fullscreen mode Exit fullscreen mode

Building the simulator

Now that I have:

  • Solved Part 1
  • Solved Part 2
  • Programmatically built the stack of crates

It's time to build a simulator of the CrateMover 9000 and 9001!

...

How it went:

  • Pretty smoothly overall
  • I got stuck wondering why it wasn't working
  • Then I realized I forgot to replace my loops with performing a single command each time!

The fun bit of code is in rendering the crates after each command:

stacks.map(
  row => row.map(
    el => `[${el}]`
  ).join('')
).join('\n')
Enter fullscreen mode Exit fullscreen mode

Try the Simulator using your puzzle input!
Crate mover simulator

I did it!!

I was initially concerned that this would be a shortest path puzzle, given the rearranging of items.

Thankfully, since it is only Day 5, it was a different challenge type: list manipulation.

Still a fun one that gave me more practice!

Top comments (0)

In defense of the modern web

I expect I'll annoy everyone with this post: the anti-JavaScript crusaders, justly aghast at how much of the stuff we slather onto modern websites; the people arguing the web is a broken platform for interactive applications anyway and we should start over;

React users; the old guard with their artisanal JS and hand authored HTML; and Tom MacWright, someone I've admired from afar since I first became aware of his work on Mapbox many years ago. But I guess that's the price of having opinions.