#!/usr/local/bin/bash
#
# Chess Bash
# a simple chess game written in an inappropriate language :)
#
# Copyright (c) 2015 by Bernhard Heinloth <bernhard@heinloth.net>
#
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

# Default values
strength=3
namePlayerA="Player"
namePlayerB="AI"
color=true
colorPlayerA=4
colorPlayerB=1
colorHover=4
colorHelper=true
colorFill=true
ascii=false
warnings=false
computer=-1
mouse=true
guiconfig=false
cursor=true
sleep=2
cache=""
cachecompress=false
unicodelabels=true
port=12433

# internal values
timestamp=$( date +%s%N )
fifopipeprefix="/tmp/chessbashpipe"
selectedX=-1
selectedY=-1
selectedNewX=-1
selectedNewY=-1
remote=0
remoteip=127.0.0.1
remotedelay=0.1
remotekeyword="remote"
aikeyword="ai"
aiPlayerA="Marvin"
aiPlayerB="R2D2"
A=-1
B=1
originY=4
originX=7
hoverX=0
hoverY=0
hoverInit=false
labelX=-2
labelY=9
type stty >/dev/null 2>&1 && useStty=true || useStty=false

# Choose unused color for hover
while (( colorHover == colorPlayerA || colorHover == colorPlayerB )) ; do
    (( colorHover++ ))
done

# Check Unicode availbility
# We do this using a trick: printing a special zero-length unicode char (http://en.wikipedia.org/wiki/Combining_Grapheme_Joiner) and retrieving the cursor position afterwards.
# If the cursor position is at beginning, the terminal knows unicode. Otherwise it has printed some replacement character.
echo -en "\e7\e[s\e[H\r\xcd\x8f\e[6n" && read -sN6 -t0.1 x
if [[ "${x:4:1}" == "1" ]] ; then
    ascii=false
    unicodelabels=true
else
    ascii=true
    unicodelabels=false
fi
echo -e "\e[u\e8\e[2K\r\e[0m\nWelcome to \e[1mChessBa.sh\e[0m - a Chess game written in Bash \e[2mby Bernhard Heinloth, 2015\e[0m\n"

# Print version information
function version() {
    echo "ChessBash 0.4"
}

# Wait for key press
# no params/return
function anyKey(){
    $useStty && stty echo
    echo -e "\e[2m(Press any key to continue)\e[0m"
    read -sN1
    $useStty && stty -echo
}

# Error message, p.a. on bugs
# Params:
#   $1  message
# (no return value, exit game)
function error() {
    if $color ; then
        echo -e "\e[0;1;41m $1 \e[0m\n\e[3m(Script exit)\e[0m" >&2
    else
        echo -e "\e[0;1;7m $1 \e[0m\n\e[3m(Script exit)\e[0m" >&2
    fi
    anyKey
    exit 1
}

# Check prerequisits (additional executables)
# taken from an old script of mine (undertaker-tailor)
# Params:
#   $1  name of executable
function require() {
    type "$1" >/dev/null 2>&1 ||
        {
            echo "This requires $1 but it is not available on your system. Aborting." >&2
            exit 1
        }
}

# Validate a number string
# Params:
#   $1  String with number
# Return 0 if valid, 1 otherwise
function validNumber() {
    if [[ "$1" =~ ^[0-9]+$ ]] ; then
        return 0
    else
        return 1
    fi
}

# Validate a port string
# Must be non privileged (>1023)
# Params:
#   $1  String with port number
# Return 0 if valid, 1 otherwise
function validPort() {
    if validNumber "$1" && (( 1 < 65536 && 1 > 1023 )) ; then
        return 0
    else
        return 1
    fi
}

# Validate an IP v4 or v6 address
# source: http://stackoverflow.com/a/9221063
# Params:
#   $1  IP address to validate
# Return 0 if valid, 1 otherwise
function validIP() {
    if [[ "$1" =~ ^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))|((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))))$ ]] ; then
        return 0
    else
        return 1
    fi
}

# Named ANSI colors
declare -a colors=( "black" "red" "green" "yellow" "blue" "magenta" "cyan" "white" )

# Retrieve ANSI color code from string
# Black and white are ignored!
# Params:
#   $1  Color string
# Return Color code or 0 if not a valid
function getColor() {
    local c
    for (( c=1; c<7; c++ )) ; do
        local v=${colors[$c]:0:1}
        local i=${1:0:1}
        if [[ "${v^^}" == "${i^^}" || "$c" -eq "$i" ]] ; then
            return $c
        fi
    done
    return 0
}

# Check if ai player
# Params:
#   $1  player
# Return status code 0 if ai player
function isAI() {
    if (( $1 < 0 )) ; then
        if [[ "${namePlayerA,,}" == "${aikeyword,,}" ]] ; then
            return 0
        else
            return 1
        fi
    else
        if [[ "${namePlayerB,,}" == "${aikeyword,,}" ]] ; then
            return 0
        else
            return 1
        fi
    fi
}

# Help message
# Writes text to stdout
function help {
    echo
    echo -e "\e[1mChess Bash\e[0m - a small chess game written in Bash"
    echo
    echo -e "\e[4mUsage:\e[0m $0 [options]"
    echo
    echo -e "\e[4mConfiguration options\e[0m"
    echo "    -g         Use a graphical user interface (instead of more parameters)"
    echo
    echo -e "\e[4mGame options\e[0m"
    echo -e "    -a \e[2mNAME\e[0m    Name of first player, \"$aikeyword\" for computer controlled or the"
    echo "               IP address of remote player (Default: $namePlayerA)"
    echo -e "    -b \e[2mNAME\e[0m    Name of second player, \"$aikeyword\" for computer controlled or"
    echo -e "               \"$remotekeyword\" for another player (Default: \e[2m$namePlayerB\e[0m)"
    echo -e "    -s \e[2mNUMBER\e[0m  Strength of computer (Default: \e[2m$strength\e[0m)"
    echo -e "    -w \e[2mNUMBER\e[0m  Waiting time for messages in seconds (Default: \e[2m$sleep\e[0m)"
    echo
    echo -e "\e[4mNetwork settings for remote gaming\e[0m"
    echo -e "    -P \e[2mNUMBER\e[0m  Set port for network connection (Default: \e[2m$port\e[0m)"
    echo -e "\e[1;33mAttention:\e[0;33m On a network game the person controlling the first player / A"
    echo -e "(using \"\e[2;33m-b $remotekeyword\e[0;33m\" as parameter) must start the game first!\e[0m"
    echo
    echo -e "\e[4mCache management\e[0m"
    echo -e "    -c \e[2mFILE\e[0m    Makes cache permanent - load and store calculated moves"
    echo "    -z         Compress cache file (only to be used with -c, requires gzip)"
    echo -e "    -t \e[2mSTEPS\e[0m   Exit after STEPS ai turns and print time (for benchmark)"
    echo
    echo -e "\e[4mOutput control\e[0m"
    echo "    -h         This help message"
    echo "    -v         Version information"
    echo "    -V         Disable VT100 cursor movement (for partial output changes)"
    echo "    -M         Disable terminal mouse support"
    echo "    -i         Enable verbose input warning messages"
    echo "    -l         Board labels in ASCII (instead of Unicode)"
    echo "    -p         Plain ascii output (instead of cute unicode figures)"
    echo "               This implies ASCII board labels (\"-l\")"
    echo "    -d         Disable colors (only black/white output)"
    echo -e "    \e[4mFollowing options will have no effect while colors are disabled:\e[0m"
    echo -e "    -A \e[2mNUMBER\e[0m  Color code of first player (Default: \e[2m$colorPlayerA\e[0m)"
    echo -e "    -B \e[2mNUMBER\e[0m  Color code of second player (Default: \e[2m$colorPlayerB\e[0m)"
    echo "    -n         Use normal (instead of color filled) figures"
    echo "    -m         Disable color marking of possible moves"
    echo
    echo -e "\e[2m(Default values/options should suit most systems - only if you encounter a"
    echo -e "problem you should have a further investigation of these script parameters."
    echo -e "Or just switch to a real chess game with great graphics and ai! ;)\e[0m"
    echo
}

