Class TChessThread (unit ChessBrd) |
Inherits from
TThread
The thinking thread contains mainly code from Tom's Simple Chess Program:
constructor Create;
- ------------------------------------------------------------------------------- Implementation of TChessThread (containing mainly Tom's Source) -------------------------------------------------------------------------------
procedure Execute;
The execution point of the thread
function attack(sq,s: Integer): Boolean;
attack returns TRUE if square sq is being attacked by side s and FALSE otherwise.
function ColorOfPiece (sq: Square): Integer;
function eval: Integer;
procedure gen;
gen() generates pseudo-legal moves for the current position.
procedure gen_caps;
gen_caps() is basically a copy of gen() that's modified to only generate capture and promote moves.
procedure gen_promote(src,dst,bits: Integer);
gen_promote() is just like gen_push(), only it puts 4 moves on the move stack, one for each possible promotion piece
procedure gen_push(src,dst,bits: Integer);
gen_push() puts a move on the move stack, unless it's a pawn promotion that needs to be handled by gen_promote().
procedure InitValues;
do the same for black
procedure init_eval;
return the score relative to the side to move (i.
procedure IntCopy (dest,source: pInt; count: Integer);
Tom's engine does not recognize the threefold position rule so the exception may occur after a draw.
function in_check(s: Integer): Boolean;
in_check() returns TRUE if side s is in check and FALSE otherwise.
function makemove (m: move_bytes): Boolean;
makemove() makes a move.
procedure PerformMove;
Synchronizer - Modifies VCL
function quiesce(alpha,beta: Integer): Integer;
quiesce() is a recursive minimax search function with alpha-beta cutoffs.
function search(alpha,beta,depth: Integer): Integer;
search() does just that, in negamax fashion
procedure sort(src: Integer);
sort() searches the current ply's move list from 'from' to the end to find the move with the highest score.
procedure sort_pv;
sort_pv() is called when the search function is following the PV (Principal Variation).
procedure takeback;
takeback() is very similar to makemove(), only backwards :
procedure ThinkAboutAMove;
procedure ThinkingFinished;
--------------------------------------------------------------------------- Synchronized Methods ---------------------------------------------------------------------------
EndFunc : TNotifyEvent;
MoveFunc : TMoveFunc;
castle : Integer;
the side not to move
castle_mask : Array [0..63] of Integer;
This is the castle_mask array.
Castling : pCastleSet;
color : Array [0..63] of Integer;
ComputerPlaysBlack : pBoolean;
ComputerPlaysWhite : pBoolean;
endgame_king_pcsq : Array[0..63] of Integer;
EnPassant : pSquare;
ep : Integer ;
a bitfield with the castle permissions.
fifty : Integer;
the en passant square.
flip : Array [0..63] of Integer;
follow_pv : Boolean;
gen_begin : Array [0..63] of Integer;
gen_dat : Array [0..MOVE_STACK-1] of gen_rec;
the half-move that we're on this is the move stack.
gen_end : Array [0..63] of Integer;
history : Array [0..63,0..63] of Integer;
hist_dat : Array [0..63] of hist_rec;
we need an array of hist_rec's so we can take back the moves we make
init_color : Array [0..63] of Integer;
the initial board state
init_piece : Array [0..63] of Integer;
kingside_pawn_pcsq : Array [0..63] of Integer;
king_pcsq : Array [0..63] of Integer;
mailbox : Array [0..119] of Integer;
Now we have the mailbox array, so called because it looks like a mailbox, at least according to Bob Hyatt.
mailbox64 : Array [0..63] of Integer;
minor_pcsq : Array [0..63] of Integer;
nodes : Integer;
offset : Array[0..5,0..7] of Integer;
offsets : Array [0..5] of Integer;
pawn_pcsq : Array [0..63] of Integer;
pcsq : Array [0..1,0..5,0..63] of Integer;
pcsq stands for piece/square table.
piece : Array [0..63] of Integer;
LIGHT, DARK, or EMPTY
piece_char : Array [0..5] of Char;
the piece letters, for print_board(
ply : Integer;
the number of moves since a capture or pawn move, used to handle the fifty-move-draw rule
Position : pChar;
pv : Array[0..63,0..63]of moverec;
the number of nodes we've searched a triangular PV array
pv_length : Array [0..63] of Integer;
queenside_pawn_pcsq : Array [0..63] of Integer;
SearchDepth : PInt;
side : Integer;
PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, or EMPTY
slide : Array[0..5] of Boolean;
slide, offsets, and offset are basically the vectors that pieces can move in.
StopThinkingNow : Boolean;
Thinking : Boolean;
---------------------------------------------------------------------
ThinkingPriority : pThreadPriority;
value : Array [0..5] of Integer;
values of the pieces
WhiteToMove : pBoolean;
xside : Integer;
the side to move
constructor Create;
------------------------------------------------------------------------------- Implementation of TChessThread (containing mainly Tom's Source) -------------------------------------------------------------------------------
procedure Execute;
The execution point of the thread
function attack(sq,s: Integer): Boolean;
attack returns TRUE if square sq is being attacked by side s and FALSE otherwise.
function ColorOfPiece (sq: Square): Integer;
function eval: Integer;
procedure gen;
gen() generates pseudo-legal moves for the current position. It scans the board to find friendly pieces and then determines what squares they attack. When it finds a piece/square combination, it calls gen_push to put the move on the "move stack."
procedure gen_caps;
gen_caps() is basically a copy of gen() that's modified to only generate capture and promote moves. It's used by the quiescence search.
procedure gen_promote(src,dst,bits: Integer);
gen_promote() is just like gen_push(), only it puts 4 moves on the move stack, one for each possible promotion piece
procedure gen_push(src,dst,bits: Integer);
gen_push() puts a move on the move stack, unless it's a pawn promotion that needs to be handled by gen_promote(). It also assigns a score to the move for alpha-beta move ordering. If the move is a capture, it uses MVV/LVA (Most Valuable Victim/Least Valuable Attacker). Otherwise, it uses the move's history heuristic value. Note that 1,000,000 is added to a capture move's score, so it always gets ordered above a "normal" move.
procedure InitValues;
do the same for black
procedure init_eval;
return the score relative to the side to move (i.e., a positive score means the side to move is winning
procedure IntCopy (dest,source: pInt; count: Integer);
Tom's engine does not recognize the threefold position rule so the exception may occur after a draw. // raise EChessException.Create('Engine tried to perform illegal move');
function in_check(s: Integer): Boolean;
in_check() returns TRUE if side s is in check and FALSE otherwise. It just scans the board to find side s's king and calls attack() to see if it's being attacked.
function makemove (m: move_bytes): Boolean;
makemove() makes a move. If the move is illegal, it undoes whatever it did and returns FALSE. Otherwise, it returns TRUE.
procedure PerformMove;
Synchronizer - Modifies VCL
function quiesce(alpha,beta: Integer): Integer;
quiesce() is a recursive minimax search function with alpha-beta cutoffs. In other words, negamax. It basically only searches capture sequences and allows the evaluation function to cut the search off (and set alpha). The idea is to find a position where there isn't a lot going on so the static evaluation function will work.
function search(alpha,beta,depth: Integer): Integer;
search() does just that, in negamax fashion
procedure sort(src: Integer);
sort() searches the current ply's move list from 'from' to the end to find the move with the highest score. Then it swaps that move and the 'from' move so the move with the highest score gets searched next, and hopefully produces a cutoff.
procedure sort_pv;
sort_pv() is called when the search function is following the PV (Principal Variation). It looks through the current ply's move list to see if the PV move is there. If so, it adds 10,000,000 to the move's score so it's played first by the search function. If not, follow_pv remains FALSE and search() stops calling sort_pv().
procedure takeback;
takeback() is very similar to makemove(), only backwards :
procedure ThinkAboutAMove;
procedure ThinkingFinished;
--------------------------------------------------------------------------- Synchronized Methods ---------------------------------------------------------------------------
EndFunc : TNotifyEvent;
MoveFunc : TMoveFunc;
castle : Integer;
the side not to move
castle_mask : Array [0..63] of Integer;
This is the castle_mask array. We can use it to determine the castling permissions after a move. What we do is logical-AND the castle bits with the castle_mask bits for both of the move's squares. Let's say castle is 1, meaning that white can still castle kingside. Now we play a move where the rook on h1 gets captured. We AND castle with castle_mask[63], so we have 1&14, and castle becomes 0 and white can't castle kingside anymore.
Castling : pCastleSet;
color : Array [0..63] of Integer;
ComputerPlaysBlack : pBoolean;
ComputerPlaysWhite : pBoolean;
endgame_king_pcsq : Array[0..63] of Integer;
EnPassant : pSquare;
ep : Integer ;
a bitfield with the castle permissions. if 1 is set, white can still castle kingside. 2 is white queenside. 4 is black kingside. 8 is black queenside.
fifty : Integer;
the en passant square. if white moves e2e4, the en passant square is set to e3, because that's where a pawn would move in an en passant capture
flip : Array [0..63] of Integer;
follow_pv : Boolean;
gen_begin : Array [0..63] of Integer;
gen_dat : Array [0..MOVE_STACK-1] of gen_rec;
the half-move that we're on this is the move stack. gen_dat is basically a list of move lists, all stored back to back. gen_begin[x] is where the first move of the ply x move list is (in gen_dat). gen_end is right after the last move.
gen_end : Array [0..63] of Integer;
history : Array [0..63,0..63] of Integer;
hist_dat : Array [0..63] of hist_rec;
we need an array of hist_rec's so we can take back the moves we make
init_color : Array [0..63] of Integer;
the initial board state
init_piece : Array [0..63] of Integer;
kingside_pawn_pcsq : Array [0..63] of Integer;
king_pcsq : Array [0..63] of Integer;
mailbox : Array [0..119] of Integer;
Now we have the mailbox array, so called because it looks like a mailbox, at least according to Bob Hyatt. This is useful when we need to figure out what pieces can go where. Let's say we have a rook on square a4 (32) and we want to know if it can move one square to the left. We subtract 1, and we get 31 (h5). The rook obviously can't move to h5, but we don't know that without doing a lot of annoying work. Sooooo, what we do is figure out a4's mailbox number, which is 61. Then we subtract 1 from 61 (60) and see what mailbox[60] is. In this case, it's -1, so it's out of bounds and we can forget it. You can see how mailbox[] is used in attack() in board.c.
mailbox64 : Array [0..63] of Integer;
minor_pcsq : Array [0..63] of Integer;
nodes : Integer;
offset : Array[0..5,0..7] of Integer;
offsets : Array [0..5] of Integer;
pawn_pcsq : Array [0..63] of Integer;
pcsq : Array [0..1,0..5,0..63] of Integer;
pcsq stands for piece/square table. It's indexed by the piece's color, type, and square. The value of pcsq[LIGHT,KNIGHT,e5] might be 310 one of the outer squares. // instead of just 300 because a knight on e5 is better than one on
piece : Array [0..63] of Integer;
LIGHT, DARK, or EMPTY
piece_char : Array [0..5] of Char;
the piece letters, for print_board(
ply : Integer;
the number of moves since a capture or pawn move, used to handle the fifty-move-draw rule
Position : pChar;
pv : Array[0..63,0..63]of moverec;
the number of nodes we've searched a triangular PV array
pv_length : Array [0..63] of Integer;
queenside_pawn_pcsq : Array [0..63] of Integer;
SearchDepth : PInt;
side : Integer;
PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, or EMPTY
slide : Array[0..5] of Boolean;
slide, offsets, and offset are basically the vectors that pieces can move in. If slide for the piece is FALSE, it can only move one square in any one direction. offsets is the number of directions it can move in, and offset is an array of the actual directions.
StopThinkingNow : Boolean;
Thinking : Boolean;
---------------------------------------------------------------------
ThinkingPriority : pThreadPriority;
value : Array [0..5] of Integer;
values of the pieces
WhiteToMove : pBoolean;
xside : Integer;
the side to move