Computer.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. this.Operation_Multiply(rawOpcode);
  51. break;
  52. }
  53. case this.OPCODES.INPUT: {
  54. this.Operation_Input();
  55. break;
  56. }
  57. case this.OPCODES.OUTPUT: {
  58. const outputPosition = this.stack.Next().Get();
  59. this.Operation_Output(outputPosition);
  60. break;
  61. }
  62. case this.OPCODES.HALT:
  63. status = false;
  64. break;
  65. default:
  66. throw Error(`Opcode ${opcode} not found\nMemdump: ${JSON.stringify(this.stack.Dump())}`);
  67. }
  68. this.stack.Next();
  69. return status;
  70. }
  71. /**
  72. * Parse operands based on the current parameter mode
  73. *
  74. * When the int computer is in Immediate Mode, the values are returned
  75. * as-is. When in Position Mode, the operands are used as memory
  76. * addresses, and the values at those addresses are returned instead.
  77. *
  78. * @returns {number[]} The parsed list of operands
  79. */
  80. _ParseOperands(...operands) {
  81. if (this.parameterMode == ComputerParameterMode.IMMEDIATE_MODE) { return operands; }
  82. return operands.map((operand) => this.stack.Get(operand));
  83. }
  84. /**
  85. * Execute the Add opcode
  86. *
  87. * Adds two numbers and stores the result at the provided position
  88. * on the stack.
  89. *
  90. * Parses the operand Parameter Mode out of the opcode used to make
  91. * this call.
  92. *
  93. * @param {number} rawOpcode The opcode in memory used to make this call
  94. * @returns {void}
  95. */
  96. Operation_Add(rawOpcode) {
  97. const operandLeftMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
  98. const operandRightMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 2);
  99. const operandLeft = this.stack.Next().Get(operandLeftMode);
  100. const operandRight = this.stack.Next().Get(operandRightMode);
  101. const outputPosition = this.stack.Next().Get(ComputerParameterMode.IMMEDIATE_MODE);
  102. const newValue = operandLeft + operandRight;
  103. this.stack.Put(outputPosition, newValue);
  104. }
  105. /**
  106. * Execute the Multiply opcode
  107. *
  108. * Multiplies two numbers and stores the result at the provided
  109. * position on the stack.
  110. *
  111. * @param {number} rawOpcode The opcode in memory used to make this call
  112. * @returns {void}
  113. */
  114. Operation_Multiply(rawOpcode) {
  115. const operandLeftMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
  116. const operandRightMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 2);
  117. const operandLeft = this.stack.Next().Get(operandLeftMode);
  118. const operandRight = this.stack.Next().Get(operandRightMode);
  119. const outputPosition = this.stack.Next().Get(ComputerParameterMode.IMMEDIATE_MODE);
  120. const newValue = operandLeft * operandRight;
  121. this.stack.Put(outputPosition, newValue);
  122. }
  123. /**
  124. * Execute the Input opcode
  125. *
  126. * Prompts the user to input a value from the command line
  127. *
  128. * @returns {void}
  129. */
  130. Operation_Input() {
  131. const outputPosition = this.stack.Next().Get(ComputerParameterMode.IMMEDIATE_MODE);
  132. console.log(`DEBUG: outputPosition: ${outputPosition}`);
  133. let userInput;
  134. do {
  135. userInput = Number(prompt("Please enter a number: "));
  136. } while (Number.isNaN(userInput));
  137. console.log(`DEBUG: userInput: ${userInput}`);
  138. this.stack.Put(outputPosition, userInput);
  139. }
  140. /**
  141. * Execute the OUTPUT opcode
  142. *
  143. * @param {number} outputPosition The memory address of the value to output
  144. * @returns {void}
  145. */
  146. Operation_Output(outputPosition) {
  147. console.log(this.stack.Get(outputPosition));
  148. }
  149. /**
  150. * Outputs the computer's stack to the console
  151. * @returns {void}
  152. */
  153. DumpMemory() {
  154. console.log(this.stack.Dump());
  155. }
  156. /**
  157. * Resets the computer's memory to the value it was created with
  158. *
  159. * @returns {void}
  160. */
  161. Reset() {
  162. this.stack = new Stack(this._initialMemory);
  163. }
  164. /**
  165. * Sets the computer's memory to a new stack
  166. *
  167. * Note: This resets the computer's initial memory, so `Reset` will use this value
  168. *
  169. * @param {number[]} stack The new memory stack for the computer
  170. * @returns {void}
  171. */
  172. SetMemory(stack) {
  173. this._initialMemory = DeepClone(stack);
  174. this.stack = new Stack(stack);
  175. }
  176. };