world_tictactoe.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /* game of tic-tac-toe with:
  2. * custom size of stage
  3. * custom number of players
  4. * mixed AI and players
  5. */
  6. class WorldTicTacToe extends World {
  7. /* assets for this world */
  8. assets() {
  9. return [
  10. "images/stage_empty.png",
  11. "images/stage_p1.png",
  12. "images/stage_p2.png",
  13. "images/stage_p3.png",
  14. "images/stage_p4.png",
  15. "images/stage_p5.png",
  16. "images/stage_p6.png",
  17. "images/button_quit.png",
  18. "images/button_restart.png",
  19. ];
  20. }
  21. /* runs before assets are initialized,
  22. * used to get data from previous world
  23. */
  24. constructor(data) {
  25. super(data);
  26. /* save who is AI and who is player */
  27. this.ai = data.ai;
  28. this.max_players = data.max_players;
  29. this.returner = data;
  30. this.stage_x = data.stage_x;
  31. this.stage_y = data.stage_y;
  32. this.mode = data.mode;
  33. } /* constructor */
  34. /* initialization */
  35. start() {
  36. /* text style */
  37. this.text_style = new PIXI.TextStyle({
  38. fill: "#ff0000",
  39. fontSize: 40,
  40. });
  41. /* game data */
  42. this.current_player = this.max_players;
  43. this.playing = true;
  44. /* stage */
  45. this.stage = [];
  46. for (let i = 0; i < this.stage_x *this.stage_y; i++) {
  47. this.stage.push(0);
  48. }
  49. /* stage appearence */
  50. this.stage_sprite = [];
  51. for (let i = 0; i < this.stage.length; i++) {
  52. this.stage_sprite[i] = new PIXI.Sprite(
  53. loader.resources["images/stage_empty.png"].texture
  54. );
  55. this.stage_sprite[i].x = width /(this.stage_x+1) *(i%this.stage_x+1);
  56. this.stage_sprite[i].y = height /(this.stage_y+1) *(Math.floor(i/this.stage_x)+1);
  57. this.stage_sprite[i].anchor = {x:0.5, y:0.5};
  58. this.stage_sprite[i].hitArea = new PIXI.Rectangle(0, 0, 100, 100);
  59. app.stage.addChild(this.stage_sprite[i]);
  60. }
  61. /* current player text */
  62. this.text_cplayer = new PIXI.Text("Hello World",
  63. this.text_style);
  64. this.text_cplayer.anchor = {x:0.5, y:1.0};
  65. this.text_cplayer.x = width/2;
  66. this.text_cplayer.y = height;
  67. app.stage.addChild(this.text_cplayer);
  68. /* restart button (visible when game ends) */
  69. this.button_restart = new PIXI.Sprite(
  70. loader.resources["images/button_restart.png"].texture
  71. );
  72. this.button_restart.visible = false;
  73. app.stage.addChild(this.button_restart);
  74. /* quit button */
  75. this.button_quit = new PIXI.Sprite(
  76. loader.resources["images/button_quit.png"].texture
  77. );
  78. this.button_quit.anchor = {x:1, y:0};
  79. this.button_quit.x = width;
  80. app.stage.addChild(this.button_quit);
  81. /* AI timer */
  82. this.ai_timer = 0;
  83. this.ai_max = 40;
  84. /* time attack mode depends on value of timer:
  85. * -1: disabled
  86. * positive: enabled
  87. */
  88. this.timer = -1;
  89. this.max_timer = 90;
  90. if (this.mode === "time") {
  91. this.timer = this.max_timer;
  92. this.text_timer = new PIXI.Text(
  93. "timer", this.text_style);
  94. this.text_timer.anchor = {x:0.5, y:0};
  95. this.text_timer.x = width/2;
  96. app.stage.addChild(this.text_timer);
  97. }
  98. /* start game */
  99. this.end_turn();
  100. }
  101. /* player touched stage,
  102. * if touched on empty part,
  103. * fill it and move to next player
  104. */
  105. update() {
  106. /* timer on time attack */
  107. if (this.playing && this.timer > 0) {
  108. this.timer--;
  109. this.text_timer.text = "time: " +this.timer;
  110. if (this.timer == 0) {
  111. this.end_turn();
  112. }
  113. }
  114. /* AI */
  115. if (this.playing && this.ai[this.current_player-1]) {
  116. /* advance timer */
  117. this.ai_timer++;
  118. /* time to make a move */
  119. if (this.ai_timer >= this.ai_max) {
  120. /* find all empty cells */
  121. let empty_stage = [];
  122. for (let i = 0; i < this.stage.length; i++) {
  123. if (this.stage[i] == 0) {
  124. empty_stage.push(i);
  125. }
  126. }
  127. /* pick one of the cells */
  128. let target = empty_stage[Math.floor( Math.random() *empty_stage.length )];
  129. /* make move
  130. * if for any reason it picks
  131. * an occupied cell, it will restart
  132. */
  133. this.make_move(target);
  134. /* restart timer for next bot */
  135. this.ai_timer = 0;
  136. } /* make move */
  137. /* ignore all input and updates while AI is playing */
  138. return;
  139. } /* AI */
  140. /* handle input */
  141. for (let e = 0; e < input.length; e++) {
  142. /* mouse left click */
  143. if (input[e].which == 1) {
  144. /* clicking point */
  145. let p = new PIXI.Point(input[e].offsetX, input[e].offsetY);
  146. /* clicked on quit button */
  147. if (this.button_quit.containsPoint(p)) {
  148. /* quit to menu */
  149. return world_list.indexOf(WorldMenu);
  150. } /* quit menu */
  151. /* game is finished
  152. * touch only restart and quit button
  153. */
  154. if (!this.playing) {
  155. /* restart button */
  156. if (this.button_restart.containsPoint(p)) {
  157. /* restart this world */
  158. return world_list.indexOf(WorldTicTacToe);
  159. } /* restart button */
  160. /* stop any other input */
  161. break;
  162. }
  163. /* through all buttons */
  164. for (let i = 0; i < this.stage_sprite.length; i++) {
  165. /* touched button */
  166. if (this.stage_sprite[i].containsPoint(p)) {
  167. this.make_move(i);
  168. /* touch only one button at a time */
  169. break;
  170. } /* touched button */
  171. } /* find button */
  172. } /* left click */
  173. } /* handle input */
  174. } /* update */
  175. /* check if someone won the game */
  176. check_victory() {
  177. /* divine the map into smaller 3x3 boxes, and
  178. * check them individually
  179. */
  180. for (let x = 0; x <= this.stage_x-3; x++)
  181. for (let y = 0; y <= this.stage_y-3; y++) {
  182. /* save result as it's the winner or draw
  183. * if it's 0, ignore it
  184. */
  185. let res = this.check_box(x, y);
  186. if (res != 0) {
  187. return res;
  188. }
  189. }
  190. /* no-one won yet */
  191. return 0;
  192. } /* check victory */
  193. /* checks a 3x3 box on the map, starting from x,y
  194. * to find a winner
  195. */
  196. check_box(x, y) {
  197. let stage = this.stage;
  198. let sp = x+(y*this.stage_x);
  199. let rows = this.stage_x;
  200. /* check rows-lines */
  201. for (let i = 0; i < 3; i++) {
  202. let hor = sp+(i*rows);
  203. /* horizontal */
  204. if (stage[hor] == stage[hor+1]
  205. && stage[hor+1] == stage[hor+2]
  206. && stage[hor] != 0) {
  207. return stage[hor];
  208. }
  209. let ver = sp+i;
  210. /* vertical */
  211. if (stage[ver] == stage[ver+rows]
  212. && stage[ver+rows] == stage[ver+rows*2]
  213. && stage[ver] != 0) {
  214. return stage[ver];
  215. }
  216. }
  217. /* diagonal */
  218. if (stage[sp] == stage[sp+rows+1]
  219. && stage[sp+rows+1] == stage[sp+rows*2+2]
  220. && stage[sp] != 0) {
  221. return stage[sp];
  222. }
  223. /* other diagonal */
  224. if (stage[sp+2] == stage[sp+rows+1]
  225. && stage[sp+rows+1] == stage[sp+rows*2]
  226. && stage[sp+2] != 0) {
  227. return stage[sp+2];
  228. }
  229. /* if all cells are filled, and there is no winner
  230. * it is a draw
  231. */
  232. let draw = true;
  233. for (let i = 0; i < this.stage_x *this.stage_y; i++) {
  234. if (this.stage[i] == 0) {
  235. draw = false;
  236. break;
  237. }
  238. }
  239. if (draw) {
  240. return -1;
  241. }
  242. /* no-one won yet */
  243. return 0;
  244. } /* check box */
  245. /* ends current player's turn */
  246. end_turn() {
  247. /* move to the next player */
  248. if (++this.current_player > this.max_players) {
  249. this.current_player = 1;
  250. }
  251. /* depending on the value of victory:
  252. * -1: draw
  253. * 0: game still running
  254. * >0: player with that number won
  255. */
  256. let v = this.check_victory();
  257. if (v > 0) {
  258. this.playing = false;
  259. this.text_cplayer.text =
  260. "player " +v +" won!";
  261. this.button_restart.visible = true;
  262. return;
  263. }
  264. else
  265. if (v == -1) {
  266. this.playing = false;
  267. this.text_cplayer.text = "Draw!";
  268. this.button_restart.visible = true;
  269. return;
  270. }
  271. /* update current player text */
  272. let txt_player = this.ai[this.current_player-1] ? "computer" : "player";
  273. this.text_cplayer.text = txt_player +": " +this.current_player;
  274. /* in time attack, reset timer for new player */
  275. if (this.timer >= 0) {
  276. this.timer = this.max_timer;
  277. }
  278. } /* end turn */
  279. /* if i is empty cell,
  280. * mark it with current player and return true
  281. * else return false
  282. */
  283. make_move(i) {
  284. /* if square is empty,
  285. * fill it with current player's index,
  286. * end turn
  287. */
  288. if (this.stage[i] == 0) {
  289. this.stage[i] = this.current_player;
  290. this.stage_sprite[i].texture =
  291. loader.resources["images/stage_p"
  292. +this.current_player +".png"].texture;
  293. this.end_turn();
  294. return true;
  295. }
  296. /* not cell changed */
  297. return false;
  298. } /* make move */
  299. } /* world tic-tac-toe */