URI:
       fen.c - chess-puzzles - chess puzzle book generator
  HTML git clone git://git.codemadness.org/chess-puzzles
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       fen.c (47563B)
       ---
            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 ((side == 'w' && piece == 'k') ||
          773                     (side == 'b' && piece == 'K'))
          774                         return 1;
          775         }
          776 
          777         /* check files and ranks (for queen and rook) */
          778         for (j = 0; j < LEN(line); j += 2) {
          779                 for (i = 1; i < 8; i++) {
          780                         x = kingx + (i * line[j]);
          781                         y = kingy + (i * line[j + 1]);
          782                         if (!(piece = getpiece(b, x, y)))
          783                                 continue;
          784                         /* a piece is in front of it */
          785                         if (piece && strchr("kKbBnNpP", piece))
          786                                 break;
          787                         /* own piece blocking/defending it */
          788                         if ((side == 'w' && iswhitepiece(piece)) ||
          789                             (side == 'b' && isblackpiece(piece)))
          790                                 break;
          791                         return 1;
          792                 }
          793         }
          794 
          795         /* check diagonals (queen and bishop) */
          796         for (j = 0; j < LEN(diag); j += 2) {
          797                 for (i = 1; i < 8; i++) {
          798                         x = kingx + (i * diag[j]);
          799                         y = kingy + (i * diag[j + 1]);
          800                         if (!(piece = getpiece(b, x, y)))
          801                                 continue;
          802                         /* a piece is in front of it */
          803                         if (piece && strchr("kKrRnNpP", piece))
          804                                 break;
          805                         /* own piece blocking/defending it */
          806                         if ((side == 'w' && iswhitepiece(piece)) ||
          807                             (side == 'b' && isblackpiece(piece)))
          808                                 break;
          809                         return 1;
          810                 }
          811         }
          812 
          813         /* check knights */
          814         piece = side == 'w' ? 'n' : 'N';
          815         for (j = 0; j < LEN(knight); j += 2) {
          816                 x = kingx + knight[j];
          817                 y = kingy + knight[j + 1];
          818                 if (getpiece(b, x, y) == piece)
          819                         return 1;
          820         }
          821 
          822         /* check pawns */
          823         if (side == 'w') {
          824                 if (getpiece(b, kingx - 1, kingy - 1) == 'p' ||
          825                     getpiece(b, kingx + 1, kingy - 1) == 'p')
          826                         return 1;
          827         } else if (side == 'b') {
          828                 if (getpiece(b, kingx - 1, kingy + 1) == 'P' ||
          829                     getpiece(b, kingx + 1, kingy + 1) == 'P')
          830                         return 1;
          831         }
          832 
          833         return 0;
          834 }
          835 
          836 /* copy the board state and try the piece move, see if the piece wouldn't put
          837    ourself in check */
          838 int
          839 trypiecemove(struct board *b, int side, int piece,
          840              int x1, int y1, int x2, int y2, int px, int py)
          841 {
          842         struct board tb;
          843 
          844         board_copy(&tb, b);
          845 
          846         /* taken en passant? remove pawn */
          847         if (x2 == px && y2 == py)
          848                 place(&tb, 0, x2, piece == 'P' ? y2 + 1 : y2 - 1);
          849 
          850         place(&tb, 0, x1, y1);
          851         place(&tb, piece, x2, y2);
          852 
          853         /* would put in check / still in check, not allowed */
          854         return !isincheck(&tb, side);
          855 }
          856 
          857 /* can piece move from (x, y), to (x, y)?
          858    en passant square if any is (px, py), otherwise (-1, -1) */
          859 int
          860 canpiecemove(struct board *b, int side, int piece,
          861              int x1, int y1, int x2, int y2, int px, int py)
          862 {
          863         int king[]   = { -1, -1, -1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 0, 1, 1 };
          864         int diag[]   = { -1, -1,  1, 1, -1, 1, 1, -1 };
          865         int line[]   = {  1,  0,  0, 1, -1, 0, 0, -1 };
          866         int knight[] = { -1, -2,  1, -2, -1, 2, 1, 2,
          867                          -2, -1,  2, -1, -2, 1, 2, 1
          868                        };
          869         int i, j, dir, x, y;
          870         int takepiece;
          871 
          872         if (!piece)
          873                 return 0; /* theres no piece so it cannot be moved */
          874 
          875         /* can't move opponent piece */
          876         if ((side == 'w' && isblackpiece(piece)) ||
          877             (side == 'b' && iswhitepiece(piece)))
          878                 return 0;
          879 
          880         if ((takepiece = getpiece(b, x2, y2))) {
          881                 /* can't take your own piece */
          882                 if ((side == 'w' && iswhitepiece(takepiece)) ||
          883                     (side == 'b' && isblackpiece(takepiece)))
          884                         return 0;
          885         }
          886 
          887         /* king movement */
          888         if (piece == 'K' || piece == 'k') {
          889                 for (j = 0; j < LEN(king); j += 2) {
          890                         if (x1 + king[j] == x2 && y1 + king[j + 1] == y2)
          891                                 goto trymove;
          892                 }
          893         }
          894 
          895         /* check files and ranks (for queen and rook) */
          896         if (piece == 'Q' || piece == 'q' || piece == 'R' || piece == 'r') {
          897                 for (j = 0; j < LEN(line); j += 2) {
          898                         for (i = 1; i < 8; i++) {
          899                                 x = x1 + (i * line[j]);
          900                                 y = y1 + (i * line[j + 1]);
          901 
          902                                 if (x == x2 && y == y2 &&
          903                                     trypiecemove(b, side, piece, x1, y1, x2, y2, px, py))
          904                                         return 1;
          905 
          906                                 /* a piece is in front of it: stop this checking this direction */
          907                                 if (getpiece(b, x, y))
          908                                         break;
          909                         }
          910                 }
          911         }
          912 
          913         /* check diagonals (queen and bishop) */
          914         if (piece == 'Q' || piece == 'q' || piece == 'B' || piece == 'b') {
          915                 for (j = 0; j < LEN(diag); j += 2) {
          916                         for (i = 1; i < 8; i++) {
          917                                 x = x1 + (i * diag[j]);
          918                                 y = y1 + (i * diag[j + 1]);
          919                                 if (x == x2 && y == y2 &&
          920                                     trypiecemove(b, side, piece, x1, y1, x2, y2, px, py))
          921                                         return 1;
          922 
          923                                 /* a piece is in front of it: stop this checking this direction */
          924                                 if (getpiece(b, x, y))
          925                                         break;
          926                         }
          927                 }
          928         }
          929 
          930         /* knight movement */
          931         if (piece == 'N' || piece == 'n') {
          932                 for (j = 0; j < LEN(knight); j += 2) {
          933                         if (x1 + knight[j] == x2 && y1 + knight[j + 1] == y2)
          934                                 goto trymove;
          935                 }
          936         }
          937 
          938         /* pawn move */
          939         if (piece == 'P' || piece == 'p') {
          940                 /* direction */
          941                 dir = piece == 'P' ? -1 : +1;
          942                 j = piece == 'P' ? 6 : 1; /* start row */
          943 
          944                 /* can move to en passant square? */
          945                 /* en passant set? try it if possible */
          946                 if (px == x2 && py == y2 &&
          947                     (py == y1 + dir) &&
          948                     ((px == x1 - 1) || px == x1 + 1)) {
          949                         if (isenpassantplayed(b, side == 'w' ? 'b' : 'w', px, y1))
          950                                 return trypiecemove(b, side, piece, x1, y1, x2, y2, px, py);
          951                 }
          952 
          953                 if (takepiece == 0) {
          954                         if (x1 != x2)
          955                                 return 0; /* move on same file */
          956                         /* start move: can be 2 moves */
          957                         if (y1 == j && y2 == y1 + (2 * dir)) {
          958                                 /* square must be empty */
          959                                 if (getpiece(b, x1, y1 + dir))
          960                                         return 0;
          961                                 goto trymove;
          962                         }
          963                         /* normal: check for one move */
          964                         if (y2 != y1 + dir)
          965                                 return 0;
          966                         goto trymove;
          967                 } else {
          968                         /* pawn takes, normal case */
          969                         if ((x2 == x1 - 1 || x2 == x1 + 1) &&
          970                             (y2 == y1 + dir))
          971                                 goto trymove;
          972                 }
          973         }
          974         return 0;
          975 
          976 /* previous checks for move succeeded, actually try move with the current
          977    board state */
          978 trymove:
          979         return trypiecemove(b, side, piece, x1, y1, x2, y2, px, py);
          980 }
          981 
          982 int
          983 ischeckmated(struct board *b, int side)
          984 {
          985         int x, y, x2, y2, px, py, piece;
          986 
          987         px = b->enpassantsquare[0];
          988         py = b->enpassantsquare[1];
          989 
          990         /* check pieces that can block or take a piece that removes the check */
          991         for (y = 0; y < 8; y++) {
          992                 for (x = 0; x < 8; x++) {
          993                         piece = getpiece(b, x, y);
          994                         if ((side == 'w' && !iswhitepiece(piece)) ||
          995                             (side == 'b' && !isblackpiece(piece)))
          996                                 continue;
          997 
          998                         for (y2 = 0; y2 < 8; y2++) {
          999                                 for (x2 = 0; x2 < 8; x2++) {
         1000                                         /* can piece move and afterwards we are not in check? */
         1001                                         if (canpiecemove(b, side, piece, x, y, x2, y2, px, py))
         1002                                                 return 0;
         1003                                 }
         1004                         }
         1005                 }
         1006         }
         1007 
         1008         return 1;
         1009 }
         1010 
         1011 void
         1012 board_setup_fen(struct board *b, const char *fen)
         1013 {
         1014         char square[3];
         1015         const char *s;
         1016         long l;
         1017         int x, y, field;
         1018 
         1019         if (!strcmp(fen, "startpos"))
         1020                 fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
         1021 
         1022         square[2] = '\0';
         1023 
         1024         /* initial board state, FEN format */
         1025         x = y = field = 0;
         1026         for (s = fen; *s && field < 6; s++) {
         1027                 switch (field) {
         1028                 case 0: /* piece placement data */
         1029                         /* skip square */
         1030                         if (*s >= '1' && *s <= '9') {
         1031                                 x += (*s - '0');
         1032                                 continue;
         1033                         }
         1034                         /* next rank */
         1035                         if (*s == '/') {
         1036                                 x = 0;
         1037                                 y++;
         1038                                 continue;
         1039                         }
         1040                         /* is piece? place it */
         1041                         if (isvalidpiece(*s))
         1042                                 place(b, *s, x++, y);
         1043                         break;
         1044                 case 1: /* active color */
         1045                         if (*s == 'w' || *s == 'b')
         1046                                 b->side_to_move = *s;
         1047                         break;
         1048                 case 2: /* castling availability */
         1049                         if (*s == '-') {
         1050                                 b->white_can_castle[0] = 0;
         1051                                 b->white_can_castle[1] = 0;
         1052                                 b->black_can_castle[0] = 0;
         1053                                 b->black_can_castle[1] = 0;
         1054                         } else if (*s == 'K') {
         1055                                 b->white_can_castle[0] = 1;
         1056                         } else if (*s == 'Q') {
         1057                                 b->white_can_castle[1] = 1;
         1058                         } else if (*s == 'k') {
         1059                                 b->black_can_castle[0] = 1;
         1060                         } else if (*s == 'q') {
         1061                                 b->black_can_castle[1] = 1;
         1062                         }
         1063                         break;
         1064                 case 3: /* en passant square */
         1065                         if (*s >= 'a' && *s <= 'h' &&
         1066                                 *(s + 1) >= '1' && *(s + 1) <= '6') {
         1067                                 square[0] = *s;
         1068                                 square[1] = *(s + 1);
         1069                                 squaretoxy(square, &x, &y);
         1070 
         1071                                 b->enpassantsquare[0] = x;
         1072                                 b->enpassantsquare[1] = y;
         1073                         }
         1074                         break;
         1075                 case 4: /* halfmove */
         1076                         if (!(*s >= '0' && *s <= '9'))
         1077                                 continue;
         1078 
         1079                         l = strtol(s, NULL, 10);
         1080                         if (l >= 0 && l < 32767) {
         1081                                 b->halfmove = l;
         1082 
         1083                                 for (; *s && ISDIGIT((unsigned char)*s); s++)
         1084                                         ;
         1085                         }
         1086                         break;
         1087                 case 5: /* move number */
         1088                         if (!(*s >= '0' && *s <= '9'))
         1089                                 continue;
         1090 
         1091                         l = strtol(s, NULL, 10);
         1092                         if (l >= 0 && l < 32767) {
         1093                                 b->movenumber = (int)l;
         1094                                 for (; *s && ISDIGIT((unsigned char)*s); s++)
         1095                                         ;
         1096                         }
         1097                         break;
         1098                 }
         1099                 if (!*s)
         1100                         break;
         1101 
         1102                 /* next field, fields are: piece placement data, active color,
         1103                    Castling availability, En passant target square,
         1104                    Halfmove clock, Fullmove number */
         1105                 if (*s == ' ') {
         1106                         field++;
         1107                         continue;
         1108                 }
         1109         }
         1110 }
         1111 
         1112 /* count ambiguity for piece moves, used to make the notation shorter */
         1113 void
         1114 countambigousmoves(struct board *b, int side, int piece,
         1115         int x, int y, int x2, int y2, int px, int py,
         1116         int *countfile, int *countrank, int *countboard)
         1117 {
         1118         int cf = 0, cr = 0, cb = 0, i, j;
         1119 
         1120         /* check same file */
         1121         for (i = 0; i < 8; i++) {
         1122                 if (getpiece(b, i, y) == piece &&
         1123                     canpiecemove(b, side, piece, i, y, x2, y2, px, py))
         1124                         cf++;
         1125         }
         1126 
         1127         /* check same rank */
         1128         for (i = 0; i < 8; i++) {
         1129                 if (getpiece(b, x, i) == piece &&
         1130                     canpiecemove(b, side, piece, x, i, x2, y2, px, py))
         1131                         cr++;
         1132         }
         1133 
         1134         /* check whole board */
         1135         if (cf <= 1 && cr <= 1) {
         1136                 /* check the whole board if there is any piece
         1137                    that can move to the same square */
         1138                 for (i = 0; i < 8; i++) {
         1139                         for (j = 0; j < 8; j++) {
         1140                                 if (getpiece(b, i, j) == piece &&
         1141                                     canpiecemove(b, side, piece, i, j, x2, y2, px, py))
         1142                                         cb++;
         1143                         }
         1144                 }
         1145         }
         1146 
         1147         *countfile = cf;
         1148         *countrank = cr;
         1149         *countboard = cb;
         1150 }
         1151 
         1152 void
         1153 board_playmoves(struct board *b, const char *moves)
         1154 {
         1155         char square[3];
         1156         const char *castled, *s;
         1157         int firstmove, i, x, y, x2, y2, side, otherside, piece;
         1158         int rookpiece, takepiece, tookpiece;
         1159         int countfile, countrank, countboard, px, py;
         1160         int promote, tookeps;
         1161 
         1162         /* process moves */
         1163         square[2] = '\0';
         1164         x = y = x2 = y2 = -1;
         1165         firstmove = 1;
         1166         /* clear previous highlights */
         1167         memset(&(b->highlight), 0, sizeof(b->highlight));
         1168 
         1169         for (s = moves; *s; s++) {
         1170                 if (*s == ' ')
         1171                         continue;
         1172                 if (!((*s >= 'a' && *s <= 'h') &&
         1173                     (*(s + 1) >= '1' && *(s + 1) <= '8') &&
         1174                     (*(s + 2) >= 'a' && *(s + 2) <= 'h') &&
         1175                     (*(s + 3) >= '1' && *(s + 3) <= '8')))
         1176                         continue;
         1177 
         1178                 /* is last move in this sequence? */
         1179                 if (onlylastmove && !strchr(s, ' '))
         1180                         silent = 0;
         1181 
         1182                 side = b->side_to_move;
         1183                 otherside = side == 'b' ? 'w' : 'b';
         1184 
         1185                 /* if first move and it is blacks turn, prefix
         1186                    with "...", because the white move was unknown */
         1187                 if (!onlylastmove && firstmove && side == 'b')
         1188                         pgn("%d. ... ", b->movenumber);
         1189 
         1190                 if (firstmove && !silent) {
         1191                         firstmove = 0;
         1192                 } else {
         1193                         pgn(" ");
         1194                         speak("\n");
         1195                 }
         1196 
         1197                 square[0] = *s;
         1198                 square[1] = *(s + 1);
         1199 
         1200                 s += 2;
         1201                 squaretoxy(square, &x, &y);
         1202                 piece = getpiece(b, x, y);
         1203 
         1204                 /* target location */
         1205                 square[0] = *s;
         1206                 square[1] = *(s + 1);
         1207                 squaretoxy(square, &x2, &y2);
         1208 
         1209                 /* take piece (can be your own) */
         1210                 takepiece = getpiece(b, x2, y2);
         1211 
         1212                 s += 2;
         1213 
         1214                 promote = 0;
         1215                 /* is a valid piece? should be queen, rook, bishop, knight */
         1216                 if (isvalidpiece(*s)) {
         1217                         if (side == 'w')
         1218                                 promote = TOUPPER(*s);
         1219                         else
         1220                                 promote = TOLOWER(*s);
         1221                         s++;
         1222                 }
         1223 
         1224                 /* took piece of opponent */
         1225                 tookpiece = (side == 'w' && isblackpiece(takepiece)) ||
         1226                             (side == 'b' && iswhitepiece(takepiece));
         1227 
         1228                 /* if pawn move or taken a piece increase halfmove counter */
         1229                 if (piece == 'p' || piece == 'P' || tookpiece)
         1230                         b->halfmove = 0;
         1231                 else
         1232                         b->halfmove++;
         1233 
         1234                 if (!onlylastmove && side == 'w')
         1235                         pgn("%d. ", b->movenumber);
         1236 
         1237                 /* castled this move? */
         1238                 castled = NULL;
         1239 
         1240                 /* castling */
         1241                 if ((piece == 'K' && y == 7 && y2 == 7) ||
         1242                     (piece == 'k' && y == 0 && y2 == 0)) {
         1243                         rookpiece = piece == 'K' ? 'R' : 'r';
         1244 
         1245                         /* kingside castling */
         1246                         if (x2 > x + 1 || (x2 > x && takepiece == rookpiece)) {
         1247                                 castled = "O-O";
         1248                                 for (i = x; i < 8; i++) {
         1249                                         if (getpiece(b, i, y2) == rookpiece) {
         1250                                                 place(b, 0, x, y); /* clear previous square */
         1251                                                 place(b, 0, i, y2); /* clear rook square */
         1252                                                 place(b, rookpiece, 5, y2); /* rook next to king */
         1253                                                 place(b, piece, 6, y2); /* place king */
         1254                                                 x2 = i; /* update square for highlight */
         1255                                                 break;
         1256                                         }
         1257                                 }
         1258                         } else if (x2 < x - 1 || (x2 < x && takepiece == rookpiece)) {
         1259                                 /* queenside castling */
         1260                                 castled = "O-O-O";
         1261                                 for (i = x; i >= 0; i--) {
         1262                                         if (getpiece(b, i, y2) == rookpiece) {
         1263                                                 place(b, 0, x, y); /* clear previous square */
         1264                                                 place(b, 0, i, y2); /* clear rook square */
         1265                                                 place(b, rookpiece, 3, y2); /* rook next to king */
         1266                                                 place(b, piece, 2, y2); /* place king */
         1267                                                 x2 = i; /* update square for highlight */
         1268                                                 break;
         1269                                         }
         1270                                 }
         1271                         }
         1272                 }
         1273 
         1274                 /* remove the ability to castle */
         1275                 if (piece == 'K') {
         1276                         b->white_can_castle[0] = b->white_can_castle[1] = 0;
         1277                 } else if (piece == 'k') {
         1278                         b->black_can_castle[0] = b->black_can_castle[1] = 0;
         1279                 } else if (piece == 'R' && y == 7) {
         1280                         for (i = 0; i < 8; i++) {
         1281                                 if (getpiece(b, i, y) == 'K') {
         1282                                         if (i < x)
         1283                                                 b->white_can_castle[0] = 0;
         1284                                         else if (i > x)
         1285                                                 b->white_can_castle[1] = 0;
         1286                                         break;
         1287                                 }
         1288                         }
         1289                 } else if (piece == 'r' && y == 0) {
         1290                         for (i = 0; i < 8; i++) {
         1291                                 if (getpiece(b, i, y) == 'k') {
         1292                                         if (i > x)
         1293                                                 b->black_can_castle[1] = 0;
         1294                                         else if (i < x)
         1295                                                 b->black_can_castle[0] = 0;
         1296                                         break;
         1297                                 }
         1298                         }
         1299                 }
         1300 
         1301                 /* taken en passant? */
         1302                 tookeps = 0;
         1303                 if (x2 == b->enpassantsquare[0] && y2 == b->enpassantsquare[1] &&
         1304                     (piece == 'P' || piece == 'p')) {
         1305                         /* clear square */
         1306                         place(b, 0, x2, piece == 'P' ? y2 + 1 : y2 - 1);
         1307                         /* set a piece is taken */
         1308                         tookpiece = 1;
         1309                         takepiece = piece == 'P' ? 'p' : 'P';
         1310                         tookeps = 1;
         1311                 }
         1312 
         1313                 /* the en passant square resets after a move */
         1314                 px = b->enpassantsquare[0] = -1;
         1315                 py = b->enpassantsquare[1] = -1;
         1316 
         1317                 /* set en passant square:
         1318                    moved 2 squares and there is an opponent pawn next to it */
         1319                 if (piece == 'P' && y == 6 && y2 == 4) {
         1320                         if (isenpassantplayed(b, side, x, y2)) {
         1321                                 px = b->enpassantsquare[0] = x;
         1322                                 py = b->enpassantsquare[1] = 5;
         1323                         }
         1324                 } else if (piece == 'p' && y == 1 && y2 == 3) {
         1325                         if (isenpassantplayed(b, side, x, y2)) {
         1326                                 px = b->enpassantsquare[0] = x;
         1327                                 py = b->enpassantsquare[1] = 2;
         1328                         }
         1329                 }
         1330 
         1331                 /* PGN for move, if output is not PGN then skip this step */
         1332                 if (outputmode == ModePGN || outputmode == ModeSpeak) {
         1333                         if (castled) {
         1334                                 pgn("%s", castled);
         1335 
         1336                                 if (side == 'w')
         1337                                         speak(dutchmode ? "wit " : "white ");
         1338                                 else if (side == 'b')
         1339                                         speak(dutchmode ? "zwart " : "black ");
         1340 
         1341                                 if (!strcmp(castled, "O-O"))
         1342                                         speak(dutchmode ? "rokeert aan koningszijde " : "castled kingside ");
         1343                                 else
         1344                                         speak(dutchmode ? "rokeert aan damezijde " : "castled queenside ");
         1345                         } else {
         1346                                 if (side == 'w')
         1347                                         speak(dutchmode ? "witte " : "white ");
         1348                                 else if (side == 'b')
         1349                                         speak(dutchmode ? "zwarte " : "black ");
         1350 
         1351                                 if (!tookpiece) {
         1352                                         if (!dutchmode)
         1353                                                 speak("moves ");
         1354                                         speakpiece(piece);
         1355                                 }
         1356 
         1357                                 /* pawn move needs no notation */
         1358                                 if (piece != 'p' && piece != 'P') {
         1359                                         pgn("%c", pgnpiece(piece));
         1360 
         1361                                         /* check ambiguity for certain pieces and make the notation shorter */
         1362                                         countambigousmoves(b, side, piece, x, y, x2, y2, px, py,
         1363                                                 &countfile, &countrank, &countboard);
         1364 
         1365                                         if (countfile > 1 || countboard > 1) {
         1366                                                 pgn("%c", xtofile(x));
         1367                                                 speak("%c", xtofile(x));
         1368                                         }
         1369                                         if (countrank > 1) {
         1370                                                 pgn("%c", ytorank(y));
         1371                                                 speak("%c", ytorank(y));
         1372                                         }
         1373                                         if (countfile > 1 || countrank > 1 || countboard > 1)
         1374                                                 speak(" ");
         1375                                 }
         1376 
         1377                                 if (tookpiece) {
         1378                                         /* pawn captures are prefixed by the file letter (no more needed) */
         1379                                         if (piece == 'p' || piece == 'P')
         1380                                                 pgn("%c", xtofile(x));
         1381                                         pgn("x");
         1382                                         speakpiece(piece);
         1383                                         speak(dutchmode ? "slaat " : "takes ");
         1384                                         speakpiece(takepiece);
         1385                                         speak(dutchmode ? "op " : "on ");
         1386                                         speak("%c%c ", xtofile(x2), ytorank(y2));
         1387                                         if (tookeps)
         1388                                                 speak("en passant ");
         1389                                 } else {
         1390                                         speak(dutchmode ? "naar " : "to ");
         1391                                         speak("%c%c ", xtofile(x2), ytorank(y2));
         1392                                 }
         1393                                 pgn("%c%c", xtofile(x2), ytorank(y2));
         1394 
         1395                                 /* possible promotion: queen, rook, bishop, knight */
         1396                                 if (promote) {
         1397                                         speak(dutchmode ? "en promoot naar " : "and promotes to ");
         1398                                         speakpiece(promote);
         1399 
         1400                                         pgn("=%c", pgnpiece(promote));
         1401                                 }
         1402                         }
         1403                 }
         1404 
         1405                 /* clear previous square (if not castled) */
         1406                 if (!castled) {
         1407                         place(b, 0, x, y);
         1408                         /* place piece or new promoted piece */
         1409                         if (promote)
         1410                                 piece = promote;
         1411                         place(b, piece, x2, y2);
         1412                 }
         1413 
         1414                 if (ischeckmated(b, otherside)) {
         1415                         /* reset en passant square on checkmate */
         1416                         b->enpassantsquare[0] = -1;
         1417                         b->enpassantsquare[1] = -1;
         1418 
         1419                         pgn("#");
         1420                         speak(dutchmode ? "mat" : "checkmate");
         1421                 } else if (isincheck(b, otherside)) {
         1422                         pgn("+");
         1423                         speak(dutchmode ? "schaak" : "check");
         1424                 }
         1425 
         1426                 /* a move by black increases the move number */
         1427                 if (side == 'b')
         1428                         b->movenumber++;
         1429 
         1430                 /* switch which side it is to move */
         1431                 b->side_to_move = otherside;
         1432 
         1433                 if (!*s)
         1434                         break;
         1435         }
         1436 
         1437         if (!firstmove) {
         1438                 pgn("\n");
         1439                 speak("\n");
         1440         }
         1441 
         1442         /* highlight last move */
         1443         if (b->highlights) {
         1444                 highlightmove(b, x, y);
         1445                 highlightmove(b, x2, y2);
         1446 
         1447                 /* highlight king in check or mate */
         1448                 if (isincheck(b, b->side_to_move) &&
         1449                     findking(b, b->side_to_move, &x, &y))
         1450                         highlightcheck(b, x, y);
         1451         }
         1452 }
         1453 
         1454 void
         1455 usage(char *argv0)
         1456 {
         1457         fprintf(stderr, "usage: %s [-cCfFhH] [-l] [-m mapping] "
         1458                 "[-o ascii|fen|pgn|speak|svg|tty] [-sS] [-t default|green|grey] "
         1459                 "[FEN] [moves]\n", argv0);
         1460         exit(1);
         1461 }
         1462 
         1463 /* CGI: get parameter */
         1464 char *
         1465 getparam(const char *query, const char *s)
         1466 {
         1467         const char *p, *last = NULL;
         1468         size_t len;
         1469 
         1470         len = strlen(s);
         1471         for (p = query; (p = strstr(p, s)); p += len) {
         1472                 if (p[len] == '=' && (p == query || p[-1] == '&' || p[-1] == '?'))
         1473                         last = p + len + 1;
         1474         }
         1475 
         1476         return (char *)last;
         1477 }
         1478 
         1479 int
         1480 hexdigit(int c)
         1481 {
         1482         if (c >= '0' && c <= '9')
         1483                 return c - '0';
         1484         else if (c >= 'A' && c <= 'F')
         1485                 return c - 'A' + 10;
         1486         else if (c >= 'a' && c <= 'f')
         1487                 return c - 'a' + 10;
         1488 
         1489         return 0;
         1490 }
         1491 
         1492 /* CGI: decode until NUL separator or end of "key". */
         1493 int
         1494 decodeparam(char *buf, size_t bufsiz, const char *s)
         1495 {
         1496         size_t i;
         1497 
         1498         if (!bufsiz)
         1499                 return -1;
         1500 
         1501         for (i = 0; *s && *s != '&'; s++) {
         1502                 switch (*s) {
         1503                 case '%':
         1504                         if (i + 3 >= bufsiz)
         1505                                 return -1;
         1506                         if (!ISXDIGIT((unsigned char)*(s+1)) ||
         1507                             !ISXDIGIT((unsigned char)*(s+2)))
         1508                                 return -1;
         1509                         buf[i++] = hexdigit(*(s+1)) * 16 + hexdigit(*(s+2));
         1510                         s += 2;
         1511                         break;
         1512                 case '+':
         1513                         if (i + 1 >= bufsiz)
         1514                                 return -1;
         1515                         buf[i++] = ' ';
         1516                         break;
         1517                 default:
         1518                         if (i + 1 >= bufsiz)
         1519                                 return -1;
         1520                         buf[i++] = *s;
         1521                         break;
         1522                 }
         1523         }
         1524         buf[i] = '\0';
         1525 
         1526         return i;
         1527 }
         1528 
         1529 enum outputmode
         1530 outputnametomode(const char *s)
         1531 {
         1532         if (!strcmp(s, "ascii"))
         1533                 return ModeASCII;
         1534         else if (!strcmp(s, "fen"))
         1535                 return ModeFEN;
         1536         else if (!strcmp(s, "pgn"))
         1537                 return ModePGN;
         1538         else if (!strcmp(s, "speak"))
         1539                 return ModeSpeak;
         1540         else if (!strcmp(s, "svg"))
         1541                 return ModeSVG;
         1542         else if (!strcmp(s, "tty"))
         1543                 return ModeTTY;
         1544         else
         1545                 return ModeInvalid;
         1546 }
         1547 
         1548 void
         1549 output(struct board *b)
         1550 {
         1551         switch (outputmode) {
         1552         case ModeASCII: output_ascii(b); break;
         1553         case ModeFEN:   output_fen(b);   break;
         1554         case ModePGN:                    break; /* handled in parsemoves() */
         1555         case ModeSVG:   output_svg(b);   break;
         1556         case ModeTTY:   output_tty(b);   break;
         1557         default:        break;
         1558         }
         1559 }
         1560 
         1561 /* CGI mode */
         1562 int
         1563 cgi_mode(void)
         1564 {
         1565         struct board board;
         1566         char *query, *p;
         1567         char buf[4096];
         1568 
         1569         board_init(&board);
         1570 
         1571         query = getenv("QUERY_STRING");
         1572         if ((p = getparam(query, "flip")) && (*p == '0' || *p == '1'))
         1573                 board.flipboard = *p == '1' ? 1 : 0;
         1574         if ((p = getparam(query, "side")) && (*p == '0' || *p == '1'))
         1575                 board.showside = *p == '1' ? 1 : 0;
         1576         if ((p = getparam(query, "coords")) && (*p == '0' || *p == '1'))
         1577                 board.showcoords = *p == '1' ? 1 : 0;
         1578         if ((p = getparam(query, "dutch")) && *p == '1') {
         1579                 dutchmode = 1;
         1580                 pgn_piecemapping = "KDTLP";
         1581         }
         1582         if ((p = getparam(query, "output"))) {
         1583                 if (decodeparam(buf, sizeof(buf), p) == -1)
         1584                         goto badrequest;
         1585                 outputmode = outputnametomode(buf);
         1586                 if (outputmode == ModeInvalid)
         1587                         goto badrequest;
         1588         }
         1589         if ((p = getparam(query, "theme"))) {
         1590                 if (decodeparam(buf, sizeof(buf), p) == -1)
         1591                         goto badrequest;
         1592                 board_set_theme(&board, buf);
         1593         }
         1594         if ((p = getparam(query, "fen"))) {
         1595                 if (decodeparam(buf, sizeof(buf), p) == -1)
         1596                         goto badrequest;
         1597                 board_setup_fen(&board, buf);
         1598         } else {
         1599                 board_setup_fen(&board, "startpos");
         1600         }
         1601         if ((p = getparam(query, "moves"))) {
         1602                 if (decodeparam(buf, sizeof(buf), p) == -1)
         1603                         goto badrequest;
         1604         }
         1605         if (!p)
         1606                 buf[0] = '\0';
         1607 
         1608         fputs("Status: 200 OK\r\n", stdout);
         1609         if (outputmode == ModeSVG)
         1610                 fputs("Content-Type: image/svg+xml\r\n\r\n", stdout);
         1611         else
         1612                 fputs("Content-Type: text/plain\r\n\r\n", stdout);
         1613 
         1614         board_playmoves(&board, buf);
         1615 
         1616         output(&board);
         1617 
         1618         return 0;
         1619 
         1620 badrequest:
         1621         fputs("Status: 400 Bad Request\r\n", stdout);
         1622         fputs("Content-Type: text/plain\r\n", stdout);
         1623         fputs("\r\n", stdout);
         1624         fputs("Bad request: make sure to use valid parameters\n", stdout);
         1625 
         1626         return 1;
         1627 }
         1628 
         1629 int
         1630 main(int argc, char *argv[])
         1631 {
         1632         struct board board;
         1633         const char *fen, *moves;
         1634         int i, j;
         1635 
         1636 #ifdef __OpenBSD__
         1637         if (pledge("stdio", NULL) == -1)
         1638                 err(1, "pledge");
         1639 #endif
         1640 
         1641         if (getenv("QUERY_STRING"))
         1642                 return cgi_mode();
         1643 
         1644         board_init(&board);
         1645         fen = "startpos";
         1646         moves = "";
         1647 
         1648         for (i = 1; i < argc; i++) {
         1649                 if (argv[i][0] != '-')
         1650                         break;
         1651 
         1652                 for (j = 1; argv[i][j]; j++) {
         1653                         switch (argv[i][j]) {
         1654                         case 'c': board.showcoords = 1; break;
         1655                         case 'C': board.showcoords = 0; break;
         1656                         case 'd': dutchmode = 1; break; /* top secret dutch mode for "speak" */
         1657                         case 'f': board.flipboard = 1; break;
         1658                         case 'F': board.flipboard = 0; break;
         1659                         case 'h': board.highlights = 1; break;
         1660                         case 'H': board.highlights = 0; break;
         1661                         case 'l': onlylastmove = 1; silent = 1; break;
         1662                         case 'm': /* remap PGN */
         1663                                 if (i + 1 >= argc)
         1664                                         usage(argv[0]);
         1665                                 i++;
         1666                                 if (strlen(argv[i]) != 5)
         1667                                         usage(argv[0]);
         1668                                 pgn_piecemapping = argv[i];
         1669                                 goto next;
         1670                         case 'o': /* output format */
         1671                                 if (i + 1 >= argc)
         1672                                         usage(argv[0]);
         1673                                 i++;
         1674 
         1675                                 outputmode = outputnametomode(argv[i]);
         1676                                 if (outputmode == ModeInvalid)
         1677                                         usage(argv[0]);
         1678                                 goto next;
         1679                         case 's': board.showside = 1; break;
         1680                         case 'S': board.showside = 0; break;
         1681                         case 't': /* theme name */
         1682                                 if (i + 1 >= argc)
         1683                                         usage(argv[0]);
         1684                                 i++;
         1685                                 board_set_theme(&board, argv[i]);
         1686                                 goto next;
         1687                         default:
         1688                                 usage(argv[0]);
         1689                                 break;
         1690                         }
         1691                 }
         1692 next:
         1693         ;
         1694         }
         1695         if (i < argc) {
         1696                 fen = argv[i];
         1697                 i++;
         1698         }
         1699         if (i < argc) {
         1700                 moves = argv[i];
         1701                 i++;
         1702         }
         1703 
         1704         board_setup_fen(&board, fen);
         1705         board_playmoves(&board, moves);
         1706 
         1707         output(&board);
         1708 
         1709         return 0;
         1710 }