228 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env bash
 | 
						||
# Copyright 2015 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.1"
 | 
						||
 | 
						||
argv0=${0##*/}
 | 
						||
 | 
						||
function usage {
 | 
						||
    cat <<EOF
 | 
						||
Automatic generation of tours.
 | 
						||
EOF
 | 
						||
 | 
						||
    exit "${1:-0}"
 | 
						||
}
 | 
						||
 | 
						||
function version {
 | 
						||
    exec echo "${argv0}-${VERSION}"
 | 
						||
}
 | 
						||
 | 
						||
function rr_setup {
 | 
						||
    : ${REPO_DIR:=`dirname "$0"`}
 | 
						||
    # 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|")
 | 
						||
    fi
 | 
						||
 | 
						||
    # Configuration file for players
 | 
						||
    ply_ini=${REPO_DIR}/${TOURNAMENT}/players.ini
 | 
						||
    [[ -f $ply_ini ]] || die "File ${ply_ini} not found."
 | 
						||
    local dir_for_tours=${REPO_DIR}/${TOURNAMENT}/tours
 | 
						||
    [[ -d $dir_for_tours ]] && die "Directory ${dir_for_tours} already exist."
 | 
						||
}
 | 
						||
 | 
						||
function rr_get_names {
 | 
						||
    declare -a TMP_NAMES
 | 
						||
    name_re="^ *name *="
 | 
						||
    TMP_NAMES=( $(grep "$name_re" "$ply_ini" | sed "s/${name_re}//") )
 | 
						||
 | 
						||
    if $RAND_ORDER; then
 | 
						||
        # Get the set of random positions
 | 
						||
        local max_range=$(( ${#TMP_NAMES[@]} - 1 ))
 | 
						||
        local rand_array=( $(seq 0 1 $max_range) )
 | 
						||
        for (( range=$max_range; range>1; --range )); do
 | 
						||
            # Peek a random position from 0 to `range'
 | 
						||
            local rand_value=$RANDOM
 | 
						||
            local norm_rand_value=$(bc -l <<< "($rand_value / 32768) * $range")
 | 
						||
            local rand_idx=$(printf "%.0f" $norm_rand_value)
 | 
						||
 | 
						||
            # Place the value at the position to the end of range
 | 
						||
            local tmp=${rand_array[range]}
 | 
						||
            rand_array[range]=${rand_array[rand_idx]}
 | 
						||
            rand_array[rand_idx]=$tmp
 | 
						||
        done
 | 
						||
 | 
						||
        # Randomize the order of players
 | 
						||
        for (( idx=0; idx<${#rand_array[@]}; ++idx )); do
 | 
						||
            local rand_idx=${rand_array[idx]}
 | 
						||
            NAMES[idx]=${TMP_NAMES[rand_idx]}
 | 
						||
        done
 | 
						||
 | 
						||
        echo "$(tput setaf 2)New randomized list of players:$(tput sgr0)"
 | 
						||
        for (( idx=0; idx<${#NAMES[@]}; ++idx )); do
 | 
						||
            echo "$(tput setaf 2)$(( idx+1 )).$(tput sgr0) ${NAMES[idx]}"
 | 
						||
        done
 | 
						||
        rr_update_ini
 | 
						||
    else
 | 
						||
        NAMES=( ${TMP_NAMES[@]} )
 | 
						||
    fi
 | 
						||
}
 | 
						||
 | 
						||
function rr_update_ini {
 | 
						||
    local answer
 | 
						||
    echo -n "Update 'players.ini'? (Y/n)> "
 | 
						||
    read answer
 | 
						||
    [[ $answer =~ ^(Y|y|Yes|yes)$ ]] || return
 | 
						||
 | 
						||
    # Extract INI blocks from config
 | 
						||
    declare -a ini_array
 | 
						||
    local ini_block=
 | 
						||
    while read line; do
 | 
						||
        if [[ $line =~ ^\[player[0-9]+\]$ ]]; then
 | 
						||
            # Close the previous INI block
 | 
						||
            if [[ -n $ini_block ]]; then
 | 
						||
                ini_array+=( "$ini_block" )
 | 
						||
                ini_block=
 | 
						||
            fi
 | 
						||
        fi
 | 
						||
 | 
						||
        [[ $line =~ ^\ *$ ]] || ini_block+="${line}\n"
 | 
						||
    done <"$ply_ini"
 | 
						||
    [[ -n $ini_block ]] && ini_array+=( "$ini_block" )
 | 
						||
 | 
						||
    echo "Updating ${ply_ini}..."
 | 
						||
    mv "$ply_ini" "${ply_ini}.orig"
 | 
						||
    for (( idx=0; idx<${#rand_array[@]}; ++idx )); do
 | 
						||
        local rand_idx=${rand_array[idx]}
 | 
						||
        echo -en "${ini_array[rand_idx]}" \
 | 
						||
            | sed "s/^\[player$(( rand_idx+1 ))\]$/[player$(( idx+1 ))]/" >> "$ply_ini"
 | 
						||
        (( idx < ${#rand_array[@]} - 1 )) && echo >> "$ply_ini"
 | 
						||
    done
 | 
						||
}
 | 
						||
 | 
						||
function rr_gen_table1 {
 | 
						||
    local ply_num= idx= next_idx=
 | 
						||
    # Number of table cells
 | 
						||
    local cell_total=$(bc <<< "$PLY_MAX * ($PLY_MAX + 1)")
 | 
						||
 | 
						||
    # First pass: fill the table with 'PLY_MAX'
 | 
						||
    for (( round=0; round < PLY_MAX; ++round )); do
 | 
						||
        idx=$(bc <<< "($PLY_MAX + 1) * $round + ($round + 1) % 2")
 | 
						||
        TABLE1[idx]=$PLY_MAX
 | 
						||
    done
 | 
						||
 | 
						||
    # Second pass: fill the table with players in ascending order
 | 
						||
    ply_num=0
 | 
						||
    for (( pair=0; pair < cell_total/2; ++pair )); do
 | 
						||
        idx=$(( 2 * pair ))
 | 
						||
        next_idx=$(( 2 * pair + 1 ))
 | 
						||
        if [[ -z ${TABLE1[idx]} ]]; then
 | 
						||
            TABLE1[idx]=$ply_num
 | 
						||
        else
 | 
						||
            TABLE1[next_idx]=$ply_num
 | 
						||
        fi
 | 
						||
 | 
						||
        ply_num=$(( (ply_num + 1) % PLY_MAX ))
 | 
						||
    done
 | 
						||
 | 
						||
    # Second pass: fill the table with players in descending order
 | 
						||
    ply_num=$(( PLY_MAX - 1 ))
 | 
						||
    for (( idx=0; idx < cell_total; ++idx )); do
 | 
						||
        if [[ -z ${TABLE1[idx]} ]]; then
 | 
						||
            TABLE1[idx]=$ply_num
 | 
						||
            ply_num=$(( PLY_MAX - 1 - (PLY_MAX - ply_num) % PLY_MAX ))
 | 
						||
        fi
 | 
						||
    done
 | 
						||
}
 | 
						||
 | 
						||
function rr_gen_table2 {
 | 
						||
    # Number of table cells
 | 
						||
    local cell_total=$(bc <<< "$PLY_MAX * ($PLY_MAX + 1)")
 | 
						||
    # Alter white and black players in the second half of tournament
 | 
						||
    for (( pair=0; pair < cell_total/2; ++pair )); do
 | 
						||
        local idx=$(( 2 * pair ))
 | 
						||
        local next_idx=$(( 2 * pair + 1 ))
 | 
						||
        TABLE2+=( ${TABLE1[next_idx]} ${TABLE1[idx]} )
 | 
						||
    done
 | 
						||
}
 | 
						||
 | 
						||
function rr_gen_tours {
 | 
						||
    declare -a tour_table
 | 
						||
    local tour_num=0 ply_num=0
 | 
						||
    for ply in ${TABLE1[@]} ${TABLE2[@]}; do
 | 
						||
        tour_table+=($ply)
 | 
						||
        (( ply_num++ ))
 | 
						||
 | 
						||
        if (( ply_num > PLY_MAX )); then
 | 
						||
            rr_gen_tour_info
 | 
						||
            # Start new tour
 | 
						||
            (( tour_num++ ))
 | 
						||
            ply_num=0
 | 
						||
            tour_table=()
 | 
						||
        fi
 | 
						||
    done
 | 
						||
}
 | 
						||
 | 
						||
function rr_gen_tour_info {
 | 
						||
    # Change tour numbers: '1' -> '01', '2' -> '02', and so on
 | 
						||
    local tour=$(printf "%02g" $(( tour_num + 1 )))
 | 
						||
    mkdir -p "${REPO_DIR}/${TOURNAMENT}/tours/${tour}"
 | 
						||
    local tour_info=${REPO_DIR}/${TOURNAMENT}/tours/${tour}/tour_info
 | 
						||
    echo "Generating ${tour_info}..."
 | 
						||
 | 
						||
    # Create header
 | 
						||
    (
 | 
						||
        echo "Тур №$(( tour_num + 1 ))"
 | 
						||
        echo "=========="
 | 
						||
        echo
 | 
						||
        echo "Время проведения: ??.??.???? - ??.??.????"
 | 
						||
        echo
 | 
						||
    ) >> "$tour_info"
 | 
						||
 | 
						||
    # Create pairs
 | 
						||
    for (( pair=0; pair < (PLY_MAX+1)/2; ++pair )); do
 | 
						||
        local idx=$(( 2 * pair ))
 | 
						||
        local next_idx=$(( 2 * pair + 1 ))
 | 
						||
        local white_ply=${tour_table[idx]}
 | 
						||
        local black_ply=${tour_table[next_idx]}
 | 
						||
        echo "${NAMES[white_ply]} - ${NAMES[black_ply]}"
 | 
						||
    done | column -t | sed "s/ - /-/; s/^/??.??.???? /" >> "$tour_info"
 | 
						||
}
 | 
						||
 | 
						||
function die {
 | 
						||
    echo "$@" >&2
 | 
						||
    exit 1
 | 
						||
}
 | 
						||
 | 
						||
RAND_ORDER=false
 | 
						||
while getopts rhv opt; do
 | 
						||
    case $opt in
 | 
						||
        r) RAND_ORDER=true ;;
 | 
						||
        h) usage ;;
 | 
						||
        v) version ;;
 | 
						||
        *) usage 1 ;;
 | 
						||
    esac
 | 
						||
done
 | 
						||
shift $(( $OPTIND - 1 ))
 | 
						||
 | 
						||
rr_setup
 | 
						||
declare -a NAMES
 | 
						||
rr_get_names
 | 
						||
# The number of players should be even
 | 
						||
PLY_MAX=$(( ${#NAMES[@]} + ${#NAMES[@]} % 2 - 1 ))
 | 
						||
declare -a TABLE1 TABLE2
 | 
						||
rr_gen_table1                   # first half of tournament
 | 
						||
rr_gen_table2                   # second half of tournament
 | 
						||
rr_gen_tours
 | 
						||
 | 
						||
exit 0
 |