starfish.js 9.1 KB

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