123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- const prompt = require("prompt-sync")({ sigint: true });
- const util = require("util");
- const uuid = require("uuid");
- const Stack = require("./Stack");
- const ComputerParameterMode = require("./ComputerParameterMode");
- const InputModes = require("./InputModes");
- const { DeepClone } = require("./common");
- module.exports = class Computer {
-
- constructor(stack, options = {}) {
- this.name = uuid.v4();
- this._initialMemory = DeepClone(stack);
- this.stack = new Stack(stack);
- this.OPCODES = {
- ADD: 1,
- MULTIPLY: 2,
- INPUT: 3,
- OUTPUT: 4,
- JUMP_IF_TRUE: 5,
- JUMP_IF_FALSE: 6,
- LESS_THAN: 7,
- EQUALS: 8,
- MODIFY_RELATIVE_BASE: 9,
- HALT: 99,
- };
- this.EQUALITY = {
- EQUALS: 0,
- LESS_THAN: 1,
- };
-
- this.skipNext = false;
- this.options = {
- debug: {
- followPointer: options?.debug?.followPointer ?? false,
- followRuntimeInput: options?.debug?.followRuntimeInput ?? false,
- followOutputValues: options?.debug?.followOutputValues ?? false,
- followRelativeBaseOffset: options?.debug?.followRelativeBaseOffset ?? false,
- tickRate: options?.debug?.tickRate ?? 0,
- },
- inputFromConsole: options.inputFromConsole ?? false,
- outputToConsole: options.outputToConsole ?? false,
- inputModeMap: options.inputModeMap ?? [],
- _initialInputModeMap: options.inputModeMap ?? [],
- };
-
- this.outputComputer = null;
-
- this.runtimeInput = [];
-
- this.outputValues = [];
-
- this.running = false;
-
- this.awaitingInput = false;
- }
-
- async Run() {
- while (this.Execute(this.stack.Get(ComputerParameterMode.IMMEDIATE_MODE)) === true) {
- if (this.options.debug.tickRate) {
-
-
- await new Promise(resolve => setTimeout(resolve, this.options.debug.tickRate));
- }
- }
- }
-
- async RunWithInput(input) {
- this.runtimeInput = input;
- while (this.Execute(this.stack.Get(ComputerParameterMode.IMMEDIATE_MODE)) === true) {
- if (this.options.debug.tickRate) {
-
-
- await new Promise(resolve => setTimeout(resolve, this.options.debug.tickRate));
- }
- }
- }
-
- Execute(rawOpcode) {
- let status = true;
- this.running = true;
- this.skipNext = false;
- this.FollowExecution();
- const opcode = rawOpcode % 100;
-
- switch (opcode) {
- case this.OPCODES.ADD: {
- this.Operation_Add(rawOpcode);
- break;
- }
- case this.OPCODES.MULTIPLY: {
- this.Operation_Multiply(rawOpcode);
- break;
- }
- case this.OPCODES.INPUT: {
-
- if (this.awaitingInput == true && this.runtimeInput.length == 0) {
- this.skipNext = true;
- status = false;
- break;
- }
- this.Operation_Input(rawOpcode);
- break;
- }
- case this.OPCODES.OUTPUT: {
- this.Operation_Output(rawOpcode);
- break;
- }
- case this.OPCODES.JUMP_IF_TRUE: {
- this.Operation_JumpIf(rawOpcode, true);
- break;
- }
- case this.OPCODES.JUMP_IF_FALSE: {
- this.Operation_JumpIf(rawOpcode, false);
- break;
- }
- case this.OPCODES.LESS_THAN: {
- this.Operation_Equality(rawOpcode, this.EQUALITY.LESS_THAN);
- break;
- }
- case this.OPCODES.EQUALS: {
- this.Operation_Equality(rawOpcode, this.EQUALITY.EQUALS);
- break;
- }
- case this.OPCODES.HALT:
- this.running = false;
- status = false;
- break;
- case this.OPCODES.MODIFY_RELATIVE_BASE: {
- this.Operation_ModifyRelativeBase(rawOpcode);
- break;
- }
- default:
- this.ThrowError(`Opcode ${opcode} not found`);
- }
- if (!this.skipNext) {
- this.stack.Next();
- }
- return status;
- }
-
- Operation_Add(rawOpcode) {
- const operandLeftMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
- const operandRightMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 2);
- const outputMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 3) || 1;
- const operandLeft = this.stack.Next().Get(operandLeftMode);
- const operandRight = this.stack.Next().Get(operandRightMode);
- const outputPosition = this.stack.Next().Get(outputMode, true);
- const newValue = operandLeft + operandRight;
- this.stack.Put(outputPosition, newValue);
- }
-
- Operation_Multiply(rawOpcode) {
- const operandLeftMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
- const operandRightMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 2);
- const outputMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 3) || 1;
- const operandLeft = this.stack.Next().Get(operandLeftMode);
- const operandRight = this.stack.Next().Get(operandRightMode);
- const outputPosition = this.stack.Next().Get(outputMode, true);
- const newValue = operandLeft * operandRight;
- this.stack.Put(outputPosition, newValue);
- }
-
- Operation_Input(rawOpcode) {
-
- const outputParamMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1) || 1;
- const outputPosition = this.stack.Next().Get(outputParamMode, true);
-
- let userInput;
-
- let inputMode = this.options.inputModeMap.shift();
-
- if (inputMode === undefined) {
- if (this.options.inputFromConsole) { inputMode = InputModes.INPUT_FROM_CONSOLE; }
- else { inputMode = InputModes.INPUT_FROM_RUNTIME_STACK; }
- }
-
- switch (inputMode) {
- case InputModes.INPUT_FROM_RUNTIME_STACK: {
- userInput = this.runtimeInput.shift();
-
- if (userInput === undefined) {
-
- this.stack.Prev();
- this.stack.Prev();
-
- this.awaitingInput = true;
-
- return;
- }
- this.awaitingInput = false;
- break;
- }
- case InputModes.INPUT_FROM_CONSOLE: {
- do {
- userInput = Number(prompt("Please enter a number: "));
- } while (Number.isNaN(userInput));
- break;
- }
- default:
- this.ThrowError("No input found");
- }
-
- this.stack.Put(outputPosition, userInput);
- }
-
- Operation_Output(rawOpcode) {
- const currAddress = this.options.outputToConsole ? this.stack.pointer : undefined;
- const outputPositionMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
- const output = this.stack.Next().Get(outputPositionMode);
- if (this.options.outputToConsole) {
- console.log(`OUTPUT FROM ADDRESS ${currAddress}: ${output}`);
- }
- else if (this.outputComputer !== null) {
- this.outputComputer.Input(output);
- }
- else {
- this.outputValues.push(output);
- }
- }
-
- Operation_JumpIf(rawOpcode, testCondition) {
- const paramMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
- const jumpAddressMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 2);
- const param = this.stack.Next().Get(paramMode);
- const jumpAddress = this.stack.Next().Get(jumpAddressMode);
- const performJump = !!param == testCondition;
- if (performJump) {
- this.skipNext = true;
- this.stack.SetPointerAddress(jumpAddress);
- }
- }
-
- Operation_Equality(rawOpcode, testCondition) {
- const operandLeftMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
- const operandRightMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 2);
- const outputMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 3) || 1;
- const operandLeft = this.stack.Next().Get(operandLeftMode);
- const operandRight = this.stack.Next().Get(operandRightMode);
- const outputPosition = this.stack.Next().Get(outputMode, true);
- let testPassed = false;
- switch (testCondition) {
- case this.EQUALITY.EQUALS:
- testPassed = operandLeft == operandRight;
- break;
- case this.EQUALITY.LESS_THAN:
- testPassed = operandLeft < operandRight;
- break;
- default:
- break;
- }
- this.stack.Put(outputPosition, Number(testPassed));
- }
-
- Operation_ModifyRelativeBase(rawOpcode) {
- const operandMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
- const adjustmentValue = this.stack.Next().Get(operandMode);
- this.stack.AdjustRelativeBaseOffset(adjustmentValue);
- }
-
- FollowExecution() {
- if (this.options.debug.followPointer) { this.DumpMemory(true); }
- if (this.options.debug.followRuntimeInput) { this.InspectProperty("Inputs", "runtimeInput"); }
- if (this.options.debug.followOutputValues) { this.InspectProperty("Outputs", "outputValues"); }
- if (this.options.debug.followRelativeBaseOffset) { this.InspectProperty("Relative Base Offset", null, this.stack.relativeBaseOffset); }
- }
-
- DumpMemory(highlightPointer = false) {
- let memory = this.stack.Dump();
- if (highlightPointer) {
- memory = memory.map((instruction, idx) => (idx == this.stack.pointer ? `{${instruction}}` : instruction));
- }
- this.InspectProperty("Memory", null, memory);
- }
-
- InspectProperty(outputMessage = "", propertyName = null, overrideValue = null) {
- let toInspect;
- if (overrideValue !== null) {
- toInspect = overrideValue;
- }
- else if (this[propertyName] !== undefined) {
- toInspect = this[propertyName];
- }
- console.log(this.name, outputMessage, util.inspect(toInspect, { breakLength: Infinity, colors: true, compact: true }));
- }
-
- HasOutput() {
- return !!this.outputValues.length;
- }
-
- FetchOutputValue() {
- return this.HasOutput() ? this.outputValues.shift() : undefined;
- }
-
- Input(inputValue) {
- this.runtimeInput.push(inputValue);
- if (this.running && this.awaitingInput) { this.Run(); }
- }
-
- Reset() {
- this.stack = new Stack(DeepClone(this._initialMemory));
- this.outputValues = [];
- this.options.inputModeMap = DeepClone(this.options._initialInputModeMap);
- this.running = false;
- this.awaitingInput = false;
- }
-
- ThrowError(msg = "") {
- throw new Error(`** IntCode Computer Error **\nError Message: ${msg}\nMemdump: ${JSON.stringify(this.stack.Dump())}\nPointer: ${this.stack.pointer}`);
- }
- };
|