Introduction
Introduction Statistics Contact Development Disclaimer Help
fen_to_tty: add initial tty version - chess-puzzles - chess puzzle book generat…
git clone git://git.codemadness.org/chess-puzzles
Log
Files
Refs
README
LICENSE
---
commit 26d727fe71a7e77b2eb1f733e2ad738b52089c54
parent 59753af31b403f30db84f07562d2614460a2d9ab
Author: Hiltjo Posthuma <[email protected]>
Date: Mon, 18 Dec 2023 15:51:15 +0100
fen_to_tty: add initial tty version
Diffstat:
M Makefile | 3 ++-
M README | 4 ++++
A fen_to_tty.c | 329 +++++++++++++++++++++++++++++…
M generate.sh | 4 ++++
4 files changed, 339 insertions(+), 1 deletion(-)
---
diff --git a/Makefile b/Makefile
@@ -1,6 +1,7 @@
build: clean
${CC} -o fen_to_svg fen_to_svg.c ${CFLAGS} ${LDFLAGS}
${CC} -o fen_to_ascii fen_to_ascii.c ${CFLAGS} ${LDFLAGS}
+ ${CC} -o fen_to_tty fen_to_tty.c ${CFLAGS} ${LDFLAGS}
db:
rm -f lichess_db_puzzle.csv.zst lichess_db_puzzle.csv
@@ -8,4 +9,4 @@ db:
zstd -d < lichess_db_puzzle.csv.zst > lichess_db_puzzle.csv
clean:
- rm -f fen_to_svg fen_to_ascii
+ rm -f fen_to_svg fen_to_ascii fen_to_tty
diff --git a/README b/README
@@ -27,6 +27,10 @@ Files
Read FEN and a few moves and generate an SVG image of the board.
* fen_to_ascii.c:
Read FEN and a few moves and generate a text representation of the board.
+* fen_to_tty.c:
+ Read FEN and a few moves and generate a text representation of the board
+ suitable for a terminal. The terminal requires UTF-8 support for chess
+ symbols and it uses truecolor for the board theme.
References
diff --git a/fen_to_tty.c b/fen_to_tty.c
@@ -0,0 +1,329 @@
+/* TODO: option to flip board? */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+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 const int showcoords = 1; /* config: show board coordinates? */
+
+#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)
+
+int
+isvalidsquare(int x, int y)
+{
+ return !(x < 0 || x >= 8 || y < 0 || y >= 8);
+}
+
+/* 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
+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);
+}
+
+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');
+ }
+
+ /* ? TODO: detect en passant, invalid castling etc? */
+}
+
+/* show board */
+void
+showboard(void)
+{
+ int *color;
+ int border[] = { 0x70, 0x49, 0x2d };
+ int darksquare[] = { 0xb5, 0x88, 0x63 };
+ int lightsquare[] = { 0xf0, 0xd9, 0xb5 };
+ int darksquarehi[] = { 0xaa, 0xa2, 0x3a };
+ int lightsquarehi[] = { 0xcd, 0xd2, 0x6a };
+ int 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 (y = 0; y < 8; y++) {
+ color = border;
+ SETBGCOLOR(color[0], color[1], color[2]);
+ SETFGCOLOR(0xff, 0xff, 0xff);
+ fputs(" ", stdout);
+
+ for (x = 0; x < 8; x++) {
+ 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(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(' ');
+ }
+
+ 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)
+ fputs(" a b c d e f g h ", stdout);
+ printf("\x1b[0m"); /* reset */
+ printf("\n");
+ printf("\x1b[0m"); /* reset */
+
+#if 0
+ if (side_to_move == 'w') {
+ fputs("White to move\n", stdout);
+ } else if (side_to_move == 'b')
+ fputs("Black to move\n", stdout);
+
+ if (white_can_castle[0])
+ fputs("White can castle king side\n", stdout);
+ if (white_can_castle[1])
+ fputs("White can castle queen side\n", stdout);
+ if (black_can_castle[0])
+ fputs("Black can castle king side\n", stdout);
+ if (black_can_castle[1])
+ fputs("Black can castle queen side\n", stdout);
+#endif
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *fen, *moves, *s;
+ int x, y, x2, y2, field, piece;
+ char pieces[] = "PNBRQKpnbrqk", square[3];
+
+ if (argc != 3) {
+ fprintf(stderr, "usage: %s <FEN> <moves>\n", argv[0]);
+ return 1;
+ }
+
+ fen = argv[1];
+ moves = argv[2];
+
+ /* initial board state, FEN format */
+ x = y = field = 0;
+ for (s = fen; *s; s++) {
+ /* next field, fields are: piece placement data, active color,
+ Castling availability, En passant target square,
+ Halfmove clock, Fullmove number */
+ if (*s == ' ') {
+ field++;
+ continue;
+ }
+
+ 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 (strchr(pieces, *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: /* TODO: en-passant square, rest of the fields */
+ break;
+ }
+ /* TODO: parse which side to move, en-passant, etc */
+ }
+
+ /* 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);
+ place(piece, x2, y2);
+ s += 2;
+ }
+ }
+ /* highlight last move */
+ highlightmove(x, y, x2, y2);
+
+ showboard();
+
+ printf("\x1b[0m"); /* reset */
+
+ return 0;
+}
diff --git a/generate.sh b/generate.sh
@@ -80,11 +80,14 @@ while read -r line; do
img="$i.svg"
txt="$i.txt"
+ vt="$i.vt"
destsvg="puzzles/$img"
desttxt="puzzles/$txt"
+ destvt="puzzles/$vt"
./fen_to_svg "$fen" "$moves" > "$destsvg"
./fen_to_ascii "$fen" "$moves" > "$desttxt"
+ ./fen_to_tty "$fen" "$moves" > "$destvt"
printf '<div class="puzzle">' >> "$index"
printf '<h2>Puzzle %s</h2>\n' "$i" >> "$index"
@@ -120,6 +123,7 @@ while read -r line; do
printf '<p><b>%s</b>%s</p>\n' "$points" "$movetext" >> "$index"
printf '%s%s\n' "$points" "$movetext" >> "$desttxt"
+ printf '\n%s%s\n' "$points" "$movetext" >> "$destvt"
printf '</div>\n' >> "$index"
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.