starfish.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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.SetPointer(this.curr_direction[0], this.curr_direction[1]);
  134. }
  135. /**
  136. * Implement C and .
  137. */
  138. SetPointer(x, y) {
  139. this.pointer = [x, y];
  140. }
  141. /**
  142. * Implement ^
  143. *
  144. * Changes the swim direction upward
  145. */
  146. MoveUp() {
  147. this.curr_direction = this.directions.NORTH;
  148. }
  149. /**
  150. * Implement >
  151. *
  152. * Changes the swim direction rightward
  153. */
  154. MoveRight() {
  155. this.curr_direction = this.directions.EAST;
  156. this.dirWasLeft = false;
  157. }
  158. /**
  159. * Implement v
  160. *
  161. * Changes the swim direction downward
  162. */
  163. MoveDown() {
  164. this.curr_direction = this.directions.SOUTH;
  165. }
  166. /**
  167. * Implement <
  168. *
  169. * Changes the swim direction leftward
  170. */
  171. MoveLeft() {
  172. this.curr_direction = this.directions.WEST;
  173. this.dirWasLeft = true;
  174. }
  175. /**
  176. * Implement /
  177. *
  178. * Reflects the swim direction depending on its starting value
  179. */
  180. ReflectForward() {
  181. if (this.curr_direction == this.directions.NORTH) {
  182. this.MoveRight();
  183. }
  184. else if (this.curr_direction == this.directions.EAST) {
  185. this.MoveUp();
  186. }
  187. else if (this.curr_direction == this.directions.SOUTH) {
  188. this.MoveLeft();
  189. }
  190. else {
  191. this.MoveDown();
  192. }
  193. }
  194. /**
  195. * Implement \
  196. *
  197. * Reflects the swim direction depending on its starting value
  198. */
  199. ReflectBack() {
  200. if (this.curr_direction == this.directions.NORTH) {
  201. this.MoveLeft();
  202. }
  203. else if (this.curr_direction == this.directions.EAST) {
  204. this.MoveDown();
  205. }
  206. else if (this.curr_direction == this.directions.SOUTH) {
  207. this.MoveRight();
  208. }
  209. else {
  210. this.MoveUp();
  211. }
  212. }
  213. /**
  214. * Implement |
  215. *
  216. * Swaps the horizontal swim direction to its opposite
  217. */
  218. HorizontalMirror() {
  219. if (this.curr_direction == this.directions.EAST) {
  220. this.MoveLeft();
  221. }
  222. else {
  223. this.MoveRight();
  224. }
  225. }
  226. /**
  227. * Implement _
  228. *
  229. * Swaps the horizontal swim direction to its opposite
  230. */
  231. VerticalMirror() {
  232. if (this.curr_direction == this.directions.NORTH) {
  233. this.MoveDown();
  234. }
  235. else {
  236. this.MoveUp();
  237. }
  238. }
  239. /**
  240. * Implement #
  241. *
  242. * A combination of the vertical and the horizontal mirror
  243. */
  244. OmniMirror() {
  245. if (this.curr_direction[0]) {
  246. this.VerticalMirror();
  247. }
  248. else {
  249. this.HorizontalMirror();
  250. }
  251. }
  252. /**
  253. * Implement x
  254. *
  255. * Pseudo-randomly switches the swim direction
  256. */
  257. ShuffleDirection() {
  258. this.curr_direction = Object.values(this.directions)[Math.floor(Math.random() * 4)];
  259. }
  260. /**
  261. * Implement `
  262. *
  263. * Changes the swim direction based on the previous direction
  264. * @see https://esolangs.org/wiki/Starfish#Fisherman
  265. */
  266. Fisherman() {
  267. if (this.curr_direction[0]) {
  268. if (this.dirWasLeft) {
  269. this.MoveLeft();
  270. }
  271. else {
  272. this.MoveRight();
  273. }
  274. }
  275. else {
  276. if (this.onTheHook) {
  277. this.onTheHook = false;
  278. this.MoveUp();
  279. }
  280. else {
  281. this.onTheHook = true;
  282. this.MoveDown();
  283. }
  284. }
  285. }
  286. }
  287. /**
  288. * The stack class
  289. */
  290. class Stack {
  291. constructor() {
  292. /**
  293. * The stack
  294. * @type {int[]}
  295. */
  296. this.stack = [];
  297. /**
  298. * A single value saved off the stack
  299. * @type {int}
  300. */
  301. this.register = null;
  302. }
  303. /**
  304. * Wrapper function for Array.prototype.push
  305. * @param {*} newValue
  306. */
  307. Push(newValue) {
  308. this.stack.push(newValue);
  309. }
  310. /**
  311. * Wrapper function for Array.prototype.pop
  312. * @returns {*}
  313. */
  314. Pop() {
  315. return this.stack.pop();
  316. }
  317. /**
  318. * Implement }
  319. *
  320. * Shifts the entire stack leftward by one value
  321. */
  322. ShiftLeft() {
  323. const temp = this.stack.shift();
  324. this.stack.push(temp);
  325. }
  326. /**
  327. * Implement {
  328. *
  329. * Shifts the entire stack rightward by one value
  330. */
  331. ShiftRight() {
  332. const temp = this.stack.pop();
  333. this.stack.unshift(temp);
  334. }
  335. /**
  336. * Implement $
  337. *
  338. * Swaps the top two values of the stack
  339. */
  340. SwapTwo() {
  341. // TODO
  342. }
  343. /**
  344. * Implement :
  345. *
  346. * Duplicates the element on the top of the stack
  347. */
  348. Duplicate() {
  349. this.stack.push(this.stack[this.stack.length-1]);
  350. }
  351. /**
  352. * Implements ~
  353. *
  354. * Removes the element on the top of the stack
  355. */
  356. Remove() {
  357. this.stack.pop();
  358. }
  359. /**
  360. * Implement r
  361. *
  362. * Reverses the entire stack
  363. */
  364. Reverse() {
  365. this.stack.reverse();
  366. }
  367. /**
  368. * Implement l
  369. *
  370. * Pushes the length of the stack onto the top of the stack
  371. */
  372. PushLength() {
  373. this.stack.push(this.stack.length);
  374. }
  375. }
  376. /**
  377. * Get the char code of any character
  378. *
  379. * Can actually take any length of a value, but only returns the
  380. * char code of the first character.
  381. *
  382. * @param {*} value Any character
  383. * @returns {int} The value's char code
  384. */
  385. function dec(value) {
  386. return value.toString().charCodeAt(0);
  387. }