DEV Community

Cover image for Advent of Code 2019 Solution Megathread - Day 2: 1202 Program Alarm
Jon Bristow
Jon Bristow

Posted on • Edited on

Advent of Code 2019 Solution Megathread - Day 2: 1202 Program Alarm

Day 2 is in the books! And surprisingly, we're into pseudo-interpreter territory already!

Day 2 - The Problem

We need to slingshot ourselves around the moon, but the computer that calculates our trajectory has sadly exploded. Given our current data and the specs for that machine, we need to build ourselves a new IntCode interpreter.

It's surprising to me to see a psuedo-assembly converter so early in the Advent of Code, but this one turned out to be vaguely straightforward. Most of my personal problems stemmed from input validation. (If you're a newbie, let me assure you that input validation is almost always something that will slow you down. This goes triply for anyone learning a new language!)

Part 2 luckily doesn't require a whole lot of extra logic, but it has a bit of "optimization" and "compartmentalization" to keep tabs on.

Ongoing Meta

Dev.to List of Leaderboards

If you were part of Ryan Palo's leaderboard last year, you're still a member of that!

If you want me to add your leaderboard code to this page, reply to one of these posts and/or send me a DM containing your code and any theming or notes you’d like me to add. (You can find your private leaderboard code on your "Private Leaderboard" page.)

I'll edit in any leaderboards that people want to post, along with any description for the kinds of people you want to have on it. (My leaderboard is being used as my office's leaderboard.) And if I get something wrong, please call me out or message me and I’ll fix it ASAP.

There's no limit to the number of leaderboards you can join, so there's no problem belonging to a "Beginner" and a language specific one if you want.

Neat Statistics

I'm planning on adding some statistics, but other than "what languages did we see yesterday" does anyone have any ideas?

Top comments (24)

Collapse
 
aspittel profile image
Ali Spittel

I struggled with reading comprehension tonight 🤦🏼‍♀️

def calculate(num1, num2, nums):
    nums[1] = num1
    nums[2] = num2
    idx = 0
    while nums[idx] != 99:
        num = nums[idx]
        val1 = nums[nums[idx + 1]]
        val2 = nums[nums[idx + 2]]
        idx3 = nums[idx + 3]
        if num == 1:
            nums[idx3] = val1 + val2
        elif num == 2:
            nums[idx3] = val1 * val2
        idx += 4
    return nums[0]


with open("input.txt") as _file:
    for line in _file:
        input_values = [int(num) for num in line.split(",")]
        # part 1
        print(calculate(12, 2, input_values[:]))

        # part 2
        GOAL = 19690720
        for i in range(100):
            for j in range(100):
                if calculate(i, j, input_values[:]) == GOAL:
                    print(100 * i + j) 
                    break
Collapse
 
coolshaurya profile image
Shaurya

Yeah, the second part's wording was really confusing to me.

Collapse
 
jbristow profile image
Jon Bristow

Me too! I spent a good couple minutes trying to figure out why the sum of their test opcode didn't match the answer... 😭

Collapse
 
strahlistvan profile image
Stráhl István

Thank you! It really helps me to understand the second part.

Collapse
 
jojonarte profile image
Jojo Narte

My solution in JS.

const operations = {
  1: (inputArray, pos1, pos2, resultPos) => inputArray[inputArray[resultPos]] = inputArray[inputArray[pos1]] + inputArray[inputArray[pos2]],
  2: (inputArray, pos1, pos2, resultPos) => inputArray[inputArray[resultPos]] = inputArray[inputArray[pos1]] * inputArray[inputArray[pos2]],
};

const part1Solution = (inputArray, index = 0) => {
  const operation = inputArray[index];
  if (operation === 99) {
    return inputArray[0];
  }

  if (operation === 1 || operation === 2) {
    operations[operation](inputArray, index + 1, index + 2, index + 3)
  } else {
    throw new Error('Something went wrong!');
  }

  return part1Solution(inputArray, index + 4);
};

const part2Solution = (inputArray, targetValue) => {
  for (let i = 0; i <= 99; i ++) {
    for (let j = 0; j <= 99; j++) {
      const newArr = [...inputArray];
      newArr[1] = i;
      newArr[2] = j;
      const currentResult = part1Solution(newArr);
      console.log('CURRENT VALUE', currentResult);

      if (currentResult === targetValue) {
        return 100 * i + j;
      }
    }
  }

  throw new Error('Value not found');
};
Collapse
 
