Skip to content
loading...

Advent of Code 2019 Solution Megathread - Day 5: Sunny with a Chance of Asteroids

Jon Bristow on December 05, 2019

Oof! The opcode from Day 2 is back with a vengeance. Day 5 - The Problem As we feared, the IntCode interpreter is back. It looks the ai... [Read Full]
markdown guide
 

OK, I'm really, really happy with how my refactoring on Day 2 came out. Even the addition of the input modes fit into place in nice, neat, decoupled chunks. And then it was a matter of letting my interpreter know about all of the new opcodes.

Note: I was very much not expecting my Day 2 setup to hold up this well. I think I got exceptionally lucky at how today's puzzle was structured. I'm fully expecting to run into a future IntCode puzzle in the coming days that blows my architecture out of the water and makes me start over.

My actual Day 5 module is not very impressive:

/// Day 5: Sunny with a Chance of Asteroids
/// 
/// Test out the Thermal Environment Supervision Terminal
/// in the Intcode Interpreter

use std::fs;
use crate::intcode;

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

pub fn run() {
    let ints = parse_input();
    let mut interpreter = intcode::IntcodeInterpreter::new(ints);
    interpreter.run();
}

Here's where all the work gets done. Also, side note, as a happy fallout of how Rust's stdin handles itself, I can either enter values or pipe them in one-per-line, if future input gets more complicated :)

use std::io;
use std::fs;

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

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

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

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

    /// Shows the memory for debugging
    pub fn print(&self) {
        println!("{:?}", self.memory);
    }

    /// Get the current instruction
    pub fn current_instruction(&self) -> isize {
        self.get(self.ip) % 100
    }

    /// Runs the program in memory until the stopcode (99) is reached
    /// 
    /// All new ops should have their own method.
    /// They take no rust args, but read in args as needed and
    /// shift the instruction pointer when they're done.
    /// Steps should be the number of args used + 1 for the opcode
    pub fn run(&mut self) {
        loop {
            match self.current_instruction() {
                1 => self.op1(),
                2 => self.op2(),
                3 => self.op3(),
                4 => self.op4(),
                5 => self.op5(),
                6 => self.op6(),
                7 => self.op7(),
                8 => self.op8(),
                99 => return,
                _ => panic!("Unrecognized opcode {}.", self.get(self.ip)),
            };
        }
    }

    /// Reads a number from STDIN
    fn read_stdin() -> isize {
        let mut buffer = String::new();
        io::stdin().read_line(&mut buffer).expect("STDIN read failed.");
        buffer.trim().parse::<isize>().unwrap()
    }

    /// Write a number to STDOUT
    fn write_stdout(number: isize) {
        println!("{}", number);
    }

    /// Process the parameter mode and provide the value given
    /// as a parameter
    fn arg(&self, offset: usize) -> isize {
        let new_index = (self.ip + offset) % self.memory.len();
        let mode = (self.memory[self.ip] / 10isize.pow(1 + offset as u32)) % 2;
        if mode == 1 {
            self.memory[new_index]
        } else if mode == 0 {
            self.get(self.memory[new_index] as usize)
        } else {
            panic!("Unknown parameter mode {}", mode);
        }
    }

    /// Returns the address to write output to
    fn output_address(&self, offset: usize) -> usize {
        let new_index = (self.ip + offset) % self.memory.len();
        self.memory[new_index] as usize
    }

    /// Steps the IP forward "count" steps, wrapping if needed
    fn step(&mut self, count: usize) {
        self.ip = (self.ip + count) % self.memory.len();
    }

    /// Add [1] + [2], store in [3]
    fn op1(&mut self) {
        let in1 = self.arg(1);
        let in2 = self.arg(2);
        let out = self.output_address(3);

        self.set(out, in1 + in2);

        self.step(4);
    }

    /// Mult [1] * [2], store in [3]
    fn op2(&mut self) {
        let in1 = self.arg(1);
        let in2 = self.arg(2);
        let out = self.output_address(3);

        self.set(out, in1 * in2);

        self.step(4);
    }

    /// Read one value from STDIN and store it in [1]
    fn op3(&mut self) {
        let out = self.output_address(1);

        self.set(out, Self::read_stdin());

        self.step(2);
    }

    /// Read [1] and send it to STDOUT
    fn op4(&mut self) {
        Self::write_stdout(self.arg(1));

        self.step(2);
    }

    /// If [1] != 0, set IP -> [2], else nothing
    fn op5(&mut self) {
        if self.arg(1) != 0 {
            self.ip = self.arg(2) as usize;
        } else {
            self.step(3);
        }
    }

    /// if [1] == 0, set IP -> [2], else nothing
    fn op6(&mut self) {
        if self.arg(1) == 0 {
            self.ip = self.arg(2) as usize;
        } else {
            self.step(3);
        }
    }

    /// if [1] < [2], set [3] to 1, else 0
    fn op7(&mut self) {
        let out = self.output_address(3);

        if self.arg(1) < self.arg(2) {
            self.set(out, 1);
        } else {
            self.set(out, 0);
        }

        self.step(4);
    }

    /// if [1] == [2], set [3] to 1, else 0
    fn op8(&mut self) {
        let out = self.output_address(3);

        if self.arg(1) == self.arg(2) {
            self.set(out, 1);
        } else {
            self.set(out, 0);
        }

        self.step(4);
    }
}
 

Today's code is a lot uglier than yesterday's, but, oh well, it works. My JavaScript solution:

const opcodes = {
    1: {
            func: (intcode, posA, posB, posC, modA=0, modB=0) =>
                intcode[posC] = applyMode(intcode, posA, modA) + applyMode(intcode, posB, modB),
            next: 4,
        },
    2: {
            func: (intcode, posA, posB, posC, modA=0, modB=0) =>
                intcode[posC] = applyMode(intcode, posA, modA) * applyMode(intcode, posB, modB),
            next: 4,
        },
    3: {
            func: (intcode, pos, input) => intcode[pos] = input,
            next: 2,
        },
    4: {
            func: (intcode, pos, _, __, mod=0) => applyMode(intcode, pos, mod),
            next: 2,
        },
    5: {
            func: (intcode, posA, posB, _, modA=0, modB=0, position) =>
                opcodes[5].next = applyMode(intcode, posA, modA) != 0
                    ? applyMode(intcode, posB, modB) - position
                    : 3,
            next: 3
    },
    6: {
            func: (intcode, posA, posB, _, modA=0, modB=0, position) =>
                opcodes[6].next = applyMode(intcode, posA, modA) == 0
                    ? applyMode(intcode, posB, modB) - position
                    : 3,
            next: 3,
        },
    7: {
            func: (intcode, posA, posB, posC, modA=0, modB=0) =>
                intcode[posC] = applyMode(intcode, posA, modA) < applyMode(intcode, posB, modB)
                    ? 1
                    : 0,
            next: 4,
    },
    8: {
            func: (intcode, posA, posB, posC, modA=0, modB=0) =>
                intcode[posC] = applyMode(intcode, posA, modA) == applyMode(intcode, posB, modB)
                    ? 1
                    : 0,
            next: 4,
    },
}

const applyMode = (intcode, pos, mode) => mode == 0 ? intcode[pos] : pos

const computeOpcode = (intcode, input, position=0, diagnosticCode) => {
    if (intcode[position] == 99) return diagnosticCode
    const [ oc, _, m1, m2, m3 ] = intcode[position].toString().split('').reverse().map(x => +x)
    const [ a, b, dest ] = intcode.slice(position + 1)
    const bInput = oc == 3 ? input : b
    diagnosticCode = opcodes[oc].func(intcode, a, bInput, dest, m1, m2, position)
    if (oc == 4) console.log(diagnosticCode)
    return computeOpcode(intcode, input, position + opcodes[oc].next, diagnosticCode)
}

