initial repo - chess-puzzles - chess puzzle book generator | |
git clone git://git.codemadness.org/chess-puzzles | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit c8aed5b7783cae9c06f50dfcb5ab0af851edf54a | |
Author: Hiltjo Posthuma <[email protected]> | |
Date: Sun, 17 Dec 2023 21:17:57 +0100 | |
initial repo | |
Diffstat: | |
A LICENSE | 15 +++++++++++++++ | |
A Makefile | 11 +++++++++++ | |
A README | 37 +++++++++++++++++++++++++++++… | |
A fen_to_ascii.c | 276 ++++++++++++++++++++++++++++++ | |
A fen_to_svg.c | 287 +++++++++++++++++++++++++++++… | |
A generate.sh | 138 ++++++++++++++++++++++++++++++ | |
6 files changed, 764 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/LICENSE b/LICENSE | |
@@ -0,0 +1,15 @@ | |
+ISC License | |
+ | |
+Copyright (c) 2023 Hiltjo Posthuma <[email protected]> | |
+ | |
+Permission to use, copy, modify, and/or distribute this software for any | |
+purpose with or without fee is hereby granted, provided that the above | |
+copyright notice and this permission notice appear in all copies. | |
+ | |
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
diff --git a/Makefile b/Makefile | |
@@ -0,0 +1,11 @@ | |
+build: clean | |
+ ${CC} -o fen_to_svg fen_to_svg.c ${CFLAGS} ${LDFLAGS} | |
+ ${CC} -o fen_to_ascii fen_to_ascii.c ${CFLAGS} ${LDFLAGS} | |
+ | |
+db: | |
+ rm -f lichess_db_puzzle.csv.zst lichess_db_puzzle.csv | |
+ curl -O 'https://database.lichess.org/lichess_db_puzzle.csv.zst' | |
+ zstd -d < lichess_db_puzzle.csv.zst > lichess_db_puzzle.csv | |
+ | |
+clean: | |
+ rm -f fen_to_svg fen_to_ascii | |
diff --git a/README b/README | |
@@ -0,0 +1,37 @@ | |
+chess puzzle book generator | |
+--------------------------- | |
+ | |
+This was a christmas hack for fun and non-profit. | |
+ | |
+ | |
+The generate.sh script generates puzzles. | |
+The puzzles used are from the lichess.org puzzle database. | |
+This database is a big CSV file. | |
+ | |
+The generate index page is a HTML page, it lists the puzzles. | |
+Each puzzle is an SVG image. | |
+ | |
+ | |
+Files | |
+----- | |
+ | |
+* generate.sh: | |
+ Read puzzles, shuffle them. Do some sorting based on difficulty and | |
+ assign score points. | |
+* fen_to_svg.c: | |
+ 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. | |
+ | |
+ | |
+References | |
+---------- | |
+ | |
+* SVG of the pieces: | |
+ https://github.com/lichess-org/lila/tree/master/public/piece/cburnett | |
+ | |
+* lichess FEN puzzle database | |
+ https://database.lichess.org/#puzzles | |
+ | |
+* lichess.org | |
+ https://lichess.org/ | |
diff --git a/fen_to_ascii.c b/fen_to_ascii.c | |
@@ -0,0 +1,276 @@ | |
+/* TODO: option to flip board? */ | |
+ | |
+#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? */ | |
+ | |
+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 1 | |
+ 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 */ | |
+/* TODO: show fancier, unicode and background square color */ | |
+/* TODO: use the output format similar to stockfish "d" command */ | |
+void | |
+showboard(void) | |
+{ | |
+ int x, y, piece; | |
+ | |
+ printf("Board FEN:\n"); | |
+ showboardfen(); | |
+ printf("\n\n"); | |
+ | |
+ for (y = 0; y < 8; y++) { | |
+ printf("+---+---+---+---+---+---+---+---+\n"); | |
+ for (x = 0; x < 8; x++) { | |
+ if (x == 0) | |
+ putchar('|'); | |
+ fputs(" ", stdout); | |
+ piece = getpiece(x, y); | |
+ if (piece) | |
+ showpiece(piece); | |
+ else | |
+ fputs(" ", stdout); | |
+ fputs(" ", stdout); | |
+ putchar('|'); | |
+ } | |
+ if (showcoords) { | |
+ putchar(' '); | |
+ putchar('8' - y); | |
+ } | |
+ putchar('\n'); | |
+ } | |
+ printf("+---+---+---+---+---+---+---+---+\n"); | |
+ if (showcoords) | |
+ printf(" a | b | c | d | e | f | g | h |\n"); | |
+ | |
+ fputs("\n", stdout); | |
+ | |
+#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(); | |
+ | |
+ return 0; | |
+} | |
diff --git a/fen_to_svg.c b/fen_to_svg.c | |
@@ -0,0 +1,287 @@ | |
+/* TODO: option to flip board? */ | |
+ | |
+#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? */ | |
+ | |
+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 = ""; | |
+ | |
+ /* 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 | |
+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? */ | |
+} | |
+ | |
+void | |
+showboard(void) | |
+{ | |
+ /* lichess default theme colors */ | |
+ const char *darksquare = "#b58863"; | |
+ const char *lightsquare = "#f0d9b5"; | |
+ const char *darksquarehi = "#aaa23a"; | |
+ const char *lightsquarehi = "#cdd26a"; | |
+ const char *color; | |
+ int 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 (y = 0; y < 8; y++) { | |
+ 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… | |
+ } | |
+ | |
+ printf("<g><rect x=\"%d\" y=\"%d\" width=\"45\" height… | |
+ x * 45, y * 45, color); | |
+ | |
+ piece = getpiece(x, y); | |
+ if (piece) { | |
+ printf("<g transform=\"translate(%d %d)\">", x… | |
+ showpiece(piece); | |
+ fputs("</g>\n", stdout); | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (showcoords) { | |
+ x = 7; | |
+ for (y = 0; y < 8; y++) { | |
+ if (y % 2 == 0) | |
+ color = highlight[y][x] ? lightsquarehi : ligh… | |
+ else | |
+ color = highlight[y][x] ? darksquarehi : darks… | |
+ printf("<text x=\"%d\" y=\"%d\" fill=\"%s\" dominant-b… | |
+ (x * 45) + 37, (y * 45) + 3, color, '8' - y); | |
+ } | |
+ y = 7; | |
+ for (x = 0; x < 8; x++) { | |
+ if (x % 2 == 0) | |
+ color = highlight[y][x] ? lightsquarehi : ligh… | |
+ else | |
+ color = highlight[y][x] ? darksquarehi : darks… | |
+ printf("<text x=\"%d\" y=\"%d\" fill=\"%s\" dominant-b… | |
+ (x * 45) + 2, (y + 1) * 45 - 3, color, x + 'a'… | |
+ } | |
+ } | |
+ | |
+ fputs("</svg>\n", stdout); | |
+} | |
+ | |
+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(); | |
+ | |
+ return 0; | |
+} | |
diff --git a/generate.sh b/generate.sh | |
@@ -0,0 +1,138 @@ | |
+#!/bin/sh | |
+ | |
+index="puzzles/index.html" | |
+mkdir -p puzzles | |
+ | |
+cat > "$index" <<! | |
+<!DOCTYPE html> | |
+<html> | |
+<head> | |
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
+<title>Puzzles</title> | |
+<style type="text/css"> | |
+body { | |
+ font-family: sans-serif; | |
+ width: 960px; | |
+ margin: 0 auto; | |
+ padding: 0 10px; | |
+} | |
+.puzzle { | |
+ float: left; | |
+ margin-right: 25px; | |
+} | |
+</style> | |
+</head> | |
+<body> | |
+<header> | |
+<h1>Puzzles, happy christmas mating!</h1> | |
+<!--<p>View the bottom of the SVG source for <a href="https://en.wikipedia.org… | |
+</header> | |
+<main> | |
+! | |
+ | |
+# shuffle, some sort of order and point system based on rating of puzzle. | |
+ | |
+db="lichess_db_puzzle.csv" | |
+count=1 | |
+ | |
+(grep 'mateIn1' < "$db" | shuf -n 100 | sed 10q | |
+grep 'mateIn2' < "$db" | shuf -n 100 | sed 10q | |
+grep 'mateIn3' < "$db" | shuf -n 100 | sed 10q | |
+grep 'mateIn4' < "$db" | shuf -n 100 | sed 10q | |
+LC_ALL=C awk -F ',' '(" " $8 " ") ~ / mateIn5 / && int($4) < 2000 { print $0 }… | |
+LC_ALL=C awk -F ',' '(" " $8 " ") ~ / mateIn5 / && int($4) >= 2000 { print $0 … | |
+LC_ALL=C awk -F ',' '(" " $8 " ") ~ / mateIn5 / && int($4) >= 2700 { print $0 … | |
+) | | |
+LC_ALL=C awk -F ',' ' | |
+{ | |
+ points="1 point"; # default | |
+} | |
+(" " $8 " ") ~ / mateIn2 / { | |
+ points="2 points"; | |
+} | |
+(" " $8 " ") ~ / mateIn3 / { | |
+ points="3 points"; | |
+} | |
+(" " $8 " ") ~ / mateIn4 / { | |
+ points="4 points"; | |
+} | |
+(" " $8 " ") ~ / mateIn5 / && int($4) < 2000 { | |
+ points="5 points"; | |
+} | |
+(" " $8 " ") ~ / mateIn5 / && int($4) >= 2000 { | |
+ points="7 points"; | |
+} | |
+(" " $8 " ") ~ / mateIn5 / && int($4) >= 2700 { | |
+ points="10 points"; | |
+} | |
+{ | |
+ print $0 "," points; | |
+}' | \ | |
+while read -r line; do | |
+ i="$count" | |
+ fen=$(printf '%s' "$line" | cut -f 2 -d ',') | |
+ tomove=$(printf '%s' "$line" | cut -f 2 -d ',' | cut -f 2 -d ' ') | |
+ moves=$(printf '%s' "$line" | cut -f 3 -d ',' | cut -b 1-4 ) # first m… | |
+ rating=$(printf '%s' "$line" | cut -f 4 -d ',') | |
+ ratingdev=$(printf '%s' "$line" | cut -f 5 -d ',') | |
+ lichess=$(printf '%s' "$line" | cut -f 9 -d ',') | |
+ | |
+ # added field | |
+ points=$(printf '%s' "$line" | cut -f "11" -d ',') | |
+ | |
+ img="$i.svg" | |
+ txt="$i.txt" | |
+ destsvg="puzzles/$img" | |
+ desttxt="puzzles/$txt" | |
+ | |
+ ./fen_to_svg "$fen" "$moves" > "$destsvg" | |
+ ./fen_to_ascii "$fen" "$moves" > "$desttxt" | |
+ | |
+ printf '<div class="puzzle">' >> "$index" | |
+ printf '<h2>Puzzle %s</h2>\n' "$i" >> "$index" | |
+ test "$lichess" != "" && printf '<a href="%s">' "$lichess" >> "$index" | |
+ | |
+ title="" | |
+ test "$rating" != "" && title="Puzzle rating: $rating" | |
+ | |
+ printf '<img src="%s" alt="Puzzle #%s" title="%s" width="360" height="… | |
+ "$img" "$i" "$title" >> "$index" | |
+ test "$lichess" != "" && printf '</a>' >> "$index" | |
+ echo "" >> "$index" | |
+ | |
+ case "$tomove" in | |
+ "w") tomove="w";; | |
+ "b") tomove="b";; | |
+ *) tomove="w";; # default | |
+ esac | |
+ | |
+ movetext="" | |
+ # if there is a first move, inverse to move. | |
+ if test "moves" != ""; then | |
+ case "$tomove" in | |
+ "w") movetext=", black to move";; | |
+ "b") movetext=", white to move";; | |
+ esac | |
+ else | |
+ case "$tomove" in | |
+ "w") movetext=", white to move";; | |
+ "b") movetext=", black to move";; | |
+ esac | |
+ fi | |
+ | |
+ printf '<p><b>%s</b>%s</p>\n' "$points" "$movetext" >> "$index" | |
+ printf '%s%s\n' "$points" "$movetext" >> "$desttxt" | |
+ | |
+ printf '</div>\n' >> "$index" | |
+ | |
+ # DEBUG | |
+ #echo "$count" >&2 | |
+ | |
+ count=$((count + 1)) | |
+done | |
+ | |
+cat >> "$index" <<! | |
+</main> | |
+</body> | |
+</html> | |
+! |