Since much of the trajectories is pre-calculated, initialization is a tough job. In the real project the engine should be configurable with the strangest moves, and the trajectories would have to be created with great variety, controlled by the move specifications in the variant definition. Most of the complexity of the engine will probably go in there. For the pilot project (orthodox Chess) the army is known at compile time, and creation of the trajectories can be hard-coded. Since leapers are basically limited-range sliders with a range of one step, we only need a routine to create a slider trajectory.
The design is as follows: All potential moves of each piece have a cell representing it in moveTable. There is an array trajectories[] that specifies where the trajectories formed by these moves start in moveTable, by holding the index. The trajectories of a given piece occupy a contiguous stretch in trajectories[], terminated by a zero. In the piece list each piece has an element p_moves[pieceIndex] that specifies where in trajectories[] the list of trajectories for that piece starts.
Code: Select all
// rights flags
#define F_FRIEND 1
#define F_FOE 2
#define F_EMPTY 4
#define F_CAPT 8
#define F_NONC 16
int p_moves[NPIECES]; // part of piece list
int piecePtr = 2; // first piece (index 0 and 1 reserved for empty squares and boundary guards)
int movePtr = 1; // moveTable[0] is a dummy
int trajectoryPtr = 0;
MoveTarget moveTable[512];
int trajectories[320];
int steps[] = { 1, 16, -1, -16, 15, 17, -15, -17, // orthogonal (0-3) and diagonal (4-7) steps on a 0x88 board
31, 33, 14, 18, -14, -18, -31, -33 }; // and hippogonal steps (8-15)
void AddSlide(int piece, int step, int range, int rights)
{
int i;
trajectories[trajectoryPtr++] = movePtr; // initiate new trajectory
for(i=1; i<=range; i++) {
MoveTarget *cell = moveTable + movePtr++; // add new move cell
cell->leap = i*step;
cell->piece = piece;
cell->reference = piece;
cell->nextRef = 0;
cell->rights = rights | (i == range ? 0 : F_EMPTY);
cell->forks[0] = 1; // on empty squares continue with next
cell->forks[1] = 0; // no alternative continuation;
}
}
void AddSlider(int a, int b)
{
int i;
p_moves[piecePtr] = trajectoryPtr;
for(i=a; i<b; i++) {
AddSlide(piecePtr, steps[i], 7, F_CAPT|F_NONC);
trajectoryPtr++;
}
trajectories[trajectoryPtr++] = 0; // sentinel
piecePtr++;
}
void AddLeaper(int a, int b)
{
int i;
p_moves[piecePtr] = trajectoryPtr;
for(i=a; i<b; i++) {
AddSlide(piecePtr, steps[i], 1, F_CAPT|F_NONC);
trajectoryPtr++;
}
trajectories[trajectoryPtr++] = 0; // sentinel
piecePtr++;
}
void AddPawn(int direction, int range)
{
p_moves[piecePtr] = trajectoryPtr;
AddSlide(piecePtr, direction+1, 1, F_CAPT);
AddSlide(piecePtr, direction-1, 1, F_CAPT);
AddSlide(piecePtr, direction, range, F_NONC);
trajectories[trajectoryPtr++] = 0; // sentinel
piecePtr++;
}
void Init()
{
int i;
trajectoryPtr = 64; // leave room for 16 Pawns (with 3 trajectories and a terminating zero each)
for(i=0; i<2; i++) AddLeaper(0, 8); // Kings
for(i=0; i<2; i++) AddSlider(0, 8); // Queens
for(i=0; i<4; i++) AddSlider(0, 4); // Rooks
for(i=0; i<4; i++) AddSlider(4, 8); // Bishops
for(i=0; i<4; i++) AddLeaper(8,16); // Knights
// this added 8x 8 + 8 x 4 = 96 trajectories, and 16 terminating zeros
int forward = 16;
trajectoryPtr = 0;
for(i=0; i<16; i++) AddPawn(forward, 1), forward *= -1; // Pawns (alternating white/black
piecePtr -= 16; trajectoryPtr = 256;
for(i=0; i<16; i++) AddPawn(forward, 2), forward *= -1; // Double-push Pawns
}
The idea is that MakeMove() would always AND the p_moves field of the moving piece with 0xff (but remember the original value, which would be put back by UnMake()). Which would have no effect on pieces with lists in the first 256 elements. So pieces with extra initial moves can be put just bejond that, and would then lose those moves when moving. The value of the mask that p_moves is ANDed with would depend on the total number of trajectories of the non-virgin pieces, and would be the next-largest power of 2.