Introduction
Introduction Statistics Contact Development Disclaimer Help
fen_to_svg.c - chess-puzzles - chess puzzle book generator
git clone git://git.codemadness.org/chess-puzzles
Log
Files
Refs
README
LICENSE
---
fen_to_svg.c (22185B)
---
1 /* TODO: option to flip board? */
2
3 #include <ctype.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #define SETFGCOLOR(r,g,b) printf("\x1b[38;2;%d;%d;%dm", r, g, b)
9 #define SETBGCOLOR(r,g,b) printf("\x1b[48;2;%d;%d;%dm", r, g, b)
10
11 static char board[8][8];
12 static char highlight[8][8];
13
14 static int side_to_move = 'w'; /* default: white to move */
15 static int white_can_castle[2] = { 0, 0 }; /* allow king side, allow que…
16 static int black_can_castle[2] = { 0, 0 }; /* allow king side, allow que…
17 static int enpassantsquare[2] = { -1, -1 };
18 static int movenumber = 1;
19 static int halfmove = 0;
20
21 static const int showcoords = 1; /* config: show board coordinates? */
22
23 int
24 isvalidsquare(int x, int y)
25 {
26 return !(x < 0 || x >= 8 || y < 0 || y >= 8);
27 }
28
29 int
30 isvalidpiece(int c)
31 {
32 static char pieces[] = "PNBRQKpnbrqk";
33
34 return strchr(pieces, c) ? 1 : 0;
35 }
36
37 /* place a piece, if possible */
38 void
39 place(int piece, int x, int y)
40 {
41 if (!isvalidsquare(x, y))
42 return;
43
44 board[y][x] = piece;
45 }
46
47 /* get piece, if possible */
48 int
49 getpiece(int x, int y)
50 {
51 if (!isvalidsquare(x, y))
52 return 0;
53 return board[y][x];
54 }
55
56 int
57 squaretoxy(const char *s, int *x, int *y)
58 {
59 if (*s >= 'a' && *s <= 'h' &&
60 *(s + 1) >= '1' && *(s + 1) <= '8') {
61 *x = *s - 'a';
62 *y = '8' - *(s + 1);
63 return 1;
64 }
65 return 0;
66 }
67
68 void
69 highlightmove(int x1, int y1, int x2, int y2)
70 {
71 if (isvalidsquare(x1, y1))
72 highlight[y1][x1] = 1;
73
74 if (isvalidsquare(x2, y2))
75 highlight[y2][x2] = 1;
76 }
77
78 void
79 showboardfen(void)
80 {
81 int x, y, piece, skip = 0;
82
83 for (y = 0; y < 8; y++) {
84 if (y > 0)
85 putchar('/');
86 skip = 0;
87 for (x = 0; x < 8; x++) {
88 piece = getpiece(x, y);
89 if (piece) {
90 if (skip)
91 putchar(skip + '0');
92 putchar(piece);
93 skip = 0;
94 } else {
95 skip++;
96 }
97 }
98 if (skip)
99 putchar(skip + '0');
100 }
101 printf(" %c ", side_to_move);
102 if (white_can_castle[0])
103 putchar('K');
104 if (white_can_castle[1])
105 putchar('Q');
106 if (black_can_castle[0])
107 putchar('k');
108 if (black_can_castle[1])
109 putchar('q');
110 if ((white_can_castle[0] + white_can_castle[1] +
111 black_can_castle[0] + black_can_castle[1]) == 0)
112 putchar('-'); /* no castling for either side */
113 putchar(' ');
114
115 if (enpassantsquare[0] != -1 && enpassantsquare[1] != -1) {
116 putchar('a' + enpassantsquare[0]);
117 putchar('8' - enpassantsquare[1]);
118 } else {
119 putchar('-');
120 }
121 printf(" %d %d", halfmove, movenumber);
122 }
123
124 void
125 svg_showpiece(int c)
126 {
127 const char *s = "";
128
129 /* lichess default set,
130 extracted from https://github.com/lichess-org/lila/tree/maste…
131 switch (c) {
132 case 'K': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#…
133 case 'Q': s = "<g fill=\"#fff\" fill-rule=\"evenodd\" stroke=\"#…
134 case 'R': s = "<g fill=\"#fff\" fill-rule=\"evenodd\" stroke=\"#…
135 case 'B': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#…
136 case 'N': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#…
137 case 'P': s = "<path d=\"M22.5 9c-2.21 0-4 1.79-4 4 0 .89.29 1.7…
138 case 'k': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#…
139 case 'q': s = "<g fill-rule=\"evenodd\" stroke=\"#000\" stroke-w…
140 case 'r': s = "<g fill-rule=\"evenodd\" stroke=\"#000\" stroke-w…
141 case 'b': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#…
142 case 'n': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#…
143 case 'p': s = "<path d=\"M22.5 9c-2.21 0-4 1.79-4 4 0 .89.29 1.7…
144 }
145
146 if (*s)
147 fputs(s, stdout);
148 }
149
150 void
151 svg_showboard(void)
152 {
153 /* lichess default theme colors */
154 const char *darksquare = "#b58863";
155 const char *lightsquare = "#f0d9b5";
156 const char *darksquarehi = "#aaa23a";
157 const char *lightsquarehi = "#cdd26a";
158 const char *color;
159 int x, y, piece;
160
161 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\…
162 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http…
163 "<svg width=\"360\" height=\"360\" viewBox=\"0 0 360 360…
164 "<rect fill=\"#fff\" stroke=\"#000\" x=\"0\" y=\"0\" wid…
165
166 fputs("<!-- Board FEN: ", stdout);
167 showboardfen();
168 fputs(" -->\n", stdout);
169
170 for (y = 0; y < 8; y++) {
171 for (x = 0; x < 8; x++) {
172 if (x % 2 == 0) {
173 if (y % 2 == 0)
174 color = highlight[y][x] ? lights…
175 else
176 color = highlight[y][x] ? darksq…
177 } else {
178 if (y % 2 == 0)
179 color = highlight[y][x] ? darksq…
180 else
181 color = highlight[y][x] ? lights…
182 }
183
184 printf("<g><rect x=\"%d\" y=\"%d\" width=\"45\" …
185 x * 45, y * 45, color);
186
187 piece = getpiece(x, y);
188 if (piece) {
189 printf("<g transform=\"translate(%d %d)\…
190 svg_showpiece(piece);
191 fputs("</g>\n", stdout);
192 }
193 }
194 }
195
196 if (showcoords) {
197 x = 7;
198 for (y = 0; y < 8; y++) {
199 if (y % 2 == 0)
200 color = highlight[y][x] ? lightsquarehi …
201 else
202 color = highlight[y][x] ? darksquarehi :…
203 printf("<text x=\"%d\" y=\"%d\" fill=\"%s\" text…
204 (x + 1) * 45 - 2, (y * 45) + 10, color, …
205 }
206 y = 7;
207 for (x = 0; x < 8; x++) {
208 if (x % 2 == 0)
209 color = highlight[y][x] ? lightsquarehi …
210 else
211 color = highlight[y][x] ? darksquarehi :…
212 printf("<text x=\"%d\" y=\"%d\" fill=\"%s\" text…
213 (x * 45) + 2, (y + 1) * 45 - 3, color, x…
214 }
215 }
216
217 fputs("</svg>\n", stdout);
218 }
219
220 void
221 tty_showpiece(int c)
222 {
223 const char *s = "";
224
225 /* simple or use unicode character */
226 #if 0
227 putchar(c);
228 return;
229 #endif
230
231 switch (c) {
232 case 'K': s = "♔"; break;
233 case 'Q': s = "♕"; break;
234 case 'R': s = "♖"; break;
235 case 'B': s = "♗"; break;
236 case 'N': s = "♘"; break;
237 case 'P': s = "♙"; break;
238 case 'k': s = "♚"; break;
239 case 'q': s = "♛"; break;
240 case 'r': s = "♜"; break;
241 case 'b': s = "♝"; break;
242 case 'n': s = "♞"; break;
243 case 'p': s = "♟"; break;
244 }
245
246 if (*s)
247 fputs(s, stdout);
248 }
249
250 /* show board */
251 void
252 tty_showboard(void)
253 {
254 int *color;
255 int border[] = { 0x70, 0x49, 0x2d };
256 int darksquare[] = { 0xb5, 0x88, 0x63 };
257 int lightsquare[] = { 0xf0, 0xd9, 0xb5 };
258 int darksquarehi[] = { 0xaa, 0xa2, 0x3a };
259 int lightsquarehi[] = { 0xcd, 0xd2, 0x6a };
260 int x, y, piece;
261
262 printf("Board FEN:\n");
263 showboardfen();
264 printf("\n\n");
265
266 SETFGCOLOR(0x00, 0x00, 0x00);
267
268 color = border;
269 SETBGCOLOR(color[0], color[1], color[2]);
270 SETFGCOLOR(0xff, 0xff, 0xff);
271 fputs(" ", stdout);
272 printf("\x1b[0m"); /* reset */
273 SETFGCOLOR(0x00, 0x00, 0x00);
274 putchar('\n');
275
276 for (y = 0; y < 8; y++) {
277 color = border;
278 SETBGCOLOR(color[0], color[1], color[2]);
279 SETFGCOLOR(0xff, 0xff, 0xff);
280 fputs(" ", stdout);
281
282 for (x = 0; x < 8; x++) {
283 if (x % 2 == 0) {
284 if (y % 2 == 0)
285 color = highlight[y][x] ? lights…
286 else
287 color = highlight[y][x] ? darksq…
288 } else {
289 if (y % 2 == 0)
290 color = highlight[y][x] ? darksq…
291 else
292 color = highlight[y][x] ? lights…
293 }
294 SETBGCOLOR(color[0], color[1], color[2]);
295
296 fputs(" ", stdout);
297 piece = getpiece(x, y);
298 if (piece) {
299 if (piece >= 'A' && piece <= 'Z')
300 SETFGCOLOR(0xff, 0xff, 0xff);
301 else
302 SETFGCOLOR(0x00, 0x00, 0x00);
303 /* workaround: use black chess symbol, b…
304 is filled and better visible */
305 tty_showpiece(tolower(piece));
306 } else {
307 fputs(" ", stdout);
308 }
309 fputs(" ", stdout);
310 }
311 printf("\x1b[0m"); /* reset */
312
313 color = border;
314 SETBGCOLOR(color[0], color[1], color[2]);
315 SETFGCOLOR(0xff, 0xff, 0xff);
316 if (showcoords) {
317 putchar(' ');
318 putchar('8' - y);
319 putchar(' ');
320 } else {
321 fputs(" ", stdout);
322 }
323
324 printf("\x1b[0m"); /* reset */
325 SETFGCOLOR(0x00, 0x00, 0x00);
326 putchar('\n');
327 }
328 color = border;
329 SETBGCOLOR(color[0], color[1], color[2]);
330 SETFGCOLOR(0xff, 0xff, 0xff);
331 if (showcoords)
332 fputs(" a b c d e f g h ", stdout);
333 else
334 fputs(" ", stdout);
335 printf("\x1b[0m"); /* reset */
336 printf("\n");
337 printf("\x1b[0m"); /* reset */
338 }
339
340 void
341 ascii_showpiece(int c)
342 {
343 putchar(c);
344 }
345
346 /* OnlyFENs */
347 void
348 fen_showboard(void)
349 {
350 showboardfen();
351 printf("\n");
352 }
353
354 /* show board */
355 /* TODO: show fancier, unicode and background square color */
356 /* TODO: use the output format similar to stockfish "d" command */
357 void
358 ascii_showboard(void)
359 {
360 int x, y, piece;
361
362 printf("Board FEN:\n");
363 showboardfen();
364 printf("\n\n");
365
366 for (y = 0; y < 8; y++) {
367 fputs("+---+---+---+---+---+---+---+---+\n", stdout);
368 for (x = 0; x < 8; x++) {
369 if (x == 0)
370 putchar('|');
371 fputs(" ", stdout);
372 piece = getpiece(x, y);
373 if (piece)
374 ascii_showpiece(piece);
375 else
376 fputs(" ", stdout);
377 fputs(" |", stdout);
378 }
379 if (showcoords) {
380 putchar(' ');
381 putchar('8' - y);
382 }
383 putchar('\n');
384 }
385 fputs("+---+---+---+---+---+---+---+---+\n", stdout);
386 if (showcoords)
387 printf(" a | b | c | d | e | f | g | h |\n");
388
389 fputs("\n", stdout);
390 }
391
392 int
393 main(int argc, char *argv[])
394 {
395 const char *progname, *fen, *moves, *s;
396 int x, y, x2, y2, field, piece, takepiece;
397 char square[3];
398 long l;
399
400 if (argc > 3) {
401 fprintf(stderr, "usage: %s <FEN> [moves] or\n", argv[0]);
402 fprintf(stderr, " %s <FEN> or\n", argv[0]);
403 fprintf(stderr, " %s\n", argv[0]);
404 return 1;
405 }
406 if (argc > 1)
407 fen = argv[1];
408 else
409 fen = "startpos";
410
411 if (argc > 2)
412 moves = argv[2];
413 else
414 moves = "";
415
416 if (!strcmp(fen, "startpos"))
417 fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQk…
418
419 /* initial board state, FEN format */
420 x = y = field = 0;
421 for (s = fen; *s && field < 6; s++) {
422 switch (field) {
423 case 0: /* piece placement data */
424 /* skip square */
425 if (*s >= '1' && *s <= '9') {
426 x += (*s - '0');
427 continue;
428 }
429 /* next rank */
430 if (*s == '/') {
431 x = 0;
432 y++;
433 continue;
434 }
435 /* is piece? place it */
436 if (isvalidpiece(*s))
437 place(*s, x++, y);
438 break;
439 case 1: /* active color */
440 if (*s == 'w' || *s == 'b')
441 side_to_move = *s;
442 break;
443 case 2: /* castling availability */
444 if (*s == '-') {
445 white_can_castle[0] = 0;
446 white_can_castle[1] = 0;
447 black_can_castle[0] = 0;
448 black_can_castle[1] = 0;
449 } else if (*s == 'K') {
450 white_can_castle[0] = 1;
451 } else if (*s == 'Q') {
452 white_can_castle[1] = 1;
453 } else if (*s == 'k') {
454 black_can_castle[0] = 1;
455 } else if (*s == 'q') {
456 black_can_castle[1] = 1;
457 }
458 break;
459 case 3: /* TODO: en-passant square, rest of the fields */
460 if (*s >= 'a' && *s <= 'h' &&
461 *(s + 1) >= '1' && *(s + 1) >= '6') {
462
463 square[0] = *s;
464 square[1] = *(s + 1);
465 square[2] = '\0';
466 squaretoxy(square, &x, &y);
467
468 enpassantsquare[0] = x;
469 enpassantsquare[1] = y;
470 }
471 break;
472 case 4: /* halfmove */
473 if (!(*s >= '0' && *s <= '9'))
474 continue;
475
476 l = strtol(s, NULL, 10);
477 if (l >= 0 && l < 32767) {
478 halfmove = l;
479
480 for (; *s && isdigit((unsigned char)*s);…
481 ;
482 }
483 break;
484 case 5: /* move number */
485 if (!(*s >= '0' && *s <= '9'))
486 continue;
487
488 l = strtol(s, NULL, 10);
489 if (l >= 0 && l < 32767) {
490 movenumber = (int)l;
491 for (; *s && isdigit((unsigned char)*s);…
492 ;
493 }
494 break;
495 }
496 if (!*s)
497 break;
498
499 /* TODO: parse which side to move, en-passant, etc */
500
501 /* next field, fields are: piece placement data, active …
502 Castling availability, En passant target square,
503 Halfmove clock, Fullmove number */
504 if (*s == ' ') {
505 field++;
506 continue;
507 }
508 }
509
510 /* process moves */
511 square[2] = '\0';
512 x = y = x2 = y2 = -1;
513 for (s = moves; *s; s++) {
514 if (*s == ' ')
515 continue;
516 if ((*s >= 'a' && *s <= 'h') &&
517 (*(s + 1) >= '1' && *(s + 1) <= '8') &&
518 (*(s + 2) >= 'a' && *(s + 2) <= 'h') &&
519 (*(s + 3) >= '1' && *(s + 3) <= '8')) {
520 square[0] = *s;
521 square[1] = *(s + 1);
522
523 s += 2;
524 squaretoxy(square, &x, &y);
525 piece = getpiece(x, y);
526
527 place(0, x, y); /* clear square */
528
529 /* place piece at new location */
530 square[0] = *s;
531 square[1] = *(s + 1);
532 squaretoxy(square, &x2, &y2);
533 takepiece = getpiece(x2, y2);
534 place(piece, x2, y2);
535 s += 2;
536
537 /* if pawn move or taken a piece increase halfmo…
538 /* TODO: taking enpassant should reset halfmove …
539 if (piece == 'p' || piece == 'P' || takepiece !=…
540 halfmove = 0;
541 else
542 halfmove++;
543
544 /* castling */
545 if (piece == 'K' && y == 7 && y2 == 7 && x == 4)…
546 /* white: kingside castling: "e1g1" */
547 if (x2 == 6) {
548 place('R', x2 - 1, y2);
549 x2 = 7;
550 y2 = 7;
551 place(0, x2, y2); /* clear rook …
552 } else if (x2 == 2) {
553 /* white: queenside castling: "e…
554 place('R', x2 + 1, y2);
555 x2 = 0;
556 y2 = 7;
557 place(0, x2, y2);
558 }
559 } else if (piece == 'k' && y == 0 && y2 == 0 && …
560 /* black: kingside castling: "e8g8" */
561 if (x2 == 6) {
562 place('r', x2 - 1, y2);
563 x2 = 7;
564 y2 = 0;
565 place(0, x2, y2); /* clear rook …
566 } else if (x2 == 2) {
567 /* black: queenside castling: "e…
568 place('r', x2 + 1, y2);
569 x2 = 0;
570 y2 = 0;
571 place(0, x2, y2); /* clear rook …
572 }
573 }
574
575 /* remove the ability to castle */
576 if (piece == 'K') {
577 white_can_castle[0] = white_can_castle[1…
578 } else if (piece == 'k') {
579 black_can_castle[0] = black_can_castle[1…
580 } else if (piece == 'R') {
581 if (x == 7 && y == 7)
582 white_can_castle[0] = 0;
583 else if (x == 0 && y == 7)
584 white_can_castle[1] = 0;
585 } else if (piece == 'r') {
586 if (x == 0 && y == 0)
587 black_can_castle[1] = 0;
588 else if (x == 7 && y == 0)
589 black_can_castle[0] = 0;
590 }
591
592 /* the en passant square resets after a move */
593 enpassantsquare[0] = -1;
594 enpassantsquare[1] = -1;
595 /* moved 2 squares and there is an opponent pawn…
596 if (piece == 'P' && y == 6 && y2 == 4) {
597 if (getpiece(x - 1, y2) == 'p' ||
598 getpiece(x + 1, y2) == 'p') {
599 enpassantsquare[0] = x;
600 enpassantsquare[1] = 5;
601 }
602 } else if (piece == 'p' && y == 1 && y2 == 3) {
603 if (getpiece(x - 1, y2) == 'P' ||
604 getpiece(x + 1, y2) == 'P') {
605 enpassantsquare[0] = x;
606 enpassantsquare[1] = 2;
607 }
608 }
609
610 /* possible promotion: queen, knight, bishop */
611 if (*s == 'q' || *s == 'n' || *s == 'b') {
612 if (side_to_move == 'w')
613 piece = toupper((unsigned char)*…
614 else
615 piece = *s;
616 place(piece, x2, y2);
617 s++;
618 }
619
620 /* a move by black increases the move number */
621 if (side_to_move == 'b')
622 movenumber++;
623
624 /* switch which side it is to move */
625 side_to_move = side_to_move == 'b' ? 'w' : 'b';
626
627 /* TODO: reset enpassant square if applicable */
628 }
629 }
630 /* highlight last move */
631 highlightmove(x, y, x2, y2);
632
633 progname = argv[0] ? argv[0] : "fen_to_svg";
634 if ((s = strrchr(progname, '/')))
635 progname = s + 1;
636 if (!strcmp(progname, "fen_to_ascii"))
637 ascii_showboard();
638 else if (!strcmp(progname, "fen_to_tty"))
639 tty_showboard();
640 else if (!strcmp(progname, "fen_to_fen"))
641 fen_showboard();
642 else
643 svg_showboard();
644
645 return 0;
646 }
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.