valentine.sh - 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"… | |
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… | |
148 LC_ALL=C awk -F ',' 'int($4) >= 1500 && int($4) < 2000 { print $0 }' "$g… | |
149 LC_ALL=C awk -F ',' 'int($4) >= 2000 { print $0 }' "$groupsdir/attractio… | |
150 ( | |
151 shuffle "$groupsdir/attraction_lt_1500.csv" 100 | sed 20q | LC_ALL=C awk… | |
152 shuffle "$groupsdir/attraction_lt_2000.csv" 100 | sed 15q | LC_ALL=C awk… | |
153 shuffle "$groupsdir/attraction_ge_2000.csv" 100 | sed 5q | LC_ALL=C awk … | |
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 … | |
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 "$f… | |
194 "$fenbin" $fenopts -m "$pgnmapping" -t "$theme" $flip -o ascii "… | |
195 "$fenbin" $fenopts -m "$pgnmapping" -t "$theme" $flip -o tty "$f… | |
196 "$fenbin" $fenopts -m "$pgnmapping" -t "$theme" $flip -o fen "$f… | |
197 pgn=$("$fenbin" $fenopts -l -m "$pgnmapping" -o pgn "$fen" "$fir… | |
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_… | |
201 test "$lichess" != "" && printf '<a href="%s">' "$lichess" >> "$… | |
202 | |
203 title="" | |
204 test "$rating" != "" && title="${text_puzzlerating}: $rating" | |
205 | |
206 printf '<img src="%s" alt="%s #%s" title="%s" width="360" height… | |
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_… | |
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" al… | |
246 "${i}.svg" "$ptitlespeak" "$pgn, $ptitlespeak" >> "$solu… | |
247 | |
248 # solution PGN | |
249 pgn_solution="$("$fenbin" $fenopts -m "$pgnmapping" -o pgn "$fen… | |
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… | |
276 | |
277 # PGN of moves so far. | |
278 pgn="$("$fenbin" $fenopts $fenopts -l -m "$pgnmapping" -… | |
279 ptitlespeak="$("$fenbin" $fenopts -l -o speak "$fen" "$m… | |
280 | |
281 printf '<img src="%s" width="180" height="180" loading="… | |
282 "solutions/${i}_${movecount}.svg" "$ptitlespeak"… | |
283 | |
284 movecount=$((movecount + 1)) | |
285 done | |
286 | |
287 printf '<p><b>PGN:</b> %s</p>\n' "${pgn_solution}" >> "$solution… | |
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_sol… | |
300 printf '<p><a href="solutions.txt">%s</a></p>\n' "${text_solutionstxtlab… | |
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" |