tic-tac-toe.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. import * as anchor from "@coral-xyz/anchor";
  2. import { AnchorError, type Program } from "@coral-xyz/anchor";
  3. import { TicTacToe } from '../target/types/tic_tac_toe';
  4. import { expect } from 'chai';
  5. async function play(program: Program<TicTacToe>, game, player, tile, expectedTurn, expectedGameState, expectedBoard) {
  6. await program.methods
  7. .play(tile)
  8. .accounts({
  9. player: player.publicKey,
  10. game
  11. })
  12. .signers(player instanceof (anchor.Wallet as any) ? [] : [player])
  13. .rpc();
  14. const gameState = await program.account.game.fetch(game);
  15. expect(gameState.turn).to.equal(expectedTurn);
  16. expect(gameState.state).to.eql(expectedGameState);
  17. expect(gameState.board)
  18. .to
  19. .eql(expectedBoard);
  20. }
  21. describe('tic-tac-toe', () => {
  22. // Configure the client to use the local cluster.
  23. anchor.setProvider(anchor.AnchorProvider.env());
  24. const program = anchor.workspace.TicTacToe as Program<TicTacToe>;
  25. const programProvider = program.provider as anchor.AnchorProvider;
  26. it('setup game!', async() => {
  27. const gameKeypair = anchor.web3.Keypair.generate();
  28. const playerOne = programProvider.wallet;
  29. const playerTwo = anchor.web3.Keypair.generate();
  30. await program.methods
  31. .setupGame(playerTwo.publicKey)
  32. .accounts({
  33. game: gameKeypair.publicKey,
  34. playerOne: playerOne.publicKey,
  35. })
  36. .signers([gameKeypair])
  37. .rpc();
  38. let gameState = await program.account.game.fetch(gameKeypair.publicKey);
  39. expect(gameState.turn).to.equal(1);
  40. expect(gameState.players)
  41. .to
  42. .eql([playerOne.publicKey, playerTwo.publicKey]);
  43. expect(gameState.state).to.eql({ active: {} });
  44. expect(gameState.board)
  45. .to
  46. .eql([[null,null,null],[null,null,null],[null,null,null]]);
  47. });
  48. it('player one wins!', async () => {
  49. const gameKeypair = anchor.web3.Keypair.generate();
  50. const playerOne = programProvider.wallet;
  51. const playerTwo = anchor.web3.Keypair.generate();
  52. await program.methods
  53. .setupGame(playerTwo.publicKey)
  54. .accounts({
  55. game: gameKeypair.publicKey,
  56. playerOne: playerOne.publicKey,
  57. })
  58. .signers([gameKeypair])
  59. .rpc();
  60. let gameState = await program.account.game.fetch(gameKeypair.publicKey);
  61. expect(gameState.turn).to.equal(1);
  62. expect(gameState.players)
  63. .to
  64. .eql([playerOne.publicKey, playerTwo.publicKey]);
  65. expect(gameState.state).to.eql({ active: {} });
  66. expect(gameState.board)
  67. .to
  68. .eql([[null,null,null],[null,null,null],[null,null,null]]);
  69. await play(
  70. program,
  71. gameKeypair.publicKey,
  72. playerOne,
  73. {row: 0, column: 0},
  74. 2,
  75. { active: {}, },
  76. [
  77. [{x:{}},null,null],
  78. [null,null,null],
  79. [null,null,null]
  80. ]
  81. );
  82. try {
  83. await play(
  84. program,
  85. gameKeypair.publicKey,
  86. playerOne, // same player in subsequent turns
  87. // change sth about the tx because
  88. // duplicate tx that come in too fast
  89. // after each other may get dropped
  90. {row: 1, column: 0},
  91. 2,
  92. { active: {}, },
  93. [
  94. [{x:{}},null,null],
  95. [null,null,null],
  96. [null,null,null]
  97. ]
  98. );
  99. chai.assert(false, "should've failed but didn't ");
  100. } catch (_err) {
  101. expect(_err).to.be.instanceOf(AnchorError);
  102. const err: AnchorError = _err;
  103. expect(err.error.errorCode.code).to.equal("NotPlayersTurn");
  104. expect(err.error.errorCode.number).to.equal(6003);
  105. expect(err.program.equals(program.programId)).is.true;
  106. expect(err.error.comparedValues).to.deep.equal([playerTwo.publicKey, playerOne.publicKey]);
  107. }
  108. await play(
  109. program,
  110. gameKeypair.publicKey,
  111. playerTwo,
  112. {row: 1, column: 0},
  113. 3,
  114. { active: {}, },
  115. [
  116. [{x:{}},null,null],
  117. [{o:{}},null,null],
  118. [null,null,null]
  119. ]
  120. );
  121. await play(
  122. program,
  123. gameKeypair.publicKey,
  124. playerOne,
  125. {row: 0, column: 1},
  126. 4,
  127. { active: {}, },
  128. [
  129. [{x:{}},{x: {}},null],
  130. [{o:{}},null,null],
  131. [null,null,null]
  132. ]
  133. );
  134. try {
  135. await play(
  136. program,
  137. gameKeypair.publicKey,
  138. playerTwo,
  139. {row: 5, column: 1}, // out of bounds row
  140. 4,
  141. { active: {}, },
  142. [
  143. [{x:{}},{x: {}},null],
  144. [{o:{}},null,null],
  145. [null,null,null]
  146. ]
  147. );
  148. chai.assert(false, "should've failed but didn't ");
  149. } catch (_err) {
  150. expect(_err).to.be.instanceOf(AnchorError);
  151. const err: AnchorError = _err;
  152. expect(err.error.errorCode.number).to.equal(6000);
  153. expect(err.error.errorCode.code).to.equal("TileOutOfBounds");
  154. }
  155. await play(
  156. program,
  157. gameKeypair.publicKey,
  158. playerTwo,
  159. {row: 1, column: 1},
  160. 5,
  161. { active: {}, },
  162. [
  163. [{x:{}},{x: {}},null],
  164. [{o:{}},{o:{}},null],
  165. [null,null,null]
  166. ]
  167. );
  168. try {
  169. await play(
  170. program,
  171. gameKeypair.publicKey,
  172. playerOne,
  173. {row: 0, column: 0},
  174. 5,
  175. { active: {}, },
  176. [
  177. [{x:{}},{x: {}},null],
  178. [{o:{}},{o:{}},null],
  179. [null,null,null]
  180. ]
  181. );
  182. chai.assert(false, "should've failed but didn't ");
  183. } catch (_err) {
  184. expect(_err).to.be.instanceOf(AnchorError);
  185. const err: AnchorError = _err;
  186. expect(err.error.errorCode.number).to.equal(6001);
  187. }
  188. await play(
  189. program,
  190. gameKeypair.publicKey,
  191. playerOne,
  192. {row: 0, column: 2},
  193. 5,
  194. { won: { winner: playerOne.publicKey }, },
  195. [
  196. [{x:{}},{x: {}},{x: {}}],
  197. [{o:{}},{o:{}},null],
  198. [null,null,null]
  199. ]
  200. );
  201. try {
  202. await play(
  203. program,
  204. gameKeypair.publicKey,
  205. playerOne,
  206. {row: 0, column: 2},
  207. 5,
  208. { won: { winner: playerOne.publicKey }, },
  209. [
  210. [{x:{}},{x: {}},{x: {}}],
  211. [{o:{}},{o:{}},null],
  212. [null,null,null]
  213. ]
  214. );
  215. chai.assert(false, "should've failed but didn't ");
  216. } catch (_err) {
  217. expect(_err).to.be.instanceOf(AnchorError);
  218. const err: AnchorError = _err;
  219. expect(err.error.errorCode.number).to.equal(6002);
  220. }
  221. })
  222. it('tie', async () => {
  223. const gameKeypair = anchor.web3.Keypair.generate();
  224. const playerOne = programProvider.wallet;
  225. const playerTwo = anchor.web3.Keypair.generate();
  226. await program.methods
  227. .setupGame(playerTwo.publicKey)
  228. .accounts({
  229. game: gameKeypair.publicKey,
  230. playerOne: playerOne.publicKey,
  231. })
  232. .signers([gameKeypair])
  233. .rpc();
  234. let gameState = await program.account.game.fetch(gameKeypair.publicKey);
  235. expect(gameState.turn).to.equal(1);
  236. expect(gameState.players)
  237. .to
  238. .eql([playerOne.publicKey, playerTwo.publicKey]);
  239. expect(gameState.state).to.eql({ active: {} });
  240. expect(gameState.board)
  241. .to
  242. .eql([[null,null,null],[null,null,null],[null,null,null]]);
  243. await play(
  244. program,
  245. gameKeypair.publicKey,
  246. playerOne,
  247. {row: 0, column: 0},
  248. 2,
  249. { active: {}, },
  250. [
  251. [{x:{}},null,null],
  252. [null,null,null],
  253. [null,null,null]
  254. ]
  255. );
  256. await play(
  257. program,
  258. gameKeypair.publicKey,
  259. playerTwo,
  260. {row: 1, column: 1},
  261. 3,
  262. { active: {}, },
  263. [
  264. [{x:{}},null,null],
  265. [null,{o:{}},null],
  266. [null,null,null]
  267. ]
  268. );
  269. await play(
  270. program,
  271. gameKeypair.publicKey,
  272. playerOne,
  273. {row: 2, column: 0},
  274. 4,
  275. { active: {}, },
  276. [
  277. [{x:{}},null,null],
  278. [null,{o:{}},null],
  279. [{x:{}},null,null]
  280. ]
  281. );
  282. await play(
  283. program,
  284. gameKeypair.publicKey,
  285. playerTwo,
  286. {row: 1, column: 0},
  287. 5,
  288. { active: {}, },
  289. [
  290. [{x:{}},null,null],
  291. [{o:{}},{o:{}},null],
  292. [{x:{}},null,null]
  293. ]
  294. );
  295. await play(
  296. program,
  297. gameKeypair.publicKey,
  298. playerOne,
  299. {row: 1, column: 2},
  300. 6,
  301. { active: {}, },
  302. [
  303. [{x:{}},null,null],
  304. [{o:{}},{o:{}},{x:{}}],
  305. [{x:{}},null,null]
  306. ]
  307. );
  308. await play(
  309. program,
  310. gameKeypair.publicKey,
  311. playerTwo,
  312. {row: 0, column: 1},
  313. 7,
  314. { active: {}, },
  315. [
  316. [{x:{}},{o:{}},null],
  317. [{o:{}},{o:{}},{x:{}}],
  318. [{x:{}},null,null]
  319. ]
  320. );
  321. await play(
  322. program,
  323. gameKeypair.publicKey,
  324. playerOne,
  325. {row: 2, column: 1},
  326. 8,
  327. { active: {}, },
  328. [
  329. [{x:{}},{o:{}},null],
  330. [{o:{}},{o:{}},{x:{}}],
  331. [{x:{}},{x:{}},null]
  332. ]
  333. );
  334. await play(
  335. program,
  336. gameKeypair.publicKey,
  337. playerTwo,
  338. {row: 2, column: 2},
  339. 9,
  340. { active: {}, },
  341. [
  342. [{x:{}},{o:{}},null],
  343. [{o:{}},{o:{}},{x:{}}],
  344. [{x:{}},{x:{}},{o:{}}]
  345. ]
  346. );
  347. await play(
  348. program,
  349. gameKeypair.publicKey,
  350. playerOne,
  351. {row: 0, column: 2},
  352. 9,
  353. { tie: {}, },
  354. [
  355. [{x:{}},{o:{}},{x:{}}],
  356. [{o:{}},{o:{}},{x:{}}],
  357. [{x:{}},{x:{}},{o:{}}]
  358. ]
  359. );
  360. })
  361. });