Introduction
Introduction Statistics Contact Development Disclaimer Help
fen.c - chess-puzzles - chess puzzle book generator
git clone git://git.codemadness.org/chess-puzzles
Log
Files
Refs
README
LICENSE
---
fen.c (47523B)
---
1 #include <stdarg.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #ifdef __OpenBSD__
7 #include <err.h>
8 #include <unistd.h>
9 #endif
10
11 #define LEN(s) (sizeof(s)/sizeof(*s))
12
13 /* ctype-like macros, but always compatible with ASCII / UTF-8 */
14 #define ISDIGIT(c) (((unsigned)c) - '0' < 10)
15 #define ISXDIGIT(c) ((((unsigned)c) - '0' < 10) || ((unsigned)c | 32) - …
16 #define TOLOWER(c) ((((unsigned)c) - 'A' < 26) ? ((c) | 32) : (c))
17 #define TOUPPER(c) ((((unsigned)c) - 'a' < 26) ? ((c) & 0x5f) : (c))
18
19 /* macro for truecolor RGB output to tty */
20 #define SETFGCOLOR(r,g,b) printf("\x1b[38;2;%d;%d;%dm", r, g, b)
21 #define SETBGCOLOR(r,g,b) printf("\x1b[48;2;%d;%d;%dm", r, g, b)
22
23 enum outputmode { ModeInvalid = 0, ModeASCII, ModeFEN, ModePGN,
24 ModeTTY, ModeSVG, ModeSpeak };
25 enum outputmode outputmode = ModeSVG; /* default is SVG */
26
27 static int onlylastmove = 0, silent = 0, dutchmode = 0;
28
29 /* localization of letter for PGN pieces */
30 const char *pgn_piecemapping = "";
31
32 typedef unsigned char Color; /* for RGB: 0-255 */
33
34 struct theme {
35 const char *name;
36 /* RGB values */
37 Color border[3];
38 Color darksquare[3];
39 Color lightsquare[3];
40 Color darksquarehi[3];
41 Color lightsquarehi[3];
42 Color lightsquarecheck[3];
43 Color darksquarecheck[3];
44 };
45
46 struct theme themes[] = {
47 /* lichess default brown theme colors (red, green, blue) */
48 {
49 .name = "default",
50 .border = { 0x70, 0x49, 0x2d },
51 .darksquare = { 0xb5, 0x88, 0x63 },
52 .lightsquare = { 0xf0, 0xd9, 0xb5 },
53 .darksquarehi = { 0xaa, 0xa2, 0x3a },
54 .lightsquarehi = { 0xcd, 0xd2, 0x6a },
55 .lightsquarecheck = { 0xff, 0x6a, 0x6a },
56 .darksquarecheck = { 0xff, 0x3a, 0x3a }
57 },
58 /* lichess green theme */
59 {
60 .name = "green",
61 .border = { 0x33, 0x33, 0x33 },
62 .darksquare = { 0x86, 0xa6, 0x66 },
63 .lightsquare = { 0xff, 0xff, 0xdd },
64 .darksquarehi = { 0x4f, 0xa1, 0x8e },
65 .lightsquarehi = { 0x96, 0xd6, 0xd4 },
66 .lightsquarecheck = { 0xff, 0x6a, 0x6a },
67 .darksquarecheck = { 0xff, 0x3a, 0x3a }
68 },
69 /* red / love theme */
70 {
71 .name = "love",
72 .border = { 0x33, 0x33, 0x33 },
73 .darksquare = { 0xd9, 0x4c, 0x4c },
74 .lightsquare = { 0xff, 0xca, 0xca },
75 .darksquarehi = { 0xaa, 0xa2, 0x3a },
76 .lightsquarehi = { 0xcd, 0xd2, 0x6a },
77 .lightsquarecheck = { 0xff, 0x6a, 0x6a },
78 .darksquarecheck = { 0xff, 0x3a, 0x3a }
79 },
80 /* greyscale theme, highlight is still green though */
81 {
82 .name = "grey",
83 .border = { 0x00, 0x00, 0x00 },
84 .darksquare = { 0x66, 0x66, 0x66 },
85 .lightsquare = { 0xaa, 0xaa, 0xaa },
86 .darksquarehi = { 0x66, 0x61, 0x23 },
87 .lightsquarehi = { 0xa8, 0xab, 0x55 },
88 .lightsquarecheck = { 0xff, 0x6a, 0x6a },
89 .darksquarecheck = { 0xff, 0x3a, 0x3a }
90 },
91 /* print theme, highlight is still green though */
92 {
93 .name = "print",
94 .border = { 0x00, 0x00, 0x00 },
95 .darksquare = { 0xcc, 0xcc, 0xcc },
96 .lightsquare = { 0xff, 0xff, 0xff },
97 .darksquarehi = { 0xbb, 0xbb, 0xbb },
98 .lightsquarehi = { 0xdd, 0xdd, 0xdd },
99 .lightsquarecheck = { 0x77, 0x77, 0x77 },
100 .darksquarecheck = { 0x77, 0x77, 0x77 }
101 }
102 };
103
104 struct board {
105 char tiles[8][8]; /* board tiles and piece placement */
106 int enpassantsquare[2]; /* default: no: { -1, -1 } */
107
108 char highlight[8][8]; /* highlighted squares, (0 = none, 1 = …
109
110 int side_to_move; /* default: white to move: 'w' */
111 int white_can_castle[2]; /* allow king side, allow queen side? d…
112 int black_can_castle[2]; /* allow king side, allow queen side? d…
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 mov…
120 int highlights; /* highlight moves and checks? default:…
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 "); brea…
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 "); bre…
284 case 'N': case 'n': speak(dutchmode ? "paard " : "knight "); bre…
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/maste…
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 use…
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\…
426 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http…
427 "<svg width=\"360\" height=\"360\" viewBox=\"0 0 360 360…
428 "<rect fill=\"#fff\" stroke=\"#000\" x=\"0\" y=\"0\" wid…
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=\"ev…
448 case 'Q': s = "<g id=\"wq\" fill=\"#fff\" fill-rule=\"ev…
449 case 'R': s = "<g id=\"wr\" fill=\"#fff\" fill-rule=\"ev…
450 case 'B': s = "<g id=\"wb\" fill=\"none\" fill-rule=\"ev…
451 case 'N': s = "<g id=\"wn\" fill=\"none\" fill-rule=\"ev…
452 case 'P':
453 case 'p':
454 s = "<path id=\"pawn\" d=\"M22.5 9c-2.21 0-4 1.7…
455 pieceused[0] = pieceused[1] = 0; /* unset used, …
456 break;
457 case 'k': s = "<g id=\"bk\" fill=\"none\" fill-rule=\"ev…
458 case 'q': s = "<g id=\"bq\" fill-rule=\"evenodd\" stroke…
459 case 'r': s = "<g id=\"br\" fill-rule=\"evenodd\" stroke…
460 case 'b': s = "<g id=\"bb\" fill=\"none\" fill-rule=\"ev…
461 case 'n': s = "<g id=\"bn\" fill=\"none\" fill-rule=\"ev…
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\" …
477 ix * 45, iy * 45, color[0], color[1], co…
478
479 piece = getpiece(b, x, y);
480 if (piece) {
481 printf("<g transform=\"translate(%d %d)\…
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…
497 (ix + 1) * 45 - 2, (iy * 45) + 10, color…
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…
507 (ix * 45) + 2, (iy + 1) * 45 - 3, color[…
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\" fil…
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); /* br…
594 else
595 fputs("\x1b[30m", stdout); /* bl…
596 /* workaround: use black unicode chess s…
597 the color is filled and better visibl…
598 showpiece_tty(TOLOWER(piece));
599 } else {
600 fputs(" ", stdout);
601 }
602 fputs(" ", stdout);
603 }
604 printf("\x1b[0m"); /* reset */
605
606 color = t->border;
607 SETBGCOLOR(color[0], color[1], color[2]);
608 if (b->showcoords) {
609 fputs("\x1b[97m", stdout); /* bright white */
610 putchar(ytorank(y));
611 putchar(' ');
612 } else {
613 fputs(" ", stdout);
614 }
615
616 printf("\x1b[0m"); /* reset */
617 putchar('\n');
618 }
619 color = t->border;
620 SETBGCOLOR(color[0], color[1], color[2]);
621 fputs("\x1b[97m", stdout); /* bright white */
622 if (b->showcoords) {
623 fputs(" ", stdout);
624 for (iy = 0; iy < 8; iy++) {
625 y = b->flipboard ? 7 - iy : iy;
626 putchar(xtofile(y));
627 fputs(" ", stdout);
628 }
629 } else {
630 fputs(" ", stdout);
631 }
632
633 if (b->showside) {
634 if (b->side_to_move == 'w' && !b->flipboard)
635 printf("\x1b[30;47m%c", b->side_to_move);
636 else if (b->side_to_move == 'b' && b->flipboard)
637 printf("\x1b[37;40m%c", b->side_to_move);
638 else
639 putchar(' ');
640 } else {
641 putchar(' ');
642 }
643
644 printf("\x1b[0m"); /* reset */
645 printf("\n");
646 }
647
648 void
649 showpiece_ascii(int c)
650 {
651 putchar(c);
652 }
653
654 /* OnlyFENs */
655 void
656 output_fen(struct board *b)
657 {
658 showboardfen(b);
659 }
660
661 /* show board */
662 void
663 output_ascii(struct board *b)
664 {
665 unsigned char hi[3] = { '>', ' ', '<' };
666 unsigned char dark[3] = { '.', '.', '.' };
667 unsigned char light[3] = { ' ', ' ', ' ' };
668 unsigned char *color;
669 int ix, iy, x, y, piece;
670
671 for (iy = 0; iy < 8; iy++) {
672 y = b->flipboard ? 7 - iy : iy;
673
674 fputs("+---+---+---+---+---+---+---+---+\n", stdout);
675 for (ix = 0; ix < 8; ix++) {
676 x = b->flipboard ? 7 - ix : ix;
677
678 if (((x % 2) ^ (y % 2)) == 0)
679 color = b->highlight[y][x] ? hi : light;
680 else
681 color = b->highlight[y][x] ? hi : dark;
682
683 if (ix == 0)
684 putchar('|');
685 putchar(color[0]);
686 piece = getpiece(b, x, y);
687 if (piece)
688 showpiece_ascii(piece);
689 else
690 putchar(color[1]);
691 putchar(color[2]);
692 putchar('|');
693 }
694 if (b->showcoords) {
695 putchar(' ');
696 putchar(ytorank(y));
697 }
698 putchar('\n');
699 }
700 fputs("+---+---+---+---+---+---+---+---+\n", stdout);
701 if (b->showcoords) {
702 fputs(" ", stdout);
703 for (iy = 0; iy < 8; iy++) {
704 if (iy)
705 fputs(" | ", stdout);
706 y = b->flipboard ? 7 - iy : iy;
707 putchar(xtofile(y));
708 }
709 fputs(" |\n", stdout);
710 }
711
712 fputs("\n", stdout);
713 }
714
715 int
716 findking(struct board *b, int side, int *kingx, int *kingy)
717 {
718 int king, x, y;
719
720 king = side == 'w' ? 'K' : 'k';
721 *kingx = -1;
722 *kingy = -1;
723
724 /* find king */
725 for (y = 0; y < 8; y++) {
726 for (x = 0; x < 8; x++) {
727 if (getpiece(b, x, y) == king) {
728 *kingx = x;
729 *kingy = y;
730 return 1;
731 }
732 }
733 }
734 return 0;
735 }
736
737 int
738 isenpassantplayed(struct board *b, int side, int x, int y)
739 {
740 if (side == 'w') {
741 return (getpiece(b, x - 1, y) == 'p' ||
742 getpiece(b, x + 1, y) == 'p');
743 } else if (side == 'b') {
744 return (getpiece(b, x - 1, y) == 'P' ||
745 getpiece(b, x + 1, y) == 'P');
746 }
747 return 0;
748 }
749
750 int
751 isincheck(struct board *b, int side)
752 {
753 int king[] = { -1, -1, -1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 0,…
754 int diag[] = { -1, -1, 1, 1, -1, 1, 1, -1 };
755 int line[] = { 1, 0, 0, 1, -1, 0, 0, -1 };
756 int knight[] = { -1, -2, 1, -2, -1, 2, 1, 2,
757 -2, -1, 2, -1, -2, 1, 2, 1
758 };
759 int i, j, x, y;
760 int kingx, kingy;
761 int piece;
762
763 /* find our king */
764 if (!findking(b, side, &kingx, &kingy))
765 return 0; /* should not happen */
766
767 /* check kings (illegal) */
768 for (j = 0; j < LEN(king); j += 2) {
769 x = kingx + king[j];
770 y = kingy + king[j + 1];
771 piece = getpiece(b, x, y);
772 if (piece && strchr("kK", piece))
773 return 1;
774 }
775
776 /* check files and ranks (for queen and rook) */
777 for (j = 0; j < LEN(line); j += 2) {
778 for (i = 1; i < 8; i++) {
779 x = kingx + (i * line[j]);
780 y = kingy + (i * line[j + 1]);
781 if (!(piece = getpiece(b, x, y)))
782 continue;
783 /* a piece is in front of it */
784 if (piece && strchr("kKbBnNpP", piece))
785 break;
786 /* own piece blocking/defending it */
787 if ((side == 'w' && iswhitepiece(piece)) ||
788 (side == 'b' && isblackpiece(piece)))
789 break;
790 return 1;
791 }
792 }
793
794 /* check diagonals (queen and bishop) */
795 for (j = 0; j < LEN(diag); j += 2) {
796 for (i = 1; i < 8; i++) {
797 x = kingx + (i * diag[j]);
798 y = kingy + (i * diag[j + 1]);
799 if (!(piece = getpiece(b, x, y)))
800 continue;
801 /* a piece is in front of it */
802 if (piece && strchr("kKrRnNpP", piece))
803 break;
804 /* own piece blocking/defending it */
805 if ((side == 'w' && iswhitepiece(piece)) ||
806 (side == 'b' && isblackpiece(piece)))
807 break;
808 return 1;
809 }
810 }
811
812 /* check knights */
813 piece = side == 'w' ? 'n' : 'N';
814 for (j = 0; j < LEN(knight); j += 2) {
815 x = kingx + knight[j];
816 y = kingy + knight[j + 1];
817 if (getpiece(b, x, y) == piece)
818 return 1;
819 }
820
821 /* check pawns */
822 if (side == 'w') {
823 if (getpiece(b, kingx - 1, kingy - 1) == 'p' ||
824 getpiece(b, kingx + 1, kingy - 1) == 'p')
825 return 1;
826 } else if (side == 'b') {
827 if (getpiece(b, kingx - 1, kingy + 1) == 'P' ||
828 getpiece(b, kingx + 1, kingy + 1) == 'P')
829 return 1;
830 }
831
832 return 0;
833 }
834
835 /* copy the board state and try the piece move, see if the piece wouldn'…
836 ourself in check */
837 int
838 trypiecemove(struct board *b, int side, int piece,
839 int x1, int y1, int x2, int y2, int px, int py)
840 {
841 struct board tb;
842
843 board_copy(&tb, b);
844
845 /* taken en passant? remove pawn */
846 if (x2 == px && y2 == py)
847 place(&tb, 0, x2, piece == 'P' ? y2 + 1 : y2 - 1);
848
849 place(&tb, 0, x1, y1);
850 place(&tb, piece, x2, y2);
851
852 /* would put in check / still in check, not allowed */
853 return !isincheck(&tb, side);
854 }
855
856 /* can piece move from (x, y), to (x, y)?
857 en passant square if any is (px, py), otherwise (-1, -1) */
858 int
859 canpiecemove(struct board *b, int side, int piece,
860 int x1, int y1, int x2, int y2, int px, int py)
861 {
862 int king[] = { -1, -1, -1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 0,…
863 int diag[] = { -1, -1, 1, 1, -1, 1, 1, -1 };
864 int line[] = { 1, 0, 0, 1, -1, 0, 0, -1 };
865 int knight[] = { -1, -2, 1, -2, -1, 2, 1, 2,
866 -2, -1, 2, -1, -2, 1, 2, 1
867 };
868 int i, j, dir, x, y;
869 int takepiece;
870
871 if (!piece)
872 return 0; /* theres no piece so it cannot be moved */
873
874 /* can't move opponent piece */
875 if ((side == 'w' && isblackpiece(piece)) ||
876 (side == 'b' && iswhitepiece(piece)))
877 return 0;
878
879 if ((takepiece = getpiece(b, x2, y2))) {
880 /* can't take your own piece */
881 if ((side == 'w' && iswhitepiece(takepiece)) ||
882 (side == 'b' && isblackpiece(takepiece)))
883 return 0;
884 }
885
886 /* king movement */
887 if (piece == 'K' || piece == 'k') {
888 for (j = 0; j < LEN(king); j += 2) {
889 if (x1 + king[j] == x2 && y1 + king[j + 1] == y2)
890 goto trymove;
891 }
892 }
893
894 /* check files and ranks (for queen and rook) */
895 if (piece == 'Q' || piece == 'q' || piece == 'R' || piece == 'r'…
896 for (j = 0; j < LEN(line); j += 2) {
897 for (i = 1; i < 8; i++) {
898 x = x1 + (i * line[j]);
899 y = y1 + (i * line[j + 1]);
900
901 if (x == x2 && y == y2 &&
902 trypiecemove(b, side, piece, x1, y1,…
903 return 1;
904
905 /* a piece is in front of it: stop this …
906 if (getpiece(b, x, y))
907 break;
908 }
909 }
910 }
911
912 /* check diagonals (queen and bishop) */
913 if (piece == 'Q' || piece == 'q' || piece == 'B' || piece == 'b'…
914 for (j = 0; j < LEN(diag); j += 2) {
915 for (i = 1; i < 8; i++) {
916 x = x1 + (i * diag[j]);
917 y = y1 + (i * diag[j + 1]);
918 if (x == x2 && y == y2 &&
919 trypiecemove(b, side, piece, x1, y1,…
920 return 1;
921
922 /* a piece is in front of it: stop this …
923 if (getpiece(b, x, y))
924 break;
925 }
926 }
927 }
928
929 /* knight movement */
930 if (piece == 'N' || piece == 'n') {
931 for (j = 0; j < LEN(knight); j += 2) {
932 if (x1 + knight[j] == x2 && y1 + knight[j + 1] =…
933 goto trymove;
934 }
935 }
936
937 /* pawn move */
938 if (piece == 'P' || piece == 'p') {
939 /* direction */
940 dir = piece == 'P' ? -1 : +1;
941 j = piece == 'P' ? 6 : 1; /* start row */
942
943 /* can move to en passant square? */
944 /* en passant set? try it if possible */
945 if (px == x2 && py == y2 &&
946 (py == y1 + dir) &&
947 ((px == x1 - 1) || px == x1 + 1)) {
948 if (isenpassantplayed(b, side == 'w' ? 'b' : 'w'…
949 return trypiecemove(b, side, piece, x1, …
950 }
951
952 if (takepiece == 0) {
953 if (x1 != x2)
954 return 0; /* move on same file */
955 /* start move: can be 2 moves */
956 if (y1 == j && y2 == y1 + (2 * dir)) {
957 /* square must be empty */
958 if (getpiece(b, x1, y1 + dir))
959 return 0;
960 goto trymove;
961 }
962 /* normal: check for one move */
963 if (y2 != y1 + dir)
964 return 0;
965 goto trymove;
966 } else {
967 /* pawn takes, normal case */
968 if ((x2 == x1 - 1 || x2 == x1 + 1) &&
969 (y2 == y1 + dir))
970 goto trymove;
971 }
972 }
973 return 0;
974
975 /* previous checks for move succeeded, actually try move with the current
976 board state */
977 trymove:
978 return trypiecemove(b, side, piece, x1, y1, x2, y2, px, py);
979 }
980
981 int
982 ischeckmated(struct board *b, int side)
983 {
984 int x, y, x2, y2, px, py, piece;
985
986 px = b->enpassantsquare[0];
987 py = b->enpassantsquare[1];
988
989 /* check pieces that can block or take a piece that removes the …
990 for (y = 0; y < 8; y++) {
991 for (x = 0; x < 8; x++) {
992 piece = getpiece(b, x, y);
993 if ((side == 'w' && !iswhitepiece(piece)) ||
994 (side == 'b' && !isblackpiece(piece)))
995 continue;
996
997 for (y2 = 0; y2 < 8; y2++) {
998 for (x2 = 0; x2 < 8; x2++) {
999 /* can piece move and afterwards…
1000 if (canpiecemove(b, side, piece,…
1001 return 0;
1002 }
1003 }
1004 }
1005 }
1006
1007 return 1;
1008 }
1009
1010 void
1011 board_setup_fen(struct board *b, const char *fen)
1012 {
1013 char square[3];
1014 const char *s;
1015 long l;
1016 int x, y, field;
1017
1018 if (!strcmp(fen, "startpos"))
1019 fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQk…
1020
1021 square[2] = '\0';
1022
1023 /* initial board state, FEN format */
1024 x = y = field = 0;
1025 for (s = fen; *s && field < 6; s++) {
1026 switch (field) {
1027 case 0: /* piece placement data */
1028 /* skip square */
1029 if (*s >= '1' && *s <= '9') {
1030 x += (*s - '0');
1031 continue;
1032 }
1033 /* next rank */
1034 if (*s == '/') {
1035 x = 0;
1036 y++;
1037 continue;
1038 }
1039 /* is piece? place it */
1040 if (isvalidpiece(*s))
1041 place(b, *s, x++, y);
1042 break;
1043 case 1: /* active color */
1044 if (*s == 'w' || *s == 'b')
1045 b->side_to_move = *s;
1046 break;
1047 case 2: /* castling availability */
1048 if (*s == '-') {
1049 b->white_can_castle[0] = 0;
1050 b->white_can_castle[1] = 0;
1051 b->black_can_castle[0] = 0;
1052 b->black_can_castle[1] = 0;
1053 } else if (*s == 'K') {
1054 b->white_can_castle[0] = 1;
1055 } else if (*s == 'Q') {
1056 b->white_can_castle[1] = 1;
1057 } else if (*s == 'k') {
1058 b->black_can_castle[0] = 1;
1059 } else if (*s == 'q') {
1060 b->black_can_castle[1] = 1;
1061 }
1062 break;
1063 case 3: /* en passant square */
1064 if (*s >= 'a' && *s <= 'h' &&
1065 *(s + 1) >= '1' && *(s + 1) <= '6') {
1066 square[0] = *s;
1067 square[1] = *(s + 1);
1068 squaretoxy(square, &x, &y);
1069
1070 b->enpassantsquare[0] = x;
1071 b->enpassantsquare[1] = y;
1072 }
1073 break;
1074 case 4: /* halfmove */
1075 if (!(*s >= '0' && *s <= '9'))
1076 continue;
1077
1078 l = strtol(s, NULL, 10);
1079 if (l >= 0 && l < 32767) {
1080 b->halfmove = l;
1081
1082 for (; *s && ISDIGIT((unsigned char)*s);…
1083 ;
1084 }
1085 break;
1086 case 5: /* move number */
1087 if (!(*s >= '0' && *s <= '9'))
1088 continue;
1089
1090 l = strtol(s, NULL, 10);
1091 if (l >= 0 && l < 32767) {
1092 b->movenumber = (int)l;
1093 for (; *s && ISDIGIT((unsigned char)*s);…
1094 ;
1095 }
1096 break;
1097 }
1098 if (!*s)
1099 break;
1100
1101 /* next field, fields are: piece placement data, active …
1102 Castling availability, En passant target square,
1103 Halfmove clock, Fullmove number */
1104 if (*s == ' ') {
1105 field++;
1106 continue;
1107 }
1108 }
1109 }
1110
1111 /* count ambiguity for piece moves, used to make the notation shorter */
1112 void
1113 countambigousmoves(struct board *b, int side, int piece,
1114 int x, int y, int x2, int y2, int px, int py,
1115 int *countfile, int *countrank, int *countboard)
1116 {
1117 int cf = 0, cr = 0, cb = 0, i, j;
1118
1119 /* check same file */
1120 for (i = 0; i < 8; i++) {
1121 if (getpiece(b, i, y) == piece &&
1122 canpiecemove(b, side, piece, i, y, x2, y2, px, py))
1123 cf++;
1124 }
1125
1126 /* check same rank */
1127 for (i = 0; i < 8; i++) {
1128 if (getpiece(b, x, i) == piece &&
1129 canpiecemove(b, side, piece, x, i, x2, y2, px, py))
1130 cr++;
1131 }
1132
1133 /* check whole board */
1134 if (cf <= 1 && cr <= 1) {
1135 /* check the whole board if there is any piece
1136 that can move to the same square */
1137 for (i = 0; i < 8; i++) {
1138 for (j = 0; j < 8; j++) {
1139 if (getpiece(b, i, j) == piece &&
1140 canpiecemove(b, side, piece, i, j, x…
1141 cb++;
1142 }
1143 }
1144 }
1145
1146 *countfile = cf;
1147 *countrank = cr;
1148 *countboard = cb;
1149 }
1150
1151 void
1152 board_playmoves(struct board *b, const char *moves)
1153 {
1154 char square[3];
1155 const char *castled, *s;
1156 int firstmove, i, x, y, x2, y2, side, otherside, piece;
1157 int rookpiece, takepiece, tookpiece;
1158 int countfile, countrank, countboard, px, py;
1159 int promote, tookeps;
1160
1161 /* process moves */
1162 square[2] = '\0';
1163 x = y = x2 = y2 = -1;
1164 firstmove = 1;
1165 /* clear previous highlights */
1166 memset(&(b->highlight), 0, sizeof(b->highlight));
1167
1168 for (s = moves; *s; s++) {
1169 if (*s == ' ')
1170 continue;
1171 if (!((*s >= 'a' && *s <= 'h') &&
1172 (*(s + 1) >= '1' && *(s + 1) <= '8') &&
1173 (*(s + 2) >= 'a' && *(s + 2) <= 'h') &&
1174 (*(s + 3) >= '1' && *(s + 3) <= '8')))
1175 continue;
1176
1177 /* is last move in this sequence? */
1178 if (onlylastmove && !strchr(s, ' '))
1179 silent = 0;
1180
1181 side = b->side_to_move;
1182 otherside = side == 'b' ? 'w' : 'b';
1183
1184 /* if first move and it is blacks turn, prefix
1185 with "...", because the white move was unknown */
1186 if (!onlylastmove && firstmove && side == 'b')
1187 pgn("%d. ... ", b->movenumber);
1188
1189 if (firstmove && !silent) {
1190 firstmove = 0;
1191 } else {
1192 pgn(" ");
1193 speak("\n");
1194 }
1195
1196 square[0] = *s;
1197 square[1] = *(s + 1);
1198
1199 s += 2;
1200 squaretoxy(square, &x, &y);
1201 piece = getpiece(b, x, y);
1202
1203 /* target location */
1204 square[0] = *s;
1205 square[1] = *(s + 1);
1206 squaretoxy(square, &x2, &y2);
1207
1208 /* take piece (can be your own) */
1209 takepiece = getpiece(b, x2, y2);
1210
1211 s += 2;
1212
1213 promote = 0;
1214 /* is a valid piece? should be queen, rook, bishop, knig…
1215 if (isvalidpiece(*s)) {
1216 if (side == 'w')
1217 promote = TOUPPER(*s);
1218 else
1219 promote = TOLOWER(*s);
1220 s++;
1221 }
1222
1223 /* took piece of opponent */
1224 tookpiece = (side == 'w' && isblackpiece(takepiece)) ||
1225 (side == 'b' && iswhitepiece(takepiece));
1226
1227 /* if pawn move or taken a piece increase halfmove count…
1228 if (piece == 'p' || piece == 'P' || tookpiece)
1229 b->halfmove = 0;
1230 else
1231 b->halfmove++;
1232
1233 if (!onlylastmove && side == 'w')
1234 pgn("%d. ", b->movenumber);
1235
1236 /* castled this move? */
1237 castled = NULL;
1238
1239 /* castling */
1240 if ((piece == 'K' && y == 7 && y2 == 7) ||
1241 (piece == 'k' && y == 0 && y2 == 0)) {
1242 rookpiece = piece == 'K' ? 'R' : 'r';
1243
1244 /* kingside castling */
1245 if (x2 > x + 1 || (x2 > x && takepiece == rookpi…
1246 castled = "O-O";
1247 for (i = x; i < 8; i++) {
1248 if (getpiece(b, i, y2) == rookpi…
1249 place(b, 0, x, y); /* cl…
1250 place(b, 0, i, y2); /* c…
1251 place(b, rookpiece, 5, y…
1252 place(b, piece, 6, y2); …
1253 x2 = i; /* update square…
1254 break;
1255 }
1256 }
1257 } else if (x2 < x - 1 || (x2 < x && takepiece ==…
1258 /* queenside castling */
1259 castled = "O-O-O";
1260 for (i = x; i >= 0; i--) {
1261 if (getpiece(b, i, y2) == rookpi…
1262 place(b, 0, x, y); /* cl…
1263 place(b, 0, i, y2); /* c…
1264 place(b, rookpiece, 3, y…
1265 place(b, piece, 2, y2); …
1266 x2 = i; /* update square…
1267 break;
1268 }
1269 }
1270 }
1271 }
1272
1273 /* remove the ability to castle */
1274 if (piece == 'K') {
1275 b->white_can_castle[0] = b->white_can_castle[1] …
1276 } else if (piece == 'k') {
1277 b->black_can_castle[0] = b->black_can_castle[1] …
1278 } else if (piece == 'R' && y == 7) {
1279 for (i = 0; i < 8; i++) {
1280 if (getpiece(b, i, y) == 'K') {
1281 if (i < x)
1282 b->white_can_castle[0] =…
1283 else if (i > x)
1284 b->white_can_castle[1] =…
1285 break;
1286 }
1287 }
1288 } else if (piece == 'r' && y == 0) {
1289 for (i = 0; i < 8; i++) {
1290 if (getpiece(b, i, y) == 'k') {
1291 if (i > x)
1292 b->black_can_castle[1] =…
1293 else if (i < x)
1294 b->black_can_castle[0] =…
1295 break;
1296 }
1297 }
1298 }
1299
1300 /* taken en passant? */
1301 tookeps = 0;
1302 if (x2 == b->enpassantsquare[0] && y2 == b->enpassantsqu…
1303 (piece == 'P' || piece == 'p')) {
1304 /* clear square */
1305 place(b, 0, x2, piece == 'P' ? y2 + 1 : y2 - 1);
1306 /* set a piece is taken */
1307 tookpiece = 1;
1308 takepiece = piece == 'P' ? 'p' : 'P';
1309 tookeps = 1;
1310 }
1311
1312 /* the en passant square resets after a move */
1313 px = b->enpassantsquare[0] = -1;
1314 py = b->enpassantsquare[1] = -1;
1315
1316 /* set en passant square:
1317 moved 2 squares and there is an opponent pawn next to…
1318 if (piece == 'P' && y == 6 && y2 == 4) {
1319 if (isenpassantplayed(b, side, x, y2)) {
1320 px = b->enpassantsquare[0] = x;
1321 py = b->enpassantsquare[1] = 5;
1322 }
1323 } else if (piece == 'p' && y == 1 && y2 == 3) {
1324 if (isenpassantplayed(b, side, x, y2)) {
1325 px = b->enpassantsquare[0] = x;
1326 py = b->enpassantsquare[1] = 2;
1327 }
1328 }
1329
1330 /* PGN for move, if output is not PGN then skip this ste…
1331 if (outputmode == ModePGN || outputmode == ModeSpeak) {
1332 if (castled) {
1333 pgn("%s", castled);
1334
1335 if (side == 'w')
1336 speak(dutchmode ? "wit " : "whit…
1337 else if (side == 'b')
1338 speak(dutchmode ? "zwart " : "bl…
1339
1340 if (!strcmp(castled, "O-O"))
1341 speak(dutchmode ? "rokeert aan k…
1342 else
1343 speak(dutchmode ? "rokeert aan d…
1344 } else {
1345 if (side == 'w')
1346 speak(dutchmode ? "witte " : "wh…
1347 else if (side == 'b')
1348 speak(dutchmode ? "zwarte " : "b…
1349
1350 if (!tookpiece) {
1351 if (!dutchmode)
1352 speak("moves ");
1353 speakpiece(piece);
1354 }
1355
1356 /* pawn move needs no notation */
1357 if (piece != 'p' && piece != 'P') {
1358 pgn("%c", pgnpiece(piece));
1359
1360 /* check ambiguity for certain p…
1361 countambigousmoves(b, side, piec…
1362 &countfile, &countrank, …
1363
1364 if (countfile > 1 || countboard …
1365 pgn("%c", xtofile(x));
1366 speak("%c", xtofile(x));
1367 }
1368 if (countrank > 1) {
1369 pgn("%c", ytorank(y));
1370 speak("%c", ytorank(y));
1371 }
1372 if (countfile > 1 || countrank >…
1373 speak(" ");
1374 }
1375
1376 if (tookpiece) {
1377 /* pawn captures are prefixed by…
1378 if (piece == 'p' || piece == 'P')
1379 pgn("%c", xtofile(x));
1380 pgn("x");
1381 speakpiece(piece);
1382 speak(dutchmode ? "slaat " : "ta…
1383 speakpiece(takepiece);
1384 speak(dutchmode ? "op " : "on ");
1385 speak("%c%c ", xtofile(x2), ytor…
1386 if (tookeps)
1387 speak("en passant ");
1388 } else {
1389 speak(dutchmode ? "naar " : "to …
1390 speak("%c%c ", xtofile(x2), ytor…
1391 }
1392 pgn("%c%c", xtofile(x2), ytorank(y2));
1393
1394 /* possible promotion: queen, rook, bish…
1395 if (promote) {
1396 speak(dutchmode ? "en promoot na…
1397 speakpiece(promote);
1398
1399 pgn("=%c", pgnpiece(promote));
1400 }
1401 }
1402 }
1403
1404 /* clear previous square (if not castled) */
1405 if (!castled) {
1406 place(b, 0, x, y);
1407 /* place piece or new promoted piece */
1408 if (promote)
1409 piece = promote;
1410 place(b, piece, x2, y2);
1411 }
1412
1413 if (ischeckmated(b, otherside)) {
1414 /* reset en passant square on checkmate */
1415 b->enpassantsquare[0] = -1;
1416 b->enpassantsquare[1] = -1;
1417
1418 pgn("#");
1419 speak(dutchmode ? "mat" : "checkmate");
1420 } else if (isincheck(b, otherside)) {
1421 pgn("+");
1422 speak(dutchmode ? "schaak" : "check");
1423 }
1424
1425 /* a move by black increases the move number */
1426 if (side == 'b')
1427 b->movenumber++;
1428
1429 /* switch which side it is to move */
1430 b->side_to_move = otherside;
1431
1432 if (!*s)
1433 break;
1434 }
1435
1436 if (!firstmove) {
1437 pgn("\n");
1438 speak("\n");
1439 }
1440
1441 /* highlight last move */
1442 if (b->highlights) {
1443 highlightmove(b, x, y);
1444 highlightmove(b, x2, y2);
1445
1446 /* highlight king in check or mate */
1447 if (isincheck(b, b->side_to_move) &&
1448 findking(b, b->side_to_move, &x, &y))
1449 highlightcheck(b, x, y);
1450 }
1451 }
1452
1453 void
1454 usage(char *argv0)
1455 {
1456 fprintf(stderr, "usage: %s [-cCfFhH] [-l] [-m mapping] "
1457 "[-o ascii|fen|pgn|speak|svg|tty] [-sS] [-t default|gree…
1458 "[FEN] [moves]\n", argv0);
1459 exit(1);
1460 }
1461
1462 /* CGI: get parameter */
1463 char *
1464 getparam(const char *query, const char *s)
1465 {
1466 const char *p, *last = NULL;
1467 size_t len;
1468
1469 len = strlen(s);
1470 for (p = query; (p = strstr(p, s)); p += len) {
1471 if (p[len] == '=' && (p == query || p[-1] == '&' || p[-1…
1472 last = p + len + 1;
1473 }
1474
1475 return (char *)last;
1476 }
1477
1478 int
1479 hexdigit(int c)
1480 {
1481 if (c >= '0' && c <= '9')
1482 return c - '0';
1483 else if (c >= 'A' && c <= 'F')
1484 return c - 'A' + 10;
1485 else if (c >= 'a' && c <= 'f')
1486 return c - 'a' + 10;
1487
1488 return 0;
1489 }
1490
1491 /* CGI: decode until NUL separator or end of "key". */
1492 int
1493 decodeparam(char *buf, size_t bufsiz, const char *s)
1494 {
1495 size_t i;
1496
1497 if (!bufsiz)
1498 return -1;
1499
1500 for (i = 0; *s && *s != '&'; s++) {
1501 switch (*s) {
1502 case '%':
1503 if (i + 3 >= bufsiz)
1504 return -1;
1505 if (!ISXDIGIT((unsigned char)*(s+1)) ||
1506 !ISXDIGIT((unsigned char)*(s+2)))
1507 return -1;
1508 buf[i++] = hexdigit(*(s+1)) * 16 + hexdigit(*(s+…
1509 s += 2;
1510 break;
1511 case '+':
1512 if (i + 1 >= bufsiz)
1513 return -1;
1514 buf[i++] = ' ';
1515 break;
1516 default:
1517 if (i + 1 >= bufsiz)
1518 return -1;
1519 buf[i++] = *s;
1520 break;
1521 }
1522 }
1523 buf[i] = '\0';
1524
1525 return i;
1526 }
1527
1528 enum outputmode
1529 outputnametomode(const char *s)
1530 {
1531 if (!strcmp(s, "ascii"))
1532 return ModeASCII;
1533 else if (!strcmp(s, "fen"))
1534 return ModeFEN;
1535 else if (!strcmp(s, "pgn"))
1536 return ModePGN;
1537 else if (!strcmp(s, "speak"))
1538 return ModeSpeak;
1539 else if (!strcmp(s, "svg"))
1540 return ModeSVG;
1541 else if (!strcmp(s, "tty"))
1542 return ModeTTY;
1543 else
1544 return ModeInvalid;
1545 }
1546
1547 void
1548 output(struct board *b)
1549 {
1550 switch (outputmode) {
1551 case ModeASCII: output_ascii(b); break;
1552 case ModeFEN: output_fen(b); break;
1553 case ModePGN: break; /* handled in parsemoves…
1554 case ModeSVG: output_svg(b); break;
1555 case ModeTTY: output_tty(b); break;
1556 default: break;
1557 }
1558 }
1559
1560 /* CGI mode */
1561 int
1562 cgi_mode(void)
1563 {
1564 struct board board;
1565 char *query, *p;
1566 char buf[4096];
1567
1568 board_init(&board);
1569
1570 query = getenv("QUERY_STRING");
1571 if ((p = getparam(query, "flip")) && (*p == '0' || *p == '1'))
1572 board.flipboard = *p == '1' ? 1 : 0;
1573 if ((p = getparam(query, "side")) && (*p == '0' || *p == '1'))
1574 board.showside = *p == '1' ? 1 : 0;
1575 if ((p = getparam(query, "coords")) && (*p == '0' || *p == '1'))
1576 board.showcoords = *p == '1' ? 1 : 0;
1577 if ((p = getparam(query, "dutch")) && *p == '1') {
1578 dutchmode = 1;
1579 pgn_piecemapping = "KDTLP";
1580 }
1581 if ((p = getparam(query, "output"))) {
1582 if (decodeparam(buf, sizeof(buf), p) == -1)
1583 goto badrequest;
1584 outputmode = outputnametomode(buf);
1585 if (outputmode == ModeInvalid)
1586 goto badrequest;
1587 }
1588 if ((p = getparam(query, "theme"))) {
1589 if (decodeparam(buf, sizeof(buf), p) == -1)
1590 goto badrequest;
1591 board_set_theme(&board, buf);
1592 }
1593 if ((p = getparam(query, "fen"))) {
1594 if (decodeparam(buf, sizeof(buf), p) == -1)
1595 goto badrequest;
1596 board_setup_fen(&board, buf);
1597 } else {
1598 board_setup_fen(&board, "startpos");
1599 }
1600 if ((p = getparam(query, "moves"))) {
1601 if (decodeparam(buf, sizeof(buf), p) == -1)
1602 goto badrequest;
1603 }
1604 if (!p)
1605 buf[0] = '\0';
1606
1607 fputs("Status: 200 OK\r\n", stdout);
1608 if (outputmode == ModeSVG)
1609 fputs("Content-Type: image/svg+xml\r\n\r\n", stdout);
1610 else
1611 fputs("Content-Type: text/plain\r\n\r\n", stdout);
1612
1613 board_playmoves(&board, buf);
1614
1615 output(&board);
1616
1617 return 0;
1618
1619 badrequest:
1620 fputs("Status: 400 Bad Request\r\n", stdout);
1621 fputs("Content-Type: text/plain\r\n", stdout);
1622 fputs("\r\n", stdout);
1623 fputs("Bad request: make sure to use valid parameters\n", stdout…
1624
1625 return 1;
1626 }
1627
1628 int
1629 main(int argc, char *argv[])
1630 {
1631 struct board board;
1632 const char *fen, *moves;
1633 int i, j;
1634
1635 #ifdef __OpenBSD__
1636 if (pledge("stdio", NULL) == -1)
1637 err(1, "pledge");
1638 #endif
1639
1640 if (getenv("QUERY_STRING"))
1641 return cgi_mode();
1642
1643 board_init(&board);
1644 fen = "startpos";
1645 moves = "";
1646
1647 for (i = 1; i < argc; i++) {
1648 if (argv[i][0] != '-')
1649 break;
1650
1651 for (j = 1; argv[i][j]; j++) {
1652 switch (argv[i][j]) {
1653 case 'c': board.showcoords = 1; break;
1654 case 'C': board.showcoords = 0; break;
1655 case 'd': dutchmode = 1; break; /* top secret du…
1656 case 'f': board.flipboard = 1; break;
1657 case 'F': board.flipboard = 0; break;
1658 case 'h': board.highlights = 1; break;
1659 case 'H': board.highlights = 0; break;
1660 case 'l': onlylastmove = 1; silent = 1; break;
1661 case 'm': /* remap PGN */
1662 if (i + 1 >= argc)
1663 usage(argv[0]);
1664 i++;
1665 if (strlen(argv[i]) != 5)
1666 usage(argv[0]);
1667 pgn_piecemapping = argv[i];
1668 goto next;
1669 case 'o': /* output format */
1670 if (i + 1 >= argc)
1671 usage(argv[0]);
1672 i++;
1673
1674 outputmode = outputnametomode(argv[i]);
1675 if (outputmode == ModeInvalid)
1676 usage(argv[0]);
1677 goto next;
1678 case 's': board.showside = 1; break;
1679 case 'S': board.showside = 0; break;
1680 case 't': /* theme name */
1681 if (i + 1 >= argc)
1682 usage(argv[0]);
1683 i++;
1684 board_set_theme(&board, argv[i]);
1685 goto next;
1686 default:
1687 usage(argv[0]);
1688 break;
1689 }
1690 }
1691 next:
1692 ;
1693 }
1694 if (i < argc) {
1695 fen = argv[i];
1696 i++;
1697 }
1698 if (i < argc) {
1699 moves = argv[i];
1700 i++;
1701 }
1702
1703 board_setup_fen(&board, fen);
1704 board_playmoves(&board, moves);
1705
1706 output(&board);
1707
1708 return 0;
1709 }
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.