# Parse command line arguments
while getopts ":a:A:b:B:c:P:s:t:w:dghilmMnpvVz" options; do
    case $options in
        a ) if [[ -z "$OPTARG" ]] ;then
                echo "No valid name for first player specified!" >&2
                exit 1
            # IPv4 && IPv6 validation, source: http://stackoverflow.com/a/9221063
            elif validIP "$OPTARG" ; then
                remote=-1
                remoteip="$OPTARG"
            else
                namePlayerA="$OPTARG"
            fi
            ;;
        A ) if ! getColor "$OPTARG" ; then
                colorPlayerA=$?
            else
                echo "'$OPTARG' is not a valid color!" >&2
                exit 1
            fi
            ;;
        b ) if [[ -z "$OPTARG" ]] ;then
                echo "No valid name for second player specified!" >&2
                exit 1
            elif [[ "${OPTARG,,}" == "$remotekeyword" ]] ; then
                remote=1
            else
                namePlayerB="$OPTARG"
            fi
            ;;
        B ) if ! getColor "$OPTARG" ; then
                colorPlayerB=$?
            else
                echo "'$OPTARG' is not a valid color!" >&2
                exit 1
            fi
            ;;
        s ) if validNumber "$OPTARG" ; then
                strength=$OPTARG
            else
                echo "'$OPTARG' is not a valid strength!" >&2
                exit 1
            fi
            ;;
        P ) if validPort "$OPTARG" ; then
                port=$OPTARG
            else
                echo "'$OPTARG' is not a valid gaming port!" >&2
                exit 1
            fi
            ;;
        w ) if validNumber "$OPTARG" ; then
                sleep=$OPTARG
            else
                echo "'$OPTARG' is not a valid waiting time!" >&2
                exit 1
            fi
            ;;
        c ) if [[ -z "$OPTARG" ]] ; then
                echo "No valid path for cache file!" >&2
                exit 1
            else
                cache="$OPTARG"
            fi
            ;;
        t ) if validNumber "$OPTARG" ; then
                computer=$OPTARG
            else
                echo "'$OPTARG' is not a valid number for steps!" >&2
                exit 1
            fi
            ;;
        d ) color=false
            ;;
        g ) guiconfig=true
            ;;
        l ) unicodelabels=false
            ;;
        n ) colorFill=false
            ;;
        m ) colorHelper=false
            ;;
        M ) mouse=false
            ;;
        p ) ascii=true
            unicodelabels=false
            ;;
        i ) warnings=true
            ;;
        v ) version
            ;;
        V ) cursor=false
            ;;
        z ) require gzip
            require zcat
            cachecompress=true
            ;;
        h ) help
            exit 0
            ;;
        \?)
            echo "Invalid option: -$OPTARG" >&2
            ;;
    esac
done

