Advent of Code 2019, Day 5

Today's advent of code challenge was an extension of day 2

The first part was pretty straightforward; to support addressing modes I wrote a dumb little function to get the arguments:

var argCount = map[int]int{
      opAdd:    2,
      opMul:    2,
      opInput:  0,
      opOutput: 1,
      opJT:     2,
      opJF:     2,
      opLT:     2,
      opEQ:     2,
      opHalt:   0,
}

func fetchArgs(mem []int, ip, params, count int) []int {
      if count == 0 {
              return nil
      }

      args := make([]int, 0, count)
      for i := 1; i <= count; i++ {
              arg := mem[ip+i]
              if params%10 == 0 {
                      arg = mem[arg]
              }
              params /= 10

              args = append(args, arg)
      }

      return args
}

The intcode Run function now takes an additional parameter (an io.Reader), and the run loop now looks like this:

for run {
        op := mem[ip] % 100
        params := mem[ip] / 100
        args := fetchArgs(mem, ip, params, argCount[op%100])
        switch op % 100 {
        case opAdd:
                dest := mem[ip+3]
                mem[dest] = args[0] + args[1]
                ip += 4
        case opMul:
                dest := mem[ip+3]
                mem[dest] = args[0] * args[1]
                ip += 4
        case opInput:
                fmt.Printf("? ")
                var arg int
                fmt.Fscan(r, &arg)
                dest := mem[ip+1]
                mem[dest] = arg
                ip += 2
        case opOutput:
                fmt.Printf("%08d: %d\n", mem[ip+1], args[0])
                ip += 2
        case opJT:
                if args[0] != 0 {
                        ip = args[1]
                } else {
                        ip += 3
                }
        case opJF:
                if args[0] == 0 {
                        ip = args[1]
                } else {
                        ip += 3
                }
        case opLT:
                dest := mem[ip+3]
                if args[0] < args[1] {
                        mem[dest] = 1
                } else {
                        mem[dest] = 0
                }
                ip += 4
        case opEQ:
                dest := mem[ip+3]
                if args[0] == args[1] {
                        mem[dest] = 1
                } else {
                        mem[dest] = 0
                }
                ip += 4
        case opHalt:
                run = false
        default:
                return mem, fmt.Errorf("VM: invalid opcode %d [ip=%d]", op, ip)
        }
}

I made the choice to use an io.Reader (and really I should convert it to an io.Writer too) so that I could "automate" entering the program.

A few things tripped me up:

  • I briefly forgot to update the ip in the jump statements when in the else clause;
  • I got the ip increment counter wrong (I'd had it as 2 instead of 3)
  • I was treating the output call as an indirect address (position mode) instead of immediate mode, which is how it should have been.

Finally, some stats - my instrumentation output:

2019/12/05 19:36:29 day05p1: complete in 350us, total allocated 75 kB,
current heap allocation 75 kB
2019/12/05 19:36:29 day05p2: complete in 29us, total allocated 83 kB,
current heap allocation 83 kB

And I'll note this was a 245 LOC diff, mostly in the ic package (+165 lines).

I really enjoyed today's challenge, but it's also not the first VM I've written --- I really enjoy writing VMs.


Tags: ,