chess.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. /**
  2. Simple chess for SAF, using smallchesslib.
  3. by drummyfish, released under CC0 1.0, public domain
  4. */
  5. #define SIMPLE 0 // simpler version with 1b graphics
  6. #define AI_COUNTDOWN 20 /* If -1, AI against AI will require pressing A to make
  7. another move. If >= 0, AI vs AI will wait this
  8. number of frames before making another move. This is
  9. because AI takes long to compute moves and delay
  10. SAF frames greatly, freezing rendering. */
  11. #define MAX_AI_LEVEL 5
  12. #ifdef SAF_PLATFORM_ARDUBOY
  13. #define SIMPLE 1
  14. #endif
  15. #if defined(SAF_PLATFORM_ARDUBOY)
  16. #define MAX_AI_LEVEL 2
  17. #define AI_COUNTDOWN -1
  18. #elif defined(SAF_PLATFORM_POKITTO) || defined(SAF_PLATFORM_GAMEBUINO_META) || defined(SAF_PLATFORM_RINGO) || defined(SAF_PLATFORM_ESPBOY) || defined(SAF_PLATFORM_NIBBLE)
  19. #define MAX_AI_LEVEL 3
  20. #define AI_COUNTDOWN -1
  21. #define SCL_RECORD_MAX_LENGTH 128
  22. #endif
  23. #if defined(SAF_PLATFORM_NIBBLE) || defined(SAF_PLATFORM_ESPBOY)
  24. #define SCL_CALL_WDT_RESET 1
  25. #endif
  26. #define SAF_SETTING_ENABLE_SAVES 0
  27. #define SAF_PROGRAM_NAME "Chess"
  28. #define SAF_SETTING_FASTER_1BIT 2
  29. #define SAF_SETTING_ENABLE_SOUND 1
  30. #define SAF_SETTING_ENABLE_SAVES 0
  31. #if SIMPLE
  32. #define SCL_RECORD_MAX_LENGTH 1
  33. #endif
  34. #define SCL_EVALUATION_FUNCTION SCL_boardEvaluateStatic
  35. #define SCL_960_CASTLING 1
  36. #include "../saf.h"
  37. #include "smallchesslib.h"
  38. #define STATE_MENU 0
  39. #define STATE_PLAYING 1
  40. #define STATE_END 255
  41. #define MENU_ITEMS 5
  42. #define GAME_TYPES 4
  43. #define SQUARE_LIGHT 0xb6
  44. #define SQUARE_DARK 0x92
  45. #define SQUARE_PIECE 0x7a
  46. #define SQUARE_MOVE 0xd1
  47. #define CURSOR_COLOR 0xa1
  48. #if SIMPLE
  49. #undef CURSOR_COLOR
  50. #define CURSOR_COLOR SAF_COLOR_BLACK
  51. #endif
  52. uint8_t state = STATE_MENU;
  53. uint8_t menuItem = 0;
  54. uint8_t playerW = 0;
  55. uint8_t playerB = 0;
  56. uint8_t variant = 0;
  57. uint8_t soundOn = 1;
  58. uint8_t visualSelectedSquare = 0;
  59. uint8_t boardFlipped = 0;
  60. int8_t previousMove[2];
  61. int8_t selectedPiece = -1;
  62. int8_t aiCountDown = AI_COUNTDOWN;
  63. SCL_Game game;
  64. SCL_SquareSet allowedSquares;
  65. #define STR(s) s
  66. #if !SIMPLE
  67. SCL_Board variantStartState;
  68. #endif
  69. const
  70. #if SAF_PLATFORM_HARWARD
  71. PROGMEM
  72. #endif
  73. uint8_t images[] =
  74. {
  75. // PIECES (0):
  76. 0xff,0xfc,0xed,0xb5,0xe0,0x00, // 0: white pawn
  77. 0x01,0xe3,0x2d,0xb5,0xe0,0x00, // 1: white rook
  78. 0xc6,0xe7,0x82,0xda,0xe8,0x00, // 2: white knight
  79. 0xce,0xd7,0x8c,0xb5,0xe0,0x00, // 3: white bishop
  80. 0xb5,0x27,0xad,0xb5,0xe0,0x00, // 4: white queen
  81. 0xfe,0x17,0x9e,0xb6,0xd0,0x00, // 5: white king
  82. 0xff,0xfc,0xe1,0x84,0x00,0x00, // 6: black pawn
  83. 0x48,0x00,0x21,0x86,0x10,0x00, // 7: black rook
  84. 0xce,0x10,0x41,0xc6,0x00,0x00, // 8: black knight
  85. 0xce,0x10,0x21,0xce,0x10,0x00, // 9: black bishop
  86. 0xb4,0x00,0x21,0x84,0x00,0x00, // 10: black queen
  87. 0xfe,0x10,0x00,0x86,0x10,0x00, // 11: black king
  88. // ICONS (12):
  89. 0xcf,0x30,0x12,0xce,0x13,0x00, // 12: human
  90. 0xfc,0x05,0x0a,0x50,0xa0,0x00, // 13: game
  91. 0x84,0xcf,0x39,0xcf,0xfc,0xc0, // 14: question mark
  92. 0xdf,0x3c,0x75,0xdc,0x71,0xc0, // 15: note
  93. 0xdf,0x30,0x40,0x07,0x3d,0xc0, // 16: arrow
  94. #if !SIMPLE
  95. // MASKS (17):
  96. 0x00,0x03,0x1e,0x7b,0xff,0xc0,
  97. 0xff,0xff,0xde,0x7b,0xff,0xc0,
  98. 0x39,0xff,0xff,0x3d,0xf7,0xc0,
  99. 0x31,0xef,0xff,0x7b,0xff,0xc0,
  100. 0x4b,0xff,0xde,0x7b,0xff,0xc0,
  101. 0x01,0xef,0xff,0x79,0xef,0xc0,
  102. 0x00,0x03,0x1e,0x7b,0xff,0xc0,
  103. 0xb7,0xff,0xde,0x79,0xef,0xc0,
  104. 0x31,0xef,0xbe,0x39,0xff,0xc0,
  105. 0x31,0xef,0xde,0x31,0xef,0xc0,
  106. 0x4b,0xff,0xde,0x7b,0xff,0xc0,
  107. 0x01,0xef,0xff,0x79,0xef,0xc0
  108. #endif
  109. };
  110. uint8_t image[8];
  111. #if !SIMPLE
  112. uint8_t imageMask[8];
  113. #endif
  114. void drawImage(uint8_t index, uint8_t x, uint8_t y)
  115. {
  116. const uint8_t *byte = images + index * 6;
  117. for (uint8_t i = 2; i < 8; ++i, ++byte)
  118. #if SAF_PLATFORM_HARWARD
  119. image[i] = pgm_read_byte(byte);
  120. #else
  121. image[i] = *byte;
  122. #endif
  123. #if !SIMPLE
  124. if (index <= 11)
  125. {
  126. byte = images + (index + 17) * 6;
  127. for (uint8_t i = 2; i < 8; ++i, ++byte)
  128. #if SAF_PLATFORM_HARWARD
  129. imageMask[i] = pgm_read_byte(byte);
  130. #else
  131. imageMask[i] = *byte;
  132. #endif
  133. byte = imageMask;
  134. }
  135. else
  136. byte = 0;
  137. #else
  138. byte = 0;
  139. #endif
  140. SAF_drawImage1Bit(image,x,y,byte,SAF_COLOR_WHITE,SAF_COLOR_BLACK,SAF_TRANSFORM_NONE);
  141. }
  142. void drawBoard(void)
  143. {
  144. #if SIMPLE
  145. SAF_clearScreen(SAF_COLOR_WHITE);
  146. #endif
  147. const char *c = game.board;
  148. uint8_t x = 0, y = boardFlipped ? 0 : 64 - 8;
  149. uint8_t square = 0;
  150. for (uint8_t i = 0; i < 8; ++i)
  151. {
  152. x = boardFlipped ? 64 - 8 : 0;
  153. for (uint8_t j = 0; j < 8; ++j)
  154. {
  155. char s = *c;
  156. uint8_t squareColor = SQUARE_LIGHT;
  157. #if SIMPLE
  158. SAF_drawPixel(x + 4, y + 4,SAF_COLOR_BLACK);
  159. #else
  160. if (square == selectedPiece || SCL_squareSetContains(allowedSquares,square))
  161. squareColor = SQUARE_PIECE;
  162. else if (square == previousMove[0] || square == previousMove[1])
  163. squareColor = SQUARE_MOVE;
  164. else if (i % 2 == j % 2)
  165. squareColor = SQUARE_DARK;
  166. SAF_drawRect(x,y,8,8,squareColor,1);
  167. #endif
  168. if (s != '.')
  169. {
  170. uint8_t image = 0;
  171. switch (s)
  172. {
  173. case 'R': image = 1; break;
  174. case 'N': image = 2; break;
  175. case 'B': image = 3; break;
  176. case 'Q': image = 4; break;
  177. case 'K': image = 5; break;
  178. case 'p': image = 6; break;
  179. case 'r': image = 7; break;
  180. case 'n': image = 8; break;
  181. case 'b': image = 9; break;
  182. case 'q': image = 10; break;
  183. case 'k': image = 11; break;
  184. default: break;
  185. }
  186. #if SIMPLE
  187. if (square != selectedPiece || SAF_frame() & 0x04)
  188. #endif
  189. drawImage(image,x + 1,y);
  190. }
  191. c++;
  192. x += boardFlipped ? -8 : 8;
  193. square++;
  194. }
  195. y += boardFlipped ? 8 : -8;
  196. }
  197. if (state == STATE_PLAYING &&(
  198. (!playerW && SCL_boardWhitesTurn(game.board)) ||
  199. (!playerB && !SCL_boardWhitesTurn(game.board))))
  200. {
  201. // cursor:
  202. x = (visualSelectedSquare % 8) * 8;
  203. y = (visualSelectedSquare / 8) * 8;
  204. SAF_drawRect(x,y,8,8,CURSOR_COLOR,0);
  205. }
  206. if (game.state != SCL_GAME_STATE_PLAYING && (SAF_frame() & 0x08))
  207. {
  208. SAF_drawRect(32 - 10,32 - 10,20,20,SAF_COLOR_WHITE,1);
  209. #if SIMPLE
  210. SAF_drawRect(32 - 11,32 - 11,22,22,SAF_COLOR_BLACK,0);
  211. #endif
  212. char resultStr[4];
  213. resultStr[0] = '1';
  214. resultStr[1] = '-';
  215. resultStr[2] = '1';
  216. resultStr[3] = 0;
  217. if (game.state == SCL_GAME_STATE_WHITE_WIN)
  218. resultStr[2] = '0';
  219. else if (game.state == SCL_GAME_STATE_BLACK_WIN)
  220. resultStr[0] = '0';
  221. else
  222. {
  223. resultStr[1] = '/';
  224. resultStr[2] = '2';
  225. }
  226. SAF_drawText(resultStr,24,30,SAF_COLOR_BLACK,1);
  227. }
  228. }
  229. void drawMenu(void)
  230. {
  231. SAF_clearScreen(SAF_COLOR_WHITE);
  232. for (uint8_t j = 0; j < 12; ++j)
  233. for (uint8_t i = 0; i < SAF_SCREEN_WIDTH; ++i)
  234. if ((j / 4) % 2 == (i / 4) % 2)
  235. SAF_drawPixel(i,j,SAF_COLOR_BLACK);
  236. #define Y0 15
  237. #define Y 9
  238. #define X 13
  239. #define X2 24
  240. uint8_t x = X, y = Y0;
  241. drawImage(2,x,y);
  242. x = X2;
  243. if (playerW == 0)
  244. drawImage(12,x,y);
  245. else
  246. for (uint8_t i = 0; i < playerW; ++i)
  247. drawImage(0,x + i * 8,y);
  248. x = X;
  249. y += Y;
  250. drawImage(8,x,y);
  251. x = X2;
  252. if (playerB == 0)
  253. drawImage(12,x,y);
  254. else
  255. for (uint8_t i = 0; i < playerB; ++i)
  256. drawImage(6,x + i * 8,y);
  257. x = X;
  258. y += Y;
  259. drawImage(13,x,y);
  260. x = X2;
  261. uint8_t variantImgaes[] = {1,14,0,2};
  262. drawImage(variantImgaes[variant],x,y);
  263. x = X;
  264. y += Y;
  265. drawImage(15,x,y);
  266. if (!soundOn)
  267. for (uint8_t i = 0; i < 5; ++i)
  268. SAF_drawPixel(x + i,y + 2 + i,SAF_COLOR_BLACK);
  269. y += Y;
  270. drawImage(16,x,y);
  271. y = menuItem * Y + 1 + Y0;
  272. x -= 6;
  273. SAF_drawPixel(x,y,SAF_COLOR_BLACK);
  274. x++; y++;
  275. SAF_drawPixel(x,y,SAF_COLOR_BLACK);
  276. x++; y++;
  277. SAF_drawPixel(x,y,SAF_COLOR_BLACK);
  278. x--; y++;
  279. SAF_drawPixel(x,y,SAF_COLOR_BLACK);
  280. x--; y++;
  281. SAF_drawPixel(x,y,SAF_COLOR_BLACK);
  282. #undef X
  283. #undef Y
  284. #undef Y0
  285. #undef X2
  286. }
  287. uint8_t button(uint8_t button)
  288. {
  289. return SAF_buttonPressed(button) == 1 || SAF_buttonPressed(button) > 12;
  290. }
  291. void handleMenuItemPlayer(uint8_t *player)
  292. {
  293. if (button(SAF_BUTTON_RIGHT))
  294. *player += *player < MAX_AI_LEVEL ? 1 : 0;
  295. else if (button(SAF_BUTTON_LEFT))
  296. *player -= *player > 0 ? 1 : 0;
  297. }
  298. void unselectPiece(void)
  299. {
  300. selectedPiece = -1;
  301. SCL_squareSetClear(allowedSquares);
  302. }
  303. void trySelect(uint8_t square)
  304. {
  305. char c = game.board[square];
  306. if (c != '.' && SCL_pieceIsWhite(c) == SCL_boardWhitesTurn(game.board))
  307. {
  308. selectedPiece = square;
  309. SCL_boardGetMoves(game.board,square,allowedSquares);
  310. }
  311. else
  312. unselectPiece();
  313. }
  314. void initGame(void)
  315. {
  316. #if SIMPLE
  317. SCL_gameInit(&game,NULL);
  318. #else
  319. switch (variant)
  320. {
  321. case 1:
  322. SCL_boardInit960(variantStartState,SAF_frame() % 960);
  323. break;
  324. case 2:
  325. SCL_boardFromFEN(variantStartState,SCL_FEN_HORDE);
  326. variantStartState[59] = 'k';
  327. break;
  328. case 3:
  329. SCL_boardFromFEN(variantStartState,SCL_FEN_KNIGHTS);
  330. break;
  331. default:
  332. break;
  333. }
  334. SCL_gameInit(&game,variant == 0 ? NULL : variantStartState);
  335. #endif
  336. SCL_randomSimpleSeed(SAF_frame() % 256);
  337. previousMove[0] = -1;
  338. previousMove[1] = -1;
  339. visualSelectedSquare = SCL_BOARD_SQUARES / 2;
  340. unselectPiece();
  341. }
  342. void getAIMove(uint8_t *s0, uint8_t *s1, char *p)
  343. {
  344. uint8_t level = SCL_boardWhitesTurn(game.board) ? playerW : playerB;
  345. uint8_t depth = level;
  346. uint8_t extraDepth = level;
  347. uint8_t randomness = game.ply < 2 ? 1 : 0;
  348. switch (level)
  349. {
  350. case 1: depth = 1; extraDepth = 1; break;
  351. case 2: depth = 2; extraDepth = 1; break;
  352. case 3: depth = 2; extraDepth = 2; break;
  353. case 4: depth = 3; extraDepth = 2; break;
  354. case 5: depth = 3; extraDepth = 3; break;
  355. default: break;
  356. }
  357. SCL_getAIMove(
  358. game.board,depth,extraDepth,0,SCL_boardEvaluateStatic,SCL_randomSimple,
  359. randomness,0,0,s0,s1,p);
  360. }
  361. void orientBoard(void)
  362. {
  363. boardFlipped = !playerB &&
  364. (
  365. playerW ||
  366. (state == STATE_PLAYING && !SCL_boardWhitesTurn(game.board))
  367. );
  368. }
  369. void playSound(uint8_t sound)
  370. {
  371. #ifndef SAF_PLATFORM_ARDUBOY
  372. // sounds are messed up on arduboy due to AI delaying frames
  373. if (soundOn)
  374. SAF_playSound(sound);
  375. #endif
  376. }
  377. void SAF_init(void)
  378. {
  379. image[0] = 6;
  380. image[1] = 7;
  381. #if !SIMPLE
  382. imageMask[0] = 6;
  383. imageMask[1] = 7;
  384. #endif
  385. initGame();
  386. }
  387. uint8_t SAF_loop(void)
  388. {
  389. orientBoard();
  390. switch (state)
  391. {
  392. case STATE_MENU:
  393. {
  394. if (button(SAF_BUTTON_UP))
  395. menuItem -= menuItem > 0 ? 1 : 0;
  396. else if (button(SAF_BUTTON_DOWN))
  397. menuItem += (menuItem < MENU_ITEMS - 1) ? 1 : 0;
  398. switch (menuItem)
  399. {
  400. case 0: handleMenuItemPlayer(&playerW); break;
  401. case 1: handleMenuItemPlayer(&playerB); break;
  402. #if !SIMPLE
  403. case 2:
  404. if (button(SAF_BUTTON_RIGHT))
  405. variant = (variant + 1) % GAME_TYPES;
  406. else if (button(SAF_BUTTON_LEFT))
  407. variant = variant > 0 ? (variant - 1) : (GAME_TYPES - 1);
  408. break;
  409. #endif
  410. case 3:
  411. if (button(SAF_BUTTON_RIGHT) || button(SAF_BUTTON_LEFT) ||
  412. button(SAF_BUTTON_A))
  413. soundOn = !soundOn;
  414. break;
  415. case 4:
  416. if (SAF_buttonJustPressed(SAF_BUTTON_A))
  417. {
  418. initGame();
  419. state = STATE_PLAYING;
  420. }
  421. break;
  422. default: break;
  423. }
  424. if (SAF_buttonJustPressed(SAF_BUTTON_B))
  425. state = STATE_PLAYING;
  426. drawMenu();
  427. break;
  428. }
  429. case STATE_PLAYING:
  430. {
  431. uint8_t moveFrom = 0, moveTo = 0;
  432. char movePromote = 'q';
  433. if (aiCountDown < 0)
  434. {
  435. if (SAF_buttonJustPressed(SAF_BUTTON_A))
  436. aiCountDown = 0;
  437. }
  438. else if (aiCountDown > 0)
  439. aiCountDown--;
  440. if ((playerW && SCL_boardWhitesTurn(game.board)) ||
  441. (playerB && !SCL_boardWhitesTurn(game.board)))
  442. {
  443. if (playerW == 0 || playerB == 0 || aiCountDown == 0)
  444. {
  445. getAIMove(&moveFrom,&moveTo,&movePromote);
  446. aiCountDown = AI_COUNTDOWN;
  447. }
  448. }
  449. else
  450. {
  451. if (button(SAF_BUTTON_RIGHT))
  452. visualSelectedSquare += visualSelectedSquare % 8 < 7 ? 1 : 0;
  453. else if (button(SAF_BUTTON_LEFT))
  454. visualSelectedSquare -= visualSelectedSquare % 8 > 0 ? 1 : 0;
  455. else if (button(SAF_BUTTON_UP))
  456. visualSelectedSquare -= visualSelectedSquare > 7 ? 8 : 0;
  457. else if (button(SAF_BUTTON_DOWN))
  458. visualSelectedSquare += visualSelectedSquare < 56 ? 8 : 0;
  459. if (SAF_buttonJustPressed(SAF_BUTTON_A))
  460. {
  461. uint8_t selectedSquare = boardFlipped ?
  462. ((visualSelectedSquare / 8) * 8 + 7 - visualSelectedSquare % 8) :
  463. ((7 - visualSelectedSquare / 8) * 8 + visualSelectedSquare % 8);
  464. if (selectedPiece < 0)
  465. trySelect(selectedSquare);
  466. else
  467. {
  468. if (SCL_squareSetContains(allowedSquares,selectedSquare))
  469. {
  470. moveFrom = selectedPiece;
  471. moveTo = selectedSquare;
  472. unselectPiece();
  473. }
  474. else if (selectedSquare == selectedPiece)
  475. unselectPiece();
  476. else
  477. trySelect(selectedSquare);
  478. }
  479. }
  480. else if (SAF_buttonJustPressed(SAF_BUTTON_C))
  481. {
  482. SCL_gameUndoMove(&game);
  483. if (playerW || playerB) // against AI actually undo 2 plys
  484. SCL_gameUndoMove(&game);
  485. previousMove[0] = -1;
  486. previousMove[1] = -1;
  487. unselectPiece();
  488. }
  489. }
  490. if (moveFrom != 0 || moveTo != 0)
  491. {
  492. uint8_t capture = game.board[moveTo] != '.';
  493. SCL_gameMakeMove(&game,moveFrom,moveTo,movePromote);
  494. previousMove[0] = moveFrom;
  495. previousMove[1] = moveTo;
  496. if (game.state != SCL_GAME_STATE_PLAYING)
  497. {
  498. state = STATE_END;
  499. playSound(SAF_SOUND_BOOM);
  500. previousMove[0] = -1;
  501. previousMove[1] = -1;
  502. unselectPiece();
  503. }
  504. else
  505. playSound(capture ? SAF_SOUND_CLICK : SAF_SOUND_BUMP);
  506. }
  507. if (SAF_buttonJustPressed(SAF_BUTTON_B))
  508. state = STATE_MENU;
  509. drawBoard();
  510. break;
  511. }
  512. case STATE_END:
  513. {
  514. if (SAF_buttonJustPressed(SAF_BUTTON_B))
  515. {
  516. initGame();
  517. state = STATE_MENU;
  518. }
  519. #if !SIMPLE
  520. else if (SAF_frame() % 10 == 0)
  521. {
  522. game.ply = (game.ply + 1) % (SCL_recordLength(game.record) + 5);
  523. if (variant == 0)
  524. SCL_boardInit(game.board);
  525. else
  526. SCL_boardCopy(variantStartState,game.board);
  527. uint16_t i = 0;
  528. while (i < game.ply && i < SCL_recordLength(game.record))
  529. {
  530. uint8_t s0, s1;
  531. char p;
  532. SCL_recordGetMove(game.record,i,&s0,&s1,&p);
  533. SCL_boardMakeMove(game.board,s0,s1,p);
  534. i++;
  535. }
  536. }
  537. #endif
  538. drawBoard();
  539. break;
  540. }
  541. }
  542. return 1;
  543. }