# get terminal dimension
echo -en '\e[18t'
if read -d "t" -s -t 1 tmp ; then
    termDim=(${tmp//;/ })
    termHeight=${termDim[1]}
    termWidth=${termDim[2]}
else
    termHeight=24
    termWidth=80
fi

# gui config
if $guiconfig ; then

    # find a dialog system
    if type gdialog >/dev/null 2>&1 ; then
        dlgtool="gdialog"
        dlgh=0
        dlgw=100
    elif type dialog >/dev/null 2>&1 ; then
        dlgtool="dialog"
        dlgh=0
        dlgw=0
    elif type whiptail >/dev/null 2>&1 ; then
        dlgtool="whiptail"
        dlgh=0
        dlgw=$(( termWidth-10 ))
    else
        dlgtool=""
        error "The graphical configuration requires gdialog/zenity, dialog or at least whiptail - but none of them was found on your system. You have to use the arguments to configure the game unless you install one of the required tools..."
    fi

    # Output the type of the first player in a readable string
    function typeOfPlayerA() {
        if [[ "$remote" -eq "-1" ]] ; then
            echo "Connect to $remoteip (Port $port)"
            return 2
        elif isAI $A ; then
            echo "Artificial Intelligence (with strength $strength)"
            return 1
        else
            echo "Human named $namePlayerA"
            return 0
        fi
    }

    # Output the type of the second player in a readable string
    function typeOfPlayerB() {
        if [[ "$remote" -eq "1" ]] ; then
            echo "Host server at port $port"
            return 2
        elif isAI $B ; then
            echo "Artificial Intelligence (with strength $strength)"
            return 1
        else
            echo "Human named $namePlayerB"
            return 0
        fi
    }

    # Execute a dialog
    # Params: Dialog params (variable length)
    # Prints: Dialog output seperated by new lines
    # Returns the dialog program return or 255 if no dialog tool available
    function dlg() {
        if [[ -n "$dlgtool" ]] ; then
            $dlgtool --backtitle "ChessBash" "$@" 3>&1 1>&2 2>&3 | sed -e "s/|/\n/g" | sort -u
            return ${PIPESTATUS[0]}
        else
            return 255
        fi
    }

    # Print a message box with a warning/error message
    # Params:
    #   $1  Message
    function dlgerror() {
        #TODO: normal error
        dlg --msgbox "$1" $dlgh $dlgw
    }

    # Start the dialog configuration
    # Neither params nor return, this is just a function for hiding local variables!
    function dlgconfig() {
        local option_mainmenu_playerA="First Player"
        local option_mainmenu_playerB="Second Player"
        local option_mainmenu_settings="Game settings"
        local dlg_on="ON"
        local dlg_off="OFF"

        declare -a option_player=( "Human" "Computer" "Network" )
        declare -a option_settings=( "Color support" "Unicode support" "Verbose Messages" "Mouse support" "AI Cache" )

        local dlg_main
        while dlg_main=$(dlg --ok-button "Edit" --cancel-button "Start Game" --menu "New Game" $dlgh $dlgw 0 "$option_mainmenu_playerA" "$(typeOfPlayerA || true)" "$option_mainmenu_playerB" "$(typeOfPlayerB || true )" "$option_mainmenu_settings" "Color, Unicode, Mouse & AI Cache") ; do
            case "$dlg_main" in

                # Player A settings
                "$option_mainmenu_playerA" )
                    typeOfPlayerA > /dev/null
                    local type=$?
                    local dlg_player
                    dlg_player=$(dlg --nocancel --default-item "${option_player[$type]}" --menu "$option_mainmenu_playerA" $dlgh $dlgw 0 "${option_player[0]}" "$( isAI $A && echo "$option_mainmenu_playerA" || echo "$namePlayerA" )" "${option_player[1]}" "with AI (of strength $strength)" "${option_player[2]}" "Connect to Server $remoteip" )
                    case "$dlg_player" in
                        # Human --> get Name
                        *"${option_player[0]}"* )
                            [[ "$remote" -eq "-1" ]] && remote=0
                            local dlg_namePlayer
                            dlg_namePlayer=$(dlg --inputbox "Name of $option_mainmenu_playerA" $dlgh $dlgw "$( isAI $A && echo "$option_mainmenu_playerA" || echo "$namePlayerA" )") && namePlayerA="$dlg_namePlayer"
                            ;;
                        # Computer --> get Strength
                        *"${option_player[1]}"* )
                            [[ "$remote" -eq "-1" ]] && remote=0
                            namePlayerA=$aikeyword
                            local dlg_strength
                            if dlg_strength=$(dlg --inputbox "Strength of Computer" $dlgh $dlgw  "$strength") ; then
                                if validNumber "$dlg_strength" ; then
                                    strength=$dlg_strength
                                else
                                    dlgerror "Your input '$dlg_strength' is not a valid number!"
                                fi
                            fi
                            ;;
                        # Network --> get Server and Port
                        *"${option_player[2]}"* )
                            local dlg_remoteip
                            if dlg_remoteip=$(dlg --inputbox "IP(v4 or v6) address of Server" $dlgh $dlgw "$remoteip") ; then
                                if validIP "$dlg_remoteip" ; then
                                    remote=-1
                                    remoteip="$dlg_remoteip"
                                    local dlg_networkport
                                    if dlg_networkport=$(dlg --inputbox "Server Port (non privileged)" $dlgh $dlgw "$port") ; then
                                         if validPort "$dlg_networkport" ; then
                                        port=$dlg_networkport
                                        else
                                        dlgerror "Your input '$dlg_remoteip' is not a valid Port!"
                                        fi
                                    fi
                                else
                                    dlgerror "Your input '$dlg_remoteip' is no valid IP address!"
                                    continue
                                fi
                            fi
                            ;;
                    esac
                    # Player color
                    if $color ; then
                        local colorlist=""
                        local c
                        for (( c=1; c<7; c++ )) ; do
                            colorlist+=" ${colors[$c]^} figures"
                        done
                        local dlg_player_color
                        if dlg_player_color=$(dlg --nocancel --default-item "${colors[$colorPlayerA]^}" --menu "Color of $option_mainmenu_playerA" $dlgh $dlgw 0 "$colorlist") ; then
                            getColor "$dlg_player_color" || colorPlayerA=$?
                        fi
                    fi
                    ;;

                # Player B settings
                "$option_mainmenu_playerB" )
                    typeOfPlayerB > /dev/null
                    local type=$?
                    local dlg_player
                    dlg_player=$(dlg --nocancel --default-item "${option_player[$type]}" --menu "$option_mainmenu_playerB" $dlgh $dlgw 0 "${option_player[0]}" "$( isAI $B && echo "$option_mainmenu_playerB" || echo "$namePlayerB" )" "${option_player[1]}" "with AI (of strength $strength)" "${option_player[2]}" "Wait for connections on port $port" )
                    case "$dlg_player" in
                        # Human --> get Name
                        *"${option_player[0]}"* )
                            [[ "$remote" -eq "1" ]] && remote=0
                            local dlg_namePlayer
                            dlg_namePlayer=$(dlg --inputbox "Name of $option_mainmenu_playerB" $dlgh $dlgw "$( isAI $B && echo "$option_mainmenu_playerB" || echo "$namePlayerB" )") && namePlayerA="$dlg_namePlayer"
                            ;;
                        # Computer --> get Strength
                        *"${option_player[1]}"* )
                            [[ "$remote" -eq "1" ]] && remote=0
                            namePlayerB=$aikeyword
                            local dlg_strength
                            if dlg_strength=$(dlg --inputbox "Strength of Computer" $dlgh $dlgw  "$strength") ; then
                                if validNumber "$dlg_strength" ; then
                                    strength=$dlg_strength
                                else
                                    dlgerror "Your input '$dlg_strength' is not a valid number!"
                                fi
                            fi
                            ;;
                        # Network --> get Server and Port
                        *"${option_player[2]}"* )
                            remote=1
                            local dlg_networkport
                            if dlg_networkport=$(dlg --inputbox "Server Port (non privileged)" $dlgh $dlgw "$port") ; then
                                 if validPort "$dlg_networkport" ; then
                                    port=$dlg_networkport
                                else
                                    dlgerror "Your input '$dlg_remoteip' is not a valid Port!"
                                fi
                            fi
                            ;;
                    esac
                    # Player color
                    if $color ; then
                        local colorlist=""
                        local c
                        for (( c=1; c<7; c++ )) ; do
                            colorlist+=" ${colors[$c]^} figures"
                        done
                        local dlg_player_color
                        if dlg_player_color=$(dlg --nocancel --default-item "${colors[$colorPlayerB]^}" --menu "Color of $option_mainmenu_playerB" $dlgh $dlgw 0 "$colorlist") ; then
                            getColor "$dlg_player_color" || colorPlayerB=$?
                        fi
                    fi
                    ;;

                # Game settings
                "$option_mainmenu_settings" )
                    if dlg_settings=$(dlg --separate-output --checklist "$option_mainmenu_settings" $dlgh $dlgw $dlgw "${option_settings[0]}" "with movements and figures" $($color && echo $dlg_on || echo $dlg_off) "${option_settings[1]}" "optional including board labels" $($ascii && echo $dlg_off || echo $dlg_on) "${option_settings[2]}" "be chatty" $($warnings && echo $dlg_on || echo $dlg_off) "${option_settings[3]}" "be clicky" $($mouse && echo $dlg_on || echo $dlg_off) "${option_settings[4]}" "in a regluar file" $([[ -n "$cache" ]] && echo $dlg_on || echo $dlg_off) ) ; then
                        # Color support
                        if [[ "$dlg_settings" == *"${option_settings[0]}"* ]] ; then
                            color=true
                            dlg --yesno "Enable movement helper (colorize possible move)?" $dlgh $dlgw && colorHelper=true || colorHelper=false
                            dlg --yesno "Use filled (instead of outlined) figures for both player?" $dlgh $dlgw && colorFill=true || colorFill=false
                        else
                            color=false
                            colorFill=false
                            colorHelper=false
                        fi
                        # Unicode support
                        if [[ "$dlg_settings" == *"${option_settings[1]}"* ]] ; then
                            ascii=false
                            ( dlg --yesno "Use Unicode for board labels?" $dlgh $dlgw ) && unicodelabels=true || unicodelabels=false
                        else
                            ascii=true
                            unicodelabels=false
                        fi
                        # Verbose messages
                        [[ "$dlg_settings" == *"${option_settings[2]}"* ]] && warnings=true || warnings=false
                        # Mouse support
                        [[ "$dlg_settings" == *"${option_settings[3]}"* ]] && mouse=true || mouse=false
                        # AI Cache
                        local dlg_cache
                        if [[ "$dlg_settings" == *"${option_settings[4]}"* ]] && dlg_cache=$(dlg --inputbox "Cache file:" $dlgh $dlgw "$([[ -z "$cache" ]] && echo "$(pwd)/chessbash.cache" || echo "$cache")") && [[ -n "$dlg_cache" ]] ; then
                            cache="$dlg_cache"
                            type gzip >/dev/null 2>&1 && type zcat >/dev/null 2>&1 && dlg --yesno "Use GZip compression for Cache?" $dlgh $dlgw && cachecompress=true || cachecompress=false
                        else
                            cache=""
                        fi
                        # Waiting time (ask always)
                        local dlg_sleep
                        if dlg_sleep=$(dlg --inputbox "How long should every message be displayed (in seconds)?" $dlgh $dlgw "$sleep") ; then
                            if validNumber "$dlg_sleep" ; then
                                sleep=$dlg_sleep
                            else
                                dlgerror "Your input '$dlg_sleep' is not a valid number!"
                            fi
                        fi
                    fi
                    ;;

                # Other --> exit (gdialog)
                * )
                    break
                    ;;
            esac
        done
    }

    # start config dialog
    dlgconfig
