valentine.sh - chess-puzzles - chess puzzle book generator
 (HTM) git clone git://git.codemadness.org/chess-puzzles
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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"