Computer.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. const Stack = require("./Stack");
  2. const ComputerParameterMode = require("./ComputerParameterMode");
  3. const { DeepClone } = require("./common");
  4. /**
  5. * An Intcode Computer for the Advent of Code 2019 challenge
  6. *
  7. * @author Apis Necros
  8. */
  9. module.exports = class Computer {
  10. constructor(stack) {
  11. this._initialMemory = DeepClone(stack);
  12. this.stack = new Stack(stack);
  13. this.OPCODES = {
  14. ADD: 1,
  15. MULTIPLY: 2,
  16. OUTPUT: 4,
  17. HALT: 99,
  18. };
  19. this.parameterMode = ComputerParameterMode.POSITION_MODE;
  20. }
  21. /**
  22. * Run the computer
  23. *
  24. * Runs opcodes on the stack until either the a HALT command is
  25. * encountered, or an error is thrown.
  26. * @returns {void}
  27. */
  28. Run() {
  29. // eslint-disable-next-line no-empty
  30. while (this.Execute(this.stack.Get()) === true) { }
  31. }
  32. /**
  33. * Execute a call using the provided opcode
  34. *
  35. * @param {number} opcode A opcode to execute
  36. * @returns {boolean} False if the opcode was HALT, otherwise true
  37. */
  38. Execute(opcode) {
  39. // console.log(`DEBUG: opcode: ${opcode}`)
  40. let status = true;
  41. switch (opcode) {
  42. case this.OPCODES.ADD: {
  43. const operandLeft = this.stack.Next().Get();
  44. const operandRight = this.stack.Next().Get();
  45. const position = this.stack.Next().Get();
  46. this.Operation_Add(operandLeft, operandRight, position);
  47. break;
  48. }
  49. case this.OPCODES.MULTIPLY: {
  50. const operandLeft = this.stack.Next().Get();
  51. const operandRight = this.stack.Next().Get();
  52. const position = this.stack.Next().Get();
  53. this.Operation_Multiply(operandLeft, operandRight, position);
  54. break;
  55. }
  56. case this.OPCODES.OUTPUT: {
  57. const outputPosition = this.stack.Next().Get();
  58. this.Operation_Output(outputPosition);
  59. break;
  60. }
  61. case this.OPCODES.HALT:
  62. status = false;
  63. break;
  64. default:
  65. throw Error(`Opcode ${opcode} not found`);
  66. }
  67. this.stack.Next();
  68. return status;
  69. }
  70. /**
  71. * Parse operands based on the current parameter mode
  72. *
  73. * When the int computer is in Immediate Mode, the values are returned
  74. * as-is. When in Position Mode, the operands are used as memory
  75. * addresses, and the values at those addresses are returned instead.
  76. *
  77. * @returns {number[]} The parsed list of operands
  78. */
  79. _ParseOperands(...operands) {
  80. if (this.parameterMode == ComputerParameterMode.IMMEDIATE_MODE) { return operands; }
  81. return operands.map((operand) => this.stack.Get(operand));
  82. }
  83. /**
  84. * Execute the Add opcode
  85. *
  86. * Adds two numbers and stores the result at the provided position
  87. * on the stack.
  88. *
  89. * @param {number} operandLeft The first operand
  90. * @param {number} operandRight The second operand
  91. * @param {number} outputPosition The position on the stack to place the result
  92. * @returns {void}
  93. */
  94. Operation_Add(operandLeft, operandRight, outputPosition) {
  95. [operandLeft, operandRight] = this._ParseOperands(operandLeft, operandRight);
  96. const newValue = operandLeft + operandRight;
  97. this.stack.Put(outputPosition, newValue);
  98. }
  99. /**
  100. * Execute the Multiply opcode
  101. *
  102. * Multiplies two numbers and stores the result at the provided
  103. * position on the stack.
  104. *
  105. * @param {number} operandLeft The first operand
  106. * @param {number} operandRight The second operand
  107. * @param {number} outputPosition The position on the stack to place the result
  108. * @returns {void}
  109. */
  110. Operation_Multiply(operandLeft, operandRight, outputPosition) {
  111. [operandLeft, operandRight] = this._ParseOperands(operandLeft, operandRight);
  112. const newValue = operandLeft * operandRight;
  113. this.stack.Put(outputPosition, newValue);
  114. }
  115. /**
  116. * Execute the OUTPUT opcode
  117. *
  118. * @param {number} outputPosition The memory address of the value to output
  119. * @returns {void}
  120. */
  121. Operation_Output(outputPosition) {
  122. console.log(this.stack.Get(outputPosition));
  123. }
  124. /**
  125. * Outputs the computer's stack to the console
  126. * @returns {void}
  127. */
  128. DumpMemory() {
  129. console.log(this.stack.Dump());
  130. }
  131. /**
  132. * Resets the computer's memory to the value it was created with
  133. *
  134. * @returns {void}
  135. */
  136. Reset() {
  137. this.stack = new Stack(this._initialMemory);
  138. }
  139. /**
  140. * Sets the computer's memory to a new stack
  141. *
  142. * Note: This resets the computer's initial memory, so `Reset` will use this value
  143. *
  144. * @param {number[]} stack The new memory stack for the computer
  145. * @returns {void}
  146. */
  147. SetMemory(stack) {
  148. this._initialMemory = DeepClone(stack);
  149. this.stack = new Stack(stack);
  150. }
  151. };