fen_to_tty.c - chess-puzzles - chess puzzle book generator
(HTM) git clone git://git.codemadness.org/chess-puzzles
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) 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 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 #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] ? lightsquarehi : lightsquare;
159 else
160 color = highlight[y][x] ? darksquarehi : darksquare;
161 } else {
162 if (y % 2 == 0)
163 color = highlight[y][x] ? darksquarehi : darksquare;
164 else
165 color = highlight[y][x] ? lightsquarehi : lightsquare;
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, because the color
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 color,
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 }