fen.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.c (47523B)
       ---
            1 #include <stdarg.h>
            2 #include <stdio.h>
            3 #include <stdlib.h>
            4 #include <string.h>
            5 
            6 #ifdef __OpenBSD__
            7 #include <err.h>
            8 #include <unistd.h>
            9 #endif
           10 
           11 #define LEN(s)    (sizeof(s)/sizeof(*s))
           12 
           13 /* ctype-like macros, but always compatible with ASCII / UTF-8 */
           14 #define ISDIGIT(c) (((unsigned)c) - '0' < 10)
           15 #define ISXDIGIT(c) ((((unsigned)c) - '0' < 10) || ((unsigned)c | 32) - 'a' < 6)
           16 #define TOLOWER(c) ((((unsigned)c) - 'A' < 26) ? ((c) | 32) : (c))
           17 #define TOUPPER(c) ((((unsigned)c) - 'a' < 26) ? ((c) & 0x5f) : (c))
           18 
           19 /* macro for truecolor RGB output to tty */
           20 #define SETFGCOLOR(r,g,b)    printf("\x1b[38;2;%d;%d;%dm", r, g, b)
           21 #define SETBGCOLOR(r,g,b)    printf("\x1b[48;2;%d;%d;%dm", r, g, b)
           22 
           23 enum outputmode { ModeInvalid = 0, ModeASCII, ModeFEN, ModePGN,
           24                   ModeTTY, ModeSVG, ModeSpeak };
           25 enum outputmode outputmode = ModeSVG; /* default is SVG */
           26 
           27 static int onlylastmove = 0, silent = 0, dutchmode = 0;
           28 
           29 /* localization of letter for PGN pieces */
           30 const char *pgn_piecemapping = "";
           31 
           32 typedef unsigned char Color; /* for RGB: 0-255 */
           33 
           34 struct theme {
           35         const char *name;
           36         /* RGB values */
           37         Color border[3];
           38         Color darksquare[3];
           39         Color lightsquare[3];
           40         Color darksquarehi[3];
           41         Color lightsquarehi[3];
           42         Color lightsquarecheck[3];
           43         Color darksquarecheck[3];
           44 };
           45 
           46 struct theme themes[] = {
           47 /* lichess default brown theme colors (red, green, blue) */
           48 {
           49         .name = "default",
           50         .border = { 0x70, 0x49, 0x2d },
           51         .darksquare = { 0xb5, 0x88, 0x63 },
           52         .lightsquare = { 0xf0, 0xd9, 0xb5 },
           53         .darksquarehi = { 0xaa, 0xa2, 0x3a },
           54         .lightsquarehi = { 0xcd, 0xd2, 0x6a },
           55         .lightsquarecheck = { 0xff, 0x6a, 0x6a },
           56         .darksquarecheck = { 0xff, 0x3a, 0x3a  }
           57 },
           58 /* lichess green theme */
           59 {
           60         .name = "green",
           61         .border = { 0x33, 0x33, 0x33 },
           62         .darksquare = { 0x86, 0xa6, 0x66 },
           63         .lightsquare = { 0xff, 0xff, 0xdd },
           64         .darksquarehi = { 0x4f, 0xa1, 0x8e },
           65         .lightsquarehi = { 0x96, 0xd6, 0xd4 },
           66         .lightsquarecheck = { 0xff, 0x6a, 0x6a },
           67         .darksquarecheck = { 0xff, 0x3a, 0x3a  }
           68 },
           69 /* red / love theme */
           70 {
           71         .name = "love",
           72         .border = { 0x33, 0x33, 0x33 },
           73         .darksquare = { 0xd9, 0x4c, 0x4c },
           74         .lightsquare = { 0xff, 0xca, 0xca },
           75         .darksquarehi = { 0xaa, 0xa2, 0x3a },
           76         .lightsquarehi = { 0xcd, 0xd2, 0x6a },
           77         .lightsquarecheck = { 0xff, 0x6a, 0x6a },
           78         .darksquarecheck = { 0xff, 0x3a, 0x3a  }
           79 },
           80 /* greyscale theme, highlight is still green though */
           81 {
           82         .name = "grey",
           83         .border = { 0x00, 0x00, 0x00 },
           84         .darksquare = { 0x66, 0x66, 0x66 },
           85         .lightsquare = { 0xaa, 0xaa, 0xaa },
           86         .darksquarehi = { 0x66, 0x61, 0x23 },
           87         .lightsquarehi = { 0xa8, 0xab, 0x55 },
           88         .lightsquarecheck = { 0xff, 0x6a, 0x6a },
           89         .darksquarecheck = { 0xff, 0x3a, 0x3a  }
           90 },
           91 /* print theme, highlight is still green though */
           92 {
           93         .name = "print",
           94         .border = { 0x00, 0x00, 0x00 },
           95         .darksquare = { 0xcc, 0xcc, 0xcc },
           96         .lightsquare = { 0xff, 0xff, 0xff },
           97         .darksquarehi = { 0xbb, 0xbb, 0xbb },
           98         .lightsquarehi = { 0xdd, 0xdd, 0xdd },
           99         .lightsquarecheck = { 0x77, 0x77, 0x77 },
          100         .darksquarecheck = { 0x77, 0x77, 0x77  }
          101 }
          102 };
          103 
          104 struct board {
          105         char tiles[8][8];        /* board tiles and piece placement */
          106         int enpassantsquare[2];  /* default: no: { -1, -1 } */
          107 
          108         char highlight[8][8];    /* highlighted squares, (0 = none, 1 = highlight, 2 = check or mate) */
          109 
          110         int side_to_move;        /* default: white to move: 'w' */
          111         int white_can_castle[2]; /* allow king side, allow queen side? default: { 0, 0 } */
          112         int black_can_castle[2]; /* allow king side, allow queen side? default: { 0, 0 } */
          113 
          114         int movenumber;          /* default: 1 */
          115         int halfmove;            /* default: 0 */
          116 
          117         int flipboard;           /* flip board ? default: 0 */
          118         int showcoords;          /* board coordinates? default: 1 */
          119         int showside;            /* show indicator for which side to move: default: 1 */
          120         int highlights;          /* highlight moves and checks? default: 1 */
          121         struct theme *theme;     /* board theme */
          122 };
          123 
          124 /* set theme by name */
          125 struct theme *
          126 board_set_theme(struct board *b, const char *name)
          127 {
          128         int i;
          129 
          130         for (i = 0; i < LEN(themes); i++) {
          131                 if (!strcmp(themes[i].name, name)) {
          132                         b->theme = &themes[i];
          133                         return b->theme;
          134                 }
          135         }
          136         return NULL;
          137 }
          138 
          139 /* initialize board and set sane defaults */
          140 void
          141 board_init(struct board *b)
          142 {
          143         memset(b, 0, sizeof(*b)); /* zero fields by default */
          144         b->side_to_move = 'w'; /* white */
          145         b->enpassantsquare[0] = -1; /* no en passant */
          146         b->enpassantsquare[1] = -1;
          147         b->movenumber = 1;
          148         b->flipboard = 0;
          149         b->showcoords = 1;
          150         b->showside = 1;
          151         b->highlights = 1;
          152         b->theme = &themes[0]; /* use first theme as default */
          153 }
          154 
          155 /* copy entire board and its state */
          156 void
          157 board_copy(struct board *bd, struct board *bs)
          158 {
          159         memcpy(bd, bs, sizeof(*bd));
          160 }
          161 
          162 int
          163 isvalidsquare(int x, int y)
          164 {
          165         return !(x < 0 || x >= 8 || y < 0 || y >= 8);
          166 }
          167 
          168 int
          169 iswhitepiece(int piece)
          170 {
          171         return piece == 'K' || piece == 'Q' || piece == 'R' ||
          172                piece == 'B' || piece == 'N' || piece == 'P';
          173 }
          174 
          175 int
          176 isblackpiece(int piece)
          177 {
          178         return piece == 'k' || piece == 'q' || piece == 'r' ||
          179                piece == 'b' || piece == 'n' || piece == 'p';
          180 }
          181 
          182 int
          183 isvalidpiece(int c)
          184 {
          185         static char pieces[] = "PNBRQKpnbrqk";
          186 
          187         return strchr(pieces, c) ? 1 : 0;
          188 }
          189 
          190 int
          191 xtofile(int c)
          192 {
          193         return 'a' + c;
          194 }
          195 
          196 int
          197 ytorank(int c)
          198 {
          199         return '8' - c;
          200 }
          201 
          202 int
          203 filetox(int c)
          204 {
          205         return c - 'a';
          206 }
          207 
          208 int
          209 ranktoy(int c)
          210 {
          211         return '8' - c;
          212 }
          213 
          214 int
          215 squaretoxy(const char *s, int *x, int *y)
          216 {
          217         if (*s >= 'a' && *s <= 'h' &&
          218             *(s + 1) >= '1' && *(s + 1) <= '8') {
          219                 *x = filetox(*s);
          220                 *y = ranktoy(*(s + 1));
          221                 return 1;
          222         }
          223         return 0;
          224 }
          225 
          226 /* write formatted string, only if output mode is ModePGN */
          227 void
          228 pgn(const char *fmt, ...)
          229 {
          230         va_list ap;
          231 
          232         if (outputmode != ModePGN || silent)
          233                 return;
          234 
          235         va_start(ap, fmt);
          236         vprintf(fmt, ap);
          237         va_end(ap);
          238 }
          239 
          240 /* write formatted string, only if output mode is ModeSpeak */
          241 void
          242 speak(const char *fmt, ...)
          243 {
          244         va_list ap;
          245 
          246         if (outputmode != ModeSpeak || silent)
          247                 return;
          248 
          249         va_start(ap, fmt);
          250         vprintf(fmt, ap);
          251         va_end(ap);
          252 }
          253 
          254 /* remap letter for PGN pieces, default: "KQRBN"
          255    Dutch: (K)oning, (D)ame, (T)oren, (L)oper, (P)aard: "KDTLP" */
          256 int
          257 pgnpiece(int piece)
          258 {
          259         piece = TOUPPER(piece);
          260 
          261         /* no mapping */
          262         if (!pgn_piecemapping[0])
          263                 return piece;
          264 
          265         switch (piece) {
          266         case 'K': piece = pgn_piecemapping[0]; break;
          267         case 'Q': piece = pgn_piecemapping[1]; break;
          268         case 'R': piece = pgn_piecemapping[2]; break;
          269         case 'B': piece = pgn_piecemapping[3]; break;
          270         case 'N': piece = pgn_piecemapping[4]; break;
          271         }
          272 
          273         return piece;
          274 }
          275 
          276 void
          277 speakpiece(int piece)
          278 {
          279         switch (piece) {
          280         case 'K': case 'k': speak(dutchmode ? "koning " : "king "); break;
          281         case 'Q': case 'q': speak(dutchmode ? "dame " : "queen "); break;
          282         case 'R': case 'r': speak(dutchmode ? "toren " : "rook "); break;
          283         case 'B': case 'b': speak(dutchmode ? "loper " : "bishop "); break;
          284         case 'N': case 'n': speak(dutchmode ? "paard " : "knight "); break;
          285         case 'P': case 'p': speak(dutchmode ? "pion " : "pawn "); break;
          286         }
          287 }
          288 
          289 /* place a piece, if possible */
          290 void
          291 place(struct board *b, int piece, int x, int y)
          292 {
          293         if (!isvalidsquare(x, y))
          294                 return;
          295 
          296         b->tiles[y][x] = piece;
          297 }
          298 
          299 /* get piece, if possible */
          300 int
          301 getpiece(struct board *b, int x, int y)
          302 {
          303         if (!isvalidsquare(x, y))
          304                 return 0;
          305         return b->tiles[y][x];
          306 }
          307 
          308 void
          309 highlightmove(struct board *b, int x, int y)
          310 {
          311         if (isvalidsquare(x, y))
          312                 b->highlight[y][x] = 1;
          313 }
          314 
          315 void
          316 highlightcheck(struct board *b, int x, int y)
          317 {
          318         if (isvalidsquare(x, y))
          319                 b->highlight[y][x] = 2;
          320 }
          321 
          322 Color *
          323 getsquarecolor(struct board *b, int x, int y, int invert)
          324 {
          325         struct theme *t;
          326 
          327         t = b->theme;
          328         if (((x % 2) ^ (y % 2)) == invert) {
          329                 switch (b->highlight[y][x]) {
          330                 case 1: return t->lightsquarehi;
          331                 case 2: return t->lightsquarecheck;
          332                 default: return t->lightsquare;
          333                 }
          334         } else {
          335                 switch (b->highlight[y][x]) {
          336                 case 1: return t->darksquarehi;
          337                 case 2: return t->darksquarecheck;
          338                 default: return t->darksquare;
          339                 }
          340         }
          341         return t->lightsquare; /* never happens */
          342 }
          343 
          344 void
          345 showboardfen(struct board *b)
          346 {
          347         int x, y, piece, skip;
          348 
          349         for (y = 0; y < 8; y++) {
          350                 if (y > 0)
          351                         putchar('/');
          352                 skip = 0;
          353                 for (x = 0; x < 8; x++) {
          354                         piece = getpiece(b, x, y);
          355                         if (piece) {
          356                                 if (skip)
          357                                         putchar(skip + '0');
          358                                 putchar(piece);
          359                                 skip = 0;
          360                         } else {
          361                                 skip++;
          362                         }
          363                 }
          364                 if (skip)
          365                         putchar(skip + '0');
          366         }
          367         printf(" %c ", b->side_to_move);
          368         if (b->white_can_castle[0])
          369                 putchar('K');
          370         if (b->white_can_castle[1])
          371                 putchar('Q');
          372         if (b->black_can_castle[0])
          373                 putchar('k');
          374         if (b->black_can_castle[1])
          375                 putchar('q');
          376         if ((b->white_can_castle[0] + b->white_can_castle[1] +
          377             b->black_can_castle[0] + b->black_can_castle[1]) == 0)
          378                 putchar('-'); /* no castling for either side */
          379         putchar(' ');
          380 
          381         if (b->enpassantsquare[0] != -1 && b->enpassantsquare[1] != -1) {
          382                 putchar(xtofile(b->enpassantsquare[0]));
          383                 putchar(ytorank(b->enpassantsquare[1]));
          384         } else {
          385                 putchar('-');
          386         }
          387         printf(" %d %d\n", b->halfmove, b->movenumber);
          388 }
          389 
          390 void
          391 showpiece_svg(int c)
          392 {
          393         const char *s = "";
          394 
          395         /* lichess default set,
          396            extracted from https://github.com/lichess-org/lila/tree/master/public/piece/cburnett */
          397         switch (c) {
          398         case 'K': s = "<use href=\"#wk\"/>"; break;
          399         case 'Q': s = "<use href=\"#wq\"/>"; break;
          400         case 'R': s = "<use href=\"#wr\"/>"; break;
          401         case 'B': s = "<use href=\"#wb\"/>"; break;
          402         case 'N': s = "<use href=\"#wn\"/>"; break;
          403         case 'P': s = "<use href=\"#pawn\" fill=\"#fff\"/>"; break;
          404         case 'k': s = "<use href=\"#bk\"/>"; break;
          405         case 'q': s = "<use href=\"#bq\"/>"; break;
          406         case 'r': s = "<use href=\"#br\"/>"; break;
          407         case 'b': s = "<use href=\"#bb\"/>"; break;
          408         case 'n': s = "<use href=\"#bn\"/>"; break;
          409         case 'p': s = "<use href=\"#pawn\" fill=\"#000\"/>"; break;
          410         }
          411 
          412         if (*s)
          413                 fputs(s, stdout);
          414 }
          415 
          416 void
          417 output_svg(struct board *b)
          418 {
          419         Color *color;
          420         const char *s;
          421         char pieces[] = "pPKQRBNkqrbn"; /* pieces, check if they are used for definitions */
          422         unsigned char pieceused[LEN("pPKQRBNkqrbn")] = { 0 };
          423         int i, ix, iy, x, y, piece;
          424 
          425         fputs("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
          426                 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
          427                 "<svg width=\"360\" height=\"360\" viewBox=\"0 0 360 360\" xmlns=\"http://www.w3.org/2000/svg\">\n"
          428                 "<rect fill=\"#fff\" stroke=\"#000\" x=\"0\" y=\"0\" width=\"360\" height=\"360\"/>\n", stdout);
          429 
          430         for (i = 0; i < LEN(pieces); i++) {
          431                 for (y = 0; y < 8 && !pieceused[i]; y++) {
          432                         for (x = 0; x < 8; x++) {
          433                                 if (getpiece(b, x, y) == pieces[i]) {
          434                                         pieceused[i] = 1;
          435                                         break;
          436                                 }
          437                         }
          438                 }
          439         }
          440 
          441         fputs("<defs>\n", stdout);
          442         for (i = 0; i < LEN(pieces); i++) {
          443                 if (!pieceused[i])
          444                         continue;
          445                 s = NULL;
          446                 switch (pieces[i]) {
          447                 case 'K': s  ="<g id=\"wk\" 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>\n"; break;
          448                 case 'Q': s = "<g id=\"wq\" 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>\n"; break;
          449                 case 'R': s = "<g id=\"wr\" 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>\n"; break;
          450                 case 'B': s = "<g id=\"wb\" 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>\n"; break;
          451                 case 'N': s = "<g id=\"wn\" 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>\n"; break;
          452                 case 'P':
          453                 case 'p':
          454                         s = "<path id=\"pawn\" 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\"/>\n";
          455                         pieceused[0] = pieceused[1] = 0; /* unset used, only output pawn once */
          456                         break;
          457                 case 'k': s = "<g id=\"bk\" 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>\n"; break;
          458                 case 'q': s = "<g id=\"bq\" 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>\n"; break;
          459                 case 'r': s = "<g id=\"br\" 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>\n"; break;
          460                 case 'b': s = "<g id=\"bb\" 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>\n"; break;
          461                 case 'n': s = "<g id=\"bn\" 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>\n"; break;
          462                 default:  break;
          463                 }
          464                 if (s)
          465                         fputs(s, stdout);
          466         }
          467         fputs("</defs>\n", stdout);
          468 
          469         for (iy = 0; iy < 8; iy++) {
          470                 y = b->flipboard ? 7 - iy : iy;
          471 
          472                 for (ix = 0; ix < 8; ix++) {
          473                         x = b->flipboard ? 7 - ix : ix;
          474                         color = getsquarecolor(b, x, y, 0);
          475 
          476                         printf("<g><rect x=\"%d\" y=\"%d\" width=\"45\" height=\"45\" fill=\"#%02x%02x%02x\"/></g>\n",
          477                                 ix * 45, iy * 45, color[0], color[1], color[2]);
          478 
          479                         piece = getpiece(b, x, y);
          480                         if (piece) {
          481                                 printf("<g transform=\"translate(%d %d)\">", ix * 45, iy * 45);
          482                                 showpiece_svg(piece);
          483                                 fputs("</g>\n", stdout);
          484                         }
          485                 }
          486         }
          487 
          488         if (b->showcoords) {
          489                 ix = 7;
          490                 x = b->flipboard ? 0 : 7;
          491                 for (iy = 0; iy < 8; iy++) {
          492                         y = b->flipboard ? 7 - iy : iy;
          493                         /* inverse square color for text */
          494                         color = getsquarecolor(b, x, y, 1);
          495 
          496                         printf("<text x=\"%d\" y=\"%d\" fill=\"#%02x%02x%02x\" text-anchor=\"end\" style=\"font-family: sans-serif; font-size: 10px\">%c</text>\n",
          497                                 (ix + 1) * 45 - 2, (iy * 45) + 10, color[0], color[1], color[2], ytorank(y));
          498                 }
          499                 iy = 7;
          500                 y = b->flipboard ? 0 : 7;
          501                 for (ix = 0; ix < 8; ix++) {
          502                         x = b->flipboard ? 7 - ix : ix;
          503                         /* inverse square color for text */
          504                         color = getsquarecolor(b, x, y, 1);
          505 
          506                         printf("<text x=\"%d\" y=\"%d\" fill=\"#%02x%02x%02x\" text-anchor=\"start\" style=\"font-family: sans-serif; font-size: 10px\">%c</text>\n",
          507                                 (ix * 45) + 2, (iy + 1) * 45 - 3, color[0], color[1], color[2], xtofile(x));
          508                 }
          509         }
          510 
          511         if (b->showside) {
          512                 /* circle indicator for which side to move */
          513                 fputs("<circle cx=\"354\" stroke-width=\"1\" r=\"5\" fill=\"", stdout);
          514                 if (b->side_to_move == 'w') {
          515                         fputs("white\" stroke=\"black\" cy=\"", stdout);
          516                         printf("%d", b->flipboard ? 6 : 354);
          517                 } else {
          518                         fputs("black\" stroke=\"white\" cy=\"", stdout);
          519                         printf("%d", b->flipboard ? 354 : 6);
          520                 }
          521                 fputs("\"></circle>", stdout);
          522         }
          523 
          524         fputs("</svg>\n", stdout);
          525 }
          526 
          527 void
          528 showpiece_tty(int c)
          529 {
          530         const char *s = "";
          531 
          532         /* unicode characters */
          533         switch (c) {
          534         case 'K': s = "♔"; break;
          535         case 'Q': s = "♕"; break;
          536         case 'R': s = "♖"; break;
          537         case 'B': s = "♗"; break;
          538         case 'N': s = "♘"; break;
          539         case 'P': s = "♙"; break;
          540         case 'k': s = "♚"; break;
          541         case 'q': s = "♛"; break;
          542         case 'r': s = "♜"; break;
          543         case 'b': s = "♝"; break;
          544         case 'n': s = "♞"; break;
          545         case 'p': s = "♟"; break;
          546         }
          547 
          548         if (*s)
          549                 fputs(s, stdout);
          550 }
          551 
          552 /* show board */
          553 void
          554 output_tty(struct board *b)
          555 {
          556         struct theme *t;
          557         Color *color;
          558         int ix, iy, x, y, piece;
          559 
          560         t = b->theme;
          561 
          562         SETBGCOLOR(t->border[0], t->border[1], t->border[2]);
          563         fputs("                           ", stdout);
          564         if (b->showside) {
          565                 if (b->side_to_move == 'w' && b->flipboard)
          566                         printf("\x1b[30;47m%c", b->side_to_move);
          567                 else if (b->side_to_move == 'b' && !b->flipboard)
          568                         printf("\x1b[37;40m%c", b->side_to_move);
          569                 else
          570                         putchar(' ');
          571         } else {
          572                 putchar(' ');
          573         }
          574         printf("\x1b[0m"); /* reset */
          575         putchar('\n');
          576 
          577         for (iy = 0; iy < 8; iy++) {
          578                 y = b->flipboard ? 7 - iy : iy;
          579 
          580                 SETBGCOLOR(t->border[0], t->border[1], t->border[2]);
          581                 fputs("\x1b[97m", stdout); /* bright white */
          582                 fputs("  ", stdout);
          583 
          584                 for (ix = 0; ix < 8; ix++) {
          585                         x = b->flipboard ? 7 - ix : ix;
          586                         color = getsquarecolor(b, x, y, 0);
          587                         SETBGCOLOR(color[0], color[1], color[2]);
          588 
          589                         fputs(" ", stdout);
          590                         piece = getpiece(b, x, y);
          591                         if (piece) {
          592                                 if (piece >= 'A' && piece <= 'Z')
          593                                         fputs("\x1b[97m", stdout); /* bright white */
          594                                 else
          595                                         fputs("\x1b[30m", stdout); /* black */
          596                                 /* workaround: use black unicode chess symbol, because
          597                                    the color is filled and better visible */
          598                                 showpiece_tty(TOLOWER(piece));
          599                         } else {
          600                                 fputs(" ", stdout);
          601                         }
          602                         fputs(" ", stdout);
          603                 }
          604                 printf("\x1b[0m"); /* reset */
          605 
          606                 color = t->border;
          607                 SETBGCOLOR(color[0], color[1], color[2]);
          608                 if (b->showcoords) {
          609                         fputs("\x1b[97m", stdout); /* bright white */
          610                         putchar(ytorank(y));
          611                         putchar(' ');
          612                 } else {
          613                         fputs(" ", stdout);
          614                 }
          615 
          616                 printf("\x1b[0m"); /* reset */
          617                 putchar('\n');
          618         }
          619         color = t->border;
          620         SETBGCOLOR(color[0], color[1], color[2]);
          621         fputs("\x1b[97m", stdout); /* bright white */
          622         if (b->showcoords) {
          623                 fputs("   ", stdout);
          624                 for (iy = 0; iy < 8; iy++) {
          625                         y = b->flipboard ? 7 - iy : iy;
          626                         putchar(xtofile(y));
          627                         fputs("  ", stdout);
          628                 }
          629         } else {
          630                 fputs("                           ", stdout);
          631         }
          632 
          633         if (b->showside) {
          634                 if (b->side_to_move == 'w' && !b->flipboard)
          635                         printf("\x1b[30;47m%c", b->side_to_move);
          636                 else if (b->side_to_move == 'b' && b->flipboard)
          637                         printf("\x1b[37;40m%c", b->side_to_move);
          638                 else
          639                         putchar(' ');
          640         } else {
          641                 putchar(' ');
          642         }
          643 
          644         printf("\x1b[0m"); /* reset */
          645         printf("\n");
          646 }
          647 
          648 void
          649 showpiece_ascii(int c)
          650 {
          651         putchar(c);
          652 }
          653 
          654 /* OnlyFENs */
          655 void
          656 output_fen(struct board *b)
          657 {
          658         showboardfen(b);
          659 }
          660 
          661 /* show board */
          662 void
          663 output_ascii(struct board *b)
          664 {
          665         unsigned char hi[3] = { '>', ' ', '<' };
          666         unsigned char dark[3] = { '.', '.', '.' };
          667         unsigned char light[3] = { ' ', ' ', ' ' };
          668         unsigned char *color;
          669         int ix, iy, x, y, piece;
          670 
          671         for (iy = 0; iy < 8; iy++) {
          672                 y = b->flipboard ? 7 - iy : iy;
          673 
          674                 fputs("+---+---+---+---+---+---+---+---+\n", stdout);
          675                 for (ix = 0; ix < 8; ix++) {
          676                         x = b->flipboard ? 7 - ix : ix;
          677 
          678                         if (((x % 2) ^ (y % 2)) == 0)
          679                                 color = b->highlight[y][x] ? hi : light;
          680                         else
          681                                 color = b->highlight[y][x] ? hi : dark;
          682 
          683                         if (ix == 0)
          684                                 putchar('|');
          685                         putchar(color[0]);
          686                         piece = getpiece(b, x, y);
          687                         if (piece)
          688                                 showpiece_ascii(piece);
          689                         else
          690                                 putchar(color[1]);
          691                         putchar(color[2]);
          692                         putchar('|');
          693                 }
          694                 if (b->showcoords) {
          695                         putchar(' ');
          696                         putchar(ytorank(y));
          697                 }
          698                 putchar('\n');
          699         }
          700         fputs("+---+---+---+---+---+---+---+---+\n", stdout);
          701         if (b->showcoords) {
          702                 fputs("  ", stdout);
          703                 for (iy = 0; iy < 8; iy++) {
          704                         if (iy)
          705                                 fputs(" | ", stdout);
          706                         y = b->flipboard ? 7 - iy : iy;
          707                         putchar(xtofile(y));
          708                 }
          709                 fputs(" |\n", stdout);
          710         }
          711 
          712         fputs("\n", stdout);
          713 }
          714 
          715 int
          716 findking(struct board *b, int side, int *kingx, int *kingy)
          717 {
          718         int king, x, y;
          719 
          720         king = side == 'w' ? 'K' : 'k';
          721         *kingx = -1;
          722         *kingy = -1;
          723 
          724         /* find king */
          725         for (y = 0; y < 8; y++) {
          726                 for (x = 0; x < 8; x++) {
          727                         if (getpiece(b, x, y) == king) {
          728                                 *kingx = x;
          729                                 *kingy = y;
          730                                 return 1;
          731                         }
          732                 }
          733         }
          734         return 0;
          735 }
          736 
          737 int
          738 isenpassantplayed(struct board *b, int side, int x, int y)
          739 {
          740         if (side == 'w') {
          741                 return (getpiece(b, x - 1, y) == 'p' ||
          742                         getpiece(b, x + 1, y) == 'p');
          743         } else if (side == 'b') {
          744                 return (getpiece(b, x - 1, y) == 'P' ||
          745                         getpiece(b, x + 1, y) == 'P');
          746         }
          747         return 0;
          748 }
          749 
          750 int
          751 isincheck(struct board *b, int side)
          752 {
          753         int king[]   = { -1, -1, -1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 0, 1, 1 };
          754         int diag[]   = { -1, -1,  1, 1, -1, 1, 1, -1 };
          755         int line[]   = {  1,  0,  0, 1, -1, 0, 0, -1 };
          756         int knight[] = { -1, -2,  1, -2, -1, 2, 1, 2,
          757                          -2, -1,  2, -1, -2, 1, 2, 1
          758                        };
          759         int i, j, x, y;
          760         int kingx, kingy;
          761         int piece;
          762 
          763         /* find our king */
          764         if (!findking(b, side, &kingx, &kingy))
          765                 return 0; /* should not happen */
          766 
          767         /* check kings (illegal) */
          768         for (j = 0; j < LEN(king); j += 2) {
          769                 x = kingx + king[j];
          770                 y = kingy + king[j + 1];
          771                 piece = getpiece(b, x, y);
          772                 if (piece && strchr("kK", piece))
          773                         return 1;
          774         }
          775 
          776         /* check files and ranks (for queen and rook) */
          777         for (j = 0; j < LEN(line); j += 2) {
          778                 for (i = 1; i < 8; i++) {
          779                         x = kingx + (i * line[j]);
          780                         y = kingy + (i * line[j + 1]);
          781                         if (!(piece = getpiece(b, x, y)))
          782                                 continue;
          783                         /* a piece is in front of it */
          784                         if (piece && strchr("kKbBnNpP", piece))
          785                                 break;
          786                         /* own piece blocking/defending it */
          787                         if ((side == 'w' && iswhitepiece(piece)) ||
          788                             (side == 'b' && isblackpiece(piece)))
          789                                 break;
          790                         return 1;
          791                 }
          792         }
          793 
          794         /* check diagonals (queen and bishop) */
          795         for (j = 0; j < LEN(diag); j += 2) {
          796                 for (i = 1; i < 8; i++) {
          797                         x = kingx + (i * diag[j]);
          798                         y = kingy + (i * diag[j + 1]);
          799                         if (!(piece = getpiece(b, x, y)))
          800                                 continue;
          801                         /* a piece is in front of it */
          802                         if (piece && strchr("kKrRnNpP", piece))
          803                                 break;
          804                         /* own piece blocking/defending it */
          805                         if ((side == 'w' && iswhitepiece(piece)) ||
          806                             (side == 'b' && isblackpiece(piece)))
          807                                 break;
          808                         return 1;
          809                 }
          810         }
          811 
          812         /* check knights */
          813         piece = side == 'w' ? 'n' : 'N';
          814         for (j = 0; j < LEN(knight); j += 2) {
          815                 x = kingx + knight[j];
          816                 y = kingy + knight[j + 1];
          817                 if (getpiece(b, x, y) == piece)
          818                         return 1;
          819         }
          820 
          821         /* check pawns */
          822         if (side == 'w') {
          823                 if (getpiece(b, kingx - 1, kingy - 1) == 'p' ||
          824                     getpiece(b, kingx + 1, kingy - 1) == 'p')
          825                         return 1;
          826         } else if (side == 'b') {
          827                 if (getpiece(b, kingx - 1, kingy + 1) == 'P' ||
          828                     getpiece(b, kingx + 1, kingy + 1) == 'P')
          829                         return 1;
          830         }
          831 
          832         return 0;
          833 }
          834 
          835 /* copy the board state and try the piece move, see if the piece wouldn't put
          836    ourself in check */
          837 int
          838 trypiecemove(struct board *b, int side, int piece,
          839              int x1, int y1, int x2, int y2, int px, int py)
          840 {
          841         struct board tb;
          842 
          843         board_copy(&tb, b);
          844 
          845         /* taken en passant? remove pawn */
          846         if (x2 == px && y2 == py)
          847                 place(&tb, 0, x2, piece == 'P' ? y2 + 1 : y2 - 1);
          848 
          849         place(&tb, 0, x1, y1);
          850         place(&tb, piece, x2, y2);
          851 
          852         /* would put in check / still in check, not allowed */
          853         return !isincheck(&tb, side);
          854 }
          855 
          856 /* can piece move from (x, y), to (x, y)?
          857    en passant square if any is (px, py), otherwise (-1, -1) */
          858 int
          859 canpiecemove(struct board *b, int side, int piece,
          860              int x1, int y1, int x2, int y2, int px, int py)
          861 {
          862         int king[]   = { -1, -1, -1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 0, 1, 1 };
          863         int diag[]   = { -1, -1,  1, 1, -1, 1, 1, -1 };
          864         int line[]   = {  1,  0,  0, 1, -1, 0, 0, -1 };
          865         int knight[] = { -1, -2,  1, -2, -1, 2, 1, 2,
          866                          -2, -1,  2, -1, -2, 1, 2, 1
          867                        };
          868         int i, j, dir, x, y;
          869         int takepiece;
          870 
          871         if (!piece)
          872                 return 0; /* theres no piece so it cannot be moved */
          873 
          874         /* can't move opponent piece */
          875         if ((side == 'w' && isblackpiece(piece)) ||
          876             (side == 'b' && iswhitepiece(piece)))
          877                 return 0;
          878 
          879         if ((takepiece = getpiece(b, x2, y2))) {
          880                 /* can't take your own piece */
          881                 if ((side == 'w' && iswhitepiece(takepiece)) ||
          882                     (side == 'b' && isblackpiece(takepiece)))
          883                         return 0;
          884         }
          885 
          886         /* king movement */
          887         if (piece == 'K' || piece == 'k') {
          888                 for (j = 0; j < LEN(king); j += 2) {
          889                         if (x1 + king[j] == x2 && y1 + king[j + 1] == y2)
          890                                 goto trymove;
          891                 }
          892         }
          893 
          894         /* check files and ranks (for queen and rook) */
          895         if (piece == 'Q' || piece == 'q' || piece == 'R' || piece == 'r') {
          896                 for (j = 0; j < LEN(line); j += 2) {
          897                         for (i = 1; i < 8; i++) {
          898                                 x = x1 + (i * line[j]);
          899                                 y = y1 + (i * line[j + 1]);
          900 
          901                                 if (x == x2 && y == y2 &&
          902                                     trypiecemove(b, side, piece, x1, y1, x2, y2, px, py))
          903                                         return 1;
          904 
          905                                 /* a piece is in front of it: stop this checking this direction */
          906                                 if (getpiece(b, x, y))
          907                                         break;
          908                         }
          909                 }
          910         }
          911 
          912         /* check diagonals (queen and bishop) */
          913         if (piece == 'Q' || piece == 'q' || piece == 'B' || piece == 'b') {
          914                 for (j = 0; j < LEN(diag); j += 2) {
          915                         for (i = 1; i < 8; i++) {
          916                                 x = x1 + (i * diag[j]);
          917                                 y = y1 + (i * diag[j + 1]);
          918                                 if (x == x2 && y == y2 &&
          919                                     trypiecemove(b, side, piece, x1, y1, x2, y2, px, py))
          920                                         return 1;
          921 
          922                                 /* a piece is in front of it: stop this checking this direction */
          923                                 if (getpiece(b, x, y))
          924                                         break;
          925                         }
          926                 }
          927         }
          928 
          929         /* knight movement */
          930         if (piece == 'N' || piece == 'n') {
          931                 for (j = 0; j < LEN(knight); j += 2) {
          932                         if (x1 + knight[j] == x2 && y1 + knight[j + 1] == y2)
          933                                 goto trymove;
          934                 }
          935         }
          936 
          937         /* pawn move */
          938         if (piece == 'P' || piece == 'p') {
          939                 /* direction */
          940                 dir = piece == 'P' ? -1 : +1;
          941                 j = piece == 'P' ? 6 : 1; /* start row */
          942 
          943                 /* can move to en passant square? */
          944                 /* en passant set? try it if possible */
          945                 if (px == x2 && py == y2 &&
          946                     (py == y1 + dir) &&
          947                     ((px == x1 - 1) || px == x1 + 1)) {
          948                         if (isenpassantplayed(b, side == 'w' ? 'b' : 'w', px, y1))
          949                                 return trypiecemove(b, side, piece, x1, y1, x2, y2, px, py);
          950                 }
          951 
          952                 if (takepiece == 0) {
          953                         if (x1 != x2)
          954                                 return 0; /* move on same file */
          955                         /* start move: can be 2 moves */
          956                         if (y1 == j && y2 == y1 + (2 * dir)) {
          957                                 /* square must be empty */
          958                                 if (getpiece(b, x1, y1 + dir))
          959                                         return 0;
          960                                 goto trymove;
          961                         }
          962                         /* normal: check for one move */
          963                         if (y2 != y1 + dir)
          964                                 return 0;
          965                         goto trymove;
          966                 } else {
          967                         /* pawn takes, normal case */
          968                         if ((x2 == x1 - 1 || x2 == x1 + 1) &&
          969                             (y2 == y1 + dir))
          970                                 goto trymove;
          971                 }
          972         }
          973         return 0;
          974 
          975 /* previous checks for move succeeded, actually try move with the current
          976    board state */
          977 trymove:
          978         return trypiecemove(b, side, piece, x1, y1, x2, y2, px, py);
          979 }
          980 
          981 int
          982 ischeckmated(struct board *b, int side)
          983 {
          984         int x, y, x2, y2, px, py, piece;
          985 
          986         px = b->enpassantsquare[0];
          987         py = b->enpassantsquare[1];
          988 
          989         /* check pieces that can block or take a piece that removes the check */
          990         for (y = 0; y < 8; y++) {
          991                 for (x = 0; x < 8; x++) {
          992                         piece = getpiece(b, x, y);
          993                         if ((side == 'w' && !iswhitepiece(piece)) ||
          994                             (side == 'b' && !isblackpiece(piece)))
          995                                 continue;
          996 
          997                         for (y2 = 0; y2 < 8; y2++) {
          998                                 for (x2 = 0; x2 < 8; x2++) {
          999                                         /* can piece move and afterwards we are not in check? */
         1000                                         if (canpiecemove(b, side, piece, x, y, x2, y2, px, py))
         1001                                                 return 0;
         1002                                 }
         1003                         }
         1004                 }
         1005         }
         1006 
         1007         return 1;
         1008 }
         1009 
         1010 void
         1011 board_setup_fen(struct board *b, const char *fen)
         1012 {
         1013         char square[3];
         1014         const char *s;
         1015         long l;
         1016         int x, y, field;
         1017 
         1018         if (!strcmp(fen, "startpos"))
         1019                 fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
         1020 
         1021         square[2] = '\0';
         1022 
         1023         /* initial board state, FEN format */
         1024         x = y = field = 0;
         1025         for (s = fen; *s && field < 6; s++) {
         1026                 switch (field) {
         1027                 case 0: /* piece placement data */
         1028                         /* skip square */
         1029                         if (*s >= '1' && *s <= '9') {
         1030                                 x += (*s - '0');
         1031                                 continue;
         1032                         }
         1033                         /* next rank */
         1034                         if (*s == '/') {
         1035                                 x = 0;
         1036                                 y++;
         1037                                 continue;
         1038                         }
         1039                         /* is piece? place it */
         1040                         if (isvalidpiece(*s))
         1041                                 place(b, *s, x++, y);
         1042                         break;
         1043                 case 1: /* active color */
         1044                         if (*s == 'w' || *s == 'b')
         1045                                 b->side_to_move = *s;
         1046                         break;
         1047                 case 2: /* castling availability */
         1048                         if (*s == '-') {
         1049                                 b->white_can_castle[0] = 0;
         1050                                 b->white_can_castle[1] = 0;
         1051                                 b->black_can_castle[0] = 0;
         1052                                 b->black_can_castle[1] = 0;
         1053                         } else if (*s == 'K') {
         1054                                 b->white_can_castle[0] = 1;
         1055                         } else if (*s == 'Q') {
         1056                                 b->white_can_castle[1] = 1;
         1057                         } else if (*s == 'k') {
         1058                                 b->black_can_castle[0] = 1;
         1059                         } else if (*s == 'q') {
         1060                                 b->black_can_castle[1] = 1;
         1061                         }
         1062                         break;
         1063                 case 3: /* en passant square */
         1064                         if (*s >= 'a' && *s <= 'h' &&
         1065                                 *(s + 1) >= '1' && *(s + 1) <= '6') {
         1066                                 square[0] = *s;
         1067                                 square[1] = *(s + 1);
         1068                                 squaretoxy(square, &x, &y);
         1069 
         1070                                 b->enpassantsquare[0] = x;
         1071                                 b->enpassantsquare[1] = y;
         1072                         }
         1073                         break;
         1074                 case 4: /* halfmove */
         1075                         if (!(*s >= '0' && *s <= '9'))
         1076                                 continue;
         1077 
         1078                         l = strtol(s, NULL, 10);
         1079                         if (l >= 0 && l < 32767) {
         1080                                 b->halfmove = l;
         1081 
         1082                                 for (; *s && ISDIGIT((unsigned char)*s); s++)
         1083                                         ;
         1084                         }
         1085                         break;
         1086                 case 5: /* move number */
         1087                         if (!(*s >= '0' && *s <= '9'))
         1088                                 continue;
         1089 
         1090                         l = strtol(s, NULL, 10);
         1091                         if (l >= 0 && l < 32767) {
         1092                                 b->movenumber = (int)l;
         1093                                 for (; *s && ISDIGIT((unsigned char)*s); s++)
         1094                                         ;
         1095                         }
         1096                         break;
         1097                 }
         1098                 if (!*s)
         1099                         break;
         1100 
         1101                 /* next field, fields are: piece placement data, active color,
         1102                    Castling availability, En passant target square,
         1103                    Halfmove clock, Fullmove number */
         1104                 if (*s == ' ') {
         1105                         field++;
         1106                         continue;
         1107                 }
         1108         }
         1109 }
         1110 
         1111 /* count ambiguity for piece moves, used to make the notation shorter */
         1112 void
         1113 countambigousmoves(struct board *b, int side, int piece,
         1114         int x, int y, int x2, int y2, int px, int py,
         1115         int *countfile, int *countrank, int *countboard)
         1116 {
         1117         int cf = 0, cr = 0, cb = 0, i, j;
         1118 
         1119         /* check same file */
         1120         for (i = 0; i < 8; i++) {
         1121                 if (getpiece(b, i, y) == piece &&
         1122                     canpiecemove(b, side, piece, i, y, x2, y2, px, py))
         1123                         cf++;
         1124         }
         1125 
         1126         /* check same rank */
         1127         for (i = 0; i < 8; i++) {
         1128                 if (getpiece(b, x, i) == piece &&
         1129                     canpiecemove(b, side, piece, x, i, x2, y2, px, py))
         1130                         cr++;
         1131         }
         1132 
         1133         /* check whole board */
         1134         if (cf <= 1 && cr <= 1) {
         1135                 /* check the whole board if there is any piece
         1136                    that can move to the same square */
         1137                 for (i = 0; i < 8; i++) {
         1138                         for (j = 0; j < 8; j++) {
         1139                                 if (getpiece(b, i, j) == piece &&
         1140                                     canpiecemove(b, side, piece, i, j, x2, y2, px, py))
         1141                                         cb++;
         1142                         }
         1143                 }
         1144         }
         1145 
         1146         *countfile = cf;
         1147         *countrank = cr;
         1148         *countboard = cb;
         1149 }
         1150 
         1151 void
         1152 board_playmoves(struct board *b, const char *moves)
         1153 {
         1154         char square[3];
         1155         const char *castled, *s;
         1156         int firstmove, i, x, y, x2, y2, side, otherside, piece;
         1157         int rookpiece, takepiece, tookpiece;
         1158         int countfile, countrank, countboard, px, py;
         1159         int promote, tookeps;
         1160 
         1161         /* process moves */
         1162         square[2] = '\0';
         1163         x = y = x2 = y2 = -1;
         1164         firstmove = 1;
         1165         /* clear previous highlights */
         1166         memset(&(b->highlight), 0, sizeof(b->highlight));
         1167 
         1168         for (s = moves; *s; s++) {
         1169                 if (*s == ' ')
         1170                         continue;
         1171                 if (!((*s >= 'a' && *s <= 'h') &&
         1172                     (*(s + 1) >= '1' && *(s + 1) <= '8') &&
         1173                     (*(s + 2) >= 'a' && *(s + 2) <= 'h') &&
         1174                     (*(s + 3) >= '1' && *(s + 3) <= '8')))
         1175                         continue;
         1176 
         1177                 /* is last move in this sequence? */
         1178                 if (onlylastmove && !strchr(s, ' '))
         1179                         silent = 0;
         1180 
         1181                 side = b->side_to_move;
         1182                 otherside = side == 'b' ? 'w' : 'b';
         1183 
         1184                 /* if first move and it is blacks turn, prefix
         1185                    with "...", because the white move was unknown */
         1186                 if (!onlylastmove && firstmove && side == 'b')
         1187                         pgn("%d. ... ", b->movenumber);
         1188 
         1189                 if (firstmove && !silent) {
         1190                         firstmove = 0;
         1191                 } else {
         1192                         pgn(" ");
         1193                         speak("\n");
         1194                 }
         1195 
         1196                 square[0] = *s;
         1197                 square[1] = *(s + 1);
         1198 
         1199                 s += 2;
         1200                 squaretoxy(square, &x, &y);
         1201                 piece = getpiece(b, x, y);
         1202 
         1203                 /* target location */
         1204                 square[0] = *s;
         1205                 square[1] = *(s + 1);
         1206                 squaretoxy(square, &x2, &y2);
         1207 
         1208                 /* take piece (can be your own) */
         1209                 takepiece = getpiece(b, x2, y2);
         1210 
         1211                 s += 2;
         1212 
         1213                 promote = 0;
         1214                 /* is a valid piece? should be queen, rook, bishop, knight */
         1215                 if (isvalidpiece(*s)) {
         1216                         if (side == 'w')
         1217                                 promote = TOUPPER(*s);
         1218                         else
         1219                                 promote = TOLOWER(*s);
         1220                         s++;
         1221                 }
         1222 
         1223                 /* took piece of opponent */
         1224                 tookpiece = (side == 'w' && isblackpiece(takepiece)) ||
         1225                             (side == 'b' && iswhitepiece(takepiece));
         1226 
         1227                 /* if pawn move or taken a piece increase halfmove counter */
         1228                 if (piece == 'p' || piece == 'P' || tookpiece)
         1229                         b->halfmove = 0;
         1230                 else
         1231                         b->halfmove++;
         1232 
         1233                 if (!onlylastmove && side == 'w')
         1234                         pgn("%d. ", b->movenumber);
         1235 
         1236                 /* castled this move? */
         1237                 castled = NULL;
         1238 
         1239                 /* castling */
         1240                 if ((piece == 'K' && y == 7 && y2 == 7) ||
         1241                     (piece == 'k' && y == 0 && y2 == 0)) {
         1242                         rookpiece = piece == 'K' ? 'R' : 'r';
         1243 
         1244                         /* kingside castling */
         1245                         if (x2 > x + 1 || (x2 > x && takepiece == rookpiece)) {
         1246                                 castled = "O-O";
         1247                                 for (i = x; i < 8; i++) {
         1248                                         if (getpiece(b, i, y2) == rookpiece) {
         1249                                                 place(b, 0, x, y); /* clear previous square */
         1250                                                 place(b, 0, i, y2); /* clear rook square */
         1251                                                 place(b, rookpiece, 5, y2); /* rook next to king */
         1252                                                 place(b, piece, 6, y2); /* place king */
         1253                                                 x2 = i; /* update square for highlight */
         1254                                                 break;
         1255                                         }
         1256                                 }
         1257                         } else if (x2 < x - 1 || (x2 < x && takepiece == rookpiece)) {
         1258                                 /* queenside castling */
         1259                                 castled = "O-O-O";
         1260                                 for (i = x; i >= 0; i--) {
         1261                                         if (getpiece(b, i, y2) == rookpiece) {
         1262                                                 place(b, 0, x, y); /* clear previous square */
         1263                                                 place(b, 0, i, y2); /* clear rook square */
         1264                                                 place(b, rookpiece, 3, y2); /* rook next to king */
         1265                                                 place(b, piece, 2, y2); /* place king */
         1266                                                 x2 = i; /* update square for highlight */
         1267                                                 break;
         1268                                         }
         1269                                 }
         1270                         }
         1271                 }
         1272 
         1273                 /* remove the ability to castle */
         1274                 if (piece == 'K') {
         1275                         b->white_can_castle[0] = b->white_can_castle[1] = 0;
         1276                 } else if (piece == 'k') {
         1277                         b->black_can_castle[0] = b->black_can_castle[1] = 0;
         1278                 } else if (piece == 'R' && y == 7) {
         1279                         for (i = 0; i < 8; i++) {
         1280                                 if (getpiece(b, i, y) == 'K') {
         1281                                         if (i < x)
         1282                                                 b->white_can_castle[0] = 0;
         1283                                         else if (i > x)
         1284                                                 b->white_can_castle[1] = 0;
         1285                                         break;
         1286                                 }
         1287                         }
         1288                 } else if (piece == 'r' && y == 0) {
         1289                         for (i = 0; i < 8; i++) {
         1290                                 if (getpiece(b, i, y) == 'k') {
         1291                                         if (i > x)
         1292                                                 b->black_can_castle[1] = 0;
         1293                                         else if (i < x)
         1294                                                 b->black_can_castle[0] = 0;
         1295                                         break;
         1296                                 }
         1297                         }
         1298                 }
         1299 
         1300                 /* taken en passant? */
         1301                 tookeps = 0;
         1302                 if (x2 == b->enpassantsquare[0] && y2 == b->enpassantsquare[1] &&
         1303                     (piece == 'P' || piece == 'p')) {
         1304                         /* clear square */
         1305                         place(b, 0, x2, piece == 'P' ? y2 + 1 : y2 - 1);
         1306                         /* set a piece is taken */
         1307                         tookpiece = 1;
         1308                         takepiece = piece == 'P' ? 'p' : 'P';
         1309                         tookeps = 1;
         1310                 }
         1311 
         1312                 /* the en passant square resets after a move */
         1313                 px = b->enpassantsquare[0] = -1;
         1314                 py = b->enpassantsquare[1] = -1;
         1315 
         1316                 /* set en passant square:
         1317                    moved 2 squares and there is an opponent pawn next to it */
         1318                 if (piece == 'P' && y == 6 && y2 == 4) {
         1319                         if (isenpassantplayed(b, side, x, y2)) {
         1320                                 px = b->enpassantsquare[0] = x;
         1321                                 py = b->enpassantsquare[1] = 5;
         1322                         }
         1323                 } else if (piece == 'p' && y == 1 && y2 == 3) {
         1324                         if (isenpassantplayed(b, side, x, y2)) {
         1325                                 px = b->enpassantsquare[0] = x;
         1326                                 py = b->enpassantsquare[1] = 2;
         1327                         }
         1328                 }
         1329 
         1330                 /* PGN for move, if output is not PGN then skip this step */
         1331                 if (outputmode == ModePGN || outputmode == ModeSpeak) {
         1332                         if (castled) {
         1333                                 pgn("%s", castled);
         1334 
         1335                                 if (side == 'w')
         1336                                         speak(dutchmode ? "wit " : "white ");
         1337                                 else if (side == 'b')
         1338                                         speak(dutchmode ? "zwart " : "black ");
         1339 
         1340                                 if (!strcmp(castled, "O-O"))
         1341                                         speak(dutchmode ? "rokeert aan koningszijde " : "castled kingside ");
         1342                                 else
         1343                                         speak(dutchmode ? "rokeert aan damezijde " : "castled queenside ");
         1344                         } else {
         1345                                 if (side == 'w')
         1346                                         speak(dutchmode ? "witte " : "white ");
         1347                                 else if (side == 'b')
         1348                                         speak(dutchmode ? "zwarte " : "black ");
         1349 
         1350                                 if (!tookpiece) {
         1351                                         if (!dutchmode)
         1352                                                 speak("moves ");
         1353                                         speakpiece(piece);
         1354                                 }
         1355 
         1356                                 /* pawn move needs no notation */
         1357                                 if (piece != 'p' && piece != 'P') {
         1358                                         pgn("%c", pgnpiece(piece));
         1359 
         1360                                         /* check ambiguity for certain pieces and make the notation shorter */
         1361                                         countambigousmoves(b, side, piece, x, y, x2, y2, px, py,
         1362                                                 &countfile, &countrank, &countboard);
         1363 
         1364                                         if (countfile > 1 || countboard > 1) {
         1365                                                 pgn("%c", xtofile(x));
         1366                                                 speak("%c", xtofile(x));
         1367                                         }
         1368                                         if (countrank > 1) {
         1369                                                 pgn("%c", ytorank(y));
         1370                                                 speak("%c", ytorank(y));
         1371                                         }
         1372                                         if (countfile > 1 || countrank > 1 || countboard > 1)
         1373                                                 speak(" ");
         1374                                 }
         1375 
         1376                                 if (tookpiece) {
         1377                                         /* pawn captures are prefixed by the file letter (no more needed) */
         1378                                         if (piece == 'p' || piece == 'P')
         1379                                                 pgn("%c", xtofile(x));
         1380                                         pgn("x");
         1381                                         speakpiece(piece);
         1382                                         speak(dutchmode ? "slaat " : "takes ");
         1383                                         speakpiece(takepiece);
         1384                                         speak(dutchmode ? "op " : "on ");
         1385                                         speak("%c%c ", xtofile(x2), ytorank(y2));
         1386                                         if (tookeps)
         1387                                                 speak("en passant ");
         1388                                 } else {
         1389                                         speak(dutchmode ? "naar " : "to ");
         1390                                         speak("%c%c ", xtofile(x2), ytorank(y2));
         1391                                 }
         1392                                 pgn("%c%c", xtofile(x2), ytorank(y2));
         1393 
         1394                                 /* possible promotion: queen, rook, bishop, knight */
         1395                                 if (promote) {
         1396                                         speak(dutchmode ? "en promoot naar " : "and promotes to ");
         1397                                         speakpiece(promote);
         1398 
         1399                                         pgn("=%c", pgnpiece(promote));
         1400                                 }
         1401                         }
         1402                 }
         1403 
         1404                 /* clear previous square (if not castled) */
         1405                 if (!castled) {
         1406                         place(b, 0, x, y);
         1407                         /* place piece or new promoted piece */
         1408                         if (promote)
         1409                                 piece = promote;
         1410                         place(b, piece, x2, y2);
         1411                 }
         1412 
         1413                 if (ischeckmated(b, otherside)) {
         1414                         /* reset en passant square on checkmate */
         1415                         b->enpassantsquare[0] = -1;
         1416                         b->enpassantsquare[1] = -1;
         1417 
         1418                         pgn("#");
         1419                         speak(dutchmode ? "mat" : "checkmate");
         1420                 } else if (isincheck(b, otherside)) {
         1421                         pgn("+");
         1422                         speak(dutchmode ? "schaak" : "check");
         1423                 }
         1424 
         1425                 /* a move by black increases the move number */
         1426                 if (side == 'b')
         1427                         b->movenumber++;
         1428 
         1429                 /* switch which side it is to move */
         1430                 b->side_to_move = otherside;
         1431 
         1432                 if (!*s)
         1433                         break;
         1434         }
         1435 
         1436         if (!firstmove) {
         1437                 pgn("\n");
         1438                 speak("\n");
         1439         }
         1440 
         1441         /* highlight last move */
         1442         if (b->highlights) {
         1443                 highlightmove(b, x, y);
         1444                 highlightmove(b, x2, y2);
         1445 
         1446                 /* highlight king in check or mate */
         1447                 if (isincheck(b, b->side_to_move) &&
         1448                     findking(b, b->side_to_move, &x, &y))
         1449                         highlightcheck(b, x, y);
         1450         }
         1451 }
         1452 
         1453 void
         1454 usage(char *argv0)
         1455 {
         1456         fprintf(stderr, "usage: %s [-cCfFhH] [-l] [-m mapping] "
         1457                 "[-o ascii|fen|pgn|speak|svg|tty] [-sS] [-t default|green|grey] "
         1458                 "[FEN] [moves]\n", argv0);
         1459         exit(1);
         1460 }
         1461 
         1462 /* CGI: get parameter */
         1463 char *
         1464 getparam(const char *query, const char *s)
         1465 {
         1466         const char *p, *last = NULL;
         1467         size_t len;
         1468 
         1469         len = strlen(s);
         1470         for (p = query; (p = strstr(p, s)); p += len) {
         1471                 if (p[len] == '=' && (p == query || p[-1] == '&' || p[-1] == '?'))
         1472                         last = p + len + 1;
         1473         }
         1474 
         1475         return (char *)last;
         1476 }
         1477 
         1478 int
         1479 hexdigit(int c)
         1480 {
         1481         if (c >= '0' && c <= '9')
         1482                 return c - '0';
         1483         else if (c >= 'A' && c <= 'F')
         1484                 return c - 'A' + 10;
         1485         else if (c >= 'a' && c <= 'f')
         1486                 return c - 'a' + 10;
         1487 
         1488         return 0;
         1489 }
         1490 
         1491 /* CGI: decode until NUL separator or end of "key". */
         1492 int
         1493 decodeparam(char *buf, size_t bufsiz, const char *s)
         1494 {
         1495         size_t i;
         1496 
         1497         if (!bufsiz)
         1498                 return -1;
         1499 
         1500         for (i = 0; *s && *s != '&'; s++) {
         1501                 switch (*s) {
         1502                 case '%':
         1503                         if (i + 3 >= bufsiz)
         1504                                 return -1;
         1505                         if (!ISXDIGIT((unsigned char)*(s+1)) ||
         1506                             !ISXDIGIT((unsigned char)*(s+2)))
         1507                                 return -1;
         1508                         buf[i++] = hexdigit(*(s+1)) * 16 + hexdigit(*(s+2));
         1509                         s += 2;
         1510                         break;
         1511                 case '+':
         1512                         if (i + 1 >= bufsiz)
         1513                                 return -1;
         1514                         buf[i++] = ' ';
         1515                         break;
         1516                 default:
         1517                         if (i + 1 >= bufsiz)
         1518                                 return -1;
         1519                         buf[i++] = *s;
         1520                         break;
         1521                 }
         1522         }
         1523         buf[i] = '\0';
         1524 
         1525         return i;
         1526 }
         1527 
         1528 enum outputmode
         1529 outputnametomode(const char *s)
         1530 {
         1531         if (!strcmp(s, "ascii"))
         1532                 return ModeASCII;
         1533         else if (!strcmp(s, "fen"))
         1534                 return ModeFEN;
         1535         else if (!strcmp(s, "pgn"))
         1536                 return ModePGN;
         1537         else if (!strcmp(s, "speak"))
         1538                 return ModeSpeak;
         1539         else if (!strcmp(s, "svg"))
         1540                 return ModeSVG;
         1541         else if (!strcmp(s, "tty"))
         1542                 return ModeTTY;
         1543         else
         1544                 return ModeInvalid;
         1545 }
         1546 
         1547 void
         1548 output(struct board *b)
         1549 {
         1550         switch (outputmode) {
         1551         case ModeASCII: output_ascii(b); break;
         1552         case ModeFEN:   output_fen(b);   break;
         1553         case ModePGN:                    break; /* handled in parsemoves() */
         1554         case ModeSVG:   output_svg(b);   break;
         1555         case ModeTTY:   output_tty(b);   break;
         1556         default:        break;
         1557         }
         1558 }
         1559 
         1560 /* CGI mode */
         1561 int
         1562 cgi_mode(void)
         1563 {
         1564         struct board board;
         1565         char *query, *p;
         1566         char buf[4096];
         1567 
         1568         board_init(&board);
         1569 
         1570         query = getenv("QUERY_STRING");
         1571         if ((p = getparam(query, "flip")) && (*p == '0' || *p == '1'))
         1572                 board.flipboard = *p == '1' ? 1 : 0;
         1573         if ((p = getparam(query, "side")) && (*p == '0' || *p == '1'))
         1574                 board.showside = *p == '1' ? 1 : 0;
         1575         if ((p = getparam(query, "coords")) && (*p == '0' || *p == '1'))
         1576                 board.showcoords = *p == '1' ? 1 : 0;
         1577         if ((p = getparam(query, "dutch")) && *p == '1') {
         1578                 dutchmode = 1;
         1579                 pgn_piecemapping = "KDTLP";
         1580         }
         1581         if ((p = getparam(query, "output"))) {
         1582                 if (decodeparam(buf, sizeof(buf), p) == -1)
         1583                         goto badrequest;
         1584                 outputmode = outputnametomode(buf);
         1585                 if (outputmode == ModeInvalid)
         1586                         goto badrequest;
         1587         }
         1588         if ((p = getparam(query, "theme"))) {
         1589                 if (decodeparam(buf, sizeof(buf), p) == -1)
         1590                         goto badrequest;
         1591                 board_set_theme(&board, buf);
         1592         }
         1593         if ((p = getparam(query, "fen"))) {
         1594                 if (decodeparam(buf, sizeof(buf), p) == -1)
         1595                         goto badrequest;
         1596                 board_setup_fen(&board, buf);
         1597         } else {
         1598                 board_setup_fen(&board, "startpos");
         1599         }
         1600         if ((p = getparam(query, "moves"))) {
         1601                 if (decodeparam(buf, sizeof(buf), p) == -1)
         1602                         goto badrequest;
         1603         }
         1604         if (!p)
         1605                 buf[0] = '\0';
         1606 
         1607         fputs("Status: 200 OK\r\n", stdout);
         1608         if (outputmode == ModeSVG)
         1609                 fputs("Content-Type: image/svg+xml\r\n\r\n", stdout);
         1610         else
         1611                 fputs("Content-Type: text/plain\r\n\r\n", stdout);
         1612 
         1613         board_playmoves(&board, buf);
         1614 
         1615         output(&board);
         1616 
         1617         return 0;
         1618 
         1619 badrequest:
         1620         fputs("Status: 400 Bad Request\r\n", stdout);
         1621         fputs("Content-Type: text/plain\r\n", stdout);
         1622         fputs("\r\n", stdout);
         1623         fputs("Bad request: make sure to use valid parameters\n", stdout);
         1624 
         1625         return 1;
         1626 }
         1627 
         1628 int
         1629 main(int argc, char *argv[])
         1630 {
         1631         struct board board;
         1632         const char *fen, *moves;
         1633         int i, j;
         1634 
         1635 #ifdef __OpenBSD__
         1636         if (pledge("stdio", NULL) == -1)
         1637                 err(1, "pledge");
         1638 #endif
         1639 
         1640         if (getenv("QUERY_STRING"))
         1641                 return cgi_mode();
         1642 
         1643         board_init(&board);
         1644         fen = "startpos";
         1645         moves = "";
         1646 
         1647         for (i = 1; i < argc; i++) {
         1648                 if (argv[i][0] != '-')
         1649                         break;
         1650 
         1651                 for (j = 1; argv[i][j]; j++) {
         1652                         switch (argv[i][j]) {
         1653                         case 'c': board.showcoords = 1; break;
         1654                         case 'C': board.showcoords = 0; break;
         1655                         case 'd': dutchmode = 1; break; /* top secret dutch mode for "speak" */
         1656                         case 'f': board.flipboard = 1; break;
         1657                         case 'F': board.flipboard = 0; break;
         1658                         case 'h': board.highlights = 1; break;
         1659                         case 'H': board.highlights = 0; break;
         1660                         case 'l': onlylastmove = 1; silent = 1; break;
         1661                         case 'm': /* remap PGN */
         1662                                 if (i + 1 >= argc)
         1663                                         usage(argv[0]);
         1664                                 i++;
         1665                                 if (strlen(argv[i]) != 5)
         1666                                         usage(argv[0]);
         1667                                 pgn_piecemapping = argv[i];
         1668                                 goto next;
         1669                         case 'o': /* output format */
         1670                                 if (i + 1 >= argc)
         1671                                         usage(argv[0]);
         1672                                 i++;
         1673 
         1674                                 outputmode = outputnametomode(argv[i]);
         1675                                 if (outputmode == ModeInvalid)
         1676                                         usage(argv[0]);
         1677                                 goto next;
         1678                         case 's': board.showside = 1; break;
         1679                         case 'S': board.showside = 0; break;
         1680                         case 't': /* theme name */
         1681                                 if (i + 1 >= argc)
         1682                                         usage(argv[0]);
         1683                                 i++;
         1684                                 board_set_theme(&board, argv[i]);
         1685                                 goto next;
         1686                         default:
         1687                                 usage(argv[0]);
         1688                                 break;
         1689                         }
         1690                 }
         1691 next:
         1692         ;
         1693         }
         1694         if (i < argc) {
         1695                 fen = argv[i];
         1696                 i++;
         1697         }
         1698         if (i < argc) {
         1699                 moves = argv[i];
         1700                 i++;
         1701         }
         1702 
         1703         board_setup_fen(&board, fen);
         1704         board_playmoves(&board, moves);
         1705 
         1706         output(&board);
         1707 
         1708         return 0;
         1709 }