#Author: Pieter van der Star (info@pietervanderstar.nl) #Modifications by: (unmodified) #This program is free software: you can redistribute it and/or modify #it under the terms of the GNU Affero General Public License as #published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. # #You should have received a copy of the GNU Affero General Public License #along with this program. If not, see <https://www.gnu.org/licenses/ # #Feel free to use and modify as long as: # - This License stays intact. # - You give the original author(s) credit for their work # This includes the author(s) of modifications. # - The modifications are indicated clearly in the changelog # - The result must be free, in both monetary sense and personal sense # i.e. no account required, no personal data needs to be handed over. # - The software is provided as-is. Feature requests or bug reports may be # ignored. # #Changelog: #┏━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━┓ #┃ 16 oct 2022 │ First release │ Pieter van der Star ┃ #┗━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━┛
#todo: x in center o in top right gives transpos error # so does x in center and o in bottom left a o in middle left # and mid right, top left vs bottom right for o are correct #TODO: add error codes #TODO: add game status flag so it can tell as soon as it knows the game is won/lost/tied and also when it is inevetable so it can tell the player ie. "you cannot win anymore" "you lost"
#This version of the game tic-tac-toe may not seem very good when you start out playing, but that is because it only knows the rules of the game, not the strategies involved. Neither does it calculate the best move by going through all the moves of the game. Well, that last part is not entirely true. It does kinda go through all the options. But it does this by playing. Each time you play the, program will learn if moves were good or bad. After a while it will stop using the bad ones and prefer the good ones, thus becoming a stronger player.
#The program has a couple of elements. #The most important elements are the boards. The boards contain all valid states of the tic-tac-toe square. The mirrorings and rotations are ignored. This means that any board with an X in a corner and nothing else on the board is the same from the programs point of view. This means some mechanism is needed to help keep track of those transpositions. That is the job of the transposition elements. Together with some functions those elements make it possible to rotate and mirror the boards and put the user input on the correct square.
#The progress of the learning is saved in a boards.txt file [note 1]. This allows curious people to monitor the learning progress, or save the intermediate stages if it becomes too smart to beat.
#all variable names of the form b<integer> are reserved for boards #boards live in the global scope. #Since the current board needs to be passed around as well the variable name bCurrent is also reserved. As is the a temporary board bTemp.
#The board element is quite complex and is stored as an array. In C it #would look something like this: #struct board{ # char* name; # int[3][3] cells; # int board_value; # int next_boards_x; # int next_boards_o; # char** children; # int* children_weights; #}
#note 1: unless otherwise specified by a command line argument.
#print the introduction #args: none #returns nothing function printIntro { printf" ###############################################\n";sleep 0.3; printf "#################################################\n";sleep 0.3; printf"## ##\n";sleep 0.3; printf "## Tic Tac Toe or naughts and crosses ##\n";sleep 0.3; printf"## ##\n";sleep 0.3; printf "## Created by Pieter van der Star ##\n";sleep 0.3; printf"## Inspired by Donald Mitchies: ##\n";sleep 0.3; printf "## M E N A C E ##\n";sleep 0.3; printf"## ##\n";sleep 0.3; printf "## Matchbox ##\n";sleep 0.3; printf"## Educable ##\n";sleep 0.5; printf "## Naughts ##\n";sleep 0.5; printf"## And ##\n";sleep 0.3; printf "## Crosses ##\n";sleep 0.3; printf"## Engine ##\n";sleep 0.5; printf "## ##\n";sleep 0.3; printf" #################################################\n";sleep 0.3; printf "###############################################\n";sleep 0.3; printf" This game is released under the GNU AGPL 3.0 license\n";sleep 0.3; printf" https://www.gnu.org/licenses/agpl-3.0.html\n";sleep 0.3; printf" A link to Matt Parkers video on Menace\n";sleep 0.3; printf" which first introduced the idea to me:\n";sleep 0.3; printf" https://m.youtube.com/watch?v=R9c-_neaxeU\n";sleep 0.3; printf" I wrote most of the code based on that video\n";sleep 0.3; printf" But when it came to the weights (number of beads)\n";sleep 0.3; printf" I read the paper Experiments on the mechanization\n";sleep 0.3; printf" of game-learning by Donald Michie.\n";sleep 0.3;
sleep 2;
}
#A bunch of constants used for board propeties and game states
#calculates and sets the id of a given transposition #ars: transposition name #returns nothing #modifies transposition id function calculateTranspositionId { local -n t="$1" local -i i=0 local -a p p=( 11 13 17 19 23 29 31 37 41 ) local -i id=0; while [ $i -lt 9 ]; do pos=${t[i]} prime=${p[$i]} id=$((id+pos*prime)) i=$((i+1)) done t[9]=$id
}
#calculates and sets the id of all transpositio ns #ars: nothing #returns nothing #modifies transposition ids function calculateTranspositionIds { local -i h=0; while [ $h -le 1 ]; do local -i v=0; while [ $v -le 1 ]; do if [[ $h -eq 1 && $v -eq 1 ]]; then break; fi local -i r=0; while [ $r -le 3 ]; do
calculateTranspositionId "h$h""v$v""r$r"; r=$((r+1)) done v=$((v+1)) done h=$((h+1)) done
}
#transpose a transposition #args: transposition name, transposition name #returns nothing #sets global transposition function transposeTransposition { local -i verbosity=0; if [ $bTmp ]; then#this function needs bTmp, so backup the current value localbTempbu=$bTmp; fi local -n a="$1"; local -n b="$2"; bTmp=( 0 0 0 0 0 0 0 0 0 0 ); local -i i=0; while [ $i -lt 9 ]; do local -i pos=${b[i]} bTmp[i]=${a[pos]} i=$((i+1)) done
calculateTranspositionId "bTmp" #find the same id local -i h=0; while [ $h -le 1 ]; do local -i v=0; while [ $v -le 1 ]; do if [[ $h -eq 1 && $v -eq 1 ]]; then break; fi local -i r=0; while [ $r -le 3 ]; do
name="h${h}v${v}r${r}"; if [ $verbosity -gt 0 ]; then echo"chk $name" fi local -n t="$name"
if [ ${t[9]} -eq ${bTmp[9]} ]; then
transposition="$name"; return fi r=$((r+1)) done v=$((v+1)) done h=$((h+1)) done
transposition="h0v0r0" unset"bTmp" if [ $bTempbu ]; then bTmp=$bTempbu; fi
}
#prints the board #arg1: board name, #arg2: transposition to apply #arg3: bool, print properties (like list of children) #arg4: bool, print coordinates (should it print column anc row indicators) function printBoard { local -i verbosity=0; if [ ! -v $1 ]; then printf"Board %s does not exist\n" $1; return 0; fi local -n board="$1" if [ $verbosity -ge 2 ]; then echo"${board[@]}" fi if [ $# -gt 1 ]; then local -r transposition="$2" else local -r transposition="h0v0r0" fi
local -n matrix="$transposition"; printProperties=$C_FALSE; if [ $# -gt 2 ]; then printProperties=$3; fi printCoordinates=$C_FALSE if [ $# -gt 3 ]; then printCoordinates=$4; fi if [ $printProperties -eq $C_TRUE ]; then printf"name: %s\n" ${board[$C_NAME_OFFSET]}; printf"transposition: %s\n" $transposition printf"cells:\n"; fi if [ $printCoordinates -eq $C_TRUE ]; then printf" A B C\n"; fi printf" ┏━┯━┯━┓\n"; local -i r=0; local -i c; while [ $r -lt 3 ]; do c=0; if [ $printCoordinates -eq $C_TRUE ]; then printf"%d" $r; else printf" "; fi printf"┃"; while [ $c -lt 3 ]; do
local -i pos=${matrix[$(((r*3+c)))]}
val="${board[$((C_CELLS_OFFSET+pos))]}"; printf"%s""$val";
if [ $c -lt 2 ]; then printf"│"; fi
c=$((c+1)) done printf"┃"; if [ $r -lt 2 ]; then printf"\n ┠─┼─┼─┨\n"; fi r=$((r+1)) done printf"\n ┗━┷━┷━┛\n";
if [ $printProperties -eq $C_TRUE ]; then printf"value: %d\n" ${board[$((C_VALUE_OFFSET))]}; local -i v=0; while [ $v -lt 2 ]; do local -i nnext=${board[$((C_NEXT_OFFSET+v))]}; local -i firstChildOffset=${board[$C_NEXT_OFFSET]}; firstChildOffset=$((firstChildOffset*v+C_NEXT_OFFSET+2)); printf"number of next boards for %s: %d\n" ${xo[$v]} $nnext; printf"firstChildOffset: %d\n" $firstChildOffset; local -i i=0; while [ $i -lt $nnext ]; do
getNNext "$1" 0 local -i nnext_x=$?
getNNext "$1" 1 local -i nnext_o=$? printf"%s(%d),""${board[$((i+firstChildOffset))]}""${board[$((i+firstChildOffset+nnext_x+nnext_o))]}"; i=$((i+1)); done printf"\n"; v=$((v+1)); done fi
}
#makes a board #args: board name #todo check if already exists function makeBoard { local -n board=$1
board[$C_NAME_OFFSET]="$1" local -i r=0; local -i c=0; while [ $r -lt 3 ]; do c=0; while [ $c -lt 3 ]; do
board[$((C_CELLS_OFFSET+(r*3+c)))]=" "; c=$((c+1)) done r=$((r+1)) done board[$C_NEXT_OFFSET]=0; board[$C_NEXT_OFFSET+1]=0;
}
#helper function for is won #checks if one player has all boxes in the row function isWonRow { local -n board=$1; local -i r=$2; local -i c=0; local curVal; local prevVal; while [ $c -lt 3 ]; do
curVal="${board[$((C_CELLS_OFFSET+(3*r+c)))]}"; if [ "$curVal"==" " ]; then return $C_FALSE; else if [[ $c -gt 0 && "$prevVal" != "$curVal" ]]; then return $C_FALSE; fi; fi
prevVal="$curVal"; c=$((c+1)); done
sleep 5 return $C_TRUE;
}
#helper function for is won #checks if one player has all boxes in the column function isWonColumn { local -n board=$1; local -i r=0; local -i c=$2; local curVal; local prevVal; while [ $r -lt 3 ]; do
curVal="${board[$((C_CELLS_OFFSET+(3*r+c)))]}"; if [ "$curVal"==" " ]; then return $C_FALSE; else if [[ $r -gt 0 && "$prevVal" != "$curVal" ]]; then return $C_FALSE; fi; fi
prevVal="$curVal"; r=$((r+1)); done return $C_TRUE;
}
#helper function for is won #checks if one player has all boxes in the diagonal going right function isWonDiagonalR { local -n board=$1; local -i i=0; local curVal; local prevVal; while [ $i -lt 3 ]; do
curVal="${board[$((C_CELLS_OFFSET+(3*i+i)))]}"; if [ "$curVal"==" " ]; then return $C_FALSE; else if [[ $i -gt 0 && "$prevVal" != "$curVal" ]]; then return $C_FALSE; fi; fi
prevVal="$curVal"; i=$((i+1)); done return $C_TRUE;
}
#helper function for is won #checks if one player has all boxes in the diagonal going left function isWonDiagonalL { local -n board=$1; local -i i=0; local curVal; local prevVal; while [ $i -lt 3 ]; do
curVal="${board[$((C_CELLS_OFFSET+(3*(i+1)-i)-1))]}"; if [ "$curVal"==" " ]; then return $C_FALSE; else if [[ $i -gt 0 && "$prevVal" != "$curVal" ]]; then return $C_FALSE; fi; fi
prevVal="$curVal"; i=$((i+1)); done return $C_TRUE;
}
#checks if one player has won the game #args: board name #returns boolean true if a player won, otherwise false function isWon { local -i verbosity=0; localboardName=$1 local -i r=0; local prevVal; #check rows while [ $r -lt 3 ]; do
isWonRow $boardName $r if [ $? -eq $C_TRUE ]; then if [ $verbosity -gt 0 ]; then printf"Found winning board(r%d)\n" $r; fi return $C_TRUE; fi r=$((r+1)); done
#check columns local -i c=0; while [ $c -lt 3 ]; do
isWonColumn $boardName $c if [ $? -eq $C_TRUE ]; then if [ $verbosity -gt 0 ]; then printf"Found winning board (c%d)\n" $c; fi return $C_TRUE; fi c=$((c+1)); done
#check diagonals
isWonDiagonalL $boardName if [ $? -eq $C_TRUE ]; then if [ $verbosity -gt 0 ]; then printf"Found winning board (DiagL)\n"; fi return $C_TRUE; fi
isWonDiagonalR $boardName if [ $? -eq $C_TRUE ]; then if [ $verbosity -gt 0 ]; then printf"Found winning board (DiagR)\n"; fi return $C_TRUE; fi
return $C_FALSE;
}
#checks if the board is a valid board #the number of x and o should never differ more than 1 #args: board name #returns boolean, true if valid, otherwise false function isValid { local -n board=$1 local -i nX=0; local -i nO=0;
local -i r=0; local -i c=0; while [ $r -lt 3 ]; do c=0; while [ $c -lt 3 ]; do
val="${board[$((C_CELLS_OFFSET+(r*3+c)))]}" if [ "$val"=="X" ]; then nX=$((nX+1)); elif [ "$val"=="O" ]; then nO=$((nO+1)); fi c=$((c+1)) done r=$((r+1)) done local -i diff=$((nX-nO));
if [[ -2 -lt $diff && $diff -lt 2 ]]; then return $C_TRUE; else return $C_FALSE; fi
}
#function to get the number of empty cells #args: board name #returns numbere of empty cells function getNEmpty { local -n board=$1 local -i nEmpty=0;
local -i r=0; local -i c=0; while [ $r -lt 3 ]; do c=0; while [ $c -lt 3 ]; do
val="${board[$((C_CELLS_OFFSET+(r*3+c)))]}" if [ "$val"==" " ]; then nEmpty=$((nEmpty+1)); fi c=$((c+1)) done r=$((r+1)) done return $nEmpty;
}
#copy the values of one board to another #args: board name to copy from # board name to copy to #returns nothing function copyBoard { #only the values are copied local -n originalBoard=$1; #create the new board boardName=$2 if [ $verbosity -gt 0 ]; then printf"copying %s to %s\n" $1 $2 fi
makeBoard "$boardName" local -n newBoard="$boardName" local -i r=0;#row local -i c=0;#column while [ $r -lt 3 ]; do c=0; while [ $c -lt 3 ]; do
newBoard[$((C_CELLS_OFFSET+(r*3+c)))]="${originalBoard[$((C_CELLS_OFFSET+(r*3+c)))]}" c=$((c+1)) done r=$((r+1)) done
}
#calculate and set the value of a board #args: bosrd name #returns nothing #sets the board value of the board object function calculateBoardValue { local -n board=$1; local -i r=0;#row local -i c;#column local -i v;#value local -i boardValue=0;
posValueMatrix=( 17 19 17
19 23 19
17 19 17 );
while [ $r -lt 3 ]; do c=0; while [ $c -lt 3 ]; do
val="${board[$((C_CELLS_OFFSET+(r*3+c)))]}"
case "$val" in " ") val=0;; "X") val=7;; "O") val=13;;
esac boardValue=$((boardValue+val*${posValueMatrix[$((r*3+c))]})); c=$((c+1)); done r=$((r+1)); done if [ $verbosity -gt 0 ]; then printf"value: %d\n" $boardValue; fi board[$C_VALUE_OFFSET]=$boardValue;
}
#check if a given board exists #arg1: board to find #returns bool, true if found, false if not. #sets global dubplicateboard with the name of the board that is a match #sets the global transposition function doesBoardExist { local -i verbosity=0; if [ $verbosity -gt 0 ]; then echo"boardNumber: $boardNumber"; fi local -n board="$1"; local -i i=0; while [ $i -ne $boardNumber ]; do local -n b="b$i"; if [ $verbosity -gt 0 ]; then echo"checking against b$i" echo"if [ ${board[$C_VALUE_OFFSET]} -eq ${b[$C_VALUE_OFFSET]} ]; then" echo"if [ ${board[$C_NAME_OFFSET]} != ${b[$C_NAME_OFFSET]} ]; then" fi if [ ${board[$C_VALUE_OFFSET]} -eq ${b[$C_VALUE_OFFSET]} ]; then if [ "${board[$C_NAME_OFFSET]}" != "${b[$C_NAME_OFFSET]}" ]; then
findTransposition "$board""$b" if [ $? -eq $C_TRUE ]; then if [ $verbosity -gt 0 ]; then printf"Found another board for %s: %s (%s)\n""${board[$C_NAME_OFFSET]}""${b[$C_NAME_OFFSET]}""$transposition"; fi
duplicateBoard="${b[$C_NAME_OFFSET]}"; return $C_TRUE; fi fi fi i=$((i+1)); done return $C_FALSE;
}
#given to boards figure out how to transpose one to get the other #arg1: board that anchors the transposition #arg2: board to get the transposition for #returns boolean, true if successful, false if boards are to different function findTransposition { local -i verbosity=0; if [ $verbosity -gt 1 ]; then printf"findTransposition: %s %s\n""$1""$2" fi
local -n a=$1; local -n b=$2; if [ $verbosity -gt 1 ]; then
printBoard "a"
printBoard "b" fi local -i h; #horizontal mirroring local -i v; #vertical mirroring local -i r; #rotation local -i i; #cell iterator
h=0; while [ $h -lt 2 ]; do v=0; while [ $v -lt 2 ]; do #double mirroring is same as no mirroring but rotated twice if [[ $h -eq 1 && $v -eq 1 ]]; then return $C_FALSE; fi r=0; while [ $r -lt 4 ]; do local -n matrix="h$h""v$v""r$r";
transposition="h$h""v$v""r$r" if [ $verbosity -gt 1 ]; then printf"transpose: %s\n" $transposition fi i=0; while [ $i -lt 9 ]; do local -i m=${matrix[$i]} if [ $verbosity -gt 1 ]; then printf"%d |%s| != |%s| %d\n" $i "${a[$((C_CELLS_OFFSET+i))]}""${b[$((C_CELLS_OFFSET+m))]}" $m fi if [ "${a[$((C_CELLS_OFFSET+i))]}" != "${b[$((C_CELLS_OFFSET+m))]}" ]; then break; fi i=$((i+1)); done if [ $i -eq 9 ]; then return $C_TRUE fi r=$((r+1)); done v=$((v+1)); done h=$((h+1)); done return $C_FALSE;
}
function getNNext { local -n -r board="$1" local -i -r v=$2 local -i nnext=${board[$((C_NEXT_OFFSET+v))]}; return $nnext;
}
function getFirstChildOffset { local -n -r board="$1" local -i -r v=$2 local -i firstChildOffset; if [ $v -eq 0 ]; then local -i firstChildOffset=$C_NEXT_OFFSET+2; else
getNNext "$board" 0 local -i nnextX=$? firstChildOffset=$((C_NEXT_OFFSET+nnextX+2)); fi return $firstChildOffset;
}
function getChildWeight { local -n -r board="$1" local -i -r v=$2 local -i -r index=$3
getNNext "$1" 0 local -i nnext_x=$?
getNNext "$1" 1 local -i nnext_o=$?
#add a child from the list of children #arg1: parent (to add the child to) #arg2: child to add #arg3: which child list to add to (X or O) #returns: nothing function insertChild { local -n -r parent="$1" local -r child="$2" local -i -r v=$3 local -i -r weight=$4 local -i verbosity=0;
if [ $verbosity -gt 0 ]; then printf"insertChild.parent: %s\n""${parent[$C_NAME_OFFSET]}" printf"insertChild.child: %s\n" $child printf"insertChild.v: %d\n" $v printf"insertChild.weight: %d\n" $weight echo"${parent[@]}" fi
getNNext "$1" 0 local -i nnext_x=$?
getNNext "$1" 1 local -i nnext_o=$?
#if for X then shift O to make room #and always shift the weights #TODO: maybe implement a smarter way, but that requires more #handling on firstChildOffset and weight offset calculations local -i weightInsertPoint=$((${#parent[@]}+1)); local -i childInsertPoint=$((${#parent[@]}-nnext_x-nnext_o)); if [ $v -eq 0 ]; then weightInsertPoint=$((weightInsertPoint-nnext_o)); childInsertPoint=$((childInsertPoint-nnext_o)); fi
if [ $verbosity -gt 0 ]; then printf"weightInsertPoint: %d\n" $weightInsertPoint printf"childInsertPoint: %d\n" $childInsertPoint fi local -i i=$((${#parent[@]}+1));
while [ $i -gt $weightInsertPoint ]; do parent[$i]=${parent[$((i-2))]}; #2 so to make room for the child and the weight i=$((i-1)) done #now we have made room for the weight we can insert that parent[$((i+0))]=$weight i=$((i-1))
while [ $i -gt $childInsertPoint ]; do parent[$i]=${parent[$((i-1))]}; i=$((i-1)) done
parent[$i]="$child"; #update nnext local -i nnext=${parent[$((C_NEXT_OFFSET+v))]}; if [ $verbosity -gt 0 ]; then printf"insertChild.nnext[%d]: %d\n" $v $nnext fi nnext=$((nnext+1));
parent[$((C_NEXT_OFFSET+v))]=$nnext; if [ $verbosity -gt 0 ]; then printf"insertChild.nnext[%d]: %d\n" $v $nnext fi
}
#makes all children and other offspring from the given board #as the function runs recursive global progress variables are used #arg1: parent board #arg2: depth, used during debug #returns nothing #sets global variables used for tracking progress #sets global variables for the boards declare -i makeNextBoardsProgress=0; declare -i makeNextBoardsProgressTreshold=20; declare -i makeNextBoardsDepth=0; function makeNextBoards { local -n board=$1; local -i depth=$2; local -i r;#row local -i c;#column local -i v;#value local -i verbosity=0; if [[ $depth -gt $makeNextBoardsDepth && $makeNextBoardsDepth -ne 0 ]]; then return 0; fi
if [ $makeNextBoardsProgress -gt $makeNextBoardsProgressTreshold ]; then makeNextBoardsProgressTreshold=$((makeNextBoardsProgressTreshold+(C_NUM_ALLBOARDS/1000))); local -i progress=$((makeNextBoardsProgress*1000/$C_NUM_ALLBOARDS)); printf"Making boards, progress: %3d.%01d%%\n" $((progress/10)) $((progress%10)); # printf "Making boards, progress: %3d.%01d%% #%9d\n" $((progress/10)) $((progress%10)) $makeNextBoardsProgress 1>&2 fi
v=0; while [ $v -lt 2 ]; do r=0; while [ $r -lt 3 ]; do c=0; while [ $c -lt 3 ]; do makeNextBoardsProgress=$((makeNextBoardsProgress+1));
val="${board[$((C_CELLS_OFFSET+(r*3+c)))]}" if [ "$val"==" " ]; then #create the new board
boardName="b$boardNumber"; if [ $verbosity -gt 0 ]; then printf"boardName: %s\n" $boardName; fi boardNumber=$((boardNumber+1));
copyBoard "$1""$boardName"; local -n newBoard=$boardName; newBoard[$((C_CELLS_OFFSET+(r*3+c)))]=${xo[$v]}; if [ $verbosity -gt 1 ]; then printf"set %d,%d to %s, v: %d\n" $r $c "${xo[$v]}" $v; fi
if [ $verbosity -gt 3 ]; then printf"new board is\n"
printBoard $boardName; fi
isValid $boardName; local -i valid=$?; if [ $valid -eq $C_FALSE ]; then if [ $verbosity -gt 1 ]; then printf"board no good, scrapping\n"; fi unset $boardName; boardNumber=$((boardNumber-1)); if [ $verbosity -gt 1 ]; then printf"skipping to next symbol\n" fi c=100; r=100; continue; fi
calculateBoardValue "$boardName";
doesBoardExist "$boardName"; local -i exists=$?;
if [ $exists -eq $C_TRUE ]; then #set the existing one as child, but add the transposition
insertChild "$1""$duplicateBoard$transposition" $v if [ $verbosity -gt 1 ]; then printf"board exists, scrapping this version\n"; fi unset $boardName; boardNumber=$((boardNumber-1));
boardName="$duplicateBoard$transposition" c=$((c+1)); continue; fi #not exist and valid if [ $verbosity -gt 1 ]; then printf"New board: %s\n" $boardName; fi
insertChild "$1""$boardName""h0v0r0" $v
isWon $boardName; if [[ $? -eq $C_FALSE && $exists -eq $C_FALSE ]]; then
makeNextBoards $boardName $((depth+1)); fi fi c=$((c+1)); done r=$((r+1)); done v=$((v+1)); done if [ $verbosity -gt 2 ]; then printf"makeNextBoards tried: %d boards\n" $makeNextBoardsProgress; fi
}
#write the board information to a file #args: filename #returns: nothing function writeBoardsInfo { if [ $# -gt 0 ]; then file=$1 else
file="boards.txt"; fi if [ $verbosity -gt 1 ]; then printf"boardNumber: %d\n" $boardNumber fi local -i i=0; #truncate file
printf"#Board config\n" > $file while [ $i -lt $boardNumber ]; do local -i j=0; local -n board="b$i";
#print name printf"%s=( %s" ${board[$C_NAME_OFFSET]} ${board[$C_NAME_OFFSET]} >> $file
#print cells local -i r=0; while [ $r -lt 3 ]; do c=0; while [ $c -lt 3 ]; do
val="${board[$((C_CELLS_OFFSET+(r*3+c)))]}" printf" \"%s\"" "$val" >> $file c=$((c+1)); done r=$((r+1)); done
#print board value printf" %d" ${board[$((C_VALUE_OFFSET))]} >> $file
#print number of boards local -i v=0; while [ $v -lt 2 ]; do
getNNext "${board[0]}" $v local -i nnext=$?; printf" %d " $nnext >> $file v=$((v+1)); done
#print the next boards local -i j=0;
getFirstChildOffset "${board[0]}" 0 j=$? while [ $j -lt ${#board[@]} ]; do printf" %s""${board[$j]}" >> $file j=$((j+1)); done
#close the array printf" );\n" >> $file
if [ $((i%(boardNumber/100))) -eq 0 ]; then local -i progress=$((i*1000/boardNumber)); printf"Writing boards info, progress: %3d.%01d%%\n" $((progress/10)) $((progress%10)); fi i=$((i+1)); done printf"boardNumber=%d;\n" $boardNumber >> $file; printf"gameCounter=%d;\n" $gameCounter >> $file; printf"\n" >> $file; printf"Writig boards info complete\n";
}
#figure out the depth of this board #arg1: boardname #returns depth function getDepth { local -n bTmp="$1"; local -i i=0; local -i depth=0; while [ $i -lt 9 ]; do if [ "${bTmp[$((C_CELLS_OFFSET+i))]}" != " " ]; then depth=$((depth+1)); fi i=$((i+1)); done return $depth;
}
#set the weight for each child. #The weight is detemined by how far down the tree the board is. #no argumens #returns nothing #modifies global boards function setStartingWeights { #michi used the following weights #|depth|weights| #+-----+-------+ #| 1 | 4 | #| 3 | 3 | #| 5 | 2 | #| 7 | 1 | #but that gives us a problem with levels 0,2,4,6, the most simple solution is to double the weights and then fill in the in between integer values. #This gives us the below table #|depth|weights| #+-----+-------+ #| 1 | 8 | #| 2 | 7 | #| 3 | 6 | #| 4 | 5 | #| 5 | 4 | #| 6 | 3 | #| 7 | 2 | #| 8 | 1 | #the downside of this approach is that training will take longer, the other option is to apply the same weight on two depths #|depth|weights| #+-----+-------+ #| 1 | 4 | #| 2 | 4 | #| 3 | 3 | #| 4 | 3 | #| 5 | 3 | #| 6 | 2 | #| 7 | 1 | #| 8 | 1 |
#this only leaves level 0, this will be getting the value 5
weights=( 5 4 4 3 3 2 2 1 1 );#todo: what was the syntax for a local array again?
locali=0; while [ $i -lt $boardNumber ]; do local boardName="b$i"; local -i v=0; while [ $v -le 1 ]; do #printf "v: %d\n" $v local -n bTmp="$boardName"
getNNext "$boardName" $v; local -i nnext=$?; local -i firstChildOffset=${bTmp[$C_NEXT_OFFSET]}; local -i c=0; while [ $c -lt $nnext ]; do local child="${bTmp[$((firstChildOffset+c))]}"
getDepth "$boardName" local -i depth=$?
setChildWeight "$boardName" $v $c ${weights[$depth]} c=$((c+1)); done v=$((v+1)); done if [ $((i%(boardNumber/100))) -eq 0 ]; then local -i progress=$((i*1000/boardNumber)); printf"Setting starting weights, progress: %3d.%01d%%\n" $((progress/10)) $((progress%10)); fi i=$((i+1)); done
}
#performs the main game b #arg1: file to read from #returns nothing function setup { if [ $# -gt 0 ]; then local file="$1" else local file="boards.txt"; fi if [ -f "$file" ]; then printf"Loading boards config\n"; source"$file" if [ $verbosity -gt 1 ]; then echo"bn: $boardNumber" fi if [ $boardNumber -eq 0 ]; then printf"Input file doesn't contain the required information\n"; return 1; fi else printf"Currently the boards file is missing.\n"; printf"A new one will be created but this will take some time.\n"; printf"press q to exit, press any other key to create the file.\n"; read -n 1 ans
case $ans in
[Qq]*) return 1;;
esac
printf"Creating boards config\n";
makeBoard "b0" boardNumber=$((boardNumber+1)); #printBoard "b0";
calculateBoardValue "b0";
makeNextBoards "b0"; printf"end of makeNextBoards\n"; if [ $verbosity -gt 2 ]; then printf"makeNextBoards tried: %d boards\n" $makeNextBoardsProgress; fi #add starting weights printf"setting starting weights\n";
setStartingWeights printf"writing config to file. This can take some time\n"
writeBoardsInfo "$board_src" fi return 0;
}
#check if a given board is a child of another board. #arg1:board name to find #arg2:board name of parent #returns bool, found or not found
#sets global transposition to transposition of the child #sets global index to the index of the child function findChild { localverbosity=0; local find="$1" local parent="$2" if [ $verbosity -gt 2 ]; then printf"see if %s has child %s\n" $parent $find fi local -i i=0; local -i v=0; if [ "$3"=="O" ]; then v=1 fi
#strip transposition if [ ${find: -2:1} == "r" ]; then find=${find:0:$((${#find}-6))}; fi
if [ $verbosity -gt 2 ]; then printf"v: %d\n" $v if [ $verbosity -gt 3 ]; then
printBoard "$parent""h0v0r0" 1 1
printBoard "$find""h0v0r0" 1 1 fi echo"${tempBoard[@]}" fi local -n parentBoard="$parent" local -n findBoard="$find"
getNNext "$parent""$v" local -i nnext=$?;
getFirstChildOffset "$parent""$v" local -i firstChildOffset=$?; while [ $i -lt $nnext ]; do
boardName="${parentBoard[$((firstChildOffset+i))]}" if [ $verbosity -ge 2 ]; then printf"checking %s (%d/%d)\n""$boardName" $((i+1)) $nnext fi #get transposition information if [ ${boardName: -2:1} == "r" ]; then transposition=${boardName:$((${#boardName}-6))}; boardName=${boardName:0:$((${#boardName}-6))}; fi local -n tempBoard=$boardName; if [ $verbosity -ge 4 ]; then
printBoard "$boardName""h0v0r0" 1 1 fi if [ $verbosity -ge 2 ]; then printf"Name: %s\n" ${findBoard[$C_NAME_OFFSET]} printf"values: %d ?= %d\n" ${findBoard[$C_VALUE_OFFSET]} ${tempBoard[$C_VALUE_OFFSET]} fi if [ ${findBoard[$C_VALUE_OFFSET]} -eq ${tempBoard[$C_VALUE_OFFSET]} ]; then if [ $verbosity -ge 2 ]; then printf"found board: %s at index %d\n" $boardName $i; fi if [ $verbosity -gt 0 ]; then
printBoard "$find""h0v0r0" 1 1
printBoard "$boardName""h0v0r0" 1 1 printf"######\n" fi
findTransposition "$boardName""findBoard"; if [ $? -eq $C_TRUE ]; then if [ $verbosity -ge 3 ]; then printf"is a transposition of child\n" fi childIndex=$i; return $C_TRUE else if [ $verbosity -ge 3 ]; then printf"not the same board\n"; fi fi else if [ $verbosity -ge 3 ]; then printf"no value match\n" fi fi i=$((i+1)); done return $C_FALSE
}
#get the indices of all the children with a given name #arg1:board name to find #arg2:board name of parent #srg3: bool, when true ignore the transposition #returns nothing
#sets global VAR to array containing the indices of all #the matching children function findChildByName { local find="$1" local parent="$2" local noTransposition="$3" unset VAR if [ "$noTransposition"=="" ]; then
noTransposition="$3" fi
getFirstChildOffset "$parent" local -i i=$?
getNNext "$parent" 0 local -i -r nnext_x=$?
getNNext "$parent" 1 local -i -r nnext_o=$? local -i nnext=$((nnext_x+nnext_o)); local -i lastChildIndex=$((i+nnext)); local -n board="$parent"
if [ $noTransposition -eq $C_TRUE ]; then if [ ${find: -2:1} == "r" ]; then find=${find:0:$((${#find}-6))}; fi fi
while [ $i -lt $lastChildIndex ]; do local child="${board[i]}"; if [ $noTransposition -eq $C_TRUE ]; then localchildName=${child:0:$((${#child}-6))}; if [ "$find"=="$childName" ]; then VAR[${#VAR[@]}]=$i; fi else if [ "$find"=="$child" ]; then VAR[0]=$i; fi fi i=$((i+1)); done
local -n board="$parent";
}
#to keep track of all the moves made #leave breadcrumbs #add a breadcrumb to the trace #arg1: board to add #returns: nothing #sets: breadcrumbs function addBreadcrumb { local board="$1"; local -i index=${breadcrumbs[0]}; index=$((index+1));
breadcrumbs[$index]="$board"; breadcrumbs[0]=$index;
}
#change the weight of a board with a given delta #arg1: board name of parent #arg2: board name of child #arg3: is the child an x or an o child #arg4: the delta with which to change the weight #TODO: use getChildWeight and setChildWeight instead # better update setWeight to allow + and - prefixes # as modifiers function changeWeight { local -r parent="$1"; local -i -r childIndex="$2"; local -i -r v=$3; local -i -r delta=$4;
getNNext "$parent" 0 local -i -r nnext_x=$?
getNNext "$parent" 1 local -i -r nnext_o=$? local -i -r nnext=$((nnext_x+nnext_o)) local -n parentBoard="$parent"; local -i curWeight=${parentBoard[$((childIndex+nnext))]}; local -i newWeight=$((curWeight+(delta))); if [ $newWeight -le 0 ]; then newWeight=0; elif [ $newWeight -ge 255 ]; then newWeight=255; fi parentBoard[$((childIndex+nnext))]=$newWeight;
}
#when the game is done change the likelyhoods of a board #being chosen #arg1: symbol of the winner #returns nothing #modifies all boards that were used during this match function setNewWeights { local whoWon="$1" local playingAs="$2" localverbosity=0; printf"Learning from last match...\n"
if [ $verbosity -ge 1 ]; then echo"winner was: $1" fi
if [ $verbosity -ge 2 ]; then if [ "$whoWon"==" " ]; then echo"Was a tie, lightly strengthening both branches" else echo"Someone won, strengthening branch for the winner and weakening branch for the other"; fi fi #winner is always the last player to do a move #working backwards local -i isWinner=1; local -i i=${breadcrumbs[0]} printf"%d moves made\n" $i #the first board is always b0, but that is not a move, so not stored as breadcrumb. Here we need it, so we replace the size field with the b0 board
breadcrumbs[0]="b0h0v0r0" while [ $i -gt 0 ]; do local parent="${breadcrumbs[$((i-1))]}" #strip the transposition parent=${parent:0:$((${#parent}-6))};
local child="${breadcrumbs[$i]}"
if [ $verbosity -ge 2 ]; then printf"processing board %s\n" $child printf"parent: %s\n""$parent" fi
findChildByName "$child""$parent" $C_TRUE local -i delta=0 if [ "$whoWon"==" " ]; then delta=1 else if [ $isWinner -gt 0 ]; then delta=3; else delta=-1; fi isWinner=$((isWinner*-1)); fi
local -i j=0; while [ $j -lt ${#VAR[@]} ]; do
changeWeight "$parent" ${VAR[$j]} $((i%2)) $delta j=$((j+1)) done
i=$((i-1)); done
}
#sets global VAR with array filled with all the possible next moves and indexes #move0, index0, move3, index3, etc function getNextMoves { local -r boardName="$1"; local -i -r v=$2; local -n -r board="$boardName"; local -i verbosity=0;
getNNext "$boardName" $v local -i nnext=$?; unset VAR if [ $nnext -eq 0 ]; then return $C_FALSE; fi local -i nMoves=0; local -i index=0; #local -a moves
getFirstChildOffset "$boardName" $v local -i firstChildOffset=$?
while [ $index -lt $nnext ]; do
getChildWeight "$boardName" $v $index local -i weight=$? if [ $verbosity -gt 0 ]; then printf"index %d, weight: %d\n" $index $weight; fi local -i j=0; while [ $j -lt $weight ]; do if [ $verbosity -gt 0 ]; then #echo "moves: ${moves[@]}"; echo"moves: ${VAR[@]}"; fi #moves[$nMoves]=${board[$((firstChildOffset+index))]}; VAR[$nMoves]=${board[$((firstChildOffset+index))]}; nMoves=$((nMoves+1)) j=$((j+1)) done
#print help on the command line arguments #args: none #returns nothing function printArgsHelp { printf"Help for the tic tac toe game\n"; printf" Start the game and then press h for gameplay help\n" printf" -r <file> read the board settings from specified file\n"; #if not exist try default or just quit? printf" -w <file> write the board settings to specified file\n"; printf" if file is an empty string the boards are\n"; printf" not saved\n"; printf" -? user goes first(unsupported)\n"; printf" -? disable learning\n"; printf" -n numeric input mode\n" printf" -N numpad numeric input mode\n" printf" -v <value> set verbosity, each subfunction has its own\n"; printf" verbosity. Default value is 0 -> off.\n"; printf" -t test mode, do not run main. Used when sourceing\n"; printf" in testscript\n"; printf" -i skip the intro\n"; printf" -h print this help and exit. More information\n"; printf" available in the i -game help. Open that by\n" printf" pressing h when asked for a move.\n" printf" -? means flag not yet determined\n";
}
function printGameHelp { printf"\n\nHow to play\n"; printf"The game is played on a grid with 9 squares. Both\n"; printf"players take turns in which they can occupy a\n"; printf"square by marking them with their symbol. One \n"; printf"player uses an X, the other a O. Only an empty\n"; printf"square can be occupied. The goal of the game is\n"; printf"to get three squares in a row. This can be\n"; printf"horizontally, vertically or diagonally. The first\n"; printf"player to align three of their symbols wins.\n"; printf"\n"; printf"The controls of this game are simple. On the edges\n" printf"of the board are letters and numbers\n";
printBoard b0 "h0v0r0" $C_FALSE $C_TRUE printf"When it is your turn ou specify a column by typing\n"; printf"One of the column letters, followed by a row number\n" printf"That will place your symbol on the board, or inform\n"; printf"you the move is invalid and you can try again. This \n"; printf"can be because there already is a symbol on that\n"; printf"cell, or the cell does not exist.\n" printf"\n"; printf"When in numeric input mode the coordinates won't be\n" printf"printed on the edge of the board.\n" printf"instead a single digit is used to identify the cells\n" printf" ┏━┯━┯━┓\n"; printf" ┃1┃2┃3┃\n"; printf" ┠─┼─┼─┨\n"; printf" ┃4┃5┃6┃\n"; printf" ┠─┼─┼─┨\n"; printf" ┃7┃8┃9┃\n"; printf" ┗━┷━┷━┛\n"; printf"In numpad numeric mode the order is slightly different:\n" printf" ┏━┯━┯━┓\n"; printf" ┃7┃8┃9┃\n"; printf" ┠─┼─┼─┨\n"; printf" ┃4┃5┃6┃\n"; printf" ┠─┼─┼─┨\n"; printf" ┃1┃2┃3┃\n"; printf" ┗━┷━┷━┛\n"; printf"\n" printf"Oh, and you quit by giving q as a move\n" printf"Press enter key to return to the game\n" read dummy #dummy to wait on user key press
}
#some global vars declare duplicateBoard=""; declare -i boardNumber=0; declare -i gameCounter=0; declare -a breadcrumbs; declare -i childIndex;
function main { local -i verbosity=0; local board_src="boards.txt"; local board_dst="boards.txt"; localcoordinateInputMode=$C_TRUE; local -i numpadMode=$C_FALSE; local -i skipIntro=$C_FALSE; whilegetopts":v:w:r:hnNti" flag; do
case ${flag} in
r) echo"src: ${OPTARG}"; board_src="${OPTARG}";;
w) echo"dst: ${OPTARG}"; board_dst="${OPTARG}";;
h) printArgsHelp; return 0;;
v) echo"v: ${OPTARG} $1 $2"; verbosity=${OPTARG};;
n) coordinateInputMode=$C_FALSE;;
N) coordinateInputMode=$C_FALSE;numpadMode=$C_TRUE;;
t) return 0;;
i) skipIntro=$C_TRUE;;
*) printf"unknown argument %s\n" $flag;;# return 1;;
esac done if [ $skipIntro -eq $C_FALSE ]; then
printIntro fi
setup "$board_src" if [ $? -ne 0 ]; then return 1; fi;
local -i playSelf=$C_FALSE; localquit=$C_FALSE local -i haveLearned=$C_FALSE;
#start playing the game while [ $quit -eq $C_FALSE ]; do#loop for multiple games localturnCounter=-1; local -i whosTurn=$C_USER; local whoWon=" "; localgameEnded=$C_FALSE; declare -n bCurrent="b0"; local transposition="h0v0r0"; unset breadcrumbs printf"%d Game(s) played\n" $gameCounter gameCounter=$((gameCounter+1)) if [ $verbosity -ge 1 ]; then echo"bCurrent: ${bCurrent[@]}"; fi while [[ $quit -eq $C_FALSE && $gameEnded -eq $C_FALSE ]]; do#loop for turns in game printf"This turn(%d) is for: %s\n" $turnCounter ${xo[$((turnCounter%2))]}
printBoard ${bCurrent[$C_NAME_OFFSET]} $transposition $C_FALSE $coordinateInputMode;
#now do stuff for the next turn turnCounter=$((turnCounter+1)) local -i v=$(((turnCounter)%2)) if [ $playSelf -ne $C_TRUE ]; then whosTurn=$((whosTurn*-1)); fi printf"preparing for next move (%s)\n" ${xo[$v]}; #check if moves left
getNextMoves "$bCurrent" $v if [ $? -ne $C_TRUE ]; then printf"no more moves for %s\n" ${xo[$v]};
isWon "${bCurrent[$C_NAME_OFFSET]}"; if [ $? -eq $C_TRUE ]; then printf"%s won.\n" ${xo[$(((turnCounter-1)%2))]}; whoWon=${xo[$(((turnCounter-1)%2))]} if [ $whosTurn -eq $C_COMPUTER ]; then#computer turn printf"congratulations!\n" else printf"better luck next time.\n" fi gameEnded=$C_TRUE; break; else#check if all squares are used
getDepth "bCurrent" localdepth=$? if [ $depth -eq 9 ]; then printf"The game ended in a tie.\n"; gameEnded=$C_TRUE; fi
printBoard "bCurrent""$transposition" $C_FALSE $coordinateInputMode; break; fi fi nextMoves=("${VAR[@]}"); #TODO: make local array if [ $verbosity -ge 1 ]; then echo"currentboard: ${bCurrent[@]}" echo"next moves: ${VAR[@]}" echo"next moves: ${nextMoves[@]}" fi
if [ $whosTurn -eq $C_COMPUTER ]; then#computer turn printf -- "--COMPUTER--\n"; #choose the next board, which is the next move local -i nnext=${#nextMoves[@]}; if [ $nnext -le 0 ]; then printf"An error occurred, something went wrong with the weights\n" printf"board %s\n""${bCurrent[@]}" printf"writing boards to error file\n"
board_dst="nonext_debug.txt" quit=$C_TRUE break; fi local -i nextBoardIndex=$((RANDOM%nnext));
boardName="${nextMoves[$nextBoardIndex]}"; if [ $verbosity -ge 1 ]; then printf"nextBoardIndex: %d, board: %s, v: %d\n" $nextBoardIndex ${nextMoves[$nextBoardIndex]} $v; printf"next board: %s\n""$boardName"; fi local prevTransposition="$transposition"; #get transposition information if [ ${boardName: -2:1} == "r" ]; then transposition=${boardName:$((${#boardName}-6))}; boardName=${boardName:0:$((${#boardName}-6))}; fi else#user move printf -- "--USER--\n"; if [ $verbosity -ge 2 ]; then printf"transposition: %s\n" $transposition printf"breadcrumbs: " printf"%s-""${breadcrumbs[@]}" printf"\n" fi local -i isValid=$C_FALSE; while [ $isValid -eq $C_FALSE ]; do printf"Your move: "; local c; local r; isValid=$C_TRUE; read -n 1 c; if [ "$c"=="q" ]; then quit=$C_TRUE; printf"\n"; break; fi if [ "$c"=="h" ]; then
printGameHelp isValid=$C_FALSE continue; fi if [ $coordinateInputMode -eq $C_TRUE ]; then read -n 1 r printf"\n move given: %s%s\n""$c""$r"; #regexp would be a great way to check validity #but not supported on dev terminal, so other way is needed local -i i=0; while [ $i -lt ${#r[@]} ]; do
case "${r[$i]}" in
[0-9]) ;;
*) isValid=$C_FALSE; printf"that is not a valid move\nplease enter a valid move.\n"; break;
esac i=$((i+1)); done if [ $isValid -eq $C_FALSE ]; then continue; fi
if [[ ! ( 0 -le $r && $r -le 3 ) ]]; then isValid=$C_FALSE; printf"that is not a valid move.\n Please enter a valid move.\n"; continue; fi local -i pos=$((r*3));
case $c in
[aA]) pos=$((pos+0));;
[bB]) pos=$((pos+1));;
[cC]) pos=$((pos+2));;
[qQ]) exit;;
*) isValid=$C_FALSE; printf"that is not a valid move.\n"; continue;;
esac else printf"\n move given: %s\n""$c"; #regexp would be a great way to check validity #but not supported on dev terminal, so other way is needed local -i i=0; while [ $i -lt ${#c[@]} ]; do
case "${c[$i]}" in
[1-9]) ;;
*) isValid=$C_FALSE; printf"that is not a valid move\nplease enter a valid move: "; break;
esac i=$((i+1)); done if [ $isValid -eq $C_FALSE ]; then continue; fi local -i pos=$((c-1)); #-1 to go from lowest 1 to lowest 0 if [ $numpadMode -eq $C_TRUE ]; then posLUT=( 6 7 8 3 4 5 0 1 2 ) pos=${posLUT[$pos]}; fi #the position is given as is on the numpad, the positions fi
#apply the transposition to get the correct position #but in case it is invalid, save the current transposition local prevTransposition="$transposition"; local -n matrix="$transposition"; pos=${matrix[pos]}
if [ $verbosity -ge 2 ]; then printf"transposed position: %d\n" $pos fi if [ "${bCurrent[$((C_CELLS_OFFSET+pos))]}" != " " ]; then isValid=$C_FALSE; printf"that is not a valid move, position already taken\n"; if [ $verbosity -ge 2 ]; then echo"${bCurrent[@]}"; fi
printBoard "bCurrent""$prevTransposition" $C_FALSE $coordinateInputMode
transposition="$prevTransposition"; echo"transposition=$prevTransposition"; fi done if [ $quit -eq $C_TRUE ]; then break; fi if [ $verbosity -ge 1 ]; then printf"%s%d %d\n" $c $r $pos; fi #now find the corresponding board name
copyBoard "bCurrent""bTemp" bTemp[$((C_CELLS_OFFSET+pos))]=${xo[$v]};
calculateBoardValue "bTemp";
if [ $verbosity -ge 1 ]; then
printBoard "bTemp" $transposition $C_TRUE; fi local prevTransposition="$transposition";
findChild "${bTemp[$C_NAME_OFFSET]}""bCurrent""${xo[$v]}"
if [ $? -eq $C_TRUE ]; then printf"Board user chose is: %s\n" $boardName; else #crash, shouldn't happen if programmed correctly #just here so it is clear what is happening #and the program doesn't try to stumble on and #makes debugging a lot harder printf"ERROR: no board found\n" exit fi
if [ $verbosity -ge 1 ]; then
printBoard $boardName printf"The transposition of that board is: %s\n" $transposition fi fi #now apply the old transposition to this one
if [ $verbosity -ge 2 ]; then printf"transposition, current, old: %s, %s\n""$transposition""$prevTransposition"; fi
transposeTransposition "$transposition""$prevTransposition";
if [ $verbosity -ge 1 ]; then printf"the new board transposition is: %s\n" $transposition; fi declare -n bCurrent="$boardName";
addBreadcrumb "$boardName$transposition" done if [ $quit -eq $C_TRUE ]; then printf"quitting\n" break; fi if [ $verbosity -ge 1 ]; then echo"breadcr.: ${breadcrumbs[@]}"; fi printf"\n";
sleep 0.5; #"learn" from the last game
setNewWeights "$whoWon""X"; #TODO: give second arg depending on playing X or O (for learning) haveLearned=$C_TRUE;
sleep 1; #TODO: ask if the user wants to play again
done #remember the learned boards if [[ "$board_dst" != "" && $haveLearned -eq $C_TRUE ]]; then
writeBoardsInfo "$board_dst"; fi