starfish.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /**
  2. * The code box class
  3. */
  4. class CodeBox {
  5. constructor(codeBoxID, initialStackID, outputID) {
  6. /**
  7. * Possible vectors the pointer can move in
  8. * @type {Object}
  9. */
  10. this.directions = {
  11. NORTH: [-1, 0],
  12. EAST: [ 0, 1],
  13. SOUTH: [ 1, 0],
  14. WEST: [ 0, -1],
  15. };
  16. /**
  17. * The current vector of the pointer
  18. * @type {int[]}
  19. */
  20. this.curr_direction = this.directions.EAST;
  21. /**
  22. * The Set of instructions to execute
  23. *
  24. * Either a 1 or 2-dimensional array
  25. * @type {Array|Array[]}
  26. */
  27. this.box = [];
  28. /**
  29. * The coordinates of the currently executing instruction inside the code box
  30. * @type {int[]}
  31. */
  32. this.pointer = [0,0];
  33. /**
  34. * Was the instruction last moving in the left direction
  35. *
  36. * Used by the {@link Fisherman}
  37. * @type {boolean}
  38. */
  39. this.dirWasLeft = false;
  40. /**
  41. * Are we currently under the influence of the {@link Fisherman}
  42. * @type {boolean}
  43. */
  44. this.onTheHook = false;
  45. /**
  46. * Are we currently processing code box instructions as a string
  47. *
  48. * 0 when false, otherwise it holds the char code for string delimiter, either
  49. * 34 or 39
  50. *
  51. * @type {int}
  52. */
  53. this.stringMode = 0;
  54. /**
  55. * The stack for the code box to work with
  56. *
  57. * @TODO Implement multiple stacks
  58. *
  59. * @type {Stack}
  60. */
  61. this.stack = new Stack();
  62. this.codeBoxDOM = document.getElementById(codeBoxID);
  63. if(!this.codeBoxDOM) {
  64. throw new Error(`Failed to find textarea with ID: ${codeBoxID}`);
  65. }
  66. this.outputDOM = document.getElementById(outputID);
  67. if(!this.outputDOM) {
  68. throw new Error(`Failed to find textarea with ID: ${outputID}`);
  69. }
  70. this.initialStackDOM = document.getElementById(initialStackID);
  71. if(!this.initialStackDOM) {
  72. throw new Error(`Failed to find input with ID: ${initialStackID}`);
  73. }
  74. }
  75. /**
  76. * Print the value to the display
  77. *
  78. * @TODO Set up an actual display
  79. * @param {*} value
  80. */
  81. Output(value) {
  82. this.outputDOM.value += value;
  83. }
  84. Execute(instruction) {
  85. let output = null;
  86. switch(instruction) {
  87. case 1:
  88. case 2:
  89. case 3:
  90. case 4:
  91. case 5:
  92. case 6:
  93. case 7:
  94. case 8:
  95. case 9:
  96. case 0:
  97. case "a":
  98. case "b":
  99. case "c":
  100. case "d":
  101. case "e":
  102. case "f":
  103. this.stack.Push(parseInt(instruction, 16));
  104. break;
  105. case "+": {
  106. const x = this.stack.Pop();
  107. const y = this.stack.Pop();
  108. this.stack.Push(x + y);
  109. break;
  110. }
  111. case "-": {
  112. const x = this.stack.Pop();
  113. const y = this.stack.Pop();
  114. this.stack.Push(x - y);
  115. break;
  116. }
  117. case "n":
  118. output = this.stack.Pop();
  119. break;
  120. case "o":
  121. output = String.fromCharCode(this.stack.Pop());
  122. break;
  123. case ";":
  124. output = true;
  125. break;
  126. default:
  127. throw new Error("Something's fishy!");
  128. }
  129. return output;
  130. }
  131. Swim() {
  132. const instruction = this.box[this.pointer[0], this.pointer[1]];
  133. if(this.stringMode != 0 && instruction != this.stringMode) {
  134. this.stack.Push(dec(instruction));
  135. }
  136. else {
  137. const exeResult = this.Execute(instruction);
  138. if(exeResult === true) {
  139. return true;
  140. }
  141. else if(exeResult != null) {
  142. this.Output(exeResult);
  143. }
  144. }
  145. this.Move();
  146. }
  147. Move() {
  148. const newX = this.pointer[0] + this.curr_direction[0];
  149. const newY = this.pointer[1] + this.curr_direction[1];
  150. this.SetPointer(newX, newY);
  151. }
  152. /**
  153. * Implement C and .
  154. */
  155. SetPointer(x, y) {
  156. this.pointer = [x, y];
  157. }
  158. /**
  159. * Implement ^
  160. *
  161. * Changes the swim direction upward
  162. */
  163. MoveUp() {
  164. this.curr_direction = this.directions.NORTH;
  165. }
  166. /**
  167. * Implement >
  168. *
  169. * Changes the swim direction rightward
  170. */
  171. MoveRight() {
  172. this.curr_direction = this.directions.EAST;
  173. this.dirWasLeft = false;
  174. }
  175. /**
  176. * Implement v
  177. *
  178. * Changes the swim direction downward
  179. */
  180. MoveDown() {
  181. this.curr_direction = this.directions.SOUTH;
  182. }
  183. /**
  184. * Implement <
  185. *
  186. * Changes the swim direction leftward
  187. */
  188. MoveLeft() {
  189. this.curr_direction = this.directions.WEST;
  190. this.dirWasLeft = true;
  191. }
  192. /**
  193. * Implement /
  194. *
  195. * Reflects the swim direction depending on its starting value
  196. */
  197. ReflectForward() {
  198. if (this.curr_direction == this.directions.NORTH) {
  199. this.MoveRight();
  200. }
  201. else if (this.curr_direction == this.directions.EAST) {
  202. this.MoveUp();
  203. }
  204. else if (this.curr_direction == this.directions.SOUTH) {
  205. this.MoveLeft();
  206. }
  207. else {
  208. this.MoveDown();
  209. }
  210. }
  211. /**
  212. * Implement \
  213. *
  214. * Reflects the swim direction depending on its starting value
  215. */
  216. ReflectBack() {
  217. if (this.curr_direction == this.directions.NORTH) {
  218. this.MoveLeft();
  219. }
  220. else if (this.curr_direction == this.directions.EAST) {
  221. this.MoveDown();
  222. }
  223. else if (this.curr_direction == this.directions.SOUTH) {
  224. this.MoveRight();
  225. }
  226. else {
  227. this.MoveUp();
  228. }
  229. }
  230. /**
  231. * Implement |
  232. *
  233. * Swaps the horizontal swim direction to its opposite
  234. */
  235. HorizontalMirror() {
  236. if (this.curr_direction == this.directions.EAST) {
  237. this.MoveLeft();
  238. }
  239. else {
  240. this.MoveRight();
  241. }
  242. }
  243. /**
  244. * Implement _
  245. *
  246. * Swaps the horizontal swim direction to its opposite
  247. */
  248. VerticalMirror() {
  249. if (this.curr_direction == this.directions.NORTH) {
  250. this.MoveDown();
  251. }
  252. else {
  253. this.MoveUp();
  254. }
  255. }
  256. /**
  257. * Implement #
  258. *
  259. * A combination of the vertical and the horizontal mirror
  260. */
  261. OmniMirror() {
  262. if (this.curr_direction[0]) {
  263. this.VerticalMirror();
  264. }
  265. else {
  266. this.HorizontalMirror();
  267. }
  268. }
  269. /**
  270. * Implement x
  271. *
  272. * Pseudo-randomly switches the swim direction
  273. */
  274. ShuffleDirection() {
  275. this.curr_direction = Object.values(this.directions)[Math.floor(Math.random() * 4)];
  276. }
  277. /**
  278. * Implement `
  279. *
  280. * Changes the swim direction based on the previous direction
  281. * @see https://esolangs.org/wiki/Starfish#Fisherman
  282. */
  283. Fisherman() {
  284. if (this.curr_direction[0]) {
  285. if (this.dirWasLeft) {
  286. this.MoveLeft();
  287. }
  288. else {
  289. this.MoveRight();
  290. }
  291. }
  292. else {
  293. if (this.onTheHook) {
  294. this.onTheHook = false;
  295. this.MoveUp();
  296. }
  297. else {
  298. this.onTheHook = true;
  299. this.MoveDown();
  300. }
  301. }
  302. }
  303. }
  304. /**
  305. * The stack class
  306. */
  307. class Stack {
  308. constructor() {
  309. /**
  310. * The stack
  311. * @type {int[]}
  312. */
  313. this.stack = [];
  314. /**
  315. * A single value saved off the stack
  316. * @type {int}
  317. */
  318. this.register = null;
  319. }
  320. /**
  321. * Wrapper function for Array.prototype.push
  322. * @param {*} newValue
  323. */
  324. Push(newValue) {
  325. this.stack.push(newValue);
  326. }
  327. /**
  328. * Wrapper function for Array.prototype.pop
  329. * @returns {*}
  330. */
  331. Pop() {
  332. return this.stack.pop();
  333. }
  334. /**
  335. * Implement }
  336. *
  337. * Shifts the entire stack leftward by one value
  338. */
  339. ShiftLeft() {
  340. const temp = this.stack.shift();
  341. this.stack.push(temp);
  342. }
  343. /**
  344. * Implement {
  345. *
  346. * Shifts the entire stack rightward by one value
  347. */
  348. ShiftRight() {
  349. const temp = this.stack.pop();
  350. this.stack.unshift(temp);
  351. }
  352. /**
  353. * Implement $
  354. *
  355. * Swaps the top two values of the stack
  356. */
  357. SwapTwo() {
  358. // TODO
  359. }
  360. /**
  361. * Implement :
  362. *
  363. * Duplicates the element on the top of the stack
  364. */
  365. Duplicate() {
  366. this.stack.push(this.stack[this.stack.length-1]);
  367. }
  368. /**
  369. * Implements ~
  370. *
  371. * Removes the element on the top of the stack
  372. */
  373. Remove() {
  374. this.stack.pop();
  375. }
  376. /**
  377. * Implement r
  378. *
  379. * Reverses the entire stack
  380. */
  381. Reverse() {
  382. this.stack.reverse();
  383. }
  384. /**
  385. * Implement l
  386. *
  387. * Pushes the length of the stack onto the top of the stack
  388. */
  389. PushLength() {
  390. this.stack.push(this.stack.length);
  391. }
  392. }
  393. /**
  394. * Get the char code of any character
  395. *
  396. * Can actually take any length of a value, but only returns the
  397. * char code of the first character.
  398. *
  399. * @param {*} value Any character
  400. * @returns {int} The value's char code
  401. */
  402. function dec(value) {
  403. return value.toString().charCodeAt(0);
  404. }