Computer.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. const Stack = require("./Stack");
  2. const ComputerParameterMode = require("./ComputerParameterMode");
  3. /**
  4. * An Intcode Computer for the Advent of Code 2019 challenge
  5. *
  6. * @author Apis Necros
  7. */
  8. module.exports = class Computer {
  9. constructor(stack) {
  10. this.stack = new Stack(stack);
  11. this.OPCODES = {
  12. ADD: 1,
  13. MULTIPLY: 2,
  14. OUTPUT: 4,
  15. HALT: 99,
  16. };
  17. this.parameterMode = ComputerParameterMode.POSITION_MODE;
  18. }
  19. /**
  20. * Run the computer
  21. *
  22. * Runs opcodes on the stack until either the a HALT command is
  23. * encountered, or an error is thrown.
  24. * @returns {void}
  25. */
  26. Run() {
  27. // eslint-disable-next-line no-empty
  28. while (this.Execute(this.stack.Get()) === true) { }
  29. }
  30. /**
  31. * Execute a call using the provided opcode
  32. *
  33. * @param {number} opcode A opcode to execute
  34. * @returns {boolean} False if the opcode was HALT, otherwise true
  35. */
  36. Execute(opcode) {
  37. // console.log(`DEBUG: opcode: ${opcode}`)
  38. let status = true;
  39. switch (opcode) {
  40. case this.OPCODES.ADD: {
  41. const operandLeft = this.stack.Next().Get();
  42. const operandRight = this.stack.Next().Get();
  43. const position = this.stack.Next().Get();
  44. this.Operation_Add(operandLeft, operandRight, position);
  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`);
  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. * @param {number} operandLeft The first operand
  88. * @param {number} operandRight The second operand
  89. * @param {number} outputPosition The position on the stack to place the result
  90. * @returns {void}
  91. */
  92. Operation_Add(operandLeft, operandRight, outputPosition) {
  93. [operandLeft, operandRight] = this._ParseOperands(operandLeft, operandRight);
  94. const newValue = operandLeft + operandRight;
  95. this.stack.Put(outputPosition, newValue);
  96. }
  97. /**
  98. * Execute the Multiply opcode
  99. *
  100. * Multiplies two numbers and stores the result at the provided
  101. * position on the stack.
  102. *
  103. * @param {number} operandLeft The first operand
  104. * @param {number} operandRight The second operand
  105. * @param {number} outputPosition The position on the stack to place the result
  106. * @returns {void}
  107. */
  108. Operation_Multiply(operandLeft, operandRight, outputPosition) {
  109. [operandLeft, operandRight] = this._ParseOperands(operandLeft, operandRight);
  110. const newValue = operandLeft * operandRight;
  111. this.stack.Put(outputPosition, newValue);
  112. }
  113. /**
  114. * Execute the OUTPUT opcode
  115. *
  116. * @param {number} outputPosition The memory address of the value to output
  117. * @returns {void}
  118. */
  119. Operation_Output(outputPosition) {
  120. console.log(this.stack.Get(outputPosition));
  121. }
  122. /**
  123. * Outputs the computer's stack to the console
  124. * @returns {void}
  125. */
  126. DumpMemory() {
  127. console.log(this.stack.Dump());
  128. }
  129. };
  130. /**
  131. * Check whether the value at a specific spot in a number is non-zero
  132. *
  133. * Similar to the bitwise & operator, checks a "place" in a decimal number
  134. * to see if it has a non-zero value.
  135. *
  136. * @example
  137. * const test = 107;
  138. * console.log(DecimalPlaceIsNonZero(test, 1)) // true
  139. * console.log(DecimalPlaceIsNonZero(test, 2)) // false
  140. * console.log(DecimalPlaceIsNonZero(test, 3)) // true
  141. *
  142. * @param {number} input The number to check
  143. * @param {number} place The power of 10 to check against
  144. * @returns {boolean} Whether the value in that number's place is non-zero
  145. */
  146. function DecimalPlaceIsNonZero(input, place) {
  147. return !!Math.floor((input % (10 ** place)) / (10 ** (place - 1)));
  148. }