const ComputerParameterMode = require("./ComputerParameterMode"); /** * The stack, or memory, for the Intcode Computer * * @author Apis Necros */ module.exports = class Stack { constructor(stack) { this.pointer = 0; this._stack = stack; /** * The relative base offset for the stack * * This value is used when finding a parameter in Relative Parameter Mode. * It is added to an address to find the needed value at the calculated * address. */ this.relativeBaseOffset = 0; } /** * Move the stack's pointer to the right by 1 * @returns {void} */ Next() { this.pointer++; return this; } /** * Move the stack's pointer to the left by 1 * @returns {void} */ Prev() { this.pointer--; } /** * Get the value from the stack by the pointer's current position * * When `isOutput` is set to `true`, then a value retrieved in Immediate Mode, * or the result of a Relative Mode offset is returned. * * @param {number} [parameterMode=0] The Parameter Mode to use to retrieve the value * @param {boolean} [isOutput=false] Is the parameter going to be an output position * @returns {number} The value at the current pointer position on the stack */ Get(parameterMode = ComputerParameterMode.POSITION_MODE, isOutput = false) { let value = this._stack[this.pointer]; if (!isOutput && parameterMode == ComputerParameterMode.POSITION_MODE) { value = this._stack[value]; } else if (parameterMode == ComputerParameterMode.RELATIVE_MODE) { const newPointer = value + this.relativeBaseOffset; value = isOutput ? newPointer : this._stack[newPointer]; } return value ?? 0; } /** * Get a value at a given index on the stack * * @param {number} index The index of the value to retrieve * @param {number} [parameterMode=0] The Parameter Mode to use to retrieve the value * @returns {number} The value at the given index, or 0 if the index hasn't been defined yet */ GetAtIndex(index, parameterMode = ComputerParameterMode.POSITION_MODE) { if (index == null || Number.isNaN(index) || !Number.isInteger(index)) { throw new TypeError("index must be an integer"); } let value = this._stack[index]; if (parameterMode == ComputerParameterMode.POSITION_MODE) { value = this._stack[value]; } else if (parameterMode == ComputerParameterMode.RELATIVE_MODE) { value = this._stack[value + this.relativeBaseOffset]; } return value ?? 0; } /** * Use the value at the current pointer to get a value at another position * * This is essentially shorthand for `stack.Get(stack.Get())` * * @returns {number} The value at the index given by the pointer's current position */ GetUsingStackValue() { return this.Get(this._stack[this.pointer]); } /** * Push a new value onto the end of the stack * * @param {number} value The value to add * @returns {void} */ Push(value) { this._stack[++this.pointer] = value; } /** * Pop a value off the end of the stack * * @returns {number} * @returns {void} */ Pop() { return this._stack.pop(); } /** * Overwrite the value at a given index with a new value * * The index need not be defined * * @param {number} index The index on the stack to write a value to * @param {number} value The value to write to that position * @returns {void} */ Put(index, value) { this._stack[index] = value; } /** * Sets the pointer to a new address in memory * * If the address is out of the upper bounds of the current memory stack, * the stack will be expanded, filling with 0's as needed. * * @param {number} newAddress The new pointer address * @returns {void} */ SetPointerAddress(newAddress) { if (newAddress > this._stack.length) { // Expand the stack if needed const oldLength = this._stack.length; this._stack[newAddress] = 0; this._stack.fill(0, oldLength, newAddress); } this.pointer = newAddress; } /** * Adjust the Relative Base Offset by an amount * * @param {number} adjustment A positive or negative integer to add to the Relative Base Offset * @returns {void} */ AdjustRelativeBaseOffset(adjustment) { this.relativeBaseOffset += adjustment; } /** * Return the whole stack * * @returns {number[]} The current stack */ Dump() { return this._stack; } };