From 3fd43cd5fcb22bb65bf2a92a25d95d801b11c9e0 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Wed, 1 Jun 2016 18:58:58 -0700 Subject: Initial checkin for typhoon chess engine. --- src/save/._chess.h | Bin 0 -> 4096 bytes src/save/._main.c | Bin 0 -> 4096 bytes src/save/._root.c | Bin 0 -> 4096 bytes src/save/._util.c | Bin 0 -> 4096 bytes src/save/._vars.c | Bin 0 -> 4096 bytes src/save/chess.h | 2982 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/save/main.c | 745 +++++++++++++ src/save/root.c | 1462 ++++++++++++++++++++++++++ src/save/util.c | 1254 ++++++++++++++++++++++ src/save/vars.c | 483 +++++++++ 10 files changed, 6926 insertions(+) create mode 100755 src/save/._chess.h create mode 100755 src/save/._main.c create mode 100755 src/save/._root.c create mode 100755 src/save/._util.c create mode 100644 src/save/._vars.c create mode 100755 src/save/chess.h create mode 100755 src/save/main.c create mode 100755 src/save/root.c create mode 100755 src/save/util.c create mode 100644 src/save/vars.c (limited to 'src/save') diff --git a/src/save/._chess.h b/src/save/._chess.h new file mode 100755 index 0000000..c896cad Binary files /dev/null and b/src/save/._chess.h differ diff --git a/src/save/._main.c b/src/save/._main.c new file mode 100755 index 0000000..405bb0d Binary files /dev/null and b/src/save/._main.c differ diff --git a/src/save/._root.c b/src/save/._root.c new file mode 100755 index 0000000..99f2e5f Binary files /dev/null and b/src/save/._root.c differ diff --git a/src/save/._util.c b/src/save/._util.c new file mode 100755 index 0000000..e0d6387 Binary files /dev/null and b/src/save/._util.c differ diff --git a/src/save/._vars.c b/src/save/._vars.c new file mode 100644 index 0000000..d436fe6 Binary files /dev/null and b/src/save/._vars.c differ diff --git a/src/save/chess.h b/src/save/chess.h new file mode 100755 index 0000000..e86911a --- /dev/null +++ b/src/save/chess.h @@ -0,0 +1,2982 @@ +/** + +Copyright (c) Scott Gasch + +Module Name: + + chess.h + +Abstract: + +Author: + + Scott Gasch (scott.gasch@gmail.com) 7 Apr 2004 + +Revision History: + + $Id: chess.h 350 2008-06-28 03:12:34Z scott $ + +**/ + +#ifndef CHESS_ +#define CHESS_ + +#include "compiler.h" + +// +// Datatype wrappers +// +#define MIN_CHAR (0x80) +#define MAX_CHAR (0x7f) +typedef char CHAR; + +#define MIN_BYTE (0x00) +#define MAX_BYTE (0xff) +typedef unsigned char BYTE; + +#define MIN_UCHAR (0x00) +#define MAX_UCHAR (0xff) +typedef unsigned char UCHAR; +#define CAN_FIT_IN_UCHAR(x) ((x) <= MAX_UCHAR) + +#define MIN_SHORT (0x8000) +#define MAX_SHORT (0x7fff) +typedef signed short SHORT; + +#define MIN_USHORT (0x0000) +#define MAX_USHORT (0xffff) +typedef unsigned short USHORT; +#define CAN_FIT_IN_USHORT(x) ((x) <= MAX_USHORT) + +#define MIN_WORD (0x0000) +#define MAX_WORD (0xffff) +typedef unsigned short WORD; + +#define MIN_INT (0x80000000) +#define MAX_INT (0x7fffffff) +typedef signed int INT; + +#define MIN_DWORD (0x00000000) +#define MAX_DWORD (0xffffffff) +typedef unsigned int DWORD; + +#define MIN_UINT (0x00000000) +#define MAX_UINT (0xffffffff) +typedef unsigned int UINT; + +#define MIN_ULONG (0x00000000) +#define MAX_ULONG (0xffffffff) +typedef unsigned int ULONG; + +#define MIN_INT64 (0x8000000000000000) +#define MAX_INT64 (0x7fffffffffffffff) +typedef signed COMPILER_LONGLONG INT64; + +#define MIN_UINT64 (0x0000000000000000) +#define MAX_UINT64 (0xffffffffffffffff) +typedef unsigned COMPILER_LONGLONG UINT64; + +#define MIN_BITBOARD (0x0000000000000000) +#define MAX_BITBOARD (0xffffffffffffffff) +typedef UINT64 BITBOARD; + +#define MIN_BITV MIN_UINT +#define MAX_BITV MAX_UINT +typedef unsigned int BITV; + +#define MIN_FLAG MIN_UINT +#define MAX_FLAG MAX_UINT +typedef unsigned int FLAG; +#define IS_VALID_FLAG(x) (((x) == TRUE) || ((x) == FALSE)) + +typedef unsigned int COOR; +typedef unsigned int PIECE; +typedef signed int SCORE; + +#define BIT1 (0x1) +#define BIT2 (0x2) +#define BIT3 (0x4) +#define BIT4 (0x8) +#define BIT5 (0x10) +#define BIT6 (0x20) +#define BIT7 (0x40) +#define BIT8 (0x80) +#define BIT9 (0x100) +#define BIT10 (0x200) +#define BIT11 (0x400) +#define BIT12 (0x800) +#define BIT13 (0x1000) +#define BIT14 (0x2000) +#define BIT15 (0x4000) +#define BIT16 (0x8000) +#define BIT17 (0x10000) +#define BIT18 (0x20000) +#define BIT19 (0x40000) +#define BIT20 (0x80000) +#define BIT21 (0x100000) +#define BIT22 (0x200000) +#define BIT23 (0x400000) +#define BIT24 (0x800000) +#define BIT25 (0x1000000) +#define BIT26 (0x2000000) +#define BIT27 (0x4000000) +#define BIT28 (0x8000000) +#define BIT29 (0x10000000) +#define BIT30 (0x20000000) +#define BIT31 (0x40000000) +#define BIT32 (0x80000000) + +typedef struct _DLIST_ENTRY +{ + struct _DLIST_ENTRY *pFlink; + struct _DLIST_ENTRY *pBlink; +} DLIST_ENTRY; + +// +// Constants +// +#define YES (1) +#define NO (0) +#ifndef TRUE +#define TRUE (YES) +#endif +#ifndef FALSE +#define FALSE (NO) +#endif + +// +// Calculate the length of an array (i.e. number of elements) +// +#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) +#define COMMAND(x) \ + void (x)(CHAR *szInput, ULONG argc, CHAR *argv[], POSITION *pos) +#define MB (1024 * 1024) + +// +// Calculate the address of the base of the structure given its type, and an +// address of a field within the structure. +// +#ifndef OFFSET_OF +#define OFFSET_OF(field, type) \ + (ULONG)(&((type *)0)->field) +#endif +#ifndef CONTAINING_STRUCT +#define CONTAINING_STRUCT(address, type, field) \ + ((type *)((BYTE *)(address) - (BYTE *)(OFFSET_OF(field, type)))) +#endif + +#define WHITE (1) +#define BLACK (0) +#define BAD_COLOR (2) +#define IS_VALID_COLOR(x) (((x) == WHITE) || ((x) == BLACK)) +#define FOREACH_COLOR(x) for((x) = BLACK; (x) < BAD_COLOR; (x)++) +#define RANDOM_COLOR (rand() & 1) +#define FLIP(color) ((color) ^ 1) +#define COLOR_NAME(color) (((color) == WHITE) ? "white" : "black") + +#define MAX_MOVES_PER_PLY (218) +#define MAX_PLY_PER_SEARCH (64) +#define MAX_MOVES_PER_GAME (1024) +#define SMALL_STRING_LEN_CHAR (256) +#define MEDIUM_STRING_LEN_CHAR (8192) +#define BIG_STRING_LEN_CHAR (16384) + +#define QUARTER_PLY 16 +#define HALF_PLY 32 +#define THREE_QUARTERS_PLY 48 +#define ONE_PLY 64 +#define TWO_PLY 128 +#define THREE_PLY 192 +#define FOUR_PLY 256 +#define MAX_DEPTH_PER_SEARCH (MAX_PLY_PER_SEARCH * ONE_PLY) + +#define IS_VALID_DEPTH(x) (((x) >= 0) && \ + ((x) <= MAX_DEPTH_PER_SEARCH) && \ + (((x) & 0xfffff00f) == 0)) + +#define INFINITY (MAX_SHORT) +#define INVALID_SCORE (INFINITY + 1) +#define IS_VALID_SCORE(x) (((x) >= -INFINITY) && \ + ((x) <= +INFINITY)) +#define NMATE (+INFINITY - 200) +#define MATED_SCORE(ply) (-INFINITY + (ply)) + +#define IS_A_POWER_OF_2(x) (((x) & (x - 1)) == 0) +// +// Program version number +// +#define VERSION "1.00" +#define REVISION "$Id: chess.h 350 2008-06-28 03:12:34Z scott $\n" + +// +// Function decorators +// +#define IN +#define OUT +#define INOUT +#define UNUSED +#define NOTHING + +// ---------------------------------------------------------------------- +// +// PIECE: +// +// a piece (4 bits) = type (3 bits) + color (1 bit) +// +// 3 1 0 +// . . . . +// | | | +// type C +// +#define KING (6) // 110X +#define QUEEN (5) // 101X +#define ROOK (4) // 100X +#define BISHOP (3) // 011X +#define KNIGHT (2) // 010X +#define PAWN (1) // 001X +#define EMPTY (0) // 000X + +// (WHATEVER << 1) +#define BLACK_PAWN (2) // 0010 +#define BLACK_KNIGHT (4) // 0100 +#define BLACK_BISHOP (6) // 0110 +#define BLACK_ROOK (8) // 1000 +#define BLACK_QUEEN (10) // 1010 +#define BLACK_KING (12) // 1100 + +// (WHATEVER << 1) | WHITE +#define WHITE_PAWN (3) // 0011 +#define WHITE_KNIGHT (5) // 0101 +#define WHITE_BISHOP (7) // 0111 +#define WHITE_ROOK (9) // 1001 +#define WHITE_QUEEN (11) // 1011 +#define WHITE_KING (13) // 1101 + +#define PIECE_TYPE(p) ((p) >> 1) +#define PIECE_COLOR(p) ((p) & WHITE) +#define RANDOM_PIECE (rand() % 12) + 2; +#define IS_PAWN(p) (((p) & 0xE) == BLACK_PAWN) +#define IS_KNIGHT(p) (((p) & 0xE) == BLACK_KNIGHT) +#define IS_BISHOP(p) (((p) & 0xE) == BLACK_BISHOP) +#define IS_ROOK(p) (((p) & 0xE) == BLACK_ROOK) +#define IS_QUEEN(p) (((p) & 0xE) == BLACK_QUEEN) +#define IS_KING(p) (((p) & 0xE) == BLACK_KING) +#define IS_KNIGHT_OR_KING(p) (((p) & 0x6) == 0x4) +#define IS_VALID_PIECE(p) ((PIECE_TYPE((p)) >= PAWN) && \ + (PIECE_TYPE((p)) <= KING)) +#define IS_WHITE_PIECE(p) ((p) & WHITE) +#define IS_BLACK_PIECE(p) (!IS_WHITE(p)) +#define OPPOSITE_COLORS(p, q) (((p) ^ (q)) & WHITE) +#define SAME_COLOR(p,q) (!OPPOSITE_COLORS(p, q)) +#define GET_COLOR(p) ((p) & WHITE) + +// ---------------------------------------------------------------------- +// +// a coordinate (8 bits) = rank + file (0x88) +// +// 7 4 3 0 +// . . . . . . . . +// | | | +// rank file +// | | +// | +--- 0-7 = file A thru file H +// | +// +------------ 0-7 = rank 1 thru rank 8 +// +// given a coordinate, C, if C & 0x88 then it is illegal (off board) +// +// +// 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 +// +---------------------------------------+ +// 8 | 00 |:01:| 02 |:03:| 04 |:05:| 06 |:07:| 0x00 +// 7 |:10:| 11 |:12:| 13 |:14:| 15 |:16:| 17 | 0x10 +// 6 | 20 |:21:| 22 |:23:| 24 |:25:| 26 |:27:| 0x20 +// 5 |:30:| 31 |:32:| 33 |:34:| 35 |:36:| 37 | 0x30 +// 4 | 40 |:41:| 42 |:43:| 44 |:45:| 46 |:47:| 0x40 +// 3 |:50:| 51 |:52:| 53 |:54:| 55 |:56:| 57 | 0x50 +// 2 | 60 |:61:| 62 |:63:| 64 |:65:| 66 |:67:| 0x60 +// 1 |:70:| 71 |:72:| 73 |:74:| 75 |:76:| 77 | 0x70 +// +---------------------------------------+ +// A B C D E F G H +// +#define A (0) +#define B (1) +#define C (2) +#define D (3) +#define E (4) +#define F (5) +#define G (6) +#define H (7) + +#define A8 (0x00) +#define B8 (0x01) +#define C8 (0x02) +#define D8 (0x03) +#define E8 (0x04) +#define F8 (0x05) +#define G8 (0x06) +#define H8 (0x07) + +#define A7 (0x10) +#define B7 (0x11) +#define C7 (0x12) +#define D7 (0x13) +#define E7 (0x14) +#define F7 (0x15) +#define G7 (0x16) +#define H7 (0x17) + +#define A6 (0x20) +#define B6 (0x21) +#define C6 (0x22) +#define D6 (0x23) +#define E6 (0x24) +#define F6 (0x25) +#define G6 (0x26) +#define H6 (0x27) + +#define A5 (0x30) +#define B5 (0x31) +#define C5 (0x32) +#define D5 (0x33) +#define E5 (0x34) +#define F5 (0x35) +#define G5 (0x36) +#define H5 (0x37) + +#define A4 (0x40) +#define B4 (0x41) +#define C4 (0x42) +#define D4 (0x43) +#define E4 (0x44) +#define F4 (0x45) +#define G4 (0x46) +#define H4 (0x47) + +#define A3 (0x50) +#define B3 (0x51) +#define C3 (0x52) +#define D3 (0x53) +#define E3 (0x54) +#define F3 (0x55) +#define G3 (0x56) +#define H3 (0x57) + +#define A2 (0x60) +#define B2 (0x61) +#define C2 (0x62) +#define D2 (0x63) +#define E2 (0x64) +#define F2 (0x65) +#define G2 (0x66) +#define H2 (0x67) + +#define A1 (0x70) +#define B1 (0x71) +#define C1 (0x72) +#define D1 (0x73) +#define E1 (0x74) +#define F1 (0x75) +#define G1 (0x76) +#define H1 (0x77) + +// TODO: if use this anywhere that matters, consider double loop +// and unrolling the inside loop to prevent the multiple +// continue for invalid squares +#define FOREACH_SQUARE(x) for((x) = (A8); (x) <= (H1); (x)++) + +#define IS_ON_BOARD(c) (!((c) & 0x88)) +#define RANDOM_COOR (rand() & 0x77) +#define FILE_RANK_TO_COOR(f, r) (((8 - (r)) << 4) | (f)) +#define RANK(c) (8 - ((c) >> 4)) +#define RANK1(c) (((c) & 0xF0) == 0x70) +#define RANK2(c) (((c) & 0xF0) == 0x60) +#define RANK3(c) (((c) & 0xF0) == 0x50) +#define RANK4(c) (((c) & 0xF0) == 0x40) +#define RANK5(c) (((c) & 0xF0) == 0x30) +#define RANK6(c) (((c) & 0xF0) == 0x20) +#define RANK7(c) (((c) & 0xF0) == 0x10) +#define RANK8(c) (((c) & 0xF0) == 0x00) +#define FILE(c) ((c) & 0x0F) +#define FILEA(c) (((c) & 0x0F) == 0x00) +#define FILEB(c) (((c) & 0x0F) == 0x01) +#define FILEC(c) (((c) & 0x0F) == 0x02) +#define FILED(c) (((c) & 0x0F) == 0x03) +#define FILEE(c) (((c) & 0x0F) == 0x04) +#define FILEF(c) (((c) & 0x0F) == 0x05) +#define FILEG(c) (((c) & 0x0F) == 0x06) +#define FILEH(c) (((c) & 0x0F) == 0x07) +#define IS_WHITE_SQUARE_FR(f, r) (((f) + (r) - 1) & 1) +#define IS_WHITE_SQUARE_COOR(c) (IS_WHITE_SQUARE_FR(FILE(c), RANK(c))) +#define VALID_EP_SQUARE(c) (((c) == ILLEGAL_COOR) || ((c) == 0) || \ + (RANK3(c)) || \ + (RANK6(c))) + +// ---------------------------------------------------------------------- +// +// a move (4 bytes) = from/to, moved, captured, promoted and special +// +// 31 24 23 16 15 12 11 8 7 4 3 0 +// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +// | from | to | moved | capt | prom | flags | +// | | | | | +// | | | | | +// | | | | | +// | | +-------+-------+-- type and +// | | color of +// | | piece moved +// | | captured and +// | | promote targ +// +---------------+-- board location where +// move begins and ends +// +// +// Special flag asserted on special move types: +// +// if (special) +// { +// if (moved == PAWN) +// { +// if (promotion) +// { +// move is a promote (w/ or w/o capture) +// } +// else if (capture) +// { +// move is an en passant catpure +// } +// else +// { +// move is a double jump +// } +// } +// else if (moved == KING) +// { +// move is a castle (long or short) +// } +// else +// { +// invalid use of special flag +// } +// } +// +typedef union _MOVE +{ + ULONG uMove; + struct + { + UCHAR cFrom : 8; + UCHAR cTo : 8; + UCHAR pMoved : 4; + UCHAR pCaptured : 4; + UCHAR pPromoted : 4; + UCHAR bvFlags : 4; + }; +} MOVE; + +#define IS_SAME_MOVE(a, b) \ + (((a).uMove & 0x0FFFFFFF) == (((b).uMove & 0x0FFFFFFF))) + +#define MAKE_MOVE(from,to,piece,cap,prom,flags) \ + (((ULONG)(from)) | \ + ((ULONG)(to) << 8) | \ + ((ULONG)(piece) << 16) | \ + ((ULONG)(cap) << 20) | \ + ((ULONG)(prom) << 24) | \ + ((ULONG)(flags) << 28)) + +#define MAKE_MOVE_WITH_NO_PROM_OR_FLAGS(from,to,piece,cap) \ + (((ULONG)(from)) | \ + ((ULONG)(to) << 8) | \ + ((ULONG)(piece) << 16) | \ + ((ULONG)(cap) << 20)) + +// +// Move flag values: +// +#define MOVE_FLAG_SPECIAL (1) // en pass, prom, castle, double +#define IS_SPECIAL_MOVE(mv) \ + ((mv).uMove & 0x10000000) + +#define IS_CASTLE(mv) \ + (IS_SPECIAL_MOVE(mv) && IS_KING((mv).pMoved)) + +#ifdef DEBUG +#define IS_PROMOTION(mv) \ + (IS_SPECIAL_MOVE(mv) && ((mv).pPromoted)) +#else +#define IS_PROMOTION(mv) \ + ((mv).pPromoted) +#endif + +#define IS_CAPTURE_OR_PROMOTION(mv) \ + ((mv).uMove & 0x0FF00000) + +#define IS_ENPASSANT(mv) \ + (IS_SPECIAL_MOVE(mv) && (mv.pCaptured) && !IS_PROMOTION(mv)) + +#define IS_DOUBLE_JUMP(mv) \ + (IS_SPECIAL_MOVE(mv) && !IS_CAPTURE_OR_PROMOTION(mv)) + +#define MOVE_FLAG_KILLERMATE (2) // killer mate +#define IS_KILLERMATE_MOVE(mv) ((mv).uMove & 0x20000000) + +#define MOVE_FLAG_ESCAPING_CHECK (4) // escaping check +#define IS_ESCAPING_CHECK(mv) ((mv).uMove & 0x40000000) + +#define MOVE_FLAG_CHECKING (8) // checking move +#define IS_CHECKING_MOVE(mv) ((mv).uMove & 0x80000000) + +// ---------------------------------------------------------------------- +// +// Move list +// +#define MAX_MOVE_LIST (MAX_MOVES_PER_GAME + MAX_PLY_PER_SEARCH) + +// ---------------------------------------------------------------------- + +#pragma pack(1) +typedef union _ATTACK_BITV +{ + ULONG uWholeThing; + struct + { + union + { + UCHAR uSmall; + struct + { + UCHAR uNumAttacks : 3; // 0..2 + UCHAR uKing : 1; // 4..7 + UCHAR uQueen : 1; + UCHAR uRook : 1; + UCHAR uMinor : 1; + UCHAR uPawn : 1; + } small; + }; + + // -------------------- + union + { + USHORT uBig; + struct + { + USHORT uKing : 1; // 8..23 + USHORT uQueens : 4; + USHORT uRooks : 4; + USHORT uMinors : 4; + USHORT uPawns : 2; + USHORT uUnusedFlag1 : 1; + } big; + }; + + // ------------------- + union + { + UCHAR uXray; + struct + { + UCHAR uNumXrays : 3; + UCHAR uUnusedFlag2 : 1; + UCHAR uQueen : 1; // 24..28 + UCHAR uRook : 1; + UCHAR uBishop : 1; + UCHAR uUnusedFlag3 : 1; + } xray; + }; + }; +} +ATTACK_BITV; + +#define UNSAFE_FOR_MINOR(x) ((ULONG)((x).uWholeThing) & 0x00000080UL) +#define UNSAFE_FOR_ROOK(x) ((ULONG)((x).uWholeThing) & 0x000000C0UL) +#define UNSAFE_FOR_QUEEN(x) ((ULONG)((x).uWholeThing) & 0x000000E0UL) + +#define PAWN_BIT 0x00000080UL +#define MINOR_BIT 0x00000040UL +#define MINOR_XRAY_BIT 0x40000000UL +#define ROOK_BIT 0x00000020UL +#define ROOK_XRAY_BIT 0x20000000UL +#define QUEEN_BIT 0x00000010UL +#define QUEEN_XRAY_BIT 0x10000000UL + +#define INVALID_PIECE_INDEX (17) +#define IS_VALID_PIECE_INDEX(x) ((x) < INVALID_PIECE_INDEX) + +typedef union _SQUARE +{ + struct + { + PIECE pPiece; + ULONG uIndex; + }; + ATTACK_BITV bvAttacks[2]; +} +SQUARE; +#pragma pack() + +// +// POSITION +// +typedef struct _POSITION +{ + SQUARE rgSquare[128]; // where the pieces are, + // also, the attack table + UINT64 u64NonPawnSig; // hash signature + UINT64 u64PawnSig; // pawn hash signature + ULONG uToMove; // whose turn? + ULONG uFifty; // 50 moves w/o progress = draw + FLAG fCastled[2]; // record when sides have castled + BITV bvCastleInfo; // who can castle how? + COOR cEpSquare; // en-passant capture square + + COOR cPawns[2][8]; // location of pawns on the board + ULONG uPawnMaterial[2]; // pawn material of each side + ULONG uPawnCount[2]; // number of pawns for each side + + COOR cNonPawns[2][16]; // location of pieces on the board + ULONG uNonPawnMaterial[2]; // piece material of each side + ULONG uNonPawnCount[2][8]; // number of non-pawns / type + // 0 and 1 are the sum, + // 2..6 are per PIECE_TYPE + + ULONG uWhiteSqBishopCount[2]; // num bishops on white squares + SCORE iMaterialBalance[2]; // material balance + + // temporary storage space for use in eval + COOR cTrapped[2]; + ULONG uArmyScaler[2]; + ULONG uClosedScaler; + SCORE iScore[2]; + SCORE iReducedMaterialDownScaler[2]; + SCORE iTempScore; + ULONG uMinMobility[2]; + COOR cPiece; + ULONG uMinorsAtHome[2]; + BITBOARD bb; + ULONG uPiecesPointingAtKing[2]; +} +POSITION; + +// +// Castling permission bitvector flags. +// +#define CASTLE_WHITE_SHORT (1) +#define CASTLE_WHITE_LONG (2) +#define WHITE_CAN_CASTLE \ + (CASTLE_WHITE_SHORT | CASTLE_WHITE_LONG) +#define CASTLE_BLACK_SHORT (4) +#define CASTLE_BLACK_LONG (8) +#define BLACK_CAN_CASTLE \ + (CASTLE_BLACK_SHORT | CASTLE_BLACK_LONG) +#define CASTLE_NONE_POSSIBLE (0) +#define CASTLE_ALL_POSSIBLE \ + (WHITE_CAN_CASTLE | BLACK_CAN_CASTLE) + +// ---------------------------------------------------------------------- +// +// Move stack +// +#define MAX_MOVE_STACK (MAX_PLY_PER_SEARCH * \ + MAX_MOVES_PER_PLY) + +// +// Main part of the move stack, triads of moves, their values and flags +// that denote where the values came from +// +#define MVF_MOVE_SEARCHED (1) +#define MVF_EXTEND_MOVE (2) +#define MVF_REDUCE_MOVE (4) +#define MVF_PRUNE_SUBTREE (8) + +typedef struct _MOVE_STACK_MOVE_VALUE_FLAGS +{ + SCORE iValue; + MOVE mv; + BITV bvFlags; +} +MOVE_STACK_MOVE_VALUE_FLAGS; + +typedef struct _KEY_POINTER +{ + int iPointer; + ULONG uKey; +} +KEY_POINTER; + +typedef union _GENERATOR_FLAGS +{ + struct { + UCHAR uMoveCount; + UCHAR uKingMoveCount; + UCHAR uCheckingPieces; + UCHAR uUnused; + }; + ULONG uAllGenFlags; +} +GENERATOR_FLAGS; + +// +// A move stack +// +typedef struct _MOVE_STACK +{ +#ifdef DEBUG + POSITION board[MAX_PLY_PER_SEARCH]; +#endif + // + // Unblocked squares map, used for check detection + // + ULONG uUnblockedKeyValue[MAX_PLY_PER_SEARCH]; + KEY_POINTER sUnblocked[MAX_PLY_PER_SEARCH][128]; + + // + // The main move list, a long series of moves, their values and some + // flag bits to tell search what the values are based upon. + // + MOVE_STACK_MOVE_VALUE_FLAGS mvf[MAX_MOVE_STACK]; + + // + // uBegin[ply] and uEnd[ply] specify the start and end of moves gen- + // erated for a position at ply distance from the root. + // + ULONG uPly; + ULONG uBegin[MAX_PLY_PER_SEARCH]; + ULONG uEnd[MAX_PLY_PER_SEARCH]; + MOVE mvHash[MAX_PLY_PER_SEARCH]; + GENERATOR_FLAGS sGenFlags[MAX_PLY_PER_SEARCH]; +} +MOVE_STACK; + +#define GENERATE_NO_MOVES \ + ctx->sMoveStack.uEnd[ctx->uPly] = \ + ctx->sMoveStack.uBegin[ctx->uPly] + 1; \ + ctx->sMoveStack.uBegin[ctx->uPly + 1] = \ + ctx->sMoveStack.uEnd[ctx->uPly] + +#define GENERATE_ALL_MOVES (1) +#define GENERATE_ESCAPES (2) +#define GENERATE_CAPTURES_PROMS_CHECKS (3) +#define GENERATE_CAPTURES_PROMS (4) +#define GENERATE_DONT_SCORE (5) +#ifdef TEST +#define GENERATE_ALL_MOVES_CHECK_OK (6) +#endif + +#define MOVE_COUNT(ctx, x) \ + (((ctx)->sMoveStack.uEnd[(x)]) - (ctx)->sMoveStack.uBegin[(x)]) +#define ONE_LEGAL_MOVE(ctx, x) \ + (MOVE_COUNT(ctx, x) == 1) +#define NUM_KING_MOVES(ctx, x) \ + ((ctx)->sMoveStack.sGenFlags[(x)].uKingMoveCount) +#define NUM_CHECKING_PIECES(ctx, x) \ + ((ctx)->sMoveStack.sGenFlags[(x)].uCheckingPieces) + + +// ---------------------------------------------------------------------- +// +// Accumulators +// +typedef struct _COUNTERS +{ + struct + { + UINT64 u64Probes; + UINT64 u64OverallHits; + UINT64 u64UsefulHits; + UINT64 u64UpperBoundHits; + UINT64 u64LowerBoundHits; + UINT64 u64ExactScoreHits; + } + hash; + + struct + { + UINT64 u64Probes; + UINT64 u64Hits; + } + pawnhash; + + struct + { + UINT64 u64TotalNodeCount; + UINT64 u64QNodeCount; + UINT64 u64LeafCount; + UINT64 u64TerminalPositionCount; + UINT64 u64BetaCutoffs; + UINT64 u64BetaCutoffsOnFirstMove; + UINT64 u64NullMoves; + UINT64 u64NullMoveSuccess; +#ifdef TEST_NULL + UINT64 u64QuickNullSuccess; + UINT64 u64QuickNullDeferredSuccess; + UINT64 u64QuickNullFailures; + UINT64 u64AvoidNullSuccess; + UINT64 u64AvoidNullFailures; +#endif + UINT64 u64EvalHashHits; + UINT64 u64LazyEvals; + UINT64 u64FullEvals; + UINT64 u64CyclesInEval; + } + tree; + + struct + { + ULONG uNumSplits; + ULONG uNumSplitsTerminated; + } + parallel; + + struct + { + ULONG uPawnPush; + ULONG uCheck; + ULONG uMateThreat; + ULONG uRecapture; + ULONG uOneLegalMove; + ULONG uNoLegalKingMoves; + ULONG uMultiCheck; + ULONG uSingularMove; + ULONG uZugzwang; + ULONG uSingularReplyToCheck; + ULONG uEndgame; + ULONG uBotvinnikMarkoff; + ULONG uQExtend; + } + extension; + + struct + { + ULONG uProbes; + ULONG uHits; + } + egtb; +} +COUNTERS; + +typedef struct _CUMULATIVE_SEARCH_FLAGS +{ + // search + ULONG uNumChecks[2]; // not used + FLAG fInSuspiciousBranch; // not used + FLAG fInReducedDepthBranch; // not used + FLAG fAvoidNullmove; // restore + FLAG fVerifyNullmove; // restore + + // qsearch + ULONG uQsearchDepth; // restore + ULONG uQsearchNodes; // incremental + ULONG uQsearchCheckDepth; // restore + FLAG fCouldStandPat[2]; // restore +} +CUMULATIVE_SEARCH_FLAGS; + +// ---------------------------------------------------------------------- +// +// Searcher thread record +// +typedef struct _SPLIT_INFO +{ + // locks / counters + volatile ULONG uLock; // lock for this split node + volatile ULONG uNumThreadsHelping; // num threads in this node + volatile FLAG fTerminate; // signal helpers to terminate + + // moves in the split node + ULONG uRemainingMoves; // num moves remaining to do + ULONG uOnDeckMove; // next move to do + ULONG uNumMoves; + ULONG uAlreadyDone; + MOVE_STACK_MOVE_VALUE_FLAGS + mvf[MAX_MOVES_PER_PLY]; // the moves to search + + // input to the split node + POSITION sRootPosition; // the root position + MOVE mvPathToHere[MAX_PLY_PER_SEARCH]; // path from root to split + MOVE mvLast; // last move before split + ULONG uDepth; // remaining depth at split + INT iPositionExtend; // positional extension + SCORE iAlpha; // original alpha at split + SCORE iBeta; // beta at split + ULONG uSplitPositional; // pos->uPositional at split + CUMULATIVE_SEARCH_FLAGS sSearchFlags; // flags at split time + + // output from the split node + MOVE mvBest; // the best move + SCORE iBestScore; // it's score + COUNTERS sCounters; // updated counters + MOVE PV[MAX_PLY_PER_SEARCH]; + +#ifdef DEBUG + ULONG uSplitPly; + POSITION sSplitPosition; +#endif +} +SPLIT_INFO; + +typedef struct _PLY_INFO +{ +#ifdef DEBUG + POSITION sPosition; + SCORE iAlpha; + SCORE iBeta; +#endif + SCORE iEval; + INT iExtensionAmount; + FLAG fInCheck; + FLAG fInQsearch; + MOVE mv; + MOVE mvBest; + MOVE PV[MAX_PLY_PER_SEARCH]; + + SCORE iKingScore[2]; + ULONG uMinMobility[2]; + UINT64 u64NonPawnSig; + UINT64 u64PawnSig; + UINT64 u64Sig; + ULONG uFifty; + FLAG fCastled[2]; + ULONG uTotalNonPawns; + BITV bvCastleInfo; + COOR cEpSquare; +} +PLY_INFO; + +#define EVAL_HASH +#ifdef EVAL_HASH +#define EVAL_HASH_TABLE_SIZE (2097152) // 32Mb (per thread) +typedef struct _EVAL_HASH_ENTRY +{ + UINT64 u64Key; + SCORE iEval; + ULONG uPositional; + COOR cTrapped[2]; + +} EVAL_HASH_ENTRY; +#endif + +#define PAWN_HASH_TABLE_SIZE (131072) // 5.5Mb (per thread) +typedef struct _PAWN_HASH_ENTRY +{ + UINT64 u64Key; + BITBOARD bbPawnLocations[2]; + BITBOARD bbPasserLocations[2]; + BITBOARD bbStationaryPawns[2]; + SHORT iScore[2]; + UCHAR uCountPerFile[2][10]; + UCHAR uNumRammedPawns; + UCHAR uNumUnmovedPawns[2]; +} +PAWN_HASH_ENTRY; + +#define NUM_SPLIT_PTRS_IN_CONTEXT (8) + +typedef struct _SEARCHER_THREAD_CONTEXT +{ + ULONG uPly; // its distance from root + ULONG uPositional; // positional component of score + POSITION sPosition; // the board + MOVE_STACK sMoveStack; // the move stack + CUMULATIVE_SEARCH_FLAGS sSearchFlags; + PLY_INFO sPlyInfo[MAX_PLY_PER_SEARCH+1]; + MOVE mvKiller[MAX_PLY_PER_SEARCH][2]; + MOVE mvKillerEscapes[MAX_PLY_PER_SEARCH][2]; + MOVE mvNullmoveRefutations[MAX_PLY_PER_SEARCH]; + COUNTERS sCounters; + ULONG uThreadNumber; + SPLIT_INFO *pSplitInfo[NUM_SPLIT_PTRS_IN_CONTEXT]; + MOVE mvRootMove; + SCORE iRootScore; + ULONG uRootDepth; + PAWN_HASH_ENTRY rgPawnHash[PAWN_HASH_TABLE_SIZE]; +#ifdef EVAL_HASH + EVAL_HASH_ENTRY rgEvalHash[EVAL_HASH_TABLE_SIZE]; +#endif + CHAR szLastPV[SMALL_STRING_LEN_CHAR]; +} +SEARCHER_THREAD_CONTEXT; + +// +// When I added the pawn hash table and eval hash tables to searcher +// thread contexts they became too heavy to just allocate on the fly +// for things like checking the legality of SAN moves or seeing if an +// opening book line leads to a draw. This +// LIGHTWEIGHT_SEARCHER_CONTEXT structure can be cast into a full +// SEARCHER_THREAD_CONTEXT and passed safely into the Generate, +// MakeMove and UnMakeMove functions because they presently only +// need: +// +// 1. uPly +// 2. sPosition +// 3. sPlyInfo +// 4. sMoveStack +// +typedef struct _LIGHTWEIGHT_SEARCHER_CONTEXT +{ + ULONG uPly; + ULONG uPositional; + POSITION sPosition; + MOVE_STACK sMoveStack; + CUMULATIVE_SEARCH_FLAGS sSearchFlags; + PLY_INFO sPlyInfo[MAX_PLY_PER_SEARCH+1]; +} +LIGHTWEIGHT_SEARCHER_CONTEXT; + +// ---------------------------------------------------------------------- +// +// Global game options +// +typedef struct _GAME_OPTIONS +{ + ULONG uMyClock; + ULONG uOpponentsClock; + FLAG fGameIsRated; + FLAG fOpponentIsComputer; + ULONG uSecPerMove; + ULONG uMaxDepth; + UINT64 u64MaxNodeCount; + FLAG fShouldPonder; + FLAG fPondering; + FLAG fThinking; + FLAG fSuccessfulPonder; + MOVE mvPonder; + FLAG fShouldPost; + FLAG fForceDrawWorthZero; + ULONG uMyIncrement; + ULONG uMovesPerTimePeriod; + CHAR szAnalyzeProgressReport[SMALL_STRING_LEN_CHAR]; + FLAG fShouldAnnounceOpening; + SCORE iLastEvalScore; + UINT64 u64NodesSearched; + CHAR szLogfile[SMALL_STRING_LEN_CHAR]; + CHAR szEGTBPath[SMALL_STRING_LEN_CHAR]; + CHAR szBookName[SMALL_STRING_LEN_CHAR]; + ULONG uNumProcessors; + ULONG uNumHashTableEntries; + FLAG fNoInputThread; + FLAG fVerbosePosting; + FLAG fRunningUnderXboard; + FLAG fStatusLine; + FLAG fFastScript; + FLAG fShouldResign; + + enum + { + CLOCK_NORMAL = 0, + CLOCK_FIXED, + CLOCK_INCREMENT, + CLOCK_NONE + } + eClock; + + enum + { + GAME_UNKNOWN = 0, + GAME_BULLET, + GAME_BLITZ, + GAME_STANDARD + } + eGameType; + + enum + { + I_PLAY_WHITE = 0, + I_PLAY_BLACK, + FORCE_MODE, + EDIT_MODE, + ANALYZE_MODE + } + ePlayMode; +} +GAME_OPTIONS; +extern GAME_OPTIONS g_Options; + +// ---------------------------------------------------------------------- +// +// Move timer +// + +#define TIMER_SEARCHING_FIRST_MOVE (0x1) +#define TIMER_SEARCHING_IMPORTANT_MOVE (0x2) +#define TIMER_RESOLVING_ROOT_FL (0x4) +#define TIMER_RESOLVING_ROOT_FH (0x8) +#define TIMER_JUST_OUT_OF_BOOK (0x10) +#define TIMER_CURRENT_OBVIOUS (0x20) +#define TIMER_CURRENT_WONT_UNBLOCK (0x40) +#define TIMER_ROOT_POSITION_CRITICAL (0x80) +#define TIMER_MOVE_IMMEDIATELY (0x100) +#define TIMER_MANY_ROOT_FLS (0x200) +#define TIMER_STOPPING (0x400) +#define TIMER_SPLIT_FAILED (0x800) + +typedef struct _MOVE_TIMER +{ + double dStartTime; + double dEndTime; + double dSoftTimeLimit; + double dHardTimeLimit; + ULONG uNodeCheckMask; + volatile BITV bvFlags; +} +MOVE_TIMER; + +// ---------------------------------------------------------------------- +// +// Special move tag literals +// +#define ILLEGALMOVE ((ULONG)0x1DDD8888) + +// +// useful macros +// +#ifdef DEBUG +void +_assert(CHAR *szFile, ULONG uLine); + +#define ASSERT(x) if (x) \ + { ; } \ + else \ + { _assert(__FILE__, __LINE__); } +#define VERIFY(x) ASSERT(x) +#else +#define ASSERT(x) ; +#define VERIFY(x) x; +#endif // DEBUG + +#ifdef PERF_COUNTERS +#define INC(x) ((x) += 1) +#else +#define INC(x) +#endif + +#define BREAKPOINT SystemDebugBreakpoint() + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +#ifdef _X86_ +// +// Note: MAXU, MINU and ABS_DIFF require arguments with the high order +// bit CLEAR to work right. +// +// These are branchless constructs. MAXU and MINU are equivalent to +// MIN and MAX (with the above restriction on inputs) +// +// MIN0 and MAX0 are equivalent to MAX(0, x) and MIN(0, x). These +// macros have no restiction on argument type. +// +// ABS_DIFF is equivalent to abs(x - y). Again, x and y must have +// their high-order bits CLEAR for this to work. +// +// Note: gcc generates code with cmovs so no need for MAXU/MINU on +// that compiler. +// +#define MINU(x, y) \ + (((((int)((x)-(y)))>>31) & ((x)-(y)))+(y)) +#define MAXU(x, y) \ + (((((int)((x)-(y)))>>31) & ((y)-(x)))+(x)) +#define MIN0(x) \ + ((x) & (((int)(x)) >> 31)) +#define MAX0(x) \ + ((x) & ~(((int)(x)) >> 31)) + + +#define ABS_DIFF(a, b) \ + (((b)-(a)) - ((((b) - (a)) & (((int)((b) - (a))) >> 31) ) << 1)) + +#endif // _X86_ + +#ifndef MINU +#define MINU(x, y) (MIN((x), (y))) +#endif + +#ifndef MAXU +#define MAXU(x, y) (MAX((x), (y))) +#endif + +#ifndef MIN0 +#define MIN0(x) (MIN((x), 0)) +#endif + +#ifndef MAX0 +#define MAX0(x) (MAX((x), 0)) +#endif + +#ifndef ABS_DIFF +#define ABS_DIFF(a, b) (abs((a) - (b))) +#endif + +#define FILE_DISTANCE(a, b) (ABS_DIFF(FILE((a)), FILE((b)))) +#define RANK_DISTANCE(a, b) (ABS_DIFF(((a) & 0xF0), ((b) & 0xF0)) >> 4) +#define REAL_DISTANCE(a, b) (MAXU(FILE_DISTANCE((a), (b)), \ + RANK_DISTANCE((a), (b)))) +#ifdef DEBUG +#define DISTANCE(a, b) DistanceBetweenSquares((a), (b)) +#else +#define DISTANCE(a, b) g_pDistance[(a) - (b)] +#endif // DEBUG + +#define IS_EMPTY( square ) (!(square)) +#define IS_OCCUPIED( square ) ((square)) + +#define IS_DEAD( listindex ) ((listindex) > 119) +#define IS_ALIVE( listindex ) ((listindex) <= 119) +#define ILLEGAL_COOR (0x88) +#define EDGE_DISTANCE(c) (MIN(MIN(abs(RANK(c) - 7), RANK(c)), \ + MIN(abs(FILE(c) - 7), FILE(c)))) +#define NOT_ON_EDGE(c) ((((c) & 0xF0) != 0x00) && \ + (((c) & 0xF0) != 0x70) && \ + (((c) & 0x0F) != 0x00) && \ + (((c) & 0x0F) != 0x07)) +#define ON_EDGE(c) (RANK8(c) || \ + RANK1(c) || \ + FILEA(c) || \ + FILEH(c)) +#define CORNER_DISTANCE(c) (MAX(MIN((ULONG)abs(RANK(c) - 7), RANK(c)),\ + MIN((ULONG)abs(FILE(c) - 7), FILE(c)))) +#define IN_CORNER(c) (((c) == A8) || \ + ((c) == A1) || \ + ((c) == H8) || \ + ((c) == H1)) +#define WHITE_CORNER_DISTANCE(c) (MIN(DISTANCE((c), A8), \ + DISTANCE((c), H1))) +#define BLACK_CORNER_DISTANCE(c) (MIN(DISTANCE((c), H8), \ + DISTANCE((c), A1))) +#define SYM_SQ(c) ((7 - (((c) & 0xF0) >> 4)) << 4) \ + | (7 - ((c) & 0x0F)) +#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a[0]))) +#define MAKE_PSQT(a, b, c, d) (((a) << 24) | ((b) << 16) | \ + ((c) << 8) | (d)) +#define TO64(x) ((x) & 0x7) + ((0x7 - ((x)>>4)) << 3) +#define COOR_TO_BIT_NUMBER(c) (((((c) & 0x70) >> 1) | ((c) & 0x7))) +#define SLOWCOOR_TO_BB(c) (1ULL << COOR_TO_BIT_NUMBER(c)) +#define COOR_TO_BB(c) (BBSQUARE[COOR_TO_BIT_NUMBER(c)]) +#define SLOW_BIT_NUMBER_TO_COOR(b) ((((b) / 8) << 4) + ((b) & 7)) +#define BIT_NUMBER_TO_COOR(b) ((((b) & 0xF8) << 1) | ((b) & 7)) + +#define BBRANK88 \ + SLOWCOOR_TO_BB(A8) | SLOWCOOR_TO_BB(B8) | SLOWCOOR_TO_BB(C8) | \ + SLOWCOOR_TO_BB(D8) | SLOWCOOR_TO_BB(E8) | SLOWCOOR_TO_BB(F8) | \ + SLOWCOOR_TO_BB(G8) | SLOWCOOR_TO_BB(H8) + +#define BBRANK77 \ + SLOWCOOR_TO_BB(A7) | SLOWCOOR_TO_BB(B7) | SLOWCOOR_TO_BB(C7) | \ + SLOWCOOR_TO_BB(D7) | SLOWCOOR_TO_BB(E7) | SLOWCOOR_TO_BB(F7) | \ + SLOWCOOR_TO_BB(G7) | SLOWCOOR_TO_BB(H7) + +#define BBRANK66 \ + SLOWCOOR_TO_BB(A6) | SLOWCOOR_TO_BB(B6) | SLOWCOOR_TO_BB(C6) | \ + SLOWCOOR_TO_BB(D6) | SLOWCOOR_TO_BB(E6) | SLOWCOOR_TO_BB(F6) | \ + SLOWCOOR_TO_BB(G6) | SLOWCOOR_TO_BB(H6) + +#define BBRANK55 \ + SLOWCOOR_TO_BB(A5) | SLOWCOOR_TO_BB(B5) | SLOWCOOR_TO_BB(C5) | \ + SLOWCOOR_TO_BB(D5) | SLOWCOOR_TO_BB(E5) | SLOWCOOR_TO_BB(F5) | \ + SLOWCOOR_TO_BB(G5) | SLOWCOOR_TO_BB(H5) + +#define BBRANK44 \ + SLOWCOOR_TO_BB(A4) | SLOWCOOR_TO_BB(B4) | SLOWCOOR_TO_BB(C4) | \ + SLOWCOOR_TO_BB(D4) | SLOWCOOR_TO_BB(E4) | SLOWCOOR_TO_BB(F4) | \ + SLOWCOOR_TO_BB(G4) | SLOWCOOR_TO_BB(H4) + +#define BBRANK33 \ + SLOWCOOR_TO_BB(A3) | SLOWCOOR_TO_BB(B3) | SLOWCOOR_TO_BB(C3) | \ + SLOWCOOR_TO_BB(D3) | SLOWCOOR_TO_BB(E3) | SLOWCOOR_TO_BB(F3) | \ + SLOWCOOR_TO_BB(G3) | SLOWCOOR_TO_BB(H3) + +#define BBRANK22 \ + SLOWCOOR_TO_BB(A2) | SLOWCOOR_TO_BB(B2) | SLOWCOOR_TO_BB(C2) | \ + SLOWCOOR_TO_BB(D2) | SLOWCOOR_TO_BB(E2) | SLOWCOOR_TO_BB(F2) | \ + SLOWCOOR_TO_BB(G2) | SLOWCOOR_TO_BB(H2) + +#define BBRANK11 \ + SLOWCOOR_TO_BB(A1) | SLOWCOOR_TO_BB(B1) | SLOWCOOR_TO_BB(C1) | \ + SLOWCOOR_TO_BB(D1) | SLOWCOOR_TO_BB(E1) | SLOWCOOR_TO_BB(F1) | \ + SLOWCOOR_TO_BB(G1) | SLOWCOOR_TO_BB(H1) + +#define BBFILEA \ + SLOWCOOR_TO_BB(A1) | SLOWCOOR_TO_BB(A2) | SLOWCOOR_TO_BB(A3) | \ + SLOWCOOR_TO_BB(A4) | SLOWCOOR_TO_BB(A5) | SLOWCOOR_TO_BB(A6) | \ + SLOWCOOR_TO_BB(A7) | SLOWCOOR_TO_BB(A8) + +#define BBFILEB \ + SLOWCOOR_TO_BB(B1) | SLOWCOOR_TO_BB(B2) | SLOWCOOR_TO_BB(B3) | \ + SLOWCOOR_TO_BB(B4) | SLOWCOOR_TO_BB(B5) | SLOWCOOR_TO_BB(B6) | \ + SLOWCOOR_TO_BB(B7) | SLOWCOOR_TO_BB(B8) + +#define BBFILEC \ + SLOWCOOR_TO_BB(C1) | SLOWCOOR_TO_BB(C2) | SLOWCOOR_TO_BB(C3) | \ + SLOWCOOR_TO_BB(C4) | SLOWCOOR_TO_BB(C5) | SLOWCOOR_TO_BB(C6) | \ + SLOWCOOR_TO_BB(C7) | SLOWCOOR_TO_BB(C8) + +#define BBFILED \ + SLOWCOOR_TO_BB(D1) | SLOWCOOR_TO_BB(D2) | SLOWCOOR_TO_BB(D3) | \ + SLOWCOOR_TO_BB(D4) | SLOWCOOR_TO_BB(D5) | SLOWCOOR_TO_BB(D6) | \ + SLOWCOOR_TO_BB(D7) | SLOWCOOR_TO_BB(D8) + +#define BBFILEE \ + SLOWCOOR_TO_BB(E1) | SLOWCOOR_TO_BB(E2) | SLOWCOOR_TO_BB(E3) | \ + SLOWCOOR_TO_BB(E4) | SLOWCOOR_TO_BB(E5) | SLOWCOOR_TO_BB(E6) | \ + SLOWCOOR_TO_BB(E7) | SLOWCOOR_TO_BB(E8) + +#define BBFILEF \ + SLOWCOOR_TO_BB(F1) | SLOWCOOR_TO_BB(F2) | SLOWCOOR_TO_BB(F3) | \ + SLOWCOOR_TO_BB(F4) | SLOWCOOR_TO_BB(F5) | SLOWCOOR_TO_BB(F6) | \ + SLOWCOOR_TO_BB(F7) | SLOWCOOR_TO_BB(F8) + +#define BBFILEG \ + SLOWCOOR_TO_BB(G1) | SLOWCOOR_TO_BB(G2) | SLOWCOOR_TO_BB(G3) | \ + SLOWCOOR_TO_BB(G4) | SLOWCOOR_TO_BB(G5) | SLOWCOOR_TO_BB(G6) | \ + SLOWCOOR_TO_BB(G7) | SLOWCOOR_TO_BB(G8) + +#define BBFILEH \ + SLOWCOOR_TO_BB(H1) | SLOWCOOR_TO_BB(H2) | SLOWCOOR_TO_BB(H3) | \ + SLOWCOOR_TO_BB(H4) | SLOWCOOR_TO_BB(H5) | SLOWCOOR_TO_BB(H6) | \ + SLOWCOOR_TO_BB(H7) | SLOWCOOR_TO_BB(H8) + +#define BBRANK72 \ + BBRANK77 | BBRANK66 | BBRANK55 | BBRANK44 | BBRANK33 | BBRANK22 + +#define BBRANK62 \ + BBRANK66 | BBRANK55 | BBRANK44 | BBRANK33 | BBRANK22 + +#define BBRANK52 \ + BBRANK55 | BBRANK44 | BBRANK33 | BBRANK22 + +#define BBRANK42 \ + BBRANK44 | BBRANK33 | BBRANK22 + +#define BBRANK32 \ + BBRANK33 | BBRANK22 + +#define BBRANK27 \ + BBRANK22 | BBRANK33 | BBRANK44 | BBRANK55 | BBRANK66 | BBRANK77 + +#define BBRANK37 \ + BBRANK33 | BBRANK44 | BBRANK55 | BBRANK66 | BBRANK77 + +#define BBRANK47 \ + BBRANK44 | BBRANK55 | BBRANK66 | BBRANK77 + +#define BBRANK57 \ + BBRANK55 | BBRANK66 | BBRANK77 + +#define BBRANK67 \ + BBRANK66 | BBRANK77 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// util.c +// +COMMAND(LearnPsqtFromPgn); + +COMMAND(GeneratePositionAndBestMoveSuite); + +CHAR * +WalkPV(SEARCHER_THREAD_CONTEXT *ctx); + +CHAR * +ReadNextGameFromPgnFile(FILE *pf); + +COOR +DistanceBetweenSquares(COOR a, COOR b); + +void CDECL +Trace(CHAR *szMessage, ...); + +void CDECL +Log(CHAR *szMessage, ...); + +void CDECL +Bug(CHAR *szMessage, ...); + +char * +FindChunk(char *sz, ULONG uTargetChunk); + +FLAG +BufferIsZeroed(BYTE *p, ULONG u); + +char * +ColorToString(ULONG u); + +char * +CoorToString(COOR c); + +char * +ScoreToString(SCORE iScore); + +char * +TimeToString(double d); + +ULONG +AcquireSpinLock(volatile ULONG *pSpinlock); + +FLAG +TryAcquireSpinLock(volatile ULONG *pSpinlock); + +void +ReleaseSpinLock(volatile ULONG *pSpinLock); + +BYTE +Checksum(BYTE *p, ULONG uSize); + +FLAG +BackupFile(CHAR *szFile); + +void +UtilPrintPV(SEARCHER_THREAD_CONTEXT *ctx, + SCORE iAlpha, + SCORE iBeta, + SCORE iScore, + MOVE mv); + +#define CANNOT_INITIALIZE_SPLIT (1) +#define INCONSISTENT_POSITION (2) +#define UNEXPECTED_SYSTEM_CALL_FAILURE (3) +#define SHOULD_NOT_GET_HERE (4) +#define GOT_ILLEGAL_MOVE_WHILE_PONDERING (5) +#define CANNOT_OFFICIALLY_MAKE_MOVE (6) +#define DETECTED_INCORRECT_INITIALIZATION (7) +#define INCONSISTENT_STATE (8) +#define FATAL_ACCESS_DENIED (9) +#define TESTCASE_FAILURE (10) +#define INITIALIZATION_FAILURE (11) + +void +UtilPanic(ULONG uPanicCode, + POSITION *pos, + void *a1, + void *a2, + void *a3, + char *file, ULONG line); + + +// +// main.c +// +#define LOGFILE_NAME "typhoon.log" +extern FILE *g_pfLogfile; + +void +Banner(void); + +FLAG +PreGameReset(FLAG fResetBoard); + +// +// system dependent exports (see win32.c or fbsd.c) +// +typedef ULONG (THREAD_ENTRYPOINT)(ULONG); + +#ifdef DEBUG +ULONG +GetHeapMemoryUsage(void); +#endif + +void +SystemDebugBreakpoint(void); + +CHAR * +SystemStrDup(CHAR *p); + +double +SystemTimeStamp(void); + +FLAG +SystemDoesFileExist(CHAR *szFilename); + +void +SystemDeferExecution(ULONG uMs); + +void +SystemFreeMemory(void *pMem); + +void * +SystemAllocateMemory(ULONG dwSizeBytes); + +FLAG +SystemMakeMemoryReadOnly(void *pMemory, ULONG dwSizeBytes); + +FLAG +SystemMakeMemoryNoAccess(void *pMemory, ULONG dwSizeBytes); + +FLAG +SystemMakeMemoryReadWrite(void *pMemory, ULONG dwSizeBytes); + +FLAG +SystemDependentInitialization(void); + +UINT64 FASTCALL +SystemReadTimeStampCounter(void); + +// +// This routine _must_ implement a full memory fence -- it is used in +// util.c to implement spinlocks. +// +FLAG +SystemCreateThread(THREAD_ENTRYPOINT *pEntry, ULONG uParam, ULONG *puHandle); + +FLAG +SystemWaitForThreadToExit(ULONG uThreadHandle); + +FLAG +SystemGetThreadExitCode(ULONG uThreadHandle, ULONG *puCode); + +FLAG +SystemDeleteThread(ULONG uThreadHandle); + +CHAR * +SystemGetDateString(void); + +CHAR * +SystemGetTimeString(void); + +FLAG +SystemCopyFile(CHAR *szSource, CHAR *szDest); + +FLAG +SystemDeleteFile(CHAR *szFile); + +ULONG +SystemCreateLock(void); + +FLAG +SystemDeleteLock(ULONG u); + +FLAG +SystemBlockingWaitForLock(ULONG u); + +FLAG +SystemReleaseLock(ULONG u); + +ULONG +SystemCreateSemaphore(ULONG u); + +FLAG +SystemDeleteSemaphore(ULONG u); + +void +SystemReleaseSemaphoreResource(ULONG u); + +void +SystemObtainSemaphoreResource(ULONG u); + + +// +// fen.c +// +#define STARTING_POSITION_IN_FEN \ + "rnbqkbnr/pppppppp/--------/8/8/--------/PPPPPPPP/RNBQKBNR w KQkq - 0 1" + +FLAG +LooksLikeFen(char *szFen); + +char * +PositionToFen(POSITION *p); + +FLAG +FenToPosition(POSITION *p, char *szFen); + +// +// testfen.c +// +#ifdef TEST +void +TestFenCode(void); +#endif + +// +// piece.c +// +// INVERTED VALUE == (VALUE_QUEEN / VALUE_PIECE) * VALUE_PAWN; +#define VALUE_PAWN 100 +#define INVERT_PAWN 900 +#define VALUE_KNIGHT 300 +#define INVERT_KNIGHT 300 +#define VALUE_BISHOP 300 +#define INVERT_BISHOP 300 +#define VALUE_ROOK 500 +#define INVERT_ROOK 180 +#define VALUE_QUEEN 975 +#define INVERT_QUEEN 100 +#define VALUE_KING (INFINITY) +#define INVERT_KING 1 + +#define VALUE_FULL_ARMY (8 * VALUE_PAWN) + (2 * VALUE_KNIGHT) + \ + (2 * VALUE_BISHOP) + (2 * VALUE_ROOK) + \ + VALUE_QUEEN + VALUE_KING +#define VALUE_MAX_ARMY (9 * VALUE_QUEEN) + (2 * VALUE_KNIGHT) + \ + (2 * VALUE_BISHOP) + (2 * VALUE_ROOK) + \ + VALUE_KING + +typedef struct _PIECE_DATA +{ + ULONG uValue; + ULONG uValueOver100; + ULONG uInvertedValue; + CHAR *szName; +} PIECE_DATA; + +extern PIECE_DATA g_PieceData[8]; + +#define PIECE_VALUE_OVER_100(p) (g_PieceData[PIECE_TYPE(p)].uValueOver100) +extern ULONG +PieceValueOver100(PIECE p); + +#define PIECE_VALUE(p) (g_PieceData[PIECE_TYPE(p)].uValue) +extern ULONG +PieceValue(PIECE p); + +#define INVERTED_PIECE_VALUE(p) (g_PieceData[PIECE_TYPE(p)].uInvertedValue) +extern ULONG +PieceInvertedValue(PIECE p); + +CHAR * +PieceAbbrev(PIECE p); + +// +// board.c +// +FLAG +VerifyPositionConsistency(POSITION *pos, FLAG fContinueOnError); + +FLAG +PositionsAreEquivalent(POSITION *p1, POSITION *p2); + +CHAR * +DrawTextBoardFromPosition(POSITION *pos); + +void +DumpPosition(POSITION *pos); + +CHAR * +CastleInfoString(BITV bv); + +void +SetRootToInitialPosition(void); + +// +// move.c +// +void +SlidePiece(POSITION *pos, COOR cFrom, COOR cTo); + +PIECE +LiftPiece(POSITION *pos, COOR cSquare); + +void +PlacePiece(POSITION *pos, COOR cSquare, PIECE pPiece); + +FLAG +MakeMove(SEARCHER_THREAD_CONTEXT *ctx, + MOVE mv); + +void +UnmakeMove(SEARCHER_THREAD_CONTEXT *ctx, + MOVE mv); + +FLAG +MakeUserMove(SEARCHER_THREAD_CONTEXT *ctx, MOVE mvUser); + +void +DumpMove(ULONG u); + +// +// movesup.c +// +#define MOVE_TO_INDEX(mv) (((mv).uMove & 0xFFFF) + \ + (0x10000 * GET_COLOR(mv.pMoved))) + +COOR +FasterExposesCheck(POSITION *pos, + COOR cRemove, + COOR cLocation); + +COOR +ExposesCheck(POSITION *pos, + COOR cRemove, + COOR cLocation); + +COOR +ExposesCheckEp(POSITION *pos, + COOR cTest, + COOR cIgnore, + COOR cBlock, + COOR cKing); + +FLAG +IsAttacked(COOR cTest, POSITION *pos, ULONG uSide); + +FLAG +InCheck(POSITION *pos, ULONG uSide); + +FLAG +SanityCheckMove(POSITION *pos, MOVE mv); + +FLAG +LooksLikeFile(CHAR c); + +FLAG +LooksLikeRank(CHAR c); + +FLAG +LooksLikeCoor(CHAR *szData); + +CHAR * +StripMove(CHAR *szMove); + +ULONG +LooksLikeMove(CHAR *szData); + +void FASTCALL +SelectBestWithHistory(SEARCHER_THREAD_CONTEXT *ctx, ULONG u); + +void FASTCALL +SelectBestNoHistory(SEARCHER_THREAD_CONTEXT *ctx, ULONG u); + +void FASTCALL +SelectMoveAtRoot(SEARCHER_THREAD_CONTEXT *ctx, ULONG u); + +#define NOT_MOVE 0 +#define MOVE_ICS 1 +#define MOVE_SAN 2 + +COMMAND(PerftCommand); + + +// +// testmove.c +// +#ifdef TEST +void +TestLiftPlaceSlidePiece(void); + +void +TestExposesCheck(void); + +void +TestIsAttacked(void); + +void +TestMakeUnmakeMove(void); +#endif + +// +// generate.c +// +#define SORT_THESE_FIRST (0x40000000) +#define FIRST_KILLER (0x20000000) +#define SECOND_KILLER (0x10000000) +#define THIRD_KILLER (0x08000000) +#define FOURTH_KILLER (0x04000000) +#define GOOD_MOVE (0x02000000) +#define STRIP_OFF_FLAGS (0x00FFFFFF) + +extern const int g_iQKDeltas[9]; +extern const int g_iNDeltas[9]; +extern const int g_iBDeltas[5]; +extern const int g_iRDeltas[5]; + +void +GenerateMoves(SEARCHER_THREAD_CONTEXT *ctx, + MOVE mvOrderFirst, + ULONG uType); + +FLAG +WouldGiveCheck(IN SEARCHER_THREAD_CONTEXT *ctx, + IN MOVE mv); + +// +// testgenerate.c +// +#ifdef TEST + +void +PlyTest(SEARCHER_THREAD_CONTEXT *ctx, + ULONG uDepth, + FLAG fRootPositionInCheck); + +void +TestMoveGenerator(void); + +void +TestLegalMoveGenerator(void); + +#endif + +// +// sig.c +// +extern UINT64 g_u64SigSeeds[128][7][2]; +extern UINT64 g_u64PawnSigSeeds[128][2]; +extern UINT64 g_u64CastleSigSeeds[16]; +extern UINT64 g_u64EpSigSeeds[9]; + +void +InitializeSigSystem(void); + +UINT64 +ComputePawnSig(POSITION *pos); + +UINT64 +ComputeSig(POSITION *pos); + +// +// mersenne.c +// +void +seedMT(unsigned int seed); + +unsigned int +reloadMT(void); + +unsigned int +randomMT(void); + +// +// data.c +// +typedef struct _VECTOR_DELTA +{ + UCHAR iVector[2]; + signed char iDelta; + signed char iNegDelta; +} +VECTOR_DELTA; + +extern ULONG g_uDistance[256]; +extern ULONG *g_pDistance; +extern VECTOR_DELTA g_VectorDelta[256]; +extern VECTOR_DELTA *g_pVectorDelta; +extern CHAR g_SwapTable[14][32][32]; +extern SCORE _PSQT[14][128]; +extern ULONG g_uSearchSortLimits[]; +extern MOVE NULLMOVE; +extern MOVE HASHMOVE; +extern MOVE RECOGNMOVE; +extern MOVE DRAWMOVE; +extern MOVE MATEMOVE; +extern FLAG g_fIsWhiteSquare[128]; +extern BITBOARD BBFILE[8]; +extern BITBOARD BBRANK[9]; +extern BITBOARD BBWHITESQ; +extern BITBOARD BBBLACKSQ; +extern BITBOARD BBLEFTSIDE; +extern BITBOARD BBRIGHTSIDE; +extern BITBOARD BBSQUARE[64]; +extern BITBOARD BBROOK_PAWNS; +extern BITBOARD BBPRECEEDING_RANKS[8][2]; +extern BITBOARD BBADJACENT_FILES[8]; +extern BITBOARD BBADJACENT_RANKS[9]; + +void +InitializeWhiteSquaresTable(void); + +void +InitializeVectorDeltaTable(void); + +void +InitializeSwapTable(void); + +void +InitializeDistanceTable(void); + +void +InitializeSearchDepthArray(void); + +ULONG +GetSearchSortLimit(ULONG); + +#ifdef DEBUG +#define SEARCH_SORT_LIMIT(x) (GetSearchSortLimit((x))) +#else +#define SEARCH_SORT_LIMIT(x) (g_uSearchSortLimits[(x)]) +#endif + +#ifdef DEBUG +ULONG CheckVectorWithIndex(int i, ULONG uColor); +#define CHECK_VECTOR_WITH_INDEX(i, color) \ + CheckVectorWithIndex(i, color) + +int DirectionBetweenSquaresWithIndex(int i); +#define CHECK_DELTA_WITH_INDEX(i) \ + DirectionBetweenSquaresWithIndex(i) + +int DirectionBetweenSquaresFromTo(COOR, COOR); +#define DIRECTION_BETWEEN_SQUARES(from, to) \ + DirectionBetweenSquaresFromTo(from, to) + +int NegativeDirectionBetweenSquaresWithIndex(int i); +#define NEG_DELTA_WITH_INDEX(i) \ + NegativeDirectionBetweenSquaresWithIndex(i) + +FLAG IsSquareWhite(COOR c); +#define IS_SQUARE_WHITE(c) \ + IsSquareWhite(c) + +#else +#define CHECK_VECTOR_WITH_INDEX(i, color) \ + (g_pVectorDelta[(i)].iVector[(color)]) + +#define CHECK_DELTA_WITH_INDEX(i) \ + (g_pVectorDelta[(i)].iDelta) + +#define DIRECTION_BETWEEN_SQUARES(cFrom, cTo) \ + CHECK_DELTA_WITH_INDEX((int)(cFrom) - (int)(cTo)) + +#define NEG_DELTA_WITH_INDEX(i) \ + (g_pVectorDelta[(i)].iNegDelta) + +#define IS_SQUARE_WHITE(c) \ + (g_fIsWhiteSquare[(c)]) +#endif + +// +// san.c +// +MOVE +ParseMoveSan(CHAR *szInput, + POSITION *pos); + +CHAR * +MoveToSan(MOVE mv, POSITION *pos); + +// +// testsan.c +// +void +TestSan(void); + +// +// list.c +// +void +InitializeListHead(IN DLIST_ENTRY *pListHead); + +FLAG +IsListEmpty(IN DLIST_ENTRY *pListHead); + +FLAG +RemoveEntryList(IN DLIST_ENTRY *pEntry); + +DLIST_ENTRY * +RemoveHeadList(IN DLIST_ENTRY *pListHead); + +DLIST_ENTRY * +RemoveTailList(IN DLIST_ENTRY *pListHead); + +void +InsertTailList(IN DLIST_ENTRY *pListHead, + IN DLIST_ENTRY *pEntry); + +void +InsertHeadList(IN DLIST_ENTRY *pListHead, + IN DLIST_ENTRY *pEntry); + +// +// command.c +// +void +ParseUserInput(FLAG fSearching); + +FLAG +InitializeCommandSystem(void); + +void +CleanupCommandSystem(void); + +// +// input.c +// +void +InitInputSystemInBatchMode(void); + +ULONG +InitInputSystemWithDedicatedThread(void); + +void +PushNewInput(CHAR *buf); + +CHAR * +PeekNextInput(void); + +CHAR * +ReadNextInput(void); + +CHAR * +BlockingReadInput(void); + +ULONG +NumberOfPendingInputEvents(void); + +volatile extern FLAG g_fExitProgram; + +// +// ics.c +// +MOVE +ParseMoveIcs(CHAR *szInput, POSITION *pos); + +CHAR * +MoveToIcs(MOVE mv); + +// +// testics.c +// +void +TestIcs(void); + +// +// gamelist.c +// +typedef enum _ERESULT +{ + RESULT_BLACK_WON = -1, + RESULT_DRAW = 0, + RESULT_WHITE_WON = 1, + RESULT_IN_PROGRESS, + RESULT_ABANDONED, + RESULT_UNKNOWN +} ERESULT; + +typedef struct _GAME_RESULT +{ + ERESULT eResult; + CHAR szDescription[256]; +} GAME_RESULT; + +typedef struct _GAME_PLAYER +{ + CHAR *szName; + CHAR *szDescription; + FLAG fIsComputer; + ULONG uRating; +} +GAME_PLAYER; + +typedef struct _GAME_HEADER +{ + CHAR *szGameDescription; + CHAR *szLocation; + GAME_PLAYER sPlayer[2]; + FLAG fGameIsRated; + UINT64 u64OpeningSig; + ERESULT eResult; + CHAR *szResultComment; + CHAR *szInitialFen; +} +GAME_HEADER; + +typedef struct _GAME_MOVE +{ + DLIST_ENTRY links; + ULONG uNumber; + MOVE mv; + CHAR *szComment; + CHAR *szDecoration; + CHAR *szMoveInSan; + CHAR *szMoveInIcs; + CHAR *szUndoPositionFen; + SCORE iMoveScore; + UINT64 u64PositionSigAfterMove; + UINT64 u64PositionSigBeforeMove; + volatile FLAG fInUse; +} +GAME_MOVE; + +typedef struct _GAME_DATA +{ + GAME_HEADER sHeader; + DLIST_ENTRY sMoveList; +} +GAME_DATA; + +extern GAME_DATA g_GameData; + +POSITION * +GetRootPosition(void); + +FLAG +SetRootPosition(CHAR *szFen); + +void +ResetGameList(void); + +ULONG +GetMoveNumber(ULONG uColor); + +void +SetGameResultAndDescription(ERESULT r, CHAR *sz); + +INT +GetGameResult(void); + +void +SetMyName(void); + +void +SetOpponentsName(CHAR *sz); + +void +SetMyRating(ULONG u); + +void +SetOpponentsRating(ULONG u); + +void +DumpGameList(void); + +void +TellGamelistThatIPlayColor(ULONG u); + +void +DumpPgn(void); + +FLAG +LoadPgn(CHAR *szPgn); + +ULONG +CountOccurrancesOfSigInOfficialGameList(UINT64 u64Sig); + +FLAG +DoesSigAppearInOfficialGameList(UINT64 u64Sig); + +FLAG +OfficiallyTakebackMove(void); + +FLAG +OfficiallyMakeMove(MOVE mv, SCORE iMoveScore, FLAG fFast); + +GAME_MOVE * +GetNthOfficialMoveRecord(ULONG n); + +void +MakeStatusLine(void); + +FLAG +IsLegalDrawByRepetition(void); + +// +// script.c +// +COMMAND(ScriptCommand); + +COMMAND(SolutionCommand); + +COMMAND(AvoidCommand); + +COMMAND(IdCommand); + +void +PostMoveTestSuiteReport(SEARCHER_THREAD_CONTEXT *); + +FLAG +CheckTestSuiteMove(MOVE mv, SCORE iScore, ULONG uDepth); + +FLAG +WeAreRunningASuite(void); + + +// +// vars.c +// +COMMAND(SetCommand); + +// +// root.c +// +extern ULONG g_uSoftExtendLimit; +extern ULONG g_uHardExtendLimit; +extern volatile MOVE_TIMER g_MoveTimer; +extern ULONG g_uExtensionReduction[MAX_PLY_PER_SEARCH]; + +#ifdef PERF_COUNTERS +#define KEEP_TRACK_OF_FIRST_MOVE_FHs(x) \ + ctx->sCounters.tree.u64BetaCutoffs++; \ + if (x) \ + { \ + ctx->sCounters.tree.u64BetaCutoffsOnFirstMove++;\ + } +#else +#define KEEP_TRACK_OF_FIRST_MOVE_FHs(x) +#endif + +#define GAME_NOT_OVER 0 +#define GAME_WHITE_WON 1 +#define GAME_BLACK_WON 2 +#define GAME_DRAW_STALEMATE 3 +#define GAME_DRAW_REPETITION 4 +#define GAME_ONE_LEGAL_MOVE 5 +#define GAME_DRAW_FIFTY_MOVES_WO_PROGRESS 6 + +GAME_RESULT +Think(POSITION *pos); + +GAME_RESULT +Ponder(POSITION *pos); + +GAME_RESULT +Iterate(SEARCHER_THREAD_CONTEXT *ctx); + +void +SetMoveTimerForSearch(FLAG fSwitchOver, ULONG uColor); + +void +SetMoveTimerToThinkForever(void); + +void +ClearRootNodecountHash(void); + +// +// draw.c +// +FLAG +IsDraw(SEARCHER_THREAD_CONTEXT *ctx); + + +// +// search.c +// +#define QPLIES_OF_NON_CAPTURE_CHECKS (1) +#define FUTILITY_BASE_MARGIN (50) // + ctx->uPositional (min 100) +#define DO_IID +#define IID_R_FACTOR (TWO_PLY + HALF_PLY) + +#ifndef MP +#define WE_SHOULD_STOP_SEARCHING (g_MoveTimer.bvFlags & TIMER_STOPPING) +#else +#define WE_SHOULD_STOP_SEARCHING ((g_MoveTimer.bvFlags & TIMER_STOPPING) || \ + (ThreadUnderTerminatingSplit(ctx))) +#endif + +SCORE FASTCALL +QSearch(SEARCHER_THREAD_CONTEXT *ctx, + SCORE iAlpha, + SCORE iBeta); + +SCORE FASTCALL +Search(SEARCHER_THREAD_CONTEXT *ctx, + SCORE iAlpha, + SCORE iBeta, + ULONG uDepth); + + +// +// searchsup.c +// +SCORE +ComputeMoveScore(IN SEARCHER_THREAD_CONTEXT *ctx, + IN MOVE mv, + IN ULONG uMoveNum); + +FLAG +ThreadUnderTerminatingSplit(SEARCHER_THREAD_CONTEXT *); + +FLAG +WeShouldDoHistoryPruning(IN SCORE iRoughEval, + IN SCORE iAlpha, + IN SCORE iBeta, + IN SEARCHER_THREAD_CONTEXT *ctx, + IN ULONG uRemainingDepth, + IN ULONG uLegalMoves, + IN MOVE mv, + IN ULONG uMoveNum, + IN INT iExtend); + +FLAG +WeShouldTryNullmovePruning(IN SEARCHER_THREAD_CONTEXT *ctx, + IN SCORE iAlpha, + IN SCORE iBeta, + IN SCORE iRoughEval, + IN ULONG uNullDepth); + +FLAG +TryNullmovePruning(IN OUT SEARCHER_THREAD_CONTEXT *ctx, + IN OUT FLAG *pfThreat, + IN SCORE iAlpha, + IN SCORE iBeta, + IN ULONG uNullDepth, + IN OUT INT *piOrigExtend, + OUT SCORE *piNullScore); + +void +UpdatePV(SEARCHER_THREAD_CONTEXT *ctx, MOVE mv); + +FLAG +CheckInputAndTimers(IN SEARCHER_THREAD_CONTEXT *ctx); + +void +ComputeReactionToCheckExtension(IN OUT SEARCHER_THREAD_CONTEXT *ctx, + IN ULONG uGenFlags, + IN ULONG uMoveNum, + IN SCORE iRoughEval, + IN SCORE iAlpha, + IN OUT INT *piExtend, + IN OUT INT *piOrigExtend); + +void +ComputeMoveExtension(IN OUT SEARCHER_THREAD_CONTEXT *ctx, + IN SCORE iAlpha, + IN SCORE iBeta, + IN ULONG uMoveNum, + IN SCORE iRoughEval, + IN ULONG uDepth, + IN OUT INT *piExtend); + +SCORE +RescoreMovesViaSearch(IN SEARCHER_THREAD_CONTEXT *ctx, + IN ULONG uDepth, + IN SCORE iAlpha, + IN SCORE iBeta); + +FLAG +MateDistancePruningCutoff(IN ULONG uPly, + IN FLAG fInCheck, + IN OUT SCORE *piBestScore, + IN OUT SCORE *piAlpha, + IN OUT SCORE *piBeta); + +FLAG +CommonSearchInit(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piAlpha, + IN OUT SCORE *piBeta, + IN OUT SCORE *piScore); + +ULONG +SelectNullmoveRFactor(IN SEARCHER_THREAD_CONTEXT *ctx, + IN INT uDepth); + +#define VERIFY_BEFORE (1) +#define VERIFY_AFTER (2) + +FLAG +SanityCheckMoves(IN SEARCHER_THREAD_CONTEXT *ctx, + IN ULONG uCurrent, + IN ULONG uType); + + +// +// testsearch.c +// +FLAG +TestSearch(void); + +// +// see.c +// +#define SEE_HEAPS + +SCORE +SEE(POSITION *pos, + MOVE mv); + +typedef struct _SEE_THREESOME +{ + PIECE pPiece; + COOR cLoc; + ULONG uVal; +} SEE_THREESOME; + +typedef struct _SEE_LIST +{ + ULONG uCount; + SEE_THREESOME data[16]; +} +SEE_LIST; + +SCORE +ControlsSquareMinusPiece(ULONG uSide, + POSITION *pos, + COOR c, + COOR cIgnore); + +// +// testsee.c +// +SCORE +DebugSEE(POSITION *pos, + MOVE mv); + +void +TestGetAttacks(void); + +// +// hash.c +// +#define NUM_HASH_ENTRIES_PER_LINE 4 +#define HASH_FLAG_EXACT 0x1 +#define HASH_FLAG_LOWER 0x2 +#define HASH_FLAG_UPPER 0x4 +#define HASH_FLAG_VALID_BOUNDS 0x7 +#define HASH_FLAG_THREAT 0x8 +#define HASH_FLAG_DIRTY 0xF0 + +typedef struct _HASH_ENTRY +{ + MOVE mv; // 0 1 2 3 + UCHAR uDepth; // 4 + UCHAR bvFlags; // 5 ==> d d d d | thr up low exact + signed short iValue; // 6 7 + UINT64 u64Sig; // 8 9 A B C D E F == 16 bytes +} HASH_ENTRY; + +FLAG +InitializeHashSystem(void); + +void +CleanupHashSystem(void); + +void +ClearHashTable(void); + +void +DirtyHashTable(void); + +void +StoreLowerBound(MOVE mvBestMove, + POSITION *pos, + SCORE iValue, + ULONG uDepth, + FLAG fThreat); +void +StoreExactScore(MOVE mvBestMove, + POSITION *pos, + SCORE iValue, + ULONG uDepth, + FLAG fThreat, + ULONG uPly); + +void +StoreUpperBound(//MOVE mvBestMove, + POSITION *pos, + SCORE iValue, + ULONG uDepth, + FLAG fThreat); + +HASH_ENTRY * +HashLookup(SEARCHER_THREAD_CONTEXT *ctx, + ULONG uDepth, + ULONG uNextDepth, + SCORE iAlpha, + SCORE iBeta, + FLAG *pfThreat, + FLAG *pfAvoidNull, + MOVE *pHashMove, + SCORE *piScore); + +COOR +CheckHashForDangerSquare(POSITION *pos); + +MOVE +GetPonderMove(POSITION *pos); + +extern ULONG g_uHashTableSizeEntries; +extern ULONG g_uHashTableSizeBytes; +extern HASH_ENTRY *g_pHashTable; + +// +// testhash.c +// +void +AnalyzeFullHashTable(void); + +// +// positionhash.c +// +// IDEA: store "mate threat" flag in here? +// IDEA: store "king safety" numbers in here? +// +typedef struct _POSITION_HASH_ENTRY { + UINT64 u64Sig; + UCHAR cEnprise[2]; + UCHAR uEnpriseCount[2]; + UCHAR cTrapped[2]; +} POSITION_HASH_ENTRY; + +void +InitializePositionHashSystem(void); + +void +CleanupPositionHashSystem(void); + +void +StoreEnprisePiece(POSITION *pos, COOR cSquare); + +void +StoreTrappedPiece(POSITION *pos, COOR cSquare); + +COOR +GetEnprisePiece(POSITION *pos, ULONG uSide); + +ULONG +GetEnpriseCount(POSITION *pos, ULONG uSide); + +COOR +GetTrappedPiece(POSITION *pos, ULONG uSide); + +FLAG +SideCanStandPat(POSITION *pos, ULONG uSide); + +ULONG +ValueOfMaterialInTroubleDespiteMove(POSITION *pos, ULONG uSide); + +ULONG +ValueOfMaterialInTroubleAfterNull(POSITION *pos, ULONG uSide); + +// +// pawnhash.c +// +#define COOR_TO_BIT(x) ((x) & 0x7) + ((0x7 - ((x) >> 4)) << 3) + +void +ClearPawnHashStats(void); + +void +ReportPawnHashStats(void); + +PAWN_HASH_ENTRY * +PawnHashLookup(SEARCHER_THREAD_CONTEXT *ctx); + +// +// eval.c +// +#define LAZY_EVAL +#define LAZE_EVAL_BASE_SCORE 10 +extern const int g_iAhead[2]; +extern const int g_iBehind[2]; + +ULONG +DNABufferSizeBytes(); + +char * +ExportEvalDNA(); + +FLAG +WriteEvalDNA(char *szFilename); + +FLAG +ImportEvalDNA(char *p); + +FLAG +ReadEvalDNA(char *szFilename); + + + +SCORE +Eval(SEARCHER_THREAD_CONTEXT *, SCORE, SCORE); + +FLAG +EvalPasserRaces(POSITION *, + PAWN_HASH_ENTRY *); + +ULONG +CountKingSafetyDefects(POSITION *pos, + ULONG uSide); + +// +// testeval.c +// +#ifdef EVAL_DUMP +void +TestEval(void); + +void +EvalTraceReport(void); + +void +EvalTrace(ULONG uColor, PIECE p, COOR c, SCORE iVal, CHAR *szMessage); + +SCORE +EvalSigmaForPiece(POSITION *pos, COOR c); + +void +EvalTraceClear(void); + +void +TestEvalWithSymmetry(void); + +#define EVAL_TERM(COL, PIE, COO, VAL, ADJ, MESS) \ + (VAL) += (ADJ); \ + EvalTrace((COL), (PIE), (COO), (ADJ), (MESS)); +#else +#define EVAL_TERM(COL, PIE, COO, VAL, ADJ, MESS) \ + (VAL) += (ADJ); +#endif + +// +// bitboard.c +// +void +InitializeBitboards(void); + +ULONG CDECL +SlowCountBits(BITBOARD bb); + +ULONG CDECL +DeBruijnFirstBit(BITBOARD bb); + +ULONG CDECL +SlowFirstBit(BITBOARD bb); + +ULONG CDECL +SlowLastBit(BITBOARD bb); + +#ifdef CROUTINES +#define CountBits SlowCountBits +#define FirstBit SlowFirstBit +#define LastBit SlowLastBit +#endif + +COOR +CoorFromBitBoardRank8ToRank1(BITBOARD *pbb); + +COOR +CoorFromBitBoardRank1ToRank8(BITBOARD *pbb); + +// +// x86.asm +// +ULONG CDECL +CountBits(BITBOARD bb); + +ULONG CDECL +FirstBit(BITBOARD bb); + +ULONG CDECL +LastBit(BITBOARD bb); + +ULONG CDECL +LockCompareExchange(volatile void *pDest, + ULONG uExch, + ULONG uComp); + +ULONG CDECL +LockIncrement(volatile void *pDest); + +ULONG CDECL +LockDecrement(volatile void *pDest); + +FLAG CDECL +CanUseParallelOpcodes(); + +ULONG CDECL +ParallelCompareUlong(ULONG uComparand, void *pComparators); + +ULONG CDECL +ParallelCompareVector(void *pComparand, void *pComparators); + +void CDECL +GetAttacks(SEE_LIST *pList, + POSITION *pos, + COOR cSquare, + ULONG uSide); + +void CDECL +SlowGetAttacks(SEE_LIST *pList, + POSITION *pos, + COOR cSquare, + ULONG uSide); +#ifdef CROUTINES +#define GetAttacks SlowGetAttacks +#endif + +#ifdef _X86_ +// +// Note: this is most of the stuff that x86.asm assumes about the +// internal data structures. If any of this fails then either the +// assembly language code needs to be updated or you need to use the C +// version of the routine in see.c instead. +// +#define ASSERT_ASM_ASSUMPTIONS \ + ASSERT(VALUE_PAWN == 100); \ + ASSERT(OFFSET_OF(uCount, SEE_LIST) == 0); \ + ASSERT(OFFSET_OF(data, SEE_LIST) == 4); \ + ASSERT(sizeof(SEE_THREESOME) == 12); \ + ASSERT(OFFSET_OF(pPiece, SEE_THREESOME) == 0); \ + ASSERT(OFFSET_OF(cLoc, SEE_THREESOME) == 4); \ + ASSERT(OFFSET_OF(uVal, SEE_THREESOME) == 8); \ + ASSERT(OFFSET_OF(cNonPawns, POSITION) == 0x478); \ + ASSERT(OFFSET_OF(uNonPawnCount, POSITION) == 0x500); \ + ASSERT(OFFSET_OF(rgSquare, POSITION) == 0); \ + ASSERT(sizeof(SQUARE) == 8); \ + ASSERT(sizeof(VECTOR_DELTA) == 4); \ + ASSERT(sizeof(g_VectorDelta) == 256 * 4); \ + ASSERT(OFFSET_OF(iVector, VECTOR_DELTA) == 0); \ + ASSERT(OFFSET_OF(iDelta, VECTOR_DELTA) == 2); \ + ASSERT(OFFSET_OF(iNegDelta, VECTOR_DELTA) == 3); \ + ASSERT(sizeof(g_PieceData) == 8 * 4 * 4); \ + ASSERT(OFFSET_OF(uValue, PIECE_DATA) == 0); +#else +#define ASSERT_ASM_ASSUMPTIONS +#endif + +// +// testbitboard.c +// +void +TestBitboards(void); + +// +// dynamic.c +// +extern ULONG g_HistoryCounters[14][128]; + +ULONG +GetMoveFailHighPercentage(MOVE mv); + +void +UpdateDynamicMoveOrdering(SEARCHER_THREAD_CONTEXT *ctx, + ULONG uRemainingDepth, + MOVE mvBest, + SCORE iScore, + ULONG uCurrent); + +void +NewKillerMove(SEARCHER_THREAD_CONTEXT *ctx, MOVE mv, SCORE iScore); + +FLAG +InitializeDynamicMoveOrdering(void); + +void +CleanupDynamicMoveOrdering(void); + +void +ClearDynamicMoveOrdering(void); + +void +MaintainDynamicMoveOrdering(void); + +void +IncrementMoveHistoryCounter(MOVE mv, ULONG u); + +void +DecrementMoveHistoryCounter(MOVE mv, ULONG u); + +// +// split.c +// +extern volatile ULONG g_uNumHelpersAvailable; +extern ULONG g_uNumHelperThreads; + +FLAG +InitializeParallelSearch(void); + +FLAG +CleanupParallelSearch(void); + +void +ClearHelperThreadIdleness(void); + +void +DumpHelperIdlenessReport(void); + +SCORE +StartParallelSearch(IN SEARCHER_THREAD_CONTEXT *ctx, + IN OUT SCORE *piAlpha, + IN SCORE iBeta, + IN OUT SCORE *piBestScore, + IN OUT MOVE *pmvBest, + IN ULONG uMoveNum, + IN INT iPositionExtend, + IN ULONG uDepth); + +void +InitializeSearcherContext(POSITION *pos, SEARCHER_THREAD_CONTEXT *ctx); + +void +ReInitializeSearcherContext(POSITION *pos, SEARCHER_THREAD_CONTEXT *ctx); + +void +InitializeLightweightSearcherContext(POSITION *pos, + LIGHTWEIGHT_SEARCHER_CONTEXT *ctx); + +// +// book.c +// + +#define BOOK_PROBE_MISS_LIMIT (7) +#define FLAG_DISABLED (1) +#define FLAG_ALWAYSPLAY (2) +#define FLAG_DELETED (4) + +#define BOOKMOVE_SELECT_MOVE (1) +#define BOOKMOVE_DUMP (2) + +#pragma pack(4) +typedef struct _BOOK_ENTRY +{ + UINT64 u64Sig; // 8 bytes + UINT64 u64NextSig; // 8 bytes + MOVE mvNext; // 4 bytes + ULONG uWins; // 4 bytes + ULONG uDraws; // 4 bytes + ULONG uLosses; // 4 bytes + BITV bvFlags; // 4 bytes +} +BOOK_ENTRY; +#pragma pack() + +typedef struct _OPENING_NAME_MAPPING +{ + UINT64 u64Sig; + CHAR *szString; +} +OPENING_NAME_MAPPING; + +#define BOOK_EDITING_RECORD "bkedit.edt" +#define OPENING_LEARNING_FILENAME "bklearn.bin" + +typedef struct _OPENING_LEARNING_ENTRY +{ + UINT64 u64Sig; + ULONG uWhiteWins; + ULONG uDraws; + ULONG uBlackWins; +} +OPENING_LEARNING_ENTRY; + +FLAG +InitializeOpeningBook(void); + +void +ResetOpeningBook(void); + +void +CleanupOpeningBook(void); + +MOVE +BookMove(POSITION *pos, + BITV bvFlags); + +COMMAND(BookCommand); + +extern ULONG g_uBookProbeFailures; +extern FLAG g_fTournamentMode; +extern CHAR *g_szBookName; + +// +// bench.c +// +COMMAND(BenchCommand); + +// +// testdraw.c +// +void +TestDraw(void); + +// +// probe.c +// +FLAG +ProbeEGTB(SEARCHER_THREAD_CONTEXT *ctx, SCORE *score); + +void +InitializeEGTB(void); + +void +CleanupEGTB(void); + +// +// dumptree.c +// +#ifdef DUMP_TREE + +void +InitializeTreeDump(void); + +void +CleanupTreeDump(void); + +void +DTEnterNode(SEARCHER_THREAD_CONTEXT *ctx, + ULONG uDepth, + FLAG fIsQNode, + SCORE iAlpha, + SCORE iBeta); + +void +DTLeaveNode(SEARCHER_THREAD_CONTEXT *ctx, + FLAG fQNode, + SCORE iBestScore, + MOVE mvBestMove); + +void CDECL +DTTrace(ULONG uPly, CHAR *szMessage, ...); + +#define DTTRACE(...) DTTrace(ctx->uPly, __VA_ARGS__) + +#else + +#define DTTRACE(...) + +#define DTEnterNode(...) + +#define DTLeaveNode(...) + +#define InitializeTreeDump(...) + +#define CleanupTreeDump(...) + +#endif + +// +// testsup.c +// +FLAG +IsBoardLegal(POSITION *pos); + +void +GenerateRandomLegalPosition(POSITION *pos); + +void +GenerateRandomLegalSymetricPosition(POSITION *pos); + +// +// recogn.c +// +#define UNRECOGNIZED (0) +#define RECOGN_EXACT (1) +#define RECOGN_UPPER (2) +#define RECOGN_LOWER (3) +#define RECOGN_EGTB (4) + +void +InitializeInteriorNodeRecognizers(void); + +ULONG +RecognLookup(SEARCHER_THREAD_CONTEXT *ctx, + SCORE *piScore, + FLAG fProbeEGTB); + +#ifdef EVAL_HASH +void +ClearEvalHashStats(void); + +void +ReportEvalHashStats(void); + +SCORE +ProbeEvalHash(SEARCHER_THREAD_CONTEXT *ctx); + +SCORE +GetRoughEvalScore(IN SEARCHER_THREAD_CONTEXT *ctx, + IN SCORE iAlpha, + IN SCORE iBeta, + IN FLAG fUseHash); + +void +StoreEvalHash(SEARCHER_THREAD_CONTEXT *ctx, SCORE iScore); +#endif // EVAL_HASH + +#endif // CHESS diff --git a/src/save/main.c b/src/save/main.c new file mode 100755 index 0000000..a1a0f1f --- /dev/null +++ b/src/save/main.c @@ -0,0 +1,745 @@ +/** + +Copyright (c) Scott Gasch + +Module Name: + + main.c + +Abstract: + + Program entry point and setup code. + +Author: + + Scott Gasch (scott.gasch@gmail.com) 8 Apr 2004 + +Revision History: + + $Id: main.c 351 2008-06-28 04:21:53Z scott $ + +**/ + +#include "chess.h" + +FILE *g_pfLogfile = NULL; +GAME_OPTIONS g_Options; +CHAR g_szInitialCommand[SMALL_STRING_LEN_CHAR]; +ULONG g_uInputThreadHandle = (ULONG)-1; + +void +Banner(void) +/** + +Routine description: + + Describe the compile/build options and program version. + +Parameters: + + void + +Return value: + + void + +**/ +{ + char *p; + + Trace("Typhoon %s (built on %s %s):\n", VERSION, __DATE__, __TIME__); + Trace(" Copyright (C) 2000-2007, Scott Gasch (scott.gasch@gmail.com)\n"); + Trace(" " REVISION); + Trace(" " COMPILER_STRING); + Trace(" Make profile used: %s\n", PROFILE); +#ifdef MP + Trace(" Multiprocessor enabled; %u searcher thread%s\n", + g_Options.uNumProcessors, (g_Options.uNumProcessors > 1) ? "s" : ""); +#else + Trace(" Single processor version\n"); +#endif +#ifdef DEBUG + Trace(" DEBUG build\n"); +#endif +#ifdef TEST + Trace(" Testcode compiled in\n"); +#endif +#ifdef EVAL_DUMP + Trace(" Evaluation terms\n"); +#endif +#ifdef EVAL_TIME + Trace(" Evaluation timing\n"); +#endif +#ifdef PERF_COUNTERS + Trace(" Performance counters\n"); +#ifdef TEST_NULL + Trace(" Testing nullmove prediction algorithms\n"); +#endif +#endif +#ifdef DUMP_TREE + Trace(" Search tree dumpfile generation enabled\n"); +#endif + Trace(" Hash sizes: %u Mb (main), %u Mb / thread (pawn), %u Mb / thread (eval)\n", + (g_uHashTableSizeEntries * sizeof(HASH_ENTRY)) / MB, + PAWN_HASH_TABLE_SIZE * sizeof(PAWN_HASH_ENTRY) / MB, + EVAL_HASH_TABLE_SIZE * sizeof(EVAL_HASH_ENTRY) / MB); + Trace(" QCheckPlies: %u\n", QPLIES_OF_NON_CAPTURE_CHECKS); + Trace(" FutilityBase: %u\n", FUTILITY_BASE_MARGIN); + p = ExportEvalDNA(); + Trace(" Logging Eval DNA.\n"); + Log(p); + free(p); +#ifdef DO_IID + Trace(" IID R-factor: %u ply\n", IID_R_FACTOR / ONE_PLY); +#endif +} + + +void +EndLogging(void) +/** + +Routine description: + + Close the main logfile. + +Parameters: + + void + +Return value: + + void + +**/ +{ + if (NULL != g_pfLogfile) + { + fflush(g_pfLogfile); + fflush(stdout); + fclose(g_pfLogfile); + g_pfLogfile = NULL; + } +} + + +FLAG +BeginLogging(void) +/** + +Routine description: + + Open the main logfile. + +Parameters: + + void + +Return value: + + FLAG + +**/ +{ + if (NULL != g_pfLogfile) + { + EndLogging(); + } + ASSERT(g_pfLogfile == NULL); + ASSERT(strlen(g_Options.szLogfile)); + + // + // If the logfile we want to write already exists, back it up. + // + if (TRUE == SystemDoesFileExist(g_Options.szLogfile)) + { + VERIFY(BackupFile(g_Options.szLogfile)); + } + ASSERT(FALSE == SystemDoesFileExist(g_Options.szLogfile)); + + g_pfLogfile = fopen(g_Options.szLogfile, "wb+"); + if (NULL == g_pfLogfile) + { + UtilPanic(INITIALIZATION_FAILURE, + (void *)"Can't open logfile", + (void *)&(g_Options.szLogfile), + (void *)"write", + NULL, + __FILE__, __LINE__); + } + return(TRUE); +} + + +FLAG +PreGameReset(FLAG fResetBoard) +/** + +Routine description: + + Reset internal state between games. + +Parameters: + + void + +Return value: + + FLAG + +**/ +{ + if (TRUE == fResetBoard) + { + SetRootToInitialPosition(); + } + SetMyName(); + ClearDynamicMoveOrdering(); + ClearHashTable(); + ResetOpeningBook(); + return(TRUE); +} + + +static ULONG +_ParseHashOption(char *p, + ULONG uEntrySize) +{ + ULONG u, v; + CHAR c; + + if ((!STRCMPI(p, "none")) || (!STRCMPI(p, "no"))) return(0); + if (2 == sscanf(p, "%u%1c", &u, &c)) + { + v = u; + switch(c) + { + case 'K': + case 'k': + u *= 1024; + break; + case 'M': + case 'm': + u *= 1024 * 1024; + break; + case 'G': + case 'g': + u *= 1024 * 1024 * 1024; + break; + default: + Trace("Error (unrecognized size): \"%s\"\n", p); + break; + } + if (u < v) + { + Trace("Error (size too large): \"%s\"\n", p); + u = v; + } + u /= uEntrySize; + } + else + { + u = (ULONG)atoi(p); + } + + while(!IS_A_POWER_OF_2(u)) + { + u &= (u - 1); + } + return(u); +} + + + +void +InitializeOptions(int argc, char *argv[]) +/** + +Routine description: + + Set global options to their initial state. This code also is + responsible for parsing the commandline in order to override the + default state of global options. + +Parameters: + + int argc, + char *argv[] + +Return value: + + void + +**/ +{ + int i; + + // + // Defaults + // + memset(&g_Options, 0, sizeof(g_Options)); + g_szInitialCommand[0] = '\0'; + g_Options.uMyClock = g_Options.uOpponentsClock = 600; + g_Options.fGameIsRated = FALSE; + g_Options.fOpponentIsComputer = FALSE; + g_Options.uSecPerMove = 0; + g_Options.uMaxDepth = MAX_PLY_PER_SEARCH - 1; + g_Options.fShouldPonder = TRUE; + g_Options.fPondering = g_Options.fThinking = FALSE; + g_Options.mvPonder.uMove = 0; + g_Options.fShouldPost = TRUE; + g_Options.fForceDrawWorthZero = FALSE; + g_Options.uMyIncrement = 0; + g_Options.uMovesPerTimePeriod = 0; + g_Options.szAnalyzeProgressReport[0] = '\0'; + g_Options.fShouldAnnounceOpening = TRUE; + g_Options.eClock = CLOCK_NONE; + g_Options.eGameType = GAME_UNKNOWN; + g_Options.ePlayMode = FORCE_MODE; + g_Options.fNoInputThread = FALSE; + g_Options.fVerbosePosting = TRUE; + strcpy(g_Options.szEGTBPath, "/egtb/three;/etc/four;/egtb/five"); + strcpy(g_Options.szLogfile, "typhoon.log"); + strcpy(g_Options.szBookName, "book.bin"); + g_Options.uNumHashTableEntries = 0x10000; + g_Options.uNumProcessors = 1; + g_Options.fStatusLine = TRUE; + g_Options.fShouldResign = FALSE; + g_Options.u64MaxNodeCount = 0ULL; + + i = 1; + while(i < argc) + { +#ifdef MP + if ((!STRCMPI(argv[i], "--cpus")) && (argc > i)) + { + g_Options.uNumProcessors = (ULONG)atoi(argv[i+1]); + if ((g_Options.uNumProcessors == 0) || + (g_Options.uNumProcessors > 64)) + { + g_Options.uNumProcessors = 2; + } + i++; + } + else +#endif + if ((!STRCMPI(argv[i], "--command")) && (argc > i)) + { + strncpy(g_szInitialCommand, + argv[i+1], + SMALL_STRING_LEN_CHAR - 2); + strcat(g_szInitialCommand, "\r\n"); + i++; + } + else if ((!STRCMPI(argv[i], "--hash")) && (argc > i)) + { + g_Options.uNumHashTableEntries = + _ParseHashOption(argv[i+1], + sizeof(HASH_ENTRY)); + i++; + } + else if ((!STRCMPI(argv[i], "--egtbpath")) && (argc > i)) + { + g_Options.szEGTBPath[0] = '\0'; + strncpy(g_Options.szEGTBPath, argv[i+1], SMALL_STRING_LEN_CHAR); + i++; + } + else if (!STRCMPI(argv[i], "--batch")) + { + g_Options.fNoInputThread = TRUE; + } + else if ((!STRCMPI(argv[i], "--dnafile")) && (argc > i)) + { + if (!ReadEvalDNA(argv[i+1])) + { + UtilPanic(INITIALIZATION_FAILURE, + (void *)"Bad DNA file", + (void *)&(argv[i+1]), + (void *)"read", + NULL, + __FILE__, __LINE__); + } + i++; + } + else if ((!STRCMPI(argv[i], "--logfile")) && (argc > i)) + { + strncpy(g_Options.szLogfile, argv[i+1], + SMALL_STRING_LEN_CHAR); + i++; + } + else if (!STRCMPI(argv[i], "--help")) { + Trace("Usage: %s [--batch] [--command arg] [--logfile arg] [--egtbpath arg]\n" + " [--dnafile arg] [--cpus arg] [--hash arg]\n\n" + " --batch : operate the engine without an input thread\n" + " --command : specify initial command(s) (requires arg)\n" + " --cpus : indicate the number of cpus to use (requires arg, MP build)\n" + " --hash : indicate desired hash size (requires arg)\n" + " --egtbpath : supplies the egtb path to engine (requires arg)\n" + " --logfile : indicate desired output logfile name (requires arg)\n" + " --dnafile : indicate desired eval profile input (requres arg)\n\n" + "Example: %s --hash 256m --cpus 2\n" + " %s --logfile test.out --batch --command \"test\" --dnafile test.in\n", argv[0], argv[0], argv[0]); + exit(-1); + } + else + { + Trace("Error (unknown argument): \"%s\"; try --help\n", argv[i]); + } + + // + // TODO: parse other commandline options and override above + // + + i++; + } +} + + +void +CleanupOptions(void) +/** + +Routine description: + + Cleanup options... + +Parameters: + + void + +Return value: + + void + +**/ +{ + NOTHING; +} + + +FLAG +MainProgramInitialization(int argc, char *argv[]) +/** + +Routine description: + + Perform main program initialization. This includes stuff like: + precomputing some data structures, resetting the global options, + parsing the commandline to override said options, and calling out + to other modules to tell them to initialize themselves. + +Parameters: + + int argc, + char *argv[] + +Return value: + + FLAG + +**/ +{ + srand((unsigned int)time(0)); + InitializeOptions(argc, argv); + InitializeTreeDump(); + InitializeEGTB(); + InitializeSigSystem(); + InitializeInteriorNodeRecognizers(); + InitializeSearchDepthArray(); + InitializeWhiteSquaresTable(); + InitializeVectorDeltaTable(); + InitializeSwapTable(); + InitializeDistanceTable(); + InitializeOpeningBook(); + InitializeDynamicMoveOrdering(); + InitializeHashSystem(); + InitializePositionHashSystem(); +#ifdef MP + InitializeParallelSearch(); +#endif + VERIFY(PreGameReset(TRUE)); + return(BeginLogging()); +} + + +FLAG +MainProgramCleanup(void) +/** + +Routine description: + + Perform main program cleanup. Tear down the stuff we set up in + MainProgramInitialization. + +Parameters: + + void + +Return value: + + FLAG + +**/ +{ + CleanupOpeningBook(); + CleanupEGTB(); + CleanupDynamicMoveOrdering(); +#ifdef MP + CleanupParallelSearch(); +#endif + CleanupPositionHashSystem(); + CleanupHashSystem(); + CleanupTreeDump(); + CleanupOptions(); + EndLogging(); + return(TRUE); +} + + +#ifdef TEST +FLAG +RunStartupProgramTest(void) +/** + +Routine description: + + If we built this version with -DTEST then this code is the start + of the self-test that runs at program startup time. + +Parameters: + + void + +Return value: + + FLAG + +**/ +{ + ULONG u, x, y; + +#ifdef _X86_ + Trace("Testing CPP macros...\n"); + for (u = 0; u < 1000000; u++) + { + x = rand() * rand(); + y = rand() * rand(); + x = x & ~(0x80000000); + y = y & ~(0x80000000); + + if (MAX(x, y) != MAXU(x, y)) + { + UtilPanic(TESTCASE_FAILURE, + NULL, "max maxu", NULL, NULL, __FILE__, __LINE__); + } + + if (MAXU(x, y) != MAXU(y, x)) + { + UtilPanic(TESTCASE_FAILURE, + NULL, "maxu assoc", NULL, NULL, __FILE__, __LINE__); + } + + if (MIN(x, y) != MINU(x, y)) + { + UtilPanic(TESTCASE_FAILURE, + NULL, "min minu", NULL, NULL, __FILE__, __LINE__); + } + + if (MINU(x, y) != MINU(y, x)) + { + UtilPanic(TESTCASE_FAILURE, + NULL, "minu assoc", NULL, NULL, __FILE__, __LINE__); + } + + if (abs(x - y) != ABS_DIFF(x, y)) + { + UtilPanic(TESTCASE_FAILURE, + NULL, "abs_diff", NULL, NULL, __FILE__, __LINE__); + } + + if (ABS_DIFF(x, y) != ABS_DIFF(y, x)) + { + UtilPanic(TESTCASE_FAILURE, + NULL, "abs_diff assoc", NULL, NULL, __FILE__, __LINE__); + } + } +#endif + + TestDraw(); +#ifdef EVAL_DUMP + TestEval(); +#endif + TestBitboards(); + TestSan(); + TestIcs(); + TestGetAttacks(); + TestMoveGenerator(); + TestLegalMoveGenerator(); + TestFenCode(); + TestLiftPlaceSlidePiece(); + TestExposesCheck(); + TestIsAttacked(); + TestMakeUnmakeMove(); + TestSearch(); + + return(TRUE); +} +#endif // TEST + + + +void DeclareTerminalStates(GAME_RESULT result) { + switch(result.eResult) { + case RESULT_IN_PROGRESS: + return; + case RESULT_BLACK_WON: + Trace("0-1 {%s}\n", result.szDescription); + SetGameResultAndDescription(result.eResult, result.szDescription); + DumpPgn(); + g_Options.ePlayMode = FORCE_MODE; + break; + case RESULT_WHITE_WON: + Trace("1-0 {%s}\n", result.szDescription); + SetGameResultAndDescription(result.eResult, result.szDescription); + DumpPgn(); + g_Options.ePlayMode = FORCE_MODE; + break; + case RESULT_DRAW: + Trace("1/2-1/2 {%s}\n", result.szDescription); + Trace("tellics draw\n"); + SetGameResultAndDescription(result.eResult, result.szDescription); + DumpPgn(); + g_Options.ePlayMode = FORCE_MODE; + break; + case RESULT_ABANDONED: + case RESULT_UNKNOWN: + ASSERT(FALSE); + SetGameResultAndDescription(result.eResult, result.szDescription); + DumpPgn(); + g_Options.ePlayMode = FORCE_MODE; + break; + } +} + + +// Main work loop +void MainProgramLoop() { + POSITION pos; + GAME_RESULT result; + FLAG fThink; + FLAG fPonder; + + while (!g_fExitProgram) { + + // Prepare to search or ponder + memcpy(&pos, GetRootPosition(), sizeof(POSITION)); + fThink = fPonder = FALSE; + result.eResult = RESULT_IN_PROGRESS; + result.szDescription[0] = '\0'; + + // What should we do? + switch(g_Options.ePlayMode) { + case I_PLAY_WHITE: + Trace("I play white.\n"); + fThink = (pos.uToMove == WHITE); + fPonder = (pos.uToMove == BLACK); + break; + case I_PLAY_BLACK: + Trace("I play black.\n"); + fThink = (pos.uToMove == BLACK); + fPonder = (pos.uToMove == WHITE); + break; + case FORCE_MODE: + default: + Trace("Force mode\n"); + fThink = fPonder = FALSE; + break; + } + + if (fThink) { + Trace("Think...\n"); + result = Think(&pos); + if ((NumberOfPendingInputEvents() != 0) && + (FALSE == g_fExitProgram)) + { + MakeStatusLine(); + ParseUserInput(FALSE); + } + DeclareTerminalStates(result); + } else if (fPonder) { + Trace("Ponder...\n"); + if (g_Options.fShouldPonder) { + result = Ponder(&pos); + } + if (FALSE == g_fExitProgram) + { + MakeStatusLine(); + ParseUserInput(FALSE); + } + DeclareTerminalStates(result); + } else { + Trace("Force mode, consume input and continue\n"); + MakeStatusLine(); + ParseUserInput(FALSE); + } + } +} + + +int CDECL +main(int argc, char *argv[]) +/** + +Routine description: + + Chess engine entry point and main loop. + +Parameters: + + int argc, + char *argv[] + +Return value: + + int CDECL + +**/ +{ + // Initial setup work... + Trace("Setting up, please wait...\n"); + if (FALSE == SystemDependentInitialization()) + { + Trace("main: Operating system dependent init code failed, " + "aborting.\n"); + exit(-1); + } + if (FALSE == MainProgramInitialization(argc, argv)) + { + Trace("main: Main program init code failed, aborting.\n"); + exit(-1); + } + Banner(); +#ifdef TEST + (void)RunStartupProgramTest(); +#endif + if (TRUE == g_Options.fNoInputThread) + { + InitInputSystemInBatchMode(); + } + else + { + g_uInputThreadHandle = InitInputSystemWithDedicatedThread(); + } + + // Enter the main work loop code... + MainProgramLoop(); + + // If we get here g_fExitProgram is set -- clean up. + Trace("MAIN THREAD: thread terminating.\n"); + if (FALSE == MainProgramCleanup()) + { + Trace("main: Main program cleanup code failed, aborting.\n"); + exit(-1); + } + exit(0); +} diff --git a/src/save/root.c b/src/save/root.c new file mode 100755 index 0000000..ddd6015 --- /dev/null +++ b/src/save/root.c @@ -0,0 +1,1462 @@ +/** + +Copyright (c) Scott Gasch + +Module Name: + + root.c + +Abstract: + + Routines dealing with the root of the search tree. Specifically + move ordering at the root node, searching, search support, pondering, + pondering support, and the main iterative deepening loop. + +Author: + + Scott Gasch (scott.gasch@gmail.com) 21 May 2004 + +Revision History: + + $Id: root.c 351 2008-06-28 04:21:53Z scott $ + +**/ + +#include "chess.h" + +volatile MOVE_TIMER g_MoveTimer; +ULONG g_uIterateDepth; +ULONG g_uSoftExtendLimit; +ULONG g_uHardExtendLimit; +ULONG g_uExtensionReduction[MAX_PLY_PER_SEARCH]; +FLAG g_fCanSplit[MAX_PLY_PER_SEARCH]; +extern FILE *g_pfLogfile; +SCORE g_iRootScore[2] = {0, 0}; +SCORE g_iScore; + + +static void +_SetMoveTimerForPonder(void) +/** + +Routine description: + + Set the move timer before a ponder operation. A time limit of -1 + means search forever... so the only way out of the ponder is for + new user input to interrupt it. + +Parameters: + + void + +Return value: + + static void + +**/ +{ + g_MoveTimer.dStartTime = SystemTimeStamp(); + g_MoveTimer.dSoftTimeLimit = -1; + g_MoveTimer.dHardTimeLimit = -1; + g_MoveTimer.uNodeCheckMask = 0x200 - 1; + g_MoveTimer.bvFlags = 0; +} + + +void +SetMoveTimerToThinkForever(void) +/** + +Routine description: + +Parameters: + + void + +Return value: + + void + +**/ +{ + g_MoveTimer.dStartTime = SystemTimeStamp(); + g_MoveTimer.dSoftTimeLimit = -1; + g_MoveTimer.dHardTimeLimit = -1; + g_MoveTimer.uNodeCheckMask = 0x1000000 - 1; + g_MoveTimer.bvFlags = 0; +} + + +void +SetMoveTimerForSearch(FLAG fSwitchOver, ULONG uColor) +/** + +Routine description: + + Set the move timer for a search operation. + +Parameters: + + FLAG fSwitchOver : if TRUE then this search is a converted ponder + (i.e. they made the ponder move and now we are searching). + +Return value: + + void + +**/ +{ + ULONG uMovesDone = GetMoveNumber(uColor); + double dClock = (double)g_Options.uMyClock; + double dSec = 0; + ULONG uMargin = 0; + ULONG uMovesToDo; + + // + // If switching over from a ponder search leave the start time and + // move flags alone (i.e. N seconds in the past already). + // + if (FALSE == fSwitchOver) + { + g_MoveTimer.dStartTime = SystemTimeStamp(); + g_MoveTimer.bvFlags = 0; + } + + // + // Check the clock more often in search if there is little time + // left on the clock -- we can't afford an oversearch when the + // bullet game is down to the wire. + // +#ifdef DEBUG + g_MoveTimer.uNodeCheckMask = 0x800 - 1; +#else + if (WeAreRunningASuite()) + { + g_MoveTimer.uNodeCheckMask = 0x20000 - 1; + } + else if (g_Options.u64MaxNodeCount) + { + g_MoveTimer.uNodeCheckMask = 0x1000 - 1; + } + else + { + if (dClock < 10.0) + { + g_MoveTimer.uNodeCheckMask = 0x400 - 1; + } + else + { + g_MoveTimer.uNodeCheckMask = 0x8000 - 1; + } + } +#endif + + switch (g_Options.eClock) + { + // + // Fixed time per move. Think for g_uMyIncrement seconds exactly and + // no hard time limit. + // + case CLOCK_FIXED: + dSec = (double)g_Options.uMyIncrement; + uMargin = 0; + break; + + // + // N moves per time control or entire game in time control. + // + case CLOCK_NORMAL: + if (g_Options.uMovesPerTimePeriod != 0) + { + // + // N moves per time control. + // + ASSERT(g_Options.uMovesPerTimePeriod < 256); + uMovesToDo = + (g_Options.uMovesPerTimePeriod - + ((uMovesDone - 1) % g_Options.uMovesPerTimePeriod)); +#ifdef DEBUG + Trace("SetMoveTimer: %u moves left to do this in %s sec.\n", + uMovesToDo, TimeToString(dClock)); +#endif + ASSERT(uMovesToDo <= g_Options.uMovesPerTimePeriod); + dSec = (dClock / (double)uMovesToDo); + dSec *= 0.95; + uMargin = (ULONG)(dSec * 2.2); + if (uMovesToDo <= 5) + { + dSec *= 0.75; + uMargin /= 2; + } + break; + } + else + { + // + // Fixed time finish entire game. + // +#ifdef DEBUG + Trace("SetMoveTimer: finish the game in %s sec.\n", + TimeToString(dClock)); +#endif + dSec = dClock / 60; + uMargin = (ULONG)(dSec * 2.2); + } + break; + + // + // We get back a certain number of seconds with each move made. + // + case CLOCK_INCREMENT: + dSec = g_Options.uMyIncrement; + dSec += (dClock / 50); + uMargin = (int)(dSec * 2.2); + +#ifdef DEBUG + Trace("SetMoveTimer: finish the game in %s sec (+%u per move).\n", + TimeToString(dClock), g_Options.uMyIncrement); +#endif + + // + // If we are running out of time, think for less than the + // increment. + // + if (dClock < 20.0) + { + dSec -= g_Options.uMyIncrement; + uMargin = 0; + } + ASSERT(dSec > 0); + break; + + case CLOCK_NONE: +#ifdef DEBUG + Trace("SetMoveTimer: no time limit, think until interrupted.\n"); +#endif + g_MoveTimer.dHardTimeLimit = -1; + g_MoveTimer.dSoftTimeLimit = -1; + goto post; + + default: + ASSERT(FALSE); + break; + } + if ((dSec + uMargin) >= (dClock * 7 / 8)) uMargin = 0; + g_MoveTimer.dSoftTimeLimit = g_MoveTimer.dStartTime + dSec; + g_MoveTimer.dHardTimeLimit = g_MoveTimer.dStartTime + dSec + uMargin; + + post: + if (TRUE == g_Options.fShouldPost) + { + if (g_MoveTimer.dSoftTimeLimit > 0) + { + Trace("SetMoveTimer: Soft time limit %s seconds.\n", + TimeToString(g_MoveTimer.dSoftTimeLimit - + g_MoveTimer.dStartTime)); + ASSERT(g_MoveTimer.dHardTimeLimit > 0); + Trace("SetMoveTimer: Hard time limit %s seconds.\n", + TimeToString(g_MoveTimer.dHardTimeLimit - + g_MoveTimer.dStartTime)); + if (TRUE == fSwitchOver) + { + Trace("SetMoveTimer: Already searched %s seconds.\n", + TimeToString(SystemTimeStamp() - + g_MoveTimer.dStartTime)); + } + } +#ifdef DEBUG + Trace("SetMoveTimer: Checking input and timer every %u nodes.\n", + g_MoveTimer.uNodeCheckMask); +#endif + } +} + + +void +ReInitializeSearcherContext(POSITION *pos, + SEARCHER_THREAD_CONTEXT *ctx) +/** + +Routine description: + + Reinitializes a SEARCHER_THREAD_CONTEXT structure. + +Parameters: + + POSITION *pos, + SEARCHER_THREAD_CONTEXT *ctx + +Return value: + + void + +**/ +{ + if (NULL != pos) + { + memmove(&(ctx->sPosition), pos, sizeof(POSITION)); + } + ctx->uPly = 0; + ctx->uPositional = 133; + memset(&(ctx->sCounters), 0, sizeof(COUNTERS)); +} + + +void +InitializeSearcherContext(POSITION *pos, + SEARCHER_THREAD_CONTEXT *ctx) +/** + +Routine description: + + Initialize a SEARCHER_THREAD_CONTEXT structure. + +Parameters: + + POSITION *pos, + SEARCHER_THREAD_CONTEXT *ctx + +Return value: + + void + +**/ +{ + ULONG u; + + memset(ctx, 0, sizeof(SEARCHER_THREAD_CONTEXT)); + ReInitializeSearcherContext(pos, ctx); + ctx->sMoveStack.uUnblockedKeyValue[0] = 1; + for (u = 1; + u < MAX_PLY_PER_SEARCH; + u++) + { + ctx->sMoveStack.uUnblockedKeyValue[u] = + ctx->sMoveStack.uUnblockedKeyValue[u-1] + 0x28F5C28; + } +} + + +void +InitializeLightweightSearcherContext(POSITION *pos, + LIGHTWEIGHT_SEARCHER_CONTEXT *ctx) +/** + +Routine description: + + Initialize a LIGHTWEIGHT_SEARCHER_CONTEXT structure. + +Parameters: + + POSITION *pos, + LIGHTWEIGHT_SEARCHER_CONTEXT *ctx + +Return value: + + void + +**/ +{ + ULONG u; + + memset(ctx, 0, sizeof(LIGHTWEIGHT_SEARCHER_CONTEXT)); + if (NULL != pos) + { + memmove(&(ctx->sPosition), pos, sizeof(POSITION)); + } + ctx->uPly = 0; + ctx->uPositional = 133; + ctx->sMoveStack.uUnblockedKeyValue[0] = 1; + for (u = 1; + u < MAX_PLY_PER_SEARCH; + u++) + { + ctx->sMoveStack.uUnblockedKeyValue[u] = + ctx->sMoveStack.uUnblockedKeyValue[u-1] + 0x28F5C28; + } +} + + +void +PostMoveSearchReport(SEARCHER_THREAD_CONTEXT *ctx) +/** + +Routine description: + + Dump a report to stdout after every move. + +Parameters: + + SEARCHER_THREAD_CONTEXT *ctx, + +Return value: + + void + +**/ +{ + double d; + char buf[256]; +#ifdef PERF_COUNTERS + double n; +#endif + d = (double)g_MoveTimer.dEndTime - (double)g_MoveTimer.dStartTime + 0.01; + ASSERT(d); + Trace("---------------------------------------------\n" + "Searched for %5.1f seconds, saw %" + COMPILER_LONGLONG_UNSIGNED_FORMAT + " nodes (%" + COMPILER_LONGLONG_UNSIGNED_FORMAT + " qnodes) (%6.0f nps).\n", + d, ctx->sCounters.tree.u64TotalNodeCount, + ctx->sCounters.tree.u64QNodeCount, + ((double)ctx->sCounters.tree.u64TotalNodeCount / d)); + if (g_Options.fThinking) + { + snprintf(buf, ARRAY_LENGTH(buf), "d%u, %s, %3.1fs, %6.0fknps, PV=%s", + ctx->uRootDepth / ONE_PLY, + ScoreToString(ctx->iRootScore), + d, + ((double)ctx->sCounters.tree.u64TotalNodeCount / d / 1000), + ctx->szLastPV); + Trace("tellothers %s\n", buf); + } +#ifdef MP +#ifdef PERF_COUNTERS + if (ctx->sCounters.parallel.uNumSplits > 0) + { + n = (double)ctx->sCounters.parallel.uNumSplits; + Trace("Split %u times total ", + ctx->sCounters.parallel.uNumSplits); + Trace("(~%7.2fx/sec).\n", (n / d)); + d = n + 1; + ASSERT(d); + n = (double)ctx->sCounters.parallel.uNumSplitsTerminated; + Trace(" ...%u (%5.2f percent) terminated early.\n", + ctx->sCounters.parallel.uNumSplitsTerminated, ((n/d) * 100.0)); + DumpHelperIdlenessReport(); + } +#endif +#endif + +#ifdef TEST + AnalyzeFullHashTable(); +#endif +#ifdef PERF_COUNTERS + d = (double)(ctx->sCounters.hash.u64Probes) + 1; + ASSERT(d); + Trace("Hashing percentages: (%5.3f total, %5.3f useful)\n", + ((double)(ctx->sCounters.hash.u64OverallHits) / d) * 100.0, + ((double)(ctx->sCounters.hash.u64UsefulHits) / d) * 100.0); + d = (double)(ctx->sCounters.pawnhash.u64Probes) + 1; + ASSERT(d); + n = (double)(ctx->sCounters.pawnhash.u64Hits); + Trace("Pawn hash hitrate: %5.3f percent.\n", (n/d) * 100.0); + n = (double)(ctx->sCounters.tree.u64NullMoveSuccess); + d = (double)(ctx->sCounters.tree.u64NullMoves) + 1; + ASSERT(d); + Trace("Null move cutoff rate: %5.3f percent.\n", + ((n / d) * 100.0)); +#ifdef TEST_ENDGAME + n = (double)(ctx->sCounters.tree.u64EndgamePruningSuccess); + d = n + (double)(ctx->sCounters.tree.u64EndgamePruningFailures); + if (d != 0.0) + { + Trace("Endgame pruning heuristic success rate was %5.3f percent.\n" + "Endgame pruning heuristic was " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT " / " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT ".\n", + ((n / d) * 100.0), + ctx->sCounters.tree.u64EndgamePruningSuccess, + (ctx->sCounters.tree.u64EndgamePruningSuccess + + ctx->sCounters.tree.u64EndgamePruningFailures)); + } + else + { + Trace("Never used endgame pruning heuristic.\n"); + } +#endif +#ifdef TEST_NULL + n = (double)(ctx->sCounters.tree.u64AvoidNullSuccess); + d = n + (double)(ctx->sCounters.tree.u64AvoidNullFailures); + if (d != 0.0) + { + Trace("Avoid null move heuristic rate was %5.3f percent.\n" + "Avoid null move heuristic was " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT " / " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT ".\n", + ((n / d) * 100.0), + ctx->sCounters.tree.u64AvoidNullSuccess, + (ctx->sCounters.tree.u64AvoidNullSuccess + + ctx->sCounters.tree.u64AvoidNullFailures)); + } + else + { + Trace("Never used the avoid null move heuristic.\n"); + } + n = (double)(ctx->sCounters.tree.u64QuickNullSuccess); + n += (double)(ctx->sCounters.tree.u64QuickNullDeferredSuccess); + d = n + (double)(ctx->sCounters.tree.u64QuickNullFailures); + if (d != 0.0) + { + Trace("Quick null move heuristic rate was %5.3f percent.\n" + "Quick null move heuristic rate was " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT " (" + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT ") / " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT ".\n", + ((n / d) * 100.0), + ctx->sCounters.tree.u64QuickNullSuccess, + ctx->sCounters.tree.u64QuickNullDeferredSuccess, + (ctx->sCounters.tree.u64QuickNullSuccess + + ctx->sCounters.tree.u64QuickNullDeferredSuccess + + ctx->sCounters.tree.u64QuickNullFailures)); + } + else + { + Trace("Never used the quick null move heuristic.\n"); + } +#endif + n = (double)ctx->sCounters.tree.u64BetaCutoffsOnFirstMove; + d = (double)ctx->sCounters.tree.u64BetaCutoffs + 1; + Trace("First move beta cutoff rate was %5.3f percent.\n", + ((n / d) * 100.0)); +#ifdef LAZY_EVAL + d = (double)ctx->sCounters.tree.u64LazyEvals; + d += (double)ctx->sCounters.tree.u64FullEvals; + d += (double)ctx->sCounters.tree.u64EvalHashHits; + d += 1; + ASSERT(d); + Trace("Eval percentages: (%5.2f hash, %5.2f lazy, %5.2f full)\n", + ((double)ctx->sCounters.tree.u64EvalHashHits / d) * 100.0, + ((double)ctx->sCounters.tree.u64LazyEvals / d) * 100.0, + ((double)ctx->sCounters.tree.u64FullEvals / d) * 100.0); +#endif + Trace("Extensions: (%u +, %u q+, %u 1mv, %u !kmvs, %u mult+, %u pawn\n" + " %u threat, %u zug, %u sing, %u endg, %u bm, %u recap)\n", + ctx->sCounters.extension.uCheck, + ctx->sCounters.extension.uQExtend, + ctx->sCounters.extension.uOneLegalMove, + ctx->sCounters.extension.uNoLegalKingMoves, + ctx->sCounters.extension.uMultiCheck, + ctx->sCounters.extension.uPawnPush, + ctx->sCounters.extension.uMateThreat, + ctx->sCounters.extension.uZugzwang, + ctx->sCounters.extension.uSingularReplyToCheck, + ctx->sCounters.extension.uEndgame, + ctx->sCounters.extension.uBotvinnikMarkoff, + ctx->sCounters.extension.uRecapture); +#ifdef EVAL_TIME + n = (double)ctx->sCounters.tree.u64CyclesInEval; + Trace("Avg. cpu cycles in eval: %8.1f.\n", (n / d)); +#endif +#endif + fflush(g_pfLogfile); +} + + +SCORE +RootSearch(SEARCHER_THREAD_CONTEXT *ctx, + SCORE iAlpha, + SCORE iBeta, + ULONG uDepth) +/** + +Routine description: + + Search at the root of the whole tree. + +Parameters: + + SEARCHER_THREAD_CONTEXT *ctx, + SCORE iAlpha, + SCORE iBeta, + ULONG uDepth, + +Return value: + + SCORE + +**/ +{ + PLY_INFO *pi = &ctx->sPlyInfo[ctx->uPly]; + SCORE iBestScore = -INFINITY; + SCORE iInitialAlpha = iAlpha; + MOVE mv; + ULONG x, y; + SCORE iScore; + ULONG uNextDepth; + UINT64 u64StartingNodeCount; + ULONG uNumLegalMoves; + +#ifdef DEBUG + POSITION *pos = &ctx->sPosition; + ASSERT(IS_VALID_SCORE(iAlpha)); + ASSERT(IS_VALID_SCORE(iBeta)); + ASSERT(iAlpha < iBeta); + ASSERT((ctx->uPly == 0) || (ctx->uPly == 1)); // 1 is for under a ponder + memcpy(&pi->sPosition, pos, sizeof(POSITION)); +#endif + + ctx->sCounters.tree.u64TotalNodeCount++; + pi->PV[ctx->uPly] = NULLMOVE; + pi->mvBest = NULLMOVE; + g_MoveTimer.bvFlags |= TIMER_SEARCHING_FIRST_MOVE; +#ifdef DEBUG + Trace("---- depth=%u, a=%d, b=%d ----\n", uDepth / ONE_PLY, iAlpha, iBeta); +#endif + + uNumLegalMoves = 0; + for (x = ctx->sMoveStack.uBegin[ctx->uPly]; + x < ctx->sMoveStack.uEnd[ctx->uPly]; + x++) + { + SelectMoveAtRoot(ctx, x); + if (ctx->sMoveStack.mvf[x].bvFlags & MVF_MOVE_SEARCHED) break; + ctx->sMoveStack.mvf[x].bvFlags |= MVF_MOVE_SEARCHED; + mv = ctx->sMoveStack.mvf[x].mv; + mv.bvFlags |= WouldGiveCheck(ctx, mv); + + if (MakeMove(ctx, mv)) + { + uNumLegalMoves++; + u64StartingNodeCount = ctx->sCounters.tree.u64TotalNodeCount; + + // + // TODO: Fancy recap shit here? + // + + uNextDepth = uDepth - ONE_PLY; + if (IS_CHECKING_MOVE(mv)) + { + uNextDepth += HALF_PLY; + ctx->sPlyInfo[ctx->uPly].iExtensionAmount = HALF_PLY; + } + + ctx->sSearchFlags.fVerifyNullmove = TRUE; + ctx->sSearchFlags.uQsearchDepth = 0; + if (uNumLegalMoves == 1) + { + // + // Move 1, full a..b window + // + iScore = -Search(ctx, -iBeta, -iAlpha, uNextDepth); + } + else + { + // + // Moves 2..N, minimal window search + // + ASSERT(x > 0); + ASSERT(uNumLegalMoves > 1); + iScore = -Search(ctx, -iAlpha - 1, -iAlpha, uNextDepth); + if ((iAlpha < iScore) && (iScore < iBeta)) + { + iScore = -Search(ctx, -iBeta, -iAlpha, uNextDepth); + } + } + ctx->sSearchFlags.fVerifyNullmove = FALSE; + UnmakeMove(ctx, mv); + if (WE_SHOULD_STOP_SEARCHING) + { + iBestScore = INVALID_SCORE; + goto end; + } +#ifdef DEBUG + Trace("%2u. %-8s => node=%" COMPILER_LONGLONG_UNSIGNED_FORMAT + ", predict=%d, actual=%d, ", + x + 1, + MoveToSan(mv, &ctx->sPosition), + u64StartingNodeCount, + ctx->sMoveStack.mvf[x].iValue, + iScore); + ASSERT(PositionsAreEquivalent(&pi->sPosition, &ctx->sPosition)); +#endif + // + // Keep track of how many moves are under each one we + // search and use this as the basis for root move ordering + // next iteration. + // + u64StartingNodeCount = (ctx->sCounters.tree.u64TotalNodeCount - + u64StartingNodeCount); + u64StartingNodeCount >>= 9; + u64StartingNodeCount &= (MAX_INT / 4); + ctx->sMoveStack.mvf[x].iValue = + (SCORE)(u64StartingNodeCount + iScore); +#ifdef DEBUG + Trace("next_predict: %d\n", ctx->sMoveStack.mvf[x].iValue); + ASSERT(iBestScore <= iAlpha); + ASSERT(iAlpha < iBeta); +#endif + if (iScore > iBestScore) + { + iBestScore = iScore; + pi->mvBest = mv; + if (iScore > iAlpha) + { + // + // We have a best move so far. As of now it is + // the one we'll be playing. Also make sure to + // search it first at the next depth. + // +#ifdef DEBUG + Trace("Best so far... root score=%d.\n", iScore); +#endif + ctx->mvRootMove = mv; + ctx->iRootScore = iScore; + ctx->uRootDepth = uDepth; + ctx->sMoveStack.mvf[x].iValue = MAX_INT; + + // + // If there was a previous PV move then knock its + // score down so that it will be considered second + // on the next depth. + // + for (y = ctx->sMoveStack.uBegin[ctx->uPly]; + y < x; + y++) + { + if (ctx->sMoveStack.mvf[y].iValue == MAX_INT) + { + ctx->sMoveStack.mvf[y].iValue /= 2; + } + } + + UpdatePV(ctx, mv); + if (iScore >= iBeta) + { + // + // Root fail high... + // + UpdateDynamicMoveOrdering(ctx, + uDepth, + mv, + iScore, + x + 1); + UtilPrintPV(ctx, iAlpha, iBeta, iScore, mv); + KEEP_TRACK_OF_FIRST_MOVE_FHs(iBestScore == -INFINITY); + ctx->sMoveStack.mvf[x].bvFlags &= ~MVF_MOVE_SEARCHED; + goto end; + } + else + { + // + // Root PV change... + // + UtilPrintPV(ctx, iAlpha, iBeta, iScore, mv); + iAlpha = iScore; + } + } + } + g_MoveTimer.bvFlags &= ~TIMER_SEARCHING_FIRST_MOVE; + } + } + + if (iAlpha == iInitialAlpha) + { + // + // Root fail low... + // + ASSERT(iBestScore <= iAlpha); + ASSERT(PositionsAreEquivalent(pos, &pi->sPosition)); + UtilPrintPV(ctx, iAlpha, iBeta, iBestScore, mv); + } + + end: + return(iBestScore); +} + + + +static GAME_RESULT +_DetectPreMoveTerminalStates(SEARCHER_THREAD_CONTEXT *ctx, + FLAG fInCheck, + MOVE *pmv) { + ULONG u; + MOVE mv; + MOVE mvLegal; + ULONG uNumLegal = 0; + GAME_RESULT ret; + + ret.eResult = RESULT_IN_PROGRESS; + ret.szDescription[0] = '\0'; + pmv->uMove = 0; + + for (u = ctx->sMoveStack.uBegin[ctx->uPly]; + u < ctx->sMoveStack.uEnd[ctx->uPly]; + u++) { + mv = ctx->sMoveStack.mvf[u].mv; + if (MakeMove(ctx, mv)) { + mvLegal = mv; + uNumLegal += 1; + UnmakeMove(ctx, mv); + if (uNumLegal > 1) { + return ret; + } + } + } + if (uNumLegal == 1) { + *pmv = mvLegal; + return ret; + } + ASSERT(uNumLegal == 0); + if (TRUE == fInCheck) { + if (ctx->sPosition.uToMove == WHITE) { + ret.eResult = RESULT_BLACK_WON; + strcpy(ret.szDescription, "black checkmated white"); + return ret; + } + ret.eResult = RESULT_WHITE_WON; + strcpy(ret.szDescription, "white checkmated black"); + return ret; + } + ret.eResult = RESULT_DRAW; + sprintf(ret.szDescription, "%s stalemated %s", + (ctx->sPosition.uToMove == WHITE) ? "black" : "white", + (ctx->sPosition.uToMove == WHITE) ? "while" : "black"); + return ret; +} + + + +static GAME_RESULT +_DetectPostMoveTerminalStates(SEARCHER_THREAD_CONTEXT *ctx) +/** + +Routine description: + + This function is called by Iterate and MakeTheMove to declare + wins/losses/draws. + +Parameters: + + SEARCHER_THREAD_CONTEXT *ctx + +Return value: + + FLAG + +**/ +{ + GAME_RESULT ret; + FLAG fInCheck = InCheck(&ctx->sPosition, ctx->sPosition.uToMove); + + // Did we just make the 50th+ move without progress? + if (ctx->sPosition.uFifty >= 100) { + ret.eResult = RESULT_DRAW; + strcpy(ret.szDescription, "fifty moves without progress"); + return ret; + } + GenerateMoves(ctx, NULLMOVE, (fInCheck ? GENERATE_ESCAPES : + GENERATE_ALL_MOVES)); + + // Did we just repeat a position for the 3rd time? + if (IsLegalDrawByRepetition()) { + ret.eResult = RESULT_DRAW; + strcpy(ret.szDescription, "third repetition of position"); + return ret; + } + + // Otherwise look for checkmates and stalemates. + MOVE mvUnused; + return _DetectPreMoveTerminalStates(ctx, + fInCheck, + &mvUnused); +} + + +static void +_IterateSetSearchGlobals(ULONG uDepth) +{ + ULONG u, v; + ULONG uDontSplitLessThan; + + // + // Set some globals that will be used throughout this search. + // + g_uIterateDepth = uDepth; + ASSERT(g_uIterateDepth > 0); + ASSERT(g_uIterateDepth < MAX_PLY_PER_SEARCH); + + // + // For use in scaling back extensions that are very far from + // the root. + // + // Iterate g_Soft g_Hard + // | | | | | | | | | + // 0 1 2 2.5 3 3.5 4 (x Iterate) + // |-------------------|----|----|---------|----... + // |full full full full|-1/4|-1/2|-3/4 -3/4|none... + // + g_uSoftExtendLimit = g_uIterateDepth * 2; + g_uHardExtendLimit = g_uIterateDepth * 4; + for (u = 0; u < MAX_PLY_PER_SEARCH; u++) + { + if (u < g_uSoftExtendLimit) + { + g_uExtensionReduction[u] = 0; + } + else if (u < (g_uSoftExtendLimit + g_uIterateDepth / 2)) + { + g_uExtensionReduction[u] = QUARTER_PLY; + } + else if (u < (g_uSoftExtendLimit + g_uIterateDepth)) + { + g_uExtensionReduction[u] = HALF_PLY; + } + else if (u < g_uHardExtendLimit) + { + g_uExtensionReduction[u] = THREE_QUARTERS_PLY; + } + else + { + g_uExtensionReduction[u] = 5 * ONE_PLY; + } + } + + // + // Determine how much depth we need below a point in the tree in + // order to split the search (which is based on iteration depth). + // + uDontSplitLessThan = (ULONG)((double)(uDepth) * 0.30); + for (v = 0; v < MAX_PLY_PER_SEARCH; v++) + { + g_fCanSplit[v] = ((v + 1) >= uDontSplitLessThan); + } + g_fCanSplit[0] = FALSE; +} + + +#define SKIP_GRADUAL_OPENING 600 +#define INITIAL_HALF_WINDOW 75 +#define FIRST_FAIL_STEP 150 +#define SECOND_FAIL_STEP 375 + +void +_IterateWidenWindow(ULONG uColor, + SCORE iScore, + SCORE *piBound, + ULONG *puState, + int iDir) +{ + SCORE iRoughScore = g_iRootScore[uColor]; + static SCORE _Steps[] = + { + FIRST_FAIL_STEP, + SECOND_FAIL_STEP, + INFINITY + }; + +// Trace("State = %u.\n", *puState); +// Trace("Bound from %d to ", *piBound); + if ((abs(iRoughScore - iScore) > SKIP_GRADUAL_OPENING) || + (*puState == 2)) + { + *piBound = iDir * INFINITY; + *puState = 2; + } + else + { + ASSERT(*puState < 2); + *piBound = iScore; + *piBound += iDir * _Steps[*puState]; + *puState += 1; + } +// Trace("%d.\n", *piBound); +// Trace("State = %u\n", *puState); +} + + +GAME_RESULT Iterate(SEARCHER_THREAD_CONTEXT *ctx) +/** + +Routine description: + + Main iterative deepening loop for both thinking and pondering. + Call RootSearch with progressively deeper depths while there is + remaining time, we haven't found a forced mate, and there's no + user interruption. + +Parameters: + + SEARCHER_THREAD_CONTEXT *ctx + +Return value: + + FLAG : is the game over? + +**/ +{ + ULONG uNumRootFailLows = 0; + ULONG uDepth; + ULONG uFailState = 0; + ULONG uColor; + ULONG u; + SCORE iAlpha = -INFINITY; + SCORE iBeta = +INFINITY; + SCORE iScore; + FLAG fInCheck = InCheck(&(ctx->sPosition), ctx->sPosition.uToMove); + MOVE mv; + GAME_RESULT ret; +#ifdef DEBUG + POSITION board; + memcpy(&board, &(ctx->sPosition), sizeof(POSITION)); + VerifyPositionConsistency(&board, FALSE); +#endif + + uColor = ctx->sPosition.uToMove; + g_iRootScore[uColor] = GetRoughEvalScore(ctx, iAlpha, iBeta, TRUE); + g_iRootScore[FLIP(uColor)] = -g_iRootScore[uColor]; + + // + // Generate moves here so that RootSearch doesn't have to. + // + ASSERT((ctx->uPly == 0) || (ctx->uPly == 1)); // 1 is under a ponder + ctx->sPlyInfo[ctx->uPly].fInCheck = fInCheck; + GenerateMoves(ctx, NULLMOVE, (fInCheck ? GENERATE_ESCAPES : + GENERATE_ALL_MOVES)); + ASSERT(ctx->sMoveStack.uBegin[0] == 0); + + // + // See if we are sitting at a checkmate or stalemate position; no + // sense searching if so. + // + ret = _DetectPreMoveTerminalStates(ctx, + fInCheck, + &mv); + if (ret.eResult != RESULT_IN_PROGRESS) { + goto end; + } + + // Game still in progress but only one legal move available? + if (mv.uMove != 0) { + ctx->mvRootMove = mv; + ctx->iRootScore = 0; + ctx->uRootDepth = 0; + ctx->sPlyInfo[0].PV[0] = mv; + ctx->sPlyInfo[0].PV[1] = NULLMOVE; + strcpy(ctx->szLastPV, "only"); + if (!g_Options.fPondering) { + Trace("ONLY MOVE --> stop searching now\n"); + g_MoveTimer.bvFlags |= TIMER_STOPPING; + g_MoveTimer.bvFlags |= TIMER_CURRENT_OBVIOUS; + } else { + Trace("ONLY PONDER --> move immediately if prediction correct.\n"); + g_MoveTimer.bvFlags |= TIMER_MOVE_IMMEDIATELY; + g_MoveTimer.bvFlags |= TIMER_CURRENT_OBVIOUS; + } + goto end; + } + + for (uDepth = 1; + uDepth <= g_Options.uMaxDepth; + uDepth++) + { + // + // Re-rank the moves based on nodecounts and mark all moves as + // not yet searched at this depth. + // + for (u = ctx->sMoveStack.uBegin[ctx->uPly]; + u < ctx->sMoveStack.uEnd[ctx->uPly]; + u++) + { + ctx->sMoveStack.mvf[u].bvFlags &= ~MVF_MOVE_SEARCHED; + } + + // + // Make sure a..b window makes sense and set some other per-depth + // globals used by the search. + // + _IterateSetSearchGlobals(uDepth); + + // + // Try to get a PV for this depth before we're out of time... + // + do + { + if (iBeta > INFINITY) iBeta = +INFINITY; + if (iAlpha < -INFINITY) iAlpha = -INFINITY; + if (iAlpha >= iBeta) iAlpha = iBeta - 1; + iScore = RootSearch(ctx, + iAlpha, + iBeta, + uDepth * ONE_PLY + HALF_PLY); + if (g_MoveTimer.bvFlags & TIMER_STOPPING) break; + mv = ctx->mvRootMove; + + // + // Got a PV, we're done with this depth. + // + if ((iAlpha < iScore) && (iScore < iBeta)) + { + ASSERT(iScore == ctx->iRootScore); + ASSERT(mv.uMove); + ASSERT(SanityCheckMove(&ctx->sPosition, mv)); + iAlpha = iScore - INITIAL_HALF_WINDOW; + iBeta = iScore + INITIAL_HALF_WINDOW; + g_iRootScore[uColor] = iScore; + g_iRootScore[FLIP(uColor)] = -iScore; + g_MoveTimer.bvFlags &= ~TIMER_RESOLVING_ROOT_FH; + g_MoveTimer.bvFlags &= ~TIMER_RESOLVING_ROOT_FL; + uFailState = 0; + (void)CheckTestSuiteMove(mv, iScore, uDepth); + break; + } + + // + // Root fail low? We'll have to research with a wider + // window. + // + else if (iScore <= iAlpha) + { + ASSERT(ctx->mvRootMove.uMove); + g_MoveTimer.bvFlags |= TIMER_RESOLVING_ROOT_FL; + uNumRootFailLows++; + if (uNumRootFailLows > 3) + { + g_MoveTimer.bvFlags |= TIMER_MANY_ROOT_FLS; + } + _IterateWidenWindow(uColor, iScore, &iAlpha, &uFailState, -1); + + // + // Consider all moves again with wider window... + // + for (u = ctx->sMoveStack.uBegin[ctx->uPly]; + u < ctx->sMoveStack.uEnd[ctx->uPly]; + u++) + { + ctx->sMoveStack.mvf[u].bvFlags &= ~MVF_MOVE_SEARCHED; + } + } + + // + // Must be a root fail high. + // + else + { + ASSERT(iScore >= iBeta); + ASSERT(mv.uMove); + ASSERT(SanityCheckMove(&ctx->sPosition, mv)); + g_MoveTimer.bvFlags |= TIMER_RESOLVING_ROOT_FH; + _IterateWidenWindow(uColor, iScore, &iBeta, &uFailState, +1); + } + } + while(1); // repeat until we run out of time or fall inside a..b + + // + // We are here because either: + // + // 1. we completed a search depth, + // or 2. we ran out of time, + // or 3. there is one legal move and we want to make it immediately, + // or 4. there was user input and we unrolled the search. + // + + // + // TODO: Scale back history between iterative depths? + // + + // + // Mate-in-n when we have searched n? Stop searching. + // + if (IS_VALID_SCORE(iScore) && + (ULONG)abs(iScore) >= (INFINITY - uDepth)) + { + Trace("FORCED MATE --> stop searching now\n"); + g_MoveTimer.bvFlags |= TIMER_STOPPING; + } + + // + // Have we reached user's maxdepth option? Stop searching. + // + if (uDepth >= g_Options.uMaxDepth) + { + Trace("DEPTH LIMIT --> stop searching now\n"); + g_MoveTimer.bvFlags |= TIMER_STOPPING; + } + + // + // Did the move timer expire? Stop searching. + // + if (g_MoveTimer.bvFlags & TIMER_STOPPING) break; + } + g_Options.u64NodesSearched = ctx->sCounters.tree.u64TotalNodeCount; + g_MoveTimer.dEndTime = SystemTimeStamp(); + + // + // Here we are at the end of a search. If we were: + // + // ...Searching then: ...Pondering then: + // + // 1. we ran out of time 1. they made a different move than + // the predicted move and we aborted + // 2. we ran out of depth + // 2. we ran out of depth + // 3. we found a mate-in-n + // 3. we found a mate-in-n + // TODO: node count limit? + // (4. if they made the predicted move + // then we converted to a search) + // + ASSERT(ctx->mvRootMove.uMove); + ASSERT(SanityCheckMove(&board, ctx->mvRootMove)); + if (g_MoveTimer.bvFlags & TIMER_RESOLVING_ROOT_FL) + { + ctx->sPlyInfo[ctx->uPly].PV[ctx->uPly] = ctx->mvRootMove; + ctx->sPlyInfo[ctx->uPly].PV[ctx->uPly+1] = NULLMOVE; + } + ret.eResult = RESULT_IN_PROGRESS; + ret.szDescription[0] = '\0'; + end: + return ret; +} + +void +MakeTheMove(SEARCHER_THREAD_CONTEXT *ctx) +/** + +Routine description: + + Make the move that search selected in the official game list. + +Parameters: + + SEARCHER_THREAD_CONTEXT *ctx + +Return value: + + FLAG + +**/ +{ + MOVE mv = ctx->mvRootMove; + ASSERT(mv.uMove); + + if (TRUE == g_Options.fThinking) + { + // + // TODO: keep track of how many moves in a row we are at or + // below a draw score and use this to respond to draw offers. + // + + // + // Was this a search that converted from a ponder? + // + if (TRUE == g_Options.fSuccessfulPonder) + { + if (FALSE == OfficiallyMakeMove(g_Options.mvPonder, 0, FALSE)) + { + UtilPanic(CANNOT_OFFICIALLY_MAKE_MOVE, + GetRootPosition(), + (void *)g_Options.mvPonder.uMove, + NULL, + NULL, + __FILE__, + __LINE__); + } + g_Options.fSuccessfulPonder = FALSE; + } + + // + // Tell the server about our move and actually play it on the + // root board. + // + Trace("move %s\n", MoveToSan(mv, &ctx->sPosition)); + if (FALSE == OfficiallyMakeMove(mv, g_iScore, FALSE)) + { + UtilPanic(CANNOT_OFFICIALLY_MAKE_MOVE, + GetRootPosition(), + (void *)mv.uMove, + NULL, + NULL, + __FILE__, + __LINE__); + } + if (g_Options.fVerbosePosting) + { + PostMoveSearchReport(ctx); + PostMoveTestSuiteReport(ctx); + } + } + g_Options.fPondering = g_Options.fThinking = FALSE; +} + + +GAME_RESULT +Ponder(POSITION *pos) +/** + +Routine description: + + Prepare to ponder. + +Parameters: + + POSITION *pos + +Return value: + + FLAG + +**/ +{ + static SEARCHER_THREAD_CONTEXT ctx; + GAME_RESULT ret; + + InitializeSearcherContext(pos, &ctx); + g_Options.fPondering = TRUE; + g_Options.fThinking = FALSE; + g_Options.fSuccessfulPonder = FALSE; + ret.eResult = RESULT_IN_PROGRESS; + ret.szDescription[0] = '\0'; + + // + // When do we not want to ponder + // + if ((g_Options.ePlayMode == FORCE_MODE) || (g_Options.uMyClock < 10)) + { + g_Options.fPondering = FALSE; + return ret; + } + + // + // What should we ponder? + // + g_Options.mvPonder = GetPonderMove(pos); + if (g_Options.mvPonder.uMove == 0) + { + g_Options.fPondering = FALSE; + return ret; + } + ASSERT(SanityCheckMove(pos, g_Options.mvPonder)); + + // + // Prepare to ponder by doing some maintenance on the dynamic move + // ordering scheme counters, changing the dirty tag in the hash + // code, and clearing the root nodes-per-move hash. + // + MaintainDynamicMoveOrdering(); + DirtyHashTable(); + + // + // Make the move ponder move. + // + if (FALSE == MakeMove(&ctx, g_Options.mvPonder)) + { + ASSERT(FALSE); + g_Options.fPondering = FALSE; + return ret; + } + + // + // TODO: probe the book + // + + _SetMoveTimerForPonder(); + + // + // TODO: Any preEval? + // + + // + // TODO: Set draw value + // + + MakeStatusLine(); +#if (PERF_COUNTERS && MP) + ClearHelperThreadIdleness(); +#endif + ASSERT(ctx.uPly == 1); + ret = Iterate(&ctx); + ASSERT(ctx.uPly == 1); + if (ret.eResult == RESULT_IN_PROGRESS) { + MakeTheMove(&ctx); + return _DetectPostMoveTerminalStates(&ctx); + } else { + return ret; + } +} + + +GAME_RESULT Think(POSITION *pos) +/** + +Routine description: + + Prepare to think. + +Parameters: + + POSITION *pos + +Return value: + + FLAG + +**/ +{ + static SEARCHER_THREAD_CONTEXT ctx; + MOVE mvBook; + CHAR *p; + + InitializeSearcherContext(pos, &ctx); + g_MoveTimer.bvFlags = 0; + g_Options.fPondering = FALSE; + g_Options.fThinking = TRUE; + g_Options.fSuccessfulPonder = FALSE; + + // + // Prepare to think by doing some maintenance on the dynamic move + // ordering scheme counters, changing the dirty tag in the hash + // code, and clearing the root nodes-per-move hash. + // + if (NULL != (p = PositionToFen(&(ctx.sPosition)))) + { + Trace("The root position is: %s\n", p); + SystemFreeMemory(p); + } + MaintainDynamicMoveOrdering(); + DirtyHashTable(); + + // + // Check the opening book, maybe it has a move for us. + // + if ((g_uBookProbeFailures < 3) && (!WeAreRunningASuite())) + { + mvBook = BookMove(pos, BOOKMOVE_SELECT_MOVE); + if (mvBook.uMove == 0) + { + g_uBookProbeFailures++; + } + else + { + g_uBookProbeFailures = 0; + ASSERT(SanityCheckMove(pos, mvBook)); + ctx.mvRootMove = mvBook; + MakeTheMove(&ctx); + return _DetectPostMoveTerminalStates(&ctx); + } + } + + // + // TODO: Any preEval? + // + + // + // Set time limit for move + // + SetMoveTimerForSearch(FALSE, pos->uToMove); + + // + // TODO: Set draw value + // +#if (PERF_COUNTERS && MP) + ClearHelperThreadIdleness(); +#endif + GAME_RESULT ret = Iterate(&ctx); + if (ret.eResult == RESULT_IN_PROGRESS) { + MakeTheMove(&ctx); + return _DetectPostMoveTerminalStates(&ctx); + } else { + return ret; + } +} diff --git a/src/save/util.c b/src/save/util.c new file mode 100755 index 0000000..90431e2 --- /dev/null +++ b/src/save/util.c @@ -0,0 +1,1254 @@ +/** + +Copyright (c) Scott Gasch + +Module Name: + + util.c + +Abstract: + + General purpose utility code to support the engine. + +Author: + + Scott Gasch (scott.gasch@gmail.com) 8 Apr 2004 + +Revision History: + + $Id: util.c 345 2007-12-02 22:56:42Z scott $ + +**/ + +#include "chess.h" + +extern ULONG g_uIterateDepth; +extern ULONG g_uFullWidthDepth; + +#define STATE_UNKNOWN (0) +#define STATE_HEADER (1) +#define STATE_MOVELIST (2) + +CHAR * +ReadNextGameFromPgnFile(FILE *pf) +/** + +Routine description: + + Reads the next game in a PGN file and returns a pointer to it. + +Parameters: + + FILE *pf : open file pointer + +Return value: + + CHAR * : NULL on error, else caller must free via SystemFreeMemory. + +**/ +{ + CHAR buf[MEDIUM_STRING_LEN_CHAR]; + CHAR *p; + long lStartOfGame = -1; + long lEndOfGame = -1; + long lBefore; + ULONG uBytes; + ULONG uState = STATE_UNKNOWN; + + while(1) + { + lBefore = ftell(pf); + if (fgets(buf, ARRAY_LENGTH(buf), pf) <= 0) + { + //Trace("fgets failed, errno=%u.\n", errno); + break; + } + + // + // Skip leading whitespace on this line... + // + p = buf; + while(*p && isspace(*p)) p++; + + if (*p == '\0') + { + // + // We just read a blank line + // + switch(uState) + { + case STATE_UNKNOWN: + break; + + case STATE_HEADER: + uState = STATE_MOVELIST; + break; + + case STATE_MOVELIST: + lEndOfGame = lBefore; + uBytes = lEndOfGame - lStartOfGame; + if (uBytes <= 0) + { + return(NULL); + } + + p = SystemAllocateMemory(uBytes + 1); + ASSERT(p); + fseek(pf, lStartOfGame, SEEK_SET); + if (1 != fread(p, uBytes, 1, pf)) + { + SystemFreeMemory(p); + return(NULL); + } + p[uBytes] = '\0'; + return(p); + } + } + else + { + // + // We saw a non-blank line + // + switch(uState) + { + case STATE_HEADER: + case STATE_MOVELIST: + break; + + case STATE_UNKNOWN: + if (*p == '[') + { + uState = STATE_HEADER; + lStartOfGame = lBefore; + } + break; + } + } + } + return(NULL); +} + + +COMMAND(LearnPsqtFromPgn) +/** + +Routine description: + +Parameters: + + CHAR *szFilename, + +Return value: + + FLAG + +**/ +{ + CHAR *szFilename; + ULONG uPSQT[12][128]; + CHAR *pPGN; + FILE *pf = NULL; + DLIST_ENTRY *p; + GAME_MOVE *q; + ULONG x, y, z; + double d; + struct stat s; +#ifdef DEBUG + ULONG uHeap; +#endif + + if (argc < 2) + { + Trace("Error (missing filename)\n"); + return; + } + szFilename = argv[1]; + + if (FALSE == SystemDoesFileExist(szFilename)) + { + Trace("LearnPsqtFromPgn: file \"%s\" doesn't exist.\n", + szFilename); + return; + } + + if (0 != stat(szFilename, &s)) + { + Trace("Stat failed.\n"); + return; + } + + pf = fopen(szFilename, "rb"); + if (NULL == pf) + { + Trace("LearnPsqtFromPgn: error reading file \"%s\".\n", + szFilename); + return; + } + + memset(uPSQT, 0, sizeof(uPSQT)); +#ifdef DEBUG + ResetGameList(); + uHeap = GetHeapMemoryUsage(); +#endif + while((pPGN = ReadNextGameFromPgnFile(pf)) != NULL) + { + d = (double)ftell(pf); + d /= (double)s.st_size; + d *= 100.0; + printf("(%5.2f%% done)\r", d); + + if (TRUE == LoadPgn(pPGN)) + { + p = g_GameData.sMoveList.pFlink; + while(p != &(g_GameData.sMoveList)) + { + q = CONTAINING_STRUCT(p, GAME_MOVE, links); + if ((q->uNumber > 10) && + (q->uNumber < 50) && + (!q->mv.pCaptured)) + { + uPSQT[q->mv.pMoved - 2][q->mv.cTo] += 1; + } + p = p->pFlink; + } + } + SystemFreeMemory(pPGN); +#ifdef DEBUG + ResetGameList(); + ASSERT(uHeap == GetHeapMemoryUsage()); +#endif + } + fclose(pf); + + z = 0; + for (x = 0; x < 12; x++) + { + for (y = 0; y < 128; y++) + { + if (uPSQT[x][y] > z) + { + z = uPSQT[x][y]; + } + } + } + + Trace("ULONG g_PSQT[12][128] = {\n"); + for (x = 0; x < 12; x++) + { + Trace("// Piece 0x%x (%s)\n", x + 2, PieceAbbrev((PIECE)(x + 2))); + Trace("{\n"); + + for (y = 0; y < 128; y++) + { + if (y % 16 == 0) Trace("\n"); + d = (double)uPSQT[x][y]; + d /= (double)z; + d *= (double)1000.0; + Trace("%4u,", (ULONG)d); + } + Trace("},\n"); + } + Trace("};\n"); +} + + +COMMAND(GeneratePositionAndBestMoveSuite) +/** + +Routine description: + +Parameters: + + CHAR *szFilename, + +Return value: + + FLAG + +**/ +{ + CHAR *szFilename; + CHAR *pPGN; + FILE *pf = NULL; + DLIST_ENTRY *p; + GAME_MOVE *q; + SEARCHER_THREAD_CONTEXT *ctx = NULL; + FLAG fPost = g_Options.fShouldPost; + struct stat s; + double d; + POSITION board; + + if (argc < 2) + { + Trace("Error (missing filename)\n"); + goto end; + } + szFilename = argv[1]; + + if (FALSE == SystemDoesFileExist(szFilename)) + { + Trace("LearnPsqtFromPgn: file \"%s\" doesn't exist.\n", + szFilename); + goto end; + } + + if (0 != stat(szFilename, &s)) + { + Trace("Stat failed.\n"); + goto end; + } + + pf = fopen(szFilename, "rb"); + if (NULL == pf) + { + Trace("LearnPsqtFromPgn: error reading file \"%s\".\n", + szFilename); + goto end; + } + + ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); + if (NULL == ctx) + { + Trace("Out of memory.\n"); + goto end; + } + + g_Options.fShouldPost = FALSE; + while((pPGN = ReadNextGameFromPgnFile(pf)) != NULL) + { + d = (double)ftell(pf); + d /= (double)s.st_size; + d *= 100.0; + printf("(%5.2f%% done)\r", d); + + if (TRUE == LoadPgn(pPGN)) + { + p = g_GameData.sMoveList.pFlink; + while(p != &(g_GameData.sMoveList)) + { + q = CONTAINING_STRUCT(p, GAME_MOVE, links); + if ((q->uNumber > 20) && (q->uNumber < 60)) + { + if (FenToPosition(&board, q->szUndoPositionFen)) + { + InitializeSearcherContext(&board, ctx); + g_MoveTimer.bvFlags = 0; + g_Options.fPondering = FALSE; + g_Options.fThinking = TRUE; + g_Options.fSuccessfulPonder = FALSE; + + MaintainDynamicMoveOrdering(); + DirtyHashTable(); + + g_MoveTimer.uNodeCheckMask = 0x1000 - 1; + g_MoveTimer.dStartTime = SystemTimeStamp(); + g_MoveTimer.bvFlags = 0; + g_MoveTimer.dSoftTimeLimit = + g_MoveTimer.dStartTime + 3.0; + g_MoveTimer.dHardTimeLimit = + g_MoveTimer.dStartTime + 3.0; + g_Options.uMaxDepth = MAX_DEPTH_PER_SEARCH - 1; + + // + // TODO: Set draw value + // +#if (PERF_COUNTERS && MP) + ClearHelperThreadIdleness(); +#endif + GAME_RESULT result = Iterate(ctx); + if (result.eResult == RESULT_IN_PROGRESS) { + ASSERT(SanityCheckMove(&board, + ctx->sPlyInfo[0].PV[0])); + Trace("setboard %s\n", q->szUndoPositionFen); + Trace("gmmove %s\n", q->szMoveInSan); + Trace("solution %s\n", + MoveToSan(ctx->sPlyInfo[0].PV[0], &board)); + } + } + } + p = p->pFlink; + } + } + SystemFreeMemory(pPGN); + } + + end: + if (NULL != pf) + { + fclose(pf); + } + + if (NULL != ctx) + { + SystemFreeMemory(ctx); + } + + g_Options.fShouldPost = fPost; +} + + +char * +FindChunk(char *sz, ULONG uTargetChunk) +/** + +Routine description: + + A support routine used by FenToPosition. Used to parse apart a + string and return the Nth chunk of text (delimited by whitespace) + +Parameters: + + char *sz, + ULONG uTargetChunk + +Return value: + + char * + +**/ +{ + char *p = sz; + ULONG u = 0; + + if (0 == uTargetChunk) return(p); + + do + { + // + // Skip whitespace between chunks + // + while(isspace(*p) && *p) p++; + if (!*p) return(NULL); + + u++; + if (u == uTargetChunk) return(p); + + // + // Skip chunks + // + while(!isspace(*p) && &p) p++; + if (!*p) return(NULL); + } + while(1); + +#ifdef DEBUG + UtilPanic(SHOULD_NOT_GET_HERE, + NULL, NULL, NULL, NULL, + __FILE__, __LINE__); +#endif + return(NULL); +} + + + +CHAR * +ScoreToString(SCORE iScore) +/** + +Routine description: + + Format a chess score as a string that looks like: + + +0.100 + -4.951 + +MATE20 + -MATE4 + +0.000 + + etc... + + Note: NOT thread safe. + +Parameters: + + SCORE iScore + +Return value: + + CHAR * + +**/ +{ + static char szBuf[10]; + ULONG uDist; + + if (abs(iScore) < +NMATE) + { + snprintf(szBuf, ARRAY_LENGTH(szBuf), + "%c%u.%02u", + (iScore < 0) ? '-' : '+', + abs(iScore / 100), + abs(iScore % 100)); + return(szBuf); + } + + if (iScore > 0) + { + uDist = +INFINITY - iScore; + uDist /= 2; + if (uDist) + { + snprintf(szBuf, ARRAY_LENGTH(szBuf), "+MATE%u", uDist); + } + else + { + snprintf(szBuf, ARRAY_LENGTH(szBuf), "+MATE"); + } + } + else + { + uDist = +INFINITY + iScore; + uDist /= 2; + if (uDist) + { + snprintf(szBuf, ARRAY_LENGTH(szBuf), "-MATE%u", uDist); + } + else + { + snprintf(szBuf, ARRAY_LENGTH(szBuf), "-MATE"); + } + } + return(szBuf); +} + + + +CHAR * +CoorToString(COOR c) +/** + +Routine description: + + Take a chessboard coordinate and convert it into a string. Note: + NOT thread safe. + +Parameters: + + COOR c + +Return value: + + CHAR * + +**/ +{ + static char buf[3]; + + if (IS_ON_BOARD(c)) + { + buf[0] = 'a' + FILE(c); + buf[1] = '0' + RANK(c); + buf[2] = '\0'; + } + else + { + buf[0] = '-'; + buf[1] = '\0'; + } + return(buf); +} + + +CHAR * +ColorToString(ULONG u) +/** + +Routine description: + + Convert a color into a string ("WHITE" or "BLACK"). Callers + should not mess with the contents of the buffer returned! + +Parameters: + + ULONG u + +Return value: + + char + +**/ +{ + static CHAR *szColor[2] = + { + "BLACK", + "WHITE" + }; + ASSERT(IS_VALID_COLOR(u)); + return(szColor[u]); +} + + +CHAR * +TimeToString(double d) +/** + +Routine description: + + Convert a time value into a string. e.g. + + 00:00:01.021 // seconds with ms + 00:15:21.998 // minutes+seconds + 01:59:23.232 // hours+minutes+sec + 27:08:11:103 // we don't do days + + Note: NOT thread safe. The caller should not mess with the + buffer returned. + +Parameters: + + double d + +Return value: + + CHAR * + +**/ +{ + ULONG uTotalSecs = (ULONG)d; + double dFract = d - (double)uTotalSecs; + ULONG uHours; + ULONG uMinutes; + ULONG uFract; + static char buf[13]; + + dFract *= 1000; + uFract = (int)dFract; + uHours = uTotalSecs / 60 / 60; + uTotalSecs -= uHours * 60 * 60; + uMinutes = uTotalSecs / 60; + uTotalSecs -= uMinutes * 60; + ASSERT(uTotalSecs < 60); + + if (uHours != 0) + { + snprintf(buf, ARRAY_LENGTH(buf), + "%2u:%02u:%02u.%03u", + uHours, uMinutes, uTotalSecs, uFract); + } + else + { + snprintf(buf, ARRAY_LENGTH(buf), + "%02u:%02u.%03u", + uMinutes, uTotalSecs, uFract); + } + return(buf); +} + + +FLAG +BufferIsZeroed(BYTE *p, ULONG u) +/** + +Routine description: + + Verify that a buffer is zeroed out. + +Parameters: + + BYTE *p : buffer start + ULONG u : buffer length + +Return value: + + FLAG + +**/ +{ + while(u) + { + if (*p != (BYTE)0) + { + return(FALSE); + } + p++; + u--; + } + return(TRUE); +} + + +void CDECL +Trace(CHAR *szMessage, ...) +/** + +Routine description: + + Trace a logging message on stdout and to the logfile + +Parameters: + + CHAR *szMessage, + ... + +Return value: + + void + +**/ +{ + va_list ap; // Variable argument list + CHAR buf[MEDIUM_STRING_LEN_CHAR]; + + memset(buf, 0, sizeof(buf)); + + // + // Populate buffer + // + va_start(ap, szMessage); + COMPILER_VSNPRINTF(buf, MEDIUM_STRING_LEN_CHAR - 1, szMessage, ap); + va_end(ap); + + // + // Send to logfile + // + if (NULL != g_pfLogfile) + { + fprintf(g_pfLogfile, buf); + } + + // + // Send to stdout + // + fprintf(stdout, buf); + fflush(stdout); +} + + +void CDECL +Log(CHAR *szMessage, ...) +/** + +Routine description: + + Trace a logging message to the logfile only + +Parameters: + + CHAR *szMessage, + ... + +Return value: + + void + +**/ +{ + va_list ap; // Variable argument list + CHAR buf[MEDIUM_STRING_LEN_CHAR]; + memset(buf, 0, sizeof(buf)); + + // + // Populate buffer + // + va_start(ap, szMessage); + COMPILER_VSNPRINTF(buf, MEDIUM_STRING_LEN_CHAR - 1, szMessage, ap); + va_end(ap); + + // + // Send to logfile + // + if (NULL != g_pfLogfile) + { + fprintf(g_pfLogfile, buf); + } +} + + +void CDECL +Bug(CHAR *szMessage, ...) +/** + +Routine description: + + Trace an error message to stderr and the logfile + +Parameters: + + CHAR *szMessage, + ... + +Return value: + + void + +**/ +{ + va_list ap; // Variable argument list + CHAR buf[MEDIUM_STRING_LEN_CHAR]; + + memset(buf, 0, sizeof(buf)); + + // + // Populate buffer + // + va_start(ap, szMessage); + COMPILER_VSNPRINTF(buf, MEDIUM_STRING_LEN_CHAR - 1, szMessage, ap); + va_end(ap); + + // + // Send it to logfile + // + if (NULL != g_pfLogfile) + { + fprintf(g_pfLogfile, buf); + fflush(g_pfLogfile); + } + + // + // Send it to stderr + // + fprintf(stderr, buf); + fflush(stderr); +} + + +#ifdef DEBUG +void +_assert(CHAR *szFile, ULONG uLine) +/** + +Routine description: + + An assertion handler + +Parameters: + + CHAR *szFile, + UINT uLine + +Return value: + + void + +**/ +{ + Bug("Assertion failure in %s, line %u.\n", szFile, uLine); + BREAKPOINT; +} +#endif + + +ULONG +AcquireSpinLock(volatile ULONG *pSpinlock) +/** + +Routine description: + + Atomically acquire a lock or spin trying. + +Parameters: + + ULONG *pSpinlock : a pointer to the lock + +Return value: + + ULONG : the number of times we had to spin + +**/ +{ + ULONG uLoops = 0; + while(0 != LockCompareExchange(pSpinlock, 1, 0)) + { + uLoops++; + } + ASSERT(*pSpinlock == 1); + return(uLoops); +} + +FLAG +TryAcquireSpinLock(volatile ULONG *pSpinlock) +{ + return(0 != LockCompareExchange(pSpinlock, 1, 0)); +} + +void +ReleaseSpinLock(volatile ULONG *pSpinlock) +/** + +Routine description: + + Release a lock so someone else can acquire it. + +Parameters: + + ULONG *pSpinlock : a pointer to the lock + +Return value: + + void + +**/ +{ + ULONG u; + ASSERT(*pSpinlock == 1); + u = LockCompareExchange(pSpinlock, 0, 1); + ASSERT(u == 1); +} + + +FLAG +BackupFile(CHAR *szFile) +/** + +Routine description: + + Backup a file to file.000. If file.000 already exists, back it up + to file.001 etc... + + Note: this function is recursive and can require quite a lot of + stack space. Also it is full of race conditions and should not be + used when some other code may be accessing the filesystem at the + same time. + +Parameters: + + CHAR *szFile : the file to backup + +Return value: + + FLAG + +**/ +{ + ULONG u; + CHAR buf[SMALL_STRING_LEN_CHAR]; + CHAR *p; + + if (TRUE == SystemDoesFileExist(szFile)) + { + if ((strlen(szFile) > 3) && + (isdigit(szFile[0])) && + (isdigit(szFile[1])) && + (isdigit(szFile[2]))) + { + u = (szFile[0] - '0') * 100; + u += (szFile[1] - '0') * 10; + u += szFile[2] - '0' + 1; + p = &(szFile[3]); + } + else + { + u = 0; + p = szFile; + } + snprintf(buf, ARRAY_LENGTH(buf), "%03u%s", u, p); + if (TRUE == SystemDoesFileExist(buf)) + { + BackupFile(buf); + } + return(SystemCopyFile(szFile, buf) && SystemDeleteFile(szFile)); + } + return(FALSE); +} + + +BYTE +Checksum(BYTE *p, ULONG uSize) +/** + +Routine description: + + Compute the checksum of a buffer. + +Parameters: + + BYTE *p, + ULONG uSize + +Return value: + + BYTE + +**/ +{ + BYTE b = 0; + while(uSize) + { + b ^= *p; + p++; + uSize--; + } + return(b); +} + +void +FinishPVTailFromHash(SEARCHER_THREAD_CONTEXT *ctx, + CHAR *buf, + ULONG uLenRemain) +{ + MOVE PV[MAX_PLY_PER_SEARCH]; + MOVE mv; + ULONG uPly = ctx->uPly; + ULONG uLen; +#ifdef DEBUG + POSITION board; + memcpy(&board, &(ctx->sPosition), sizeof(POSITION)); +#endif + + if (NULL == g_pHashTable) return; + do + { + mv = GetPonderMove(&ctx->sPosition); + if (mv.uMove == 0) break; + PV[ctx->uPly] = mv; + + uLen = (ULONG)strlen(MoveToSan(mv, &ctx->sPosition)) + 1; + ASSERT(uLen <= 11); + if (uLenRemain <= uLen) break; + strcat(buf, MoveToSan(PV[ctx->uPly], &ctx->sPosition)); + strcat(buf, " "); + uLenRemain -= uLen; + + if (FALSE == MakeMove(ctx, mv)) break; + } + while(1); + + // + // Unmake the moves + // + while(ctx->uPly > uPly) + { + UnmakeMove(ctx, PV[ctx->uPly - 1]); + } + ASSERT(ctx->uPly == uPly); + ASSERT(PositionsAreEquivalent(&board, &ctx->sPosition)); +} + + + +char * +WalkPV(SEARCHER_THREAD_CONTEXT *ctx) +/** + +Routine description: + + Return a string buffer representing the principle variation. + +Parameters: + + SEARCHER_THREAD_CONTEXT *ctx + +Return value: + + CHAR * + +**/ +{ + static CHAR buf[SMALL_STRING_LEN_CHAR]; + ULONG u = ctx->uPly; + ULONG v = u; + MOVE mv; +#ifdef DEBUG + POSITION board; + memcpy(&board, &(ctx->sPosition), sizeof(POSITION)); +#endif + + buf[0] = '\0'; + while((mv.uMove = ctx->sPlyInfo[v].PV[u].uMove) != 0) + { + if (mv.uMove == HASHMOVE.uMove) + { + strncat(buf, " ", ARRAY_LENGTH(buf) - strlen(buf) - 1); + FinishPVTailFromHash(ctx, buf, ARRAY_LENGTH(buf) - strlen(buf)); + ASSERT(ctx->sPlyInfo[v].PV[u+1].uMove == 0); + break; + } + else if (mv.uMove == RECOGNMOVE.uMove) + { + strncat(buf, "", ARRAY_LENGTH(buf) - strlen(buf) - 1); + ASSERT(ctx->sPlyInfo[v].PV[u+1].uMove == 0); + break; + } + else if (mv.uMove == DRAWMOVE.uMove) + { + strncat(buf, "<=>", ARRAY_LENGTH(buf) - strlen(buf) - 1); + ASSERT(ctx->sPlyInfo[v].PV[u+1].uMove == 0); + break; + } + else + { + strncat(buf, MoveToSan(mv, &ctx->sPosition), + ARRAY_LENGTH(buf) - strlen(buf)); + strncat(buf, " ", ARRAY_LENGTH(buf) - strlen(buf) - 1); + } + + if (FALSE == MakeMove(ctx, mv)) break; + u++; + + if (strlen(buf) > (SMALL_STRING_LEN_CHAR - 10)) break; + } + + // + // Unmake the moves + // + u--; + while(ctx->uPly > v) + { + UnmakeMove(ctx, ctx->sPlyInfo[v].PV[u]); + u--; + } + ASSERT(ctx->uPly == v); + ASSERT(PositionsAreEquivalent(&board, &ctx->sPosition)); + return(buf); +} + + +void +UtilPanic(ULONG uPanicCode, + POSITION *pos, + void *arg1, void *arg2, void *arg3, + char *szFile, ULONG uLine) +{ + Bug("Typhoon PANIC 0x%x (%p, %p, %p, %p)\n" + "Location: %s at line %u\n\n", + uPanicCode, pos, arg1, arg2, arg3, szFile, uLine); + Banner(); + Bug("----------------------------------------------------------------\n"); + switch(uPanicCode) { + case CANNOT_INITIALIZE_SPLIT: + case GOT_ILLEGAL_MOVE_WHILE_PONDERING: + case CANNOT_OFFICIALLY_MAKE_MOVE: + DumpPosition(pos); + DumpMove((ULONG)arg1); + break; + case INITIALIZATION_FAILURE: + Bug("%s\n", (char *)arg1); + break; + default: + break; + } + fflush(stdout); + fflush(g_pfLogfile); + BREAKPOINT; + exit(-1); +} + +void +UtilPrintPV(SEARCHER_THREAD_CONTEXT *ctx, + SCORE iAlpha, + SCORE iBeta, + SCORE iScore, + MOVE mv) +/** + +Routine description: + + Print a PV out to stdout and the log. + +Parameters: + + SEARCHER_THREAD_CONTEXT *ctx, + SCORE iAlpha, + SCORE iBeta, + SCORE iScore, + MOVE mv + +Return value: + + void + +**/ +{ + double dNow = SystemTimeStamp(); + + ASSERT(ctx->uThreadNumber == 0); + ASSERT(IS_VALID_SCORE(iAlpha)); + ASSERT(IS_VALID_SCORE(iBeta)); + ASSERT(iAlpha < iBeta); + ASSERT(IS_VALID_SCORE(iScore)); + + dNow -= g_MoveTimer.dStartTime; + + // + // Set the last PV in the context if we have a root PV or a root + // fail high. + // + if ((iAlpha < iScore) && (iScore < iBeta)) + { + strncpy(ctx->szLastPV, WalkPV(ctx), SMALL_STRING_LEN_CHAR); + } + else if (iScore > iBeta) + { + strncpy(ctx->szLastPV, MoveToSan(mv, &(ctx->sPosition)), + SMALL_STRING_LEN_CHAR); + } + + if ((TRUE == g_Options.fThinking) && (TRUE == g_Options.fShouldPost)) + { + // + // Maybe output the PV. Note: xboard gets confused by PV + // lines that don't match its requirements exactly; if we are + // running under xboard, match its expected format. Otherwise + // be more helpful / verbose. + // + if ((dNow > 0.35) || ((dNow > 0.15) && (g_Options.fVerbosePosting))) + { + if (TRUE == g_Options.fRunningUnderXboard) + { + if ((iAlpha < iScore) && (iScore < iBeta)) + { + Trace("%2u %6d %5u %-12" + COMPILER_LONGLONG_UNSIGNED_FORMAT" %s\n", + g_uIterateDepth, + iScore, + (ULONG)(dNow * 100), + ctx->sCounters.tree.u64TotalNodeCount, + ctx->szLastPV); + } + else if (iScore <= iAlpha) + { + Trace("%2u %6d %5u %-12" + COMPILER_LONGLONG_UNSIGNED_FORMAT" FL --\n", + g_uIterateDepth, + iScore, + (ULONG)(dNow * 100), + ctx->sCounters.tree.u64TotalNodeCount); + } + else + { + ASSERT(iScore >= iBeta); + Trace("%2u %6d %5u %-12" + COMPILER_LONGLONG_UNSIGNED_FORMAT" %s ++\n", + g_uIterateDepth, + iScore, + (ULONG)(dNow * 100), + ctx->sCounters.tree.u64TotalNodeCount, + ctx->szLastPV); + } + } + else // !running under xboard + { + if ((iAlpha < iScore) && (iScore < iBeta)) + { + Trace("%2u %5s %-9s %-11" + COMPILER_LONGLONG_UNSIGNED_FORMAT" %s\n", + g_uIterateDepth, + ScoreToString(iScore), + TimeToString(dNow), + ctx->sCounters.tree.u64TotalNodeCount, + ctx->szLastPV); + } + else if (iScore <= iAlpha) + { + Trace("%2u- %5s %-9s %-11" + COMPILER_LONGLONG_UNSIGNED_FORMAT" FL --\n", + g_uIterateDepth, + ScoreToString(iScore), + TimeToString(dNow), + ctx->sCounters.tree.u64TotalNodeCount); + } + else + { + ASSERT(iScore >= iBeta); + Trace("%2u+ %5s %-9s %-11" + COMPILER_LONGLONG_UNSIGNED_FORMAT" %s ++\n", + g_uIterateDepth, + ScoreToString(iScore), + TimeToString(dNow), + ctx->sCounters.tree.u64TotalNodeCount, + ctx->szLastPV); + } + } + } + } +} diff --git a/src/save/vars.c b/src/save/vars.c new file mode 100644 index 0000000..1db4c72 --- /dev/null +++ b/src/save/vars.c @@ -0,0 +1,483 @@ +/** + +Copyright (c) Scott Gasch + +Module Name: + + vars.c + +Abstract: + + This is code that implements variables in the engine. Some of + these variables are read only but others can be set by the engine + user to affect certain aspects of engine operation. + +Author: + + Scott Gasch (scott.gasch@gmail.com) 18 May 2004 + +Revision History: + + $Id: vars.c 345 2007-12-02 22:56:42Z scott $ + +**/ + +#include "chess.h" + +extern ULONG g_uNumInputEvents; + +typedef void(PUSERVARFUN)(void); + +// +// Here's how we expose engine variables to the user. +// +typedef struct _USER_VARIABLE +{ + char *szName; // variable name + char *szType; // variable type: + // * - char *, strdup/free + // H - hash size + // B - boolean + // U - unsigned + // I - int + // M - move + // D - double + // S - string buffer + void *pValue; // pointer to value + PUSERVARFUN *pFunction; // funct to call when it changes +} USER_VARIABLE; + +extern ULONG g_uBookProbeFailures; + +USER_VARIABLE g_UserVarList[] = +{ + { "AnnounceOpening", + "B", + (void *)&(g_Options.fShouldAnnounceOpening), + NULL }, + { "BatchMode", + "b", + (void *)&(g_Options.fNoInputThread), + NULL }, + { "BlackPlayer", + "*", + (void *)&(g_GameData.sHeader.sPlayer[BLACK].szName), + NULL }, + { "BlackRating", + "U", + (void *)&(g_GameData.sHeader.sPlayer[BLACK].uRating), + NULL }, + { "BlackDescription", + "*", + (void *)&(g_GameData.sHeader.sPlayer[BLACK].szDescription), + NULL }, + { "BlackIsComputer", + "B", + (void *)&(g_GameData.sHeader.sPlayer[BLACK].fIsComputer), + NULL }, + { "BookFileName", + "S", + (void *)&(g_Options.szBookName), + NULL }, + { "BookProbeFailures", + "u", + (void *)&(g_uBookProbeFailures), + NULL }, + { "ComputerTimeRemainingSec", + "U", + (void *)&(g_Options.uOpponentsClock), + NULL }, //&DoVerifyClocks }, + { "EGTBPath", + "S", + (void *)&(g_Options.szEGTBPath), + InitializeEGTB }, + { "FastScript", + "B", + (void *)&(g_Options.fFastScript), + NULL }, + { "GameDescription", + "*", + (void *)&(g_GameData.sHeader.szGameDescription), + NULL }, + { "GameLocation", + "*", + (void *)&(g_GameData.sHeader.szLocation), + NULL }, + { "GameIsRated", + "B", + (void *)&(g_GameData.sHeader.fGameIsRated), + NULL }, + { "GameResultComment", + "*", + (void *)&(g_GameData.sHeader.szResultComment), + NULL }, + { "LastEval", + "I", + (void *)&(g_Options.iLastEvalScore), + NULL }, + { "LogfileName", + "S", + (void *)&(g_Options.szLogfile), + NULL }, + { "MoveToPonder", + "m", + (void *)&(g_Options.mvPonder), + NULL }, + { "MovesPerTimePeriod", + "U", + (void *)&(g_Options.uMovesPerTimePeriod), + NULL }, + { "OpponentTimeRemainingSec", + "U", + (void *)&(g_Options.uMyClock), + NULL }, //&DoVerifyClocks }, + { "PendingInputEvents", + "u", + (void *)&(g_uNumInputEvents), + NULL }, + { "PonderingNow", + "b", + (void *)&(g_Options.fPondering), + NULL }, + { "PostLines", + "B", + (void *)&(g_Options.fShouldPost), + NULL }, + { "Resign", + "B", + (void *)&(g_Options.fShouldResign), + NULL }, + { "SearchDepthLimit", + "U", + (void *)&(g_Options.uMaxDepth), + NULL }, //&DoVerifySearchDepth }, + { "SearchTimeLimit", + "U", + (void *)&(g_Options.uMyIncrement), + NULL }, //&DoVerifySearchTime }, + { "SearchStartedTime", + "d", + (void *)&(g_MoveTimer.dStartTime), + NULL }, + { "SearchSoftTimeLimit", + "d", + (void *)&(g_MoveTimer.dSoftTimeLimit), + NULL }, + { "SearchHardTimeLimit", + "d", + (void *)&(g_MoveTimer.dHardTimeLimit), + NULL }, + { "SearchNodesBetweenTimeCheck", + "u", + (void *)&(g_MoveTimer.uNodeCheckMask), + NULL }, + { "StatusLine", + "B", + (void *)&(g_Options.fStatusLine), + NULL }, + { "ThinkOnOpponentsTime", + "B", + (void *)&(g_Options.fShouldPonder), + NULL }, + { "ThinkingNow", + "b", + (void *)&(g_Options.fThinking), + NULL }, + { "TournamentMode", + "B", + (void *)&(g_fTournamentMode), + NULL }, + { "VerbosePosting", + "B", + (void *)&(g_Options.fVerbosePosting), + NULL }, + { "WhitePlayer", + "*", + (void *)&(g_GameData.sHeader.sPlayer[WHITE].szName), + NULL }, + { "WhiteRating", + "U", + (void *)&(g_GameData.sHeader.sPlayer[WHITE].uRating), + NULL }, + { "WhiteDescription", + "*", + (void *)&(g_GameData.sHeader.sPlayer[WHITE].szDescription), + NULL }, + { "WhiteIsComputer", + "B", + (void *)&(g_GameData.sHeader.sPlayer[WHITE].fIsComputer), + NULL }, + { "Xboard", + "B", + (void *)&(g_Options.fRunningUnderXboard), + NULL } +}; + + + + +COMMAND(SetCommand) +/** + +Routine description: + + This function implements the 'set' engine command. + + Usage: + + set [variable name [value]] + + With no arguments, set dumps a list of all variables and their + current values. + + With one argument, set displays the specified variable's value. + + With two arguments, set changes the value of the specified + variable to the given value. + +Parameters: + + The COMMAND macro hides four arguments from the input parser: + + CHAR *szInput : the full line of input + ULONG argc : number of argument chunks + CHAR *argv[] : array of ptrs to each argument chunk + POSITION *pos : a POSITION pointer to operate on + +Return value: + + void + +**/ +{ + ULONG x, y; + FLAG fFound; + + if (argc == 1) + { + for (x = 0; + x < ARRAY_SIZE(g_UserVarList); + x++) + { + switch(toupper(g_UserVarList[x].szType[0])) + { + case 'H': + case 'U': + Trace("%-28s => %u\n", + g_UserVarList[x].szName, + *((unsigned int *)g_UserVarList[x].pValue)); + break; + case 'I': + Trace("%-28s => %d\n", + g_UserVarList[x].szName, + *((int *)g_UserVarList[x].pValue)); + break; + case 'S': + Trace("%-28s => \"%s\"\n", + g_UserVarList[x].szName, + (char *)g_UserVarList[x].pValue); + break; + case 'M': + Trace("%-28s => \"%s\"\n", + g_UserVarList[x].szName, + MoveToIcs(*((MOVE *)g_UserVarList[x].pValue))); + break; + case 'B': + Trace("%-28s => \"%s\"\n", + g_UserVarList[x].szName, + (*((FLAG *)g_UserVarList[x].pValue) == TRUE) ? + "TRUE" : "FALSE"); + break; + case 'D': + Trace("%-28s => %f\n", + g_UserVarList[x].szName, + *((double *)g_UserVarList[x].pValue)); + break; + case '*': + Trace("%-28s => \"%s\"\n", + g_UserVarList[x].szName, + *((char **)g_UserVarList[x].pValue)); + break; + default: + Trace("%-28s => ???\n", + g_UserVarList[x].szName); + // ASSERT(FALSE); + break; + } + } + return; + } + else if (argc == 2) + { + fFound = FALSE; + for (x = 0; + x < ARRAY_SIZE(g_UserVarList); + x++) + { + if (!STRNCMPI(argv[1], + g_UserVarList[x].szName, + strlen(argv[1]))) + { + fFound = TRUE; + switch(toupper(g_UserVarList[x].szType[0])) + { + case 'H': + case 'U': + Trace("%-28s => %u\n", + g_UserVarList[x].szName, + *((unsigned int *)g_UserVarList[x].pValue)); + break; + case 'I': + Trace("%-28s => %d\n", + g_UserVarList[x].szName, + *((int *)g_UserVarList[x].pValue)); + break; + case 'S': + Trace("%-28s => \"%s\"\n", + g_UserVarList[x].szName, + (char *)g_UserVarList[x].pValue); + break; + case 'M': + Trace("%-28s => \"%s\"\n", + g_UserVarList[x].szName, + MoveToIcs(*((MOVE *)g_UserVarList[x].pValue))); + break; + case 'B': + Trace("%-28s => \"%s\"\n", + g_UserVarList[x].szName, + (*((FLAG *)g_UserVarList[x].pValue) == TRUE) ? + "TRUE" : "FALSE"); + break; + case 'D': + Trace("%-28s => %f\n", + g_UserVarList[x].szName, + *((double *)g_UserVarList[x].pValue)); + break; + case '*': + Trace("%-28s => \"%s\"\n", + g_UserVarList[x].szName, + *((char **)g_UserVarList[x].pValue)); + break; + default: + Trace("%-28s => ???\n", + g_UserVarList[x].szName); + // ASSERT(FALSE); + break; + } + } + } + + if (FALSE == fFound) + { + Trace("Error (invalid variable): %s\n", argv[1]); + } + return; + } + else + { + ASSERT(argc > 2); + + fFound = FALSE; + + for (x = 0; + x < ARRAY_SIZE(g_UserVarList); + x++) + { + if (!STRNCMPI(argv[1], + g_UserVarList[x].szName, + strlen(argv[1]))) + { + fFound = TRUE; + + if (islower(g_UserVarList[x].szType[0])) + { + Trace("Error (variable is read only): %s\n", + argv[1]); + } + + switch(g_UserVarList[x].szType[0]) + { + case 'H': + y = atoi(argv[2]); + if ((y < 0) || (y & (y - 1))) + { + Trace("Error (invalid size): %d\n", y); + } + else + { + *((int *)g_UserVarList[x].pValue) = y; + } + break; + + case '*': + if (NULL != *((char **)g_UserVarList[x].pValue)) + { + SystemFreeMemory( *((char **)g_UserVarList[x].pValue ) ); + } + *((char **)g_UserVarList[x].pValue) = STRDUP(argv[2]); + if (NULL == *((char **)g_UserVarList[x].pValue)) + { + Trace("Error (out of memory)\n"); + return; + } + break; + + case 'S': + strncpy(((char *)g_UserVarList[x].pValue), + argv[2], + 127); + break; + + case 'U': + y = atoi(argv[2]); + if (y < 0) + { + Trace("Error (invalid value): %d\n", y); + return; + } + *((unsigned *)g_UserVarList[x].pValue) = y; + break; + + case 'I': + y = atoi(argv[2]); + *((int *)g_UserVarList[x].pValue) = y; + break; + + case 'B': + if (toupper(*(argv[2])) == 'T') + { + *((FLAG *)g_UserVarList[x].pValue) = TRUE; + } + else if (toupper(*(argv[2])) == 'F') + { + *((FLAG *)g_UserVarList[x].pValue) = FALSE; + } + else + { + Trace("Error (invalid value, must be TRUE or " + "FALSE): %s\n", argv[2]); + } + break; + + default: + ASSERT(FALSE); + break; + } + + if (NULL != g_UserVarList[x].pFunction) + { + (void)(g_UserVarList[x].pFunction)(); + } + } + } + + if (FALSE == fFound) + { + Trace("Error (invalid variable): %s\n", argv[1]); + } + } +} + +// +// TODO: unset? +// -- cgit v1.3