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?

Latest comments (24)

Collapse
 
bretthancox profile image
bretthancox • Edited

I wrote my day 2 up here:

Day 2

Written in Clojure

Collapse
 
thibpat profile image
Thibaut Patel

I got a couple of issues with types in JavaScript, but here is the solution:

Collapse
 
noakpalander profile image
Noak Palander • Edited

A bit late to the party, got my c++17 code working however

#include <fstream>
#include <vector>
#include <string>
#include <optional>
#include <optional>
#include <iostream>
#include <cmath>
#include <sstream>

std::optional<std::vector<unsigned int>> ReadFile(const std::string& filename) {
std::ifstream reader(filename.c_str());

    if (reader) {
        std::string data;
        reader >> data;

        std::stringstream ss(data);

        std::vector<unsigned int> out;
        for (unsigned int val; ss >> val;) {
            out.emplace_back(val);
            if (ss.peek() == ',')
                ss.ignore();
        }

        return out;
    }

    return {};
}

unsigned int Calculate(unsigned int first, unsigned int second, std::vector<unsigned int> data) {
    // Set initial values
    data[1] = first;
    data[2] = second;

    for (unsigned int i = 0; i < data.size(); i += 4) {
        const unsigned int FIRST = data[i + 1];
        const unsigned int SECOND = data[i + 2];
        const unsigned int LOCATION = data[i + 3];


        switch (data[i]) {
            // Addition
            case 1:
                data[LOCATION] = data[FIRST] + data[SECOND];
                break;

            // Multiplication
            case 2:
                data[LOCATION] = data[FIRST] * data[SECOND];
                break;

            // Terminate
            case 99:
                return data[0];
                break;

            default:
                break;
        }
    }

    return data[0];
}


int main() {
    if (auto data = ReadFile("puzzle_input"); data.has_value()) {
        /* Part one */
        unsigned int ans1 = Calculate(12, 2, data.value());
        std::cout << "The answer to part_1 is " << ans1 << "\n";

        /* Part 2 */
        // Reset data set
        data.value() = ReadFile("puzzle_input").value();

        const unsigned int GOAL = 19690720;

        // 100*100 is enough iterations
        for (unsigned int noun = 0; noun < 100; noun++)
            for (unsigned int verb = 0; verb < 100; verb++)
                if (Calculate(noun, verb, data.value()) == GOAL)
                    std::cout << "\nThe answer to part_2 is " << 100 * noun + verb << "\n";
    }
    else
        std::cerr << "Failed to read puzzle_input!\n";

    return 0;
}
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

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
 
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
 
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
 
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
 
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
 
coolshaurya profile image
Shaurya

My solution in Rust

fn part_a(mut input: Vec<u32>) -> u32 {
    input[1] = 12;
    input[2] = 2;

    let mut i: usize = 0;
    while i < input.len() {
        if i % 4 == 0 {
            let opcode = input[i];
            if opcode == 99 {
                break;
            }
            let index_1 = input[i + 1] as usize;
            let index_2 = input[i + 2] as usize;
            let index_3 = input[i + 3] as usize;

            if opcode == 1 {
                input[index_3] = input[index_1] + input[index_2];
            } else if opcode == 2 {
                input[index_3] = input[index_1] * input[index_2];
            }
        }
        i = i + 1;
    }

    input[0]
}

fn part_b(input: Vec<u32>) -> u32 {
    let mut i = 0;
    let mut j = 0;
    while i < 100 {
        let mut input = input.clone();

        if exec_intcode_program(input, i, j) == 19690720 {
            return 100 * i + j;
            break;
        }

        if j == 99 {
            i = i + 1;
            j = 0;
        }
        j = j + 1;
    }

    0
}
fn exec_intcode_program(input: Vec<u32>, addl_inp_1: u32, addl_inp_2: u32) -> u32 {
    let mut input = input.clone();

    input[1] = addl_inp_1;
    input[2] = addl_inp_2;

    let mut i: usize = 0;
    while i < input.len() {
        if i % 4 == 0 {
            let opcode = input[i];
            if opcode == 99 {
                break;
            }
            let index_1 = input[i + 1] as usize;
            let index_2 = input[i + 2] as usize;
            let index_3 = input[i + 3] as usize;

            if opcode == 1 {
                input[index_3] = input[index_1] + input[index_2];
            } else if opcode == 2 {
                input[index_3] = input[index_1] * input[index_2];
            }
        }
        i = i + 1;
    }

    input[0]
}