Skip to content

Latest commit

 

History

History
265 lines (232 loc) · 15 KB

12.md

File metadata and controls

265 lines (232 loc) · 15 KB

Day 12

Part 1

Navigate the ship by following the instructions. What is the Manhattan distance between the ship's final and starting locations?

const input = `
F10
N3
F7
R90
F11
`.trim()

const Dir = {
  E: 0,
  S: 90,
  W: 180,
  N: 270,
}

const result = input
  .split('\n')
  .map((line) => line.match(/(?<action>\w)(?<value>\d+)/).groups)
  .map(({ action, value }) => ({ action, value: +value }))
  .reduce(
    (pos, { action, value }) => {
      if (action === 'E') return { ...pos, x: pos.x + value }
      if (action === 'S') return { ...pos, y: pos.y + value }
      if (action === 'W') return { ...pos, x: pos.x - value }
      if (action === 'N') return { ...pos, y: pos.y - value }

      if (action === 'R') {
        return { ...pos, dir: (pos.dir + value) % 360 }
      }
      if (action === 'L') {
        return { ...pos, dir: (pos.dir + (360 - value)) % 360 }
      }

      if (action === 'F') {
        if (pos.dir === Dir.E) return { ...pos, x: pos.x + value }
        if (pos.dir === Dir.S) return { ...pos, y: pos.y + value }
        if (pos.dir === Dir.W) return { ...pos, x: pos.x - value }
        if (pos.dir === Dir.N) return { ...pos, y: pos.y - value }
        throw new Error('Invalid current direction: ' + pos.dir)
      }

      throw new Error('Invalid action: ' + action + '; value: ' + value)
    },
    { dir: Dir.E, x: 0, y: 0 }
  )

console.log(result)
console.log(Math.abs(result.x) + Math.abs(result.y))

Try it out on flems.io

Part 2

The navigation instructions are to be interpreted slightly differently. What is the Manhattan distance now?

const result = input
  .split('\n')
  .map((line) => line.match(/(?<action>\w)(?<value>\d+)/).groups)
  .map(({ action, value }) => ({ action, value: +value }))
  .reduce(
    (acc, { action, value }) => {
      const { ship, waypoint } = acc

      if (action === 'E') {
        return {
          ship,
          waypoint: {
            x: waypoint.x + value,
            y: waypoint.y,
          },
        }
      }
      if (action === 'S') {
        return {
          ship,
          waypoint: {
            x: waypoint.x,
            y: waypoint.y + value,
          },
        }
      }
      if (action === 'W') {
        return {
          ship,
          waypoint: {
            x: waypoint.x - value,
            y: waypoint.y,
          },
        }
      }
      if (action === 'N') {
        return {
          ship,
          waypoint: {
            x: waypoint.x,
            y: waypoint.y - value,
          },
        }
      }

      if (
        (action === 'R' && value === 90) ||
        (action === 'L' && value === 270)
      ) {
        return {
          ship,
          waypoint: {
            x: -waypoint.y,
            y: waypoint.x,
          },
        }
      }
      if ((action === 'R' || action === 'L') && value === 180) {
        return {
          ship,
          waypoint: {
            x: -waypoint.x,
            y: -waypoint.y,
          },
        }
      }
      if (
        (action === 'R' && value === 270) ||
        (action === 'L' && value === 90)
      ) {
        return {
          ship,
          waypoint: {
            x: waypoint.y,
            y: -waypoint.x,
          },
        }
      }

      if (action === 'F') {
        return {
          ship: {
            x: ship.x + waypoint.x * value,
            y: ship.y + waypoint.y * value,
          },
          waypoint,
        }
      }

      throw new Error('Invalid action: ' + action + '; value: ' + value)
    },
    { ship: { x: 0, y: 0 }, waypoint: { x: 10, y: -1 } }
  )

console.log(result)
console.log(Math.abs(result.ship.x) + Math.abs(result.ship.y))

flems

The code works, but the reducer's return statements are annoyingly verbose. It's also easy to forget something from the returns, leading to annoying bugs.

That's why I made another version using Mergerino. Now the code is less verbose and the returns are simpler, so it's easier to see what's actually being changed:

const merge = mergerino

const result = input
  .split('\n')
  .map((line) => line.match(/(?<action>\w)(?<value>\d+)/).groups)
  .map(({ action, value }) => ({ action, value: +value }))
  .reduce(
    (acc, { action, value }) => {
      const { ship, waypoint } = acc

      if (action === 'E') {
        return merge(acc, { waypoint: { x: waypoint.x + value } })
      }
      if (action === 'S') {
        return merge(acc, { waypoint: { y: waypoint.y + value } })
      }
      if (action === 'W') {
        return merge(acc, { waypoint: { x: waypoint.x - value } })
      }
      if (action === 'N') {
        return merge(acc, { waypoint: { y: waypoint.y - value } })
      }

      if (
        (action === 'R' && value === 90) ||
        (action === 'L' && value === 270)
      ) {
        return merge(acc, {
          waypoint: ({ x, y }) => ({ x: -y, y: x }),
        })
      }
      if ((action === 'R' || action === 'L') && value === 180) {
        return merge(acc, {
          waypoint: ({ x, y }) => ({ x: -x, y: -y }),
        })
      }
      if (
        (action === 'R' && value === 270) ||
        (action === 'L' && value === 90)
      ) {
        return merge(acc, {
          waypoint: ({ x, y }) => ({ x: y, y: -x }),
        })
      }

      if (action === 'F') {
        return merge(acc, {
          ship: {
            x: ship.x + waypoint.x * value,
            y: ship.y + waypoint.y * value,
          },
        })
      }

      throw new Error('Invalid action: ' + action + '; value: ' + value)
    },
    { ship: { x: 0, y: 0 }, waypoint: { x: 10, y: -1 } }
  )

console.log(result)
console.log(Math.abs(result.ship.x) + Math.abs(result.ship.y))

flems

Another option would have been to use Immer. I find Mergerino and Immer quite similar, at least for simple use cases like this puzzle.

In this case, I prefer Mergerino, because Mergerino's minzipped size is just 415 bytes, whereas Immer's minzipped size is 5.4 kilobytes. The latter might be acceptable in a larger project where you utilize Immer more heavily. The former is so tiny that I feel okay using it even in a tiny puzzle like this.

What did I learn?

Nothing, but I'm content that I got to use Mergerino. I like it!