A Chess-Playing Computer Program
A Chess-Playing Computer Program
Mikko Malinen
-4 -7 5 9 7 -6 4 -1 -2 2 2 3 2 -5 6 -6 3 -2 1 -3
Picture 1. A game tree.
1
To investigate a speci c state of a game chess programs use an evaluation func-
tion. Usually it counts the material balance, which chessmen are on the board.
It may contain also other factors such as how many squares the chessmen can
attack. Chess programs search states which can be obtained after some moves.
Look at picture 1. The evaluation function is calculated four half moves (plys)
forward. The minimax-algorithm is based on a principle that both players
choose the best alternative for them when the tree is traversed from bottom
to top. When it is B's turn the smallest is chosen and when it is A's turn the
biggest is chosen. The result is a path at picture 2, where A chooses the rst
move.
A chooses the biggest:
-1 -1 > -2
4 -1 -2 2
2
Event ["COMPUTER CHESS 1996"]
Date ["1996.11.06"]
Round [" nal"]
White ["TITAANIT VI"]
Black ["TITAANIT IV"]
Result ["0-1"]
1. h2h3 a7a6 2. h3h4 a6a5 3. h1h3 a8a6 4. h3a3 a6h6 5. g2g3 b7b5 6.
e2e3 b8a6 7. f1b5 c7c6 8. b5a6 c8a6 9. g1h3 a5a4 10. b1c3 a6c8 11. f2f4 c6c5
12. h3f2 c5c4 13. a3a4 e7e6 14. a4a8 c8b7 15. a8d8 e8d8 16. g3g4 b7f3 17.
d1f3 d7d6 18. f4f5 e6f5 19. f3a8 f5g4
The next example is made with a powerful unix workstation with larger mem-
ory capabilities. With this equipment ve plys can be investigated. Running
the program resulted in segmentation fault at the 14th move from a reason un-
known at the moment. However, the quality improvement in moves can be seen.
1. e2e3 e7e6 2. d1g4 d8f6 3. g4f4 f6f4 4. e3f4 f8d6 5. d2d4 b8e6 6. g1f3
d6b4 7. e1d1 a7a6 8. f1d3 h7h5 9. f3e5 c6e5 10. f4e5 h5h4 11. c1d2 b4d2 12.
b1d2 h8h5 13. d1e2 d7d6
T1 B
-4 T11 ? T12 A
-4 -7 B
-4 -7 5 9 7 -6
3
In alpha-beta pruning the game tree must be traversed from left to right, not
only from bottom to top as in this program. Look at the part of the game tree
at picture 3. Minimax-method gives the value -4 for the left part (B has chosen
smaller values which are best for him and A has chosen the bigger values). At
the point marked by a question mark (T12) A could choose the left part. Then
the result would be 9. So the value of T12 is in all cases at least 9. Because A
knows that B can reach the value -4 by choosing the left-side alternative (T11),
the other states (after T12) need not be investigated. In alpha-beta pruning
two variables and are used. Alpha is the best value which the opponent (B)
may reach. Beta is the worst value, where the program A may go into. With
alpha-beta pruning as much as 99,5% may be saved. This means that only every
200th state needs to be investigated.
3.3.2 Decreasing memory usage
At the moment one node reserves memory by three di erent classes. The mem-
ory requirement is as follows:
Class Memory, bytes
Board 260
OwnMoveGenerator 28
Possibilities 720
The Board -class stores the state of a board. In chess, there are 12 di er-
ent types of chessmen on the board. 13, if empty is included. This means that
one type may be expressed with four bits. There are 64 squares on the board.
Places of the chessmen may be expressed with 64 4 bits = 256 bits = 32 bytes.
The other classes may be optimized by the similar way.
3.3.3 Finding the optimal parameters
As mentioned earlier, the evaluation function usually takes into account the
material balance. It may consider also other factors such as how many move
possibilities a player has. The evaluation function may be more complicated
than just the material value. One example is
value = coefficient1 factor1 + coefficient2 factor2 + :::
The coecients for factors may be determined by playing games against game
programs with di erent evaluation functions.
3.3.4 Estimating the opponents parameters
When there is available a game program that is better than the program under
development the coecients of the evaluation function of the better program
may be determined. We do not know the actual evaluation function, but in the
best case the function is determined at least approximatively. The coecients of
an evaluation function are to be determined. We know the values of the factors.
4
The values of the function we do not know but we may give them estimates
because we know which moves are selected and which are not. With these
information we can apply a linear regression model to determine the coecients.
Of course the predictivity of the estimated evaluation function must be checked
by comparing the moves with moves made by a program with the estimated
evaluation function.
3.3.5 Increasing computing power
The game program is programmed in PC environment. The rst runs were
made in PC. The portable code was then run on a high performance UNIX
workstation. The next step would be to divide the computing to several work-
stations. It must be kept in mind that although the huge speed increase we
need to make the computing power 37 times bigger to add one ply more. In
chess there is on average 37 move possibilities. To add n plys we need 37n times
more computing.
class Board {
public:
Board();
~Board();
= { { 2,1,0,0,0,0,9,10},
{ 3,1,0,0,0,0,9,11},
{ 4,1,0,0,0,0,9,12},
{ 6,1,0,0,0,0,9,14},
{ 5,1,0,0,0,0,9,13},
{ 4,1,0,0,0,0,9,12},
{ 3,1,0,0,0,0,9,11},
5
{ 2,1,0,0,0,0,9,10}
};
/*
char state[8][4]
= { { 33,0,0,154},
{ 49,0,0,155},
{ 65,0,0,156},
{ 97,0,0,158},
{ 81,0,0,157},
{ 65,0,0,156},
{ 49,0,0,155},
{ 33,0,0,154}
};
*/
void print();
void move(char *m); // format of move "e2e4"
void move(char *x1,char *y1,char *x2,char *y2); // format of move 4,0,4,2
//unsigned whiteInChess :1; // 0=no, 1=yes
//unsigned blackInChess :1;
int value = 0;
//protected:
};
3.4.2 Board.C
3.4.3 MoveGenerator.h
class MoveGenerator {
public:
6
~MoveGenerator();
Board *C;
char *generate();
/* possibilities to move from x1,y1 to x2,y2 and material after the move */
char x1[100] = {0};
char y1[100] = {0};
char x2[100] = {0};
char y2[100] = {0};
int futureMaterial[100] = {0};
void searchTheOpponentsPossibilities();
void searchThePossibilities();
};
3.4.4 MoveGenerator.C
3.4.5 mchess. ex
/* mchess - chess playing program */
/* This program participates COMPUTER CHESS 1996 KONESHAKKI 1996 */
/* Team TITAANIT */
7
%{
/* place any includes here */
#include<iostream.h>
#include<fstream.h>
#include"Board.h"
#include"MoveGenerator.h"
#include<time.h>
#include"binom.h"
int fieldNo = 1;
int fileNo = 1;
int fileRest = 0;
char *fileName = new char [80];
char *filePostfix = new char [80];
char *varString = new char[80]; /* to store the variable read from input */
ofstream outFile;
char *tuple[10];
void writeToFile();
int p;
int p2;
int p3;
long int seconds;
int bestIndexSoFar;
char *bestMove = new char[6];
int level = 1;
MoveGenerator *MG[10][100];
int totalNodesAtLevel[10] = {0};
8
char *
generateMove()
{
seconds = time(NULL);
totalNodesAtLevel[0]=1;
MG[0][1] = new MoveGenerator(b,0);
do {
level+=1;
for (p=1;p<=totalNodesAtLevel[level-1];p++) {
for (p2=1;p2<=MG[level-1][p]->totalPossibilities;p2++) {
MG[level][p2]=new MoveGenerator(MG[level-1][p]->C,p);
totalNodesAtLevel[level]++;
MG[level][p2]->C->move(&MG[level-1][p]->x1[p2],
&MG[level-1][p]->y1[p2],
&MG[level-1][p]->x2[p2],
&MG[level-1][p]->y2[p2]);
if (odd(level)) {MG[level][p2]->searchThePossibilities();}
else {MG[level][p2]->searchTheOpponentsPossibilities();};
}
}
} while (time(NULL)-seconds < 40 );
else {
for (p2=1;p2<=totalNodesAtLevel[p];p++) {
bestIndexSoFar = 1;
for (p3=1;p3<=MG[p][p2]->totalPossibilities;p3++) {
if (MG[p+1][p3]->value >= MG[p+1][bestIndexSoFar]->value) {
9
bestIndexSoFar = p3;
}
}
}
if (MG[p][p2]->value < 50) { // if opponents king is dead then different value
MG[p][p2]->value = MG[p+1][bestIndexSoFar]->value; }
}
} /* for p */
bestMove[0] = (char)(MG[0][1]->x1[bestIndexSoFar]+97);
bestMove[1] = (char)(MG[0][1]->y1[bestIndexSoFar]+49);
bestMove[2] = (char)(MG[0][1]->x2[bestIndexSoFar]+97);
bestMove[3] = (char)(MG[0][1]->y2[bestIndexSoFar]+49);
bestMove[4] = 0;
/* C->move(bestMove); should the move be stored somewhere? */
// deleting of MGs
for (int i=0; i>=level; i--) {
for (p=1;p<=totalNodesAtLevel[i];p++) {
delete MG[i][p];
}
}
for (i=1;i<11;i++) {
totalNodesAtLevel[i] = 0;
}
return bestMove;
%}
DIGIT [0-9]
LOWID [a-z][a-z0-9]*
ID [a-zA-Z][a-zA-Z0-9]*
STRING [a-zA-Z0-9]+
EXTSTRING [a-zA-Z0-9.-:]+
UNSIGNINT {DIGIT}
INTEGER [-]*{DIGIT}+
RESTOFLINE [ \t]*\noutFile.open("saate001.tex", ios::out );
10
%%
end { exit(0); }
black {
b->state[3][0] = 5;
b->state[4][0] = 6;
b->state[3][7] = 14;
b->state[4][7] = 13;
/* b->state = { {2,1,0,0,0,0,9,10},
{3,1,0,0,0,0,9,11},
{4,1,0,0,0,0,9,12},
{5,1,0,0,0,0,9,13},
{6,1,0,0,0,0,9,14},
{4,1,0,0,0,0,9,12},
{3,1,0,0,0,0,9,11},
{2,1,0,0,0,0,9,10}
};
*/
/* change has to be made also to MG */
/* MG.C->state[3][1] = 5;
MG.C->state[4][1] = 6;
MG.C->state[3][7] = 14;
MG.C->state[4][7] = 13;
*/
color = 1;
}
white {
moveFromMoveGenerator = generateMove();
b->move(moveFromMoveGenerator);
cout << moveFromMoveGenerator;
delete moveFromMoveGenerator;
}
[a-h][1-8][a-h][1-8] {
if (color) {invertMove(yytext);};
b->move(yytext);
/* MG.C->move(yytext); */
moveFromMoveGenerator = generateMove();
b->move(moveFromMoveGenerator);
11
if (color) {invertMove(moveFromMoveGenerator);};
cout << moveFromMoveGenerator;
delete moveFromMoveGenerator;
}
[o][-][o][-][o] {
/* WRITE COLOR-ADDS ! */
b->move(yytext);
/* MG.C->move(yytext); */
moveFromMoveGenerator = generateMove();
b->move(moveFromMoveGenerator);
cout << moveFromMoveGenerator;
delete moveFromMoveGenerator;
/* long castling */
}
[o][-][o] {
/* WRITE COLOR-ADDS ! */
b->move(yytext);
/* MG.C->move(yytext); */
moveFromMoveGenerator = generateMove();
b->move(moveFromMoveGenerator);
cout << moveFromMoveGenerator;
delete moveFromMoveGenerator;
/* short castling */
}
. { }
<<EOF>>{ exit(1); }
%%
main()
{
yyin = stdin;
cout << "start" << endl;
yylex();
12
4 References
Minimax-algorithm and alpha-beta -pruning:
Hyvonen-Karanta-Syrjanen: Tekoalyn ensyklopedia. Gaudeamus.
13