chess-puzzles

chess puzzle book generator
git clone git://git.codemadness.org/chess-puzzles
Log | Files | Refs | README | LICENSE

valentine.sh (8866B)


      1 #!/bin/sh
      2 
      3 fenbin="./fen"
      4 db="lichess_db_puzzle.csv"
      5 # default, green, grey
      6 theme="love"
      7 lang="nl" # en, nl
      8 fenopts="-d" # dutch mode (for speak output)
      9 
     10 # texts / localization.
     11 # English
     12 if [ "$lang" = "en" ]; then
     13 text_solutions="Solutions"
     14 text_solutionstxtlabel="Text listing of solutions"
     15 text_puzzles="Attraction puzzles for valentine 💕"
     16 text_puzzle="Puzzle"
     17 text_puzzlerating="Puzzel rating"
     18 text_point="point"
     19 text_points="points"
     20 text_whitetomove="white to move"
     21 text_blacktomove="black to move"
     22 text_title="${text_puzzles}"
     23 text_header="${text_puzzles}!"
     24 pgnmapping="KQRBN"
     25 fi
     26 
     27 # Dutch
     28 if [ "$lang" = "nl" ]; then
     29 text_solutions="Oplossingen"
     30 text_solutionstxtlabel="Tekstbestand, lijst met oplossingen"
     31 text_puzzles="Aantrekkingspuzzles voor valentijn 💕"
     32 text_puzzle="Puzzel"
     33 text_puzzlerating="Puzzel moeilijkheidsgraad"
     34 text_point="punt"
     35 text_points="punten"
     36 text_whitetomove="wit aan zet"
     37 text_blacktomove="zwart aan zet"
     38 text_title="${text_puzzles}"
     39 text_header="${text_puzzles}!"
     40 # Dutch: (K)oning, (D)ame, (T)oren, (L)oper, (P)aard.
     41 pgnmapping="KDTLP"
     42 fi
     43 
     44 if ! test -f "$db"; then
     45 	printf 'File "%s" not found, run `make db` to update it\n' "$db" >&2
     46 	exit 1
     47 fi
     48 
     49 index="puzzles/index.html"
     50 indexvt="puzzles/index.vt"
     51 
     52 # clean previous files.
     53 rm -rf puzzles
     54 mkdir -p puzzles/solutions
     55 
     56 solutions="$(mktemp)"
     57 seedfile="$(mktemp)"
     58 seed=20240201 # must be a integer value
     59 # seed for random sorting, makes it deterministic for the same system
     60 # seed must be sufficiently long.
     61 echo "${seed}_chess_puzzles" > "$seedfile"
     62 
     63 # shuffle(file, amount)
     64 shuffle() {
     65 	f="$1"
     66 	total="$2"
     67 	nlines="$(wc -l < "$f")"
     68 	nlines="$((nlines + 0))"
     69 	results="$(mktemp)"
     70 
     71 # generate list of lines to use. Not perfectly random but good enough.
     72 LC_ALL=C awk -v "seed=$seed" -v "nlines=$nlines" -v "total=$total" '
     73 BEGIN {
     74 	srand(seed);
     75 	for (i = 0; i < total; i++)
     76 		sel[int(rand() * nlines)] = 1;
     77 }
     78 sel[NR] {
     79 	print $0;
     80 }' "$f" > "$results"
     81 
     82 	# now we have less results we can use the slow sort -R.
     83 	sort -R --random-source "$seedfile" "$results"
     84 	rm -f "$results"
     85 }
     86 
     87 # solutions.txt header.
     88 solutionstxt="puzzles/solutions.txt"
     89 printf '%s\n\n' "${text_solutions}" >> "$solutionstxt"
     90 
     91 cat > "$indexvt" <<!
     92 ${text_header}
     93 
     94 !
     95 
     96 cat > "$index" <<!
     97 <!DOCTYPE html>
     98 <html dir="ltr" lang="${lang}">
     99 <head>
    100 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    101 <title>${text_title}</title>
    102 <style type="text/css">
    103 body {
    104 	font-family: sans-serif;
    105 	width: 775px;
    106 	margin: 0 auto;
    107 	padding: 0 10px;
    108 	background-color: pink;
    109 	color: #ff0000;
    110 }
    111 a {
    112 	color: #ff0000;
    113 }
    114 h2 a {
    115 	color: #ff0000;
    116 	text-decoration: none;
    117 }
    118 h2 a:hover:after {
    119 	content: " #";
    120 }
    121 .puzzle {
    122 	float: left;
    123 	margin-right: 25px;
    124 }
    125 footer {
    126 	clear: both;
    127 }
    128 details summary {
    129 	cursor: pointer; /* show hand */
    130 }
    131 </style>
    132 </head>
    133 <body>
    134 <header>
    135 <h1>${text_header}</h1>
    136 </header>
    137 <main>
    138 !
    139 
    140 # shuffle, some sort of order and point system based on rating of puzzle.
    141 count=1
    142 
    143 groupsdir="$(mktemp -d)"
    144 test "$groupsdir" = "" && exit 1
    145 
    146 grep 'attraction' "$db" > "$groupsdir/attraction.csv"
    147 LC_ALL=C awk -F ',' 'int($4) < 1500 { print $0 }' "$groupsdir/attraction.csv" > "$groupsdir/attraction_lt_1500.csv"
    148 LC_ALL=C awk -F ',' 'int($4) >= 1500 && int($4) < 2000 { print $0 }' "$groupsdir/attraction.csv" > "$groupsdir/attraction_lt_2000.csv"
    149 LC_ALL=C awk -F ',' 'int($4) >= 2000 { print $0 }' "$groupsdir/attraction.csv" > "$groupsdir/attraction_ge_2000.csv"
    150 (
    151 shuffle "$groupsdir/attraction_lt_1500.csv" 100 | sed 20q | LC_ALL=C awk '{ print $0 ",1" }'
    152 shuffle "$groupsdir/attraction_lt_2000.csv" 100 | sed 15q | LC_ALL=C awk '{ print $0 ",2" }'
    153 shuffle "$groupsdir/attraction_ge_2000.csv" 100 | sed 5q | LC_ALL=C awk '{ print $0 ",3" }'
    154 rm -rf "$groupsdir"
    155 ) | \
    156 while read -r line; do
    157 	i="$count"
    158 	fen=$(printf '%s' "$line" | cut -f 2 -d ',')
    159 
    160 	tomove=$(printf '%s' "$line" | cut -f 2 -d ',' | cut -f 2 -d ' ')
    161 	allmoves="$(printf '%s' "$line" | cut -f 3 -d ',')"
    162 	firstmove=$(printf '%s' "$line" | cut -f 3 -d ',' | cut -f 1 -d ' ' ) # first move only.
    163 	rating=$(printf '%s' "$line" | cut -f 4 -d ',')
    164 	ratingdev=$(printf '%s' "$line" | cut -f 5 -d ',')
    165 	lichess=$(printf '%s' "$line" | cut -f 9 -d ',')
    166 
    167 	case "$tomove" in
    168 	"w") tomove="w";;
    169 	"b") tomove="b";;
    170 	*) tomove="w";; # default
    171 	esac
    172 
    173 	# first move is played so flip when white (not black).
    174 	flip=""
    175 	test "$tomove" = "w" && flip="-f"
    176 
    177 	# added field: points
    178 	points=$(printf '%s' "$line" | cut -f "11" -d ',')
    179 	if [ "$points" = "1" ]; then
    180 		points="$points ${text_point}"
    181 	else
    182 		points="$points ${text_points}"
    183 	fi
    184 
    185 	img="$i.svg"
    186 	txt="$i.txt"
    187 	vt="$i.vt"
    188 	destfen="puzzles/$i.fen"
    189 	destsvg="puzzles/$img"
    190 	desttxt="puzzles/$txt"
    191 	destvt="puzzles/$vt"
    192 
    193 	"$fenbin" $fenopts -m "$pgnmapping" -t "$theme" $flip -o svg "$fen" "$firstmove" > "$destsvg"
    194 	"$fenbin" $fenopts -m "$pgnmapping" -t "$theme" $flip -o ascii "$fen" "$firstmove" > "$desttxt"
    195 	"$fenbin" $fenopts -m "$pgnmapping" -t "$theme" $flip -o tty "$fen" "$firstmove" > "$destvt"
    196 	"$fenbin" $fenopts -m "$pgnmapping" -t "$theme" $flip -o fen "$fen" "$firstmove" > "$destfen"
    197 	pgn=$("$fenbin" $fenopts -l -m "$pgnmapping" -o pgn "$fen" "$firstmove")
    198 
    199 	printf '<div class="puzzle" id="puzzle-%s">\n' "$i" >> "$index"
    200 	printf '<h2><a href="#puzzle-%s">%s %s</a></h2>\n' "$i" "${text_puzzle}" "$i" >> "$index"
    201 	test "$lichess" != "" && printf '<a href="%s">' "$lichess" >> "$index"
    202 
    203 	title=""
    204 	test "$rating" != "" && title="${text_puzzlerating}: $rating"
    205 
    206 	printf '<img src="%s" alt="%s #%s" title="%s" width="360" height="360" loading="lazy" />' \
    207 		"$img" "${text_puzzle}" "$i" "$title" >> "$index"
    208 	test "$lichess" != "" && printf '</a>' >> "$index"
    209 	echo "" >> "$index"
    210 
    211 	movetext=""
    212 	# if there is a first move, inverse to move.
    213 	if test "$firstmove" != ""; then
    214 		case "$tomove" in
    215 		"w") movetext=", ${text_blacktomove}";;
    216 		"b") movetext=", ${text_whitetomove}";;
    217 		esac
    218 	else
    219 		case "$tomove" in
    220 		"w") movetext=", ${text_whitetomove}";;
    221 		"b") movetext=", ${text_blacktomove}";;
    222 		esac
    223 	fi
    224 
    225 	printf '<p><b>%s</b>%s</p>\n' "$points" "$movetext" >> "$index"
    226 	printf '%s%s\n' "$points" "$movetext" >> "$desttxt"
    227 	printf '\n%s%s\n' "$points" "$movetext" >> "$destvt"
    228 
    229 	# vt
    230 	printf 'Puzzle %s\n\n' "$i" >> "$indexvt"
    231 	cat "$destvt" >> "$indexvt"
    232 	printf '\n\n' >> "$indexvt"
    233 
    234 	# solutions per puzzle.
    235 	printf '<div class="puzzle-solution">\n' >> "$solutions"
    236 	printf '<h2><a href="#puzzle-%s">%s %s</a></h2>\n' "$i" "${text_puzzle}" "$i" >> "$solutions"
    237 
    238 	m="${allmoves}"
    239 	movecount=0
    240 	# create a move list, removing one move each step, for generating
    241 	# the solution images.
    242 
    243 	# add initial puzzle aswell for context.
    244 	ptitlespeak="$("$fenbin" $fenopts -l -o speak "$fen" "$firstmove")"
    245 	printf '<img src="%s" width="180" height="180" loading="lazy" alt="%s" title="%s" />\n' \
    246 		"${i}.svg" "$ptitlespeak" "$pgn, $ptitlespeak" >> "$solutions"
    247 
    248 	# solution PGN
    249 	pgn_solution="$("$fenbin" $fenopts -m "$pgnmapping" -o pgn "$fen" "$allmoves")"
    250 
    251 	destsolpgn="puzzles/solutions/${i}.pgn"
    252 	printf '%s\n' "$pgn_solution" > "$destsolpgn"
    253 
    254 #	printf 'DEBUG: #%s: "%s" "%s"\n' "$i" "$fen" "$allmoves" >&2
    255 
    256 	while [ "$m" != "" ]; do
    257 		prevmoves="$m"
    258 
    259 		echo "$m"
    260 
    261 		m="${m% }"
    262 		m="${m%[^ ]*}"
    263 		m="${m% }"
    264 
    265 		test "$prevmoves" = "$m" && break # same, break also
    266 	done | sort | while read -r movelist; do
    267 		# first move is already shown, skip it.
    268 		if test "$movecount" = "0"; then
    269 			movecount=$((movecount + 1))
    270 			continue
    271 		fi
    272 
    273 		# process move list in sequence.
    274 		destsolsvg="puzzles/solutions/${i}_${movecount}.svg"
    275 		"$fenbin" $fenopts -m "$pgnmapping" -t "$theme" $flip -o svg "$fen" "$movelist" > "$destsolsvg"
    276 
    277 		# PGN of moves so far.
    278 		pgn="$("$fenbin" $fenopts $fenopts -l -m "$pgnmapping" -o pgn "$fen" "$movelist")"
    279 		ptitlespeak="$("$fenbin" $fenopts -l -o speak "$fen" "$movelist")"
    280 
    281 		printf '<img src="%s" width="180" height="180" loading="lazy" alt="%s" title="%s" />\n' \
    282 			"solutions/${i}_${movecount}.svg" "$ptitlespeak" "$pgn, $ptitlespeak" >> "$solutions"
    283 
    284 		movecount=$((movecount + 1))
    285 	done
    286 
    287 	printf '<p><b>PGN:</b> %s</p>\n' "${pgn_solution}" >> "$solutions"
    288 	printf '</div>\n' >> "$solutions"
    289 
    290 	printf '</div>\n' >> "$index"
    291 
    292 	# add PGN solution to solutions text file.
    293 	printf '%s. %s\n' "$i" "${pgn_solution}" >> "$solutionstxt"
    294 
    295 	count=$((count + 1))
    296 done
    297 
    298 # solutions / spoilers
    299 printf '<footer><br/><br/><details>\n<summary>%s</summary>\n' "$text_solutions" >> "$index"
    300 printf '<p><a href="solutions.txt">%s</a></p>\n' "${text_solutionstxtlabel}" >> "$index"
    301 
    302 # add solutions HTML to index page.
    303 cat "$solutions" >> "$index"
    304 echo "</details>\n<br/><br/></footer>\n" >> "$index"
    305 
    306 # add solutions to vt index page.
    307 printf '\n\n\n\n\n\n\n\n\n\n' >> "$indexvt"
    308 printf '\n\n\n\n\n\n\n\n\n\n' >> "$indexvt"
    309 printf '\n\n\n\n\n' >> "$indexvt"
    310 cat "$solutionstxt" >> "$indexvt"
    311 
    312 cat >> "$index" <<!
    313 </main>
    314 </body>
    315 </html>
    316 !
    317 
    318 rm -f "$solutions" "$seedfile"