[转贴] tictactoe bash script

[转贴] tictactoe bash script

as below , from comp.unix.shell

#!/bin/bash
# Sat Jul 12 16:52:30 EDT 2003
# NAME: ttt
# Copyright 2003, Chris F.A. Johnson
# Released under the terms of the GNU General Public License

ESC=$'\e'

tictactoe() {
[ $verbose -ge 1 ] && echo tictactoe $* >&2
    board=`printf "%9.9s" "        "`
    mvnum=1
    remainder=" 1 2 3 4 5 6 7 8 9 "
    show "$board"
    while :
    do
      status
      eval \$player$P
      [ $? -ne 0 ] && return
      put $_MOVE "$board"
      board=$_PUT
#      show "$board"
      mkbold "$_MOVE"
      printat 0 0 $mvnum
      printat $prompt
      sleep $pause &
      mlist="$mlist $_MOVE"
      remainder=${remainder/ $_MOVE / }

      checkboard "$board" && {
          mkbold $win
          printat $prompt "$cle"
          eval winner \$player$P
          return
      }

      [ $mvnum -eq 9 ] && {
          B=$U mkbold "$_MOVE"
          winner "Neither player"
          return
      }
      wait
      B=$U mkbold "$_MOVE"
      if [ $P = O ]
      then
          P=X
          OP=O
      else
          P=O
          OP=X
      fi
      mvnum=$(( $mvnum + 1 ))
    done
}

winner() {
[ $verbose -ge 1 ] && echo winner $* >&2
     printat $prompt "$cle $* wins\n\n"
}

status() {
[ $verbose -ge 1 ] && echo status $* >&2
    if [ $verbose -ge 1 ]
    then
        printat 4 0
        printf "${cle}Move: %s Player: %s Board: \"%s\"  Last move: %s  Move list: %s" \
            "$mvnum" "$P" "${board// /-}" "$_MOVE" "$mlist"
    else
        false
    fi
}

show() {
[ $verbose -ge 1 ] && echo show $* >&2
    printat 0 0 $mvnum
    printat 0 $top  ##$(( ($LINES - 5) / 2 ))
    printf "  %${margin}.${margin}s | %1s | %1s\n" "${1:0:1}" "${1:1:1}" "${1:2:1}"
    printf "%${margin}.${margin}s---+---+---\n" " "
    printf "  %${margin}.${margin}s | %1s | %1s\n" "${1:3:1}" "${1:4:1}" "${1:5:1}"
    printf "%${margin}.${margin}s---+---+---\n" " "
    printf "  %${margin}.${margin}s | %1s | %1s\n" "${1:6:1}" "${1:7:1}" "${1:8:1}"
}

put() {
[ $verbose -ge 1 ] && echo put $1 \""$2"\" >&2
   local n=$(( $1 - 1 ))
   _PUT=${2:-$board}
   [ "${_PUT:$n:1}" = " " ] &&
            _PUT=${_PUT:0:$n}$P${_PUT:$1}
}

checkboard() {
[ $verbose -ge 1 ] && echo checkboard \"$*\" >&2
    case $1 in
        $P??$P??$P??) win="1 4 7" ;;
        ?$P??$P??$P?) win="2 5 8" ;;
        ??$P??$P??$P) win="3 6 9" ;;
        ??$P?$P?$P??) win="3 5 7" ;;
        $P???$P???$P) win="1 5 9" ;;
        $P$P$P??????) win="1 2 3" ;;
        ???$P$P$P???) win="4 5 6" ;;
        ??????$P$P$P) win="7 8 9" ;;
        *) false ;;
    esac
}

mkbold() {
[ $verbose -ge 1 ] && echo mkbold $* >&2
  for sq in $*
  do
    row=$(( ($sq - 1) / 3 ))
    col=$(( ($sq - 1) % 3 + 1 ))
    printat $(( ($col * 4) + $margin - 2 )) $(( ($row * 2) + $top ))
    printf "${B}$P${U}"
  done
}

human() {
[ $verbose -ge 1 ] && echo human $* >&2
   while :
   do
       key "Select ($remainder)"
#       case $_MOVE in
#          "$ESC") read -sn1; read -sn1 kp; continue ;;
#       esac
       _MOVE=$_KEY
       case $_MOVE in
           1|2|3|4|5|6|7|8|9) [ "${board:_MOVE-1:1}" = " " ] && break ;;
           q|Q|x|X) echo; return 5 ;;
           p) printat $prompt
              KEYECHO= KEYMAX=$(( $COLUMNS - 10 )) key "Enter delay in seconds"
              pause=$_KEY
              ;;
           "") set -- $remainder
               _MOVE=$1
               [ $verbose -ge 1 ] && echo "|$_MOVE|" >&2
               break ;;
           *) printat $prompt
              printf "\r%s%s\r" "  "$cle" $_MOVE: Invalid move"
              sleep 1
              printf "%s\r" "$cle" ;;
       esac
   done
}

randy() {
[ $verbose -ge 1 ] && echo randy $* >&2
    randstr $remainder
    _MOVE=$_RANDSTR
}

key() {
[ $verbose -ge 1 ] && echo key $* >&2
    printat $prompt "$cle$CVIS$beep"
    read -${KEYECHO}n$KEYMAX -p "${1:-PAK}: " _KEY 2>&1
    printf "$CINV"
}

