@alexaegis/advent-of-code-intcode
Version:
Advent of Code Intcode Computer
273 lines (272 loc) • 6.59 kB
JavaScript
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const adventOfCodeLib = require("@alexaegis/advent-of-code-lib");
var Instruction = /* @__PURE__ */ ((Instruction2) => {
Instruction2[Instruction2["ADD"] = 1] = "ADD";
Instruction2[Instruction2["MUL"] = 2] = "MUL";
Instruction2[Instruction2["IN"] = 3] = "IN";
Instruction2[Instruction2["OUT"] = 4] = "OUT";
Instruction2[Instruction2["JIT"] = 5] = "JIT";
Instruction2[Instruction2["JIF"] = 6] = "JIF";
Instruction2[Instruction2["LT"] = 7] = "LT";
Instruction2[Instruction2["EQ"] = 8] = "EQ";
Instruction2[Instruction2["REL"] = 9] = "REL";
Instruction2[Instruction2["HALT"] = 99] = "HALT";
return Instruction2;
})(Instruction || {});
const isInstruction = (n) => n >= 1 && n <= 9 || n === 99;
const toInstruction = (code) => {
const inst = code % 100;
if (isInstruction(inst)) {
return inst;
} else {
throw new Error(`Unsupported instruction ${inst}`);
}
};
var Mode = /* @__PURE__ */ ((Mode2) => {
Mode2[Mode2["POS"] = 0] = "POS";
Mode2[Mode2["VAL"] = 1] = "VAL";
Mode2[Mode2["REL"] = 2] = "REL";
return Mode2;
})(Mode || {});
class IntCodeComputer {
tape;
cursor = 0;
relBase = 0;
halt = false;
inputQueue;
inputCallback;
outputCallback;
constructor(tape) {
this.tape = tape.reduce((m, n, i) => m.set(i, n), /* @__PURE__ */ new Map());
}
*iter() {
for (const res of this) {
if (res !== void 0) {
yield res;
}
}
}
*stepper() {
yield* this;
}
set input(input) {
if (typeof input === "number") {
this.inputQueue = [input];
} else if (typeof input === "function") {
this.inputCallback = input;
} else {
this.inputQueue = input;
}
}
withInput(input) {
this.input = input;
return this;
}
withInputCallback(inputCallback) {
this.inputCallback = inputCallback;
return this;
}
withOutputCallback(outputCallback) {
this.outputCallback = outputCallback;
return this;
}
isHalt() {
return this.halt;
}
pushInput(...input) {
if (!this.inputQueue) {
this.inputQueue = [];
}
this.inputQueue.push(...input);
return this;
}
pushAsciiInput(input, nl = true) {
if (nl) {
input += "\n";
}
return this.pushInput(...[...input].map((s) => s.codePointAt(0) ?? 0));
}
pushInputIfEmpty(input) {
if (!this.inputQueue || this.inputQueue.length === 0) {
this.pushInput(input);
return true;
} else
return false;
}
getValue(pos, mode = Mode.POS, asIndex = false) {
let v;
switch (mode) {
case Mode.POS: {
v = this.tape.get(pos) ?? 0;
break;
}
case Mode.REL: {
v = this.relBase + (this.tape.get(pos) ?? 0);
break;
}
default: {
v = pos;
break;
}
}
return asIndex ? v : this.tape.get(v) ?? 0;
}
reset(tape) {
if (tape) {
this.tape = tape.reduce((m, n, i) => m.set(i, n), /* @__PURE__ */ new Map());
}
this.cursor = 0;
this.relBase = 0;
this.halt = false;
return this;
}
peek(at) {
return this.tape.get(at);
}
set noun(noun) {
this.tape.set(1, noun);
}
withNoun(noun) {
this.noun = noun;
return this;
}
set verb(verb) {
this.tape.set(2, verb);
}
withVerb(verb) {
this.verb = verb;
return this;
}
getArg(v, n, asIndex = false, mode) {
return this.getValue(this.cursor + n + 1, mode ?? this.getMode(v, n), asIndex);
}
getMode(v, n) {
return adventOfCodeLib.numAt(v, adventOfCodeLib.integerLength(v) - n - 3);
}
*[Symbol.iterator]() {
do {
const v = this.tape.get(this.cursor) ?? 0;
const i = toInstruction(v);
switch (i) {
case Instruction.ADD: {
this.addOp(this.getArg(v, 0), this.getArg(v, 1), this.getArg(v, 2, true));
break;
}
case Instruction.MUL: {
this.mulOp(this.getArg(v, 0), this.getArg(v, 1), this.getArg(v, 2, true));
break;
}
case Instruction.IN: {
this.inOp(this.getArg(v, 0, true));
yield void 0;
break;
}
case Instruction.OUT: {
yield this.outOp(this.getArg(v, 0));
break;
}
case Instruction.JIT: {
this.jitOp(this.getArg(v, 0), this.getArg(v, 1));
break;
}
case Instruction.JIF: {
this.jifOp(this.getArg(v, 0), this.getArg(v, 1));
break;
}
case Instruction.LT: {
this.ltOp(this.getArg(v, 0), this.getArg(v, 1), this.getArg(v, 2, true));
break;
}
case Instruction.EQ: {
this.eqOp(this.getArg(v, 0), this.getArg(v, 1), this.getArg(v, 2, true));
break;
}
case Instruction.REL: {
this.relOp(this.getArg(v, 0));
break;
}
default: {
this.haltOp();
break;
}
}
} while (!this.halt);
}
run(target) {
if (target) {
target.push(...this.execute());
} else {
this.execute();
}
return this;
}
execute() {
return [...this.iter()];
}
addOp(a, b, pos) {
this.tape.set(pos, a + b);
this.cursor += 4;
}
mulOp(a, b, pos) {
this.tape.set(pos, a * b);
this.cursor += 4;
}
inOp(pos) {
if (this.inputQueue && this.inputQueue.length > 0) {
const next = this.inputQueue.shift();
if (next !== void 0) {
this.tape.set(pos, next);
}
} else if (this.inputCallback) {
this.tape.set(pos, this.inputCallback());
} else {
throw new Error("No input");
}
this.cursor += 2;
}
outOp(pos) {
if (pos === void 0) {
throw new Error("Not valid output" + pos);
}
if (this.outputCallback) {
this.outputCallback(pos);
}
this.cursor += 2;
return pos;
}
jitOp(v, target) {
if (v) {
this.cursor = target;
} else {
this.cursor += 3;
}
}
jifOp(v, target) {
if (v) {
this.cursor += 3;
} else {
this.cursor = target;
}
}
ltOp(a, b, pos) {
this.tape.set(pos, a < b ? 1 : 0);
this.cursor += 4;
}
eqOp(a, b, pos) {
this.tape.set(pos, a === b ? 1 : 0);
this.cursor += 4;
}
relOp(pos) {
this.relBase += pos;
this.cursor += 2;
}
haltOp() {
this.halt = true;
}
}
exports.Instruction = Instruction;
exports.IntCodeComputer = IntCodeComputer;
exports.Mode = Mode;
exports.isInstruction = isInstruction;
exports.toInstruction = toInstruction;