Advertisement

How to program a chess: a forum based tutorial

Started by December 23, 2004 11:09 PM
276 comments, last by da_grat1 19 years, 8 months ago
Yo guys... This tutorial is still ALIVE~~~

A new tutorial will come very soon..
llvllatrix.. Is it possible to finish the bottom layer within this week...?
Advertisement
Two more subsystems to go and they will be out in this tutorial.
2 more tutorials.. will that be long..?
can finish within this week...?
If so you are super fast.. lol..really hope for that :)
does the two subsystems include the moving piece..?hoping for this one too.. :)
Hey guys,

Here’s a bit of an update. I've actually been parsing the code, making a few changes to some of the subsystems to make it easier to preload them. Now that this is over with, I'm onto researching the Beowulf chess engine. I encourage you to download the source and take a look for yourself. Unlike the earlier subsystems we have wrapped, this one does not come in a nice dll package so we have our work cut out for us.

We have two options; we can either integrate the Beowulf source directly into our game or we can create our own beowulf dll. Integrating the source will make our code very messy. Do you remember how I said consistency was important? Throwing in something that uses a completely different style into our source is a very bad idea; its a maintenance nightmare. So we are going to be using the beowulf source to make a dll.

The process is actually quite simple, just tedious. What we have to do is take apart the main.c file in the beowulf source and replace it with a tidy interface that will work with our code. In short; we are going to wrap it and the interface style we will be using is hopefully going to end up similar to OpenGL.

How exactly does the Beowulf game engine work? It behaves like message queue. However, instead of using messages, the engine uses a single state. The state tells the engine whose turn it is ect. We are going to want to make this state structure into a class so that we can reuse the functionality in other areas of the game like changing the states of our in game ui.

So, I'm off to snuggle up with a warm cup of hot chocolate and some beowulf code and I suggest you do the same :) I also have some other good news. I've been talking to my publisher at Charles River Media (the guys who do http://www.gameprogramminggems.com/) and we may be able to get these tutorials published in a text! Currently, I'm working on a proposal for the publisher. I'll keep the info coming as it becomes available to me.

Cheers,
- llvllatrix
Quote: Do you remember how I said consistency was important? Throwing in something that uses a completely different style into our source is a very bad idea; its a maintenance nightmare. So we are going to be using the beowulf source to make a dll.


Yeah.. I do.... :) so that means we need a plugin to change the source code to .dll...
I have downloaded the beowulf source code... It's so confusing.. it is making me dizzy.. I really need someone to explain about it later on.. :)

Just want to ask, is the AI engine the only thing left...
that means after that, we will be able to play the game.. is it?

By the way.. when are you going to upload the latest source code... i really want to see it and test it...
Advertisement
Quote:
I have downloaded the beowulf source code... It's so confusing.. it is making me dizzy.. I really need someone to explain about it later on.. :)


lol; I was thinking the same thing. Its almost unreadable...Hopefully, after I'm done we wont have to look at it ever again.

Quote:
Just want to ask, is the AI engine the only thing left...
that means after that, we will be able to play the game.. is it?


Yup; We will be able to play chess with the next source release. Also, Beowulf supports player vs player, player vs computer or computer vs computer so we'll have a really flexible game when we're done.

Cheers,
- llvllatrix
Hey guys,

I thought I would release the current version of the source code while i work on the Beowulf stuff. Its similar to the previous release except with a few changes. No major functionality was added but quite a bit of structure was setup. Note that there are two load phases; preload and load. During preload, everything that can be loaded before a window is created is loaded. During the load phase (which currently does not exist in the basecode) the window is created and our preloaded resources are rendered to OpenGL. Heres a list of the changes:

- divided texture loading into two parts so that textures can be read off the disk and stored in memory until they can be sent to opengl; this makes preloading easier
- percolated changes throughout the rest of the resource subsystem
- added two more slices (a collection of subsystems); the rout slice and the game slice
- the rout slice is responsible for routing the processing flow of our application. It contains the application entry point, does the preloading, does the loading, calls the game's functions and then unloads all the systems. If the game were a pc then this would be the motherboard.
- the game slice contains all of our game specific information. You'll notice that I've extracted some of our resource code from the resource slice and moved it here. I want to keep everything specific to chess in this subsystem so that we can easily use our engine to write other games.

You can find the new source at (note that its a fairly rough cut; the only thing i've updated is the gl_basecode. You'll also have to Install SDL to get this cut to work; Instructions in inc_sdl.h.):
http://www.eng.uwaterloo.ca/~rramraj/Gamedev/chess_tutorial/src_tree/chess_src.zip

My next steps are to create the Beowulf dll. Basically I'll be taking the function calls that compose beowulf's main function and wrapping them for use in our game. This will be done separately from our current source tree and will consist of the code which produces our beowulf dll and an ascii test app which uses the dll. I should be finished by tomorrow and I'll have a tutorial out then.

For those interested, while working on understanding the Beowulf source, I spent a bit of time making the main.c file easier to read. A bit of proper indenting goes a long way :) Here are the results:

