home *** CD-ROM | disk | FTP | other *** search
- {************************************************}
- { }
- { Chess - Shared DLL Example }
- { CHESS.DLL Primary search engine. }
- { Copyright (c) 1992 by Borland International }
- { }
- {************************************************}
-
- unit LEval;
-
- {$R-,Q-,S-,W-}
-
- interface
-
- function FindMove(MaxLevel: integer): integer;
- procedure InitPawnStrTables;
-
- implementation
-
- uses Strings, GameRec, LBoard, LMovegen, LMoves, LTimer, TaskMgr, GameTask;
-
- type
- { File numbers }
- FileType = 0..7;
- { Rank numbers }
- RankType = 0..7;
-
- { Evaluation parameters }
- const
-
- { Value of Pieces used in Evaluation }
- PieceValue: array[Empty..Pawn] of integer =
- (0, $1000, $900, $4C0, $300, $300, $100);
-
- { Tolerance Width }
- Tolerance = 8;
-
- { Used to calculate distance to center }
- DistAn: array[0..7] of Integer = (3,2,1,0,0,1,2,3);
-
- { The Value of a Pawn is the sum of Rank and file values.
- The file Value is equal to PawnFileFactor * (Rank Number + 2) }
- PawnRank: array [RankType] of Integer = (0,0, 0, 2, 4, 8,30,0);
- PassPawnRank: array [RankType] of Integer = (0,0,10,20,40,60,70,0);
- PawnFileFactor: array [FileType] of Integer = (0,0,2,5,6,2,0,0);
-
- { Value of castling (Long..Short) }
- CastValue: array[CastDirType] of Integer = (4,32);
-
- { Value of exchanging Pieces (not pawns) when ahead }
- ExchangeValue = 32;
-
- { Pawnstructure values }
- IsolatedPawn = 20; { Isolated Pawn. Double isolated Pawn is 3 * 20 }
- DoublePawn = 8; { Double Pawn }
- SidePawn = 6; { Having a Pawn On the side }
- ChainPawn = 3; { Being covered by a Pawn }
- CoverPawn = 3; { Covering a Pawn }
- NotMovePawn = 3; { Penalty for moving Pawn }
-
- { Penalty for Bishop blocking d2/e2 Pawn }
- BishopBlockValue = 20;
-
- { Bonus for Rook behind passed Pawn }
- RookBehindPassPawn = 16;
-
- { Values for calculating importance of each Square (AttVal) }
- SquareRankValue: array [RankType] of byte = (0,0,0,0,1,2,4,4);
-
- { Pawnstructure table.
- One and Dob are SETs of FILEs, and contains
- the FILEs which has One respectively more pawns.
- The table is updated and evaluated at each Ply in the Search.
- FileBitTab is used to lookup a file in a set of FILEs }
- type
- SetofFile = byte;
- PawnBitRec = record
- One,Dob: SetofFile;
- end;
- PawnBitType = array [ColorType] of PawnBitRec;
-
- const
- FileBitTab: array [FileType] of SetofFile = (1,2,4,8,$10,$20,$40,$80);
-
-
- { Calculates the Value of the Piece On the Square }
- function PiecePosVal(Piece: PieceType; Color: ColorType;
- Square: SquareType): Integer;
- begin
- PiecePosVal := PieceValue[Piece] + CC.PVTable[Color,Piece,Square];
- end;
-
- { Pawn strength tables }
-
- var
- PawnStrDob: array[0..255] of Byte;
- PawnStrIso: array[0..255] of Byte;
-
- { Initialize the pawn strength tables }
- procedure InitPawnStrTables;
- var
- I: Integer;
-
- function BitCount(B: SetOFFile): Integer; assembler;
- asm
- XOR AX,AX
- MOV DL,B
- MOV CX,8
- CLC
- @@1: SHR DL,1
- JNC @@2
- INC AX
- @@2: JZ @@3
- LOOP @@1
- @@3:
- end;
-
- begin
- for I := 0 to 255 do
- begin
- PawnStrDob[I] := BitCount(I) * DoublePawn;
- PawnStrIso[I] := BitCount(I) * IsolatedPawn;
- end;
- end;
-
- { Calculate the pawn strength from the pawn strength tables }
- function PawnStrVal(var PB: PawnBitRec): Integer; near; assembler;
- asm
- XOR AX,AX
- LES DI,PB
- MOV AL,ES:[DI].PawnBitRec.One
- MOV CX,AX
- MOV BX,AX
- SHR CX,1
- SHL BX,1
- OR CX,BX
- NOT CX
- MOV BX,AX
- AND BX,CX
- MOV AL,ES:[DI].PawnBitRec.Dob
- MOV SI,AX
- MOV AL,BYTE PTR PawnStrDob[SI]
- ADD AL,BYTE PTR PawnStrIso[BX]
- ADC AH,0
- AND BL,ES:[DI].PawnBitRec.Dob
- ADD AL,BYTE PTR PawnStrIso[BX]
- ADC AH,0
- ADD AL,BYTE PTR PawnStrIso[BX]
- ADC AH,0
- NEG AX
- end;
-
- { Finds a move in the position.
- On exit :
- MAINLINE contains principal variation.
- MAINEVALU contains evaluation of the line
- }
- function FindMove(MaxLevel: Integer): Integer;
- var
- { Value of root position in the Search }
- RootValue: MaxType;
-
- { Total material and material advantage }
- TotalMaterial,PawnTotalMaterial,Material: Integer;
-
- { Material Level of the game (early middlegame = 43 - 32, endgame = 0) }
- MaterialLevel: 0..60;
-
- PawnBit: array[-1..MaxPly] of PawnBitType;
-
- Mating: Boolean; { Mating Evaluation function is used }
-
- { Calculates Piece-Value table for the static Evaluation function }
- procedure CalcPVTable;
- type
- { Pawn table, containing the squares with a Pawn }
- PawnTabType = array[RankType] of SetofFile;
-
- var
- PawnTab: array[ColorType] of PawnTabType;
-
- { Bit tables for static Pawn structure Evaluation }
- PawnFileTab, Bit,
- OpPassTab, BehindOpPass,
- LeftSideTab, RightSideTab ,SideTab,
- LeftChainTab, RightChainTab,ChainTab,
- LeftCoverTab, RightCoverTab: SetofFile;
-
- { Importance of an attack of the Square }
- AttackValue: array[ColorType,SquareType] of 0..120;
-
- { Value of squares controlled from the Square }
- PVControl: array[ColorType,Rook..Bishop,SquareType] of 0..250;
-
- LosingColor: ColorType; { The Color which is being mated }
- PosVal: Integer; { The positional Value of Piece }
- AttVal: Integer; { The attack Value of the sqaure }
- Line: FileType; { The file of the Piece }
- Rank, Row: RankType; { The Rank and Row of the Piece }
- Dist, { Distance to center }
- KingDist: 0..14; { Distance to opponents King }
- Cast: CastType; { Possible castlings }
- Direct: Boolean; { Indicates Direct attack }
- Cnt: Integer; { Counter for attack values }
- StrVal: Integer; { Pawnstructure Value }
- Color,
- OppColor: ColorType; { Color and opponents Color }
- PieceCount: PieceType; { Piece counter }
- Square: SquareType; { Square counter }
- Dir: DirType; { Direction counter }
- Sq: EdgeSquareType; { Square counter }
- Temp,Temp2: Integer; { Temporaries }
- TempColor: ColorType;
-
- begin
- { Calculate SAMMAT, PAWNSAMMAT and Material }
- Mating := False;
- TotalMaterial := 0;
- PawnTotalMaterial := 0;
- Material := 0;
- with CC do
- begin
- for Square := 0 to $77 do
- if (Square and $88) = 0 then
- with Board[Square] do
- if Piece <> Empty then
- if Piece <> King then
- begin
- Temp := PieceValue[Piece];
- TotalMaterial := TotalMaterial + Temp;
- if Piece = Pawn then
- PawnTotalMaterial := PawnTotalMaterial + PieceValue[Pawn];
- if Color = White then Temp := -Temp;
- Material := Material - Temp;
- end;
- MaterialLevel := Max(0,TotalMaterial - $2000) div $100;
-
- { set Mating if weakest Player has less that the equivalence of
- two Bishops and the advantage is at least a Rook for a Bishop }
- if Material < 0 then
- LosingColor := White
- else
- LosingColor := Black;
- Mating := ((TotalMaterial - abs(Material)) div 2 <=
- PieceValue[Bishop] * 2) and (Abs(Material) >= PieceValue[Rook] -
- PieceValue[Bishop]);
-
- { Calculate ATTACKVAL (importance of each Square) }
- for Rank := 0 to 7 do
- for Line := 0 to 7 do
- begin
- Square := Rank shl 4 + Line;
-
- { Center importance }
- AttVal := Max(0,8 - 3 * (DistAn[Rank] + DistAn[Line]));
-
- { Rank importance }
- for Color := White to Black do
- begin
- AttackValue[Color,Square] := SquareRankValue[Rank] * 3 *
- (MaterialLevel + 8) shr 5 + AttVal;
- Square := Square xor $70;
- end;
- end; { for }
- for Color := White to Black do
- begin
- OppColor := ColorType(1 - ord(Color));
- CalcCastling(OppColor,Cast);
- if not (Short in Cast) and (MaterialLevel > 0) then
- { Importance of the 8 squares around the Opponent's King }
- with PieceTab[OppColor,0] do
- for Dir := 0 to 7 do
- begin
- Sq := ISquare + DirTab[Dir];
- if (Sq and $88) = 0 then
- AttackValue[Color,Sq] := AttackValue[Color,Sq] +
- 12 * (MaterialLevel + 8) shr 5;
- end;
- end; { for }
-
- { Calculate PVControl }
- for Square := $77 downto 0 do
- if (Square and $88) = 0 then
- for Color := White to Black do
- for PieceCount := Rook to Bishop do
- PVControl[Color,PieceCount,Square] := 0;
-
- for Square := $77 downto 0 do
- if (Square and $88) = 0 then
- for Color := White to Black do
- begin
- for Dir := 7 downto 0 do
- begin
- if Dir < 4 then
- PieceCount := Rook
- else
- PieceCount := Bishop;
- { Count Value of all Attacks from the Square in
- the Direction.
- The Value of attacking a Square is Found in ATTACKVAL.
- Indirect Attacks (e.g. a Rook attacking through
- another Rook) counts for a Normal attack,
- Attacks through another Piece counts half }
- Cnt := 0;
- Sq := Square;
- Direct := True;
- repeat
- { Get Next Square }
- Sq := Sq + DirTab[Dir];
- if (Sq and $88) <> 0 then Break;
- Temp:=AttackValue[Color,Sq];
- if Direct then { Add AttackValue }
- Inc(Cnt, Temp)
- else
- Inc(Cnt, Temp shr 1);
- with Board[Sq] do
- if Piece <> Empty then
- if (Piece <> PieceCount) and (Piece <> Queen) then
- Direct := False;
- until Board[Sq].Piece = Pawn;
- Inc(PVControl[Color,PieceCount,Square],Cnt shr 2);
- end { for Dir };
- end { for Color };
-
- { Calculate PVTable, Value by Value }
- for Square := $77 downto 0 do
- if (Square and $88) = 0 then
- begin
- for Color := White to Black do
- begin
- OppColor := ColorType(1 - ord(Color));
- Line := Square and 7;
- Row := Square shr 4;
- Rank := Row;
- if Color = Black then Rank := 7 - Rank;
- Dist := DistAn[Rank] + DistAn[Line];
- with PieceTab[OppColor,0] do
- KingDist := abs(Square shr 4 - ISquare shr 4) +
- (Square - ISquare) and 7;
- for PieceCount := King to Pawn do
- begin
- { Calculate POSITIONAL Value for the Piece On the Square }
- PosVal := 0;
- if Mating and (PieceCount <> Pawn) then
- begin
- { Mating Evaluation }
- if PieceCount = King then
- if Color = LosingColor then
- begin
- PosVal := 128 - 16 * DistAn[Rank] - 12 * DistAn[Line];
- if DistAn[Rank] = 3 then
- PosVal := PosVal - 16;
- end
- else
- begin
- PosVal := 128 - 4 * KingDist;
- if (DistAn[Rank] >= 2) or (DistAn[Line] = 3) then
- PosVal := PosVal - 16;
- end;
- end { Mating }
- else
- begin
- Temp := PVControl[Color,Rook,Square];
- Temp2:= PVControl[Color,Bishop,Square];
-
- { Normal Evaluation function }
- case PieceCount of
- King:
- if MaterialLevel <= 0 then PosVal := -2 * Dist;
- Queen:
- PosVal := (Temp + Temp2) shr 2;
- Rook:
- PosVal := Temp;
- Bishop:
- PosVal := Temp2;
- Knight:
- begin
- Cnt := 0;
- for Dir := 0 to 7 do
- begin
- Sq := Square + KnightDir[Dir];
- if (Sq and $88) = 0 then
- Cnt := Cnt + AttackValue[Color,Sq];
- end;
- PosVal := Cnt shr 1 - Dist * 3;
- end;
- Pawn:
- if (Rank <> 0) and (Rank <> 7) then
- PosVal := PawnRank[Rank] + PawnFileFactor[Line] *
- (Rank + 2) - 12;
- end { case };
- end; { else }
- PVTable[Color,PieceCount,Square] := PosVal;
- end { for PieceCount };
- end { for Color };
- end { for Square };
-
- { Calculate PawnTab (indicates which squares contain pawns) }
- for Color := White to Black do
- for Rank := 0 to 7 do
- PawnTab[Color,Rank] := 0;
- for Square := $77 downto 0 do
- if (Square and $88) = 0 then
- with Board[Square] do
- if Piece = Pawn then
- begin
- Rank := Square shr 4;
- if Color = Black then Rank := 7 - Rank;
- PawnTab[Color,Rank] :=
- PawnTab[Color,Rank] or FileBitTab[Square and 7];
- end; { if }
- for Color := White to Black do { Initialize PawnBit }
- with PawnBit[-1,Color] do
- begin
- One := 0;
- Dob := 0;
- for Rank := 1 to 6 do
- begin
- Temp := PawnTab[Color,Rank];
- Dob := Dob or One and Temp;
- One := One or Temp;
- end;
- end;
-
- { Calculate PawnStructureValue }
- RootValue := PawnStrVal(PawnBit[-1,Player]) - PawnStrVal(PawnBit[-1,Opponent]);
-
- { Calculate static Value for Pawn structure }
- for Color := White to Black do
- begin
- OppColor := ColorType(1 - ord(Color));
- PawnFileTab := 0;
- LeftSideTab := 0;
- RightSideTab := 0;
- OpPassTab := $FF;
- BehindOpPass := 0;
- for Rank := 1 to 6 do { Squares where opponents pawns are passed pawns }
- begin
- OpPassTab := OpPassTab and not (PawnFileTab or
- LeftSideTab or RightSideTab);
- { Squares behind the opponents passed pawns }
- BehindOpPass := BehindOpPass or
- (OpPassTab and PawnTab[OppColor,7 - Rank]);
- { Squares which are covered by a Pawn }
- LeftChainTab := LeftSideTab;
- RightChainTab := RightSideTab;
- PawnFileTab := PawnTab[Color,Rank]; { Squares with pawns }
- { Squares with a Pawn beside them }
- LeftSideTab := (PawnFileTab shl 1) and $FF;
- RightSideTab := (PawnFileTab shr 1) and $FF;
- SideTab := LeftSideTab or RightSideTab;
- ChainTab := LeftChainTab or RightChainTab;
- { Squares covering a Pawn }
- Temp := PawnTab[Color,Succ(Rank)];
- LeftCoverTab := (Temp shl 1) and $FF;
- RightCoverTab := (Temp shr 1) and $FF;
- Sq := Rank shl 4;
- if Color = Black then Sq := Sq xor $70;
- Bit := 1;
- while Bit <> 0 do
- begin
- StrVal := 0;
- if (Bit and SideTab) <> 0 then
- StrVal := SidePawn
- else if (Bit and ChainTab) <> 0 then
- StrVal := ChainPawn;
- if (Bit and LeftCoverTab) <> 0 then
- StrVal := StrVal + CoverPawn;
- if (Bit and RightCoverTab) <> 0 then
- StrVal := StrVal + CoverPawn;
- if (Bit and PawnFileTab) <> 0 then
- StrVal := StrVal + NotMovePawn;
- PVTable[Color,Pawn,Sq] := PVTable[Color,Pawn,Sq] + StrVal;
- if (MaterialLevel <= 0) or (OppColor <> ProgramColor) then
- begin
- if (Bit and OpPassTab) <> 0 then { Passed pawns }
- PVTable[OppColor,Pawn,Sq] :=
- PVTable[OppColor,Pawn,Sq] + PassPawnRank[7 - Rank];
- if (Bit and BehindOpPass) <> 0 then { Rooks behind passed pawns }
- begin
- Temp := Sq xor $10;
- for TempColor := Black to White do
- begin
- PVTable[TempColor,Rook,Sq] :=
- PVTable[TempColor,Rook,Sq] + RookBehindPassPawn;
- if Rank = 6 then
- PVTable[TempColor,Rook,Temp] :=
- PVTable[TempColor,Rook,Temp] + RookBehindPassPawn;
- end; { for }
- end; { if }
- end; { if }
- Sq := Succ(Sq);
- Bit := (Bit shl 1) and $FF;
- end; { while }
- end; { for }
- end; { for }
-
- { Calculate penalty for blocking center pawns with a Bishop }
- for Sq := 3 to 4 do
- begin
- with Board[Sq + $10] do
- if (Piece = Pawn) and (Color = White) then
- PVTable[White,Bishop,Sq + $20] :=
- PVTable[White,Bishop,Sq + $20] - BishopBlockValue;
- with Board[Sq + $60] do
- if (Piece = Pawn) and (Color = Black) then
- PVTable[Black,Bishop,Sq + $50] :=
- PVTable[Black,Bishop,Sq + $50] - BishopBlockValue;
- end; { for }
-
- { Calculate RootValue }
- for Square := $77 downto 0 do
- if (Square and $88) = 0 then
- with Board[Square] do
- if Piece <> Empty then
- if Color = Player then
- RootValue := RootValue + PiecePosVal(Piece,Player,Square)
- else
- RootValue := RootValue - PiecePosVal(Piece,Opponent,Square);
- end; { with CC^ }
- end; { CalcPVTable }
-
-
- { Updates PawnBit and calculates Value when a Pawn is
- removed from Line }
- function DecPawnStrVal(Color: ColorType; Line: FileType): Integer;
- var
- Temp: Integer;
- begin
- with PawnBit[CC.Depth,Color] do
- begin
- Temp := not FileBitTab[Line];
- One := One and Temp or Dob;
- Dob := Dob and Temp;
- end;
- DecPawnStrVal := PawnStrVal(PawnBit[CC.Depth,Color]) -
- PawnStrVal(PawnBit[Pred(CC.Depth),Color]);
- end; { DecPawnStrVal }
-
- { Updates PawnBit and calculates Value when a Pawn moves
- from Old to New1 file }
- function MovePawnStrVal(Color: ColorType; New1, Old: FileType): Integer;
- var
- Temp, Temp2: Integer;
- begin
- with PawnBit[CC.Depth,Color] do
- begin
- Temp := FileBitTab[New1];
- Temp2 := not FileBitTab[Old];
- Dob := Dob or One and Temp;
- One := One and Temp2 or Dob or Temp;
- Dob := Dob and Temp2;
- end; { with }
- MovePawnStrVal := PawnStrVal(PawnBit[CC.Depth,Color]) -
- PawnStrVal(PawnBit[Pred(CC.Depth),Color]);
- end; { MovePawnStrVal }
-
- { Calculates STATIC Evaluation of the Move }
- function StateValu(MoveIt: MoveType): Integer;
- var
- Value: Integer;
- CastSquare,CornerSquare,EpSquare: SquareType;
- begin { StateValu }
- with CC, MoveIt do
- begin
- Value := 0;
- if Spe then
- if MovPiece = King then
- begin
- GenCastSquare(New1,CastSquare,CornerSquare); { Castling }
- Value := PiecePosVal(Rook,Player,CastSquare) -
- PiecePosVal(Rook,Player,CornerSquare);
- if New1 > Old then
- Inc(Value, CastValue[Short])
- else
- Inc(Value, CastValue[Long]);
- end
- else
- if MovPiece = Pawn then
- begin
- EpSquare := New1 - PawnDir[Player]; { E.p. capture }
- Value := PiecePosVal(Pawn,Opponent,EpSquare);
- end
- else
- { Pawnpromotion }
- Value := PiecePosVal(MovPiece,Player,Old) -
- PiecePosVal(Pawn, Player, Old) +
- DecPawnStrVal(Player, Old and 7);
- if Content <> Empty then { Normal moves }
- begin
- Value := Value + PiecePosVal(Content,Opponent,New1);
- { Penalty for exchanging Pieces when behind in material }
- if abs(MainEvalu) >= $100 then
- if Content <> Pawn then
- if (ProgramColor = Opponent) = (MainEvalu >= 0) then
- Dec(Value,ExchangeValue);
- end; { if }
- PawnBit[Depth] := PawnBit[Pred(Depth)]; { Calculate PawnBit }
- if (MovPiece = Pawn) and ((Content <> Empty) or Spe) then
- Value := Value + MovePawnStrVal(Player,New1 and 7,Old and 7);
- if (Content = Pawn) or Spe and (MovPiece = Pawn) then
- Value := Value - DecPawnStrVal(Opponent,New1 and 7);
- { Calculate Value of Move }
- StateValu := Value + PiecePosVal(MovPiece,Player,New1) -
- PiecePosVal(MovPiece,Player,Old);
- end; { with }
- end; { StateValu }
-
- var
- KillingMove: array[ 0..MaxPly,0..1] of MoveType;
- CheckTab: array[-1..MaxPly] of Boolean; { Table of checks }
- { Square of eventual pawn On 7th Rank }
- PassedPawn: array[-2..MaxPly] of EdgeSquareType;
-
- { Initializes KillingMove, CheckTab and PassedPawn }
- procedure ClearKillMove;
- const
- rank7: array[ColorType] of SquareType = ($60, $10);
- var
- Dep: DepthType;
- Col: ColorType;
- Sq: SquareType;
- i : byte;
- begin
- {Clear KillingMove }
- FillChar(KillingMove, SizeOf(KillingMove), 0);
- CheckTab[-1] := False; { No Check at First Ply }
- PassedPawn[-2] := -1;
- PassedPawn[-1] := -1;
- { Place eventual pawns On 7th Rank in PassedPawn }
- for Col := White to Black do
- for Sq := rank7[Col] to rank7[Col] + 7 do
- with CC, Board[Sq] do
- if (Piece = Pawn) and (Color = Col) then
- if Col = Player then
- PassedPawn[-2] := Sq
- else
- PassedPawn[-1] := Sq;
- end; { ClearKillMove }
-
- var
- SkipSearch: Boolean;
-
- { Communicates with the user }
- procedure CallSmallTalk;
- var
- SearchStateDepth: DepthType;
- StoredMove: MoveType;
- Msg: Word;
- OpAn: Boolean;
-
- { Backup the Search and setup Talk surroundings }
- procedure GetProgramState;
- var
- OldPlayer: ColorType;
- begin
- with CC do
- begin
- SearchStateDepth := Depth;
- while Depth > 0 do
- begin
- Dec(Depth);
- OldPlayer := Opponent;
- Opponent := Player;
- Player := OldPlayer;
- Perform(MovTab[Depth],True);
- end;
- Dec(Depth);
- if OpAn then
- TakeBackMove(MovTab[Depth]);
- end;
- end; { GetProgramState }
-
- { Restore the Search surroundings }
- procedure GetSearchState;
- var
- OldPlayer: ColorType;
- begin
- with CC do
- begin
- if OpAn then
- MakeMove(MovTab[Depth + 1]);
- Inc(Depth);
- while Depth < SearchStateDepth do
- begin
- Perform(MovTab[Depth],False);
- OldPlayer := Player;
- Player := Opponent;
- Opponent := OldPlayer;
- Inc(Depth);
- end;
- end;
- end; { GetSearchState }
-
- function WaitFor(Send, Receive: Word): Word;
- var
- Msg: Word;
- begin
- repeat
- Msg := Message(Send);
- until Msg in [tmAbort, Receive];
- WaitFor := Msg;
- end;
-
- begin { CallSmallTalk }
- with CC do
- begin
- if TaskTimer.TimeExpired then
- begin
- OpAn := OppAnalysis in State;
- GetProgramState; { Save Search surroundings }
- StoredMove := MovTab[Depth + 1];
-
- Msg := Message(tmTimeExpired);
- case Msg of
- tmEnterMove:
- begin
- SkipSearch := True;
- Include(State, MovePending);
- if (OppAnalysis in State) and EqMove(KeyMove,StoredMove) then
- begin
- Exclude(State, MovePending);
- Exclude(State, OppAnalysis);
- SkipSearch := False;
- EnterKeyMove;
- repeat until Message(tmFindMove) = tmFindMove;
- Include(State, Analysis);
- if WaitFor(tmFindMove, tmResume) <> tmAbort then
- Clock.Start
- else
- begin
- Exclude(State, Analysis);
- SkipSearch := True;
- end;
- end;
- end;
- tmAbort:
- begin
- Exclude(State, Analysis);
- Exclude(State, OppAnalysis);
- SkipSearch := True;
- end;
- tmResume:
- SkipSearch := False;
- end;
- GetSearchState; { Restore Search surroundings }
- end;
- end;
- end;
-
- type
- InfType = record
- PrincipVar: Boolean; { Principal Variation Search }
- Value, { Static incremental Evaluation }
- Evaluation: MaxType; { Evaluation of position }
- end;
-
- var
- StartInf: InfType; { Inf at First Ply }
- AlphaWindow: MaxType; { Alpha window Value }
- RepeatEvalu: MaxType; { MainEvalu at Ply One }
-
- { Performs the Search.
- On entry :
- Player is Next to Move.
- MovTab[Depth - 1] contains Last Move.
- Alpha, Beta contains the Alpha - Beta window.
- Ply contains the Depth of the Search.
- Inf contains various informations.
-
- On Exit :
- BestLine contains the principal variation.
- Search contains the Evaluation for Player }
- function Search(Alpha, Beta: MaxType; Ply: Integer; Inf: InfType;
- var BestLine: LineType): MaxType;
- var
- Line: LineType; { Best Line at Next Ply }
- CaptureSearch: Boolean; { Indicates capture Search }
- MaxVal: MaxType; { Maximal Evaluation (returned in Search) }
- NextPly: Integer; { Depth of Search at Next Ply }
- NextInf: InfType; { Information at Next Ply }
- ZeroWindow: Boolean; { Zero-Width Alpha-Beta-window }
-
- { Move type }
- MovGenType: (Main, SpecialCap, Kill, Normal);
-
- { Update KillingMove using BestMove }
- procedure UpdateKill(BestMove: MoveType);
- begin
- with CC, BestMove do
- if MovPiece <> Empty then
- begin
- { Update KillingMove unless the Move is a capture of
- Last moved Piece }
- if (MovTab[Depth - 1].MovPiece = Empty) or
- (New1 <> MovTab[Depth - 1].New1) then
- if (KillingMove[Depth,0].MovPiece = Empty) or
- EqMove(BestMove,KillingMove[Depth,1]) then
- begin
- KillingMove[Depth,1] := KillingMove[Depth,0];
- KillingMove[Depth,0] := BestMove;
- end
- else
- if not EqMove(BestMove,KillingMove[Depth,0]) then
- KillingMove[Depth,1] := BestMove;
- end; { if }
- end; { UpdateKill }
-
-
- { Test if Move has been generated before }
- function GeneratedBefore: Boolean;
- var
- i: 0..1;
- begin
- GeneratedBefore := True;
- with CC do
- begin
- if MovGenType <> Main then
- begin
- if EqMove(MovTab[Depth],BestLine[Depth]) then Exit;
- if not CaptureSearch then
- if MovGenType <> Kill then
- for i := 0 to 1 do
- if EqMove(MovTab[Depth], KillingMove[Depth,i]) then Exit;
- end;
- end;
- GeneratedBefore := False;
- end; { GeneratedBefore }
-
- { Tests Cut-off. CutVal contains the maximal Possible Evaluation }
- function Cut(CutVal: MaxType): Boolean;
- begin
- Cut := False;
- if CutVal <= Alpha then
- begin
- Cut := True;
- if MaxVal < CutVal then MaxVal := CutVal;
- end;
- end; { Cut }
-
- { Performs Move, calculates Evaluation, tests Cut-off etc. }
- function Update: Boolean;
- var
- Selection: Boolean;
- label AcceptMove,TakeBackMove,CutMove;
- begin { Update }
- with CC do
- begin
- with MovTab[Depth] do
- begin
- Inc(Nodes);
- NextPly := Ply - 1; { Calculate next ply }
- { MateSearch }
- if Level = MateSearch then
- begin
- { Perform Move On the Board }
- Perform(MovTab[Depth],False);
-
- { Check if Move is Legal }
- if Attacks(Opponent,PieceTab[Player,0].ISquare) then
- goto TakeBackMove;
- if Depth = 0 then
- LegalMoves := LegalMoves + 1;
- CheckTab[Depth] := False;
- PassedPawn[Depth] := -1;
- NextInf.Value := 0;
- NextInf.Evaluation := 0;
- if NextPly <= 0 then { Calculate Check and Perform evt. Cut-off }
- begin
- if NextPly = 0 then
- CheckTab[Depth] := Attacks(Player,PieceTab[Opponent,0].ISquare);
- if not CheckTab[Depth] then
- begin
- if Cut(NextInf.Value) then goto TakeBackMove;
- end;
- end;
- goto AcceptMove;
- end;
-
- { Make special limited CaptureSearch at First iteration }
- if MaxDepth <= 1 then
- if CaptureSearch and (Depth >= 2) then
- if not ((Content < MovPiece) or (MovGenType = SpecialCap) or
- (Old = MovTab[Depth - 2].New1)) then
- goto CutMove;
-
- { Calculate Next static incremental Evaluation }
- NextInf.Value := -Inf.Value + StateValu(MovTab[Depth]);
-
- { Calculate CheckTab (only checks with moved Piece are calculated).
- Giving Check does not Count as a Ply }
- CheckTab[Depth] := PieceAttacks(MovPiece,Player,New1,
- PieceTab[Opponent, 0].ISquare);
- if CheckTab[Depth] then NextPly := Ply;
-
- { Calculate PassedPawn. Moving a Pawn to 7th Rank does not Count
- as a Ply }
- PassedPawn[Depth] := PassedPawn[Depth - 2];
- if MovPiece = Pawn then
- if (New1 < $18) or (New1 >= $60) then
- begin
- PassedPawn[Depth] := New1;
- NextPly := Ply;
- end;
-
- { Perform Selection at Last Ply and in capture Search }
- Selection := (NextPly <= 0) and not CheckTab[Depth] and
- (Depth > 0);
- if Selection then { Check Evaluation }
- if Cut(NextInf.Value + 0) then goto CutMove;
-
- { Perform Move On the Board }
- Perform(MovTab[Depth],False);
-
- { Check if move is legal }
- if Attacks(Opponent,PieceTab[Player,0].ISquare) then
- goto TakeBackMove;
- if PassedPawn[Depth] >= 0 then { Check PassedPawn }
- with Board[PassedPawn[Depth]] do
- if (Piece <> Pawn) or (Color <> Player) then
- PassedPawn[Depth] := -1;
-
- { Count Number of Legal moves at Ply 0 }
- if Depth = 0 then
- LegalMoves := LegalMoves + 1;
-
- { Calculate Random }
- if Depth = 0 then
- NextInf.Value := NextInf.Value + Random(4);
-
- { Calculate the Evaluation for the position }
- NextInf.Evaluation := NextInf.Value;
- end { with };
-
- AcceptMove:
- Update := False;
- Exit;
-
- TakeBackMove:
- Perform(MovTab[Depth],True);
-
- CutMove:
- end; { with CC^ }
- Update := True;
- end { Update };
-
- { Calculate Draw bonus/penalty, and set Draw if the game is a Draw }
- function DrawGame: Boolean;
- var
- DrawCount: 0..4;
- SearchRepeat: RepeatType;
- SearchFifty: FiftyType;
- begin
- DrawGame := False;
- with CC do
- begin
- if Depth = 1 then
- begin
- SearchFifty := FiftyMoveCnt;
- SearchRepeat := Repetition(False);
-
- { 3rd Repetition is Draw }
- if SearchRepeat >= 3 then
- begin
- DrawGame := True;
- NextInf.Evaluation := 0;
- Exit;
- end;
- DrawCount := 0;
- if SearchFifty >= 96 then
- { 48 moves without Pawn }
- DrawCount := 3
- else if SearchRepeat >= 2 then
- { 2nd Repetition }
- DrawCount := 2
- else
-
- { 10 moves without Pawn }
- if SearchFifty >= 20 then
- DrawCount := 1;
- Inc(NextInf.Value, (RepeatEvalu div 4) * DrawCount);
- Inc(NextInf.Evaluation, (RepeatEvalu div 4) * DrawCount);
- end; { if }
-
- if Depth >= 3 then
- begin
- SearchRepeat := Repetition(True);
- if SearchRepeat >= 2 then
- begin
- { Immediate Repetition }
- DrawGame := True;
- NextInf.Evaluation := 0;
- Exit;
- end;
- end; { if }
- end; { with }
- end; { DrawGame }
-
- { Update BestLine and MainEvalu using Line and MaxVal }
- procedure UpdateBestLine;
- begin
- BestLine := Line;
- with CC do
- begin
- BestLine[Depth] := MovTab[Depth];
- if Depth = 0 then
- begin
- MainEvalu := MaxVal;
- if Level = MateSearch then
- MaxVal := AlphaWindow;
- end;
- end;
- end { UpdateBestLine };
-
-
- { The inner loop of the Search procedure. MovTab[Depth] contains
- the Move }
- function LoopBody: Boolean;
- var
- OldPlayer: ColorType;
- LastAnalysis: Boolean;
- label RepeatSearch,NotSearch;
- begin { LoopBody }
- LoopBody := False;
- if GeneratedBefore then Exit;
- with CC do
- begin
-
- { Initialize Line }
- if Depth < MaxPly then
- begin
- Line[Depth + 1] := ZeroMove;
- if MovGenType = Main then
- Line := BestLine;
- end;
-
- { PrincipVar indicates Principal Variation Search.
- ZeroWindow indicates zero - Width Alpha - Beta window }
- NextInf.PrincipVar := False;
- ZeroWindow := False;
- if Inf.PrincipVar then
- if MovGenType = Main then
- NextInf.PrincipVar := BestLine[Depth + 1].MovPiece <> Empty
- else
- ZeroWindow := MaxVal >= Alpha;
-
- RepeatSearch :
-
- { Update and test Cut-off }
- if Update then Exit;
-
- { Stop evt. Search }
- if Level = MateSearch then
- if (NextPly <= 0) and not CheckTab[Depth] then goto NotSearch;
- if DrawGame then goto NotSearch;
- if Depth >= MaxPly then goto NotSearch;
-
- { Analyse NextPly using a recursive call to Search }
- OldPlayer := Player; Player := Opponent; Opponent := OldPlayer;
- Inc(Depth);
- if ZeroWindow then
- NextInf.Evaluation := -Search(-Alpha - 1,- Alpha,NextPly,NextInf,Line)
- else
- NextInf.Evaluation := -Search(-Beta,- Alpha,NextPly,NextInf,Line);
- Dec(Depth);
- OldPlayer := Opponent; Opponent := Player; Player := OldPlayer;
-
- NotSearch :
- { Take Back Move }
- Perform(MovTab[Depth], True);
- if SkipSearch then
- begin
- LoopBody := True;
- Exit;
- end;
- LastAnalysis := Analysis in State;
-
- { Check elapsed tic time and test SkipSearch }
- CallSmallTalk;
-
- if (not SkipSearch) and (Analysis in State) {and ((Depth = 0) or
- not LastAnalysis) {and (MainEvalu > AlphaWindow)} then
- SkipSearch := Clock.TimeExpired;
-
- if (Analysis in State) and (MaxDepth <= 1) then
- SkipSearch := False;
-
- { Update MaxVal }
- MaxVal := Max(MaxVal,NextInf.Evaluation);
-
- { Update evt. BestLine }
- if EqMove(BestLine[Depth],MovTab[Depth]) then
- UpdateBestLine;
-
- { Update Alpha and test Cut-off }
- if Alpha < MaxVal then
- begin
- UpdateBestLine;
- if MaxVal >= Beta then
- begin
- { Cut-off }
- LoopBody := True;
- Exit;
- end;
-
- { Adjust MaxVal (Tolerance Search) }
- if (Ply >= 2) and Inf.PrincipVar and not ZeroWindow then
- MaxVal := Min(MaxVal + Tolerance,Beta - 1);
- Alpha := MaxVal;
- if ZeroWindow and not SkipSearch then
- begin
- { repeat Search with full window }
- ZeroWindow := False;
- goto RepeatSearch;
- end;
- end;
- end; { with CC^ }
- LoopBody := SkipSearch;
- end { LoopBody };
-
-
- { Generate Pawn promotions }
- function PawnPromotionGen: Boolean;
- var Promote: PieceType;
- begin
- PawnPromotionGen := True;
- with CC, MovTab[Depth] do
- begin
- Spe := True;
- for Promote := Queen to Knight do
- begin
- MovPiece := Promote;
- if LoopBody then Exit;
- end;
- Spe := False;
- end;
- PawnPromotionGen := False;
- end; { PawnPromotionGen }
-
- { Generates captures of the Piece On NewSq }
- function CapMovGen(NewSq: SquareType): Boolean;
- var
- NextSq, Sq: EdgeSquareType;
- i: IndexType;
- begin
- CapMovGen := True;
- with CC, MovTab[Depth] do
- begin
- Content := Board[NewSq].Piece;
- Spe := False;
- New1 := NewSq;
-
- { Pawn captures }
- MovPiece := Pawn;
- NextSq := New1 - PawnDir[Player];
- for Sq := NextSq - 1 to NextSq + 1 do
- with Board[Sq] do
- if (Sq <> NextSq) and ((Sq and $88) = 0) and
- (Piece = Pawn) and (Color = Player) then
- begin
- Old := Sq;
- if (New1 < 8) or (New1 >= $70) then
- begin
- if PawnPromotionGen then Exit;
- end
- else
- if LoopBody then Exit;
- end;
-
- { Other captures }
- for i := OfficerNo[Player] downto 0 do
- with PieceTab[Player,i] do
- if (IPiece <> Empty) and (IPiece <> Pawn) and
- PieceAttacks(IPiece, Player, ISquare, NewSq) then
- begin
- Old := ISquare;
- MovPiece := IPiece;
- if LoopBody then Exit;
- end;
- end;
- CapMovGen := False;
- end; { CapMovGen }
-
- { Generates non captures for the Piece On OldSq }
- function NonCapMovGen(OldSq: SquareType): Boolean;
- var
- First, Last, Dir: DirType;
- Direction: Integer;
- NewSq: EdgeSquareType;
- begin
- NonCapMovGen := True;
- with CC, MovTab[Depth] do
- begin
- Spe := False;
- Old := OldSq;
- MovPiece := Board[OldSq].Piece;
- Content := Empty;
- case MovPiece of
- King:
- for Dir := 7 downto 0 do
- begin
- NewSq := Old + DirTab[Dir];
- if (NewSq and $88) = 0 then
- if Board[NewSq].Piece = Empty then
- begin
- New1 := NewSq;
- if LoopBody then Exit;
- end;
- end;
- Knight:
- for Dir := 7 downto 0 do
- begin
- NewSq := Old + KnightDir[Dir];
- if (NewSq and $88) = 0 then
- if Board[NewSq].Piece = Empty then
- begin
- New1 := NewSq;
- if LoopBody then Exit;
- end;
- end;
- Queen,
- Rook,
- Bishop:
- begin
- First := 7;
- Last := 0;
- if MovPiece = Rook then First := 3;
- if MovPiece = Bishop then Last := 4;
- for Dir := First downto Last do
- begin
- Direction := DirTab[Dir];
- NewSq := Old + Direction;
- while (NewSq and $88) = 0 do
- begin
- if Board[NewSq].Piece <> Empty then Break;
- New1 := NewSq;
- if LoopBody then Exit;
- NewSq := New1 + Direction;
- end;
- end;
- end;
- Pawn:
- begin
- { One Square forward }
- New1 := Old + PawnDir[Player];
- if Board[New1].Piece = Empty then
- if (New1 < 8) or (New1 >= $70) then
- begin
- if PawnPromotionGen then Exit;
- end
- else
- begin
- if LoopBody then Exit;
- if (Old < $18) or (Old >= $60) then
- begin
- { Two squares forward }
- New1 := New1 + (New1 - Old);
- if Board[New1].Piece = Empty then
- if LoopBody then Exit;
- end;
- end;
- end;
- end { case };
- end { with };
- NonCapMovGen := False;
- end { NonCapMovGen };
-
- { Castling moves }
- function CastlingMovGen: Boolean;
- var
- CastDir: CastDirType;
- begin
- CastlingMovGen := True;
- with CC, MovTab[Depth] do
- begin
- Spe := True;
- MovPiece := King;
- Content := Empty;
- for CastDir := Short downto Long do
- with CastMove[Player,CastDir] do
- begin
- New1 := CastNew;
- Old := CastOld;
- if KillMovGen(MovTab[Depth]) then
- if LoopBody then Exit;
- end;
- end;
- CastlingMovGen := False;
- end; { CastlingMovGen }
-
- { E.p. captures }
- function EpCapMovGen: Boolean;
- var
- Sq: EdgeSquareType;
- begin
- EpCapMovGen := True;
- with CC, MovTab[Depth - 1] do
- if MovPiece = Pawn then
- if abs(New1 - Old) >= $20 then
- begin
- MovTab[Depth].Spe := True;
- MovTab[Depth].MovPiece := Pawn;
- MovTab[Depth].Content := Empty;
- MovTab[Depth].New1 := (New1 + Old) div 2;
- for Sq := New1 - 1 to New1 + 1 do if Sq <> New1 then
- if (Sq and $88) = 0 then
- begin
- MovTab[Depth].Old := Sq;
- if KillMovGen(MovTab[Depth]) then
- if LoopBody then Exit;
- end;
- end;
- EpCapMovGen := False;
- end { EpCapMovGen };
-
-
- { Generates the Next Move to be analysed.
- Controls the order of the movegeneration.
- The moves are generated in the order :
- Main variation
- Captures of Last moved Piece
- Killing moves
- Other captures
- Pawnpromovtions
- Castling
- Normal moves
- E.p. captures
- }
- procedure SearchMovGen;
- var
- Index: IndexType;
- KillNo: 0..1;
- begin
- with CC, MovTab[Depth] do
- begin
- { Generate Move from Main variation }
- if BestLine[Depth].MovPiece <> Empty then
- begin
- MovTab[Depth] := BestLine[Depth];
- MovGenType := Main;
- if LoopBody then Exit;
- end;
-
- { Captures of Last moved Piece }
- with MovTab[Depth - 1] do
- if MovPiece <> Empty then if MovPiece <> King then
- begin
- MovGenType := SpecialCap;
- if CapMovGen(New1) then Exit;
- end;
-
- { Killing moves }
- MovGenType := Kill;
- if not CaptureSearch then
- for KillNo := 0 to 1 do
- begin
- MovTab[Depth] := KillingMove[Depth,KillNo];
- if MovPiece <> Empty then
- if KillMovGen(MovTab[Depth]) then
- if LoopBody then Exit;
- end;
-
- { Captures }
- MovGenType := Normal;
- for Index := 1 to PawnNo[Opponent] do
- with PieceTab[Opponent,Index] do
- if IPiece <> Empty then
- with MovTab[Depth - 1] do
- if (MovPiece = Empty) or (ISquare <> New1) then
- if CapMovGen(ISquare) then Exit;
-
- { Pawn promotions }
- if CaptureSearch then
- if PassedPawn[Depth - 2] >= 0 then
- with Board[PassedPawn[Depth - 2]] do
- if (Piece = Pawn) and (Color = Player) then
- if NonCapMovGen(PassedPawn[Depth - 2]) then Exit;
-
- { Non-captures }
- if not CaptureSearch then
- begin
- { Castling }
- if CastlingMovGen then Exit;
-
- { other moves }
- for Index := PawnNo[Player] downto 0 do
- with PieceTab[Player,Index] do
- if IPiece <> Empty then
- if NonCapMovGen(ISquare) then Exit;
- end;
-
- { E.p. captures }
- if EpCapMovGen then Exit;
- end;
- end { SearchMovGen };
-
- label Stop;
- begin { Search }
- with CC do
- begin
-
- { Perform CaptureSearch if Ply<=0 and not Check }
- CaptureSearch := (Ply <= 0) and not CheckTab[Depth - 1];
- if CaptureSearch then { Initialize MaxVal }
- begin
- MaxVal := -Inf.Evaluation;
- if Alpha < MaxVal then
- begin
- Alpha := MaxVal;
- if MaxVal >= Beta then goto Stop;
- end;
- end
- else
- MaxVal := -(LoseValue - Depth * DepthFactor);
- SearchMovGen; { The Search loop }
- if SkipSearch then goto Stop;
- if MaxVal = -(LoseValue - Depth * DepthFactor) then { test stalemate }
- if not Attacks(Opponent,PieceTab[Player,0].ISquare) then
- begin
- MaxVal := 0;
- goto Stop;
- end;
- UpdateKill(BestLine[Depth]);
- end; { with }
- Stop:
- Search := MaxVal;
- end { Search };
-
- { Perform the Search }
- function CallSearch(Alpha,Beta: MaxType): MaxType;
- var
- MaxVal: MaxType;
- begin
- with CC do
- begin
- StartInf.PrincipVar := MainLine[0].MovPiece <> Empty;
- LegalMoves := 0;
- MaxVal := Search(Alpha,Beta,MaxDepth,StartInf,MainLine);
- if LegalMoves = 0 then MainEvalu := MaxVal;
- CallSearch := MaxVal;
- end;
- end; { CallSearch }
-
- { Checks whether the Search Time is used }
- function TimeUsed: Boolean;
- begin
- TimeUsed := False;
- with CC do
- if Analysis in State then
- TimeUsed := Clock.TimeExpired;
- end; { TimeUsed }
-
- var
- MaxVal: MaxType;
- CalcPVTime: real;
- begin { FindMove }
- with CC do
- begin
-
- { Initialize variables }
- Clock.Start;
- Nodes := 0;
- SkipSearch := False;
- ClearKillMove;
-
- { Calculate the P-V table }
- CalcPVTable;
- CalcPVTime := Clock.GetElapsedTime;
-
- { Initiate the search }
- StartInf.Value := -RootValue;
- StartInf.Evaluation := -RootValue;
- MaxDepth := 0;
- MainLine[0] := ZeroMove;
- MainEvalu := RootValue;
- AlphaWindow := MaxInt;
-
- { The iterative search loop }
- repeat
-
- { Update various variables }
- if MaxDepth <= 1 then RepeatEvalu := MainEvalu;
- AlphaWindow := Min(AlphaWindow,MainEvalu - $80);
- if Level = MateSearch then
- begin
- AlphaWindow := $6000;
- if MaxDepth > 0 then Inc(MaxDepth);
- end;
- Inc(MaxDepth);
-
- { Perform the search }
- MaxVal := CallSearch(AlphaWindow,$7F00);
- if (MaxVal <= AlphaWindow) and not SkipSearch and
- (Level <> MateSearch) and (LegalMoves > 0) then
- begin
-
- { repeat the search if the value falls below the Alpha - window }
- MainEvalu := AlphaWindow;
- MaxVal := CallSearch(-$7F00,AlphaWindow - Tolerance * 2);
- LegalMoves := 2;
- end;
- until SkipSearch or TimeUsed or (MaxDepth >= MaxLevel) or
- (LegalMoves <= 1) or (abs(MainEvalu) >= MateValue - 24 * DepthFactor);
- Clock.Stop;
- end; { with }
- end; { FindMove }
-
- end.