Introduction
Introduction Statistics Contact Development Disclaimer Help
command-line parsing for formats, add OpenBSD pledge - chess-puzzles - chess pu…
git clone git://git.codemadness.org/chess-puzzles
Log
Files
Refs
README
LICENSE
---
commit b50c708ac5b44591b62aaa443022625c3fdd44cb
parent 3d921c8e0e2011ae994e508e89fc71086fffd598
Author: Hiltjo Posthuma <[email protected]>
Date: Wed, 20 Dec 2023 22:31:43 +0100
command-line parsing for formats, add OpenBSD pledge
Diffstat:
M Makefile | 7 ++-----
A fen.c | 730 +++++++++++++++++++++++++++++…
D fen_to_svg.c | 700 -----------------------------…
3 files changed, 732 insertions(+), 705 deletions(-)
---
diff --git a/Makefile b/Makefile
@@ -1,8 +1,5 @@
build: clean
- ${CC} -o fen_to_svg fen_to_svg.c ${CFLAGS} ${LDFLAGS}
- ln -s fen_to_svg fen_to_ascii
- ln -s fen_to_svg fen_to_tty
- ln -s fen_to_svg fen_to_fen
+ ${CC} -o fen fen.c ${CFLAGS} ${LDFLAGS}
db:
rm -f lichess_db_puzzle.csv.zst lichess_db_puzzle.csv
@@ -10,4 +7,4 @@ db:
zstd -d < lichess_db_puzzle.csv.zst > lichess_db_puzzle.csv
clean:
- rm -f fen_to_ascii fen_to_fen fen_to_svg fen_to_tty
+ rm -f fen
diff --git a/fen.c b/fen.c
@@ -0,0 +1,730 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __OpenBSD__
+#include <err.h>
+#include <unistd.h>
+#endif
+
+/* macro for truecolor RGB output to tty */
+#define SETFGCOLOR(r,g,b) printf("\x1b[38;2;%d;%d;%dm", r, g, b)
+#define SETBGCOLOR(r,g,b) printf("\x1b[48;2;%d;%d;%dm", r, g, b)
+
+static char board[8][8];
+static char highlight[8][8];
+
+static int side_to_move = 'w'; /* default: white to move */
+static int white_can_castle[2] = { 0, 0 }; /* allow king side, allow queen sid…
+static int black_can_castle[2] = { 0, 0 }; /* allow king side, allow queen sid…
+static int enpassantsquare[2] = { -1, -1 };
+static int movenumber = 1;
+static int halfmove = 0;
+
+/* lichess default theme colors */
+static const int border[] = { 0x70, 0x49, 0x2d };
+static const int darksquare[] = { 0xb5, 0x88, 0x63 };
+static const int lightsquare[] = { 0xf0, 0xd9, 0xb5 };
+static const int darksquarehi[] = { 0xaa, 0xa2, 0x3a };
+static const int lightsquarehi[] = { 0xcd, 0xd2, 0x6a };
+
+static int showcoords = 1; /* config: show board coordinates? */
+static int flipboard = 0; /* config: flip board ? */
+
+int
+isvalidsquare(int x, int y)
+{
+ return !(x < 0 || x >= 8 || y < 0 || y >= 8);
+}
+
+int
+isvalidpiece(int c)
+{
+ static char pieces[] = "PNBRQKpnbrqk";
+
+ return strchr(pieces, c) ? 1 : 0;
+}
+
+/* place a piece, if possible */
+void
+place(int piece, int x, int y)
+{
+ if (!isvalidsquare(x, y))
+ return;
+
+ board[y][x] = piece;
+}
+
+/* get piece, if possible */
+int
+getpiece(int x, int y)
+{
+ if (!isvalidsquare(x, y))
+ return 0;
+ return board[y][x];
+}
+
+int
+squaretoxy(const char *s, int *x, int *y)
+{
+ if (*s >= 'a' && *s <= 'h' &&
+ *(s + 1) >= '1' && *(s + 1) <= '8') {
+ *x = *s - 'a';
+ *y = '8' - *(s + 1);
+ return 1;
+ }
+ return 0;
+}
+
+void
+highlightmove(int x1, int y1, int x2, int y2)
+{
+ if (isvalidsquare(x1, y1))
+ highlight[y1][x1] = 1;
+
+ if (isvalidsquare(x2, y2))
+ highlight[y2][x2] = 1;
+}
+
+void
+showboardfen(void)
+{
+ int x, y, piece, skip = 0;
+
+ for (y = 0; y < 8; y++) {
+ if (y > 0)
+ putchar('/');
+ skip = 0;
+ for (x = 0; x < 8; x++) {
+ piece = getpiece(x, y);
+ if (piece) {
+ if (skip)
+ putchar(skip + '0');
+ putchar(piece);
+ skip = 0;
+ } else {
+ skip++;
+ }
+ }
+ if (skip)
+ putchar(skip + '0');
+ }
+ printf(" %c ", side_to_move);
+ if (white_can_castle[0])
+ putchar('K');
+ if (white_can_castle[1])
+ putchar('Q');
+ if (black_can_castle[0])
+ putchar('k');
+ if (black_can_castle[1])
+ putchar('q');
+ if ((white_can_castle[0] + white_can_castle[1] +
+ black_can_castle[0] + black_can_castle[1]) == 0)
+ putchar('-'); /* no castling for either side */
+ putchar(' ');
+
+ if (enpassantsquare[0] != -1 && enpassantsquare[1] != -1) {
+ putchar('a' + enpassantsquare[0]);
+ putchar('8' - enpassantsquare[1]);
+ } else {
+ putchar('-');
+ }
+ printf(" %d %d", halfmove, movenumber);
+}
+
+void
+showpiece_svg(int c)
+{
+ const char *s = "";
+
+ /* lichess default set,
+ extracted from https://github.com/lichess-org/lila/tree/master/publ…
+ switch (c) {
+ case 'K': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
+ case 'Q': s = "<g fill=\"#fff\" fill-rule=\"evenodd\" stroke=\"#000\" …
+ case 'R': s = "<g fill=\"#fff\" fill-rule=\"evenodd\" stroke=\"#000\" …
+ case 'B': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
+ case 'N': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
+ case 'P': s = "<path d=\"M22.5 9c-2.21 0-4 1.79-4 4 0 .89.29 1.71.78 2…
+ case 'k': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
+ case 'q': s = "<g fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\…
+ case 'r': s = "<g fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\…
+ case 'b': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
+ case 'n': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
+ case 'p': s = "<path d=\"M22.5 9c-2.21 0-4 1.79-4 4 0 .89.29 1.71.78 2…
+ }
+
+ if (*s)
+ fputs(s, stdout);
+}
+
+void
+showboard_svg(void)
+{
+ const int *color;
+ int ix, iy, x, y, piece;
+
+ fputs("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www…
+ "<svg width=\"360\" height=\"360\" viewBox=\"0 0 360 360\" xml…
+ "<rect fill=\"#fff\" stroke=\"#000\" x=\"0\" y=\"0\" width=\"3…
+
+ fputs("<!-- Board FEN: ", stdout);
+ showboardfen();
+ fputs(" -->\n", stdout);
+
+ for (iy = 0; iy < 8; iy++) {
+ y = flipboard ? 7 - iy : iy;
+
+ for (ix = 0; ix < 8; ix++) {
+ x = flipboard ? 7 - ix : ix;
+
+ if (x % 2 == 0) {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? lightsquareh…
+ else
+ color = highlight[y][x] ? darksquarehi…
+ } else {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? darksquarehi…
+ else
+ color = highlight[y][x] ? lightsquareh…
+ }
+
+ printf("<g><rect x=\"%d\" y=\"%d\" width=\"45\" height…
+ ix * 45, iy * 45, color[0], color[1], color[2]…
+
+ piece = getpiece(x, y);
+ if (piece) {
+ printf("<g transform=\"translate(%d %d)\">", i…
+ showpiece_svg(piece);
+ fputs("</g>\n", stdout);
+ }
+ }
+ }
+
+ if (showcoords) {
+ ix = 7;
+ x = flipboard ? 0 : 7;
+ for (iy = 0; iy < 8; iy++) {
+ y = flipboard ? 7 - iy : iy;
+
+ /* inverse square color for text */
+ if (x % 2 == 0) {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? darksquarehi…
+ else
+ color = highlight[y][x] ? lightsquareh…
+ } else {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? lightsquareh…
+ else
+ color = highlight[y][x] ? darksquarehi…
+ }
+
+ printf("<text x=\"%d\" y=\"%d\" fill=\"#%02x%02x%02x\"…
+ (ix + 1) * 45 - 2, (iy * 45) + 10, color[0], c…
+ }
+ iy = 7;
+ y = flipboard ? 0 : 7;
+ for (ix = 0; ix < 8; ix++) {
+ x = flipboard ? 7 - ix : ix;
+
+ /* inverse square color for text */
+ if (x % 2 == 0) {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? darksquarehi…
+ else
+ color = highlight[y][x] ? lightsquareh…
+ } else {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? lightsquareh…
+ else
+ color = highlight[y][x] ? darksquarehi…
+ }
+
+ printf("<text x=\"%d\" y=\"%d\" fill=\"#%02x%02x%02x\"…
+ (ix * 45) + 2, (iy + 1) * 45 - 3, color[0], co…
+ }
+ }
+
+ fputs("</svg>\n", stdout);
+}
+
+void
+showpiece_tty(int c)
+{
+ const char *s = "";
+
+ /* simple or use unicode character */
+#if 0
+ putchar(c);
+ return;
+#endif
+
+ switch (c) {
+ case 'K': s = "♔"; break;
+ case 'Q': s = "♕"; break;
+ case 'R': s = "♖"; break;
+ case 'B': s = "♗"; break;
+ case 'N': s = "♘"; break;
+ case 'P': s = "♙"; break;
+ case 'k': s = "♚"; break;
+ case 'q': s = "♛"; break;
+ case 'r': s = "♜"; break;
+ case 'b': s = "♝"; break;
+ case 'n': s = "♞"; break;
+ case 'p': s = "♟"; break;
+ }
+
+ if (*s)
+ fputs(s, stdout);
+}
+
+/* show board */
+void
+showboard_tty(void)
+{
+ const int *color;
+ int ix, iy, x, y, piece;
+
+ printf("Board FEN:\n");
+ showboardfen();
+ printf("\n\n");
+
+ SETFGCOLOR(0x00, 0x00, 0x00);
+
+ color = border;
+ SETBGCOLOR(color[0], color[1], color[2]);
+ SETFGCOLOR(0xff, 0xff, 0xff);
+ fputs(" ", stdout);
+ printf("\x1b[0m"); /* reset */
+ SETFGCOLOR(0x00, 0x00, 0x00);
+ putchar('\n');
+
+ for (iy = 0; iy < 8; iy++) {
+ y = flipboard ? 7 - iy : iy;
+
+ color = border;
+ SETBGCOLOR(color[0], color[1], color[2]);
+ SETFGCOLOR(0xff, 0xff, 0xff);
+ fputs(" ", stdout);
+
+ for (ix = 0; ix < 8; ix++) {
+ x = flipboard ? 7 - ix : ix;
+
+ if (x % 2 == 0) {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? lightsquareh…
+ else
+ color = highlight[y][x] ? darksquarehi…
+ } else {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? darksquarehi…
+ else
+ color = highlight[y][x] ? lightsquareh…
+ }
+ SETBGCOLOR(color[0], color[1], color[2]);
+
+ fputs(" ", stdout);
+ piece = getpiece(x, y);
+ if (piece) {
+ if (piece >= 'A' && piece <= 'Z')
+ SETFGCOLOR(0xff, 0xff, 0xff);
+ else
+ SETFGCOLOR(0x00, 0x00, 0x00);
+ /* workaround: use black chess symbol, because…
+ is filled and better visible */
+ showpiece_tty(tolower(piece));
+ } else {
+ fputs(" ", stdout);
+ }
+ fputs(" ", stdout);
+ }
+ printf("\x1b[0m"); /* reset */
+
+ color = border;
+ SETBGCOLOR(color[0], color[1], color[2]);
+ SETFGCOLOR(0xff, 0xff, 0xff);
+ if (showcoords) {
+ putchar(' ');
+ putchar('8' - y);
+ putchar(' ');
+ } else {
+ fputs(" ", stdout);
+ }
+
+ printf("\x1b[0m"); /* reset */
+ SETFGCOLOR(0x00, 0x00, 0x00);
+ putchar('\n');
+ }
+ color = border;
+ SETBGCOLOR(color[0], color[1], color[2]);
+ SETFGCOLOR(0xff, 0xff, 0xff);
+ if (showcoords) {
+ if (flipboard)
+ fputs(" h g f e d c b a ", stdout);
+ else
+ fputs(" a b c d e f g h ", stdout);
+ } else {
+ fputs(" ", stdout);
+ }
+ printf("\x1b[0m"); /* reset */
+ printf("\n");
+}
+
+void
+showpiece_ascii(int c)
+{
+ putchar(c);
+}
+
+/* OnlyFENs */
+void
+showboard_fen(void)
+{
+ showboardfen();
+ printf("\n");
+}
+
+/* show board */
+void
+showboard_ascii(void)
+{
+ int hi[3] = { '>', ' ', '<' };
+ int dark[3] = { '.', '.', '.' };
+ int light[3] = { ' ', ' ', ' ' };
+ int *color, ix, iy, x, y, piece;
+
+ printf("Board FEN:\n");
+ showboardfen();
+ printf("\n\n");
+
+ for (iy = 0; iy < 8; iy++) {
+ y = flipboard ? 7 - iy : iy;
+
+ fputs("+---+---+---+---+---+---+---+---+\n", stdout);
+ for (ix = 0; ix < 8; ix++) {
+ x = flipboard ? 7 - ix : ix;
+
+ if (x % 2 == 0) {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? hi : light;
+ else
+ color = highlight[y][x] ? hi : dark;
+ } else {
+ if (y % 2 == 0)
+ color = highlight[y][x] ? hi : dark;
+ else
+ color = highlight[y][x] ? hi : light;
+ }
+
+ if (ix == 0)
+ putchar('|');
+ putchar(color[0]);
+ piece = getpiece(x, y);
+ if (piece)
+ showpiece_ascii(piece);
+ else
+ putchar(color[1]);
+ putchar(color[2]);
+ putchar('|');
+ }
+ if (showcoords) {
+ putchar(' ');
+ putchar('8' - y);
+ }
+ putchar('\n');
+ }
+ fputs("+---+---+---+---+---+---+---+---+\n", stdout);
+ if (showcoords) {
+ if (flipboard)
+ printf(" h | g | f | e | d | c | b | a |\n");
+ else
+ printf(" a | b | c | d | e | f | g | h |\n");
+ }
+
+ fputs("\n", stdout);
+}
+
+void
+usage(char *argv0)
+{
+ fprintf(stderr, "usage: %s [-cCfF] [-o ascii|fen|tty|svg] [FEN] [moves…
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *fen, *moves, *s, *output = "svg";
+ int x, y, x2, y2, field, piece, takepiece;
+ int i, j;
+ char square[3];
+ long l;
+
+#ifdef __OpenBSD__
+ if (pledge("stdio", NULL) = -1)
+ err(1, "pledge");
+#endif
+
+ fen = "startpos";
+ moves = "";
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] != '-')
+ break;
+
+ for (j = 1; argv[i][j]; j++) {
+ switch (argv[i][j]) {
+ case 'c': showcoords = 1; break;
+ case 'C': showcoords = 0; break;
+ case 'f': flipboard = 1; break;
+ case 'F': flipboard = 0; break;
+ case 'o':
+ if (i + 1 >= argc)
+ usage(argv[0]);
+ output = argv[++i];
+ goto next;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+next:
+ }
+ if (i < argc) {
+ fen = argv[i];
+ i++;
+ }
+ if (i < argc) {
+ moves = argv[i];
+ i++;
+ }
+
+ if (!strcmp(fen, "startpos"))
+ fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 …
+
+ /* initial board state, FEN format */
+ x = y = field = 0;
+ for (s = fen; *s && field < 6; s++) {
+ switch (field) {
+ case 0: /* piece placement data */
+ /* skip square */
+ if (*s >= '1' && *s <= '9') {
+ x += (*s - '0');
+ continue;
+ }
+ /* next rank */
+ if (*s == '/') {
+ x = 0;
+ y++;
+ continue;
+ }
+ /* is piece? place it */
+ if (isvalidpiece(*s))
+ place(*s, x++, y);
+ break;
+ case 1: /* active color */
+ if (*s == 'w' || *s == 'b')
+ side_to_move = *s;
+ break;
+ case 2: /* castling availability */
+ if (*s == '-') {
+ white_can_castle[0] = 0;
+ white_can_castle[1] = 0;
+ black_can_castle[0] = 0;
+ black_can_castle[1] = 0;
+ } else if (*s == 'K') {
+ white_can_castle[0] = 1;
+ } else if (*s == 'Q') {
+ white_can_castle[1] = 1;
+ } else if (*s == 'k') {
+ black_can_castle[0] = 1;
+ } else if (*s == 'q') {
+ black_can_castle[1] = 1;
+ }
+ break;
+ case 3: /* en passant square */
+ if (*s >= 'a' && *s <= 'h' &&
+ *(s + 1) >= '1' && *(s + 1) >= '6') {
+
+ square[0] = *s;
+ square[1] = *(s + 1);
+ square[2] = '\0';
+ squaretoxy(square, &x, &y);
+
+ enpassantsquare[0] = x;
+ enpassantsquare[1] = y;
+ }
+ break;
+ case 4: /* halfmove */
+ if (!(*s >= '0' && *s <= '9'))
+ continue;
+
+ l = strtol(s, NULL, 10);
+ if (l >= 0 && l < 32767) {
+ halfmove = l;
+
+ for (; *s && isdigit((unsigned char)*s); s++)
+ ;
+ }
+ break;
+ case 5: /* move number */
+ if (!(*s >= '0' && *s <= '9'))
+ continue;
+
+ l = strtol(s, NULL, 10);
+ if (l >= 0 && l < 32767) {
+ movenumber = (int)l;
+ for (; *s && isdigit((unsigned char)*s); s++)
+ ;
+ }
+ break;
+ }
+ if (!*s)
+ break;
+
+ /* next field, fields are: piece placement data, active color,
+ Castling availability, En passant target square,
+ Halfmove clock, Fullmove number */
+ if (*s == ' ') {
+ field++;
+ continue;
+ }
+ }
+
+ /* process moves */
+ square[2] = '\0';
+ x = y = x2 = y2 = -1;
+ for (s = moves; *s; s++) {
+ if (*s == ' ')
+ continue;
+ if ((*s >= 'a' && *s <= 'h') &&
+ (*(s + 1) >= '1' && *(s + 1) <= '8') &&
+ (*(s + 2) >= 'a' && *(s + 2) <= 'h') &&
+ (*(s + 3) >= '1' && *(s + 3) <= '8')) {
+ square[0] = *s;
+ square[1] = *(s + 1);
+
+ s += 2;
+ squaretoxy(square, &x, &y);
+ piece = getpiece(x, y);
+
+ place(0, x, y); /* clear square */
+
+ /* place piece at new location */
+ square[0] = *s;
+ square[1] = *(s + 1);
+ squaretoxy(square, &x2, &y2);
+ takepiece = getpiece(x2, y2);
+ place(piece, x2, y2);
+ s += 2;
+
+ /* if pawn move or taken a piece increase halfmove cou…
+ if (piece == 'p' || piece == 'P' || takepiece != 0)
+ halfmove = 0;
+ else
+ halfmove++;
+
+ /* castling */
+ if (piece == 'K' && y == 7 && y2 == 7 && x == 4) {
+ /* white: kingside castling: "e1g1" */
+ if (x2 == 6) {
+ place('R', x2 - 1, y2);
+ x2 = 7;
+ y2 = 7;
+ place(0, x2, y2); /* clear rook square…
+ } else if (x2 == 2) {
+ /* white: queenside castling: "e1c1" */
+ place('R', x2 + 1, y2);
+ x2 = 0;
+ y2 = 7;
+ place(0, x2, y2);
+ }
+ } else if (piece == 'k' && y == 0 && y2 == 0 && x == 4…
+ /* black: kingside castling: "e8g8" */
+ if (x2 == 6) {
+ place('r', x2 - 1, y2);
+ x2 = 7;
+ y2 = 0;
+ place(0, x2, y2); /* clear rook square…
+ } else if (x2 == 2) {
+ /* black: queenside castling: "e8c8" */
+ place('r', x2 + 1, y2);
+ x2 = 0;
+ y2 = 0;
+ place(0, x2, y2); /* clear rook square…
+ }
+ }
+
+ /* remove the ability to castle */
+ if (piece == 'K') {
+ white_can_castle[0] = white_can_castle[1] = 0;
+ } else if (piece == 'k') {
+ black_can_castle[0] = black_can_castle[1] = 0;
+ } else if (piece == 'R') {
+ if (x == 7 && y == 7)
+ white_can_castle[0] = 0;
+ else if (x == 0 && y == 7)
+ white_can_castle[1] = 0;
+ } else if (piece == 'r') {
+ if (x == 0 && y == 0)
+ black_can_castle[1] = 0;
+ else if (x == 7 && y == 0)
+ black_can_castle[0] = 0;
+ }
+
+ /* the en passant square resets after a move */
+ enpassantsquare[0] = -1;
+ enpassantsquare[1] = -1;
+ /* moved 2 squares and there is an opponent pawn next …
+ if (piece == 'P' && y == 6 && y2 == 4) {
+ if (getpiece(x - 1, y2) == 'p' ||
+ getpiece(x + 1, y2) == 'p') {
+ enpassantsquare[0] = x;
+ enpassantsquare[1] = 5;
+ }
+ } else if (piece == 'p' && y == 1 && y2 == 3) {
+ if (getpiece(x - 1, y2) == 'P' ||
+ getpiece(x + 1, y2) == 'P') {
+ enpassantsquare[0] = x;
+ enpassantsquare[1] = 2;
+ }
+ }
+
+ /* possible promotion: queen, knight, bishop */
+ if (*s == 'q' || *s == 'n' || *s == 'b') {
+ if (side_to_move == 'w')
+ piece = toupper((unsigned char)*s);
+ else
+ piece = *s;
+ place(piece, x2, y2);
+ s++;
+ }
+
+ /* a move by black increases the move number */
+ if (side_to_move == 'b')
+ movenumber++;
+
+ /* switch which side it is to move */
+ side_to_move = side_to_move == 'b' ? 'w' : 'b';
+ }
+ }
+ /* highlight last move */
+ highlightmove(x, y, x2, y2);
+
+ if (!strcmp(output, "ascii"))
+ showboard_ascii();
+ else if (!strcmp(output, "fen"))
+ showboard_fen();
+ else if (!strcmp(output, "svg"))
+ showboard_svg();
+ else if (!strcmp(output, "tty"))
+ showboard_tty();
+ else
+ usage(argv[0]);
+
+ return 0;
+}
diff --git a/fen_to_svg.c b/fen_to_svg.c
@@ -1,700 +0,0 @@
-/* TODO: output for PGN notation for moves */
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* macro for truecolor RGB output to tty */
-#define SETFGCOLOR(r,g,b) printf("\x1b[38;2;%d;%d;%dm", r, g, b)
-#define SETBGCOLOR(r,g,b) printf("\x1b[48;2;%d;%d;%dm", r, g, b)
-
-static char board[8][8];
-static char highlight[8][8];
-
-static int side_to_move = 'w'; /* default: white to move */
-static int white_can_castle[2] = { 0, 0 }; /* allow king side, allow queen sid…
-static int black_can_castle[2] = { 0, 0 }; /* allow king side, allow queen sid…
-static int enpassantsquare[2] = { -1, -1 };
-static int movenumber = 1;
-static int halfmove = 0;
-
-/* lichess default theme colors */
-static const int border[] = { 0x70, 0x49, 0x2d };
-static const int darksquare[] = { 0xb5, 0x88, 0x63 };
-static const int lightsquare[] = { 0xf0, 0xd9, 0xb5 };
-static const int darksquarehi[] = { 0xaa, 0xa2, 0x3a };
-static const int lightsquarehi[] = { 0xcd, 0xd2, 0x6a };
-
-static int showcoords = 1; /* config: show board coordinates? */
-static int flipboard = 0; /* config: flip board ? */
-
-int
-isvalidsquare(int x, int y)
-{
- return !(x < 0 || x >= 8 || y < 0 || y >= 8);
-}
-
-int
-isvalidpiece(int c)
-{
- static char pieces[] = "PNBRQKpnbrqk";
-
- return strchr(pieces, c) ? 1 : 0;
-}
-
-/* place a piece, if possible */
-void
-place(int piece, int x, int y)
-{
- if (!isvalidsquare(x, y))
- return;
-
- board[y][x] = piece;
-}
-
-/* get piece, if possible */
-int
-getpiece(int x, int y)
-{
- if (!isvalidsquare(x, y))
- return 0;
- return board[y][x];
-}
-
-int
-squaretoxy(const char *s, int *x, int *y)
-{
- if (*s >= 'a' && *s <= 'h' &&
- *(s + 1) >= '1' && *(s + 1) <= '8') {
- *x = *s - 'a';
- *y = '8' - *(s + 1);
- return 1;
- }
- return 0;
-}
-
-void
-highlightmove(int x1, int y1, int x2, int y2)
-{
- if (isvalidsquare(x1, y1))
- highlight[y1][x1] = 1;
-
- if (isvalidsquare(x2, y2))
- highlight[y2][x2] = 1;
-}
-
-void
-showboardfen(void)
-{
- int x, y, piece, skip = 0;
-
- for (y = 0; y < 8; y++) {
- if (y > 0)
- putchar('/');
- skip = 0;
- for (x = 0; x < 8; x++) {
- piece = getpiece(x, y);
- if (piece) {
- if (skip)
- putchar(skip + '0');
- putchar(piece);
- skip = 0;
- } else {
- skip++;
- }
- }
- if (skip)
- putchar(skip + '0');
- }
- printf(" %c ", side_to_move);
- if (white_can_castle[0])
- putchar('K');
- if (white_can_castle[1])
- putchar('Q');
- if (black_can_castle[0])
- putchar('k');
- if (black_can_castle[1])
- putchar('q');
- if ((white_can_castle[0] + white_can_castle[1] +
- black_can_castle[0] + black_can_castle[1]) == 0)
- putchar('-'); /* no castling for either side */
- putchar(' ');
-
- if (enpassantsquare[0] != -1 && enpassantsquare[1] != -1) {
- putchar('a' + enpassantsquare[0]);
- putchar('8' - enpassantsquare[1]);
- } else {
- putchar('-');
- }
- printf(" %d %d", halfmove, movenumber);
-}
-
-void
-svg_showpiece(int c)
-{
- const char *s = "";
-
- /* lichess default set,
- extracted from https://github.com/lichess-org/lila/tree/master/publ…
- switch (c) {
- case 'K': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
- case 'Q': s = "<g fill=\"#fff\" fill-rule=\"evenodd\" stroke=\"#000\" …
- case 'R': s = "<g fill=\"#fff\" fill-rule=\"evenodd\" stroke=\"#000\" …
- case 'B': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
- case 'N': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
- case 'P': s = "<path d=\"M22.5 9c-2.21 0-4 1.79-4 4 0 .89.29 1.71.78 2…
- case 'k': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
- case 'q': s = "<g fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\…
- case 'r': s = "<g fill-rule=\"evenodd\" stroke=\"#000\" stroke-width=\…
- case 'b': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
- case 'n': s = "<g fill=\"none\" fill-rule=\"evenodd\" stroke=\"#000\" …
- case 'p': s = "<path d=\"M22.5 9c-2.21 0-4 1.79-4 4 0 .89.29 1.71.78 2…
- }
-
- if (*s)
- fputs(s, stdout);
-}
-
-void
-svg_showboard(void)
-{
- const int *color;
- int ix, iy, x, y, piece;
-
- fputs("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
- "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www…
- "<svg width=\"360\" height=\"360\" viewBox=\"0 0 360 360\" xml…
- "<rect fill=\"#fff\" stroke=\"#000\" x=\"0\" y=\"0\" width=\"3…
-
- fputs("<!-- Board FEN: ", stdout);
- showboardfen();
- fputs(" -->\n", stdout);
-
- for (iy = 0; iy < 8; iy++) {
- y = flipboard ? 7 - iy : iy;
-
- for (ix = 0; ix < 8; ix++) {
- x = flipboard ? 7 - ix : ix;
-
- if (x % 2 == 0) {
- if (y % 2 == 0)
- color = highlight[y][x] ? lightsquareh…
- else
- color = highlight[y][x] ? darksquarehi…
- } else {
- if (y % 2 == 0)
- color = highlight[y][x] ? darksquarehi…
- else
- color = highlight[y][x] ? lightsquareh…
- }
-
- printf("<g><rect x=\"%d\" y=\"%d\" width=\"45\" height…
- ix * 45, iy * 45, color[0], color[1], color[2]…
-
- piece = getpiece(x, y);
- if (piece) {
- printf("<g transform=\"translate(%d %d)\">", i…
- svg_showpiece(piece);
- fputs("</g>\n", stdout);
- }
- }
- }
-
- if (showcoords) {
- ix = 7;
- x = flipboard ? 0 : 7;
- for (iy = 0; iy < 8; iy++) {
- y = flipboard ? 7 - iy : iy;
-
- /* inverse square color for text */
- if (x % 2 == 0) {
- if (y % 2 == 0)
- color = highlight[y][x] ? darksquarehi…
- else
- color = highlight[y][x] ? lightsquareh…
- } else {
- if (y % 2 == 0)
- color = highlight[y][x] ? lightsquareh…
- else
- color = highlight[y][x] ? darksquarehi…
- }
-
- printf("<text x=\"%d\" y=\"%d\" fill=\"#%02x%02x%02x\"…
- (ix + 1) * 45 - 2, (iy * 45) + 10, color[0], c…
- }
- iy = 7;
- y = flipboard ? 0 : 7;
- for (ix = 0; ix < 8; ix++) {
- x = flipboard ? 7 - ix : ix;
-
- /* inverse square color for text */
- if (x % 2 == 0) {
- if (y % 2 == 0)
- color = highlight[y][x] ? darksquarehi…
- else
- color = highlight[y][x] ? lightsquareh…
- } else {
- if (y % 2 == 0)
- color = highlight[y][x] ? lightsquareh…
- else
- color = highlight[y][x] ? darksquarehi…
- }
-
- printf("<text x=\"%d\" y=\"%d\" fill=\"#%02x%02x%02x\"…
- (ix * 45) + 2, (iy + 1) * 45 - 3, color[0], co…
- }
- }
-
- fputs("</svg>\n", stdout);
-}
-
-void
-tty_showpiece(int c)
-{
- const char *s = "";
-
- /* simple or use unicode character */
-#if 0
- putchar(c);
- return;
-#endif
-
- switch (c) {
- case 'K': s = "♔"; break;
- case 'Q': s = "♕"; break;
- case 'R': s = "♖"; break;
- case 'B': s = "♗"; break;
- case 'N': s = "♘"; break;
- case 'P': s = "♙"; break;
- case 'k': s = "♚"; break;
- case 'q': s = "♛"; break;
- case 'r': s = "♜"; break;
- case 'b': s = "♝"; break;
- case 'n': s = "♞"; break;
- case 'p': s = "♟"; break;
- }
-
- if (*s)
- fputs(s, stdout);
-}
-
-/* show board */
-void
-tty_showboard(void)
-{
- const int *color;
- int ix, iy, x, y, piece;
-
- printf("Board FEN:\n");
- showboardfen();
- printf("\n\n");
-
- SETFGCOLOR(0x00, 0x00, 0x00);
-
- color = border;
- SETBGCOLOR(color[0], color[1], color[2]);
- SETFGCOLOR(0xff, 0xff, 0xff);
- fputs(" ", stdout);
- printf("\x1b[0m"); /* reset */
- SETFGCOLOR(0x00, 0x00, 0x00);
- putchar('\n');
-
- for (iy = 0; iy < 8; iy++) {
- y = flipboard ? 7 - iy : iy;
-
- color = border;
- SETBGCOLOR(color[0], color[1], color[2]);
- SETFGCOLOR(0xff, 0xff, 0xff);
- fputs(" ", stdout);
-
- for (ix = 0; ix < 8; ix++) {
- x = flipboard ? 7 - ix : ix;
-
- if (x % 2 == 0) {
- if (y % 2 == 0)
- color = highlight[y][x] ? lightsquareh…
- else
- color = highlight[y][x] ? darksquarehi…
- } else {
- if (y % 2 == 0)
- color = highlight[y][x] ? darksquarehi…
- else
- color = highlight[y][x] ? lightsquareh…
- }
- SETBGCOLOR(color[0], color[1], color[2]);
-
- fputs(" ", stdout);
- piece = getpiece(x, y);
- if (piece) {
- if (piece >= 'A' && piece <= 'Z')
- SETFGCOLOR(0xff, 0xff, 0xff);
- else
- SETFGCOLOR(0x00, 0x00, 0x00);
- /* workaround: use black chess symbol, because…
- is filled and better visible */
- tty_showpiece(tolower(piece));
- } else {
- fputs(" ", stdout);
- }
- fputs(" ", stdout);
- }
- printf("\x1b[0m"); /* reset */
-
- color = border;
- SETBGCOLOR(color[0], color[1], color[2]);
- SETFGCOLOR(0xff, 0xff, 0xff);
- if (showcoords) {
- putchar(' ');
- putchar('8' - y);
- putchar(' ');
- } else {
- fputs(" ", stdout);
- }
-
- printf("\x1b[0m"); /* reset */
- SETFGCOLOR(0x00, 0x00, 0x00);
- putchar('\n');
- }
- color = border;
- SETBGCOLOR(color[0], color[1], color[2]);
- SETFGCOLOR(0xff, 0xff, 0xff);
- if (showcoords) {
- if (flipboard)
- fputs(" h g f e d c b a ", stdout);
- else
- fputs(" a b c d e f g h ", stdout);
- } else {
- fputs(" ", stdout);
- }
- printf("\x1b[0m"); /* reset */
- printf("\n");
-}
-
-void
-ascii_showpiece(int c)
-{
- putchar(c);
-}
-
-/* OnlyFENs */
-void
-fen_showboard(void)
-{
- showboardfen();
- printf("\n");
-}
-
-/* show board */
-void
-ascii_showboard(void)
-{
- int hi[3] = { '>', ' ', '<' };
- int dark[3] = { '.', '.', '.' };
- int light[3] = { ' ', ' ', ' ' };
- int *color, ix, iy, x, y, piece;
-
- printf("Board FEN:\n");
- showboardfen();
- printf("\n\n");
-
- for (iy = 0; iy < 8; iy++) {
- y = flipboard ? 7 - iy : iy;
-
- fputs("+---+---+---+---+---+---+---+---+\n", stdout);
- for (ix = 0; ix < 8; ix++) {
- x = flipboard ? 7 - ix : ix;
-
- if (x % 2 == 0) {
- if (y % 2 == 0)
- color = highlight[y][x] ? hi : light;
- else
- color = highlight[y][x] ? hi : dark;
- } else {
- if (y % 2 == 0)
- color = highlight[y][x] ? hi : dark;
- else
- color = highlight[y][x] ? hi : light;
- }
-
- if (ix == 0)
- putchar('|');
- putchar(color[0]);
- piece = getpiece(x, y);
- if (piece)
- ascii_showpiece(piece);
- else
- putchar(color[1]);
- putchar(color[2]);
- putchar('|');
- }
- if (showcoords) {
- putchar(' ');
- putchar('8' - y);
- }
- putchar('\n');
- }
- fputs("+---+---+---+---+---+---+---+---+\n", stdout);
- if (showcoords) {
- if (flipboard)
- printf(" h | g | f | e | d | c | b | a |\n");
- else
- printf(" a | b | c | d | e | f | g | h |\n");
- }
-
- fputs("\n", stdout);
-}
-
-int
-main(int argc, char *argv[])
-{
- const char *progname, *fen, *moves, *s;
- int x, y, x2, y2, field, piece, takepiece;
- char square[3];
- long l;
-
- if (argc > 3) {
- fprintf(stderr, "usage: %s <FEN> [moves] or\n", argv[0]);
- fprintf(stderr, " %s <FEN> or\n", argv[0]);
- fprintf(stderr, " %s\n", argv[0]);
- return 1;
- }
- if (argc > 1)
- fen = argv[1];
- else
- fen = "startpos";
-
- if (argc > 2)
- moves = argv[2];
- else
- moves = "";
-
- if (!strcmp(fen, "startpos"))
- fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 …
-
- /* initial board state, FEN format */
- x = y = field = 0;
- for (s = fen; *s && field < 6; s++) {
- switch (field) {
- case 0: /* piece placement data */
- /* skip square */
- if (*s >= '1' && *s <= '9') {
- x += (*s - '0');
- continue;
- }
- /* next rank */
- if (*s == '/') {
- x = 0;
- y++;
- continue;
- }
- /* is piece? place it */
- if (isvalidpiece(*s))
- place(*s, x++, y);
- break;
- case 1: /* active color */
- if (*s == 'w' || *s == 'b')
- side_to_move = *s;
- break;
- case 2: /* castling availability */
- if (*s == '-') {
- white_can_castle[0] = 0;
- white_can_castle[1] = 0;
- black_can_castle[0] = 0;
- black_can_castle[1] = 0;
- } else if (*s == 'K') {
- white_can_castle[0] = 1;
- } else if (*s == 'Q') {
- white_can_castle[1] = 1;
- } else if (*s == 'k') {
- black_can_castle[0] = 1;
- } else if (*s == 'q') {
- black_can_castle[1] = 1;
- }
- break;
- case 3: /* en passant square */
- if (*s >= 'a' && *s <= 'h' &&
- *(s + 1) >= '1' && *(s + 1) >= '6') {
-
- square[0] = *s;
- square[1] = *(s + 1);
- square[2] = '\0';
- squaretoxy(square, &x, &y);
-
- enpassantsquare[0] = x;
- enpassantsquare[1] = y;
- }
- break;
- case 4: /* halfmove */
- if (!(*s >= '0' && *s <= '9'))
- continue;
-
- l = strtol(s, NULL, 10);
- if (l >= 0 && l < 32767) {
- halfmove = l;
-
- for (; *s && isdigit((unsigned char)*s); s++)
- ;
- }
- break;
- case 5: /* move number */
- if (!(*s >= '0' && *s <= '9'))
- continue;
-
- l = strtol(s, NULL, 10);
- if (l >= 0 && l < 32767) {
- movenumber = (int)l;
- for (; *s && isdigit((unsigned char)*s); s++)
- ;
- }
- break;
- }
- if (!*s)
- break;
-
- /* next field, fields are: piece placement data, active color,
- Castling availability, En passant target square,
- Halfmove clock, Fullmove number */
- if (*s == ' ') {
- field++;
- continue;
- }
- }
-
- /* process moves */
- square[2] = '\0';
- x = y = x2 = y2 = -1;
- for (s = moves; *s; s++) {
- if (*s == ' ')
- continue;
- if ((*s >= 'a' && *s <= 'h') &&
- (*(s + 1) >= '1' && *(s + 1) <= '8') &&
- (*(s + 2) >= 'a' && *(s + 2) <= 'h') &&
- (*(s + 3) >= '1' && *(s + 3) <= '8')) {
- square[0] = *s;
- square[1] = *(s + 1);
-
- s += 2;
- squaretoxy(square, &x, &y);
- piece = getpiece(x, y);
-
- place(0, x, y); /* clear square */
-
- /* place piece at new location */
- square[0] = *s;
- square[1] = *(s + 1);
- squaretoxy(square, &x2, &y2);
- takepiece = getpiece(x2, y2);
- place(piece, x2, y2);
- s += 2;
-
- /* if pawn move or taken a piece increase halfmove cou…
- /* TODO: taking enpassant should reset halfmove too */
- if (piece == 'p' || piece == 'P' || takepiece != 0)
- halfmove = 0;
- else
- halfmove++;
-
- /* castling */
- if (piece == 'K' && y == 7 && y2 == 7 && x == 4) {
- /* white: kingside castling: "e1g1" */
- if (x2 == 6) {
- place('R', x2 - 1, y2);
- x2 = 7;
- y2 = 7;
- place(0, x2, y2); /* clear rook square…
- } else if (x2 == 2) {
- /* white: queenside castling: "e1c1" */
- place('R', x2 + 1, y2);
- x2 = 0;
- y2 = 7;
- place(0, x2, y2);
- }
- } else if (piece == 'k' && y == 0 && y2 == 0 && x == 4…
- /* black: kingside castling: "e8g8" */
- if (x2 == 6) {
- place('r', x2 - 1, y2);
- x2 = 7;
- y2 = 0;
- place(0, x2, y2); /* clear rook square…
- } else if (x2 == 2) {
- /* black: queenside castling: "e8c8" */
- place('r', x2 + 1, y2);
- x2 = 0;
- y2 = 0;
- place(0, x2, y2); /* clear rook square…
- }
- }
-
- /* remove the ability to castle */
- if (piece == 'K') {
- white_can_castle[0] = white_can_castle[1] = 0;
- } else if (piece == 'k') {
- black_can_castle[0] = black_can_castle[1] = 0;
- } else if (piece == 'R') {
- if (x == 7 && y == 7)
- white_can_castle[0] = 0;
- else if (x == 0 && y == 7)
- white_can_castle[1] = 0;
- } else if (piece == 'r') {
- if (x == 0 && y == 0)
- black_can_castle[1] = 0;
- else if (x == 7 && y == 0)
- black_can_castle[0] = 0;
- }
-
- /* the en passant square resets after a move */
- enpassantsquare[0] = -1;
- enpassantsquare[1] = -1;
- /* moved 2 squares and there is an opponent pawn next …
- if (piece == 'P' && y == 6 && y2 == 4) {
- if (getpiece(x - 1, y2) == 'p' ||
- getpiece(x + 1, y2) == 'p') {
- enpassantsquare[0] = x;
- enpassantsquare[1] = 5;
- }
- } else if (piece == 'p' && y == 1 && y2 == 3) {
- if (getpiece(x - 1, y2) == 'P' ||
- getpiece(x + 1, y2) == 'P') {
- enpassantsquare[0] = x;
- enpassantsquare[1] = 2;
- }
- }
-
- /* possible promotion: queen, knight, bishop */
- if (*s == 'q' || *s == 'n' || *s == 'b') {
- if (side_to_move == 'w')
- piece = toupper((unsigned char)*s);
- else
- piece = *s;
- place(piece, x2, y2);
- s++;
- }
-
- /* a move by black increases the move number */
- if (side_to_move == 'b')
- movenumber++;
-
- /* switch which side it is to move */
- side_to_move = side_to_move == 'b' ? 'w' : 'b';
-
- /* TODO: reset enpassant square if applicable */
- }
- }
- /* highlight last move */
- highlightmove(x, y, x2, y2);
-
- progname = argv[0] ? argv[0] : "fen_to_svg";
- if ((s = strrchr(progname, '/')))
- progname = s + 1;
- if (!strcmp(progname, "fen_to_ascii"))
- ascii_showboard();
- else if (!strcmp(progname, "fen_to_tty"))
- tty_showboard();
- else if (!strcmp(progname, "fen_to_fen"))
- fen_showboard();
- else
- svg_showboard();
-
- return 0;
-}
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.