fi

# Save screen
if $cursor ; then
    echo -e "\e7\e[s\e[?47h\e[?25l\e[2J\e[H"
fi

# lookup tables
declare -A cacheLookup
declare -A cacheFlag
declare -A cacheDepth

# associative arrays are faster than numeric ones and way more readable
declare -A redraw
if $cursor ; then
    for (( y=0; y<10; y++ )) ; do
        for (( x=-2; x<8; x++ )) ; do
            redraw[$y,$x]=""
        done
    done
fi

declare -A field

# initialize setting - first row
declare -a initline=( 4  2  3  6  5  3  2  4 )
for (( x=0; x<8; x++ )) ; do
    field[0,$x]=${initline[$x]}
    field[7,$x]=$(( (-1) * ${initline[$x]} ))
done
# set pawns
for (( x=0; x<8; x++ )) ; do
    field[1,$x]=1
    field[6,$x]=-1
done
# set empty fields
for (( y=2; y<6; y++ )) ; do
    for (( x=0; x<8; x++ )) ; do
        field[$y,$x]=0
    done
done

# readable figure names
declare -a figNames=( "(empty)" "pawn" "knight" "bishop" "rook" "queen" "king" )
# ascii figure names (for ascii output)
declare -a asciiNames=( "k" "q" "r" "b" "n" "p" " " "P" "N" "B" "R" "Q" "K" )
# figure weight (for heuristic)
declare -a figValues=( 0 1 5 5 6 17 42 )

# Warning message on invalid moves (Helper)
# Params:
#   $1  message
# (no return value)
function warn() {
    message="\e[41m\e[1m$1\e[0m\n"
    draw
}

# Readable coordinates
# Params:
#   $1  row position
#   $2  column position
# Writes coordinates to stdout
function coord() {
    echo -en "\x$((48-$1))$(($2+1))"
}

# Get name of player
# Params:
#   $1  player
# Writes name to stdout
function namePlayer() {
    if (( $1 < 0 )) ; then
        if $color ; then
            echo -en "\e[3${colorPlayerA}m"
        fi
        if isAI "$1" ; then
            echo -n "$aiPlayerA"
        else
            echo -n "$namePlayerA"
        fi
    else
        if $color ; then
            echo -en "\e[3${colorPlayerB}m"
        fi
        if isAI "$1" ; then
            echo -n "$aiPlayerB"
        else
            echo -n "$namePlayerB"
        fi
    fi
    if $color ; then
        echo -en "\e[0m"
    fi
}

# Get name of figure
# Params:
#   $1  figure
# Writes name to stdout
function nameFigure() {
    if (( $1 < 0 )) ; then
        echo -n "${figNames[$1*(-1)]}"
    else
        echo -n "${figNames[$1]}"
    fi
}

# Check win/loose position
# (player has king?)
# Params:
#   $1  player
# Return status code 1 if no king
function hasKing() {
    local player=$1;
    local x
    local y
    for (( y=0;y<8;y++ )) ; do
        for (( x=0;x<8;x++ )) ; do
            if (( ${field[$y,$x]} * player == 6 )) ; then
                return 0
            fi
        done
    done
    return 1
}

# Check validity of a concrete single movement
# Params:
#   $1  origin Y position
#   $2  origin X position
#   $3  target Y position
#   $4  target X position
#   $5  current player
# Returns status code 0 if move is valid
function canMove() {
    local fromY=$1
    local fromX=$2
    local toY=$3
    local toX=$4
    local player=$5

    local i
    if (( fromY < 0 || fromY >= 8 || fromX < 0 || fromX >= 8 || toY < 0 || toY >= 8 || toX < 0 || toX >= 8 || ( fromY == toY && fromX == toX ) )) ; then
        return 1
    fi
    local from=${field[$fromY,$fromX]}
    local to=${field[$toY,$toX]}
    local fig=$(( from * player ))
    if (( from == 0 || from * player < 0 || to * player > 0 || player * player != 1 )) ; then
        return 1
    # pawn
    elif (( fig == 1 )) ; then
        if (( fromX == toX && to == 0 && ( toY - fromY == player || ( toY - fromY == 2 * player && ${field["$((player + fromY)),$fromX"]} == 0 && fromY == ( player > 0 ? 1 : 6 ) ) ) )) ; then
                return 0
            else
                return $(( ! ( (fromX - toX) * (fromX - toX) == 1 && toY - fromY == player && to * player < 0 ) ))
        fi
    # queen, rock and bishop
    elif (( fig == 5 || fig == 4  || fig == 3 )) ; then
        # rock - and queen
        if (( fig != 3 )) ; then
            if (( fromX == toX )) ; then
                for (( i = ( fromY < toY ? fromY : toY ) + 1 ; i < ( fromY > toY ? fromY : toY ) ; i++ )) ; do
                    if (( ${field[$i,$fromX]} != 0 )) ; then
                        return 1
                    fi
                done
                return 0
            elif (( fromY == toY )) ; then
                for (( i = ( fromX < toX ? fromX : toX ) + 1 ; i < ( fromX > toX ? fromX : toX ) ; i++ )) ; do
                        if (( ${field[$fromY,$i]} != 0 )) ; then
                            return 1
                        fi
                done
                return 0
            fi
        fi
        # bishop - and queen
        if (( fig != 4 )) ; then
            if (( ( fromY - toY ) * ( fromY - toY ) != ( fromX - toX ) * ( fromX - toX ) )) ; then
                return 1
            fi
            for (( i = 1 ; i < ( $fromY > toY ? fromY - toY : toY - fromY) ; i++ )) ; do
                if (( ${field[$((fromY + i * (toY - fromY > 0 ? 1 : -1 ) )),$(( fromX + i * (toX - fromX > 0 ? 1 : -1 ) ))]} != 0 )) ; then
                    return 1
                fi
            done
            return 0
        fi
        # nothing found? wrong move.
        return 1
    # knight
    elif (( fig == 2 )) ; then
        return $(( ! ( ( ( fromY - toY == 2 || fromY - toY == -2) && ( fromX - toX == 1 || fromX - toX == -1 ) ) || ( ( fromY - toY == 1 || fromY - toY == -1) && ( fromX - toX == 2 || fromX - toX == -2 ) ) ) ))
    # king
    elif (( fig == 6 )) ; then
        return $(( !( ( ( fromX - toX ) * ( fromX - toX ) ) <= 1 &&  ( ( fromY - toY ) * ( fromY - toY ) ) <= 1 ) ))
    # invalid figure
    else
        error "Invalid figure '$from'!"
        exit 1
    fi
}

