tic-tac-toe.ts 9.2 KB

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