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