neilgall profile image
Neil Gall • Edited

In past Advent of Codes I've belligerently tried to write these machine simulators in Haskell or pure functional Kotlin or something else completely inappropriate. Maybe it's a sign of maturity that this year I've done it in C.

It's been a while since I've written C. The purity and simplicity is refreshing.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

typedef unsigned int opcode;

struct program {
    size_t size;
    opcode base[];
};

size_t program_size(size_t n) {
    return sizeof(size_t) + sizeof(opcode) * n;
}

struct program *load_program(char *filename) {
    size_t size = 100;
    struct program *program = (struct program *)malloc(program_size(size));
    size_t pos = 0;
    opcode cur = 0;
    FILE *f = fopen(filename, "rt");
    for (;;) {
        int c = fgetc(f);
        if (c == EOF || c == ',') {
            if (pos == size) {
                size *= 2;
                program = (struct program *)realloc(program, program_size(size));
            }
            program->base[pos++] = cur;
            cur = 0;
        } else if ('0' <= c && c <= '9') {
            cur = (cur * 10) + (c - '0');
        }
        if (c == EOF) break;
    }
    fclose(f);
    program->size = pos;
    return program;
}

struct program *copy_program(struct program *program) {
    size_t size = program_size(program->size);
    struct program *copy = (struct program *)malloc(size);
    memcpy(copy, program, size);
    return copy;
}

int read_program(struct program *program, size_t pos, opcode *x) {
    if (pos >= program->size) {
        return 1;
    }
    *x = program->base[pos];
    return 0;
}

int write_program(struct program *program, size_t pos, opcode value) {
    if (pos >= program->size) {
        return 1;
    }
    program->base[pos] = value;
    return 0;
}

void print_program(struct program *program) {
    printf("%u", program->base[0]);
    for (int i = 1; i < program->size; ++i) {
        printf(",%u", program->base[i]);
    }
    printf("\n");
}

int run_program(struct program *program, int debug) {
    size_t pc = 0;
    opcode i;
    while (read_program(program, pc, &i) == 0 && i != 99) {
        opcode x, y, z;
        if (read_program(program, pc+1, &x) != 0) return 1;
        if (read_program(program, x,    &x) != 0) return 1;
        if (read_program(program, pc+2, &y) != 0) return 1;
        if (read_program(program, y,    &y) != 0) return 1;
        if (read_program(program, pc+3, &z) != 0) return 1;
        if (debug) printf("pc=%u i=%u x=%u y=%u z=%u\n", pc, i, x, y, z);
        switch (i) {
            case 1: 
                if (write_program(program, z, x + y) != 0) return 1;
                break;
            case 2:
                if (write_program(program, z, x * y) != 0) return 1;
                break;
            default:
                return 1;
        }
        if (debug) print_program(program);
        pc += 4;
    }
    return 0;
}

void modify_program(struct program *program) {
    program->base[1] = 12;
    program->base[2] = 2;
}

void part1(struct program *original) {
    printf("Part 1\n");
    struct program *program = copy_program(original);
    modify_program(program);
    run_program(program, 0);
    print_program(program);
    free(program);  
}

void part2(struct program *original) {
    opcode noun = 0;
    opcode verb = 0;
    int result = 0;
    while (noun < 100 && verb < 100) {
        struct program *program = copy_program(original);
        write_program(program, 1, noun);
        write_program(program, 2, verb);
        int result = run_program(program, 0) == 0 && program->base[0] == 19690720;
        free(program);

        if (result) {
            break;
        }

        if (++noun > 99) {
            noun = 0;
            ++verb;
        }
    }
    printf("Part 2\nnoun = %u\nverb = %u\nresult = %u\n", noun, verb, 100*noun+verb);
}

static struct program test_program = {
    .size = 12,
    .base = { 1,9,10,3,2,3,11,0,99,30,40,50 }
};

int main(int argc, char **argv) {
    struct program *program = (argc < 2) ? load_program("input.txt") : &test_program;
    part1(program);
    part2(program);

    if (program != &test_program) {
        free(program);
    }
    return 0;
}
Collapse
 
jbristow profile image
Jon Bristow

Funny, I think they're harder to write initially in a functional language, but easier to modify and extend in later questions.

Collapse
 
neilgall profile image
Neil Gall

