#!/usr/bin/env bash # Copyright 2014 Vladimir Ivanov # Distributed under the terms of the GNU General Public License v2 REPO_DIR= # Specify the tournament here TOURNAMENT= # Version information VERSION="0.3" # Firefox User Agent FIREFOX_UA="Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0" argv0=${0##*/} function usage { cat < $argv0 -h $argv0 -v Put the script under the root directory of your repository or set inner variable REPO_DIR to it. If the tournament is not the last one (default), store its sub-directory in inner variable TOURNAMENT. The first form fills the result of a chess game and stores its PGN file, assuming that the game is available at (lichess.org) and corresponds to tournament tour . The second form shows this help output. The third form shows version information. EOF exit "${1:-0}" } function version { exec echo "${argv0}-${VERSION}" } function game_setup { [[ -z $REPO_DIR ]] && REPO_DIR=`dirname "$0"` # Convert REPO_DIR to an absolute path [[ ! $REPO_DIR =~ ^/ ]] && REPO_DIR=$(cd ${REPO_DIR}; pwd) # If no tournament given, set it to the last one if [[ -z $TOURNAMENT ]]; then local year_dir=$(ls -1 -d ${REPO_DIR}/[0-9][0-9][0-9][0-9]/ | tail -1) TOURNAMENT=$(ls -1 -d ${year_dir}[0-9]-*/ | tail -1 \ | sed -E "s|${REPO_DIR}/(.*)|\1|") # Remove the trailing slash TOURNAMENT=${TOURNAMENT%/} fi # Configuration file for players ply_ini=${REPO_DIR}/${TOURNAMENT}/players.ini } function game_get_players { # Extract players on Lichess local wt_lichess=$(sed -En "s/\[White \"([^\"]*)\"\]/\1/p" $tmp_pgn) local bk_lichess=$(sed -En "s/\[Black \"([^\"]*)\"\]/\1/p" $tmp_pgn) # Get names of white and black players local counter=1 players=() game_parse_config while eval "config_section_player${counter}" 2>/dev/null; do players+=("$name") [[ $lichess == $wt_lichess ]] && white=$name [[ $lichess == $bk_lichess ]] && black=$name ((counter += 1)) done [[ -z $white ]] && fix_white_player [[ -z $black ]] && fix_black_player } function game_parse_config { # Copy player INI file to the temporary location # NOTE: an empty line is added to the file beginning in order to # match the only first occurrence for non-GNU sed echo > $tmp_ini cat "$ply_ini" >> $tmp_ini # Remove tabs or spaces around the `=' sed -E -i.prev "s/[[:blank:]]*=[[:blank:]]*/=/" "$tmp_ini" # Transform section labels into function declaration sed -E -i.prev "1,/^\[.*\]/s/^\[([^]]*)\]/config_section_\1() {/" "$tmp_ini" sed -E -i.prev "s/^\[([^]]*)\]/}\\"$'\n'"config_section_\1() {/" "$tmp_ini" echo -e "\n}" >> $tmp_ini # Source the file source "$tmp_ini" } function fix_white_player { local number echo "Lichess player '${wt_lichess}' not found." choose_player white=${players[$number]} } function fix_black_player { local number echo "Lichess player '${bk_lichess}' not found." choose_player black=${players[$number]} } function choose_player { echo "Please choose a proper name from the list below:" for ((i=0; i<${#players[@]}; ++i)); do echo "$((i+1)) ${players[$i]}" done \ | column -t \ | sed -E "s/^([0-9]*)/$(tput setaf 6)\1$(tput sgr0)/" # highlight number echo -n "Put number> " read number if (( $number < 1 || $number > ${#players[@]} )); then die "Incorrect player number." fi ((number += -1)) } function game_add_to_repo { local date_re="[0-9?]{2}\.[0-9?]{2}\.[0-9?]{4}" local correct=false length_max=0 # Change field separator to read a file line by line old_IFS=$IFS IFS=$'\n' # Check if the tour number is correct for line in $(cat "$tour_info"); do if [[ $line =~ ^(${date_re}\ +([^\ ]+)\ +-\ +([^\ ]+)) ]]; then local length=${#BASH_REMATCH[1]} local fst_ply=${BASH_REMATCH[2]} snd_ply=${BASH_REMATCH[3]} if [[ $fst_ply == $white && $snd_ply == $black ]]; then local length_rec=${#BASH_REMATCH[1]} correct=true fi if [[ $fst_ply == $black && $snd_ply == $white ]]; then local length_rec=${#BASH_REMATCH[1]} local answer echo -n "Approve game with wrong players' sides? (Y/n)> " read answer [[ ! $answer =~ ^(Y|y|Yes|yes)$ ]] && exit 1 white=$fst_ply black=$snd_ply correct=true fi # Determine the maximal length of game records (( $length > $length_max )) && length_max=$length fi done IFS=$old_IFS if ! $correct; then die "Game '${white} vs. ${black}' not found in ${tour_info}." fi local pgn_date=$(sed -En "s/\[Date \"([^\"]*)\"\]/\1/p" "$tmp_pgn") local pgn_date=$(tr "." "-" <<< "$pgn_date") pgn_dir=${REPO_DIR}/${TOURNAMENT}/tours/${TOUR}/${pgn_date}-${white}-vs-${black} [[ -d $pgn_dir ]] && die "Directory ${pgn_dir} already exist." echo "Creating directory ${pgn_dir}..." mkdir -p "$pgn_dir" echo "Storing PGN file..." cp "$tmp_pgn" "${pgn_dir}/1.pgn" echo "Updating tour_info..." local game_date=${pgn_date:8:2}.${pgn_date:5:2}.${pgn_date::4} local result=$(sed -En "s/\[Result \"([^\"]*)\"\]/\1/p" $tmp_pgn) local spaces=$((length_max - length_rec + 1)) local sep=$(printf "%${spaces}s" " ") # Change the representation of draw [[ $result == 1/2-1/2 ]] && result=1/2 sed -E -i.orig \ "s|${date_re}( +${white} +- +${black})|${game_date}\1${sep}${result}|" "$tour_info" rm "${tour_info}.orig" } function game_git_commit { cd $REPO_DIR git add "${pgn_dir}/1.pgn" "$tour_info" git commit -m "Tour ${TOUR#0}: ${white} vs. ${black}." git push } function die { echo "$@" 1>&2 exit 1 } function checkargs { if [[ $opt == t && $OPTARG =~ ^[0-9]+$ ]]; then TOUR=$(printf "%02g" $OPTARG) fi } while getopts t:hv opt; do case $opt in t) checkargs ;; h) usage ;; v) version ;; *) usage 1 ;; esac done shift $(($OPTIND - 1)) # For now, tour number should be given explicitly [[ -z $TOUR || -z $1 ]] && usage 1 game_setup tour_info=${REPO_DIR}/${TOURNAMENT}/tours/${TOUR}/tour_info [[ ! -f $tour_info ]] && die "File ${tour_info} not found." # Ensure that the repository is up-to-date git pull # Temporary files tmp_ini=$(mktemp -t `basename $ply_ini`.XXXXXX) tmp_pgn=$(mktemp -t pgn.XXXXXX) trap "rm ${tmp_ini} ${tmp_ini}.prev ${tmp_pgn}" EXIT # Download PGN file [[ $1 =~ ^(http://[^/]*)/([^/]*) ]] # Link to annotated PGN pgn_url=${BASH_REMATCH[1]}/game/export/${BASH_REMATCH[2]::8}.pgn wget -q -U "$FIREFOX_UA" -O $tmp_pgn "$pgn_url" \ || die "PGN file at $pgn_url not found." game_get_players game_add_to_repo game_git_commit exit 0