/********************************** *    main.c                      * *    Colin Frayn                 * *    March 2001                  * **********************************//* This file contains all the main control structure for the program, initialises and runs the state machine, and handles loading of data etc...*/  #include <stdlib.h>#include <stdio.h>#include <math.h>#include <string.h>#include <ctype.h>#include <assert.h>#include "common.h"#include "defs.h"#include "params.h"#include "main.h"#include "parser.h"#include "checks.h"#include "comp.h"#include "computil.h"#include "board.h"#include "utils.h"#include "probe.h"#include "rand64.h"/*#define INITIAL_BOARD "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -"*/Board Current_Board;CompDat Params;void *TBBuffer;extern HashElt *TableW,*TableB;int mvno,automoves,testtime=5;BOOL XBoard=FALSE,LoadPGN=FALSE,Post=TRUE, UCI=FALSE;BOOL Computer[2]={FALSE,FALSE},ComputerGO = FALSE,AutoMove=FALSE,Ponder=FALSE, Force=FALSE;char TB_PATH[FILENAME_MAX]="TB";char BOOK_FILE[FILENAME_MAX]="book.dat";char PERSON_FILE[FILENAME_MAX]="default.per";char EPD_FILE[FILENAME_MAX]="";extern int InputFlag, GlobalAlpha, GlobalBeta;extern long int NHash;#ifdef BEOSERVERint BenchmarkSpeed;BOOL bParallel = TRUE;extern int TimeTaken;#endif/* The main() function - main control structure. */int main(int argc, char **argv) {	int state=S_NOP, oldmvno;	char input[FILENAME_MAX];	/* No buffering, please... */	setbuf(stdout, NULL);	setbuf(stdin, NULL);	/* and I *really* mean it, too! */	setvbuf(stdout, NULL, _IONBF, 0);	setvbuf(stdin, NULL, _IONBF, 0);	fflush(NULL); 	GetConfigFile();	GetEnvironmentVars();	if (!ParseCommandLine(argc,argv)) 	{	  return 0;	}	CheckParams();	fprintf(stdout,"Welcome to "NAME" Version "VER"!\n\n");#ifdef BEOSERVER  	fprintf(stdout,"**Chessbrain Server Version**\n");#endif // BEOSERVER	LoadOpeningBook();	LoadPersonalityFile();	SetupPrecalculatedData();	Defaults();	InitialiseTB();#ifdef BEOSERVER  	BenchmarkSpeed = Benchmark();#endif // BEOSERVER	if (strlen(EPD_FILE)>0) 	{		RunTestSuite(EPD_FILE,testtime);		return 0;	}	if (!XBoard) 	{		PrintBoard(Current_Board);	}	if (!XBoard) 	{		Prompt();	}	do 	{		oldmvno = mvno;		if (state == S_NOP) 		{			(void)fgets(input,FILENAME_MAX,stdin);			if (feof(stdin)) 			{				state = S_QUIT;			}			else 			{				state=ParseInput(input);			}		}		if (state == S_SEARCH) 		{			if (Comp()==-1)			{				ComputerGO = FALSE;			}			if (oldmvno != mvno && InputFlag==INPUT_NULL) 			{				state = S_CHKDRW;			}			else if (InputFlag==INPUT_NULL) 			{				state = S_NOP;			}			if (InputFlag==INPUT_STOP) 			{				ParseInput("FORCE");				state = S_NOP;			}		}		if (state == S_AUTO)   		{			AutoMove=TRUE;			(void)Comp();			AutoMove=FALSE;						if (CheckForDraw()>0 || --automoves==0) 			{				state=S_NOP;			}		}		if (state == S_CHKDRW) 		{			(void)CheckForDraw();			state=S_NOP;		}		if (state != S_QUIT && state != S_AUTO && !XBoard) 		{			Prompt();		}		if (Computer[Current_Board.side]==1 && ComputerGO && state != S_AUTO && !Force)		{			if (!XBoard) 			{				fprintf(stdout,"\n");			}			state=S_SEARCH;		}		if (state == S_NOP && XBoard && Ponder && !Force) 		{			PonderPosition();		}	} while (state!=S_QUIT);	FreeOpenings();	ResetHash();	return 0;}#ifdef BEOSERVER/* Benchmark the computer using the opening position to 9 ply. * Return the time taken in centiseconds. */int Benchmark(void) {	fprintf(stdout,"Running Standard BeoServer Benchmark\n");	bParallel = FALSE;	ParseInput("BOOK");	ParseInput("RESET");	ParseInput("ST 0");	ParseInput("DEPTH 9");	(void)Comp();	ParseInput("BOOK");	bParallel = TRUE;	fprintf(stdout,"Benchmark Result : %d\n",TimeTaken);	return TimeTaken;}#endif/* Setup the precalculated bitboards that we're going to need * later on in the game */void SetupPrecalculatedData(void) {	BITBOARD rank=FullRank,mask,mask2;	int i,j,x,y,rankno,filenum,diagstart,dsfile,dl,fi,dx,dy;	/* seed the 64-bit psuedo-random number generator */	randinit(TRUE);	/* Rank and File Masks */	FullBoard=0;	for (i=0;i<8;i++) 	{		FileMask = 0;				for (j=i;j<64;j+=8) 		{			FileMask += (UNIT<<j);		}		RankMask = (rank << (8*i));		FullBoard += RankMask;	}	/* Masks for the FirstPiece algorithm */	FPMask1 = (BITBOARD)TwoFullRanks << 16;	FPMask2 = (BITBOARD)TwoFullRanks << 32;	/* Masks for the squares on a file above (or below) the specified square */	for (i=0;i<64;i++) 	{		FileUpMask = FileDownMask = 0;				for (j=i-8;j>=0;j-=8) 		{			FileUpMask += (UNIT << j);		}		for (j=i+8;j<64;j+=8)		{			FileDownMask += (UNIT << j);		}	}	/* A mask with 1's in all the white squares, and the same for black */	BlackMask = WhiteMask = 0;	for (i=0;i<64;i++) 	{		if (IsWhite(i))		{			WhiteMask += (UNIT << i);		}		else		{			BlackMask += (UNIT << i);		}	}	/* Masks for the half ranks/files/diagonals from the square, like	* FileUpMask & FileDownMask, but in different directions. */	for (i=0;i<64;i++) 	{		DirL=DirR=DirUR=DirUL=DirDR=DirDL=0;		for (j=i-1;File(j)<File(i);j--) 		{			DirL += (UNIT<<j);		}		for (j=i+1;File(j)>File(i);j++) 		{			DirR += (UNIT<<j);		}		for (j=i-9;File(j)<File(i) && j>=0;j-=9) 		{			DirUL += (UNIT<<j);		}		for (j=i-7;File(j)>File(i) && j>=0;j-=7) 		{			DirUR += (UNIT<<j);		}		for (j=i+7;File(j)<File(i) && j<64;j+=7) 		{			DirDL += (UNIT<<j);		}		for (j=i+9;File(j)>File(i) && j<64;j+=9) 		{			DirDR += (UNIT<<j);		}	}	/* Bonuses for centre proximity and avoidance */	for (i=0;i<64;i++) 	{		CentreBoard=0;		dx = (2*File(i) - 7);		dy = (2*Rank(i) - 7);		CentreBoard = 10 - (int)sqrt((double)(dx*dx + dy*dy));		CentreBoard2 = CentreBoard*2;		CornerBoard2 = CornerBoard*2;		CornerBoard3 = CornerBoard*3;	}	/* Square Masks */	for (i=0;i<64;i++) 	{		Mask = (UNIT<<i);		InvMask = ~Mask;	}	/* King Safety Mask - all squares within 2 in x or y of the current sq */	for (i=0;i<64;i++) 	{		KingSafetyMask = EMPTY;		for (x=File(i)-2;x<=File(i)+2;x++) 		{			if (x<0 || x>7) 			{				continue;			}			for (y=Rank(i)-2;y<=Rank(i)+2;y++) 			{				if (y<0 || y>7) 				{					continue;				}				if ((y*8)+x == i) 				{					continue;				}				KingSafetyMask |= Mask[(y*8)+x];			}		}	}	/* Diagonal Masks */	for (i=0;i<64;i++) 	{		DiagMaska1h8 = DiagMaska8h1 = 0;				/* a1h8 direction first */		diagstart = 7*(min((File(i)),7-(Rank(i)))) + i;		for (j=0;j<DiagonalLength_a1h8;j++) 		{			DiagMaska1h8 += (UNIT << (diagstart - j*7));		}				/* a8h1 direction next */		diagstart = i - 9*(min((File(i)),(Rank(i))));		for (j=0;j<DiagonalLength_a8h1;j++) 		{			DiagMaska8h1 += (UNIT << (diagstart + j*9));		}	}	/* Setup inverse transformation matrices */	for (i=0;i<64;i++) 	{		InvRotateL45[(RotateL45)]=i;		InvRotateR45[(RotateR45)]=i;	}	/* Setup masks for diagonal sliding pieces */	for (i=0;i<64;i++) 	{		DiagonalMask_a1h8 = (1 << DiagonalLength_a1h8)-1;		DiagonalMask_a8h1 = (1 << DiagonalLength_a8h1)-1;	}	/* Ok this one is a little bizarre.  Basically they're	* bitboards with which I XOR the relevant rotated bitboards	* after a castling move to get the correct resultant board */	CastleMaskR90[0] = Mask[(RotateL90[e1])] | Mask[(RotateL90[g1])] | 					 Mask[(RotateL90)] | Mask[(RotateL90[f1])];	CastleMaskR90[1] = Mask[(RotateL90[e1])] | Mask[(RotateL90[c1])] | 					 Mask[(RotateL90[a1])] | Mask[(RotateL90[d1])];	CastleMaskR90[2] = Mask[(RotateL90[e8])] | Mask[(RotateL90[g8])] | 					 Mask[(RotateL90[h8])] | Mask[(RotateL90[f8])];	CastleMaskR90[3] = Mask[(RotateL90[e8])] | Mask[(RotateL90[c8])] | 					 Mask[(RotateL90[a8])] | Mask[(RotateL90[d8])];	CastleMaskR45[0] = Mask[(InvRotateR45[e1])] | Mask[(InvRotateR45[g1])] | 					 Mask[(InvRotateR45)] | Mask[(InvRotateR45[f1])];	CastleMaskR45[1] = Mask[(InvRotateR45[e1])] | Mask[(InvRotateR45[c1])] | 					 Mask[(InvRotateR45[a1])] | Mask[(InvRotateR45[d1])];	CastleMaskR45[2] = Mask[(InvRotateR45[e8])] | Mask[(InvRotateR45[g8])] | 					 Mask[(InvRotateR45[h8])] | Mask[(InvRotateR45[f8])];	CastleMaskR45[3] = Mask[(InvRotateR45[e8])] | Mask[(InvRotateR45[c8])] | 					 Mask[(InvRotateR45[a8])] | Mask[(InvRotateR45[d8])];	CastleMaskL45[0] = Mask[(InvRotateL45[e1])] | Mask[(InvRotateL45[g1])] | 					 Mask[(InvRotateL45)] | Mask[(InvRotateL45[f1])];	CastleMaskL45[1] = Mask[(InvRotateL45[e1])] | Mask[(InvRotateL45[c1])] | 					 Mask[(InvRotateL45[a1])] | Mask[(InvRotateL45[d1])];	CastleMaskL45[2] = Mask[(InvRotateL45[e8])] | Mask[(InvRotateL45[g8])] | 					 Mask[(InvRotateL45[h8])] | Mask[(InvRotateL45[f8])];	CastleMaskL45[3] = Mask[(InvRotateL45[e8])] | Mask[(InvRotateL45[c8])] | 					 Mask[(InvRotateL45[a8])] | Mask[(InvRotateL45[d8])];	/* And similarly, these are just a few masks I need later on to save a few ops ;) */	for (i=0;i<64;i++) 	{		MR90 = Mask[(RotateL90)];		IMR90 = InvMask[(RotateL90)];		MR45 = Mask[(InvRotateR45)];		IMR45 = InvMask[(InvRotateR45)];		ML45 = Mask[(InvRotateL45)];		IML45 = InvMask[(InvRotateL45)];	}	/* This one effectively flips the y-coord */	for (i=0;i<64;i++) 	{		Flip = ((7-Rank(i))*8) + File(i);	}	/* Setup a random key table for the hash function.	* The hash key is of type KeyType. */	for (i=0;i<64;i++) 	{		for (j=0;j<13;j++) 		{			RandomTable[j] = (KeyType)(Rand64());		}	}	/* Hash keys for castling and ep priorities */	for (i=0;i<16;i++) 	{		CastleKey = (KeyType)(Rand64());	}	/* Lookup tables for the first- & last- piece functions */	for (i=0;i<65536;i++) 	{		/* First piece in a two rank chunk */		for (j=0;j<16;j++) 		{			if (i&(1<<j)) 			{				first_piece = j; 				break;			}		}				/* Last piece in a two rank chunk */		for (j=15;j>=0;j--) 		{			if (i&(1<<j)) 			{				last_piece = j; 				break;			}		}	}	/* Pawn Moves */	for (i=0;i<64;i++) 	{		PawnAttacksBlack = PawnAttacksWhite = 0;		PawnMovesBlack = PawnMovesWhite = 0;		if (File(i)>0) 		{			if (i<a1) 			{				PawnAttacksBlack += (UNIT<<(i+7));			}			if (i>7)			{				PawnAttacksWhite += (UNIT<<(i-9));			}		}		if (File(i)<7) 		{			if (i<a1) 			{				PawnAttacksBlack += (UNIT<<(i+9));			}			if (i>7)			{				PawnAttacksWhite += (UNIT<<(i-7));			}		}		if (Rank(i)==0 || Rank(i)==7) 		{			continue;		}		PawnMovesBlack += (UNIT<<(i+8));		PawnMovesWhite += (UNIT<<(i-8));		if (Rank(i)==1) 		{			PawnMovesBlack+=(UNIT<<(i+16));		}		if (Rank(i)==6) 		{			PawnMovesWhite+=(UNIT<<(i-16));		}	}	/* Knight Moves */	for (i=0;i<64;i++) 	{		KnightMoves=0;		if (Rank(i)>0) 		{			if (Rank(i)>1) 			{				if (File(i)>0) 				{					KnightMoves += (UNIT<<(i-17)); 				}				if (File(i)<7) 				{					KnightMoves += (UNIT<<(i-15)); 				}			}			if (File(i)>1) 			{				KnightMoves += (UNIT<<(i-10));			}			if (File(i)<6) 			{				KnightMoves += (UNIT<<(i-6));			}		}				if (Rank(i)<7) 		{			if (Rank(i)<6) 			{				if (File(i)>0) 				{					KnightMoves += (UNIT<<(i+15)); 				}				if (File(i)<7) 				{					KnightMoves += (UNIT<<(i+17)); 				}			}			if (File(i)>1) 			{				KnightMoves += (UNIT<<(i+6));			}			if (File(i)<6) 			{				KnightMoves += (UNIT<<(i+10));			}		}	}	/* King Moves */	for (i=0;i<64;i++) 	{		KingMoves=0;		if (Rank(i)>0) 		{			if (File(i)>0) 			{				KingMoves += (UNIT<<(i-9));			}			if (File(i)<7) 			{				KingMoves += (UNIT<<(i-7));			}			KingMoves += (UNIT<<(i-8));		}		if (Rank(i)<7) 		{			if (File(i)>0) 			{				KingMoves += (UNIT<<(i+7));			}			if (File(i)<7) 			{				KingMoves += (UNIT<<(i+9));			}			KingMoves += (UNIT<<(i+8));		}		if (File(i)>0) 		{			KingMoves += (UNIT<<(i-1));		}		if (File(i)<7) 		{			KingMoves += (UNIT<<(i+1));		}	}	/* Sliding Piece Move Masks. Note that this includes being able to	* 'capture' friendly pieces and kings.  These are filtered out 	* later with an AND in the move generation routine.  Basically, 	* for each square on the board, generate every possible rank using	* 1 for occupied and 0 for empty.  This gives a list of all possible	* occupation states from 0-255 for that rank.  Then generate the	* possible move bitboard for each of these configurations.	*/	/* Horizontal Sliders */	for (filenum=0;filenum<8;filenum++) 	{		for (j=0;j<256;j++) 		{			mask=0;			for (x=filenum-1;x>=0;x--) 			{				mask += (UNIT<<x);				if (j & (1<<x)) 				{					break;				}			}			for (x=filenum+1;x<8;x++) 			{				mask += (UNIT<<x);				if (j & (1<<x)) 				{					break;				}			}			for (rankno=0;rankno<8;rankno++)			{				MovesRank[(rankno*8)+filenum][j] = mask<<(rankno*8);			}		}	}	/* Vertical Sliders */	for (rankno=0;rankno<8;rankno++) 	{		for (j=0;j<256;j++) 		{			mask=0;			for (x=6-rankno;x>=0;x--) 			{				mask += (UNIT<<(8*(7-x)));				if (j & (1<<x)) 				{					break;				}			}			for (x=8-rankno;x<8;x++) 			{				mask += (UNIT<<(8*(7-x)));				if (j & (1<<x)) 				{					break;				}			}			for (filenum=0;filenum<8;filenum++)			{				MovesFile[(rankno*8)+filenum][j] = mask << filenum;			}		}	}	/* Now here is where is gets rather nasty.	* Generate the possible moves for bishops (and queens) using	* 45 degrees rotated bitboards, both right and left rotated for	* the two perpendicular diagonal directions. A bit scary. */	/* a1h8 Diagonal Sense first. Note all diagonals are of	* different lengths, precalculated in common.h. */	for (i=0;i<64;i++) 	{		/* Get the far left hand square on this diagonal */		diagstart = 7*(min((File(i)),7-(Rank(i)))) + i;		dsfile = File(diagstart);		dl = DiagonalLength_a1h8;		fi = File(i);		/* Loop through all possible occupations of this diagonal line */		for (j=0 ; j < (1 << dl) ; j++) 		{			mask=mask2=0;			/* Calculate possible target squares */			for (x=(fi-dsfile)-1;x>=0;x--) 			{				mask += (UNIT<<x);				if (j & (1<<x))				{					break;				}			}			for (x=(fi-dsfile)+1;x<dl;x++) 			{				mask += (UNIT<<x);				if (j & (1<<x)) 				{					break;				}			}			/* Rotate the target line back onto the required diagonal */			for (x=0;x<dl;x++)			{				mask2 += ( ((mask >> x) & 1) << (diagstart-(7*x)) ) ;			}			Movesa1h8[j] = mask2;		}	}	/* a8h1 Diagonal Sense */	for (i=0;i<64;i++) 	{		/* Get the far left hand square on this diagonal */		diagstart = i - 9*(min((File(i)),(Rank(i))));		dsfile = File(diagstart);		dl = DiagonalLength_a8h1;		fi = File(i);		/* Loop through all possible occupations of this diagonal line */		for (j=0 ; j < (1 << dl) ; j++) 		{			mask=mask2=0;			/* Calculate possible target squares */			for (x=(fi-dsfile)-1;x>=0;x--) 			{				mask += (UNIT<<x);				if (j & (1<<x)) 				{					break;				}			}			for (x=(fi-dsfile)+1;x<dl;x++) 			{				mask += (UNIT<<x);				if (j & (1<<x)) 				{					break;				}			}			/* Rotate target squares back */			for (x=0;x<dl;x++)			{				mask2 += ( ((mask >> x) & 1) << (diagstart+(9*x)) ) ;			}			Movesa8h1[j] = mask2;		}	}	/* Finally, make sliding mask templates, or equivalent to	* all possible queen moves on an open board from this position */	for (i=0;i<64;i++) 	{		RookMask   = MovesRank[empty] | MovesFile[empty];		BishopMask = Movesa1h8[empty] | Movesa8h1[empty];		QueenMask  = RookMask | BishopMask;                 	}	/* Fill the array for piece values */	PieceValue100[pawn] = PAWN_SCORE;	PieceValue100[rook] = ROOK_SCORE;	PieceValue100[knight] = KNIGHT_SCORE;	PieceValue100[bishop] = BISHOP_SCORE;	PieceValue100[queen] = QUEEN_SCORE;	/* Distances between squares */	for (i=0;i<64;i++) 	{		for (j=0;j<64;j++) 		{			Distance[j] = max(abs(Rank(j)-Rank(i)),abs(File(j)-File(i)));		}	}	/* Masks for sections of the board */	/* Above Mask = All squares from this Rank upwards (inclusive) 	* Below Mask = All squares from this Rank downwards (inclusive) 	* Left Mask  = All squares from this Rank leftwards (inclusive) 	* Right Mask = All squares from this Rank rightwards (inclusive) */	for (i=0;i<8;i++) 	{		AboveMask = EMPTY;		BelowMask = EMPTY;		LeftMask = EMPTY;		RightMask = EMPTY;		for (j=0;j<i;j++) 		{			AboveMask += RankMask[j];			LeftMask += FileMask[j];		}		for (j=7;j>i;j--) 		{			BelowMask += RankMask[j];			RightMask += FileMask[j];		}	}	/* The four board quartiles */	WhiteKingSide = BelowMask[3] & RightMask[3];	WhiteQueenSide = BelowMask[3] & LeftMask[4];	BlackKingSide = AboveMask[4] & RightMask[3];	BlackQueenSide = AboveMask[4] & LeftMask[4];	/* Get the Gamestage lookup */	for (i=0;i<256;i++) 	{		Gamestage = GetGamestage(i);	}	/* Doubled Pawns score based on number of pawns on the board */	for (i=0;i<17;i++)	{		DoubledPawns = (DoubledPawns*DOUBLED_PAWNS)/10;	}	/* Squares surrounding the king */	for (i=0;i<64;i++) 	{		j=0;		for (y = Rank(i)-2; y <= Rank(i)+2; y++) 		{			if (y<0) 			{				continue;			}			if (y>7) 			{				break;			}			for (x = File(i)-2; x <= File(i)+2; x++) 			{				if (x<0) 				{					continue;				}				if (x>7) 				{					break;				}				if (x==File(i) && y==Rank(i)) 				{					continue; 				}				if (abs(File(i)-x) + abs(Rank(i)-y) == 4) 				{					continue;				}				KingArea[j++] = (y*8) + x;			}			KingArea[j] = -1;		}	}	/* Double knight moves	* This is a bit complicated */	for (i=0;i<64;i++) 	{		mask = KnightMoves;		DoubleKnightMove = mask;		while (mask) 		{			j = FirstPiece(mask);			DoubleKnightMove |= KnightMoves[j];			RemoveFirst(mask);		}	}	/* Square control weightings based on game stage and rank */	for (j=0;j<6;j++) 	{		for (i=0;i<64;i++) 		{			x = 12;			if (j==2)			{				x=8;			}			if (j==3) 			{				x=4;			}			if (Rank(i)==7) 			{				x-=2;			}			if (Rank(i)==6) 			{				x--;			}			if (Rank(i)==4 || Rank(i)==0) 			{				x++;			}			if (Rank(i)==3 || Rank(i)==1) 			{				x+=2;			}			if (Rank(i)==2) 			{				x+=3;			}			if (File(i)==0 || File(i)==7) 			{				x--;			}			if (File(i)==2 || File(i)==5) 			{				x++;			}			if (File(i)==3 || File(i)==4) 			{				x+=2;			}			if (j>=4) 			{				x=0;			}			SpaceWon[j] = (SPACE_WON*x)/12;			SpaceDefended[j] = (SPACE_DEFENDED*x)/12;		}	}	/* A board with +1 if a square is white, and -1 if it is black */	for (i=0;i<64;i++) 	{		if (IsWhite(i)) 		{			SquareColour=1;		}		else 		{			SquareColour=-1;		}	}	/* Pawn Positional advantages based on gamestage */	for (j=0;j<6;j++) 	{		for (i=0;i<64;i++) 		{			PawnPosition[j] = PawnPositionI;			/* Scale down pawn advancement in the opening */			if (j==0 && Rank(i)<Rank4) 			{				PawnPosition[j] = (PawnPositionI / 5);			}			if (j==1 && Rank(i)<Rank4) 			{				PawnPosition[j] = (PawnPositionI / 2);			}			PawnPositionPenalty[j] = PawnPosition[j]/2;		}	}	/* Penalty for a side attack based on gamestage */	SideAttack[5] = SideAttack[4] = 0;	SideAttack[3] = SIDE_ATTACK;	SideAttack[2] = SIDE_ATTACK*2;	SideAttack[1] = SIDE_ATTACK*2;	SideAttack[0] = SIDE_ATTACK*3;	/* Corner masks for testing rook positions */	ULCorner = Mask[a8] + Mask[b8] + Mask[c8];	ULCorner += ULCorner << 8;	URCorner = Mask[f8] + Mask[g8] + Mask[h8];	URCorner += URCorner << 8;	LLCorner = ULCorner << 48;	LRCorner = URCorner << 48;	/* Masks for the squares on a rank left or right of the specified square */	for (i=0;i<64;i++) 	{		RankLeftMask = RankRightMask = 0;		for (j=i-1;File(j)<File(i);j--) 		{			RankLeftMask += (UNIT<<j);		}		for (j=i+1;File(j)>File(i);j++) 		{			RankRightMask += (UNIT<<j);		}	}	/* Masks for supporting pieces (usually pawns) on either flank */	for (i=0;i<64;i++) 	{		FlankMaskUp = FlankMaskDown = SideMask = EMPTY;		if (File(i)>0) 		{			FlankMaskUp |= (FileUpMask[i-1] | Mask[i-1]);		}		if (File(i)<7) 		{			FlankMaskUp |= (FileUpMask[i+1] | Mask[i+1]);		}		if (File(i)>0) 		{			FlankMaskDown |= (FileDownMask[i-1] | Mask[i-1]);		}		if (File(i)<7) 		{			FlankMaskDown |= (FileDownMask[i+1] | Mask[i+1]);		}		if (File(i)>0) 		{			SideMask |= Mask[i-1];		}		if (File(i)<7) 		{			SideMask |= Mask[i+1];		}		FlankMask = FlankMaskDown | FlankMaskUp;	}	/* Masks for pawn pairs seperated by at most 1 rank */	for (i=0;i<64;i++) 	{		PairMaskUp = PairMaskDown = EMPTY;		if (File(i)>0) 		{			PairMaskUp |= Mask[i-1];			if (Rank(i)>0) 			{				PairMaskUp |= Mask[i-9];			}			PairMaskDown |= Mask[i-1];			if (Rank(i)<7) 			{				PairMaskDown |= Mask[i+7];			}		}		if (File(i)<7) 		{			PairMaskUp |= Mask[i+1];			if (Rank(i)>0) 			{				PairMaskUp |= Mask[i-7];			}			PairMaskDown |= Mask[i+1];			if (Rank(i)<7) 			{				PairMaskDown |= Mask[i+9];			}		}		PairMask = PairMaskDown | PairMaskUp;	}	/* Set up offside mask.  This contains a file mask for	* all files NOT within 2 of the specified rank */	for (i=0;i<8;i++) 	{		OffsideMask = EMPTY;		for (j=0;j<8;j++) 		{			if (abs(i-j)>2) 			{				OffsideMask |= FileMask[j];			}		}	}	/* Passed Pawn scoring based on board square. */	for (i=0;i<64;i++) 	{		PassedPawn = (PassedPawnI[Rank(i)] * FileMod[File(i)]) / 10;	}	/* Backward pawns are worse in the centre */	for (i=0;i<8;i++) 	{		Backward[0] = 0;		Backward[1] = BACKWARD_PAWN_1 / 2;		Backward[2] = BACKWARD_PAWN_1;		Backward[3] = (BACKWARD_PAWN_1 + BACKWARD_PAWN_2)/2;		Backward[4] = BACKWARD_PAWN_2;		Backward[5] = (BACKWARD_PAWN_2 + BACKWARD_PAWN_3)/2;		Backward[6] = BACKWARD_PAWN_3;		for (j=1;j<7;j++) 		{			Backward[j] = (Backward[j] * (18 - FileMod)) / 10; 		}	}	/* Masks for the three squares in front of (behind) passed pawns.	* "Front" is taken as meaning in front of WHITE pawns */	for (i=0; i<64; i++) 	{		FrontMask = PawnAttacksWhite;		if (Rank(i) != Rank8) 		{			FrontMask |= Mask[i-8];		}		BackMask  = PawnAttacksBlack;		if (Rank(i) != Rank1) 		{			BackMask  |= Mask[i+8];		}	}	/* Mask for the centre of the board minus files A and H (used for rook pawn tests) */	EdgeMask = FileMask[FileA]|FileMask[FileH];	CentreMask = ~EdgeMask;	/* Penalty for trapping a rook as a function of gamestage */	RookTrapped[Opening]  = ROOK_TRAPPED / 4;	RookTrapped[EarlyMid] = ROOK_TRAPPED / 2;	RookTrapped[Middle]   =(ROOK_TRAPPED * 2) / 3;	RookTrapped[LateMid]  = ROOK_TRAPPED;	RookTrapped[Endgame]  =(ROOK_TRAPPED * 3) / 2;	RookTrapped[LateEnd]  = ROOK_TRAPPED * 2;	/* Masks for proximate pawn shields */	for (i=0;i<64;i++) 	{		PawnShieldMaskWhite = (FlankMaskUp|FileUpMask) &								 BelowMask[Rank7] & KingSafetyMask;		PawnShieldMaskBlack = (FlankMaskDown|FileDownMask) &								 AboveMask[Rank2] & KingSafetyMask;	}	/* Phew */}/* Show the prompt */void Prompt(void) {	int ch=0;	fprintf(stdout,"[%d]",(mvno/2)+1);	if (Current_Board.side==WHITE) 	{		fprintf(stdout,"W");	}	if (Current_Board.side==BLACK) 	{		fprintf(stdout,"B");	}	if ( (ch = InCheck(&Current_Board,Current_Board.side)) ) 	{		if (InCM(&Current_Board,ch)) 		{			fprintf(stdout,"#");		}		else 		{			fprintf(stdout,"+");		}	}	else 	{		fprintf(stdout," ");	}	fprintf(stdout,"> ");}void Defaults(void) {	/* Reset the board to the opening layout */	ResetBoard(&Current_Board);#ifdef INITIAL_BOARD	SetBoard(&Current_Board,INITIAL_BOARD);#endif	fprintf(stdout,"\n\n");	/* Setup the computer parameters */	Params.Depth = 5;                     /* Minimum Search Depth in ply.  Overrides 'MoveTime' */	Params.Time = Params.OTime = 30000;   /* Total Clock Time = 5 minutes in centiseconds */	Params.MoveTime = 10.0;               /* Maximum time in seconds.  'Depth' overrides this */	Params.Test = FALSE;                  /* Not running a test suite */	Params.Mps = 0;                       /* Moves per session.  0 = all */	Params.Inc = 0;                       /* Time increase per move */	automoves = 0;	NHash = 0;	TableW = TableB = NULL;	Randomise();	GlobalAlpha = -CMSCORE;	GlobalBeta = CMSCORE;}/* Load in the opening book. */void LoadOpeningBook(void) {	int i,nm=0,sc=0;	FILE *fp;	char FEN[100],moves[200],ch;	MOVE m;	Openpos *O;	NPos=0;	if ((fp=fopen(BOOK_FILE,"r"))==NULL) 	{		fprintf(stdout,"Could not find Opening Book %s!\n",BOOK_FILE);		return;	}	fprintf(stdout,"Loading Opening Book %s ... ",BOOK_FILE);	/* Step through the file, one line at a time */	do 	{		/* Read in the data */		if (!fgets(FEN,100,fp)) 		{			break;		}		for (i=0;i<(int)strlen(FEN);i++) 		{			if (FEN<' ') 			{				FEN = 0;				break;			}		}		if (strstr(FEN,"#END#")) 		{			break;		}		if (!fgets(moves,200,fp)) 		{			break;		}		NPos++;		if (NPos>1) 		{			Openings = (Openpos *)realloc(Openings,sizeof(Openpos)*NPos);		}		else 		{			Openings = (Openpos *)malloc(sizeof(Openpos));		}		assert(Openings != NULL);		O = (Openings+(NPos-1));		O->FEN = (char *)malloc(sizeof(char)*(strlen(FEN)+1));		assert(O->FEN != NULL);		strncpy(O->FEN,FEN,strlen(FEN));		O->FEN[strlen(FEN)]=0;		/* Load in the suggested moves */		i=-1;		nm=0;		do 		{			do 			{				ch=moves[++i];			} while (ch==' ');			if (ch != '\n') 			{				m = (MOVE)moves-97;				m += (56-(MOVE)moves[++i])<<3;				m += ((MOVE)moves[++i]-97)<<6;				m += (56-(MOVE)moves[++i])<<9;				ch=moves[++i];				/* Check for a promotion move */				if (ch!='{') 				{					switch(toupper(ch)) 					{					case 'Q': 						{							m += promote_q; 							break;						}					case 'R': 						{							m += promote_r; 							break;						}					case 'N': 						{							m += promote_n; 							break;						}					case 'B': 						{							m += promote_b; 							break;						}					}// switch(toupper(ch)) 					i++;				}// if (ch!='{') 				i++;				sc=0;				/* Get the score/weighting for this move */				while ((ch=moves) != '}') 				{					sc *= 10;					sc += (int)ch - 48;					i++;				}				nm++;							/* Update the opening data */				if (nm>1) 				{					O->m  = (MOVE *)realloc(O->m,sizeof(MOVE)*nm);					O->sc = (int *)realloc(O->sc,sizeof(int)*nm);				}				else 				{					O->m  = (MOVE *)malloc(sizeof(MOVE));					O->sc = (int *)malloc(sizeof(int));				}				assert(O->m != NULL);				assert(O->sc != NULL);				O->m[nm-1]=m;				O->sc[nm-1]=sc;				ch = moves[++i];			}// if (ch != '\n') 		} while (ch != '\n');		/* Set the number of moves */		O->nmoves=nm;	} while (!feof(fp));	fclose(fp);   	fprintf(stdout,"OK\n[%d Position",NPos);	if (NPos!=1)	{		fprintf(stdout,"s");	}	fprintf(stdout,"]\n");}/* Frees the memory allocated to the opening book */void FreeOpenings(void) {	int n;	for (n=0;n<NPos;n++) 	{		free(Openings[n].FEN);		free(Openings[n].m);		free(Openings[n].sc);	}	free (Openings);}/* Parse the command line input (if any) */int ParseCommandLine(int argc,char **argv) {	int n,i;	char *arg,*loc;	if (argc<2)	{		return 1;	}	fprintf(stdout,"%d Argument(s) Obtained\n",argc-1);	for (n=1;n<argc;n++) 	{		arg = argv[n];		for (i=0;i<(int)strlen(arg);i++) 		{			arg = toupper(arg);			if (arg=='=') 			{				break;			}		}				fprintf(stdout,"Argument %d: %s\n",n,arg);				if (strstr(arg,"HELP") || strchr(arg,'?')) 		{			PrintArgs();			return 0;		}		if ((loc=strstr(arg,"HASH"))) 		{			Params.HashSize = (int)strtol(loc+4,NULL,10);		}		if ((loc=strstr(arg,"WSKILL"))) 		{			Params.WSkill = (int)strtol(loc+6,NULL,10);		}		if ((loc=strstr(arg,"BSKILL"))) 		{			Params.BSkill = (int)strtol(loc+6,NULL,10);		}		if ((loc=strstr(arg,"TBLOC="))) 		{			strncpy(TB_PATH,loc+6,sizeof(TB_PATH));		}		if ((loc=strstr(arg,"BOOK="))) 		{			strncpy(BOOK_FILE,loc+5,sizeof(BOOK_FILE));		}		if ((loc=strstr(arg,"PERSON="))) 		{			strncpy(PERSON_FILE,loc+7,sizeof(PERSON_FILE));		}		if ((loc=strstr(arg,"EPDTEST="))) 		{			strncpy(EPD_FILE,loc+8,sizeof(EPD_FILE));		}		if ((loc=strstr(arg,"ST"))) 		{			testtime = (int)strtol(loc+2,NULL,10);		}	}// for (n=1;n<argc;n++) 	return 1;}/* Print command line arguments */void PrintArgs(void) {	fprintf(stdout,"Command Line Arguments:\n\n");	fprintf(stdout,"?, help            - Print this file\n");	fprintf(stdout,"hash<num>          - Set the hash table size (2^n kb)\n");	fprintf(stdout,"tbloc=<directory>  - Set the tablebase location\n");	fprintf(stdout,"book=<filename>    - Set the opening book\n");	fprintf(stdout,"person=<filename>  - Set the personality file\n");	fprintf(stdout,"wskill<num>        - Set the skill level for white (1 to 10)\n");	fprintf(stdout,"bskill<num>        - Set the skill level for black (1 to 10)\n");	fprintf(stdout,"epdtest=<file>     - Run an EPD test and quit immediately\n");	fprintf(stdout,"st<num>            - Set the time per move for an EPD test (default 5s)\n");}/* Get any environment variables that are set * Does anyone actually use these any more? */void GetEnvironmentVars(void) {	char *where;	if ((where = getenv("EGTBPATH"))) 	{		strncpy(TB_PATH, where, sizeof(TB_PATH));	}	if ((where = getenv("BOOKFILE"))) 	{		strncpy(BOOK_FILE, where, sizeof(BOOK_FILE));	}	if ((where = getenv("PERSONALITYFILE"))) 	{		strncpy(PERSON_FILE, where, sizeof(PERSON_FILE));	}}/* Get the setup from the config file, if it exists */void GetConfigFile(void) {	FILE *fp;	char ch;	/* Set up some default values in case config file is not found */	Params.WSkill = 10;                   /* [1-10] Higher is stronger */	Params.BSkill = 10;                   /* [1-10] Higher is stronger */	Params.HashSize = 14;                 /* Hash Table Size is 2^HashSize kb */	Params.Resign = TRUE;                 /* Do we resign? */	/* Open the config file */	if ((fp=fopen("beowulf.cfg","r"))==NULL) 	{		fprintf(stdout,"Could not Load Config File beowulf.cfg.  Continuing...\n");		return;	}	/* Browse the config file for the relevant data */	do 	{		fscanf(fp,"%c",&ch);		switch(ch) 		{		case '#' :			{				do 				{					fscanf(fp,"%c",&ch);				} while (ch != '\n' && !feof(fp)); 				break;			}		case 'H' : 			{				fscanf(fp,"ASH %d\n",&Params.HashSize); 				break;			}		case 'T' : 			{				fscanf(fp,"BLOC %s\n",TB_PATH); 				break;			}		case 'O' : 			{				fscanf(fp,"PBOOK %s\n",BOOK_FILE); 				break;			}		case 'P' : 			{				fscanf(fp,"ERSON %s\n",PERSON_FILE); 				break;			}		case 'W' : 			{				fscanf(fp,"SKILL %d\n",&Params.WSkill); 				break;			}		case 'B' : 			{				fscanf(fp,"SKILL %d\n",&Params.BSkill); 				break;			}		case 'R' : 			{				fscanf(fp,"ESIGN O%c",&ch);				if (ch == 'N') 				{					Params.Resign = TRUE;				}				else 				{					Params.Resign = FALSE;					fscanf(fp,"F");				}				fscanf(fp,"\n");				break;			}		default  : 			{				break;			}		}// switch(ch) 	} while (!feof(fp));	fclose(fp);}/* Check the parameters are legal */void CheckParams(void) {	if (Params.HashSize<0) 	{		Params.HashSize=0;	}	if (Params.HashSize>20) 	{		Params.HashSize=20;	}	if (Params.WSkill>10) 	{		Params.WSkill=10;	}	if (Params.WSkill<1) 	{		Params.WSkill=1;	}	if (Params.BSkill>10) 	{		Params.BSkill=10;	}	if (Params.BSkill<1) 	{		Params.BSkill=1;	}}/* Load in the personality file */void LoadPersonalityFile(void) {	FILE *fp;	init_personality();	if ((fp = fopen(PERSON_FILE,"r")) != NULL) 	{		fprintf(stdout,"Loading Personality File %s ... ",PERSON_FILE);		(void)read_person(fp);		fclose(fp);		fprintf(stdout,"OK\n");	}	else 	{		fprintf(stdout,"Could Not Find Personality File %s.  Continuing...\n",PERSON_FILE);	}}/* Sorts out what stage of the game the board is in.  This is now * used for a lookup table instead. */int GetGamestage(int tpts) {	if (tpts>70)	{ 		return Opening;	}	if (tpts>62) 	{		return EarlyMid;	}	if (tpts>54) 	{		return Middle;	}	if (tpts>46) 	{		return LateMid;	}	if (tpts>22) 	{		return Endgame;	}	return LateEnd;}