# minimax (game theory) algorithm for evaluate possible movements
# (the heart of your computer enemy)
# currently based on negamax with alpha/beta pruning and transposition tables liked described in
# http://en.wikipedia.org/wiki/Negamax#NegaMax_with_Alpha_Beta_Pruning_and_Transposition_Tables
# Params:
#   $1  current search depth
#   $2  alpha (for pruning)
#   $3  beta (for pruning)
#   $4  current moving player
#   $5  preserves the best move (for ai) if true
# Returns best value as status code
function negamax() {
    local depth=$1
    local a=$2
    local b=$3
    local player=$4
    local save=$5
    # transposition table
    local aSave=$a
    local hash
    hash="$player ${field[@]}"
    if ! $save && test "${cacheLookup[$hash]+set}" && (( ${cacheDepth[$hash]} >= depth )) ; then
        local value=${cacheLookup[$hash]}
        local flag=${cacheFlag[$hash]}
        if (( flag == 0 )) ; then
            return $value
        elif (( flag == 1 && value > a )) ; then
            a=$value
        elif (( flag == -1 && value < b )) ; then
            b=$value
        fi
        if (( a >= b )) ; then
            return $value
        fi
    fi
    # lost own king?
    if ! hasKing "$player" ; then
        cacheLookup[$hash]=$(( strength - depth + 1 ))
        cacheDepth[$hash]=$depth
        cacheFlag[$hash]=0
        return $(( strength - depth + 1 ))
    # use heuristics in depth
    elif (( depth <= 0 )) ; then
        local values=0
        for (( y=0; y<8; y++ )) ; do
            for (( x=0; x<8; x++ )) ; do
                local fig=${field[$y,$x]}
                if (( ${field[$y,$x]} != 0 )) ; then
                    local figPlayer=$(( fig < 0 ? -1 : 1 ))
                    # a more simple heuristic would be values=$(( $values + $fig ))
                    (( values += ${figValues[$fig * $figPlayer]} * figPlayer ))
                    # pawns near to end are better
                    if (( fig == 1 )) ; then
                        if (( figPlayer > 0 )) ; then
                            (( values += ( y - 1 ) / 2 ))
                        else
                            (( values -= ( 6 + y ) / 2 ))
                        fi
                    fi
                fi
            done
        done
        values=$(( 127 + ( player * values ) ))
        # ensure valid bash return range
        if (( values > 253 - strength )) ; then
            values=$(( 253 - strength ))
        elif (( values < 2 + strength )) ; then
            values=$(( 2 + strength ))
        fi
        cacheLookup[$hash]=$values
        cacheDepth[$hash]=0
        cacheFlag[$hash]=0
        return $values
    # calculate best move
    else
        local bestVal=0
        local fromY
        local fromX
        local toY
        local toX
        local i
        local j
        for (( fromY=0; fromY<8; fromY++ )) ; do
            for (( fromX=0; fromX<8; fromX++ )) ; do
                local fig=$(( ${field[$fromY,$fromX]} * ( player ) ))
                # precalc possible fields (faster then checking every 8*8 again)
                local targetY=()
                local targetX=()
                local t=0
                # empty or enemy
                if (( fig <= 0 )) ; then
                    continue
                # pawn
                elif (( fig == 1 )) ; then
                    targetY[$t]=$(( player + fromY ))
                    targetX[$t]=$(( fromX ))
                    (( t += 1 ))
                    targetY[$t]=$(( 2 * player + fromY ))
                    targetX[$t]=$(( fromX ))
                    (( t += 1 ))
                    targetY[$t]=$(( player + fromY ))
                    targetX[$t]=$(( fromX + 1 ))
                    (( t += 1 ))
                    targetY[$t]=$(( player + fromY ))
                    targetX[$t]=$(( fromX - 1 ))
                    (( t += 1 ))
                # knight
                elif (( fig == 2 )) ; then
                    for (( i=-1 ; i<=1 ; i=i+2 )) ; do
                        for (( j=-1 ; j<=1 ; j=j+2 )) ; do
                            targetY[$t]=$(( fromY + 1 * i ))
                            targetX[$t]=$(( fromX + 2 * j ))
                            (( t + 1 ))
                            targetY[$t]=$(( fromY + 2 * i ))
                            targetX[$t]=$(( fromX + 1 * j ))
                            (( t + 1 ))
                        done
                    done
                # king
                elif (( fig == 6 )) ; then
                    for (( i=-1 ; i<=1 ; i++ )) ; do
                        for (( j=-1 ; j<=1 ; j++ )) ; do
                            targetY[$t]=$(( fromY + i ))
                            targetX[$t]=$(( fromX + j ))
                            (( t += 1 ))
                        done
                    done
                else
                    # bishop or queen
                    if (( fig != 4 )) ; then
                        for (( i=-8 ; i<=8 ; i++ )) ; do
                            if (( i != 0 )) ; then
                                # can be done nicer but avoiding two loops!
                                targetY[$t]=$(( fromY + i ))
                                targetX[$t]=$(( fromX + i ))
                                (( t += 1 ))
                                targetY[$t]=$(( fromY - i ))
                                targetX[$t]=$(( fromX - i ))
                                (( t += 1 ))
                                targetY[$t]=$(( fromY + i ))
                                targetX[$t]=$(( fromX - i ))
                                (( t += 1 ))
                                targetY[$t]=$(( fromY - i ))
                                targetX[$t]=$(( fromX + i ))
                                (( t += 1 ))
                            fi
                        done
                    fi
                    # rock or queen
                    if (( fig != 3 )) ; then
                        for (( i=-8 ; i<=8 ; i++ )) ; do
                            if (( i != 0 )) ; then
                                targetY[$t]=$(( fromY + i ))
                                targetX[$t]=$(( fromX ))
                                (( t += 1 ))
                                targetY[$t]=$(( fromY - i ))
                                targetX[$t]=$(( fromX ))
                                (( t += 1 ))
                                targetY[$t]=$(( fromY ))
                                targetX[$t]=$(( fromX + i ))
                                (( t += 1 ))
                                targetY[$t]=$(( fromY ))
                                targetX[$t]=$(( fromX - i ))
                                (( t += 1 ))
                            fi
                        done
                    fi
                fi
                # process all available moves
                for (( j=0; j < t; j++ )) ; do
                    local toY=${targetY[$j]}
                    local toX=${targetX[$j]}
                    # move is valid
                    if (( toY >= 0 && toY < 8 && toX >= 0 && toX < 8 )) &&  canMove "$fromY" "$fromX" "$toY" "$toX" "$player" ; then
                        local oldFrom=${field[$fromY,$fromX]};
                        local oldTo=${field[$toY,$toX]};
                        field[$fromY,$fromX]=0
                        field[$toY,$toX]=$oldFrom
                        # pawn to queen
                        if (( oldFrom == player && toY == ( player > 0 ? 7 : 0 ) )) ;then
                            field["$toY,$toX"]=$(( 5 * player ))
                        fi
                        # recursion
                        negamax $(( depth - 1 )) $(( 255 - b )) $(( 255 - a )) $(( player * (-1) )) false
                        local val=$(( 255 - $? ))
                        field[$fromY,$fromX]=$oldFrom
                        field[$toY,$toX]=$oldTo
                        if (( val > bestVal )) ; then
                            bestVal=$val
                            if $save ; then
                                selectedX=$fromX
                                selectedY=$fromY
                                selectedNewX=$toX
                                selectedNewY=$toY
                            fi
                        fi
                        if (( val > a )) ; then
                            a=$val
                        fi
                        if (( a >= b )) ; then
                            break 3
                        fi
                    fi
                done
            done
        done
        cacheLookup[$hash]=$bestVal
        cacheDepth[$hash]=$depth
        if (( bestVal <= aSave )) ; then
            cacheFlag[$hash]=1
        elif (( bestVal >= b )) ; then
            cacheFlag[$hash]=-1
        else
            cacheFlag[$hash]=0
        fi
        return $bestVal
    fi
}

