0% found this document useful (0 votes)
17 views

A Chess-Playing Computer Program

This document describes a chess-playing computer program. It discusses how chess programs work by searching game trees and using the minimax algorithm. It provides details on the implementation of this specific chess program in C++, including test results playing against itself on different hardware. It also outlines several ways the program could be improved, such as through alpha-beta pruning and increasing computing power.

Uploaded by

Luis Migliorero
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views

A Chess-Playing Computer Program

This document describes a chess-playing computer program. It discusses how chess programs work by searching game trees and using the minimax algorithm. It provides details on the implementation of this specific chess program in C++, including test results playing against itself on different hardware. It also outlines several ways the program could be improved, such as through alpha-beta pruning and increasing computing power.

Uploaded by

Luis Migliorero
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

A chess-playing computer program

Mikko Malinen

July 23, 2000

1 Comparison of chess programs


Strength of a chess player is sometimes presented as an elo number. There is a
formula for determining player's elo rating:
(w l)=g  400 + (average of opponents rating) = player s rating 0

where w = wins, l = losses and g = number of games. If the elo di erence


between two computer programs is small - let's say that it is 40 - then the better
program wins only six out of ten games. This must be taken in consideration
when comparing computer programs for example in tournaments. These small
di erences can not usually be seen there and the winner may be determined by
good luck.

2 How chess programs work?


player A chooses

-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

B chooses the smallest:


-1 -2 -1 < 4 and -2 < 2

4 -1 -2 2

Picture 2. Choosing the move in minimax-algorithm.

3 The implemented chessprogram


3.1 The operation of the program
The program is written in C++ and compiled with g++ to allow maximum
portability. Program listing mchess. ex has the function generateMove(). This
function explains the operation of the program. The C++ class MoveGenerator
stores the state of the node at instance C which is of class Board. MoveGenerator
has also member functions to nd all the move possibilities from a state. When
the program has found all the states reachable after a speci c number of levels
- that is - plys - it nds the optimal move by minimax-algorithm.

3.2 Test results


The game program was let to play against itself. The rst game is run on a PC
with 64 megabytes of memory. With this equipment four levels - four plys - can
be investigated.

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

3.3 How the performance could be improved?


3.3.1 Alpha-beta pruning

T1 B

-4 T11 ? T12 A

-4 -7 B

-4 -7 5 9 7 -6

Picture 3. The use of alpha-beta -pruning.

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.

3.4 Program listing


Here are some les from the 1996 version of the program.
3.4.1 Board.h
//extern int manAsAmaterial[15] = {0,1,3,4,5,100,12,0,0,-1,-3,-4,-5,-100,-12};
//extern char manAsALetter[16] = {"-STRLKQ strlkq"};

class Board {

public:

Board();
~Board();

//unsigned whosTurn : 1; // bit field of length 1: 0=white's turn, 1=black's

unsigned state[8][8] // every square is filled with either blanc or


// a man of some type. [x][y] [0-7][0-7]

= { { 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;

//int manAsAmaterial[15] = {0,1,3,4,5,100,12,0,0,-1,-3,-4,-5,-100,-12};

//protected:

//char manAsALetter[16] = { "-STRLKQ strlkq" };

};

3.4.2 Board.C
3.4.3 MoveGenerator.h
class MoveGenerator {

public:

MoveGenerator(Board *b = 0, int from = 0);

6
~MoveGenerator();

Board *C;

/* parameters for this specific move generator */


/* material values of men */
/* ... */

char *generate();

int manAsAmaterial[15] = {0,1,3,4,5,8,100,0,0,-1,-3,-4,-5,-8,-100};


int materialDifference();

/* 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();

void addAPossibility(unsigned a1,unsigned b1,unsigned a2,unsigned b2);


void printPossibilities();
int indexOfPossibilities = 0;
int totalPossibilities = 0;
int materialNow;
int fromPrevLevelLeaveNo;
int value;

};

3.4.4 MoveGenerator.C
3.4.5 mchess. ex
/* mchess - chess playing program */
/* This program participates COMPUTER CHESS 1996 KONESHAKKI 1996 */
/* Team TITAANIT */

/* The most important objects are Board *B and MoveGenerator *MG */

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();

Board *b =new Board;


char *myMove =new char[6];
char *moveFromMoveGenerator;
/* MoveGenerator MG(b); */
char color = 0; /* 0=white, 1=black */

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};

void invertMove(char *move)


{
move[1]=56-(move[1]-49); /* y1 */
move[3]=56-(move[3]-49); /* y2 */
move[0]=105-(move[0]-97); /* x1 */
move[2]=105-(move[2]-97); /* x2 */
}

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 );

/* count move starting from leaves and ending to root */

/* counting will be started just "above" leaves */


for (p=level-1;p>=0;p--) {
if (even(p)) {
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) {
bestIndexSoFar = p3;
}
}
}
if (MG[p][p2]->value > -50) { // if king is dead then different value
MG[p][p2]->value = MG[p+1][bestIndexSoFar]->value; }
}

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 */

/* preparing for returning the move */

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

You might also like