Computer Chess Club Archives


Search

Terms

Messages

Subject: Re: High performance move generation

Author: Pat King

Date: 06:33:09 11/16/99

Go up one level in this thread


On November 16, 1999 at 03:47:16, Dan Newman wrote:

>
>In my latest code I'm using C++ classes for move type so that I can play around
>with the internal structure of the move without having to (in theory) make
>massive changes to the code...  I currently use (and have nearly always used)
>an int for the move, but I'd like to try out a struct with separate from, to,
>etc. to see what that does to performance.
>
[big snip]
>In my 0x88 move generator I use piece lists so that I don't have to
>scan the board for the STM's pieces and a switch statement to
>handle move generation for different piece types rather than having
>arrays of offset values and so forth indexed by piece type or direction.
>This means the move generator isn't very compact, but it avoids doing
>array lookups for offset values by directly embedding them in the code:
>
>    while( *piece ) {
>        switch( piece_type( *piece) ) {
>          case PAWN:
>            // move gen code for pawn...
>            break;
>            ...
>          case BISHOP: {
>              int from = piece_coord( *piece);
>              // NE dir
>              int to = from + 17;
>              while( (to & 0x88) == 0 ) {
>                  if( board[to] ) break;
>                  *movep++ = from | (to << 7);
>                  to += 17
>              }
>              // NW dir
>              // etc...
>              break;
>          }
>          ...
>          case KING:
>            // move gen code for king
>            break;
>        }
>        piece++;
>    }
>
>Another thing I do is have completely separate capture and non-capture
>move generators.  This actually hurts on Vincent's test since I have the
>overhead of calling two generators and must do many things twice, but in
>actual use the capture generator is called far more often than the
>non-capture generator.  This is because I use only the capture generator
>in the quiescence search, and in the full width search a capture quite
>often is the cause of a cutoff preventing the non-capture generator from
>getting called...  So, it likely saves some cycles to separate the move
>generation like this.  It also makes move ordering easier.
>
>-Dan.
I do the same thing seperating capture and non-capture generation. I also extend
your piece list concept a bit to avoid switches and big array lookups.

In part...

class CPiece {
  private:
    int Square;
    int Color;
    // etc
  public:
    // CMove set up as linked list. Slow, I know.
    virtual CMove * GenCaptures(CMove * ML);
    virtual CMove * GenNoCaptures(CMove * ML);
    virtual void MakeMove(CMove * M);
    virtual void UnMove( );
    // etc
};

CPiece * Pieces[32];

CMove * GenerateCaptureMoves(CMove * ML) {
  if (WhiteToMove) for (int i = WP1; i <= WK; i++)
    ML = Pieces[i]->GenCaptures(ML);
  else for (int i = BP1; i <= BK; i++)
    ML = Pieces[i]->GenCaptures(ML);
  return ML;
};

// GenerateNoCaptureMoves similar, and GenerateMoves just calls the two.

class CKing: public CPiece { // etc

In exchange for the overhead for the extra calls, you get very readable code and
less complexity (at least in terms of switches and ifs). Of course, my engine
isn't winning any speed awards :)

Pat




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.