Computer.js 6.0 KB

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