# Perform a concrete single movement
# Params:
#   $1  current player
# Globals:
#   $selectedY
#   $selectedX
#   $selectedNewY
#   $selectedNewX
# Return status code 0 if movement was successfully performed
function move() {
    local player=$1
    if canMove "$selectedY" "$selectedX" "$selectedNewY" "$selectedNewX" "$player" ; then
        local fig=${field[$selectedY,$selectedX]}
        field[$selectedY,$selectedX]=0
        field[$selectedNewY,$selectedNewX]=$fig
        # pawn to queen
        if (( fig == player && selectedNewY == ( player > 0 ? 7 : 0 ) )) ; then
            field[$selectedNewY,$selectedNewX]=$(( 5 * player ))
        fi
        return 0
    fi
    return 1
}

# Unicode helper function (for draw)
# Params:
#   $1  first hex unicode character number
#   $2  second hex unicode character number
#   $3  third hex unicode character number
#   $4  integer offset of third hex
# Outputs escape character
function unicode() {
    if ! $ascii ; then
        printf '\\x%s\\x%s\\x%x' "$1" "$2" "$(( 0x$3 + ( $4 ) ))"
    fi
}

# Ascii helper function (for draw)
# Params:
#   $1  decimal ascii character number
# Outputs escape character
function ascii() {
    echo -en "\x$1"
}

# Get ascii code number of character
# Params:
#   $1  ascii character
# Outputs decimal ascii character number
function ord() {
    LC_CTYPE=C printf '%d' "'$1"
}

# Audio and visual bell
# No params or return
function bell() {
    if (( lastBell != SECONDS )) ; then
        echo -en "\a\e[?5h"
        sleep 0.1
        echo -en "\e[?5l"
        lastBell=$SECONDS
    fi
}

# Draw one field (of the gameboard)
# Params:
#   $1  y coordinate
#   $2  x coordinate
#   $3  true if cursor should be moved to position
# Outputs formated field content
function drawField(){
    local y=$1
    local x=$2
    echo -en "\e[0m"
    # move coursor to absolute position
    if $3 ; then
        local yScr=$(( y + originY ))
        local xScr=$(( x * 2 + originX ))
        if $ascii && (( x >= 0 )) ; then
            local xScr=$(( x * 3 + originX ))
        fi
        echo -en "\e[${yScr};${xScr}H"
    fi
    # draw vertical labels
    if (( x==labelX && y >= 0 && y < 8)) ; then
        if $hoverInit && (( hoverY == y )) ; then
            if $color ; then
                echo -en "\e[3${colorHover}m"
            else
                echo -en "\e[4m"
            fi
        elif (( selectedY == y )) ; then
            if ! $color ; then
                echo -en "\e[2m"
            elif (( ${field[$selectedY,$selectedX]} < 0 )) ; then
                echo -en "\e[3${colorPlayerA}m"
            else
                echo -en "\e[3${colorPlayerB}m"
            fi
        fi
        # line number (alpha numeric)
        if $unicodelabels ; then
            echo -en "$(unicode e2 92 bd -$y) "
        else
            echo -en " \x$((48 - $y))"
        fi
        # clear format
    # draw horizontal labels
    elif (( x>=0 && y==labelY )) ; then
        if $hoverInit && (( hoverX == x )) ; then
            if $color ; then
                echo -en "\e[3${colorHover}m"
            else
                echo -en "\e[4m"
            fi
        elif (( selectedX == x )) ; then
            if ! $color ; then
                echo -en "\e[2m"
            elif (( ${field[$selectedY,$selectedX]} < 0 )) ; then
                echo -en "\e[3${colorPlayerA}m"
            else
                echo -en "\e[3${colorPlayerB}m"
            fi
        else
            echo -en "\e[0m"
        fi
        if $unicodelabels ; then
            echo -en "$(unicode e2 9e 80 $x )\e[0m "
        else
            if $ascii ; then
                echo -n " "
            fi
            echo -en "\x$((31 + $x))\e[0m "
        fi
    # draw field
    elif (( y >=0 && y < 8 && x >= 0 && x < 8 )) ; then
        local f=${field["$y,$x"]}
        local black=false
        if (( ( x + y ) % 2 == 0 )) ; then
            local black=true
        fi
        # black/white fields
        if $black ; then
            if $color ; then
                echo -en "\e[47;107m"
            else
                echo -en "\e[7m"
            fi
        else
            $color && echo -en "\e[40m"
        fi
        # background
        if $hoverInit && (( hoverX == x && hoverY == y )) ; then
            if ! $color ; then
                echo -en "\e[4m"
            elif $black ; then
                echo -en "\e[4${colorHover};10${colorHover}m"
            else
                echo -en "\e[4${colorHover}m"
            fi
        elif (( selectedX != -1 && selectedY != -1 )) ; then
            local selectedPlayer=$(( ${field[$selectedY,$selectedX]} > 0 ? 1 : -1 ))
            if (( selectedX == x && selectedY == y )) ; then
                if ! $color ; then
                    echo -en "\e[2m"
                elif $black ; then
                    echo -en "\e[47m"
                else
                    echo -en "\e[40;100m"
                fi
            elif $color && $colorHelper && canMove "$selectedY" "$selectedX" "$y" "$x" "$selectedPlayer" ; then
                if $black ; then
                    if (( selectedPlayer < 0 )) ; then
                        echo -en "\e[4${colorPlayerA};10${colorPlayerA}m"
                    else
                        echo -en "\e[4${colorPlayerB};10${colorPlayerB}m"
                    fi
                else
                    if (( selectedPlayer < 0 )) ; then
                        echo -en "\e[4${colorPlayerA}m"
                    else
                        echo -en "\e[4${colorPlayerB}m"
                    fi
                fi
            fi
        fi
        # empty field?
        if ! $ascii && (( f == 0 )) ; then
            echo -en "  "
        else
            # figure colors
            if $color ; then
                if (( selectedX == x && selectedY == y )) ; then
                    if (( f < 0 )) ; then
                        echo -en "\e[3${colorPlayerA}m"
                    else
                        echo -en "\e[3${colorPlayerB}m"
                    fi
                else
                    if (( f < 0 )) ; then
                        echo -en "\e[3${colorPlayerA};9${colorPlayerA}m"
                    else
                        echo -en "\e[3${colorPlayerB};9${colorPlayerB}m"
                    fi
                fi
            fi
            # unicode figures
            if $ascii ; then
                echo -en " \e[1m${asciiNames[ $f + 6 ]} "
            elif (( f > 0 )) ; then
                if $color && $colorFill ; then
                    echo -en "$( unicode e2 99 a0 -$f ) "
                else
                    echo -en "$( unicode e2 99 9a -$f ) "
                fi
            else
                echo -en "$( unicode e2 99 a0 $f ) "
            fi
        fi
    # three empty chars
    elif $ascii && (( x >= 0 )) ; then
        echo -n "   "
    # otherwise: two empty chars (on unicode boards)
    else
        echo -n "  "
    fi
    # clear format
    echo -en "\e[0m\e[8m"
}

# Draw the battlefield
# (no params / return value)
function draw() {
    local ty
    local tx
    $useStty && stty -echo
    $cursor || echo -e "\e[2J"
    echo -e "\e[H\e[?25l\e[0m\n\e[K$title\e[0m\n\e[K"
    for (( ty=0; ty<10; ty++ )) ; do
        for (( tx=-2; tx<8; tx++ )) ; do
            if $cursor ; then
                local t
                t="$(drawField "$ty" "$tx" true)"
                if [[ "${redraw[$ty,$tx]}" != "$t" ]]; then
                    echo -n "$t"
                    redraw[$ty,$tx]="$t"
                    log="[$ty,$tx]"
                fi
            else
                drawField "$ty" "$tx" false
            fi
        done
        $cursor || echo ""
    done
    $useStty && stty echo
    # clear format
    echo -en "\e[0m\e[$(( originY + 10 ));0H\e[2K\n\e[2K$message\e[8m"
}

