fen_to_svg.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_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 queen side? */
           16 static int black_can_castle[2] = { 0, 0 }; /* allow king side, allow queen side? */
           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/master/public/piece/cburnett */
          131         switch (c) {
          132         case 'K': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22.5 11.63V6M20 8h5\" stroke-linejoin=\"miter\"/><path d=\"M22.5 25s4.5-7.5 3-10.5c0 0-1-2.5-3-2.5s-3 2.5-3 2.5c-1.5 3 3 10.5 3 10.5\" fill=\"#fff\" stroke-linecap=\"butt\" stroke-linejoin=\"miter\"/><path d=\"M11.5 37c5.5 3.5 15.5 3.5 21 0v-7s9-4.5 6-10.5c-4-6.5-13.5-3.5-16 4V27v-3.5c-3.5-7.5-13-10.5-16-4-3 6 5 10 5 10V37z\" fill=\"#fff\"/><path d=\"M11.5 30c5.5-3 15.5-3 21 0m-21 3.5c5.5-3 15.5-3 21 0m-21 3.5c5.5-3 15.5-3 21 0\"/></g>"; break;
          133         case 'Q': s = "<g fill=\"#fff\" fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 12a2 2 0 1 1-4 0 2 2 0 1 1 4 0zm16.5-4.5a2 2 0 1 1-4 0 2 2 0 1 1 4 0zM41 12a2 2 0 1 1-4 0 2 2 0 1 1 4 0zM16 8.5a2 2 0 1 1-4 0 2 2 0 1 1 4 0zM33 9a2 2 0 1 1-4 0 2 2 0 1 1 4 0z\"/><path d=\"M9 26c8.5-1.5 21-1.5 27 0l2-12-7 11V11l-5.5 13.5-3-15-3 15-5.5-14V25L7 14l2 12z\" stroke-linecap=\"butt\"/><path d=\"M9 26c0 2 1.5 2 2.5 4 1 1.5 1 1 .5 3.5-1.5 1-1.5 2.5-1.5 2.5-1.5 1.5.5 2.5.5 2.5 6.5 1 16.5 1 23 0 0 0 1.5-1 0-2.5 0 0 .5-1.5-1-2.5-.5-2.5-.5-2 .5-3.5 1-2 2.5-2 2.5-4-8.5-1.5-18.5-1.5-27 0z\" stroke-linecap=\"butt\"/><path d=\"M11.5 30c3.5-1 18.5-1 22 0M12 33.5c6-1 15-1 21 0\" fill=\"none\"/></g>"; break;
          134         case 'R': s = "<g fill=\"#fff\" fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 39h27v-3H9v3zm3-3v-4h21v4H12zm-1-22V9h4v2h5V9h5v2h5V9h4v5\" stroke-linecap=\"butt\"/><path d=\"M34 14l-3 3H14l-3-3\"/><path d=\"M31 17v12.5H14V17\" stroke-linecap=\"butt\" stroke-linejoin=\"miter\"/><path d=\"M31 29.5l1.5 2.5h-20l1.5-2.5\"/><path d=\"M11 14h23\" fill=\"none\" stroke-linejoin=\"miter\"/></g>"; break;
          135         case 'B': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><g fill=\"#fff\" stroke-linecap=\"butt\"><path d=\"M9 36c3.39-.97 10.11.43 13.5-2 3.39 2.43 10.11 1.03 13.5 2 0 0 1.65.54 3 2-.68.97-1.65.99-3 .5-3.39-.97-10.11.46-13.5-1-3.39 1.46-10.11.03-13.5 1-1.354.49-2.323.47-3-.5 1.354-1.94 3-2 3-2z\"/><path d=\"M15 32c2.5 2.5 12.5 2.5 15 0 .5-1.5 0-2 0-2 0-2.5-2.5-4-2.5-4 5.5-1.5 6-11.5-5-15.5-11 4-10.5 14-5 15.5 0 0-2.5 1.5-2.5 4 0 0-.5.5 0 2z\"/><path d=\"M25 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 1 1 5 0z\"/></g><path d=\"M17.5 26h10M15 30h15m-7.5-14.5v5M20 18h5\" stroke-linejoin=\"miter\"/></g>"; break;
          136         case 'N': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 10c10.5 1 16.5 8 16 29H15c0-9 10-6.5 8-21\" fill=\"#fff\"/><path d=\"M24 18c.38 2.91-5.55 7.37-8 9-3 2-2.82 4.34-5 4-1.042-.94 1.41-3.04 0-3-1 0 .19 1.23-1 2-1 0-4.003 1-4-4 0-2 6-12 6-12s1.89-1.9 2-3.5c-.73-.994-.5-2-.5-3 1-1 3 2.5 3 2.5h2s.78-1.992 2.5-3c1 0 1 3 1 3\" fill=\"#fff\"/><path d=\"M9.5 25.5a.5.5 0 1 1-1 0 .5.5 0 1 1 1 0zm5.433-9.75a.5 1.5 30 1 1-.866-.5.5 1.5 30 1 1 .866.5z\" fill=\"#000\"/></g>"; break;
          137         case 'P': s = "<path d=\"M22.5 9c-2.21 0-4 1.79-4 4 0 .89.29 1.71.78 2.38C17.33 16.5 16 18.59 16 21c0 2.03.94 3.84 2.41 5.03-3 1.06-7.41 5.55-7.41 13.47h23c0-7.92-4.41-12.41-7.41-13.47 1.47-1.19 2.41-3 2.41-5.03 0-2.41-1.33-4.5-3.28-5.62.49-.67.78-1.49.78-2.38 0-2.21-1.79-4-4-4z\" fill=\"#fff\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>"; break;
          138         case 'k': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22.5 11.63V6\" stroke-linejoin=\"miter\"/><path d=\"M22.5 25s4.5-7.5 3-10.5c0 0-1-2.5-3-2.5s-3 2.5-3 2.5c-1.5 3 3 10.5 3 10.5\" fill=\"#000\" stroke-linecap=\"butt\" stroke-linejoin=\"miter\"/><path d=\"M11.5 37c5.5 3.5 15.5 3.5 21 0v-7s9-4.5 6-10.5c-4-6.5-13.5-3.5-16 4V27v-3.5c-3.5-7.5-13-10.5-16-4-3 6 5 10 5 10V37z\" fill=\"#000\"/><path d=\"M20 8h5\" stroke-linejoin=\"miter\"/><path d=\"M32 29.5s8.5-4 6.03-9.65C34.15 14 25 18 22.5 24.5l.01 2.1-.01-2.1C20 18 9.906 14 6.997 19.85c-2.497 5.65 4.853 9 4.853 9\" stroke=\"#ececec\"/><path d=\"M11.5 30c5.5-3 15.5-3 21 0m-21 3.5c5.5-3 15.5-3 21 0m-21 3.5c5.5-3 15.5-3 21 0\" stroke=\"#ececec\"/></g>"; break;
          139         case 'q': s = "<g fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><g stroke=\"none\"><circle cx=\"6\" cy=\"12\" r=\"2.75\"/><circle cx=\"14\" cy=\"9\" r=\"2.75\"/><circle cx=\"22.5\" cy=\"8\" r=\"2.75\"/><circle cx=\"31\" cy=\"9\" r=\"2.75\"/><circle cx=\"39\" cy=\"12\" r=\"2.75\"/></g><path d=\"M9 26c8.5-1.5 21-1.5 27 0l2.5-12.5L31 25l-.3-14.1-5.2 13.6-3-14.5-3 14.5-5.2-13.6L14 25 6.5 13.5 9 26z\" stroke-linecap=\"butt\"/><path d=\"M9 26c0 2 1.5 2 2.5 4 1 1.5 1 1 .5 3.5-1.5 1-1.5 2.5-1.5 2.5-1.5 1.5.5 2.5.5 2.5 6.5 1 16.5 1 23 0 0 0 1.5-1 0-2.5 0 0 .5-1.5-1-2.5-.5-2.5-.5-2 .5-3.5 1-2 2.5-2 2.5-4-8.5-1.5-18.5-1.5-27 0z\" stroke-linecap=\"butt\"/><path d=\"M11 38.5a35 35 1 0 0 23 0\" fill=\"none\" stroke-linecap=\"butt\"/><path d=\"M11 29a35 35 1 0 1 23 0m-21.5 2.5h20m-21 3a35 35 1 0 0 22 0m-23 3a35 35 1 0 0 24 0\" fill=\"none\" stroke=\"#ececec\"/></g>"; break;
          140         case 'r': s = "<g fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 39h27v-3H9v3zm3.5-7l1.5-2.5h17l1.5 2.5h-20zm-.5 4v-4h21v4H12z\" stroke-linecap=\"butt\"/><path d=\"M14 29.5v-13h17v13H14z\" stroke-linecap=\"butt\" stroke-linejoin=\"miter\"/><path d=\"M14 16.5L11 14h23l-3 2.5H14zM11 14V9h4v2h5V9h5v2h5V9h4v5H11z\" stroke-linecap=\"butt\"/><path d=\"M12 35.5h21m-20-4h19m-18-2h17m-17-13h17M11 14h23\" fill=\"none\" stroke=\"#ececec\" stroke-width=\"1\" stroke-linejoin=\"miter\"/></g>"; break;
          141         case 'b': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><g fill=\"#000\" stroke-linecap=\"butt\"><path d=\"M9 36c3.39-.97 10.11.43 13.5-2 3.39 2.43 10.11 1.03 13.5 2 0 0 1.65.54 3 2-.68.97-1.65.99-3 .5-3.39-.97-10.11.46-13.5-1-3.39 1.46-10.11.03-13.5 1-1.354.49-2.323.47-3-.5 1.354-1.94 3-2 3-2z\"/><path d=\"M15 32c2.5 2.5 12.5 2.5 15 0 .5-1.5 0-2 0-2 0-2.5-2.5-4-2.5-4 5.5-1.5 6-11.5-5-15.5-11 4-10.5 14-5 15.5 0 0-2.5 1.5-2.5 4 0 0-.5.5 0 2z\"/><path d=\"M25 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 1 1 5 0z\"/></g><path d=\"M17.5 26h10M15 30h15m-7.5-14.5v5M20 18h5\" stroke=\"#ececec\" stroke-linejoin=\"miter\"/></g>"; break;
          142         case 'n': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 10c10.5 1 16.5 8 16 29H15c0-9 10-6.5 8-21\" fill=\"#000\"/><path d=\"M24 18c.38 2.91-5.55 7.37-8 9-3 2-2.82 4.34-5 4-1.042-.94 1.41-3.04 0-3-1 0 .19 1.23-1 2-1 0-4.003 1-4-4 0-2 6-12 6-12s1.89-1.9 2-3.5c-.73-.994-.5-2-.5-3 1-1 3 2.5 3 2.5h2s.78-1.992 2.5-3c1 0 1 3 1 3\" fill=\"#000\"/><path d=\"M9.5 25.5a.5.5 0 1 1-1 0 .5.5 0 1 1 1 0zm5.433-9.75a.5 1.5 30 1 1-.866-.5.5 1.5 30 1 1 .866.5z\" fill=\"#ececec\" stroke=\"#ececec\"/><path d=\"M24.55 10.4l-.45 1.45.5.15c3.15 1 5.65 2.49 7.9 6.75S35.75 29.06 35.25 39l-.05.5h2.25l.05-.5c.5-10.06-.88-16.85-3.25-21.34-2.37-4.49-5.79-6.64-9.19-7.16l-.51-.1z\" fill=\"#ececec\" stroke=\"none\"/></g>"; break;
          143         case 'p': s = "<path d=\"M22.5 9c-2.21 0-4 1.79-4 4 0 .89.29 1.71.78 2.38C17.33 16.5 16 18.59 16 21c0 2.03.94 3.84 2.41 5.03-3 1.06-7.41 5.55-7.41 13.47h23c0-7.92-4.41-12.41-7.41-13.47 1.47-1.19 2.41-3 2.41-5.03 0-2.41-1.33-4.5-3.28-5.62.49-.67.78-1.49.78-2.38 0-2.21-1.79-4-4-4z\" stroke=\"#000\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>"; break;
          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\"?>\n"
          162                 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
          163                 "<svg width=\"360\" height=\"360\" viewBox=\"0 0 360 360\" xmlns=\"http://www.w3.org/2000/svg\">\n"
          164                 "<rect fill=\"#fff\" stroke=\"#000\" x=\"0\" y=\"0\" width=\"360\" height=\"360\"/>\n", stdout);
          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] ? lightsquarehi : lightsquare;
          175                                 else
          176                                         color = highlight[y][x] ? darksquarehi : darksquare;
          177                         } else {
          178                                 if (y % 2 == 0)
          179                                         color = highlight[y][x] ? darksquarehi : darksquare;
          180                                 else
          181                                         color = highlight[y][x] ? lightsquarehi : lightsquare;
          182                         }
          183 
          184                         printf("<g><rect x=\"%d\" y=\"%d\" width=\"45\" height=\"45\" fill=\"%s\"/></g>\n",
          185                                 x * 45, y * 45, color);
          186 
          187                         piece = getpiece(x, y);
          188                         if (piece) {
          189                                 printf("<g transform=\"translate(%d %d)\">", x * 45, y * 45);
          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 : lightsquare;
          201                         else
          202                                 color = highlight[y][x] ? darksquarehi : darksquare;
          203                         printf("<text x=\"%d\" y=\"%d\" fill=\"%s\" text-anchor=\"end\" style=\"font-family: sans-serif; font-size: 10px\">%c</text>\n",
          204                                 (x + 1) * 45 - 2, (y * 45) + 10, color, '8' - y);
          205                 }
          206                 y = 7;
          207                 for (x = 0; x < 8; x++) {
          208                         if (x % 2 == 0)
          209                                 color = highlight[y][x] ? lightsquarehi : lightsquare;
          210                         else
          211                                 color = highlight[y][x] ? darksquarehi : darksquare;
          212                         printf("<text x=\"%d\" y=\"%d\" fill=\"%s\" text-anchor=\"start\" style=\"font-family: sans-serif; font-size: 10px\">%c</text>\n",
          213                                 (x * 45) + 2, (y + 1) * 45 - 3, color, x + 'a');
          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] ? lightsquarehi : lightsquare;
          286                                 else
          287                                         color = highlight[y][x] ? darksquarehi : darksquare;
          288                         } else {
          289                                 if (y % 2 == 0)
          290                                         color = highlight[y][x] ? darksquarehi : darksquare;
          291                                 else
          292                                         color = highlight[y][x] ? lightsquarehi : lightsquare;
          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, because the color
          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 KQkq - 0 1";
          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); 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); 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 color,
          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 halfmove counter */
          538                         /* TODO: taking enpassant should reset halfmove too */
          539                         if (piece == 'p' || piece == 'P' || takepiece != 0)
          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 square */
          552                                 } else if (x2 == 2) {
          553                                         /* white: queenside castling: "e1c1" */
          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 && x == 4) {
          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 square */
          566                                 } else if (x2 == 2) {
          567                                         /* black: queenside castling: "e8c8" */
          568                                         place('r', x2 + 1, y2);
          569                                         x2 = 0;
          570                                         y2 = 0;
          571                                         place(0, x2, y2); /* clear rook square */
          572                                 }
          573                         }
          574 
          575                         /* remove the ability to castle */
          576                         if (piece == 'K') {
          577                                 white_can_castle[0] = white_can_castle[1] = 0;
          578                         } else if (piece == 'k') {
          579                                 black_can_castle[0] = black_can_castle[1] = 0;
          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 next to it */
          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)*s);
          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 }