DEV Community

Discussion on: Advent of Code 2019 Solution Megathread - Day 11: Space Police

Collapse
 
nordfjord profile image
Einar Norðfjörð

Solution in JS

I used an object to represent the grid to make counting the "painted" block easier.

Had a bit of trouble realizing that the top left of the grid is 0,0 and not the bottom left :)

const INSTRUCTIONS = {
  ADD: 1,
  MULT: 2,
  INPUT: 3,
  OUTPUT: 4,
  JUMP_IF_TRUE: 5,
  JUMP_IF_FALSE: 6,
  LESS_THAN: 7,
  EQUALS: 8,
  ADJUST_RELATIVE_BASE: 9,
  HALT: 99
}

function runProgram(instructions) {
  instructions = instructions.slice()

  return function* amplifier() {
    let lastOutput = null
    let relativeBase = 0
    for (let i = 0; i < instructions.length; ++i) {
      const instruction = instructions[i]
      const parsed = String(instruction)
        .padStart(5, '0')
        .split('')
      const getValue = (value, mode = '0') => {
        if (mode === '0') {
          return instructions[value] || 0
        } else if (mode === '1') {
          return value
        } else if (mode === '2') {
          return instructions[relativeBase + value] || 0
        }
      }
      const setValue = (index, value, mode = '0') => {
        if (mode === '0') {
          instructions[index] = value
        } else if (mode === '2') {
          instructions[relativeBase + index] = value
        }
      }
      const opCode = Number(parsed.slice(3).join(''))
      const modes = parsed.slice(0, 3)
      switch (opCode) {
        case INSTRUCTIONS.ADD: {
          const x = getValue(instructions[++i], modes[2])
          const y = getValue(instructions[++i], modes[1])
          setValue(instructions[++i], x + y, modes[0])
          break
        }
        case INSTRUCTIONS.MULT: {
          const x = getValue(instructions[++i], modes[2])
          const y = getValue(instructions[++i], modes[1])
          setValue(instructions[++i], x * y, modes[0])
          break
        }
        case INSTRUCTIONS.INPUT: {
          setValue(instructions[++i], yield { type: 'INPUT' }, modes[2])
          break
        }
        case INSTRUCTIONS.OUTPUT: {
          lastOutput = getValue(instructions[++i], modes[2])
          yield { type: 'OUTPUT', value: lastOutput }
          break
        }
        case INSTRUCTIONS.JUMP_IF_TRUE: {
          const compare = getValue(instructions[++i], modes[2])
          const jumpTo = getValue(instructions[++i], modes[1]) - 1
          if (compare != 0) {
            i = jumpTo
          }
          break
        }
        case INSTRUCTIONS.JUMP_IF_FALSE: {
          const compare = getValue(instructions[++i], modes[2])
          const jumpTo = getValue(instructions[++i], modes[1]) - 1
          if (compare == 0) {
            i = jumpTo
          }
          break
        }
        case INSTRUCTIONS.LESS_THAN: {
          const x = getValue(instructions[++i], modes[2])
          const y = getValue(instructions[++i], modes[1])
          setValue(instructions[++i], x < y ? 1 : 0, modes[0])
          break
        }
        case INSTRUCTIONS.EQUALS: {
          const x = getValue(instructions[++i], modes[2])
          const y = getValue(instructions[++i], modes[1])
          setValue(instructions[++i], x === y ? 1 : 0, modes[0])
          break
        }
        case INSTRUCTIONS.ADJUST_RELATIVE_BASE: {
          const adjustBy = getValue(instructions[++i], modes[2])
          relativeBase += adjustBy
          break
        }
        case INSTRUCTIONS.HALT:
          return lastOutput
      }
    }
  }
}

const directions = {
  UP: 0,
  RIGHT: 1,
  DOWN: 2,
  LEFT: 3
}
const modulo = (x, n) => ((x % n) + n) % n

const runRobotProgram = (robot, initialBlockColor = 0) => {
  const grid = {}
  let location = [0, 0]
  grid[location.join(':')] = initialBlockColor
  let direction = directions.UP
  robot.next()
  while (true) {
    const input = grid[location.join(':')] || 0
    const {
      value: { value: color }
    } = robot.next(input)
    grid[location.join(':')] = color
    const {
      value: { value: turn }
    } = robot.next()
    const { done } = robot.next()
    if (done) return { grid }
    if (turn === 0) {
      direction = modulo(direction - 1, 4)
    } else if (turn === 1) {
      direction = (direction + 1) % 4
    }

    if (direction === directions.DOWN) {
      location = [location[0], location[1] + 1]
    } else if (direction === directions.RIGHT) {
      location = [location[0] + 1, location[1]]
    } else if (direction === directions.UP) {
      location = [location[0], location[1] - 1]
    } else if (direction === directions.LEFT) {
      location = [location[0] - 1, location[1]]
    }
  }
}

function printGrid(grid) {
  const [[minX, maxX], [minY, maxY]] = Object.keys(grid)
    .map(x => x.split(':').map(Number))
    .reduce(
      (state, [x, y]) => {
        if (state[0][0] > x) state[0][0] = x
        if (state[0][1] < x) state[0][1] = x
        if (state[1][0] > y) state[1][0] = y
        if (state[1][1] < y) state[1][1] = y
        return state
      },
      [
        [Infinity, -Infinity],
        [Infinity, -Infinity]
      ]
    )

  let gridMap = ''
  for (let y = minY; y <= maxY; ++y) {
    for (let x = minX; x <= maxX; ++x) {
      gridMap += grid[`${x}:${y}`] === 1 ? '#' : ' '
    }
    gridMap += '\n'
  }
  return gridMap
}

function part1(instructions) {
  const { grid } = runRobotProgram(runProgram(instructions)())

  const gridMap = printGrid(grid)
  console.log(gridMap)
  console.log(Object.keys(grid).length)
}

function part2(instructions) {
  const { grid } = runRobotProgram(runProgram(instructions)(), 1)

  const gridMap = printGrid(grid)
  console.log(gridMap)
}

const input = require('fs')
  .readFileSync(require('path').resolve(__dirname, './input.txt'))
  .toString()

const instructions = input.split(',').map(Number)

part1(instructions)
part2(instructions)