fen_to_ascii.c (5802B)
1 /* TODO: option to flip board? */ 2 3 #include <ctype.h> 4 #include <stdio.h> 5 #include <string.h> 6 7 static char board[8][8]; 8 static char highlight[8][8]; 9 10 static int side_to_move = 'w'; /* default: white to move */ 11 static int white_can_castle[2] = { 0, 0 }; /* allow king side, allow queen side */ 12 static int black_can_castle[2] = { 0, 0 }; /* allow king side, allow queen side */ 13 14 static const int showcoords = 1; /* config: show board coordinates? */ 15 16 int 17 isvalidsquare(int x, int y) 18 { 19 return !(x < 0 || x >= 8 || y < 0 || y >= 8); 20 } 21 22 /* place a piece, if possible */ 23 void 24 place(int piece, int x, int y) 25 { 26 if (!isvalidsquare(x, y)) 27 return; 28 29 board[y][x] = piece; 30 } 31 32 /* get piece, if possible */ 33 int 34 getpiece(int x, int y) 35 { 36 if (!isvalidsquare(x, y)) 37 return 0; 38 return board[y][x]; 39 } 40 41 int 42 squaretoxy(const char *s, int *x, int *y) 43 { 44 if (*s >= 'a' && *s <= 'h' && 45 *(s + 1) >= '1' && *(s + 1) <= '8') { 46 *x = *s - 'a'; 47 *y = '8' - *(s + 1); 48 return 1; 49 } 50 return 0; 51 } 52 53 void 54 highlightmove(int x1, int y1, int x2, int y2) 55 { 56 if (isvalidsquare(x1, y1)) 57 highlight[y1][x1] = 1; 58 59 if (isvalidsquare(x2, y2)) 60 highlight[y2][x2] = 1; 61 } 62 63 void 64 showpiece(int c) 65 { 66 const char *s = ""; 67 68 /* simple or use unicode character */ 69 #if 1 70 putchar(c); 71 return; 72 #endif 73 74 switch (c) { 75 case 'K': s = "♔"; break; 76 case 'Q': s = "♕"; break; 77 case 'R': s = "♖"; break; 78 case 'B': s = "♗"; break; 79 case 'N': s = "♘"; break; 80 case 'P': s = "♙"; break; 81 case 'k': s = "♚"; break; 82 case 'q': s = "♛"; break; 83 case 'r': s = "♜"; break; 84 case 'b': s = "♝"; break; 85 case 'n': s = "♞"; break; 86 case 'p': s = "♟"; break; 87 } 88 89 if (*s) 90 fputs(s, stdout); 91 } 92 93 void 94 showboardfen(void) 95 { 96 int x, y, piece, skip = 0; 97 98 for (y = 0; y < 8; y++) { 99 if (y > 0) 100 putchar('/'); 101 skip = 0; 102 for (x = 0; x < 8; x++) { 103 piece = getpiece(x, y); 104 if (piece) { 105 if (skip) 106 putchar(skip + '0'); 107 putchar(piece); 108 skip = 0; 109 } else { 110 skip++; 111 } 112 } 113 if (skip) 114 putchar(skip + '0'); 115 } 116 117 /* ? TODO: detect en passant, invalid castling etc? */ 118 } 119 120 /* show board */ 121 /* TODO: show fancier, unicode and background square color */ 122 /* TODO: use the output format similar to stockfish "d" command */ 123 void 124 showboard(void) 125 { 126 int x, y, piece; 127 128 printf("Board FEN:\n"); 129 showboardfen(); 130 printf("\n\n"); 131 132 for (y = 0; y < 8; y++) { 133 printf("+---+---+---+---+---+---+---+---+\n"); 134 for (x = 0; x < 8; x++) { 135 if (x == 0) 136 putchar('|'); 137 fputs(" ", stdout); 138 piece = getpiece(x, y); 139 if (piece) 140 showpiece(piece); 141 else 142 fputs(" ", stdout); 143 fputs(" ", stdout); 144 putchar('|'); 145 } 146 if (showcoords) { 147 putchar(' '); 148 putchar('8' - y); 149 } 150 putchar('\n'); 151 } 152 printf("+---+---+---+---+---+---+---+---+\n"); 153 if (showcoords) 154 printf(" a | b | c | d | e | f | g | h |\n"); 155 156 fputs("\n", stdout); 157 158 #if 0 159 if (side_to_move == 'w') { 160 fputs("White to move\n", stdout); 161 } else if (side_to_move == 'b') 162 fputs("Black to move\n", stdout); 163 164 if (white_can_castle[0]) 165 fputs("White can castle king side\n", stdout); 166 if (white_can_castle[1]) 167 fputs("White can castle queen side\n", stdout); 168 if (black_can_castle[0]) 169 fputs("Black can castle king side\n", stdout); 170 if (black_can_castle[1]) 171 fputs("Black can castle queen side\n", stdout); 172 #endif 173 } 174 175 int 176 main(int argc, char *argv[]) 177 { 178 const char *fen, *moves, *s; 179 int x, y, x2, y2, field, piece; 180 char pieces[] = "PNBRQKpnbrqk", square[3]; 181 182 if (argc != 3) { 183 fprintf(stderr, "usage: %s <FEN> <moves>\n", argv[0]); 184 return 1; 185 } 186 187 fen = argv[1]; 188 moves = argv[2]; 189 190 /* initial board state, FEN format */ 191 x = y = field = 0; 192 for (s = fen; *s; s++) { 193 /* next field, fields are: piece placement data, active color, 194 Castling availability, En passant target square, 195 Halfmove clock, Fullmove number */ 196 if (*s == ' ') { 197 field++; 198 continue; 199 } 200 201 switch (field) { 202 case 0: /* piece placement data */ 203 /* skip square */ 204 if (*s >= '1' && *s <= '9') { 205 x += (*s - '0'); 206 continue; 207 } 208 /* next rank */ 209 if (*s == '/') { 210 x = 0; 211 y++; 212 continue; 213 } 214 /* is piece? place it */ 215 if (strchr(pieces, *s)) 216 place(*s, x++, y); 217 break; 218 case 1: /* active color */ 219 if (*s == 'w' || *s == 'b') 220 side_to_move = *s; 221 break; 222 case 2: /* castling availability */ 223 if (*s == '-') { 224 white_can_castle[0] = 0; 225 white_can_castle[1] = 0; 226 black_can_castle[0] = 0; 227 black_can_castle[1] = 0; 228 } else if (*s == 'K') { 229 white_can_castle[0] = 1; 230 } else if (*s == 'Q') { 231 white_can_castle[1] = 1; 232 } else if (*s == 'k') { 233 black_can_castle[0] = 1; 234 } else if (*s == 'q') { 235 black_can_castle[1] = 1; 236 } 237 break; 238 case 3: /* TODO: en-passant square, rest of the fields */ 239 break; 240 } 241 /* TODO: parse which side to move, en-passant, etc */ 242 } 243 244 /* process moves */ 245 square[2] = '\0'; 246 x = y = x2 = y2 = -1; 247 for (s = moves; *s; s++) { 248 if (*s == ' ') 249 continue; 250 if ((*s >= 'a' && *s <= 'h') && 251 (*(s + 1) >= '1' && *(s + 1) <= '8') && 252 (*(s + 2) >= 'a' && *(s + 2) <= 'h') && 253 (*(s + 3) >= '1' && *(s + 3) <= '8')) { 254 square[0] = *s; 255 square[1] = *(s + 1); 256 257 s += 2; 258 squaretoxy(square, &x, &y); 259 piece = getpiece(x, y); 260 261 place(0, x, y); /* clear square */ 262 263 /* place piece at new location */ 264 square[0] = *s; 265 square[1] = *(s + 1); 266 squaretoxy(square, &x2, &y2); 267 place(piece, x2, y2); 268 s += 2; 269 270 /* possible promotion? (queen, knight, bishop) */ 271 if (*s == 'q' || *s == 'n' || *s == 'b') { 272 if (side_to_move == 'w') 273 piece = toupper(*s); 274 else 275 piece = *s; 276 place(piece, x2, y2); 277 s++; 278 } 279 280 /* switch which side it is to move */ 281 side_to_move = side_to_move == 'b' ? 'w' : 'b'; 282 } 283 } 284 /* highlight last move */ 285 highlightmove(x, y, x2, y2); 286 287 showboard(); 288 289 return 0; 290 }