Extending to part 2 was easy enough. But the description today made it clear more is coming so I can't wait to find out!

Collapse
 
supriyasrivatsa profile image
Supriya Srivatsa

Some Kotlin code :)

fun main() {
    val input = readDayInputAsString(2).split(",").map { it.toInt() }
    val noun = 12
    val verb = 2

    println(solvePart1(input, noun, verb))  //5434663
    println(solvePart2(input, noun, verb))  //4559
}

private fun solvePart1(programInput: List<Int>, noun: Int, verb: Int) : Int {
    val input = programInput.toMutableList()
    input[1] = noun
    input[2] = verb
    for(i in input.indices step 4) {
        when(input[i]) {
            99 -> return input[0]
            1 -> input[input[i+3]] = input[input[i+1]] + input[input[i+2]]
            2 -> input[input[i+3]] = input[input[i+1]] * input[input[i+2]]
        }
    }
    return input[0]
}

private fun solvePart2(programInput: List<Int>, noun: Int, verb: Int) : Int {
    for(i in 0..99) {
        for(j in 0..99) {
            if(solvePart1(programInput, i, j) == 19690720)
                return 100 * i + j
        }
    }
    return 100 * noun + verb
}

On github: github.com/sup95/AdventOfCode-19/b...

Collapse
 
katafrakt profile image
Paweł Świątkowski • Edited

I can't believe no one has posted a solution in Janet yet! Oh wait, no, I totally believe it. It was also my first attempt in this language. I've been looking at it for some time already but only today I decided to give it a shot. It basically feels like a imperativ-ish LISP.

(def input "1,0,0,the rest of your input...")

(defn run-prog [noun verb]
  (def codes (map (fn [x] (scan-number x)) (string/split "," input)))
  (put codes 1 noun)
  (put codes 2 verb)

  (var curpos 0)
  (var curinstr (get codes curpos))
  (while (not (= curinstr 99))
    (def elem1 (get codes (+ curpos 1)))
    (def elem2 (get codes (+ curpos 2)))
    (def elem3 (get codes (+ curpos 3)))

    (if (= curinstr 1)
      (put codes elem3 (+ (get codes elem1) (get codes elem2))))

    (if (= curinstr 2)
      (put codes elem3 (* (get codes elem1) (get codes elem2))))

    (set curpos (+ curpos 4))
    (set curinstr (get codes curpos)))
  (get codes 0))

(print (string/format "%d" (run-prog 12 2)))

(loop [noun :range [0 99]]
  (loop [verb :range [0 99]]
    (def res (run-prog noun verb))
    (if (= res 19690720)
      (print (string/format "%d" (+ (* 100 noun) verb))))))
Collapse
 
lindakatcodes profile image
Linda Thompson

JavaScript checking in! I think it'll be interesting, seeing how we re-use this in future challenges. It did take me reading the end of part 2 a number of times before I understood what I was looking for! :)

I also feel like there should be a better way to process the second part...I saw folks on the subreddit talking about just changing the noun value until it's as high as can be while still below the desired output, then changing the verb, but it seems maybe there's unforeseen issues with that? I don't know, everyone I've seen so far is just nesting for loops so I'm not that worried about it. :)

// copy of initial input, so we can reset properly
let inputCopy = [...input];

// noun - input for address 1; verb - input for address 2
let noun = 12;
let verb = 2;

// for part 2 - desired output to match - output is final value at address 0 when program is done
const desiredOutput = 19690720;
// opcode 1 - get values at position 1&2 right after code, add together, store in position 3
// opcode 2 - get values at position 1&2 right after code, multiply, store in position 3
// opcode 99 - stop program
function intCode (op, a, b, c) {
  let valA = inputCopy[a];
  let valB = inputCopy[b];

  if (op === 1) {
    inputCopy[c] = valA + valB;
  } else if (op === 2) {
    inputCopy[c] = valA * valB;
  }
}

// run through memory input, following instructions until 99 is hit
function runProgram() {
  for (let i = 0; i < inputCopy.length; i += 4) {
    if (inputCopy[i] === 99) {
      break;
    }
    intCode(inputCopy[i], inputCopy[i+1], inputCopy[i+2], inputCopy[i+3]);
  }
}

// for part 1 - insert noun & verb provided
inputCopy[1] = noun;
inputCopy[2] = verb;

runProgram();
console.log(`Part 1: position 0 value is ${inputCopy[0]}`);

