#!/usr/bin/env ksh # '_' : unclaimed # 'O' : player # 'X' : AI set -A board "_ _ _" "_ _ _" "_ _ _" # manual function instructions { echo "type position to claim (x:y)" echo "type 'q' to quit" } # if check founds a connected line, this determines who won function get_outcome { [ $1 = "X" ] && echo "lost" || echo "won" } function check { # rows for row in "${board[@]}"; do if [[ $row = "O O O" || $row = "X X X" ]]; then echo $(get_outcome $(echo row | awk '{print $1}')) return fi done # columns local r1 local r2 local r3 set -A r1 ${board[0]} set -A r2 ${board[1]} set -A r3 ${board[2]} for i in `seq 0 2`; do local column="${r1[$i]} ${r3[$i]} ${r2[$i]}" if [[ $column = "O O O" || $column = "X X X" ]]; then echo $(get_outcome ${r1[$i]}) return fi done # diagonals local d1="${r1[0]} ${r2[1]} ${r3[2]}" local d2="${r3[0]} ${r2[1]} ${r1[2]}" if [[ $d1 = "O O O" || $d2 = "O O O" || $d1 = "X X X" || $d2 = "X X X" ]] then echo $(get_outcome ${r2[1]}) return fi # tie if [[ $(echo ${board[@]} | grep '_') = '' ]]; then echo "tie" return fi echo "nothing" } function player_turn { read pos?'> ' # quit [ "$pos" = 'q' ] && exit 0 # validate input [[ "$pos" = [012]:[012] ]] || { player_turn; return; } local x=$(echo $pos | awk -F ':' '{print $1}') local y=$(echo $pos | awk -F ':' '{print $2}') # claim if unclaimed local row set -A row ${board[$y]} if [ ${row[$x]} = '_' ]; then row[$x]='O' board[$y]=${row[@]} else player_turn fi } # determines how beneficial would some play be for AI function ai_try { # x y local outcome local row set -A row ${board[$2]} # check if it would win the game row[$1]='X' board[$2]=${row[@]} outcome=$(check) # if not, check if it would win for the player if [ $outcome != 'lost' ]; then row[$1]='O' board[$2]=${row[@]} outcome=$(check) fi # change back state row[$1]='_' board[$2]=${row[@]} echo $outcome } function ai_turn { # possible spaces local possible # needs local to be local, but that somehow makes it persistent # unset clears it unset possible set -A possible # these would prevent player from winning local block unset block set -A block local to_play # gather all possible plays for y in `seq 0 2`; do local row set -A row ${board[$y]} for x in `seq 0 2`; do if [[ ${row[$x]} = "_" ]]; then case $(ai_try $x $y) in # if it would win, play it "lost") to_play="$x $y" ;; # this is how you append I guess "won") block[${#block[@]}]="$x $y" ;; *) possible[${#possible[@]}]="$x $y" ;; esac fi done done # choose which one to play if [[ -z $to_play ]]; then # prefer blocks if [[ ${#block[@]} != 0 ]]; then to_play="${block[$(( RANDOM % ${#block[@]} ))]}" else to_play="${possible[$(( RANDOM % ${#possible[@]} ))]}" fi fi # play local x=$(echo $to_play | awk '{print $1}') local y=$(echo $to_play | awk '{print $2}') local row set -A row ${board[$y]} row[$x]="X" board[$y]=${row[@]} } function display { local i=0 echo "\n 0 1 2" for row in "${board[@]}"; do # needs to be in quotes echo " $i $row" ((i++)) done } function lost { display echo echo "###############" echo "## YOU LOST! ##" echo "###############" exit 0 } function won { display echo echo "##############" echo "## YOU WON! ##" echo "##############" exit 0 } function tie { display echo echo "#################" echo "## IT'S A TIE! ##" echo "#################" exit 0 } function play { instructions local turn="player" while true; do if [ $turn = "player" ]; then display player_turn turn="ai" else ai_turn turn="player" fi case "$(check)" in "lost") lost ;; "won") won ;; "tie") tie ;; esac done } play