Computer.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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. this.Operation_Output();
  59. break;
  60. }
  61. case this.OPCODES.HALT:
  62. status = false;
  63. break;
  64. default:
  65. throw Error(`Opcode ${opcode} not found\nMemdump: ${JSON.stringify(this.stack.Dump())}`);
  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. * Parses the operand Parameter Mode out of the opcode used to make
  90. * this call.
  91. *
  92. * @param {number} rawOpcode The opcode in memory used to make this call
  93. * @returns {void}
  94. */
  95. Operation_Add(rawOpcode) {
  96. const operandLeftMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
  97. const operandRightMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 2);
  98. const operandLeft = this.stack.Next().Get(operandLeftMode);
  99. const operandRight = this.stack.Next().Get(operandRightMode);
  100. const outputPosition = this.stack.Next().Get(ComputerParameterMode.IMMEDIATE_MODE);
  101. const newValue = operandLeft + operandRight;
  102. this.stack.Put(outputPosition, newValue);
  103. }
  104. /**
  105. * Execute the Multiply opcode
  106. *
  107. * Multiplies two numbers and stores the result at the provided
  108. * position on the stack.
  109. *
  110. * @param {number} rawOpcode The opcode in memory used to make this call
  111. * @returns {void}
  112. */
  113. Operation_Multiply(rawOpcode) {
  114. const operandLeftMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
  115. const operandRightMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 2);
  116. const operandLeft = this.stack.Next().Get(operandLeftMode);
  117. const operandRight = this.stack.Next().Get(operandRightMode);
  118. const outputPosition = this.stack.Next().Get(ComputerParameterMode.IMMEDIATE_MODE);
  119. const newValue = operandLeft * operandRight;
  120. this.stack.Put(outputPosition, newValue);
  121. }
  122. /**
  123. * Execute the Input opcode
  124. *
  125. * Prompts the user to input a value from the command line
  126. *
  127. * @returns {void}
  128. */
  129. Operation_Input() {
  130. const outputPosition = this.stack.Next().Get(ComputerParameterMode.IMMEDIATE_MODE);
  131. let userInput;
  132. do {
  133. userInput = Number(prompt("Please enter a number: "));
  134. } while (Number.isNaN(userInput));
  135. this.stack.Put(outputPosition, userInput);
  136. }
  137. /**
  138. * Execute the OUTPUT opcode
  139. *
  140. * @returns {void}
  141. */
  142. Operation_Output() {
  143. const currAddress = this.stack.pointer;
  144. const outputPosition = this.stack.Next().Get(ComputerParameterMode.IMMEDIATE_MODE);
  145. console.log(`OUTPUT FROM ADDRESS ${currAddress}: ${this.stack.GetAtIndex(outputPosition, ComputerParameterMode.IMMEDIATE_MODE)}`);
  146. }
  147. /**
  148. * Outputs the computer's stack to the console
  149. * @returns {void}
  150. */
  151. DumpMemory() {
  152. console.log(this.stack.Dump());
  153. }
  154. /**
  155. * Resets the computer's memory to the value it was created with
  156. *
  157. * @returns {void}
  158. */
  159. Reset() {
  160. this.stack = new Stack(this._initialMemory);
  161. }
  162. /**
  163. * Sets the computer's memory to a new stack
  164. *
  165. * Note: This resets the computer's initial memory, so `Reset` will use this value
  166. *
  167. * @param {number[]} stack The new memory stack for the computer
  168. * @returns {void}
  169. */
  170. SetMemory(stack) {
  171. this._initialMemory = DeepClone(stack);
  172. this.stack = new Stack(stack);
  173. }
  174. };