fen_to_tty.c - chess-puzzles - chess puzzle book generator | |
git clone git://git.codemadness.org/chess-puzzles | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
fen_to_tty.c (7357B) | |
--- | |
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 que… | |
12 static int black_can_castle[2] = { 0, 0 }; /* allow king side, allow que… | |
13 | |
14 static const int showcoords = 1; /* config: show board coordinates? */ | |
15 | |
16 #define SETFGCOLOR(r,g,b) printf("\x1b[38;2;%d;%d;%dm", r, g, b) | |
17 #define SETBGCOLOR(r,g,b) printf("\x1b[48;2;%d;%d;%dm", r, g, b) | |
18 | |
19 int | |
20 isvalidsquare(int x, int y) | |
21 { | |
22 return !(x < 0 || x >= 8 || y < 0 || y >= 8); | |
23 } | |
24 | |
25 /* place a piece, if possible */ | |
26 void | |
27 place(int piece, int x, int y) | |
28 { | |
29 if (!isvalidsquare(x, y)) | |
30 return; | |
31 | |
32 board[y][x] = piece; | |
33 } | |
34 | |
35 /* get piece, if possible */ | |
36 int | |
37 getpiece(int x, int y) | |
38 { | |
39 if (!isvalidsquare(x, y)) | |
40 return 0; | |
41 return board[y][x]; | |
42 } | |
43 | |
44 int | |
45 squaretoxy(const char *s, int *x, int *y) | |
46 { | |
47 if (*s >= 'a' && *s <= 'h' && | |
48 *(s + 1) >= '1' && *(s + 1) <= '8') { | |
49 *x = *s - 'a'; | |
50 *y = '8' - *(s + 1); | |
51 return 1; | |
52 } | |
53 return 0; | |
54 } | |
55 | |
56 void | |
57 highlightmove(int x1, int y1, int x2, int y2) | |
58 { | |
59 if (isvalidsquare(x1, y1)) | |
60 highlight[y1][x1] = 1; | |
61 | |
62 if (isvalidsquare(x2, y2)) | |
63 highlight[y2][x2] = 1; | |
64 } | |
65 | |
66 void | |
67 showpiece(int c) | |
68 { | |
69 const char *s = ""; | |
70 | |
71 /* simple or use unicode character */ | |
72 #if 0 | |
73 putchar(c); | |
74 return; | |
75 #endif | |
76 | |
77 switch (c) { | |
78 case 'K': s = "♔"; break; | |
79 case 'Q': s = "♕"; break; | |
80 case 'R': s = "♖"; break; | |
81 case 'B': s = "♗"; break; | |
82 case 'N': s = "♘"; break; | |
83 case 'P': s = "♙"; break; | |
84 case 'k': s = "♚"; break; | |
85 case 'q': s = "♛"; break; | |
86 case 'r': s = "♜"; break; | |
87 case 'b': s = "♝"; break; | |
88 case 'n': s = "♞"; break; | |
89 case 'p': s = "♟"; break; | |
90 } | |
91 | |
92 if (*s) | |
93 fputs(s, stdout); | |
94 } | |
95 | |
96 void | |
97 showboardfen(void) | |
98 { | |
99 int x, y, piece, skip = 0; | |
100 | |
101 for (y = 0; y < 8; y++) { | |
102 if (y > 0) | |
103 putchar('/'); | |
104 skip = 0; | |
105 for (x = 0; x < 8; x++) { | |
106 piece = getpiece(x, y); | |
107 if (piece) { | |
108 if (skip) | |
109 putchar(skip + '0'); | |
110 putchar(piece); | |
111 skip = 0; | |
112 } else { | |
113 skip++; | |
114 } | |
115 } | |
116 if (skip) | |
117 putchar(skip + '0'); | |
118 } | |
119 | |
120 /* ? TODO: detect en passant, invalid castling etc? */ | |
121 } | |
122 | |
123 /* show board */ | |
124 void | |
125 showboard(void) | |
126 { | |
127 int *color; | |
128 int border[] = { 0x70, 0x49, 0x2d }; | |
129 int darksquare[] = { 0xb5, 0x88, 0x63 }; | |
130 int lightsquare[] = { 0xf0, 0xd9, 0xb5 }; | |
131 int darksquarehi[] = { 0xaa, 0xa2, 0x3a }; | |
132 int lightsquarehi[] = { 0xcd, 0xd2, 0x6a }; | |
133 int x, y, piece; | |
134 | |
135 printf("Board FEN:\n"); | |
136 showboardfen(); | |
137 printf("\n\n"); | |
138 | |
139 SETFGCOLOR(0x00, 0x00, 0x00); | |
140 | |
141 color = border; | |
142 SETBGCOLOR(color[0], color[1], color[2]); | |
143 SETFGCOLOR(0xff, 0xff, 0xff); | |
144 fputs(" ", stdout); | |
145 printf("\x1b[0m"); /* reset */ | |
146 SETFGCOLOR(0x00, 0x00, 0x00); | |
147 putchar('\n'); | |
148 | |
149 for (y = 0; y < 8; y++) { | |
150 color = border; | |
151 SETBGCOLOR(color[0], color[1], color[2]); | |
152 SETFGCOLOR(0xff, 0xff, 0xff); | |
153 fputs(" ", stdout); | |
154 | |
155 for (x = 0; x < 8; x++) { | |
156 if (x % 2 == 0) { | |
157 if (y % 2 == 0) | |
158 color = highlight[y][x] ? lights… | |
159 else | |
160 color = highlight[y][x] ? darksq… | |
161 } else { | |
162 if (y % 2 == 0) | |
163 color = highlight[y][x] ? darksq… | |
164 else | |
165 color = highlight[y][x] ? lights… | |
166 } | |
167 SETBGCOLOR(color[0], color[1], color[2]); | |
168 | |
169 fputs(" ", stdout); | |
170 piece = getpiece(x, y); | |
171 if (piece) { | |
172 if (piece >= 'A' && piece <= 'Z') | |
173 SETFGCOLOR(0xff, 0xff, 0xff); | |
174 else | |
175 SETFGCOLOR(0x00, 0x00, 0x00); | |
176 /* workaround: use black chess symbol, b… | |
177 is filled and better visible */ | |
178 showpiece(tolower(piece)); | |
179 } else { | |
180 fputs(" ", stdout); | |
181 } | |
182 fputs(" ", stdout); | |
183 } | |
184 printf("\x1b[0m"); /* reset */ | |
185 | |
186 color = border; | |
187 SETBGCOLOR(color[0], color[1], color[2]); | |
188 SETFGCOLOR(0xff, 0xff, 0xff); | |
189 if (showcoords) { | |
190 putchar(' '); | |
191 putchar('8' - y); | |
192 putchar(' '); | |
193 } else { | |
194 fputs(" ", stdout); | |
195 } | |
196 | |
197 printf("\x1b[0m"); /* reset */ | |
198 SETFGCOLOR(0x00, 0x00, 0x00); | |
199 putchar('\n'); | |
200 } | |
201 color = border; | |
202 SETBGCOLOR(color[0], color[1], color[2]); | |
203 SETFGCOLOR(0xff, 0xff, 0xff); | |
204 if (showcoords) | |
205 fputs(" a b c d e f g h ", stdout); | |
206 else | |
207 fputs(" ", stdout); | |
208 printf("\x1b[0m"); /* reset */ | |
209 printf("\n"); | |
210 printf("\x1b[0m"); /* reset */ | |
211 | |
212 #if 0 | |
213 if (side_to_move == 'w') { | |
214 fputs("White to move\n", stdout); | |
215 } else if (side_to_move == 'b') | |
216 fputs("Black to move\n", stdout); | |
217 | |
218 if (white_can_castle[0]) | |
219 fputs("White can castle king side\n", stdout); | |
220 if (white_can_castle[1]) | |
221 fputs("White can castle queen side\n", stdout); | |
222 if (black_can_castle[0]) | |
223 fputs("Black can castle king side\n", stdout); | |
224 if (black_can_castle[1]) | |
225 fputs("Black can castle queen side\n", stdout); | |
226 #endif | |
227 } | |
228 | |
229 int | |
230 main(int argc, char *argv[]) | |
231 { | |
232 const char *fen, *moves, *s; | |
233 int x, y, x2, y2, field, piece; | |
234 char pieces[] = "PNBRQKpnbrqk", square[3]; | |
235 | |
236 if (argc != 3) { | |
237 fprintf(stderr, "usage: %s <FEN> <moves>\n", argv[0]); | |
238 return 1; | |
239 } | |
240 | |
241 fen = argv[1]; | |
242 moves = argv[2]; | |
243 | |
244 /* initial board state, FEN format */ | |
245 x = y = field = 0; | |
246 for (s = fen; *s; s++) { | |
247 /* next field, fields are: piece placement data, active … | |
248 Castling availability, En passant target square, | |
249 Halfmove clock, Fullmove number */ | |
250 if (*s == ' ') { | |
251 field++; | |
252 continue; | |
253 } | |
254 | |
255 switch (field) { | |
256 case 0: /* piece placement data */ | |
257 /* skip square */ | |
258 if (*s >= '1' && *s <= '9') { | |
259 x += (*s - '0'); | |
260 continue; | |
261 } | |
262 /* next rank */ | |
263 if (*s == '/') { | |
264 x = 0; | |
265 y++; | |
266 continue; | |
267 } | |
268 /* is piece? place it */ | |
269 if (strchr(pieces, *s)) | |
270 place(*s, x++, y); | |
271 break; | |
272 case 1: /* active color */ | |
273 if (*s == 'w' || *s == 'b') | |
274 side_to_move = *s; | |
275 break; | |
276 case 2: /* castling availability */ | |
277 if (*s == '-') { | |
278 white_can_castle[0] = 0; | |
279 white_can_castle[1] = 0; | |
280 black_can_castle[0] = 0; | |
281 black_can_castle[1] = 0; | |
282 } else if (*s == 'K') { | |
283 white_can_castle[0] = 1; | |
284 } else if (*s == 'Q') { | |
285 white_can_castle[1] = 1; | |
286 } else if (*s == 'k') { | |
287 black_can_castle[0] = 1; | |
288 } else if (*s == 'q') { | |
289 black_can_castle[1] = 1; | |
290 } | |
291 break; | |
292 case 3: /* TODO: en-passant square, rest of the fields */ | |
293 break; | |
294 } | |
295 /* TODO: parse which side to move, en-passant, etc */ | |
296 } | |
297 | |
298 /* process moves */ | |
299 square[2] = '\0'; | |
300 x = y = x2 = y2 = -1; | |
301 for (s = moves; *s; s++) { | |
302 if (*s == ' ') | |
303 continue; | |
304 if ((*s >= 'a' && *s <= 'h') && | |
305 (*(s + 1) >= '1' && *(s + 1) <= '8') && | |
306 (*(s + 2) >= 'a' && *(s + 2) <= 'h') && | |
307 (*(s + 3) >= '1' && *(s + 3) <= '8')) { | |
308 square[0] = *s; | |
309 square[1] = *(s + 1); | |
310 | |
311 s += 2; | |
312 squaretoxy(square, &x, &y); | |
313 piece = getpiece(x, y); | |
314 | |
315 place(0, x, y); /* clear square */ | |
316 | |
317 /* place piece at new location */ | |
318 square[0] = *s; | |
319 square[1] = *(s + 1); | |
320 squaretoxy(square, &x2, &y2); | |
321 place(piece, x2, y2); | |
322 s += 2; | |
323 | |
324 /* possible promotion? (queen, knight, bishop) */ | |
325 if (*s == 'q' || *s == 'n' || *s == 'b') { | |
326 if (side_to_move == 'w') | |
327 piece = toupper(*s); | |
328 else | |
329 piece = *s; | |
330 place(piece, x2, y2); | |
331 s++; | |
332 } | |
333 | |
334 /* switch which side it is to move */ | |
335 side_to_move = side_to_move == 'b' ? 'w' : 'b'; | |
336 } | |
337 } | |
338 /* highlight last move */ | |
339 highlightmove(x, y, x2, y2); | |
340 | |
341 showboard(); | |
342 | |
343 printf("\x1b[0m"); /* reset */ | |
344 | |
345 return 0; | |
346 } |