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.

323 lines
8.0 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
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. char* flags_to_string(int flags) {
  139. switch (flags) {
  140. case PAWN | LIGHT: return "white pawn";
  141. case ROOK | LIGHT: return "white rook";
  142. case KNIGHT | LIGHT: return "white knight";
  143. case BISHOP | LIGHT: return "white bishop";
  144. case QUEEN | LIGHT: return "white queen";
  145. case KING | LIGHT: return "white king";
  146. case PAWN | DARK: return "black pawn";
  147. case ROOK | DARK: return "black root";
  148. case KNIGHT | DARK: return "black knight";
  149. case BISHOP | DARK: return "black bishop";
  150. case QUEEN | DARK: return "black queen";
  151. case KING | DARK: return "black king";
  152. }
  153. }
  154. void print_square(int x, int y, square* s) {
  155. printf("%d,%d - %s", x, y, flags_to_string(s->flags));
  156. }
  157. // all pieces can't move to a square if another piece of the same color occupies it
  158. int validate_move(int x1, int y1, int x2, int y2) {
  159. if (board[x2][y2] && ((board[x1][y1]->flags | LIGHT) | DARK) == (((board[x2][y2]->flags | LIGHT) | DARK))) {
  160. return 0;
  161. }
  162. return 1;
  163. }
  164. int validate_pawn_move(int x1, int y1, int x2, int y2) {
  165. return validate_move(x1, y1, x2, y2);
  166. }
  167. int validate_knight_move(int x1, int y1, int x2, int y2) {
  168. return validate_move(x1, y1, x2, y2);
  169. }
  170. int validate_bishop_move(int x1, int y1, int x2, int y2) {
  171. if (+(x2 - x1) != +(y2 - y1)) {
  172. return 0;
  173. }
  174. return validate_move(x1, y1, x2, y2);
  175. }
  176. int validate_rook_move(int x1, int y1, int x2, int y2) {
  177. return validate_move(x1, y1, x2, y2);
  178. }
  179. int validate_queen_move(int x1, int y1, int x2, int y2) {
  180. return validate_move(x1, y1, x2, y2);
  181. }
  182. int validate_king_move(int x1, int y1, int x2, int y2) {
  183. return validate_move(x1, y1, x2, y2);
  184. }
  185. int file_to_board_index(char c) {
  186. if (c >= 'a') {
  187. return c - 'a';
  188. } else {
  189. return c - 'A';
  190. }
  191. }
  192. int rank_to_board_index(char c) {
  193. return c - '0' - 1;
  194. }
  195. typedef int (*piece_move_validator)(int, int, int, int);
  196. int turn = 1;
  197. int handle_move(char in[6]) {
  198. int ix1 = file_to_board_index(in[0]);
  199. int iy1 = rank_to_board_index(in[1]);
  200. int ix2 = file_to_board_index(in[2]);
  201. int iy2 = rank_to_board_index(in[3]);
  202. square* source = board[ix1][iy1];
  203. if (source == NULL) {
  204. return -1;
  205. }
  206. if ((turn == 0) && (source->flags & DARK) == 0) {
  207. return -3;
  208. } else if ((turn == 1) && (source->flags & LIGHT) == 0) {
  209. return -4;
  210. }
  211. piece_move_validator validate = NULL;
  212. switch ((source->flags & ~LIGHT) & ~DARK) {
  213. case PAWN: validate = validate_pawn_move; break;
  214. case KNIGHT: validate = validate_knight_move; break;
  215. case BISHOP: validate = validate_bishop_move; break;
  216. case ROOK: validate = validate_rook_move; break;
  217. case QUEEN: validate = validate_queen_move; break;
  218. case KING: validate = validate_king_move; break;
  219. }
  220. if (!validate(ix1, iy1, ix2, iy2)) {
  221. return -2;
  222. }
  223. board[ix2][iy2] = source;
  224. board[ix1][iy1] = NULL;
  225. return 0;
  226. }
  227. int validate_coordinate_notation(char in[4]) {
  228. for (int i = 0; i < 2; i++) {
  229. switch (in[i * 2]) {
  230. default: return -1;
  231. case 'a': case 'A':
  232. case 'b': case 'B':
  233. case 'c': case 'C':
  234. case 'd': case 'D':
  235. case 'e': case 'E':
  236. case 'f': case 'F':
  237. case 'g': case 'G':
  238. case 'h': case 'H': break;
  239. }
  240. switch (in[i * 2 + 1]) {
  241. default: return -2;
  242. case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break;
  243. }
  244. }
  245. return 0;
  246. }
  247. int main(void) {
  248. setlocale(LC_CTYPE, "");
  249. char in[6];
  250. do {
  251. printf("\n\n");
  252. draw_board_unicode();
  253. 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);
  254. if (fgets(in, 6, stdin) == NULL) {
  255. return 1;
  256. }
  257. if (validate_coordinate_notation(in) != 0) {
  258. printf("\tinvalid coordinate notation.\n"); fflush(stdin); continue;
  259. }
  260. switch (handle_move(in)) {
  261. case -1: printf("\tthere isn't a piece on that square!\n"); continue;
  262. case -2: printf("\tinvalid move\n"); continue;
  263. case -3: case -4: printf("\tnot your turn."); continue;
  264. default: turn = !turn; break;
  265. }
  266. } while (1);
  267. return 0;
  268. }