No worries if you dont understand it and other things that go on in this file. You should be able to browse the main function and pick appart how the game works at the highest level of it's pyramid (it helps if you have the binaries for the programming running while reading the code so that you can follow along). If you can figure out the main function your on good standing :) . You can find the Beowulf files at:
http://www.chessbrain.net/beowulf/

Will post later,
- llvllatrix

wow.. You really did modify the main.c.. Great~ ~ this time it looks better than before and more understandable :)

Quote: This will be done separately from our current source tree and will consist of the code which produces our beowulf dll and an ascii test app which uses the dll. I should be finished by tomorrow and I'll have a tutorial out then.


Does the main function, control the movement of the chess pieces... that means we really can start playing human vs human by tomorrow ...lol :)
Hey guys,

I dont think I'll be able to get the Beowulf code running tonight; the code base is a bit of a mess. I should have something by tomorrow.

The first cut of the code will let us implement what we need to. In sucessive cuts (ie once the game is complete) we will be able to do things like change the difficulty of the computer, add our own opening book, change the "personality" of the engine.

To be honest, I think the Beowulf code needs to be rewritten because I'm finding it almost illegible. Heres a challenge for someone with a lot of time on their hands. Read the Beowulf chess programming page. Then, take the Beowulf source and begin to rewrite the code so that it is both legible and maintainable.

You should know what ledgible and illegible code is by now. By maintainable code, I mean code that you can easily edit without having to look everywhere for answers. To make the Beowulf code maintainable you could make the variable names more descriptive. You could also add in more descriptive comments (comment why something is being done, not what is being done).

Another big problem with this particular code base is that the private variables of each .c file usually are exposed via the extern keyword. Instead all global variables should be private (via the static keyword) and the functions that call them should be passed the appropriate values (either in the parameters or by an accessor function).

Thanks to anyone who takes up the challenge. I'll have the source code for the Beowulf dll online tomorrow, which should help if your up to it.

Will post later,
- llvllatrix

This topic is closed to new replies.

Advertisement