// part 2 - test different values for noun & verb, insert into memory, run program - when desired output is matched, log noun & verb used & end cycle

for (let n = 0; n < 100; n++) {
  noun = n;
  let found = false;
  for (let v = 0; v < 100; v++) {
    verb = v;

    // reset copy to initial input, then replace noun & verb
    inputCopy = [...input];
    inputCopy[1] = noun;
    inputCopy[2] = verb;

    runProgram();
    let currentOutput = inputCopy[0];

    if (currentOutput === desiredOutput) {
      found = true;
      break;
    }
  }
  if (found) {
    break;
  }
}

console.log(`desired noun and verb are ${noun} & ${verb}; output value is ${inputCopy[0]}`);
console.log(`Part 2 - computed value is ${100 * noun + verb}`);
Collapse
 
jakcharvat profile image
Jakub Charvat • Edited

Here's my Python solution - nothing special or well optimised, but it works:

def puzzle_one():
    with open('inputs/day_2.txt') as file: 
        input = list(map(lambda a: int(a), file.read().split(',')))

    opcode, index = int(input[0]), 0
    while opcode != 99:
        if opcode == 1:
            sum = input[input[index + 1]] + input[input[index + 2]]
            input[input[index + 3]] = sum
        elif opcode == 2:
            product = input[input[index + 1]] * input[input[index + 2]]
            input[input[index + 3]] = product
        elif opcode != 99:
            print('Something went wrong')

        index += 4
        opcode = input[index]

    print(f'Puzzle 1 - {input[0]}')

def puzzle_two():

    goal = 19690720

    with open('inputs/day_2.txt') as file:
        input = list(map(lambda a: int(a), file.read().split(',')))

    found = False
    for noun in range(100):
        for verb in range(100):
            memory = input[:]
            memory[1] = noun
            memory[2] = verb

            opcode, index = int(input[0]), 0
            while opcode != 99:
                if opcode == 1:
                    sum = memory[memory[index + 1]] + memory[memory[index + 2]]
                    memory[memory[index + 3]] = sum
                elif opcode == 2:
                    product = memory[memory[index + 1]] * memory[memory[index + 2]]
                    memory[memory[index + 3]] = product
                elif opcode != 99:
                    print('Something went wrong')

                index += 4
                opcode = memory[index]

            output = memory[0]

            if output == goal:
                final_noun = noun
                final_verb = verb
                found = True
                break
        if found == True:
            break
    print(f'Puzzle 2 - {100 * final_noun + final_verb}')

puzzle_one()
puzzle_two()
Collapse
 
mellen profile image
Matt Ellen-Tsivintzeli

In the browser!

function process(one, two)
{
    let pretext = document.getElementsByTagName('pre')[0].innerHTML;
    let instructions = pretext.split(',').map(i => parseInt(i, 10));
    instructions[1] = one;
    instructions[2] = two;
    for(let i = 0; i < instructions.length; i += 4)
    {
    let inst = instructions[i];
    if(inst == 99)
    {
        break;
    }

    let answerPos = instructions[i+3];
    let ai = instructions[i+1];
    let bi = instructions[i+2];

    if(inst == 1)
    {
        instructions[answerPos] = instructions[ai] + instructions[bi];
    }
    else if(inst == 2)
    {
        instructions[answerPos] = instructions[ai] * instructions[bi];
    }
    }

    return instructions[0];
}

/* part 2 */
let done = false;
for(let a = 0; a < 100 && !done; a++)
{
  for(let b = 0; b < 100; b++)
  {
    let n = process(a, b);
    if(n == 19690720)
    {
      console.log(a, b)
      done = true;
      break;
    }
  }
}
Collapse
 
rpalo profile image
Ryan Palo

Here's my Rust code. I was lucky that I caught that bit at the very end of the instructions about setting the initial values. Otherwise I probably would have spiraled trying to figure out why my answer to Part 1 was wrong.

Now I just need to refactor this out to make it easy to extend for future days!

/// Day 2: 1202 Program Alarm
/// 
/// Create an "Intcode" computer that can calculate a gravity slingshot

use std::fs;

/// Parses the input.  Expects a single line of integers separated by
/// commas
fn parse_input() -> Vec<usize> {
    let text: String = fs::read_to_string("data/day2.txt").unwrap();
    let cleaned = text.trim();
    cleaned.split(",").map( |c| c.parse().unwrap()).collect()
}

