Computer.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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(ComputerParameterMode.IMMEDIATE_MODE)) === true) { }
  31. }
  32. /**
  33. * Execute a call using the provided opcode
  34. *
  35. * @param {number} rawOpcode A opcode to execute
  36. * @returns {boolean} False if the opcode was HALT, otherwise true
  37. */
  38. Execute(rawOpcode) {
  39. let status = true;
  40. const opcode = rawOpcode % 100;
  41. // console.log(`DEBUG: opcode: ${opcode}`);
  42. switch (opcode) {
  43. case this.OPCODES.ADD: {
  44. this.Operation_Add(rawOpcode);
  45. break;
  46. }
  47. case this.OPCODES.MULTIPLY: {
  48. const operandLeft = this.stack.Next().Get();
  49. const operandRight = this.stack.Next().Get();
  50. const position = this.stack.Next().Get();
  51. this.Operation_Multiply(operandLeft, operandRight, position);
  52. break;
  53. }
  54. case this.OPCODES.OUTPUT: {
  55. const outputPosition = this.stack.Next().Get();
  56. this.Operation_Output(outputPosition);
  57. break;
  58. }
  59. case this.OPCODES.HALT:
  60. status = false;
  61. break;
  62. default:
  63. throw Error(`Opcode ${opcode} not found\nMemdump: ${JSON.stringify(this.stack.Dump())}`);
  64. }
  65. this.stack.Next();
  66. return status;
  67. }
  68. /**
  69. * Parse operands based on the current parameter mode
  70. *
  71. * When the int computer is in Immediate Mode, the values are returned
  72. * as-is. When in Position Mode, the operands are used as memory
  73. * addresses, and the values at those addresses are returned instead.
  74. *
  75. * @returns {number[]} The parsed list of operands
  76. */
  77. _ParseOperands(...operands) {
  78. if (this.parameterMode == ComputerParameterMode.IMMEDIATE_MODE) { return operands; }
  79. return operands.map((operand) => this.stack.Get(operand));
  80. }
  81. /**
  82. * Execute the Add opcode
  83. *
  84. * Adds two numbers and stores the result at the provided position
  85. * on the stack.
  86. *
  87. * Parses the operand Parameter Mode out of the opcode used to make
  88. * this call.
  89. *
  90. * @param {number} rawOpcode The opcode in memory used to make this call
  91. * @returns {void}
  92. */
  93. Operation_Add(rawOpcode) {
  94. const operandLeftMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 1);
  95. const operandRightMode = ComputerParameterMode.ParseParameterMode(rawOpcode, 2);
  96. const operandLeft = this.stack.Next().Get(operandLeftMode);
  97. const operandRight = this.stack.Next().Get(operandRightMode);
  98. const outputPosition = this.stack.Next().Get(ComputerParameterMode.IMMEDIATE_MODE);
  99. const newValue = operandLeft + operandRight;
  100. this.stack.Put(outputPosition, newValue);
  101. }
  102. /**
  103. * Execute the Multiply opcode
  104. *
  105. * Multiplies two numbers and stores the result at the provided
  106. * position on the stack.
  107. *
  108. * @param {number} operandLeft The first operand
  109. * @param {number} operandRight The second operand
  110. * @param {number} outputPosition The position on the stack to place the result
  111. * @returns {void}
  112. */
  113. Operation_Multiply(operandLeft, operandRight, outputPosition) {
  114. [operandLeft, operandRight] = this._ParseOperands(operandLeft, operandRight);
  115. const newValue = operandLeft * operandRight;
  116. this.stack.Put(outputPosition, newValue);
  117. }
  118. /**
  119. * Execute the OUTPUT opcode
  120. *
  121. * @param {number} outputPosition The memory address of the value to output
  122. * @returns {void}
  123. */
  124. Operation_Output(outputPosition) {
  125. console.log(this.stack.Get(outputPosition));
  126. }
  127. /**
  128. * Outputs the computer's stack to the console
  129. * @returns {void}
  130. */
  131. DumpMemory() {
  132. console.log(this.stack.Dump());
  133. }
  134. /**
  135. * Resets the computer's memory to the value it was created with
  136. *
  137. * @returns {void}
  138. */
  139. Reset() {
  140. this.stack = new Stack(this._initialMemory);
  141. }
  142. /**
  143. * Sets the computer's memory to a new stack
  144. *
  145. * Note: This resets the computer's initial memory, so `Reset` will use this value
  146. *
  147. * @param {number[]} stack The new memory stack for the computer
  148. * @returns {void}
  149. */
  150. SetMemory(stack) {
  151. this._initialMemory = DeepClone(stack);
  152. this.stack = new Stack(stack);
  153. }
  154. };