240 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env bash
 | 
						|
# Copyright 2014 Vladimir Ivanov <ivvl82@gmail.com>
 | 
						|
# Distributed under the terms of the GNU General Public License v2
 | 
						|
 | 
						|
REPO_DIR=
 | 
						|
 | 
						|
# Specify the tournament here
 | 
						|
TOURNAMENT=
 | 
						|
 | 
						|
# Version information
 | 
						|
VERSION="0.3"
 | 
						|
 | 
						|
argv0=${0##*/}
 | 
						|
 | 
						|
function usage {
 | 
						|
    cat <<EOF
 | 
						|
Store a chess game played on lichess.org
 | 
						|
 | 
						|
Usage:
 | 
						|
  $argv0 -t <num> <url>
 | 
						|
  $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 <url> (lichess.org) and
 | 
						|
  corresponds to tournament tour <num>. 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://[^/]*)/([^/]*) ]]
 | 
						|
pgn_url=${BASH_REMATCH[1]}/${BASH_REMATCH[2]::8}/pgn
 | 
						|
wget -q "$pgn_url" -O $tmp_pgn || die "PGN file not found."
 | 
						|
 | 
						|
game_get_players
 | 
						|
game_add_to_repo
 | 
						|
game_git_commit
 | 
						|
 | 
						|
exit 0
 |