lorchess/game-add
2014-11-08 11:44:23 +03:00

241 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://[^/]*)/([^/]*) ]]
# Link to annotated PGN
pgn_url=${BASH_REMATCH[1]}/game/pgn/${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