getkey() {
    local OKchars=${1:-${remainder// /}}
    local kp2 kp3
    stty -echo
    while :
      do
      prompt="==>"
      prompt
      IFS= read -r -sn1 -p " " kp 2>&1 || exit 2 #cleanup
      case $OKchars in
          *"$kp"*)
              case $kp in
                  $ESC)
                      read -st1 -n1 kp2
                      case $kp2 in
                          \[) read -st1 -n1 kp3
                              case $kp3 in
                                  A) kp=$UP; break ;;
                                  B) kp=$DN; break ;;
                                  C) kp=$RT; break ;;
                                  D) kp=$LF; break ;;
                              esac
                              ;;
                       esac
                       ;;
                  *) break ;;
              esac
              ;;
       esac
    done
    _KEY=$kp
}

randstr() {
[ $verbose -ge 1 ] && echo randstr $* >&2
#    [ -n "$1" ] || return 1
    n=$(( ($RANDOM % $#) + 1 ))
    eval _RANDSTR=\${$n}
}

isblock() {
[ $verbose -ge 1 ] && echo isblock $* >&2
    win=
    block=
    for _MOVE in $remainder
    do
        P=$OP put $_MOVE
        P=$OP checkboard "$_PUT" && return
    done
    _MOVE=
}

block() {
[ $verbose -ge 1 ] && echo block $* >&2
    block=
    for _MOVE in $remainder
    do
        put $_MOVE
        checkboard "$_PUT" && return
        P=$OP put $_MOVE
        P=$OP checkboard "$_PUT" && block=$_MOVE
    done
    [ "$block" ] && { _MOVE=$block; return; }
    randy
}

computer() {
[ $verbose -ge 1 ] && echo computer $* >&2
   local r c
   case $mvnum in
       1) randstr 1 3 7 9
          _MOVE=$_RANDSTR ;;
       2)
           if [ "${board:4:1}" = " " ]
           then
               _MOVE=5
           else
               randstr 1 3 7 9
               _MOVE=$_RANDSTR
           fi
           ;;
       3)  r=${mlist// /}
           c='1 3 7 9'
           randstr ${c//[$r]/}
#           randstr ${remainder//[$r]/}
           _MOVE=$_RANDSTR
           ;;
       4)
           isblock
           [ "$_MOVE" ] && return
            r=${mlist// /}
            c='2 4 6 8'
            randstr ${remainder//[$r]/}
            _MOVE=$_RANDSTR
            ;;
       *) block ;;
   esac
}

printat() { #== print arguments 3-... at Y=$2 X=$1
[ $verbose -ge 1 ] && echo printat $* >&2
    [ $# -lt 2 ] && return 1
    local y=$2
    local x=$1
    shift 2
    msg="$*"
    printf "\e[%d;%dH%b" $y $x "$msg"
}

version()
{
    echo "    $progname, version $version
    Copyright $copyright, $author $email
    This is free software, released under the terms of the GNU General
    Public License.  There is NO warranty; not even for MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.
"
}

usage()
{
echo "
    ${B}NAME${U}:  $progname - Tic-Tac-Toe

    ${B}USAGE${U}: $progname [OPTIONS]

    ${B}OPTIONS${U}:

        -x player1         - specify first player
        -o player2         - specify second player
        -c player1,player2 - specify both players
        -p seconds         - time to pause between moves

        -h, --help       - help: print this message
        -H, --help_long  - help: print more detailed message (if available)
        -v, --verbose    - sends messsages to \$HOME/tttlog
        -V, --version    - print version information

    Copyright 2003, Chris F.A. Johnson
"
}

verbose=0
longusage=0
version="1.0"
copyright=2003
author="Chris F.A. Johnson"
progname=${0##*/}
P=X
OP=O

playerX=human
playerO=computer
co=
li=

if tput ce >/dev/null 2>&1
then ## e.g. FreeBSD
    co=co
    li=li
elif tput el 2>/dev/null
then ## e.g. Linux
    co=cols
    li=lines
fi

UNBOLD=$'\E[0m'
standout=$'\E[0;1;7m'
cle=$'\E[K'
clb=$'\E[1K'
ULINE=$'\E[0;4m'
REVERSE=$'\E[0;7m'
BLINK=$'\E[0;5m'
BOLD=$'\E[0;1m'
CINV=$'\E[0;8m'

R=$REVERSE
U=$UNBOLD
B=$BOLD
BR=$B$R
Cl=$'\f'
if [ "$co" ]
then
    tput reset
    COLUMNS=${COLUMNS:=`tput $co`}
else
    COLUMNS=${COLUMNS:-80}
fi
top=6
margin=11
prompt="3 $(( $top + 9 ))"
beep=  #$'\a'
pause=1
KEYMAX=1
KEYECHO=s

while getopts vVhH-oX:O:c:p: var
do
case "$var" in

     x) playerX=human
        playerO-computer
        ;;
     o) playerO=human
        playerX=computer
        ;;
     X) playerX=$OPTARG ;;
     O) playerO=$OPTARG ;;
     c|C) playerO=${OPTARG#*,}
          playerX=${OPTARG%,*}
          ;;
     p) pause=$OPTARG ;;

    -) case $OPTARG in
         help) usage; exit ;;
         verbose) verbose=$(( $verbose + 1 )) ;;
         version) version; exit ;;
       esac
       ;;
    h) usage; exit ;;
    H) longusage=1; usage; exit ;;
    v) verbose=$(( $verbose + 1 )) ;;
    V) version; exit ;;
    *);;
esac
done
shift $(( $OPTIND - 1 ))

if [ "$co" ]
then
    COLUMNS=`tput $co`
else
    COLUMNS=${COLUMNS:-80}
fi
margin=11  ##$(( ($COLUMNS - 11) / 2 ))
clear

if [ $verbose -ge 1 ]
then
    exec 2>$HOME/tttlog
    set -x
fi

tictactoe
printat $prompt "\n$CVIS\n"
journalist
已阅