module.exports = input => {
    const data = input.split(',').map(x => +x)
    const dataOne = Object.assign([], data)
    const dataTwo = Object.assign([], data)
    const partOne = computeOpcode(dataOne, 1)
    console.log('Part Two')
    const partTwo = computeOpcode(dataTwo, 5)
    return({ partOne, partTwo })
}
 

I really enjoyed this one, actually. This is so much better than this time last year, when I was doing all kinds of input parsing tricks and tripping over log files. Give me straight-up integers in a comma-separated list any day!

I decided to make my code really readable and branched out into creating 2 Python enums and a class. It made writing the code feel like a snap, as I no longer had to wrap my brain around all kinds of numbers and could hang my head on descriptive operations.

A couple of the lines are unnecessary (like writing the repl function as well as there's a DRY violation with parameter interpretation)...but I really like the ternary if/then/else operator in Python. I'm used to Java's print(x == 5 ? "yup" : "nope")... print("yup" if x is 5 else "nope") is far superior!

from enum import Enum

# Returns the number at the given position (0 being the rightmost)
def get_nth_digit(n, number):
    return number // 10**n % 10

class Operation(Enum):
    ADDITION = 1
    MULTIPLICATION = 2
    INPUT = 3
    OUTPUT = 4
    JUMP_IF_TRUE = 5
    JUMP_IF_FALSE = 6
    LESS_THAN = 7
    EQUALS = 8
    TERMINATION = 99

class Mode(Enum):
    POSITION = 0
    IMMEDIATE = 1

class Instruction:
    def __init__(self, opcode):
        # instruction: 1 is add, 2 is multiply, 3 is input, 4 is output, 99 is end
        self.operation = Operation(get_nth_digit(1, opcode) * 10 + get_nth_digit(0, opcode))
        # mode: 0 is indirect, 1 is immediate
        self.modes = list(map(Mode, [get_nth_digit(2, opcode), get_nth_digit(3, opcode), get_nth_digit(4, opcode)]))

    def __str__(self):
        return "{}, {}".format(repr(self.operation), self.modes)


def opcode_run(ops):
    i = 0
    while ops[i] != 99:
        instruction = Instruction(ops[i])
        if instruction.operation is Operation.ADDITION:
            first = ops[i+1] if instruction.modes[0] is Mode.IMMEDIATE else ops[ops[i+1]]
            second = ops[i+2] if instruction.modes[1] is Mode.IMMEDIATE else ops[ops[i+2]]
            result = first + second
            # the last mode should *always* be POSITION
            ops[ops[i+3]] = result
            i += 4
        elif instruction.operation is Operation.MULTIPLICATION:
            first = ops[i+1] if instruction.modes[0] is Mode.IMMEDIATE else ops[ops[i+1]]
            second = ops[i+2] if instruction.modes[1] is Mode.IMMEDIATE else ops[ops[i+2]]
            val = first * second
            # the last mode should *always* be POSITION
            ops[ops[i+3]] = val
            i += 4
        elif instruction.operation is Operation.INPUT:
            stuff = input("Please enter ID: --> ")
            ops[ops[i+1]] = int(stuff)
            i += 2
        elif instruction.operation is Operation.OUTPUT:
            print (ops[ops[i+1]])
            i += 2
        elif instruction.operation is Operation.JUMP_IF_TRUE:
            first = ops[i+1] if instruction.modes[0] is Mode.IMMEDIATE else ops[ops[i+1]]
            second = ops[i+2] if instruction.modes[1] is Mode.IMMEDIATE else ops[ops[i+2]]
            if first != 0:
                i = second
            else:
                i += 3
        elif instruction.operation is Operation.JUMP_IF_FALSE:
            first = ops[i+1] if instruction.modes[0] is Mode.IMMEDIATE else ops[ops[i+1]]
            second = ops[i+2] if instruction.modes[1] is Mode.IMMEDIATE else ops[ops[i+2]]
            if first == 0:
                i = second
            else:
                i += 3
        elif instruction.operation is Operation.LESS_THAN:
            first = ops[i+1] if instruction.modes[0] is Mode.IMMEDIATE else ops[ops[i+1]]
            second = ops[i+2] if instruction.modes[1] is Mode.IMMEDIATE else ops[ops[i+2]]
            ops[ops[i+3]] = 1 if first < second else 0
            i += 4
        elif instruction.operation is Operation.EQUALS:
            first = ops[i+1] if instruction.modes[0] is Mode.IMMEDIATE else ops[ops[i+1]]
            second = ops[i+2] if instruction.modes[1] is Mode.IMMEDIATE else ops[ops[i+2]]
            ops[ops[i+3]] = 1 if first == second else 0
            i += 4
    print ("HALT!")

# Initialize: open file, turn all op codes into integers
with open('day05.txt') as f:
    # split line into operation list
    opsAsStrings = f.read().split(",")
    # turn them all into integers
    ops = list(map(int, opsAsStrings))

# Part One: Enter 1 when prompted and enter number right before HALT!
# Part Two: Enter 5 when prompted and enter number right before HALT!
myOps = ops.copy()
opcode_run(myOps)
 

I built my original solution to this on top of my Day 2 solution, but was really unhappy about how that solution had turned out. So, I spent time over the weekend and into this morning building out a more clean solution around the Intcode Computer (knowing that it's going to likely be used more later).

The implementation is below (and also accessible here), along with the new execution calls for Day 2:

;; incode_computer.clj
(ns aoc2019.intcode-computer)

(def operations
  {1 {:params 3, :action (fn [p1 p2 _] {:output (+ p1 p2)})}
   2 {:params 3, :action (fn [p1 p2 _] {:output (* p1 p2)})}
   3 {:params 1, :action (fn [_]
                           {:output (Integer/parseInt (read-line))})}
   4 {:params 1, :action (fn [p1] (println p1) {})}
   5 {:params 2, :action (fn [p1 p2] (if (not= 0 p1) {:jump p2} {}))}
   6 {:params 2, :action (fn [p1 p2] (if (= 0 p1) {:jump p2} {}))}
   7 {:params 3, :action (fn [p1 p2 _] (if (< p1 p2) {:output 1} {:output 0}))}
   8 {:params 3, :action (fn [p1 p2 _] (if (= p1 p2) {:output 1} {:output 0}))}
   99 {:params 0}})

(defn fetch
  "Retrieves the opcode, and the number of arguments for that opcode"
  [machine]
  (let [opcode (get (:instructions machine) (:program-counter machine))
        action (mod opcode 100)]
    {:opcode opcode
     :action action
     :params (:params (get operations action))}))

(defn opcode-to-parameters
  [opcode]
  (let [modes (->> opcode
                   (format "%05d")
                   (into [])
                   (take 3)
                   (reverse))]
    (map #(condp = %
            \0 {:mode :direct}
            \1 {:mode :immediate}) modes)))

(defn retrieve-args
  "Retrieves the parameters of the instruction from the code list, and
  returns a map of their positions, values, and mode"
  [machine fetched-instruction]
  (let [in-params (take (:params fetched-instruction)
                        (drop (inc (:program-counter machine)) (:instructions machine)))
        op-params (opcode-to-parameters (:opcode fetched-instruction))
        joiner (fn [in-param op-param]
                 {:mode (:mode op-param)
                  :param-pos in-param
                  :param-val (get (:instructions machine) in-param)})]
    (assoc fetched-instruction :params
           (map joiner in-params op-params))))

(defn execute
  "Executes the instruction provided, and returns any results of that
  execution."
  [instruction]
  (let [params (map #(condp = (:mode %)
                       :direct (:param-val %)
                       :immediate (:param-pos %)) (:params instruction))
        action (:action (get operations (:action instruction)))]
    (if action
      (assoc instruction :result (apply action params))
      (assoc instruction :result :stop))))