# Read the next move coordinates
# from keyboard (direct access or cursor keypad)
# or use mouse input (if available)
# Returns 0 on success and 1 on abort
function inputCoord(){
    inputY=-1
    inputX=-1
    local ret=0
    local t
    local tx
    local ty
    local oldHoverX=$hoverX
    local oldHoverY=$hoverY
    IFS=''
    $useStty && stty echo
    if $mouse ; then
        echo -en "\e[?9h"
    fi
    while (( inputY < 0 || inputY >= 8 || inputX < 0  || inputX >= 8 )) ; do
        read -sN1 a
        case "$a" in
            $'\e' )
                if read -t0.1 -sN2 b ; then
                    case "$b" in
                        '[A' | 'OA' )
                            hoverInit=true
                            if (( --hoverY < 0 )) ; then
                                hoverY=0
                                bell
                            fi
                            ;;
                        '[B' | 'OB' )
                            hoverInit=true
                            if (( ++hoverY > 7 )) ; then
                                hoverY=7
                                bell
                            fi
                            ;;
                        '[C' | 'OC' )
                            hoverInit=true
                            if (( ++hoverX > 7 )) ; then
                                hoverX=7
                                bell
                            fi
                            ;;
                        '[D' | 'OD' )
                            hoverInit=true
                            if (( --hoverX < 0 )) ; then
                                hoverX=0
                                bell
                            fi
                            ;;
                        '[3' )
                            ret=1
                            bell
                            break
                            ;;
                        '[5' )
                            hoverInit=true
                            if (( hoverY == 0 )) ; then
                                bell
                            else
                                hoverY=0
                            fi
                            ;;
                        '[6' )
                            hoverInit=true
                            if (( hoverY == 7 )) ; then
                                bell
                            else
                                hoverY=7
                            fi
                            ;;
                        'OH' )
                            hoverInit=true
                            if (( hoverX == 0 )) ; then
                                bell
                            else
                                hoverX=0
                            fi
                            ;;
                        'OF' )
                            hoverInit=true
                            if (( hoverX == 7 )) ; then
                                bell
                            else
                                hoverX=7
                            fi
                            ;;
                        '[M' )
                            read -sN1 t
                            read -sN1 tx
                            read -sN1 ty
                            ty=$(( $(ord "$ty") - 32 - originY ))
                            if $ascii ; then
                                tx=$(( ( $(ord "$tx") - 32 - originX) / 3 ))
                            else
                                tx=$(( ( $(ord "$tx") - 32 - originX) / 2 ))
                            fi
                            if (( tx >= 0 && tx < 8 && ty >= 0 && ty < 8 )) ; then
                                inputY=$ty
                                inputX=$tx
                                hoverY=$ty
                                hoverX=$tx
                            else
                                ret=1
                                bell
                                break
                            fi
                            ;;
                        * )
                            bell
                    esac
                else
                    ret=1
                    bell
                    break
                fi
                ;;
            $'\t' | $'\n' | ' ' )
                if $hoverInit ; then
                    inputY=$hoverY
                    inputX=$hoverX
                fi
                ;;
            '~' )
                ;;
            $'\x7f' | $'\b' )
                ret=1
                bell
                break
                ;;
            [A-Ha-h] )
                t=$(ord $a)
                if (( t < 90 )) ; then
                    inputY=$(( 72 - $(ord $a) ))
                else
                    inputY=$(( 104 - $(ord $a) ))
                fi
                hoverY=$inputY
                ;;
            [1-8] )
                inputX=$(( a - 1 ))
                hoverX=$inputX
                ;;
            * )
                bell
                ;;
        esac
        if $hoverInit && (( oldHoverX != hoverX || oldHoverY != hoverY )) ; then
            oldHoverX=$hoverX
            oldHoverY=$hoverY
            draw
        fi
    done
    if $mouse ; then
        echo -en "\e[?9l"
    fi
    $useStty && stty -echo
    return $ret
}

# Player input
# (reads a valid user movement)
# Params
#   $1  current (user) player
# Returns status code 0
function input() {
    local player=$1
    SECONDS=0
    message="\e[1m$(namePlayer "$player")\e[0m: Move your figure"
    while true ; do
        selectedY=-1
        selectedX=-1
        title="It's $(namePlayer "$player")s turn"
        draw >&3
        if inputCoord ; then
            selectedY=$inputY
            selectedX=$inputX
            if (( ${field["$selectedY,$selectedX"]} == 0 )) ; then
                warn "You cannot choose an empty field!" >&3
            elif (( ${field["$selectedY,$selectedX"]} * player  < 0 )) ; then
                warn "You cannot choose your enemies figures!" >&3
            else
                send "$player" "$selectedY" "$selectedX"
                local figName=$(nameFigure ${field[$selectedY,$selectedX]} )
                message="\e[1m$(namePlayer "$player")\e[0m: Move your \e[3m$figName\e[0m at $(coord "$selectedY" "$selectedX") to"
                draw >&3
                if inputCoord ; then
                    selectedNewY=$inputY
                    selectedNewX=$inputX
                    if (( selectedNewY == selectedY && selectedNewX == selectedX )) ; then
                        warn "You didn't move..." >&3
                    elif (( ${field[$selectedNewY,$selectedNewX]} * $player > 0 )) ; then
                        warn "You cannot kill your own figures!" >&3
                    elif move "$player" ; then
                        title="$(namePlayer "$player") moved the \e[3m$figName\e[0m from $(coord "$selectedY" "$selectedX") to $(coord "$selectedNewY" "$selectedNewX") \e[2m(took him $SECONDS seconds)\e[0m"
                    send "$player" "$selectedNewY" "$selectedNewX"
                        return 0
                    else
                        warn "This move is not allowed!" >&3
                    fi
                    # Same position again --> revoke
                    send "$player" "$selectedY" "$selectedX"
                fi
            fi
        fi
    done
}

# AI interaction
# (calculating movement)
# Params
#   $1  current (ai) player
# Verbose movement messages to stdout
function ai() {
    local player=$1
    local val
    SECONDS=0
    title="It's $(namePlayer "$player")s turn"
    message="Computer player \e[1m$(namePlayer "$player")\e[0m is thinking..."
    draw >&3
    negamax "$strength" 0 255 "$player" true
    val=$?
    local figName
    figName=$(nameFigure ${field[$selectedY,$selectedX]} )
    message="\e[1m$( namePlayer "$player" )\e[0m moves the \e[3m$figName\e[0m at $(coord "$selectedY" "$selectedX")..."
    draw >&3
    send "$player" "$selectedY" "$selectedX"
    sleep "$sleep"
    if move $player ; then
        message="\e[1m$( namePlayer "$player" )\e[0m moves the \e[3m$figName\e[0m at $(coord "$selectedY" "$selectedX") to $(coord "$selectedNewY" "$selectedNewX")"
        draw >&3
        send "$player" "$selectedNewY" "$selectedNewX"
        sleep "$sleep"
        title="$( namePlayer "$player" ) moved the $figName from $(coord "$selectedY" "$selectedX") to $(coord "$selectedNewY" "$selectedNewX" ) (took him $SECONDS seconds)."
    else
        error "AI produced invalid move - that should not hapen!"
    fi
}

