command-line chess
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

305 lines
7.4 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. #include <stdint.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <locale.h>
  5. enum square_states {
  6. PAWN = 0x1,
  7. KNIGHT = 0x2,
  8. BISHOP = 0x4,
  9. ROOK = 0x8,
  10. QUEEN = 0x10,
  11. KING = 0x20,
  12. LIGHT = 0x40,
  13. DARK = 0x80
  14. };
  15. typedef struct {
  16. uint8_t flags;
  17. } square;
  18. static square *board[8][8] = {
  19. { /* A */
  20. &(square) { ROOK | LIGHT },
  21. &(square) { PAWN | LIGHT },
  22. NULL,
  23. NULL,
  24. NULL,
  25. NULL,
  26. &(square) { PAWN | DARK },
  27. &(square) { ROOK | DARK }
  28. },
  29. { /* B */
  30. &(square) { KNIGHT | LIGHT },
  31. &(square) { PAWN | LIGHT },
  32. NULL,
  33. NULL,
  34. NULL,
  35. NULL,
  36. &(square) { PAWN | DARK },
  37. &(square) { KNIGHT | DARK }
  38. },
  39. { /* C */
  40. &(square) { BISHOP | LIGHT },
  41. &(square) { PAWN | LIGHT },
  42. NULL,
  43. NULL,
  44. NULL,
  45. NULL,
  46. &(square) { PAWN | DARK },
  47. &(square) { BISHOP | DARK }
  48. },
  49. { /* D */
  50. &(square) { QUEEN | LIGHT },
  51. &(square) { PAWN | LIGHT },
  52. NULL,
  53. NULL,
  54. NULL,
  55. NULL,
  56. &(square) { PAWN | DARK },
  57. &(square) { QUEEN | DARK }
  58. },
  59. { /* E */
  60. &(square) { KING | LIGHT },
  61. &(square) { PAWN | LIGHT },
  62. NULL,
  63. NULL,
  64. NULL,
  65. NULL,
  66. &(square) { PAWN | DARK },
  67. &(square) { KING | DARK }
  68. },
  69. { /* F */
  70. &(square) { BISHOP | LIGHT },
  71. &(square) { PAWN | LIGHT },
  72. NULL,
  73. NULL,
  74. NULL,
  75. NULL,
  76. &(square) { PAWN | DARK },
  77. &(square) { BISHOP | DARK }
  78. },
  79. { /* G */
  80. &(square) { KNIGHT | LIGHT },
  81. &(square) { PAWN | LIGHT },
  82. NULL,
  83. NULL,
  84. NULL,
  85. NULL,
  86. &(square) { PAWN | DARK },
  87. &(square) { KNIGHT | DARK }
  88. },
  89. { /* H */
  90. &(square) { ROOK | LIGHT },
  91. &(square) { PAWN | LIGHT },
  92. NULL,
  93. NULL,
  94. NULL,
  95. NULL,
  96. &(square) { PAWN | DARK },
  97. &(square) { ROOK | DARK }
  98. }
  99. };
  100. void draw_board_unicode() {
  101. for (int i = 0; i < 8; i++) {
  102. printf("\t%c ", '0' + 8 - i);
  103. for (int j = 0; j < 8; j++) {
  104. // reset the color settings
  105. printf("\033[0m");
  106. square* s = board[j][7 - i];
  107. // color the background of the squares
  108. if ((i + j + 1) % 2 == 0) {
  109. printf("\033[0;100m");
  110. } else {
  111. printf("\033[0;107m");
  112. }
  113. // fill in the blanks
  114. if (s == NULL) {
  115. printf(" ");
  116. continue;
  117. }
  118. // else, print the unicode character for the piece on that square
  119. switch (s->flags) {
  120. case PAWN | LIGHT: printf("%lc ", 0x2659); break;
  121. case ROOK | LIGHT: printf("%lc ", 0x2656); break;
  122. case KNIGHT | LIGHT: printf("%lc ", 0x2658); break;
  123. case BISHOP | LIGHT: printf("%lc ", 0x2657); break;
  124. case QUEEN | LIGHT: printf("%lc ", 0x2655); break;
  125. case KING | LIGHT: printf("%lc ", 0x2654); break;
  126. case PAWN | DARK: printf("%lc ", 0x265F); break;
  127. case ROOK | DARK: printf("%lc ", 0x265C); break;
  128. case KNIGHT | DARK: printf("%lc ", 0x265E); break;
  129. case BISHOP | DARK: printf("%lc ", 0x265D); break;
  130. case QUEEN | DARK: printf("%lc ", 0x265B); break;
  131. case KING | DARK: printf("%lc ", 0x265A); break;
  132. }
  133. }
  134. printf("\033[0m\n");
  135. }
  136. printf("\t a b c d e f g h\n");
  137. }
  138. void print_square(square* s) {
  139. printf("square: %d\n", s ? s->flags : 0);
  140. }
  141. // all pieces can't move to a square if another piece of the same color occupies it
  142. int validate_move(int x1, int y1, int x2, int y2) {
  143. if (board[x2][y2] && ((board[x1][y1]->flags | LIGHT) | DARK) == (((board[x2][y2]->flags | LIGHT) | DARK))) {
  144. return 0;
  145. }
  146. return 1;
  147. }
  148. int validate_pawn_move(int x1, int y1, int x2, int y2) {
  149. return validate_move(x1, y1, x2, y2);
  150. }
  151. int validate_knight_move(int x1, int y1, int x2, int y2) {
  152. return validate_move(x1, y1, x2, y2);
  153. }
  154. int validate_bishop_move(int x1, int y1, int x2, int y2) {
  155. if (+(x2 - x1) != +(y2 - y1)) {
  156. return 0;
  157. }
  158. return validate_move(x1, y1, x2, y2);
  159. }
  160. int validate_rook_move(int x1, int y1, int x2, int y2) {
  161. return validate_move(x1, y1, x2, y2);
  162. }
  163. int validate_queen_move(int x1, int y1, int x2, int y2) {
  164. return validate_move(x1, y1, x2, y2);
  165. }
  166. int validate_king_move(int x1, int y1, int x2, int y2) {
  167. return validate_move(x1, y1, x2, y2);
  168. }
  169. int file_to_board_index(char c) {
  170. if (c >= 'a') {
  171. return c - 'a';
  172. } else {
  173. return c - 'A';
  174. }
  175. }
  176. int rank_to_board_index(char c) {
  177. return c - '0' - 1;
  178. }
  179. typedef int (*piece_move_validator)(int, int, int, int);
  180. int turn = 1;
  181. int handle_move(char in[6]) {
  182. int ix1 = file_to_board_index(in[0]);
  183. int iy1 = rank_to_board_index(in[1]);
  184. int ix2 = file_to_board_index(in[2]);
  185. int iy2 = rank_to_board_index(in[3]);
  186. square* source = board[ix1][iy1];
  187. if (source == NULL) {
  188. return -1;
  189. }
  190. if ((turn == 0) && (source->flags & DARK) == 0) {
  191. return -3;
  192. } else if ((turn == 1) && (source->flags & LIGHT) == 0) {
  193. return -4;
  194. }
  195. piece_move_validator validate = NULL;
  196. switch ((source->flags & ~LIGHT) & ~DARK) {
  197. case PAWN: validate = validate_pawn_move; break;
  198. case KNIGHT: validate = validate_knight_move; break;
  199. case BISHOP: validate = validate_bishop_move; break;
  200. case ROOK: validate = validate_rook_move; break;
  201. case QUEEN: validate = validate_queen_move; break;
  202. case KING: validate = validate_king_move; break;
  203. }
  204. if (!validate(ix1, iy1, ix2, iy2)) {
  205. return -2;
  206. }
  207. board[ix2][iy2] = source;
  208. board[ix1][iy1] = NULL;
  209. return 0;
  210. }
  211. int validate_coordinate_notation(char in[4]) {
  212. for (int i = 0; i < 2; i++) {
  213. switch (in[i * 2]) {
  214. default: return -1;
  215. case 'a': case 'A':
  216. case 'b': case 'B':
  217. case 'c': case 'C':
  218. case 'd': case 'D':
  219. case 'e': case 'E':
  220. case 'f': case 'F':
  221. case 'g': case 'G':
  222. case 'h': case 'H': break;
  223. }
  224. switch (in[i * 2 + 1]) {
  225. default: return -2;
  226. case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break;
  227. }
  228. }
  229. return 0;
  230. }
  231. int main(void) {
  232. setlocale(LC_CTYPE, "");
  233. char in[6];
  234. do {
  235. printf("\n\n");
  236. draw_board_unicode();
  237. printf("\n\tmake moves by typing them in non-hyphenated coordinate notation ('e2e4', for example)\n\t> \033[5m%lc%c\033[0m", 0x2588, 0x8);
  238. if (fgets(in, 6, stdin) == NULL) {
  239. return 1;
  240. }
  241. if (validate_coordinate_notation(in) != 0) {
  242. printf("\tinvalid coordinate notation.\n"); fflush(stdin); continue;
  243. }
  244. switch (handle_move(in)) {
  245. case -1: printf("\tthere isn't a piece on that square!\n"); continue;
  246. case -2: printf("\tinvalid move\n"); continue;
  247. case -3: case -4: printf("\tnot your turn."); continue;
  248. default: turn = !turn; break;
  249. }
  250. } while (1);
  251. return 0;
  252. }