const Stack = require("./Stack"); const ComputerParameterMode = require('./ComputerParameterMode'); /** * An Intcode Computer for the Advent of Code 2019 challenge * * @author Apis Necros */ module.exports = class Computer { constructor(stack) { this.stack = new Stack(stack); this.OPCODES = { ADD: 1, MULTIPLY: 2, OUTPUT: 4, HALT: 99, }; this.paramMode = ComputerParameterMode.POSITION_MODE; } /** * Run the computer * * Runs opcodes on the stack until either the a HALT command is * encountered, or an error is thrown. * @returns {void} */ Run() { // eslint-disable-next-line no-empty while (this.Execute(this.stack.Get()) === true) { } } /** * Execute a call using the provided opcode * * @param {number} opcode A opcode to execute * @returns {boolean} False if the opcode was HALT, otherwise true */ Execute(opcode) { // console.log(`DEBUG: opcode: ${opcode}`) let status = true; switch (opcode) { case this.OPCODES.ADD: { const operandLeft = this.stack.Next().Get(); const operandRight = this.stack.Next().Get(); const position = this.stack.Next().Get(); this.Operation_Add(operandLeft, operandRight, position); break; } case this.OPCODES.MULTIPLY: { const operandLeft = this.stack.Next().Get(); const operandRight = this.stack.Next().Get(); const position = this.stack.Next().Get(); this.Operation_Multiply(operandLeft, operandRight, position); break; } case this.OPCODES.OUTPUT: { const outputPosition = this.stack.Next().Get(); this.Operation_Output(outputPosition); break; } case this.OPCODES.HALT: status = false; break; default: throw Error(`Opcode ${opcode} not found`); } this.stack.Next(); return status; } /** * Execute the Add opcode * * Adds two numbers and stores the result at the provided position * on the stack. * * @param {number} operandLeft The first operand * @param {number} operandRight The second operand * @param {number} outputPosition The position on the stack to place the result * @returns {void} */ Operation_Add(operandLeft, operandRight, outputPosition) { const newValue = operandLeft + operandRight; this.stack.Put(outputPosition, newValue); } /** * Execute the Multiply opcode * * Multiplies two numbers and stores the result at the provided * position on the stack. * * @param {number} operandLeft The first operand * @param {number} operandRight The second operand * @param {number} outputPosition The position on the stack to place the result * @returns {void} */ Operation_Multiply(operandLeft, operandRight, outputPosition) { const newValue = operandLeft * operandRight; this.stack.Put(outputPosition, newValue); } /** * Execute the OUTPUT opcode * * @param {number} outputPosition The memory address of the value to output */ Operation_Output(outputPosition) { console.log(this.stack.Get(outputPosition)); } /** * Outputs the computer's stack to the console * @returns {void} */ DumpMemory() { console.log(this.stack.Dump()); } }; /** * Check whether the value at a specific spot in a number is non-zero * * Similar to the bitwise & operator, checks a "place" in a decimal number * to see if it has a non-zero value. * * @example * const test = 107; * console.log(DecimalPlaceIsNonZero(test, 1)) // true * console.log(DecimalPlaceIsNonZero(test, 2)) // false * console.log(DecimalPlaceIsNonZero(test, 3)) // true * * @param {number} input The number to check * @param {number} place The power of 10 to check against * @returns {boolean} Whether the value in that number's place is non-zero */ function DecimalPlaceIsNonZero(input, place) { return !!Math.floor((input % 10**place) / 10**(place - 1)); }