# Read row from remote
# Returns row (0-7) as status code
function receiveY() {
    local i
    while true; do
        read -n 1 i
        case $i in
            [hH] ) return 0 ;;
            [gG] ) return 1 ;;
            [fF] ) return 2 ;;
            [eE] ) return 3 ;;
            [dD] ) return 4 ;;
            [cC] ) return 5 ;;
            [bB] ) return 6 ;;
            [aA] ) return 7 ;;
            * )
                if $warnings ; then
                    warn "Invalid input '$i' for row from network (character between 'A' and 'H' required)!"
                fi
        esac
    done
}

# Read column from remote
# Returns column (0-7) as status code
function receiveX() {
    local i
    while true; do
        read -n 1 i
        case $i in
            [1-8] ) return $(( i - 1 )) ;;
            * )
                if $warnings ; then
                    warn "Invalid input '$i' for column from network (character between '1' and '8' required)!"
                fi
        esac
    done
}

# receive movement from connected player
# (no params/return value)
function receive() {
    local player=$remote
    SECONDS=0
    title="It's $(namePlayer "$player")s turn"
    message="Network player \e[1m$(namePlayer "$player")\e[0m is thinking... (or sleeping?)"
    draw >&3
    while true ; do
        receiveY
        selectedY=$?
        receiveX
        selectedX=$?
        local figName
        figName=$(nameFigure ${field[$selectedY,$selectedX]} )
        message"\e[1m$( namePlayer "$player" )\e[0m moves the \e[3m$figName\e[0m at $(coord $selectedY $selectedX)..."
        draw >&3
        receiveY
        selectedNewY=$?
        receiveX
        selectedNewX=$?
        if (( selectedNewY == selectedY && selectedNewX == selectedX )) ; then
            selectedY=-1
            selectedX=-1
            selectedNewY=-1
            selectedNewX=-1
            message="\e[1m$( namePlayer "$player" )\e[0m revoked his move... okay, that'll be time consuming"
            draw >&3
        else
            break
        fi
    done
    if move $player ; then
        message="\e[1m$( namePlayer "$player" )\e[0m moves the \e[3m$figName\e[0m at $(coord $selectedY $selectedX) to $(coord $selectedNewY $selectedNewX)"
        draw >&3
        sleep "$sleep"
        title="$( namePlayer $player ) moved the $figName from $(coord $selectedY $selectedX) to $(coord $selectedNewY $selectedNewX) (took him $SECONDS seconds)."
    else
        error "Received invalid move from network - that should not hapen!"
    fi
}

# Write coordinates to network
# Params:
#   $1  player
#   $2  row
#   $3  column
# (no return value/exit code)
function send() {
    local player=$1
    local y=$2
    local x=$3
    if (( remote == player * (-1) )) ; then
        sleep "$remotedelay"
        coord "$y" "$x"
        echo
        sleep "$remotedelay"
    fi
}

# Import transposition tables
# by reading serialised cache from stdin
# (no params / return value)
function importCache() {
    while IFS=$'\t' read hash lookup depth flag ; do
        cacheLookup["$hash"]=$lookup
        cacheDepth["$hash"]=$depth
        cacheFlag["$hash"]=$flag
    done
}

# Export transposition tables
# Outputs serialised cache (to stdout)
# (no params / return value)
function exportCache() {
    for hash in "${!cacheLookup[@]}" ; do
        echo -e "$hash\t${cacheLookup[$hash]}\t${cacheDepth[$hash]}\t${cacheFlag[$hash]}"
    done
}

# Trap function for exporting cache
# (no params / return value)
function exitCache() {
    # permanent cache: export
    if [[ -n "$cache" ]] ; then
        echo -en "\r\n\e[2mExporting cache..." >&3
        if $cachecompress ; then
            exportCache | gzip > "$cache"
        else
            exportCache > "$cache"
        fi
        echo -e " done!\e[0m" >&3
    fi
}

# Perform necessary tasks for exit
# like deleting files and measuring runtime
# (no params / return value)
function end() {
    # remove pipe
    if [[ -n "$fifopipe" && -p "$fifopipe" ]] ; then
        rm "$fifopipe"
    fi
    # disable mouse
    if $mouse ; then
        echo -en "\e[?9l"
    fi
    # enable input
    stty echo
    # restore screen
    if $cursor ; then
        echo -en "\e[2J\e[?47l\e[?25h\e[u\e8"
    fi
    # exit message
    duration=$(( $( date +%s%N ) - timestamp ))
    seconds=$(( duration / 1000000000 ))
    echo -e "\r\n\e[2mYou've wasted $seconds,$(( duration -( seconds * 1000000000 ))) seconds of your lifetime playing with a Bash script.\e[0m\n"
}

# Exit trap
trap "end" 0

# setting up requirements for network
piper="cat"
fifopipe="/dev/fd/1"
initializedGameLoop=true
if (( remote != 0 )) ; then
    require nc
    require mknod
    initializedGameLoop=false
    if (( remote == 1 )) ; then
        fifopipe="$fifopipeprefix.server"
        piper="nc -l $port"
    else
        fifopipe="$fifopipeprefix.client"
        piper="nc $remoteip $port"
        echo -e "\e[1mWait!\e[0mPlease make sure the Host (the other Player) has started before continuing.\e[0m"
        anyKey
    fi
    if [[ ! -e "$fifopipe" ]] ; then
        mkfifo "$fifopipe"
    fi
    if [[ ! -p "$fifopipe" ]] ; then
        echo "Could not create FIFO pipe '$fifopipe'!" >&2
    fi
fi

# print welcome title
title="Welcome to ChessBa.sh"
if isAI "1" || isAI "-1" ; then
    title="$title - your room heater tool!"
fi

# permanent cache: import
if [[ -n "$cache" && -f "$cache" ]] ; then
    echo -en "\n\n\e[2mImporting cache..."
    if $cachecompress ; then
        importCache < <( zcat "$cache" )
    else
        importCache < "$cache"
    fi
    echo -e " done\e[0m"
fi

# main game loop
{
    p=1
    while true ; do
        # initialize remote connection on first run
        if ! $initializedGameLoop ; then
            # set cache export trap
            trap "exitCache" 0
            warn "Waiting for the other network player to be ready..." >&3
            # exchange names
            if (( remote == -1 )) ; then
                read namePlayerA < $fifopipe
                echo "$namePlayerB"
                echo "connected with first player." >&3
            elif (( remote == 1 )) ; then
                echo "$namePlayerA"
                read namePlayerB < $fifopipe
                echo "connected with second player." >&3
            fi
            # set this loop initialized
            initializedGameLoop=true
        fi
        # reset global variables
        selectedY=-1
        selectedX=-1
        selectedNewY=-1
        selectedNewX=-1
        # switch current player
        (( p *= (-1) ))
        # check check (or: if the king is lost)
        if hasKing "$p" ; then
            if (( remote == p )) ; then
                receive < $fifopipe
            elif isAI "$p" ; then
                if (( computer-- == 0 )) ; then
                    echo "Stopping - performed all ai steps" >&3
                    exit 0
                fi
                ai "$p"
            else
                input "$p"
            fi
        else
            title="Game Over!"
            message="\e[1m$(namePlayer $(( p * (-1) )) ) wins the game!\e[1m\n"
            draw >&3
            anyKey
            exit 0
        fi
    done | $piper > "$fifopipe"

    # check exit code
    netcatExit=$?
    gameLoopExit=${PIPESTATUS[0]}
    if (( netcatExit != 0 )) ; then
        error "Network failure!"
    elif (( gameLoopExit != 0 )) ; then
        error "The game ended unexpected!"
    fi
} 3>&1