#!/usr/bin/env bash # Copyright 2014 Vladimir Ivanov # Distributed under the terms of the GNU General Public License v2 # Specify the tournament here TOURNAMENT= # Version information VERSION="0.2" argv0=${0##*/} function usage { cat < $argv0 -h $argv0 -v The first form fills the result of a chess game and stores its PGN file. Assumes 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 { REPO_DIR=`dirname "$0"` # Convert REPO_DIR to an absolute path 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 w_lichess=$(sed -En "s/\[White \"([^\"]*)\"\]/\1/p" < $tmp_pgn) local b_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 == $w_lichess ]] && white=$name [[ $lichess == $b_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 "s/[[:blank:]]*=[[:blank:]]*/=/" -i.prev "$tmp_ini" # Transform section labels into function declaration sed -E "1,/^\[.*\]/s/^\[([^]]*)\]/config_section_\1() {/" -i.prev "$tmp_ini" sed -E "s/^\[([^]]*)\]/}\\"$'\n'"config_section_\1() {/g" -i.prev "$tmp_ini" echo -e "\n}" >> $tmp_ini # Source the file source "$tmp_ini" } function fix_white_player { local number echo "Lichess player '${w_lichess}' not found." choose_player white=${players[$number]} } function fix_black_player { local number echo "Lichess player '${b_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)/g" # 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} # Check if the tour number is correct local correct=false length_max=0 while IFS= read line; do if [[ $line =~ ^(${date_re}\ +([^\ ]+)\ +-\ +([^\ ]+)) ]]; then if [[ ${BASH_REMATCH[2]} == $white && ${BASH_REMATCH[3]} == $black ]]; then correct=true local length_rec=${#BASH_REMATCH[1]} fi # Determine the maximal length of game record local length=${#BASH_REMATCH[1]} (( $length > $length_max )) && length_max=$length fi done < $tour_info if ! $correct; then die "Game '${white} vs. ${black}' not found in ${tour_info}." fi [[ -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" " ") sed -E "s/${date_re}( +${white} +- +${black})/${game_date}\1${sep}${result}/g" \ -i.orig "$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://[^/]*)/([^/]*) ]] pgn_url=${BASH_REMATCH[1]}/${BASH_REMATCH[2]::8}/pgn curl -q --fail --location --silent "$pgn_url" > $tmp_pgn \ || die "PGN file not found." game_get_players pgn_date=$(sed -En "s/\[Date \"([^\"]*)\"\]/\1/p" < "$tmp_pgn") pgn_date=$(tr "." "-" <<< "$pgn_date") pgn_dir="${REPO_DIR}/${TOURNAMENT}/tours/${TOUR}/${pgn_date}-${white}-vs-${black}" game_add_to_repo game_git_commit exit 0