(defn apply-changes
  "Applies any changes based on the result of the instruction. Returns a
  potentially modified instruction list."
  [machine instruction-with-results]
  (let [result (:result instruction-with-results)
        inc-program-counter (fn []
                              (+ 1 (:program-counter machine)
                                 (count (:params instruction-with-results))))]
    (if (:jump result)
      (assoc machine :program-counter (:jump result))
      (if (= result :stop)
        (assoc machine :stop true)
        (if (:output result)
          (let [adjusted-instructions (assoc (:instructions machine)
                                             (:param-pos (last (:params instruction-with-results)))
                                             (:output result))]
            (assoc machine :instructions adjusted-instructions :program-counter (inc-program-counter)))
          (assoc machine :program-counter (inc-program-counter)))))))

;; day2.clj
(ns aoc2019.day2
  (:require [clojure.string :as st]
            [aoc2019.intcode-computer :as ic]))

(defn parse-input
  "Converts input into atom holding instruction integers."
  [input]
  (map #(Integer/parseInt %) (st/split (st/trim-newline input) #",")))

(defn new-calculate
  [input]
  (let [instructions (vec (parse-input input))]
    (loop [machine {:instructions instructions
                    :program-counter 0
                    :stop false}]
      (if (:stop machine)
        (first (:instructions machine))
        (recur (->> (ic/fetch machine)
                    (ic/retrieve-args machine)
                    (ic/execute)
                    (ic/apply-changes machine)))))))

(defn preprocess-input
  ([input]
   (preprocess-input input 12 2))
  ([input noun verb]
   (let [parsed-input (vec (parse-input input))]
     (st/join "," (-> parsed-input
                      (assoc 1 noun)
                      (assoc 2 verb))))))

(defn p2019-02-part1
  [input]
  (new-calculate (preprocess-input input)))

(defn p2019-02-part2
  "Using the calculator from part 1, determine the proper inputs for our expected value."
  [input]
  (let [expected-result 19690720
        noun (atom 0)
        verb (atom 0)]
    (while (not= expected-result (new-calculate (preprocess-input input @noun @verb)))
      (swap! verb inc)
      (if (= @verb 100)
        (do
          (swap! noun inc)
          (reset! verb 0))))
    (+ @verb (* 100 @noun))))

 

Jayyy-zus today was a long challenge 😫
I don't like it much when AoC does that, and when it back-reference problems from past days.

Anyway, here's my solution in JavaScript - just for part 2, as part 1 is basically embedded, you just have to change the input value to 1.

Not particularly proud of this, as the challenge itself was long but a bit bland. It's just parse, get instruction, execute, rinse and repeat. The only interesting part is the use of a generator function I guess...

const parameterCount = { 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 99: 0 };
const codes = input.split(',').map(Number);

let instructionPointer = 0;
function* getInstructions() {
  while (instructionPointer < codes.length) {
    const instruction = codes[instructionPointer];
    const opcode = instruction % 100;
    const modes = Array.from({ length: 3 }, (_, index) => Math.floor(instruction / 10 ** (index + 2) % 10));
    const paramCount = parameterCount[opcode];
    const params = codes.slice(instructionPointer + 1, instructionPointer + paramCount + 1);
    instructionPointer += paramCount + 1;
    yield { opcode, modes, params };
  }
}

function getValue(index, mode) {
  return mode === 0 ? codes[index] : index;
}

function execute({ opcode, modes, params }) {
  switch (opcode) {
    case 1:
      codes[params[2]] = getValue(params[0], modes[0]) + getValue(params[1], modes[1]);
      break;
    case 2:
      codes[params[2]] = getValue(params[0], modes[0]) * getValue(params[1], modes[1]);
      break;
    case 3:
      codes[params[0]] = 5;
      break;
    case 4:
      return getValue(params[0], modes[0]);
    case 5:
      if (getValue(params[0], modes[0])) {
        instructionPointer = getValue(params[1], modes[1]);
      }
      break;
    case 6:
      if (getValue(params[0], modes[0]) === 0) {
        instructionPointer = getValue(params[1], modes[1]);
      }
      break;
    case 7:
      codes[params[2]] = +(getValue(params[0], modes[0]) < getValue(params[1], modes[1]));
      break;
    case 8:
      codes[params[2]] = +(getValue(params[0], modes[0]) === getValue(params[1], modes[1]));
      break;
  }
}

let lastOutput;
for (const instruction of getInstructions()) {
  if (instruction.opcode === 99) {
    break;
  }
  const output = execute(instruction);
  if (typeof output === 'number') {
    lastOutput = output;
  }
}
console.log(lastOutput);

As usual, I'm publishing my solutions on GitHub.

 

Here's my Python solution!

def get_modes(modes):
    return [int(mode) for mode in [modes[2], modes[1], modes[0], modes[3:]]]


def get_param(mode, increment, idx, data):
    if mode == 0:
        return data[data[idx + increment]]
    return data[idx + increment]

def get_params(mode1, mode2, idx, data):
    return get_param(mode1, 1, idx, data), get_param(mode2, 2, idx, data)


def add(mode1, mode2, idx, data):
    param1, param2 = get_params(mode1, mode2, idx, data)
    data[data[idx + 3]] = param1 + param2

def multiply(mode1, mode2, idx, data):
    param1, param2 = get_params(mode1, mode2, idx, data)
    data[data[idx + 3]] = param1 * param2

def less_than(mode1, mode2, idx, data):
    param1, param2 = get_params(mode1, mode2, idx, data)
    data[data[idx + 3]] = 1 if param1 < param2 else 0

def equals(mode1, mode2, idx, data):
    param1, param2 = get_params(mode1, mode2, idx, data)
    data[data[idx + 3]] = 1 if param1 == param2 else 0

def jump_if_true(mode1, mode2, idx, data):
    param1, param2 = get_params(mode1, mode2, idx, data)
    return param2 if param1 != 0 else idx + 3

def jump_if_false(mode1, mode2, idx, data):
    param1, param2 = get_params(mode1, mode2, idx, data)
    return param2 if param1 == 0 else idx + 3


def calculate(data, input_val):
    idx = 0
    diagnostic_code = None
    while data[idx] != 99:
        mode1, mode2, mode3, opcode = get_modes(f"{data[idx]:05}")
        if opcode == 1:
            add(mode1, mode2, idx, data)
            idx += 4
        elif opcode == 2:
            multiply(mode1, mode2, idx, data)
            idx += 4
        elif opcode == 3:
            data[data[idx+1]] = input_val
            idx += 2
        elif opcode == 4:
            diagnostic_code = data[data[idx + 1]]
            idx += 2
        elif opcode == 5:
            idx = jump_if_true(mode1, mode2, idx, data)
        elif opcode == 6:
            idx = jump_if_false(mode1, mode2, idx, data)
        elif opcode == 7:
            less_than(mode1, mode2, idx, data)
            idx += 4
        elif opcode == 8:
            equals(mode1, mode2, idx, data)
            idx += 4
    return diagnostic_code


with open("input.txt") as _file:
    for line in _file:
        input_vals = [int(num) for num in line.split(",")]
        print(f"Part 1: {calculate(input_vals[:], 1)}")
        print(f"Part 2: {calculate(input_vals[:], 5)}")

 

Took lots of my time today. Finally in swift


extension BinaryInteger {
    var digits: [Int] {
        return String(describing: self).compactMap { Int(String($0)) }
    }
}

func computeIntCode(_ input: [Int], _ inputId: Int = 1) -> Int {
    var numbers = input
    var output: Int = -1
    func add(_ aPosition: Int, _ bPosistion: Int) -> Int {
        return numbers[aPosition] + numbers[bPosistion]
    }

    func multiply(_ aPosition: Int, _ bPosistion: Int) -> Int {
        return numbers[aPosition] * numbers[bPosistion]
    }

    var index = 0
    while true {
        let optcode = numbers[index]
        let instruction = ((optcode.digits.dropLast().last ?? 0) * 10) + optcode.digits.last!
        let numberOfValues = 4
        var parameters: [Int] = []
        if (instruction != 99 && instruction != 4) {
            parameters = Array(numbers[index+1...index+numberOfValues])
        }
        let digits = optcode.digits

        func getValue(_ i: Int) -> Int {
            return digits.dropLast(2 + i).last == 1 ? numbers[index + 1 + i] : numbers[numbers[index + 1 + i]]
        }
        func getValues() -> [Int] {
            var values : [Int] = []
            values.append(getValue(0))
            values.append(getValue(1))
            print("v: \(values)")

            return values
        }


        if instruction == 01 {
            let values = getValues()
            let result = values[0] + values[1]
            numbers[parameters[2]] = result
            index = index + numberOfValues
        }
        else if instruction == 02 {
            let values = getValues()
            let result = values[0] * values[1]
            numbers[parameters[2]] = result
            index = index + numberOfValues
        }
        else if optcode == 3 || instruction == 03 {
            let parameter = numbers[index + 1]
            let input: Int = inputId;
            numbers[parameter] = input
            index = index + 2
        }
        else if optcode == 4 || instruction == 04 {
//            let value = getValue(0)
//            print(value)
            let parameter = numbers[index + 1]
            output = numbers[parameter]
            index = index + 2
        }
        else if instruction == 5 {
            let values = getValues()
            if values[0] != 0 {
                index = values[1]
            }
            else {
                index = index + 3
            }
        }
        else if instruction == 6 {
            let values = getValues()
            if values[0] == 0 {
                index = values[1]
            }
            else {
                index = index + 3
            }
        }
        else if instruction == 07 {
            let values = getValues()
            let result = values[0] < values[1] ? 1 : 0
            numbers[parameters[2]] = result
            index = index + 4
        }
        else if instruction == 08 {
            let values = getValues()
            let result = values[0] == values[1] ? 1 : 0
            numbers[parameters[2]] = result
            index = index + 4
        }
        else if optcode == 99 {
            return output
        }
        else {
            fatalError("unexpected optcode \(instruction)")
        }
    }
    print(numbers)
    return numbers[0]
}

let input = """
3,225,1,225,6,6,1100,1,238,225,104,0,1102,40,93,224,1001,224,-3720,224,4,224,102,8,223,223,101,3,224,224,1,224,223,223,1101,56,23,225,1102,64,78,225,1102,14,11,225,1101,84,27,225,1101,7,82,224,1001,224,-89,224,4,224,1002,223,8,223,1001,224,1,224,1,224,223,223,1,35,47,224,1001,224,-140,224,4,224,1002,223,8,223,101,5,224,224,1,224,223,223,1101,75,90,225,101,9,122,224,101,-72,224,224,4,224,1002,223,8,223,101,6,224,224,1,224,223,223,1102,36,63,225,1002,192,29,224,1001,224,-1218,224,4,224,1002,223,8,223,1001,224,7,224,1,223,224,223,102,31,218,224,101,-2046,224,224,4,224,102,8,223,223,101,4,224,224,1,224,223,223,1001,43,38,224,101,-52,224,224,4,224,1002,223,8,223,101,5,224,224,1,223,224,223,1102,33,42,225,2,95,40,224,101,-5850,224,224,4,224,1002,223,8,223,1001,224,7,224,1,224,223,223,1102,37,66,225,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,1007,226,677,224,1002,223,2,223,1005,224,329,1001,223,1,223,1007,226,226,224,1002,223,2,223,1006,224,344,101,1,223,223,1107,677,226,224,102,2,223,223,1006,224,359,1001,223,1,223,108,677,677,224,1002,223,2,223,1006,224,374,1001,223,1,223,107,677,677,224,1002,223,2,223,1005,224,389,101,1,223,223,8,677,677,224,1002,223,2,223,1005,224,404,1001,223,1,223,108,226,226,224,1002,223,2,223,1005,224,419,101,1,223,223,1008,677,677,224,1002,223,2,223,1005,224,434,101,1,223,223,1008,226,226,224,1002,223,2,223,1005,224,449,101,1,223,223,7,677,226,224,1002,223,2,223,1006,224,464,1001,223,1,223,7,226,226,224,1002,223,2,223,1005,224,479,1001,223,1,223,1007,677,677,224,102,2,223,223,1005,224,494,101,1,223,223,1108,677,226,224,102,2,223,223,1006,224,509,1001,223,1,223,8,677,226,224,102,2,223,223,1005,224,524,1001,223,1,223,1107,226,226,224,102,2,223,223,1006,224,539,1001,223,1,223,1008,226,677,224,1002,223,2,223,1006,224,554,1001,223,1,223,1107,226,677,224,1002,223,2,223,1006,224,569,1001,223,1,223,1108,677,677,224,102,2,223,223,1005,224,584,101,1,223,223,7,226,677,224,102,2,223,223,1006,224,599,1001,223,1,223,1108,226,677,224,102,2,223,223,1006,224,614,101,1,223,223,107,226,677,224,1002,223,2,223,1005,224,629,101,1,223,223,108,226,677,224,1002,223,2,223,1005,224,644,101,1,223,223,8,226,677,224,1002,223,2,223,1005,224,659,1001,223,1,223,107,226,226,224,1002,223,2,223,1006,224,674,101,1,223,223,4,223,99,226
""".components(separatedBy: ",").compactMap{Int($0)}

func partOne() {
    dump(computeIntCode(input,1))
}

func partTwo() {
    dump(computeIntCode(input,5))
}

partOne()
partTwo()
 

I wrote lots of bugs and ended up with ugly code. Sorry for this mess:

function process()
{
    let input = 5; //input = 1; /* for part 1 */
    const pretext = document.getElementsByTagName('pre')[0].innerHTML;
    let instructions = pretext.split(',');
    let instructionLength = 0;
    for(let i = 0; i < instructions.length; i += instructionLength)
    {
        let inst = instructions[i].padStart(5, '0');
        if(inst === '00099')
        {
            break;
        }

        let opcode = inst.slice(-2);
        let parameterModes = inst.substr(0,3).split('').reverse();

        if(['01', '02', '07', '08'].indexOf(opcode) > -1)
        {
            instructionLength = 4;

            let values = [0,0];

            for(let pi = 0; pi < values.length; pi++)
            {
                values[pi] = parseInt(instructions[i+pi+1], 10);
                if(parameterModes[pi] === '0')
                {
                    values[pi] = parseInt(instructions[values[pi]], 10);
                }
            }

            let answerPos = parseInt(instructions[i+3], 10);

            if(opcode === '01')
            {
                instructions[answerPos] = (values[0] + values[1]).toString();
            }
            else if(opcode === '02')
            {
                instructions[answerPos] = (values[0] * values[1]).toString();
            }
            else if(opcode === '07')
            {
                instructions[answerPos] = values[0] < values[1] ? '1' : '0';
            }
            else if(opcode === '08')
            {
                instructions[answerPos] = values[0] === values[1] ? '1' : '0';
            }
        }
        else if(opcode === '05' || opcode == '06')
        {
            instructionLength = 3;
            let parameterModes = inst.substr(0,3).split('').reverse();
            let value = parseInt(instructions[i+1]);
            if(parameterModes[0] == 0)
            {
                value = parseInt(instructions[value], 10);
            }
            let point = parseInt(instructions[i+2]);
            if(parameterModes[1] == 0)
            {
                point = parseInt(instructions[point], 10);
            }

            if(opcode === '05' && value !== 0)
            {
                i = point;
                instructionLength = 0;
            }

            if(opcode === '06' && value === 0)
            {
                i = point;
                instructionLength = 0;
            }

        }
        else
        {
            instructionLength = 2;
            let position = parseInt(instructions[i+1], 10);

            if(opcode === '03')
            {
                instructions[position] = input.toString();
            }
            else if(opcode === '04')
            {
                console.log(instructions[position]);
            }
        }
    }

    return instructions[0];
}

I like how this can still be used to solve day 2 😁

 

Ooh, now I have to load day 2’s code. 🤩

 

I didn't reuse my code from day 2, instead I wrote a new one in Racket. I feel that the choice of the language was right, but I made a lot of mistakes and the task itself did not help with any example inputs. Finally I made it. It's not as bad as I feared, but could be better:

#lang racket

(require racket/match)

(define input (file->string "input"))
(define (codes input) 
    (map (lambda (elem) (string->number elem)) (string-split input ",")))

(define (parse-compound-opcode matches)
    (let* ([opcode (string->number (list-ref matches 2))]
        [modes-int (string->number (list-ref matches 1))]
        [modes (list (modulo modes-int 10) (modulo (quotient modes-int 10) 10) (modulo (quotient modes-int 100) 100))])
        (list opcode modes)))

(define (get-intructions index codes)
    (let* ([opcode (list-ref codes index)]
        [matches (regexp-match #px"(\\d+)(\\d{2})" (number->string opcode))])
        (if matches (parse-compound-opcode matches) (list opcode '(0 0 0)))))


(define (get-value codes position mode)
    (if (zero? mode) (list-ref codes (list-ref codes position)) (list-ref codes position)))

; operations
(define (do-the-math op codes index modes)
    (let* ([arg1 (get-value codes (+ 1 index) (list-ref modes 0))]
        [arg2 (get-value codes (+ 2 index) (list-ref modes 1))]
        [output-pos (get-value codes (+ 3 index) 1)]
        [result (op arg1 arg2)])
        (list-set codes output-pos result)))

(define (accept-input codes index modes)
    (let ([position (get-value codes (+ 1 index) 1)])
        (list-set codes position user-input)))

(define (append-output codes index modes outputs)
    (define value (get-value codes (+ 1 index) (list-ref modes 0)))
    (append outputs (list value)))

(define (jump-if bool codes index modes)
    (let ([value (get-value codes (+ 1 index) (list-ref modes 0))])
        (if (equal? bool (not (= 0 value))) (get-value codes (+ 2 index) (list-ref modes 1)) (+ index 3))))

(define (compare op codes index modes)
    (let* ([arg1 (get-value codes (+ 1 index) (list-ref modes 0))]
        [arg2 (get-value codes (+ 2 index) (list-ref modes 1))]
        [target (get-value codes (+ 3 index) 1)])
        (if (op arg1 arg2) (list-set codes target 1) (list-set codes target 0))))

(define (run-instruction opcode modes index codes outputs)
    (match opcode
        [1 (list (do-the-math + codes index modes) outputs 4)]
        [2 (list (do-the-math * codes index modes) outputs 4)]
        [3 (list (accept-input codes index modes) outputs 2)]
        [4 (list codes (append-output codes index modes outputs) 2)]
        [5 (list codes outputs (- (jump-if true codes index modes) index))]
        [6 (list codes outputs (- (jump-if false codes index modes) index))]
        [7 (list (compare < codes index modes) outputs 4)]
        [8 (list (compare = codes index modes) outputs 4)]))

(define (run-prog index codes outputs)
    (define instruction (get-intructions index codes))
    (define opcode (first instruction))
    (if (not (= opcode 99))
        (let* ([result (run-instruction opcode (second instruction) index codes outputs)]
            [new-codes (list-ref result 0)]
            [new-outputs (list-ref result 1)]
            [offset (list-ref result 2)])
            (run-prog (+ index offset) new-codes new-outputs)) 
        (printf "~s\n" outputs)))

(define user-input 1)
(run-prog 0 (codes input) '())

(set! user-input 5)
(run-prog 0 (codes input) '())

EDIT: Ugh, what is with this code highlighting? :/

 

I didn't enjoy this, it ate my entire evening, and I'm sad that it looks like I'm going to have to come back to it on future days. I've tried to clean it up so it's less of a nightmare to return to, but...

$input = file_get_contents("input5.txt");
$memory = explode(',', $input);
run_intcode();

function run_intcode()
{
    global $memory;

    $pointer = 0;
    $opcode_full =  "0000". $memory[$pointer];
    $opcode = substr($opcode_full, strlen($opcode_full) - 2);

    while ($opcode != 99) {

        $param1_mode = $opcode_full[-3];
        $param2_mode = $opcode_full[-4];

        $p1 = get_value($pointer+1, $param1_mode);
        $p2 = get_value($pointer+2, $param2_mode);

        switch ($opcode) {
            case 01:
                write_value($pointer+3,  $p1 + $p2);
                $pointer += 4;
                break;
            case 02:
                write_value($pointer+3,  $p1 * $p2);
                $pointer += 4;
                break;
            case 03:
                echo "enter the system ID number: ";
                $input_param = readline("enter the system to test: ");
                echo "\n";
                write_value($pointer+1, $input_param);
                $pointer += 2;
                break;
            case 04:
                echo 'output= ' . $p1 . "\n";
                $pointer += 2;
                break;
            case 05:
                if ($p1!=0){
                    $pointer = $p2;
                } else $pointer +=3;
                break;
            case 06:
                if($p1 ==0){
                    $pointer = $p2;
                } else $pointer += 3;
                break;
            case 07:
                write_value($pointer+3, $p1<$p2?1:0);
                $pointer +=4;
                break;
            case '08':
                write_value($pointer+3, $p1==$p2?1:0);
                $pointer += 4;
                break;
            default:
                echo "nope\n";
                break;
        }
        $opcode_full = "0000" . $memory[$pointer];
        $opcode = substr($opcode_full, strlen($opcode_full) - 2);
    }
}

function get_value($position, $mode){
    global $memory;
    $value = $mode ? $memory[$position] : $memory[$memory[$position]];
    return $value;
}

function write_value($position, $value){
    global $memory;
    $memory[$memory[$position]] = $value;
}
 

Initial kotlin solution (I'm going to clean this up a LOT, but in the interest of sharing what I learn, I promise not to pave this post over.)

import arrow.core.None
import arrow.core.Option
import arrow.core.Some
import arrow.core.some
import java.nio.file.Files
import java.nio.file.Paths

object Day05 {
    sealed class Mode {
        object Immediate : Mode()
        object Position : Mode()
    }

    fun Char.toMode(): Mode {
        return when (this) {
            '0' -> Mode.Position
            '1' -> Mode.Immediate
            else -> throw Error("Bad mode $this")
        }
    }

    fun parseInstruction(instruction: String): Instruction {
        return Day05.Instruction(
            opcode = instruction.takeLast(2).toInt(),
            paramModeInput = instruction.take(
                kotlin.math.max(
                    0,
                    instruction.count() - 2
                )
            ).map { it.toMode() }.reversed()
        )
    }

    class Instruction(val opcode: Int, paramModeInput: List<Mode>) {
        val paramModes: Array<Mode> = arrayOf(Mode.Position, Mode.Position, Mode.Position, Mode.Position)

        init {
            paramModeInput.forEachIndexed { i, it ->
                paramModes[i] = it
            }
        }

        override fun toString(): String {
            return "Instruction[$opcode, ${paramModes.contentToString()}]"
        }
    }

    fun handle(pointer: Int, input: Int, code: Array<String>): Option<Int> {
        val instr = parseInstruction(code[pointer])
        val inputs = code.drop(pointer + 1).take(3)
        val params = inputs
            .zip(instr.paramModes).map { (value, mode) ->
                when (mode) {
                    Mode.Immediate -> value.toInt()
                    Mode.Position -> {
                        code.getOrNull(value.toInt())?.toInt() ?: -1000000
                    }
                }
            }
        return when (instr.opcode) {
            1 -> {
                code[inputs[2].toInt()] = (params[0] + params[1]).toString()
                (pointer + 4).some()
            }
            2 -> {
                code[inputs[2].toInt()] = (params[0] * params[1]).toString()
                (pointer + 4).some()
            }
            3 -> {
                code[inputs[0].toInt()] = input.toString()
                (pointer + 2).some()
            }
            4 -> {
                println("output:${params[0]}")
                (pointer + 2).some()
            }
            5 -> {
                when (params[0]) {
                    0 -> pointer + 3
                    else -> params[1]
                }.some()
            }
            6 -> {
                when (params[0]) {
                    0 -> params[1]
                    else -> pointer + 3
                }.some()
            }
            7 -> {
                code[inputs[2].toInt()] = when {
                    params[0] < params[1] -> "1"
                    else -> "0"
                }
                (pointer + 4).some()
            }
            8 -> {
                code[inputs[2].toInt()] = when {
                    params[0] == params[1] -> "1"
                    else -> "0"
                }
                (pointer + 4).some()
            }
            99 -> Option.empty<Int>()
            else -> throw Error("Unknown opcode: ${instr.opcode}")
        }
    }

    tailrec fun step(
        code: Array<String>,
        input: Int,
        instructionPointer: Option<Int> = 0.some()
    ) {
        return when (instructionPointer) {
            is None -> Unit
            is Some<Int> -> {
                val nextInstruction = handle(instructionPointer.t, input, code)
                step(code, input, nextInstruction)
            }
        }
    }

    const val FILENAME = "src/main/resources/day05.txt"
}

fun main() {
    // Part 01
    Day05.step(
        code = Files.readString(Paths.get(Day05.FILENAME)).trim().split(",").toTypedArray(),
        input = 1
    )

    // Part 02
    Day05.step(
        code = Files.readString(Paths.get(Day05.FILENAME)).trim().split(",").toTypedArray(),
        input = 5
    )
}

 

Ok, as promised, here's the code I wanted to write, but I was halfway done before I saw it, and I wanted the answer so bad I didn't give myself the time to rewrite.

EDIT 2: Oops, all monads.

import arrow.core.Either
import arrow.core.Option
import arrow.core.flatMap
import arrow.core.getOrElse
import arrow.core.left
import arrow.core.right
import arrow.core.some
import java.nio.file.Files
import java.nio.file.Paths

sealed class Mode {
    object Immediate : Mode()
    object Position : Mode()
}

sealed class Instruction {
    abstract val opcodeFormat: String
    abstract fun execute(pointer: Int, code: Array<Int>, params: List<Int>): Either<Option<String>, Int>

    open fun findInputs(code: Array<Int>, pointer: Int) =
        code.drop(pointer + 1)
            .take(3)
            .zip(opcodeFormat.format(code[pointer] / 100).map {
                when (it) {
                    '0' -> Mode.Position
                    '1' -> Mode.Immediate
                    else -> throw Error("Bad mode $it")
                }
            }.reversed())
            .map { (it, mode) ->
                when (mode) {
                    Mode.Position -> code[it]
                    Mode.Immediate -> it
                }
            }

    sealed class ThreeParameterInstruction : Instruction() {
        override val opcodeFormat = "1%02d"

        class Add : ThreeParameterInstruction() {
            override fun execute(pointer: Int, code: Array<Int>, params: List<Int>): Either<Option<String>, Int> {
                code[params[2]] = params[0] + params[1]
                return (pointer + 4).right()
            }
        }

        class Multiply : ThreeParameterInstruction() {
            override fun execute(pointer: Int, code: Array<Int>, params: List<Int>): Either<Option<String>, Int> {
                code[params[2]] = params[0] * params[1]
                return (pointer + 4).right()
            }
        }

        class LessThan : ThreeParameterInstruction() {
            override fun execute(pointer: Int, code: Array<Int>, params: List<Int>): Either<Option<String>, Int> {
                code[params[2]] = when {
                    params[0] < params[1] -> 1
                    else -> 0
                }
                return (pointer + 4).right()
            }
        }

        class Equal : ThreeParameterInstruction() {
            override fun execute(pointer: Int, code: Array<Int>, params: List<Int>): Either<Option<String>, Int> {
                code[params[2]] = when {
                    params[0] == params[1] -> 1
                    else -> 0
                }
                return (pointer + 4).right()
            }
        }
    }

    sealed class TwoParameterInstruction : Instruction() {
        override val opcodeFormat = "%02d"

        class JumpIfTrue : TwoParameterInstruction() {
            override fun execute(pointer: Int, code: Array<Int>, params: List<Int>) =
                when (params[0]) {
                    0 -> pointer + 3
                    else -> params[1]
                }.right()
        }

        class JumpIfFalse : TwoParameterInstruction() {
            override fun execute(pointer: Int, code: Array<Int>, params: List<Int>) =
                when (params[0]) {
                    0 -> params[1]
                    else -> pointer + 3
                }.right()
        }
    }

    class SetFromInput(private val input: Int) : Instruction() {
        override val opcodeFormat = "1"
        override fun execute(pointer: Int, code: Array<Int>, params: List<Int>): Either<Option<String>, Int> {
            code[params[0]] = input
            return (pointer + 2).right()
        }
    }

    class Output : Instruction() {
        override val opcodeFormat = "0"
        override fun execute(pointer: Int, code: Array<Int>, params: List<Int>): Either<Option<String>, Int> {
            println("output: ${params[0]}")
            return (pointer + 2).right()
        }
    }

    object End : Instruction() {
        override val opcodeFormat: String
            get() = throw Error("No opcode format for End instructions.")

        override fun findInputs(code: Array<Int>, pointer: Int) = emptyList<Int>()
        override fun execute(pointer: Int, code: Array<Int>, params: List<Int>): Either<Option<String>, Int> =
            Option.empty<String>().left()
    }
}

object Day05 {

    private fun parseInstruction(instruction: String, input: Int) =
        when (instruction.takeLast(2).toInt()) {
            1 -> Instruction.ThreeParameterInstruction.Add().right()
            2 -> Instruction.ThreeParameterInstruction.Multiply().right()
            3 -> Instruction.SetFromInput(input).right()
            4 -> Instruction.Output().right()
            5 -> Instruction.TwoParameterInstruction.JumpIfTrue().right()
            6 -> Instruction.TwoParameterInstruction.JumpIfFalse().right()
            7 -> Instruction.ThreeParameterInstruction.LessThan().right()
            8 -> Instruction.ThreeParameterInstruction.Equal().right()
            99 -> Instruction.End.right()
            else -> "Problem parsing instruction $instruction".some().left()
        }

    private fun handleCodePoint(pointer: Int, input: Int, code: Array<Int>) =
        parseInstruction(code[pointer].toString(), input).flatMap { instr ->
            instr.execute(pointer, code, instr.findInputs(code, pointer))
        }

    tailrec fun step(
        code: Array<Int>,
        input: Int,
        instructionPointer: Either<Option<String>, Int> = Either.right(0)
    ): String = when (instructionPointer) {
        is Either.Left<Option<String>> -> instructionPointer.a.getOrElse { "Program terminated successfully." }
        is Either.Right<Int> -> {
            val nextInstruction = handleCodePoint(instructionPointer.b, input, code)
            step(code, input, nextInstruction)
        }
    }

    const val FILENAME = "src/main/resources/day05.txt"
}

fun main() {
    val problemInput = Files.readAllLines(Paths.get(Day05.FILENAME))
        .first()
        .split(",")
        .map { it.toInt() }

    // Part 01
    println("Part 01")
    Day05.step(code = problemInput.toTypedArray(), input = 1)

    // Part 02
    println("\nPart 02")
    Day05.step(code = problemInput.toTypedArray(), input = 5)

    println("\nDay 02")
    val day02Code = Files.readAllLines(Paths.get("src/main/resources/day02.txt")).first()
        .split(",")
        .map { it.toInt() }.toTypedArray()
    Day05.step(code = day02Code, input = 5)
    println(day02Code.take(10))
}
 

Definitely can say this implementation has a lot of class. 🤣

Yeah, I wish there was a more elegant way of doing discriminated unions in Kotlin. I'm used to the lightweight kinds/types of Haskell. Arrow-KT lets you do basic stuff pretty nicely, but it gets ugly the more you try to be full functional style only.

I'm also disappointed in the type erasure.

 

Agreed, this one was a doozy for me.

Kind of reminds of my experience when learning pointers for the first time.

Here's my solution in Python!

def helper(program, ptr, param_modes):
    a_mode = param_modes.pop() if len(param_modes) > 0 else 0
    b_mode = param_modes.pop() if len(param_modes) > 0 else 0

    a = program[ptr + 1] if a_mode == 1 else program[program[ptr + 1]]
    b = program[ptr + 2] if b_mode == 1 else program[program[ptr + 2]]
    c = program[ptr + 3] if ptr + 3 < len(program) else None

    return (a, b, c)

def run_test(program, input_code):
    ptr = 0
    output_codes = []
    while program[ptr] != 99:
        instruction = str(program[ptr])
        n = len(instruction)
        opcode, param_modes = int(instruction[n -2:n]), list(map(int, instruction[:-2]))
        if opcode == 1:
            a, b, c = helper(program, ptr, param_modes)
            program[c] = a + b
        elif opcode == 2:
            a, b, c = helper(program, ptr, param_modes)
            program[c] = a * b
        elif opcode == 3:
            program[program[ptr + 1]] = input_code
        elif opcode == 4:
            output_codes.append(program[program[ptr + 1]])
        elif opcode == 5:
            a, b, _ = helper(program, ptr, param_modes)
            if a != 0:
                ptr = b
                continue
        elif opcode == 6:
            a, b, _ = helper(program, ptr, param_modes)
            if a == 0:
                ptr = b
                continue
        elif opcode == 7:
            a, b, c = helper(program, ptr, param_modes)
            program[c] = 1 if a < b else 0
        elif opcode == 8:
            a, b, c = helper(program, ptr, param_modes)
            program[c] = 1 if a == b else 0
        else:
            raise Exception('Unknown opcode: {}'.format(opcode))

        if opcode == 3 or opcode == 4:
            step = 2
        elif opcode == 5 or opcode == 6:
            step = 3
        else:
            step = 4
        ptr += step
    return output_codes


program = list(map(int, open('input.txt', 'r').readline().strip().split(',')))

# part 1
print('Result: {}'.format(run_test(list(program), 1)[-1]))

# part 2
print('Result: {}'.format(run_test(list(program), 5)[-1]))
 

So, I actually reasonably enjoyed this day! :) It did take a good bit of deciphering exactly what I needed to make it do....the wording is so similar in the description of things! But once I got it, I knew what to do, which I honestly really like. lol :)

I did refactor a bit of what I did for day 2, so this currently contains all of my opcode options. Fingers crossed it will be easier to add onto when the next day comes to update it! (Though knowing AoC, chances of that might be slim lol)

I've got a decent bit of descriptive comments in here, to help make things easier for myself in the future. :) So hopefully that helps some folks out, too! Also, I really enjoy switch statements, and I like that they've worked for multiple days so far this year. lol

// Memory - initial puzzle input, a list of integers


const input = [3,225,1,225,6,6,1100,99,226]; // shortened input for brevity

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

// opcode 1 - get values at position 1&2 right after code, add together, store in position 3
function opcode1 (a, b, c, p) {
  let valA = ptest(p[0], a);
  let valB = ptest(p[1], b);
  inputCopy[c] = valA + valB;
  console.log(`op1: ${valA} + ${valB} = ${valA + valB}`)
}

// opcode 2 - get values at position 1&2 right after code, multiply, store in position 3
function opcode2 (a, b, c, p) {
  let valA = ptest(p[0], a);
  let valB = ptest(p[1], b);
  inputCopy[c] = valA * valB;
  console.log(`op2: ${valA} * ${valB} = ${valA * valB}`)
}


// opcode 3 - takes an input and stores in position 1
function opcode3 (iv, s) {
  inputCopy[s] = iv;
  console.log(`op3: putting ${iv} into spot ${s}`)
}

// opcode 4 - outputs value at position 1
function opcode4 (s, p) {
  let val = ptest(p[0], s);
  console.log(`op4: outputting ${val}`)
  return val;
}

// opcode 5 - if position 1 != 0, changes i to position 2; otherwise, does nothing
function opcode5 (a, b, inp, p) {
  let valA = ptest(p[0], a);
  let valB = ptest(p[1], b);

  if (valA !== 0) {
    inp = valB;
  }
  console.log(`op5: inst. pointer is now ${inp}`);
  return inp;
}

// opcode 6 - if position 1 == 0, changes i to position 2; otherwise, does nothing
function opcode6 (a, b, inp, p) {
  let valA = ptest(p[0], a);
  let valB = ptest(p[1], b);

  if (valA === 0) {
    inp = valB;
  }
  console.log(`op6: inst. pointer is now ${inp}`);

  return inp;
}

// opcode 7 - if position 1 < position 2, position 3 is set to 1; otherwise, it's set to 0
function opcode7 (a, b, c, p) {
  let valA = ptest(p[0], a);
  let valB = ptest(p[1], b);

  if (valA < valB) {
    inputCopy[c] = 1;
  } else {
    inputCopy[c] = 0;
  }
  console.log(`op7: comparing if ${valA} is < ${valB}`);
}

// opcode 8 - if position 1 == position 2, position 3 is set to 1; otherwise, it's set to 0
function opcode8 (a, b, c, p) {
  let valA = ptest(p[0], a);
  let valB = ptest(p[1], b);

  if (valA == valB) {
    inputCopy[c] = 1;
  } else {
    inputCopy[c] = 0;
  }
  console.log(`op8: comparing if ${valA} equals ${valB}`);
}

// allows parameter modes - checks for 0 or 1, decides if returning actual number called or position of number in input
function ptest(param, checkval) {
  if (param == 0 || !param) {
    return inputCopy[checkval];
  } else if (param == 1) {
    return checkval;
  }
}

// opcode 99 - stop program

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

    let instruct = inputCopy[i].toString();
    let opval = parseInt(instruct.slice(-2), 10);
    let params = instruct.slice(0, -2).split('').reverse();


    let ione = inputCopy[i+1];
    let itwo = inputCopy[i+2];
    let ithree = inputCopy[i+3];

    switch (opval) {
      case 01:
        opcode1(ione, itwo, ithree, params);
        i += 3;
        break;
      case 02:
        opcode2(ione, itwo, ithree, params);
        i += 3;
        break;
      case 03:
        opcode3(inputval, ione);
        i++;
        break;
      case 04:
        let res = opcode4(ione, params);
        console.log(res);
        i++;
        break;
      case 05:
        let checkt = opcode5(ione, itwo, i, params);
        if (i != checkt) {
          i = checkt - 1;
        } else {
          i += 2;
        }
        break;
      case 06:
        let checkf = opcode6(ione, itwo, i, params);
        if (i != checkf) {
          i = checkf - 1;
        } else {
          i += 2;
        }
        break;
      case 07:
        opcode7(ione, itwo, ithree, params);
        i += 3;
        break;
      case 08:
        opcode8(ione, itwo, ithree, params);
        i += 3;
        break;
    }
  }
}

// for part 1, inputval is 1; for part 2, it's 5
let inputval = 5;
runProgram();
 

JavaScript solution

const { createInterface } = require('readline')

const rl = createInterface({
  input: process.stdin,
  output: process.stdout
})

const question = str =>
  new Promise(res => {
    rl.question(str, res)
  })

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

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

  loop: for (let i = 0; i < instructions.length; ++i) {
    const instruction = instructions[i]
    const parsed = String(instruction)
      .padStart(5, '0')
      .split('')
    const valueMode = (value, mode = '0') =>
      mode === '0' ? instructions[value] : value
    const opCode = Number(parsed.slice(3).join(''))
    const modes = parsed.slice(0, 3)
    switch (opCode) {
      case INSTRUCTIONS.ADD: {
        const x = valueMode(instructions[++i], modes[2])
        const y = valueMode(instructions[++i], modes[1])
        instructions[instructions[++i]] = x + y
        break
      }
      case INSTRUCTIONS.MULT: {
        const x = valueMode(instructions[++i], modes[2])
        const y = valueMode(instructions[++i], modes[1])
        instructions[instructions[++i]] = x * y
        break
      }
      case INSTRUCTIONS.INPUT: {
        instructions[instructions[++i]] = Number(await question('input: '))
        break
      }
      case INSTRUCTIONS.OUTPUT: {
        console.log(valueMode(instructions[++i], modes[2]))
        break
      }
      case INSTRUCTIONS.JUMP_IF_TRUE: {
        const compare = valueMode(instructions[++i], modes[2])
        const jumpTo = valueMode(instructions[++i], modes[1]) - 1
        if (compare != 0) {
          i = jumpTo
        }
        break
      }
      case INSTRUCTIONS.JUMP_IF_FALSE: {
        const compare = valueMode(instructions[++i], modes[2])
        const jumpTo = valueMode(instructions[++i], modes[1]) - 1
        if (compare == 0) {
          i = jumpTo
        }
        break
      }
      case INSTRUCTIONS.LESS_THAN: {
        const x = valueMode(instructions[++i], modes[2])
        const y = valueMode(instructions[++i], modes[1])
        instructions[instructions[++i]] = x < y ? 1 : 0
        break
      }
      case INSTRUCTIONS.EQUALS: {
        const x = valueMode(instructions[++i], modes[2])
        const y = valueMode(instructions[++i], modes[1])
        instructions[instructions[++i]] = x === y ? 1 : 0
        break
      }
      case INSTRUCTIONS.HALT:
        break loop
    }
  }
  return instructions[0]
}

module.exports = runProgram

if (process.argv[1] === __filename) {
  const input = require('fs')
    .readFileSync('./puzzle-input.txt')
    .toString()

  const instructions = input.split(',').map(Number)
  runProgram(instructions).then(() => process.exit(0))
}
 

I found the directions for this challenge much more confusing - and my confusion led to some weird bugs! But ultimately I'm pretty happy with my solution (in ruby):

def position? mode
  mode == 0
end

# return an array with the modes for the params (always length 3)
def get_modes instruction
  modes = instruction.to_s.chars.map(&:to_i)
  while modes.length < 5
    modes.unshift 0
  end
  modes[0..2]
  # flip the order so they are in order of the param order
  # [param_1_mode, param_2_mode, param_3_mode]
  modes = [modes[2], modes[1], modes[0]]
end

def compute input
  memory = [3,225,1,225,6,6,1100,1,238,225,104,0,2,136,183,224,101,-5304,224,224,4,224,1002,223,8,223,1001,224,6,224,1,224,223,223,1101,72,47,225,1101,59,55,225,1101,46,75,225,1101,49,15,224,101,-64,224,224,4,224,1002,223,8,223,1001,224,5,224,1,224,223,223,102,9,210,224,1001,224,-270,224,4,224,1002,223,8,223,1001,224,2,224,1,223,224,223,101,14,35,224,101,-86,224,224,4,224,1002,223,8,223,101,4,224,224,1,224,223,223,1102,40,74,224,1001,224,-2960,224,4,224,1002,223,8,223,101,5,224,224,1,224,223,223,1101,10,78,225,1001,39,90,224,1001,224,-149,224,4,224,102,8,223,223,1001,224,4,224,1,223,224,223,1002,217,50,224,1001,224,-1650,224,4,224,1002,223,8,223,1001,224,7,224,1,224,223,223,1102,68,8,225,1,43,214,224,1001,224,-126,224,4,224,102,8,223,223,101,3,224,224,1,224,223,223,1102,88,30,225,1102,18,80,225,1102,33,28,225,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,108,677,677,224,102,2,223,223,1005,224,329,1001,223,1,223,1107,677,226,224,102,2,223,223,1006,224,344,1001,223,1,223,108,226,226,224,102,2,223,223,1005,224,359,1001,223,1,223,1108,677,226,224,102,2,223,223,1006,224,374,101,1,223,223,108,677,226,224,102,2,223,223,1006,224,389,1001,223,1,223,107,226,226,224,102,2,223,223,1005,224,404,1001,223,1,223,8,226,226,224,102,2,223,223,1006,224,419,101,1,223,223,1107,677,677,224,102,2,223,223,1006,224,434,1001,223,1,223,1107,226,677,224,1002,223,2,223,1006,224,449,101,1,223,223,7,677,677,224,1002,223,2,223,1006,224,464,1001,223,1,223,1108,226,677,224,1002,223,2,223,1005,224,479,1001,223,1,223,8,677,226,224,1002,223,2,223,1005,224,494,101,1,223,223,7,226,677,224,102,2,223,223,1005,224,509,101,1,223,223,1008,677,226,224,102,2,223,223,1006,224,524,101,1,223,223,8,226,677,224,1002,223,2,223,1006,224,539,1001,223,1,223,1007,677,677,224,102,2,223,223,1005,224,554,101,1,223,223,107,226,677,224,1002,223,2,223,1005,224,569,1001,223,1,223,1108,677,677,224,1002,223,2,223,1006,224,584,1001,223,1,223,1008,226,226,224,1002,223,2,223,1005,224,599,101,1,223,223,1008,677,677,224,102,2,223,223,1005,224,614,101,1,223,223,7,677,226,224,1002,223,2,223,1005,224,629,1001,223,1,223,107,677,677,224,1002,223,2,223,1006,224,644,101,1,223,223,1007,226,677,224,1002,223,2,223,1005,224,659,1001,223,1,223,1007,226,226,224,102,2,223,223,1005,224,674,101,1,223,223,4,223,99,226]
  i = 0
  diagnostic_code = nil

  while i < memory.length
    opcode = memory[i] % 100 # the opcode is the 1's and 10's place

    modes = get_modes(memory[i])
    mode_1 = modes[0]
    mode_2 = modes[1]
    mode_3 = modes[2]

    if [1, 2, 4, 5, 6, 7, 8].include? opcode
      param_1 = memory[(position?(mode_1) ? memory[i+1] : i+1)]
    end

    if [1, 2, 5, 6, 7, 8].include? opcode
      param_2 = memory[(position?(mode_2) ? memory[i+2] : i+2)]
    end

    if opcode == 1
      # add
      memory[(position?(mode_3) ? memory[i+3] : i+3)] = param_1 + param_2
      i += 4
    elsif opcode == 2
      # multiply
      memory[(position?(mode_3) ? memory[i+3] : i+3)] = param_1 * param_2
      i += 4
    elsif opcode == 3
      # input
      memory[(position?(mode_1) ? memory[i+1] : i+1)] = input
      i += 2
    elsif opcode == 4
      # output
      diagnostic_code = param_1
      i += 2
    elsif opcode == 5
      # jump-if-true
      if param_1 != 0
        i = param_2
      else
        i += 3
      end
    elsif opcode == 6
      # jump-if-false
      if param_1 == 0
        i = param_2
      else
        i += 3
      end
    elsif opcode == 7
      # less than
      memory[(position?(mode_3) ? memory[i+3] : i+3)] = (param_1 < param_2 ? 1 : 0)
      i += 4
    elsif opcode == 8
      # equals
      memory[(position?(mode_3) ? memory[i+3] : i+3)] = (param_1 == param_2 ? 1 : 0)
      i += 4
    elsif opcode == 99
      # halt
      break
    end
  end
  diagnostic_code
end

puts compute 1
puts compute 5
 

Today was a fairly mechanical extension of day 2. The details of the parameter modes are the trickiest part, with some instructions having implicit immediate or position mode. Not that hard overall however.

I extended my day2 C program which is now too long to post. See github.com/neilgall/adventofcode20...

 

This one was tough! Here is my solution walkthrough in javascript:

 

I'm lagging behind, but still moving.

Clojure code for Day 5

code of conduct - report abuse