Author: Dann Corbit
Date: 00:27:24 07/24/02
Go up one level in this thread
On July 24, 2002 at 02:33:30, Pham Hong Nguyen wrote:
>Hi all,
>
>I think I am so crazy to do that project. It is a chess program for newbies of
>computer chess and so stupid for all people here. However, if you have been
>dealing with newbies, please take a look and give me your thought, correcting
>and suggestions.
>
>http://www.geocities.com/axchess/firstchess.html
A very interesting and laudable project. I think it is somewhat of a mistake to
assume that code which is more comprehensible to the author will be more
comprehensible to some other student reading it.
Some remarks:
/*
FirstChess - Freeware, by Pham Hong Nguyen
Version: alpha
*/
/*
* BASIC PARTS: *
* Some definitions *
* Board representation and main varians *
* Move generator *
* Evaluation for current position *
* Make and Take back a move, IsInCheck *
* Search function - a typical alphabeta *
* Utility *
* Main program *
*/
#include <stdio.h>
#include <string.h>
/*
****************************************************************************
* Some definitions *
****************************************************************************
*/
#define PAWN 0
#define KNIGHT 1
#define BISHOP 2
#define ROOK 3
#define QUEEN 4
#define KING 5
#define EMPTY 6
#define WHITE 0
#define BLACK 1
#define VALUE_PAWN 100
#define VALUE_KNIGHT 300
#define VALUE_BISHOP 300
#define VALUE_ROOK 500
#define VALUE_QUEEN 900
#define VALUE_KING 10000
#define MATE 10000
#define COL(pos) ((pos)&7)
#define ROW(pos) ((pos)>>3)
/*
****************************************************************************
* Board representation and main varians *
****************************************************************************
*/
/* Board representation */
int piece[64] = { ROOK, KNIGHT,BISHOP,QUEEN, KING, BISHOP,KNIGHT,ROOK,
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN,
ROOK, KNIGHT,BISHOP,QUEEN, KING, BISHOP,KNIGHT,ROOK};
int color[64] = { BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK,
BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK, BLACK,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE,
WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE};
int side; /* side to move, value = BLACK or WHITE */
/* For move generation */
#define MOVE_TYPE_NONE 0
#define MOVE_TYPE_NORMAL 1
#define MOVE_TYPE_CASTLE 2
#define MOVE_TYPE_PROMOTION_TO_QUEEN 3
#define MOVE_TYPE_PROMOTION_TO_ROOK 4
#define MOVE_TYPE_PROMOTION_TO_BISHOP 5
#define MOVE_TYPE_PROMOTION_TO_KNIGHT 6
struct MOVE { int from, dest, type; };
/* For storing all moves of game */
struct HIST { MOVE m; int cap; };
HIST hist[1000]; /* Game length < 1000 */
/*
BUGBUG: DRC
The maximum legal game length is just under 6000 moves. Therefore, I don't like
the magic number 1000,even though everyone does this. It's not like we're still
on PDP 11/70's. Make it 6000 and waste 1/100 of one cent's worth of RAM.
*/
int hdp; /* Current move order */
/* For searching */
int nodes; /* Count all visited nodes when searching */
int ply; /* ply of search */
/*
****************************************************************************
* Move generator *
* Lack: no enpassant, no castle *
****************************************************************************
*/
/*
BUGBUG: DRC
The minimum chess game can make e.p. captures and castle. If you cannot do
that, you are playing some other game besides chess. It's like writing a
checkers game where the checkers cannot turn into kings. The primary rules of
the game should be included.
Also, 50 move and 3x repeat are pretty basic and pretty important. If your
program does not understand these rules, it will annoy people.
*/
/*
It is obvious wher you are doing here. But if this is supposed to be for the
raw boned beginner, include an explanation of what it does
*/
void Gen_Push(int from, int dest, int type, MOVE* pBuf, int* pMCount)
{
MOVE move;
move.from = from; move.dest = dest; move.type = type;
pBuf[*pMCount] = move; *pMCount = *pMCount + 1;
}
void Gen_PushNormal(int from, int dest, MOVE* pBuf, int* pMCount)
{
Gen_Push(from, dest, MOVE_TYPE_NORMAL, pBuf, pMCount);
}
/* Pawn can promote */
void Gen_PushPawn(int from, int dest, MOVE* pBuf, int* pMCount)
{
/*
BUGBUG: DRC
It is obvious that the 7 and 56 are to limit pawns to the 2nd through 7th ranks.
Will this be obvious to someone who has never sen the guts of a chess program?
*/
if (dest>7 && dest<56) Gen_Push(from, dest, MOVE_TYPE_NORMAL, pBuf, pMCount);
else {
Gen_Push(from, dest, MOVE_TYPE_PROMOTION_TO_QUEEN, pBuf, pMCount);
Gen_Push(from, dest, MOVE_TYPE_PROMOTION_TO_ROOK, pBuf, pMCount);
Gen_Push(from, dest, MOVE_TYPE_PROMOTION_TO_BISHOP, pBuf, pMCount);
Gen_Push(from, dest, MOVE_TYPE_PROMOTION_TO_KNIGHT, pBuf, pMCount);
}
}
/* Gen all moves of side to move and push them to pBuf, return number of moves
*/
int Gen(int side, MOVE* pBuf)
{
int i, k, y, row, col, movecount;
movecount = 0;
/* Scan all board */
for (i=0; i<64; i++)
if (color[i]==side) {
switch (piece[i]) {
case PAWN:
col = COL(i); row = ROW(i);
if (side==BLACK) {
/*
BUGBUG: DRC
8 means next rank. Might be nice to spell it out or have a macro.
*/
if (color[i+8]==EMPTY) Gen_PushPawn(i, i+8, pBuf, &movecount);
/*
BUGBUG: DRC
16 is for the initial 2 square advancement... Why not spell it out?
*/
if (row==1 && color[i+8]==EMPTY && color[i+16]==EMPTY) Gen_PushNormal(i,
i+16, pBuf, &movecount);
/*
BUGBUG: DRC
Now we are doing captures. How about telling why 7 and 9 mean the obvious pawn
diagonal capture squares somewhere?
*/
if (col && color[i+7]==WHITE) Gen_PushNormal(i, i+7, pBuf, &movecount);
if (col<7 && color[i+9]==WHITE) Gen_PushNormal(i, i+9, pBuf, &movecount);
} else {
if (color[i-8]==EMPTY) Gen_PushPawn(i, i-8, pBuf, &movecount);
if (row==6 && color[i-8]==EMPTY && color[i-16]==EMPTY) Gen_PushNormal(i,
i-16, pBuf, &movecount);
if (col && color[i-9]==BLACK) Gen_PushNormal(i, i-9, pBuf, &movecount);
if (col<7 && color[i-7]==BLACK) Gen_PushNormal(i, i-7, pBuf, &movecount);
}
break;
case QUEEN: /* == BISHOP+ROOK */
case BISHOP:
/*
BUGBUG: DRC
Might be nice to rename the 9 as DIAG_PLUS or something.
*/
for (y=i-9; y>=0 && COL(y)!=7; y-=9) { /* go left up */
if (color[y]!=side) Gen_PushNormal(i, y, pBuf, &movecount);
if (color[y]!=EMPTY) break;
}
/*
BUGBUG: DRC
See above
*/
for (y=i-7; y>=0 && COL(y)!=0; y-=7) { /* go right up */
if (color[y]!=side) Gen_PushNormal(i, y, pBuf, &movecount);
if (color[y]!=EMPTY) break;
}
for (y=i+9; y<64 && COL(y)!=0; y+=9) { /* go right down */
if (color[y]!=side) Gen_PushNormal(i, y, pBuf, &movecount);
if (color[y]!=EMPTY) break;
}
for (y=i+7; y<64 && COL(y)!=7; y+=7) { /* go right down */
if (color[y]!=side) Gen_PushNormal(i, y, pBuf, &movecount);
if (color[y]!=EMPTY) break;
}
if (piece[i]==BISHOP)
break;
case ROOK:
for (k=i-COL(i),y=i-1; y>=k; y--) { /* go left */
if (color[y]!=side) Gen_PushNormal(i, y, pBuf, &movecount);
if (color[y]!=EMPTY) break;
}
for (k=i-COL(i)+7,y=i+1; y<=k; y++) { /* go right */
if (color[y]!=side) Gen_PushNormal(i, y, pBuf, &movecount);
if (color[y]!=EMPTY) break;
}
for (y=i-8; y>=0; y-=8) { /* up */
if (color[y]!=side) Gen_PushNormal(i, y, pBuf, &movecount);
if (color[y]!=EMPTY) break;
}
for (y=i+8; y<64; y+=8) { /* down */
if (color[y]!=side) Gen_PushNormal(i, y, pBuf, &movecount);
if (color[y]!=EMPTY) break;
}
break;
case KNIGHT:
/*
BUGBUG: DRC
How about some brief prose explaining why these are the knight target squares.
Even a knight on a 5x5 grid with some offsets would be nice...
*/
col = COL(i); row = ROW(i);
y = i-6 ; if (y>=0 && col<6 && color[y]!=side) Gen_PushNormal(i, y, pBuf,
&movecount);
y = i-10; if (y>=0 && col>1 && color[y]!=side) Gen_PushNormal(i, y, pBuf,
&movecount);
y = i-15; if (y>=0 && col<7 && color[y]!=side) Gen_PushNormal(i, y, pBuf,
&movecount);
y = i-17; if (y>=0 && col>0 && color[y]!=side) Gen_PushNormal(i, y, pBuf,
&movecount);
y = i+6 ; if (y<64 && col>1 && color[y]!=side) Gen_PushNormal(i, y, pBuf,
&movecount);
y = i+10; if (y<64 && col<6 && color[y]!=side) Gen_PushNormal(i, y, pBuf,
&movecount);
y = i+15; if (y<64 && col>0 && color[y]!=side) Gen_PushNormal(i, y, pBuf,
&movecount);
y = i+17; if (y<64 && col<7 && color[y]!=side) Gen_PushNormal(i, y, pBuf,
&movecount);
break;
case KING:
/*
BUGBUG: DRC
Explain that the column and rank checks are to make sure it is on the board
perhaps.
*/
if (COL(i) && color[i-1]!=side) Gen_PushNormal(i, i-1, pBuf,
&movecount); /* left */
if (COL(i)<7 && color[i+1]!=side) Gen_PushNormal(i, i+1, pBuf,
&movecount); /* right */
if (i>7 && color[i-8]!=side) Gen_PushNormal(i, i-8, pBuf, &movecount); /*
up */
if (i<56 && color[i+8]!=side) Gen_PushNormal(i, i+8, pBuf,
&movecount); /* down */
if (COL(i) && i>7 && color[i-9]!=side) Gen_PushNormal(i, i-9, pBuf,
&movecount); /* left up */
if (COL(i)<7 && i>7 && color[i-7]!=side) Gen_PushNormal(i, i-7, pBuf,
&movecount); /* right up */
if (COL(i) && i<56 && color[i+7]!=side) Gen_PushNormal(i, i+7, pBuf,
&movecount); /* left down */
if (COL(i)<7 && i<56 && color[i+9]!=side) Gen_PushNormal(i, i+9, pBuf,
&movecount); /* right down */
break;
}
}
return movecount;
}
/*
****************************************************************************
* Evaluation for current position *
* Lack: almost no knowlegde *
****************************************************************************
*/
int Eval()
{
/*
BUGBUG: DRC
You have those lovely macros at the top of the program, and ere you hard code
them.
tsk, tsk, tks.
*/
int value_piece[6] = { 100, 300, 300, 500, 900, 10000};
int i, score = 0;
for(i=0; i<64; i++) {
if (color[i]==WHITE) score += value_piece[piece[i]];
else
if (color[i]==BLACK) score -= value_piece[piece[i]];
}
if (side==WHITE) return score;
return -score;
}
/*
****************************************************************************
* Make and Take back a move, IsInCheck *
****************************************************************************
*/
/* Check and return 1 if side is in check */
int IsInCheck(int side)
{
/*
BUGBUG: DRC
As above, this routine is sprinkled with magic numbers.
Chess coders tend to code that way. It would never pass a real code review.
*/
int k, h, y, row, col, xside;
xside = (WHITE+BLACK)-side; /* opposite side, who may be checking */
/* Find king */
for(k=0; k<64; k++)
if (piece[k]==KING && color[k]==side) break;
row = ROW(k), col = COL(k);
/* Knight check */
if (col>0 && row>1 && color[k-17]==xside && piece[k-17]==KNIGHT) return 1;
if (col<7 && row>1 && color[k-15]==xside && piece[k-15]==KNIGHT) return 1;
if (col>1 && row>0 && color[k-10]==xside && piece[k-10]==KNIGHT) return 1;
if (col<6 && row>0 && color[k-6]==xside && piece[k-6]==KNIGHT) return 1;
if (col>1 && row<7 && color[k+6]==xside && piece[k+6]==KNIGHT) return 1;
if (col<6 && row<7 && color[k+10]==xside && piece[k+10]==KNIGHT) return 1;
if (col>0 && row<6 && color[k+15]==xside && piece[k+15]==KNIGHT) return 1;
if (col<7 && row<6 && color[k+17]==xside && piece[k+17]==KNIGHT) return 1;
/* Check horizontal and vertical lines for attacking of QUEEN, ROOK, KING */
/* go down */
y = k+8;
if (y<64) {
if (color[y]==xside && (piece[y]==KING || piece[y]==QUEEN || piece[y]==ROOK))
return 1;
if (piece[y]==EMPTY)
for(y+=8; y<64; y+=8) {
if (color[y]==xside && (piece[y]==QUEEN || piece[y]==ROOK)) return 1;
if (piece[y]!=EMPTY) break;
}
}
/* go left */
y = k-1; h = k-col;
if (y>=h) {
if (color[y]==xside && (piece[y]==KING || piece[y]==QUEEN || piece[y]==ROOK))
return 1;
if (piece[y]==EMPTY)
for(y--; y>=h; y--) {
if (color[y]==xside && (piece[y]==QUEEN || piece[y]==ROOK)) return 1;
if (piece[y]!=EMPTY) break;
}
}
/* go right */
y=k+1; h = k-col+7;
if (y<=h) {
if (color[y]==xside && (piece[y]==KING || piece[y]==QUEEN || piece[y]==ROOK))
return 1;
if (piece[y]==EMPTY)
for(y++; y<=h; y++) {
if (color[y]==xside && (piece[y]==QUEEN || piece[y]==ROOK)) return 1;
if (piece[y]!=EMPTY) break;
}
}
/* go up */
y = k-8;
if (y>=0) {
if (color[y]==xside && (piece[y]==KING || piece[y]==QUEEN || piece[y]==ROOK))
return 1;
if (piece[y]==EMPTY)
for(y-=8; y>=0; y-=8) {
if (color[y]==xside && (piece[y]==QUEEN || piece[y]==ROOK)) return 1;
if (piece[y]!=EMPTY) break;
}
}
/* Check diagonal lines for attacking of QUEEN, BISHOP, KING, PAWN */
/* go right down */
y = k+9;
if (y<64 && COL(y)!=0) {
if (color[y]==xside) {
if (piece[y]==KING || piece[y]==QUEEN || piece[y]==BISHOP) return 1;
if (side==BLACK && piece[y]==PAWN) return 1;
}
if (piece[y]==EMPTY)
for (y+=9; y<64 && COL(y)!=0; y+=9) {
if (color[y]==xside && (piece[y]==QUEEN || piece[y]==BISHOP)) return 1;
if (piece[y]!=EMPTY) break;
}
}
/* go left down */
y = k+7;
if (y<64 && COL(y)!=7) {
if (color[y]==xside) {
if (piece[y]==KING || piece[y]==QUEEN || piece[y]==BISHOP) return 1;
if (side==BLACK && piece[y]==PAWN) return 1;
}
if (piece[y]==EMPTY)
for(y+=7; y<64 && COL(y)!=7; y+=7) {
if (color[y]==xside && (piece[y]==QUEEN || piece[y]==BISHOP)) return 1;
if (piece[y]!=EMPTY) break;
}
}
/* go left up */
y = k-9;
if (y>=0 && COL(y)!=7) {
if (color[y]==xside) {
if (piece[y]==KING || piece[y]==QUEEN || piece[y]==BISHOP) return 1;
if (side==WHITE && piece[y]==PAWN) return 1;
}
if (piece[y]==EMPTY)
for(y-=9; y>=0 && COL(y)!=7; y-=9) {
if (color[y]==xside && (piece[y]==QUEEN || piece[y]==BISHOP)) return 1;
if (piece[y]!=EMPTY) break;
}
}
/* go right up */
y = k-7;
if (y>=0 && COL(y)!=0) {
if (color[y]==xside) {
if (piece[y]==KING || piece[y]==QUEEN || piece[y]==BISHOP) return 1;
if (side==WHITE && piece[y]==PAWN) return 1;
}
if (piece[y]==EMPTY)
for(y-=7; y>=0 && COL(y)!=0; y-=7) {
if (color[y]==xside && (piece[y]==QUEEN || piece[y]==BISHOP)) return 1;
if (piece[y]!=EMPTY) break;
}
}
return 0;
}
int MakeMove(MOVE m)
{
/*
KUDOS: DRC
EXCELLENT! You have handled underpromotion.
*/
hist[hdp].m = m; hist[hdp].cap = piece[m.dest];
piece[m.dest] = piece[m.from]; piece[m.from] = EMPTY;
color[m.dest] = color[m.from]; color[m.from] = EMPTY;
if (m.type>=MOVE_TYPE_PROMOTION_TO_QUEEN) { /* Promotion */
switch (m.type) {
case MOVE_TYPE_PROMOTION_TO_QUEEN: piece[m.dest] = QUEEN; break;
case MOVE_TYPE_PROMOTION_TO_ROOK: piece[m.dest] = ROOK; break;
case MOVE_TYPE_PROMOTION_TO_BISHOP: piece[m.dest] = BISHOP; break;
case MOVE_TYPE_PROMOTION_TO_KNIGHT: piece[m.dest] = KNIGHT; break;
}
}
ply++; hdp++;
int r = !IsInCheck(side);
side = (WHITE+BLACK)-side; /* After making move, give turn to opponent */
return r;
}
void TakeBack()
{
/*
BUGBUG: DRC
side = !side seems a lot simpler.
*/
side = (WHITE+BLACK)-side;
hdp--; ply--;
piece[hist[hdp].m.from] = piece[hist[hdp].m.dest]; piece[hist[hdp].m.dest] =
hist[hdp].cap;
color[hist[hdp].m.from] = side;
if (hist[hdp].cap!=EMPTY) color[hist[hdp].m.dest] = (WHITE+BLACK)-side;
else color[hist[hdp].m.dest] = EMPTY;
if (hist[hdp].m.type>=MOVE_TYPE_PROMOTION_TO_QUEEN) /* Promotion */
piece[hist[hdp].m.from] = PAWN;
}
/*
****************************************************************************
* Search function - a typical alphabeta *
* Lack: no any technique for move ordering *
****************************************************************************
*/
int Search(int alpha, int beta, int depth, MOVE* pBestMove)
{
int i, value;
bool havemove;
nodes++;
/*
BUGBUG: DRC
Magic number of 200 -- justify it.
*/
MOVE moveBuf[200], tmpMove;
int movecnt = Gen(side, moveBuf);
havemove = false;
pBestMove->type = MOVE_TYPE_NONE;
/* loop through the moves */
for (i = 0; i < movecnt; ++i) {
if (!MakeMove(moveBuf[i])) {
TakeBack();
continue;
}
havemove = true;
if (depth-1>0)
value = -Search(-beta, -alpha, depth - 1, &tmpMove);
else
value = Eval();
TakeBack();
if (value > alpha) {
/* this move is so good and caused a cutoff */
if (value >= beta)
return beta;
alpha = value;
*pBestMove = moveBuf[i];
}
}
if (!havemove) { /* If no legal moves, that is checkmate or stalemate */
if (IsInCheck(side))
/*
BUGBUG: DRC
You have the 10,000 encoded as a macro above. Use it.
Better yet, change it to 32767, so it will look somewhat like a checkmate.
*/
return -10000 + ply;
else
return 0;
}
return alpha;
}
MOVE ComputerThink(int max_depth)
{
MOVE m;
ply = 0; nodes = 0;
int score = Search(-MATE, MATE, max_depth, &m);
printf("Search result: move = %c%d%c%d; nodes = %d, score =
%d\n",'a'+COL(m.from), 8-ROW(m.from), 'a'+COL(m.dest), 8-ROW(m.dest), nodes,
score);
return m;
}
/*
****************************************************************************
* Utilities *
****************************************************************************
*/
void PrintBoard()
{
char pieceName[] = "PNBRQKpnbrqk";
for(int i=0; i<64; i++) {
if((i&7)==0) {
printf(" +---+---+---+---+---+---+---+---+\n");
if (i<=56) {
printf(" %d |", 8-(i>>3));
}
}
if (piece[i]==EMPTY) printf(" |");
else {
printf(" %c |", pieceName[piece[i]+ (color[i]==WHITE? 0 : 6)]);
}
if ((i&7)==7) printf("\n");
}
printf(" +---+---+---+---+---+---+---+---+\n a b c d e f g
h\n");
}
/*
****************************************************************************
* Main program *
****************************************************************************
*/
void main()
/*
BUGBUG: DRC
Grr....
main returns int.
*/
{
char s[256];
int from, dest, i;
int computer_side;
int max_depth; /* max depth to search */
/*
BUGBUG: DRC
magic number of 200. Justify it.
You should have an assert everywhere moveBuf is used to prove tht the maximum
can never be exceeded.
*/
MOVE moveBuf[200];
int movecnt;
printf("First Chess, by Pham Hong Nguyen\n");
printf("Help\n d: display board\n MOVE: make a move (e.g. b1c3, a7a8q)\n quit:
exit\n\n");
side = WHITE;
computer_side = BLACK; /* Human is white side */
max_depth = 4;
hdp = 0;
for (;;) {
if (side==computer_side) { /* computer's turn */
/* Find out the best move to react the current position */
MOVE bestMove = ComputerThink(max_depth);
MakeMove(bestMove);
continue;
}
/* get user input */
printf("fc> ");
if (scanf("%s", s) == EOF)
return;
if (!strcmp(s, "d")) {
PrintBoard();
continue;
}
if (!strcmp(s, "quit")) {
printf("Good bye!\n");
return;
}
/* maybe the user entered a move? */
from = s[0] - 'a';
from += 8 * (8 - (s[1] - '0'));
dest = s[2] - 'a';
dest += 8 * (8 - (s[3] - '0'));
ply = 0;
movecnt = Gen(side, moveBuf);
/* loop through the moves to see if it's legal */
for (i = 0; i < movecnt; i++)
if (moveBuf[i].from==from && moveBuf[i].dest==dest) {
if (piece[from]==PAWN && (dest<8||dest>55)) { /* Promotion move? */
switch (s[4]) {
case 'q': moveBuf[i].type = MOVE_TYPE_PROMOTION_TO_QUEEN; break;
case 'r': moveBuf[i].type = MOVE_TYPE_PROMOTION_TO_ROOK; break;
case 'b': moveBuf[i].type = MOVE_TYPE_PROMOTION_TO_BISHOP; break;
case 'n': moveBuf[i].type = MOVE_TYPE_PROMOTION_TO_KNIGHT; break;
}
}
if (!MakeMove(moveBuf[i])) {
TakeBack();
printf("Illegal move.\n");
}
break;
}
}
}
/*
General comments:
This is very well done.
I would suggest that you can cut the code size nearly in half in some routines
by generalizing for the side to move.
Clean up the magic numbers and it will be a lot cleaner.
Now for a correctness check (he feels an evil laugh welling up inside)
*/
This page took 0 seconds to execute
Last modified: Thu, 15 Apr 21 08:11:13 -0700
Current Computer Chess Club Forums at Talkchess. This site by Sean Mintz.