PieceLists ?

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Karlo Bala
Posts: 373
Joined: Wed Mar 22, 2006 10:17 am
Location: Novi Sad, Serbia
Full name: Karlo Balla

Re: PieceLists ?

Post by Karlo Bala »

Aleks Peshkov wrote:I do not consider given code reasonably elegant. Generating attacks of pieces is not equal to generating legal moves.

What is elegant is to transform attacks bitboards into legal move bitboards generically as possible.
IMO, the most elegant (not the best, just elegant) move generator I've seen is from Hans Eric Sandstroem, implemented in GNU Chess 3.

Code: Select all


New move Generation algoritm:
Revision: 1989-09-06
Author: Hans Eric Sandstroem.

This algortim is the result of an attempt to make an hardware move generator, but since I newer had the time and resources to build the hardware I wrote a software version and incorporated that one into gnuchess. This was the best way I could think of sharing this algorithm with the computer chess community.

If there is anybody out there with the time and rescources to build a hardware move generator I will be glad to assist.

The general idea behind this algoritm is to pre calculate a lot of data. The data that is pre calculated is every possible move for every piece from every square disregarding any other pieces on the board. This pre calculated data is stored in an array that looks like this:

struct sqdata {
  short nextpos;
  short nextdir;
};
struct sqdata posdata[8][64][64];
/* posdata[piecetype][fromsquare][destinationsquare] */
example:
	the first move for a queen at e8 is stored at;
	posdata[queen][e8][e8].nextpos
	suppose this is e7 and e7 is occupied then the next move
	will be found in;
	posdata[queen][e8][e7].nextdir

To handle the differeces between white and black pawns (they move in opposite directions) an array ptype has been introduced:  
static const short ptype[2][8] = {
  no_piece,pawn,knight,bishop,rook,queen,king,no_piece,
  no_piece,bpawn,knight,bishop,rook,queen,king,no_piece};
           ^^^^^
And it is used like this:
   piecetype = ptype[side][piece]
When generating moves for pieces that are not black pawns, piece can be used directly in posdata. As in the example above.

Thus the only thing one has to do when generating the moves is to check for collisions with other pieces.  the move generation to do this looks like this: (for non pawns)

    p = posdata[piece][sq];
    u = p[sq].nextpos;
    do {
      if (color[u] == neutral) {
        LinkMove(ply,sq,u,xside);
        u = p[u].nextpos;
      }
      else {
        if (color[u] == xside) LinkMove(ply,sq,u,xside);
        u = p[u].nextdir;
      }
    } while (u != sq);

 - I`nt this just beautiful!

The array posdata is initialized in the routine Initialize_moves. This routine is called just once and it works so no time has been spent on the structure of this code. GenMoves and CaptureList generates the moves but the routines ataks, BRscan, Sqatakd, KingScan and trapped also relies on the move generation algoritm so they have also been rewritten.
Best Regards,
Karlo Balla Jr.
pkumar
Posts: 100
Joined: Tue Oct 15, 2013 5:45 pm

Re: My antique move generator

Post by pkumar »

Karlo Bala wrote:IMO, the most elegant (not the best, just elegant) move generator I've seen is from Hans Eric Sandstroem, implemented in GNU Chess 3.

Code: Select all

    p = posdata[piece][sq];
    u = p[sq].nextpos;
    do {
      if (color[u] == neutral) {
        LinkMove(ply,sq,u,xside);
        u = p[u].nextpos;
      }
      else {
        if (color[u] == xside) LinkMove(ply,sq,u,xside);
        u = p[u].nextdir;
      }
    } while (u != sq);

My noncapture slide move generator is somewhat similar, probably slower.

Code: Select all

enum PieceType{EMPTY, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING};
enum ColorType {WHITE, BLACK, FREE, EDGE};
struct Square
{
    char piece;
    char color;
    char list;//link to piecelist
    char junk;
};

    Square board[65];//board[0..63] are usual, board[64] is set to piece= EMPTY and color=EDGE

//moves_piece[from][direction][slides+1]
    char moves_B[64][4][8];
    char moves_R[64][4][8];
    char moves_Q[64][8][8];

//movept_piece[from][directions+1]
    char* movept_B[64][5];
    char* movept_R[64][5];
    char* movept_Q[64][9];

/*
After initialisation, ROOK at A1 would have
	moves_R[A1]=
	{{B1,C1,D1,E1,F1,G1,H1,64}
	,{A2,A3,A4,A5,A6,A7,A8,64}
	,{64,64,64,64,64,64,64,64}
	,{64,64,64,64,64,64,64,64}};
	movept_R[A1]={moves_R[A1][0],moves_R[A1][1],NULL,NULL,NULL};
*/

void getSlideMoves(int from, char** movept)
{
    int to;
    int piece=board[from].piece;
    char* moves= *movept++;

	//Only open directions are scanned, e.g., 3 directions for Q at A1
	//For QUEEN, up to 8 edges may be tested(tad wasteful) unless obstracted
    do
	{
        to= *moves++;
        if(board[to].color== FREE)
            PUSH(MOVE(from,to,piece));
        else //obstacle or edge
        {
            moves= *movept++;
            if(moves==NULL) break;
        }
	}while(true);
}

//calling code
	switch(board[from].piece)
	{
	...code...
        	case BISHOP: getSlideMoves(from, movept_B[from]); break;
	...code...
	}