/// An Intcode Interpreter is a simple virtual machine that uses opcodes
/// to modify its internal memory
struct IntcodeInterpreter {
    ints: Vec<usize>,
    ip: usize,
}

impl IntcodeInterpreter {
    pub fn new(ints: Vec<usize>) -> Self {
        Self { ints, ip: 0}
    }

    /// Sets a memory address to a value
    pub fn set(&mut self, position: usize, value: usize) {
        self.ints[position] = value;
    }

    /// Reads from a memory address
    pub fn get(&self, position: usize) -> usize {
        self.ints[position]
    }

    /// Runs the program in memory until the stopcode (99) is reached
    /// (To be refactored and generalized)
    pub fn run(&mut self) {
        while self.ints[self.ip] != 99 {
            let opcode = self.ints[self.ip];
            let in1 = self.ints[self.ip + 1];
            let in2 = self.ints[self.ip + 2];
            let out = self.ints[self.ip + 3];

            if opcode == 1 {
                self.ints[out] = self.ints[in1] + self.ints[in2];
            } else if opcode == 2 {
                self.ints[out] = self.ints[in1] * self.ints[in2];
            } else {
                panic!("Unrecognized opcode {}!", opcode);
            }
            self.ip = (self.ip + 4) % self.ints.len();
        }
    }
}

/// Given a desired output, hunts through the possible values of position
/// 1 and 2 (termed "noun" and "verb") by brute force until the output
/// is found.
fn find_output(output: usize, ints: Vec<usize>) -> (usize, usize) {
    for noun in 0..=99 {
        for verb in 0..=99 {
            let mut interpreter = IntcodeInterpreter::new(ints.clone());
            interpreter.set(1, noun);
            interpreter.set(2, verb);
            interpreter.run();
            if interpreter.get(0) == output {
                return (noun, verb);
            }
        }
    }

    panic!("Couldn't find output!");
}

pub fn run() {
    let ints = parse_input();

    println!("Part 1:");
    let mut interpreter = IntcodeInterpreter::new(ints.clone());
    interpreter.set(1, 12);
    interpreter.set(2, 2);
    interpreter.run();
    println!("After running, position 0 is: {}", interpreter.get(0));

    println!("Part 2:");
    let (noun, verb) = find_output(19690720, ints.clone());
    println!("Noun: {}, Verb: {}", noun, verb);
    println!("Secret code is: {}{}", noun, verb);

}
Collapse
 
yordiverkroost profile image
Yordi Verkroost

Pretty rough assignment on day two already, but got it working in the end! I've solved it in Elixir. Here's my solution for part two:

defmodule Aoc19.Day2b do
  @moduledoc false

  alias Aoc19.Utils.Common

  @add 1
  @multiply 2
  @halt 99
  @opcode [@add, @multiply, @halt]

  def start(input_location) do
    opcode =
      input_location
      |> Common.read_numbers(",")
      |> verify()

    try do
      for i <- 1..100 do
        for j <- 1..100 do
          _ =
            case result(opcode, i, j) do
              19_690_720 ->
                throw(100 * i + j)

              _ ->
                nil
            end
        end
      end
    catch
      result -> result
    end
  end

  defp result(opcode, noun, verb) do
    opcode = replace(opcode, noun, verb)

    opcode
    |> intcode(opcode, 0)
    |> Enum.at(0)
  end

  defp verify([x | _] = opcode) when x in @opcode, do: opcode

  defp replace(opcode, noun, verb) do
    opcode
    |> List.update_at(1, fn _ -> noun end)
    |> List.update_at(2, fn _ -> verb end)
  end

  defp intcode([@halt | _], opcode, _), do: opcode

  defp intcode([@add, int1, int2, result | _], opcode, index) do
    opcode =
      List.update_at(opcode, result, fn _ ->
        Enum.at(opcode, int1) + Enum.at(opcode, int2)
      end)

    index = index + 4
    {_, rest} = Enum.split(opcode, index)

    intcode(rest, opcode, index)
  end

  defp intcode([@multiply, int1, int2, result | _], opcode, index) do
    opcode =
      List.update_at(opcode, result, fn _ ->
        Enum.at(opcode, int1) * Enum.at(opcode, int2)
      end)

    index = index + 4
    {_, rest} = Enum.split(opcode, index)

    intcode(rest, opcode, index)
  end
end

Check out part one here