This is an attempt to develop a minmax based game playing program. We will discuss program development strategy.
Assumptions
Variables
BD = Board Dimension. This can be changed and the program recompiled to allow playing on a board larger than 4 x 4.
CTYPE = BD x BD character array. The defined type of the characteristics of the nodes.
HUMAN and COMPUTER which side is which color (as in Black and White of Chess)
Program Elements
Main Program: Sown in Listing 1. Controls everything, the main loop.
MoveSide: Shown in Listing 2. The heart of the program.
MoveSide examines the color to determine if the player is human or computer.
If it is the Computer’s turn, MoveSide computes the move by calling ComputeAMove (Shown in Listing 3).
FindFirst is a depth-first search thru the tree in search for unexpanded nodes.Sample Coding:
Expand (Listing 5) looks at the board position at node (N, Side). It examines every location on the board for a friendly piece and considers every possible move for it with a routine called ConsiderMove.
ConsiderMove then adds any appropriate nodes to the tree with CreateNode from the tree-handling toolbox.Program Flow:
Main Program calls (SetUpGame, ChooseColor, MoveSide, ShowBoard)
MoveSide calls (GetInputMove, ComputeAMove, Won)
ComputeAMove calls (GenTree, MinMax)
GenTree calls (FindFirst, Expand)
Only the code for the underlines procedures is shown below.
Listing 1. Main Program
Begin
SetUpTheGame (CurrentBoard);
ChooseColor (Computer, Human);
{play White or Black? - White goes first}
Side-to-move := White;
Repeat
MoveSide (SideToMove, CurrentBoard);
ShowBoard (CurerntBoard);
WhoWon := Won (CurrentBoard, SideToMove);
SideToMove := Opposite (SideToMove);
Until WhoWon <> Neither;
If WhoWon = Computer then Writeln (‘I have won!’)
else writeln (‘you have won’);
End.
Listing 2. Moveside Routine (This was called in Listing 1.)
PROCEDURE MoveSide(Side: Char; Var CurrentBoard: Ctype; WhoWon: char);
Begin
IF Side = Human THEN
GetInputMove (CurerntBoard, Side);
ELSE
ComputeAMove (CurerntBoard, Side);
End
Listing 3. ComputeAMove Routine
Procedure ComputeAMove(Var Board: Ctype; Side: char);
Begin
GenTree (Nlevels, side); {Generates move tree}
MinMax (Board, Side,
Nlevels); {Returns new board position}
End
Listing 4. GenTree Routine
{Generates a tree N levels deep for side Side}
Procedure GenTree (Var Nlevels: INTEGER; Side: CHAR)
Var
N, INTEGER
AllDone: Boolean;
TreeFull: Boolean;
Begin {Outer loop}
Repeat
Treefull := FALSE;
Inittree; {then
set up root characteristics}
AllDone:= FALSE;
Begin {This is the
main loop}
{Find first unexpanded
node by calling FindFirst, which is a depth-first search in
search for an unexpanded node}
FindFirst(N);
If N=NULL then AllDone := TRUE Else
Begin {This is the Inner loop}
{Expand node n, by calling Expand}
Expand (N, Side, TreeFull, Nlevels);
If TreeFull then
Begin {Innermost loop}
{Reduce Search Depth}
Nlevels := Nlevels - 1;
writeln ('GenTree: Tree overflow. Reducing plies'
'temporarily to ' nlevels);
End; {Reduce search depth}
End; {Expand node n}
End {main loop}
Until NOT TreeFull;
End;
Listing 5. Procedure for expanding gametree nodes.
Procedure Expand (N: INTEGER; OriginalSide: CHAR; VAR Nlevels: INTEGER);
Begin
{First, we have to know which side
is making the move}
IF odd(Depth(N))
THEN ThisSide := Opposite (OriginalSide)
ELSE ThisSide := OriginalSide;
Characteristic
(B, N);
{Now find pieces
and see if they can move}
FOR I := 1 TO BD
DO FOR J :=1 TO BD DO
Begin
IF B[I, J] = ThisSide
THEN FOR K :=1 TO NMoves DO
ConsiderMove (I, J, K, B, ThisSide, TreefUll, N, OriginalSide, Nlevels);
SetValue (N, Expanded);
{now the parent node has been expanded}
End;
End;
Notes: We may have to pull together a number of tree
handling routines.