--- /dev/null
+#define VERSION "0.0"
+
+/********************************************************************************************/
+/* Simple XBoard-compatible engine for playing Chess variants with drops, by H.G. Muller. */
+/* Handles boards up to 11 x 11, with up to 16 droppable piece types, and 15 promoted types.*/
+/********************************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ON 1
+#define OFF 0
+#define INVALID 0
+
+#define INF 8000
+#define MAXPLY 100
+#define MAXMOVES 500
+
+#define NONE 0
+#define ANALYZE 1
+#define WHITE 32
+#define BLACK 64
+#define COLOR (WHITE | BLACK)
+
+#define CK_UNKNOWN 255
+#define CK_NONE 254
+#define CK_DOUBLE 253
+
+#define C_DISTANT 0xFF00
+#define C_CONTACT 0x00FF
+
+int ply, nodeCount, forceMove, choice, rootMove, lastGameMove, rootScore, abortFlag, postThinking=1; // some frequently used data
+int maxDepth=MAXPLY, timeControl=3000, mps=40, inc, timePerMove, timeLeft=1000; // TC parameters
+
+#define H_LOWER 1
+#define H_UPPER 2
+
+int ReadClock (int start);
+char *MoveToText (int move);
+int TimeIsUp (int mode);
+
+
+#define captCode (rawInts + 22*10 + 10)
+#define deltaVec (rawChar + 22*10 + 10)
+#define promoInc (rawChar + 21*22)
+#define board (rawByte + 2*22 + 2)
+#define pawnCount (board + 9*22 + 11)
+#define dropType (rawByte + 15*22)
+#define toDecode (rawByte + 26*22)
+#define spoiler (rawByte + 37*22)
+#define zoneTab (rawByte + 48*22)
+#define sqr2file (rawByte + 59*22)
+#define dist (rawByte + 70*22 + 22*10 + 10)
+#define location (rawLocation + 23)
+#define promoGain (rawGain + 1)
+
+int pvStack[MAXPLY*MAXPLY/2];
+int nrRanks, nrFiles, specials, pinCodes, maxDrop, moveSP, pawn, *pvPtr = pvStack, boardEnd, searchNr;
+int rawInts[21*22], pieceValues[96], pieceCode[96];
+signed char rawChar[32*22], steps[512];
+unsigned char rawByte[91*22], firstDir[64], rawBulk[98], handSlot[97], promoCode[96], aVal[64], vVal[64], rawLocation[96+23];
+long long int handKey[96], pawnKey;
+int handVal[96], rawGain[97];
+unsigned int moveStack[500*MAXPLY];
+int killers[MAXPLY][2];
+int path[MAXPLY], deprec[MAXPLY];
+int repKey[512+20];
+
+/*
+ Drops: piece is the (negated) count, promo the piece to be dropped.
+ The from-square has to be cleared for moves, but incremented for drops.
+ piece = board[from];
+ board[from] = (piece >> 7) & (piece + 1); // 0 if piece >= 0
+ Promotion on moves indicated by to-square
+ promo = piece + promoTab[to];
+ to = toTab[to];
+ On drops, however, it is determined by the from-square
+ promo = dropTab[from];
+ Combine:
+ promo = dropTab[from] + promoTab[to] + (piece & ~mask);
+
+ piece = board[from]; // off-board is negative
+ mask = piece >> 7; // on board 0, off board -1
+ board[from] = mask & (piece + 1); //
+ promo = dropTab[from] + promoTab[to];
+
+
+ dropTab[] = {
+ 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, // off-board from-square contains piece codes
+ 0, 0, 0, 0, 0, 6, 7, 8, 9,10,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+ promoTab[] = {
+ 0, 0, 0, 0, 0, 8, 8, 8, 8, 8,
+ 0, 0, 0, 0, 0, 8, 8, 8, 8, 8,
+ 0, 0, 0, 0, 0, 8, 8, 8, 8, 8,
+ 0, 0, 0, 0, 0, 8, 8, 8, 8, 8,
+ 0, 0, 0, 0, 0, 8, 8, 8, 8, 8,
+ };
+
+ encoding:
+ 0 = empty
+ 1-16 = unpromoted pieces SP SC SO FC FG CM BD VS VW OC LH SW FF RR TR CE
+ 17-31 = promoted pieces GB FF CE RF SW VS VW RB BE PO HH GS TF TR K
+ promotion adds 16
+ handTab[] = { 11 12 13 14 15 16 17 18 19 20 21 33 34 34 36 37
+
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 };
+ P N B R Q
+ N~ B~ R~ Q~ K
+ promotion adds 17-20
+ 32 = white
+ 64 = black
+ 128 = off board
+ negative piece types = hand counts
+ all of those point to same PST, which only has to exist for off-board squares
+ */
+
+// Capture codes
+// the needs of shogi and chess can be met with 8 contact and 4 distant bits,
+// by splitting the Knight and Rook in f, b and s components
+// wa needs additional f/bW2 (LH) and f/bW3 (CE) distant, and AvD (TF) instead of sN
+// Chess fF, bF, fW, bW, sW, fN, bN, sN (6) R, B (2)
+// wP x
+// bP x
+// N x x x
+// B x x x
+// R x x x x
+// Q x x x x x x x
+// K x x x x x
+// Shogi fF, bF, fW, bW, sW, fN, bN (7) fR, bR, sR, B (5)
+// wP x
+// wL x
+// wN x
+// wS x x x
+// wG x x x x
+// B x x x
+// R x x x x x x
+// +B x x x x x x
+// +R x x x x x x x x
+// K x x x x x
+// Tori fFbD, bFfD, fW, bW, sW, F, fAbD, bAfD (8) fRblB, fRbrB, bRflB, bRfrB, fA, bA (6)
+// wP x
+// wPh x
+// wCr x x x
+// wQl x x x
+// wQr x x x
+// wFa x x x
+// +wP x
+// +wFa x x x x x x x
+// K x x x x
+// Wa fF, bF, fW, bW, sW, vN, AvD (7) fR, bR, sR, B, fGfA, bGbA, fD, bD (8)
+/*
+ The slider tracks skip the first square.
+ Test for slider alignment first. If hit, we do ray scan, and never try leaps.
+ This allows slides to mask jumps, so that a Quail can have bFfD + fRlbB.
+ */
+
+// for each of the 16 bits in the capture codes there is a zero-terminated list of board steps to which it corresponds
+// the first 8 are leaps, the next 4 unlimited-range sliders, than a pair of range 2 and a pair of range 3.
+signed char
+toriCodes[] = { 21,23,-44,0, -21,-23,44,0, 22,0,-22,0, 1,-1,0, 21,23,-21,-23,0, 42,46,-44,0, -42,-46,44,0,
+ 22,-23,0, 22,-21,0, -22,23,0, -22,21,0, 21,23,0, -21,-23,0, 0,0 },
+chessCodes[] = { 21,23,0, -21,-23,0, 22,0, -22,0, 1,-1,0, 43,45,0, -43,-45,0, 20,24,-20,-24,0, 22,0, -22,0, 1,-1,0, 21,23,-21,-23,0,0,0,0,0,0,0},
+shogiCodes[] = { 21,23,0, -21,-23,0, 22,0, -22,0, 1,-1,0, 43,45,0, -43,-45,0, 42,44,46,-42,-44,-46,0, 22,0, -22,0, 1,-1,0, 21,23,-21,-23,0,
+ 22,0, -22,0, 21,23,0, -21,-23,0 };
+
+void
+InitCaptureCodes(signed char *codes)
+{
+ int i, piece, dir=0;
+ for(i=-10-10*22; i<=10+10*22; i++) captCode[i] = 0; // clear capture codes and step vectors
+ // build variant-specific alignment map, marking each square with the capture sets to which it belongs
+ for(i=0; i<16; i++) { // for all 16 capture sets
+ int step, b = 1<<i, range = 10; // unlimited range
+ if(i >= 12) range = i/2 - 4; // 2 or 3, for sets 12/13 and 14/15, respectively
+ while((step = codes[dir++])) {
+ if(i < 8) captCode[step] |= b; else { // first 8 capture sets are leaps
+ int d; // other are slides, so scan ray
+ for(d=2; d<=range; d++) captCode[d*step] |= b;
+ for(d=1; d<=10; d++) deltaVec[d*step] = step, dist[d*step] = d; // step for sliding to square
+ }
+ }
+ }
+ // collect codes of squares that each piece hits
+ for(piece=WHITE; piece<BLACK+32; piece++) {
+ int step, code = 0;
+ dir = 2*firstDir[piece-WHITE];
+ if(!dir) continue;
+ while((step = steps[dir++])) {
+ int range = steps[dir++], c = captCode[step] & C_DISTANT;
+ if((range & 3) == 2) continue;
+ range = range + 7 >> 3;
+ if(c) code |= c; // jump excludes slides to same square (which would mask it)
+ for(i=1; i<=range; i++) captCode[i*step] ^= -1; // flip reachable codes, clear bits
+ }
+ for(i=-10-10*22; i<=10+10*22; i++) { // scan over all possible moves
+ int c = captCode[i];
+ if(c < 0) captCode[i] ^= -1; // restore reachable squares to normal
+ else code |= c; // set bits of sets that contain squares we cannot reach
+ }
+ pieceCode[piece] = code ^ 0xFFFF;
+#if 0
+ {
+ code = captCode[step] & C_CONTACT; // first step: unblockable codes
+ for(i=2; i<=range; i++) code |= captCode[i*step] & C_DISTANT; // later steps are blockable
+ }
+#endif
+ }
+}
+
+/*
+ Piece encoding
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ Wa: SP OC LH SC SO CM BD FC FG VS VW SW FF RR CE Tr
+ GB PO HH FF CE VS VW RF SW RB BE GS TF Tr CK
+ Shogi: P N B R L S R
+ +P +N H D +L +S K
+ Tori: S Ph Cr Ql Qr Fa
+ G Ea Px
+ Chess: P N B R A C Q
+ Q~ N~ B~ R~ A~ C~ K
+ */
+
+unsigned char handSlot[97], rawBulk[98];
+
+#define pawnBulk (rawBulk + 1)
+#define maxBulk (rawBulk + 1 + 30) /* piece type 30 is not even used in Wa */
+
+// Move tables: (step,range) pair for each direction a piece moves in, terminated by (0,0) sentinel
+// divergent moves (for FIDE Pawn) are flagged in high nibble of range byte
+// *** Note that this is re-packed at run time to have flags in low three bits! ***
+#define MOVE_ONLY 32
+#define CAPT_ONLY (16+32)
+
+signed char steps[] = {
+ 21,10, 23,10, -21,10, -23,10, 20,1, 24,1, -20,1, -24,1, 43,1, 45,1, -43,1, -45,1, 0,0, // A=0, N=4, HH=8, bN=10
+ 1,10, -1,10, 22,10, -22,10, 21,10, 23,10, -21,10, -23,10, 0,0, // Q=13
+ 21,1, -21,1, 23,1, -23,1, 1,10, -1,10, 22,10, -22,10, 0,0, // +R=22, R=26
+ 1,1, -1,1, 22,1, -22,1, 21,10, 23,10, -21,10, -23,10, 0,0, // +B=31, bFF=34, B=35
+ 1,1, -1,1, 22,1, -22,1, 21,1, 23,1, -21,1, -23,1, 0,0, // K=40, FL=42, bS=43
+ 22,2+MOVE_ONLY, 21,1+CAPT_ONLY, 23,1+CAPT_ONLY, 0,0, // wP=49
+ -22,2+MOVE_ONLY, -21,1+CAPT_ONLY, -23,1+CAPT_ONLY, 0,0, // bP=53
+ 1,1, -1,1, 22,1, 21,1, 23,1, -22,1, 0,0, // wG=57, wC=59, bSO=60, bP=62
+ 1,1, -1,1, -22,1, -21,1, -23,1, 22,1, 0,0, // bG=64, bC=66, wSO=67, wP=69
+ -21,1, -23,1, -22,1, 1,1, -1,1, 21,1, 23,1, 0,0, // bDE=71, wBD=73, wFC=74
+ 21,1, 23,1, 22,1, 1,1, -1,1, -21,1, -23,1, 0,0, // wDE=79, bBD=81, bFC=82
+ 22,1, 21,1, 23,1, -21,1, -23,1, 0,0, // wS=87
+ 22,2, -22,10, 0,0, // bLH=93, bL=94
+ -22,2, 22,10, 0,0, // wLH=96, wL=97
+ 22,1, -22,1, 21,1, 23,1, -21,1, -23,1, -44,1, 46,1, 42,1, -46,1, -42,1, 44,1, 0,0, // TF=99, +wSw=108
+ 46,1, 42,1, -44,1, 0,0, // +bSw=112
+ 22,1, -22,1, 1,10, -1,10, 0,0, // SW=116
+ 22,10, -22,1, 21,1, 23,1, -21,1, -23,1, 0,0, // wRR=121
+ -22,10, 22,1, -21,1, -23,1, 21,1, 23,1, 0,0, // bRR=128
+ 22,1, 21,10, 23,10, -21,10, -23,10, 0,0, // wFF=135
+ 1,1, -1,1, 22,10, -22,10, 21,10, 23,10, -21,10, -23,10, 0,0, // TF=141
+ 1,1, -1,1, 22,10, -22,10, 21,3, 23,3, -21,1, -23,1, 0,0, // wCE=150
+ 1,1, -1,1, 22,10, -22,10, -21,3, -23,3, 21,1, 23,1, 0,0, // bCE=159
+ 1,1, -1,1, 22,1, -22,10, 21,10, 23,10, -21,2, -23,2, 0,0, // +wFa=168
+ 1,1, -1,1, 22,10, -22,1, -21,10, -23,10, 21,2, 23,2, 0,0, // +bFa=177
+ 43,1, 45,1, 0,0, // wN=186
+ 44,1, -21,1, -23,1 ,0,0, // wPh=189
+ -44,1, 21,1, 23,1, 0,0, // bPh=193
+ 22,10, -21,10, -23,1, 0,0, // wQl=197
+ 22,10, -21,1, -23,10, 0,0, // wQr=201
+ -22,10, 21,10, 23,1, 0,0, // bQl=205
+ -22,10, 21,1, 23,10, 0,0, // bQr=209
+ 22,10, 21,1, 23,1, 1,1, -1,1, -22,10, 0,0, // wRF=213
+ -22,10, -21,1, -23,1, 1,1, -1,1, 22,10, 0,0, // bRF=220
+};
+
+// first direction in 'steps' table for the various piece types
+// organized in four sections, each terminated by 255: unprom white, prom white, unprom black, prom black
+// first of unprom section is always king (which in reality is type 31, while unprom = 0-15 and prom = 16-31)
+
+int normalValue[] = {0, 10, 50, 130, 150, 60, 80, 90, 0, 110, 96, 155, 180, 94, 92, 90, // basic and promoted
+ 0, 20, 60, 150, 170, 75, 88, 90, 0, 20, 90, 165, 190, 95,100,110 }; // pre-promoted and in hand
+int euroValue[] = {0, 10, 50, 130, 150, 80, 80, 90, 0, 110, 96, 155, 180, 94, 92, 90, // basic and promoted
+ 0, 20, 60, 150, 170, 80, 88, 90, 0, 20, 90, 165, 190, 95,100,110 }; // pre-promoted and in hand
+int miniValue[] = {0, 20, 50, 81, 110, 60, 65, 79, 0, 99, 96, 125, 140, 94, 80, 79, // basic and promoted
+ 0, 20, 60, 120, 135, 70, 79, 79, 0, 40, 90, 105, 130, 95, 81, 84 }; // pre-promoted and in hand
+int judkinValue[] = {0, 20, 50, 96, 115, 40, 65, 79, 0, 99, 96, 140, 155, 81, 80, 79, // basic and promoted
+ 0, 20, 60, 135, 150, 70, 79, 79, 0, 25, 90, 120, 135, 70, 81, 84 }; // pre-promoted and in hand
+int toriValue[] = {0, 20, 0, 70, 70, 40, 90, 79, 0, 50, 0, 0, 0, 0,150, 79, // basic and promoted
+ 0, 20, 0, 70, 70, 40,140, 79, 0, 25, 0, 100, 100, 60,130,100 }; // pre-promoted and in hand
+int
+chessValues[] = { 100, 315, 300, 375, 600, -1, 700, 340, 325, 450, -1, 150, 385, 350, 400, 600, -1 },
+shogiValues[] = { 10, 80, 90, 130, 150, 50, 60, -1, 110, 92, 90, 155, 180, 96, 94, -1, 20, 100, 110, 165, 190, 90, 95, -1 },
+miniValues[] = { 20, 65, 79, 81, 110, -1, 99, 80, 79, 125, 140, -1, 40, 81, 84, 105, 130, -1 },
+judkinValues[] = { -1, -1, -1 },
+toriValues[] = { -1, -1, -1 },
+waValues[] = { -1, -1, -1 };
+
+unsigned char
+chessDirs[] = { 40, 49, 4, 35, 26, 13, 255, 13, 4, 35, 26, 255, 40, 53, 4, 35, 26, 13, 255, 13, 4, 35, 26, 255 }, // K,P,N,B,R,Q / Q~,N~,B~,R~
+shogiDirs[] = { 40, 69, 87, 57, 35, 26, 186, 97, 255, 57, 57, 57, 31, 22, 57, 57, 255, // K,P,S,G,B,R,N,L / +P,+S,-,DH,DK,+N,+L
+ 40, 62, 43, 64, 35, 26, 10, 94, 255, 64, 64, 64, 31, 22, 64, 64, 255 },
+toriDirs[] = { 40, 69, 189, 197, 201, 42, 79, 255, 108, 0, 0, 0, 0, 168, 255, // Ph, S, Pt, Ql, Qr, Cr, Fa / G - - - - Ea
+ 40, 62, 193, 205, 209, 42, 71, 255, 112, 0, 0, 0, 0, 177, 255 },
+waDirs[] = { 40, 69, 67, 67, 74, 59, 59, 73, 87, 57, 97, 96, 116, 121, 135, 99, 150, 255, // CK,SP,SC,SO,FC,CM,FG,BD,VS,VW,OC,LH,SW,RR,FF,Tr,CE
+ 57,135,150,213, 87,116, 57, 79, 40, 40, 8, 26, 99, 141, 255, // GB,FF,CE,RF,VS,SW,VW,RB,BE,PO,HH,GS,Tr,TF
+ 40, 62, 60, 60, 82, 66, 66, 81, 43, 64, 94, 93, 116, 128, 34, 99, 159, 255,
+ 64, 34,159,220, 43,116, 64, 71, 40, 40, 8, 26, 99, 141, 255 },
+
+chessIDs[] = "PNBRQ",
+shogiIDs[] = "PSGBRNL",
+toriIDs[] = "SPLRCF",
+waIDs[] = "PCOQMGDSVLHWRFXE",
+
+chessFEN[] = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -",
+shogiFEN[] = "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w",
+toriFEN[] = "rpckcpl/3f3/sssssss/2s1S2/SSSSSSS/3F3/LPCKCPR w",
+waFEN[] = "hmoqskvgcdl/1e3w3f1/ppprpppxppp/3p3p3/11/11/11/3P3P3/PPPXPPPRPPP/1F3W3E1/LDCGVKSQOMH w",
+miniFEN[] = "rbsgk/4p/5/P4/KGSBR w",
+judkinFEN[]= "rnbsgk/5p/6/6/P5/KGSBNR w",
+euroFEN[] = "r w",
+
+// promotion codes for unpromoted pieces. Will be ANDed with Z_WHITE or Z_BLACK to fill promoCode[] table
+
+#define Z_FIDE 1 /* piece has promotion choice */
+#define Z_2ND 0x66 /* must promote on last 2 ranks */
+#define Z_MUST 0x78 /* must promote in entire zone */
+#define Z_WHITE 0x2B /* bits applying to white zone */
+#define Z_BLACK 0x55 /* bits applying to black zone */
+#define Z_LAST 0x80 /* ???? */
+#define Z_DOUBLE 0x80 /* where Pawns have double step */
+
+chessProms[16] = { Z_FIDE|Z_MUST },
+shogiProms[16] = { Z_MUST, COLOR, 0, Z_MUST, Z_MUST, Z_2ND, Z_2ND },
+toriProms[16] = { Z_MUST, 0, 0, 0, 0, Z_MUST },
+waProms[16] = { Z_MUST, Z_MUST, Z_MUST, Z_MUST, COLOR, COLOR, Z_MUST, Z_MUST, Z_MUST, COLOR, COLOR, Z_MUST, Z_MUST, Z_MUST };
+
+typedef struct {
+ int files, ranks, hand, zone;
+ char *name;
+ unsigned char *pieces, *fen, *dirs, *proms;
+ signed char *codes;
+ int *values;
+} VariantDesc;
+
+VariantDesc variants[] = {
+ { 8, 8, 5, 1, "crazyhouse\n", chessIDs, chessFEN, chessDirs, chessProms, chessCodes, chessValues },
+ { 5, 5, 5, 1, "minishogi\n", shogiIDs, miniFEN, shogiDirs, shogiProms, shogiCodes, miniValues },
+ { 6, 6, 6, 2, "judkinshogi\n", shogiIDs, judkinFEN, shogiDirs, shogiProms, shogiCodes, judkinValues },
+ { 7, 7, 6, 2, "torishogi\n", toriIDs, toriFEN, toriDirs, toriProms, toriCodes, toriValues },
+ { 9, 9, 7, 3, "shogi\n", shogiIDs, shogiFEN, shogiDirs, shogiProms, shogiCodes, shogiValues },
+ { 11, 11, 16, 3, "crazywa\n", waIDs, waFEN, waDirs, waProms, shogiCodes, waValues },
+};
+
+// info per piece type. sometimes indexed by negated holdings count instead of piece
+#define pieceKey (rawKey+1)
+#define PST (rawPST+1)
+signed char *rawPST[COLOR+1]; // PST[-1...95] indexed by 'mutation', which is -1 for drops
+unsigned int rawKey[COLOR+1];
+unsigned int squareKey[22*11];
+
+// piece-square tables. White and black tables interleave. The first two pairs are (0, center) and (hand1, ???)
+#define center (pstData + 22*11)
+#define hand1 (pstData + 22*11) /* beware: uses off-board part only */
+#define sparePST (pstData + 22*11) /* on-board part still available for something else */
+#define pawnPST (pstData + 22*11*2) /* from here interleaved (white, black) */
+#define kingPST (pstData + 22*11*3)
+
+signed char pstData[22*11*8]; // actual tables (for now 8 pairs)
+
+int
+MyRandom ()
+{
+ return (rand() >> 10) + rand() + (rand() << 10) + (rand() << 20);
+}
+
+void
+EngineInit ()
+{
+ int i, r, f;
+ for(i=1; i<sizeof(steps); i+=2) { // pack range encoding differently
+ steps[i] = steps[i] >> 4 | steps[i] << 3 & 127;
+ if(steps[i] & 7) steps[i] -= 8;
+ }
+ // set tables for on-the-fly Zobrist key creation as pieceKey[piece]*squareKey[sqr]
+ for(r=0; r<11; r++) for(f=0; f<11; f++) {
+ int sqr = 22*r + f;
+ squareKey[sqr] = MyRandom(); squareKey[sqr+11] = MyRandom() << 16; // low 16 bits 0 for holdings squares
+ }
+ for(r=WHITE; r<COLOR; r++) pieceKey[r] = MyRandom();
+ // for drops the from-key will be pieceKey[-1]*squareKey[typeLocation]
+ pieceKey[-1] = MyRandom() << 16; // clear lowest 16 bits to make sure lowest 32 of product are zero
+ PST[0] = pstData; PST[-1] = hand1; // PST for empty squares
+printf("init done\n");
+}
+
+void
+ClearBoard ()
+{
+ int i, r, f;
+ for(i=-2*22-2; i<13*22-2; i++) board[i] = -1; // boundary guards (or holdings counters)
+ for(r=0; r<nrRanks; r++) for(f=0; f<nrFiles; f++) board[22*r+f] = 0; // playing area
+ for(f=0; f<11; f++) pawnCount[f] = 0xF0; // Pawn occupancy per file
+ for(i=0; i<512+20; i++) repKey[i] = 0; // game-history hash
+}
+
+char *pieces, *startPos;
+
+void
+GameInit (char *name)
+{
+ int v, *ip, i, color, r, f, zone;
+ unsigned char *moves, *codes;
+
+ // determine variant parameters
+ for(v=6; --v>0;) if(!strcmp(name, variants[v].name)) break;
+printf("# variant %d: %s\n", v, variants[v].name);
+ nrFiles = variants[v].files;
+ nrRanks = variants[v].ranks;
+ zone = variants[v].zone;
+ maxDrop = variants[v].hand - 1;
+ pieces = variants[v].pieces;
+ moves = variants[v].dirs;
+ startPos= variants[v].fen;
+ codes = variants[v].proms;
+
+ // board
+ ClearBoard();
+ boardEnd = specials = 22*nrRanks;
+
+ for(i=0; i<COLOR; i++) PST[i] = pstData;
+
+ for(r=0; r<11; r++) {
+ for(f=0; f<11; f++) {
+ int sqr = 22*r + f, piece = 11*r + f + WHITE - 1;
+ sqr2file[sqr] = f; sqr2file[sqr+11] = 12;
+ toDecode[sqr] = toDecode[sqr+11] = sqr;
+ promoInc[sqr] = 0; promoInc[sqr+11] = 16;
+ dropType[sqr] = 0; dropType[sqr+11] = piece + 1; // map counters to pieces
+ if(!(piece & 16) && piece < COLOR) { // promotable piece
+ handSlot[piece ^ COLOR] = sqr + 11; // map pieces back to the counter
+ handSlot[piece+16 ^ COLOR] = sqr + 11; // also the piece that demotes to it
+ }
+ zoneTab[sqr] = 0;
+ }
+ }
+ sqr2file[CK_DOUBLE] = sqr2file[CK_NONE] = 13; // for decoding hashed checker
+#define CASTLE(R,F,RT,KT,RF) toDecode[R*22+F] = RT; dropType[R*22+F] = KT; zoneTab[R*22+F] = RF; promoInc[R*22+F] = 3-31; sqr2file[R*22+F] = 11;
+ if(v == 0) for(f=0; f<11; f++) { // In Chess we re-assign other ranks to under-promotions in zone
+ for(r=1; r<7; r++) toDecode[r*22+11+f] = f + (r < 4 ? 0 : 7*22); // redirect to last rank
+ promoInc[1*22+11+f] = promoInc[6*22+11+f] += 1;
+ promoInc[2*22+11+f] = promoInc[5*22+11+f] += 2;
+ promoInc[3*22+11+f] = promoInc[4*22+11+f] += 3;
+ handSlot[WHITE + 16 + f] = handSlot[WHITE]; // all promoted pieces demote to Pawn
+ handSlot[BLACK + 16 + f] = handSlot[BLACK];
+ pawnBulk[WHITE] = 0; // no limit to Pawns per file in Crazyhouse
+ zoneTab[1*22+f] = zoneTab[6*22+f] = Z_DOUBLE;
+ // special Pawn moves
+ toDecode[8*22+f] = 3*22 + f; // white double push
+ toDecode[9*22+f] = 4*22 + f; // black double push
+ toDecode[8*22+11+f] = 2*22 + f; // black e.p. capture
+ toDecode[9*22+11+f] = 5*22 + f;
+ promoInc[8*22+f] = promoInc[9*22+f] = promoInc[8*22+11+f] = promoInc[9*22+11+f] = 0;
+ // castlings
+ CASTLE(8, 10, 5, 6, 7) // K-side
+ CASTLE(9, 10, 7*22+5, 7*22+6, 7*22+7)
+ CASTLE(8, 21, 3, 2, 0) // Q-side
+ CASTLE(9, 21, 7*22+3, 7*22+2, 7*22+0)
+ spoiler[0*22+0] = 4; // initial King and Rook squares marked as castling-right spoilers
+ spoiler[0*22+0] = 5;
+ spoiler[0*22+7] = 1;
+ spoiler[7*22+0] = 8;
+ spoiler[7*22+4] = 10;
+ spoiler[7*22+7] = 2;
+ PST[WHITE+4] = PST[BLACK+4] = kingPST+11; // Queens
+ } else {
+ pawnBulk[WHITE] = (nrRanks == 7 ? 1 : 2); // board file considered full when total reaches 2
+ }
+ pawnBulk[BLACK] = 4*pawnBulk[WHITE];
+ maxBulk[BLACK] = 4*2; maxBulk[WHITE] = 2; // bits to test to see if file is full for specified side
+ pawnBulk[-1] = 0; // used with drops, so they never take away a Pawn from any board file
+
+ // precalculated hash keys for transferring to holdings
+ handSlot[0] = 11*21 + 4; // map empty square safely away from edges
+ for(f=WHITE; f<COLOR; f++) { // all pieces
+ r = handSlot[f]; // location in holdings where piece goes
+ handKey[f] = pieceKey[-1]*squareKey[r];
+ }
+
+ // move-generation tables
+ for(color=0; color<=WHITE; color+=WHITE) {
+ firstDir[color+31] = *moves++; // first is King
+ for(i=0; *moves < 255; i++) firstDir[color+i] = *moves++; // unpromoted pieces
+ moves++; // skip the sentinel
+ for(i=16; *moves < 255; i++) firstDir[color+i] = *moves++; // promoted pieces
+ moves++; // skip the sentinel
+ }
+
+ // promotion tables
+ for(i=0; i<16; i++) { // per-piece promotion abilities (for unpromoted series only)
+ promoCode[i] = Z_WHITE & *codes;
+ promoCode[i+WHITE] = Z_BLACK & *codes++;
+ }
+ for(r=0; r<zone; r++) { // board-size table to indicate promotion zones and force-promotion zones
+ for(f=0; f<nrFiles; f++) {
+ zoneTab[22*r + f] = Z_BLACK & (Z_MUST | COLOR | (r<2)*Z_2ND);
+ zoneTab[22*(nrRanks-1-r)+f] = Z_WHITE & (Z_MUST | COLOR | (r<2)*Z_2ND);
+ }
+ }
+
+ ip = variants[v].values;
+ for(i=0; *ip >= 0; i++) pieceValues[WHITE+i] = pieceValues[BLACK+i] = *ip++; // basic
+ for(i=16,ip++; *ip >= 0; i++) pieceValues[WHITE+i] = pieceValues[BLACK+i] = *ip++; // promoted
+ for(i=0, ip++; *ip >= 0; i++) handVal[WHITE+i] = handVal[BLACK+i] = *ip++; // in hand
+ pawn = 2*handVal[WHITE] << 21; // used for detection of material-loosing loops
+ for(i=0; i<16; i++) {
+ int demoted = dropType[handSlot[WHITE+i+16]]-1; // piece type after demotion (could be Pawn, in Chess)
+ handVal[WHITE+i+16] = handVal[BLACK+i+16] = pieceValues[WHITE+i+16] + handVal[demoted]; // gain by capturing promoted piece
+ }
+ for(i=0; i<16; i++) handVal[WHITE+i] = handVal[BLACK+i] += pieceValues[WHITE+i]; // gain by capturing base piece
+ for(i=0; i<16; i++) {
+ int demoted = dropType[handSlot[WHITE+i+16]]-1; // piece type after demotion (could be Pawn, in Chess)
+ promoGain[WHITE+i+16] = promoGain[BLACK+i+16] = pieceValues[WHITE+i+16] - pieceValues[demoted];
+ }
+ for(i=WHITE; i<COLOR; i++) vVal[i-WHITE] = (handVal[i] + pieceValues[i])/16, aVal[i-WHITE] = handVal[i]/64;
+ promoGain[WHITE+31] = promoGain[BLACK+31] = 0; // King counts as unpromoted (to make castling work, where it promotes to unpromoted Rook)
+
+ // piece-square table
+ for(r=0; r<nrRanks; r++) for(f=0; f<nrFiles; f++) {
+ int sqr = 22*r + f;
+ center[sqr] = 8 - (f - nrFiles/2. + 0.5)*(f - nrFiles/2. + 0.5) - (r - nrRanks/2. + 0.5)*(r - nrRanks/2.+ 0.5);
+ }
+ for(i=0; i<16; i++) { // PST for in-hand pieces. type = -1 only occurs on (all) drops, but true type determined by (off-board) square
+ PST[-1][handSlot[WHITE+i]] = PST[-1][handSlot[BLACK+i]] = handVal[WHITE+i] - 2*pieceValues[WHITE+i];
+ }
+ PST[WHITE+2] = PST[BLACK+2] = center; // Bishops
+ for(f=0; f<11; f++) { // pawn tables
+ for(r=0; r<=zone; r++) pawnPST[22*(nrRanks - 1 - r) + f] = pawnPST[22*r + f + 11] = 1.2*pieceValues[WHITE]; // in and just before zone ('7th rank')
+ pawnPST[22*(nrRanks - 2 - zone) + f] = pawnPST[22*(zone + 1) + f + 11] = 0.6*pieceValues[WHITE]; // ('6th rank')
+ pawnPST[22*(nrRanks - 2) + f + 11] = pawnPST[22 + f] = 10; // discourage leaving 2nd rank
+ for(r=2; r<nrRanks-2; r++) kingPST[22*r+f] = -127; kingPST[f] = kingPST[22*(nrRanks-1)+f] = 90;
+ for(r=2; r<nrRanks-2; r++) kingPST[22*r+f+11] = -40;
+ }
+ PST[WHITE] = pawnPST; PST[BLACK] = pawnPST + 11;
+ PST[WHITE+31] = PST[BLACK+31] = kingPST;
+
+ InitCaptureCodes(variants[v].codes);
+ pinCodes = (v == 3 ? 0xFF2C : 0xFF1F); // rays along which pinning ispossible
+}
+
+void
+PrintDBoard (char *msg, unsigned char *b, char *sep, int ranks)
+{
+ int r, f;
+ printf("\n%s\n", msg);
+ for(r=0; r<ranks; r++) for(f=0; f<22; f++) printf(" %02x%s", b[22*r+f], (f == 10 ? sep : f== 21 ? "\n" : ""));
+}
+
+void
+PrintPieces (char *msg, unsigned char *p, int n)
+{
+ int i; if(n==0) n = COLOR;
+ printf("\n%s\n", msg);
+ for(i=0; i<n; i++) printf(" %02x%s", p[i], (i & 15) == 15 ? "\n" : "");
+}
+
+void
+PrintValues (char *msg, int *p, int n)
+{
+ int i; if(n==0) n = COLOR;
+ printf("\n%s\n", msg);
+ for(i=0; i<n; i++) printf(" %3d%s", p[i], (i & 15) == 15 ? "\n" : "");
+}
+
+void
+Debug ()
+{
+ int i, j, r, f;
+ printf("\npieceKey\n");
+ for(i=0; i<96; i++) printf(" %08x%s", pieceKey[i], (i&7)==7 ?"\n" : "");
+ printf("\nsquareKey\n");
+ for(i=0; i<22*11; i++) printf(" %08x%s", squareKey[i], (i&7)==7 ?"\n" : "");
+ printf("\ncapt_code\n");
+ for(r=2; r<19; r++) for(f=2; f<19; f++) printf(" %3x%s", rawInts[22*r+f], (f== 18 ? "\n" : ""));
+ printf("\ndelta_vec\n");
+ for(r=2; r<19; r++) for(f=2; f<19; f++) printf(" %3d%s", rawChar[22*r+f], (f== 18 ? "\n" : ""));
+ printf("\ndistance\n");
+ for(r=2; r<19; r++) for(f=2; f<19; f++) printf(" %3d%s", rawByte[70*22+22*r+f], (f== 18 ? "\n" : ""));
+ PrintDBoard("zoneTab:", zoneTab, " ", 11);
+ PrintDBoard("dropType:", dropType, " ", 11);
+ PrintDBoard("toDecode:", toDecode, " ", 11);
+ PrintDBoard("promoInc:", promoInc, " ", 11);
+ PrintDBoard("sqr2file:", sqr2file, " ", 11);
+ PrintPieces("handSlot:", handSlot, 0);
+ PrintPieces("promoCode:", promoCode, 0);
+ PrintPieces("pawnBulk:", pawnBulk, 0);
+ printf("\nnearCode:\n"); for(i=0; i<96; i++) printf(" %2x%s", pieceCode[i] & 255, (i&15) == 15 ? "\n" : "");
+ printf("\nfarCode:\n"); for(i=0; i<96; i++) printf(" %2x%s", pieceCode[i]>>8 & 255, (i&15) == 15 ? "\n" : "");
+ PrintValues("pieceValues:", pieceValues, 0);
+ PrintValues("handVal:", handVal, 0);
+ PrintValues("promoGain:", promoGain, 96);
+ PrintPieces("vVal:", vVal, 64);
+ PrintPieces("aVal:", aVal, 64);
+ PrintDBoard("hand PST:", PST[-1], " ", 11);
+ PrintDBoard("Pawn PST:", PST[WHITE], " ", 11);
+ PrintDBoard("board:", board, " ", 11);
+}
+
+#define KEY(A, B) (pieceKey[A]*(Key) squareKey[B])
+
+typedef long long int Key;
+
+typedef struct { // 12 bytes
+ unsigned int lock;
+ short int score;
+ unsigned short int move;
+ unsigned char depth;
+ unsigned char flags;
+ unsigned char checker;
+ char age;
+} HashEntry;
+
+typedef struct {
+ Key hashKey, newKey;
+ unsigned char fromSqr, toSqr, captSqr, epSqr, rookSqr, rights;
+ signed char fromPiece, toPiece, victim, savePiece, rook, mutation;
+ int pstEval, newEval;
+ int move, depth;
+ int checker, checkDir, checkDist, xking;
+} StackFrame;
+
+typedef struct { // move stack sectioning
+ int firstMove; // start of move list for current ply
+ int unsorted; // start of unsorted tail of move list
+ int nonCapts; // index of first non-capture in move list
+ int drops; // index of first quiet drop
+ int stage; // stage of move generation (0=hash/capt/prom, 1=killer/noncapt, 2=check-drops, 3=quiet drops)
+ int late; // start of late moves
+ int epSqr;
+ int checker;
+} MoveStack;
+
+HashEntry *hashTable;
+Key hashKey, pawnKey;
+int hashMask;
+
+int
+Evaluate (int stm)
+{
+ int k, score = 0;
+ k = location[31];
+ score += ((board[k+22] == WHITE) + (board[k+22+1] == WHITE) + (board[k+22-1] == WHITE))*2;
+ score -= !board[k+22] + !board[k+22+1] + !board[k+22-1];
+ score -= ((board[k+44] == BLACK) + (board[k+44+1] == BLACK) + (board[k+44-1] == BLACK))*5;
+ k = location[WHITE+31];
+ score -= ((board[k-22] == BLACK) + (board[k-22+1] == BLACK) + (board[k-22-1] == BLACK))*2;
+ score += !board[k-22] + !board[k-22+1] + !board[k-22-1];
+ score += ((board[k-44] == WHITE) + (board[k-44+1] == WHITE) + (board[k-44-1] == WHITE))*5;
+ score *= 20;
+ return stm == WHITE ? score : -score;
+}
+
+int
+PseudoLegal (int stm, int move)
+{ // used for testing killers, so we can assume the move must be pseudo-legal for the stm in some position
+ int match, from = move >> 8 & 0xFF, to = move & 255;
+ signed char piece = board[from];
+ to = toDecode[to];
+ if(piece < 0) { // drop
+ if(piece == -1) return 0; // type not in hand
+ piece = dropType[from] - 1;
+ if((piece & ~COLOR) == 0 && pawnCount[sqr2file[to]] & maxBulk[stm]) return 0; // Pawn drop would over-crowd file
+ return (board[to] == 0); // otherwise drop is legal on empty square (assumes drop location always legal for piece type)
+ }
+ if(!(piece & stm)) return 0; // piece has wrong color
+ match = pieceCode[piece] & captCode[to - from];
+ if(!match) return 0; // not aligned
+ if(match & C_DISTANT) { // distant alignment
+ int step = deltaVec[to - from];
+ while(board[to -= step] == 0) {} // ray scan towards mover
+ return (from == to); // legal if it reaches mover
+ }
+ return 1; // must be leaper alignment, which guarantees hit
+}
+
+void
+Dump (char *s)
+{int i; printf("%s\n",s); for(i=0; i<ply; i++) printf(" {%x} %s", deprec[i], MoveToText(path[i])); PrintDBoard("board", board, " ", 11); exit(1); }
+
+int
+MoveGen (int stm, MoveStack *m)
+{ // generate all board moves, return 1 if King capture found amongst those
+ int r, f;
+ m->firstMove = m->nonCapts = m->late = moveSP; m->stage = 0;
+ for(r=0; r<boardEnd; r+=22) for(f=0; f<nrFiles; f++) {
+ int from = r + f, piece = board[from];
+ if(piece & stm) {
+ int step, dir = 2*firstDir[piece-WHITE]; // steps data comes in pairs
+ while((step = steps[dir++])){ // next direction
+ int range = steps[dir++], to = from, victim, inZone = zoneTab[from];
+ do {
+ int move, promote, slot;
+ victim = board[to += step];
+ if(victim & stm) break; // captures own piece
+ if(range & 2) { // divergent move: must be FIDE Pawn
+ if(to == m->epSqr) { // reaches e.p. square: must be through diagonal move, and e.p. square is always empty
+ moveStack[--m->firstMove] = to + 4*22+11 + 44*(stm == BLACK) | from << 8 | vVal[0] << 24;
+ break;
+ }
+ if(!victim == (range & 1)) break; // wrong type (lsb set = capture only, cleared = move only)
+ if(range > 7) { // can do double push
+ if(inZone & Z_DOUBLE && board[to+step] == 0) { // started on Pawn rank, and square in front of it is empty
+ moveStack[moveSP++] = from << 8 | to + step + 22*5; // generate now, as special move
+ }
+ range -= 8; // make sure it is not done again
+ }
+ }
+ if((victim & ~COLOR) == 31) return 1; // captures King; abort!
+ move = piece << 16; // store piece in move
+ promote = (inZone | zoneTab[to]) & promoCode[piece-WHITE];
+ if((promote & (Z_2ND | Z_MUST )) == 0) { // not in place where deferral forbidden
+ int slot;
+ if(victim) { // capture
+ slot = --m->firstMove;
+ move += vVal[victim-WHITE] - aVal[piece-WHITE] << 24; // MVV/LVA sort code
+ } else { // non-capture
+ slot = moveSP++;
+ }
+ moveStack[slot] = move | from << 8 | to;
+ }
+ if(promote & stm) { // promotion is (also?) possible
+ moveStack[--m->firstMove] = (move | from << 8 | to + 11) + (vVal[victim-WHITE] + 20 << 24); // put it amongst captures
+ if(promoCode[piece-WHITE] & Z_FIDE) { // only for FIDE Pawns
+ moveStack[--m->firstMove] = move | from << 8 | to - 11 + (stm >> 6)*44; // under-promotion
+ // other under-promotions could go here (Capahouse?)
+ }
+ }
+
+
+ if((range -= 8) <= 0) break; // range exhausted
+ } while(!victim);
+
+ }
+ }
+ }
+ m->unsorted = m->firstMove; m->drops = moveSP;
+
+ return 0;
+}
+
+void
+CheckDrops (int stm, int king)
+{
+ int i;
+ stm ^= COLOR;
+ for(i=maxDrop; i>=0; i--) {
+ int piece = stm + i, from = handSlot[piece];
+ if((signed char)board[from] < -1) { // piece type is in hand
+ int step, dir = 2*firstDir[piece-WHITE];
+ while((step = steps[dir++])) {
+ int to = king, range = steps[dir++];
+ if((range & 3) == 2) break; // non-capture direction
+ while(board[to-=step] == 0) {
+ moveStack[moveSP++] = to | from << 8;
+ if((range -= 8) <= 0) break;
+ }
+ }
+ }
+ }
+}
+
+void
+EvasionDrops (int stm, StackFrame *f)
+{
+ int i, x = f->checker, v = f->checkDir;
+ stm ^= COLOR;
+ while(board[x+=v] == 0) { // all squares on check ray
+ i = (x < 22 || x >= 7*22); // not on last rang. *** WRONG FOR SHOGI ***
+ for(i=0; i<=maxDrop; i++) { // all droppable types
+ int piece = stm + i, from = handSlot[piece];
+ if((signed char)board[from] < -1) // piece type is in hand
+ moveStack[moveSP++] = from << 8 | x;
+ }
+ }
+}
+
+void
+AllDrops (int stm)
+{
+ int i, start = 22, end = boardEnd-22;
+ stm ^= COLOR;
+ for(i=0; i<=maxDrop; i++) {
+ int piece = stm + i, from = handSlot[piece];
+ if((signed char)board[from] < -1) { // piece type is in hand
+ int r, f;
+ for(r=start; r<end; r+=22) for(f=0; f<nrFiles; f++)
+ if(board[r+f] == 0) moveStack[moveSP++] = from << 8 | f + r;
+ }
+ start = 0; end = boardEnd;
+ }
+}
+
+int
+NonEvade (StackFrame *f)
+{
+ if((f->fromPiece & ~COLOR) != 31) { // moves non-royal (or drops)
+ int d;
+ if(f->checker == CK_DOUBLE) return 1; // never helps against double check
+ if(f->toSqr == f->checker) return 0; // captures only checker: OK
+ d = dist[f->checker - f->toSqr];
+ if(d && deltaVec[f->toSqr - f->checker] == f->checkDir && d < f->checkDist) return 0; // interposes: OK
+ return 1;
+ }
+ // king move
+ return 0; // for now, defer testing to daughter node
+}
+
+int
+MakeMove (StackFrame *f, int move)
+{
+ int to;
+ f->fromSqr = move >> 8 & 255;
+ to = move & 255;
+ f->toSqr = f->captSqr = toDecode[to]; // real to-square for to-encoded special moves
+ f->fromPiece = board[f->fromSqr]; // occupant or (for drops) complemented holdings count
+ if(f->checker != CK_NONE && NonEvade(f)) return 0; // abort if move did not evade existing check
+ f->mutation = (f->fromPiece >> 7) | f->fromPiece; // occupant or (for drops) -1
+ f->toPiece = f->mutation + promoInc[to] + dropType[f->fromSqr]; // (possibly promoted) occupant or (for drops) piece to drop
+ f->savePiece = board[f->toSqr]; // replacement victim
+ f->newEval = f->pstEval; f->newKey = f->hashKey; // start building new key and eval
+ f->epSqr = 255;
+ f->rookSqr = sqr2file[f->toSqr] + (pawnCount - board); // normally (i.e. when not castling) use for pawnCount
+ f->rook = board[f->rookSqr]; // save and update Pawn occupancy
+ board[f->rookSqr] = f->rook + pawnBulk[f->toPiece] - pawnBulk[f->mutation] - pawnBulk[f->savePiece]; // assumes all on same file!
+//printf("f=%02x t=%02x fp=%02x tp=%02x sp=%02x mut=%02x ep=%02x\n", f->fromSqr, f->toSqr, f->fromPiece, f->toPiece, f->savePiece, f->mutation, f->epSqr);
+ if(to >= specials) { // treat special moves for Chess
+ if(sqr2file[to] > 11) { // e.p. capture, shift capture square
+//printf("# e.p. %02x\n", to);
+ f->captSqr = toDecode[to-11]; // use to-codes from double pushes, which happen to be what we need
+ f->victim = board[f->captSqr];
+ board[f->captSqr] = 0; // e.p. is only case with toSqr != captSqr where we have to clear captSqr
+ } else if(sqr2file[to] < 8) { // double push
+ int xpawn = f->toPiece ^ COLOR; // enemy Pawn
+ if(board[f->toSqr + 1] == xpawn || // if land next to one
+ board[f->toSqr - 1] == xpawn ) {
+ f->epSqr = (f->fromSqr + f->toSqr) >> 1; // set e.p. rights
+ }
+ f->victim = 0; // for key and pst update
+ } else { // castling. at this point we are set up to 'promote' a King to Rook (so the check tests sees the Rook, and UnMake restores location[K])
+ f->rookSqr = zoneTab[to]; // Rook from-square
+ f->rook = board[f->rookSqr]; // arrange Rook to be put back on UnMake (pawnCount is never modified in chess)
+ board[f->rookSqr] = 0; // and remove it
+ f->newEval -= PST[f->toPiece][f->rookSqr];
+ f->newKey -= KEY(f->toPiece, f->rookSqr);
+ f->captSqr = dropType[to]; // this tabulates to-square of the King
+ f->victim = board[f->captSqr]; // should be 0, but who knows?
+ board[f->captSqr] = f->mutation; // place the King
+ f->newEval += PST[f->mutation][f->captSqr];
+ f->newKey += KEY(f->mutation, f->captSqr);
+ location[f->mutation] = f->captSqr; // be sure King location stays known
+ }
+ } else f->victim = f->savePiece; // for normal moves replacement victim counts
+ board[f->fromSqr] = f->fromPiece - f->mutation; // 0 or (for drops) decremented count
+ board[f->toSqr] = f->toPiece;
+ board[handSlot[f->victim]]--; // put victim in holdings
+//printf("# capt=%02x vic=%02x slot=%02x\n", f->captSqr, f->victim, handSlot[f->victim]);
+
+ f->newEval += promoGain[f->toPiece] - promoGain[f->mutation] + handVal[f->victim] +
+ PST[f->toPiece][f->toSqr] - PST[f->mutation][f->fromSqr] + PST[f->victim][f->captSqr];
+ f->newKey += KEY(f->toPiece, f->toSqr) - KEY(f->mutation, f->fromSqr) - KEY(f->victim, f->captSqr) + handKey[f->victim];
+ location[f->toPiece] = f->toSqr;
+
+ return 1;
+}
+
+void
+UnMake (StackFrame *f)
+{
+ board[f->rookSqr] = f->rook; // restore either pawnCount or (after castling) Rook from-square
+ board[f->captSqr] = f->victim; // differs from toSqr on e.p. (Pawn to-square) and castling, (King to-square) and should be cleared then
+ board[f->toSqr] = f->savePiece; // put back the regularly captured piece (for castling that captured by Rook)
+ board[f->fromSqr] = f->fromPiece; // and the mover
+ board[handSlot[f->victim]]++;
+ location[f->fromPiece] = f->fromSqr;
+}
+
+#define PATH 0
+//ply==0 || path[0]==0x0017b1 && (ply==1 || (ply==2))
+
+int
+Search (int stm, int alpha, int beta, StackFrame *ff, int depth, int reduction, int maxDepth)
+{
+ MoveStack m; StackFrame f; HashEntry *entry;
+ int oldSP = moveSP, *pvStart = pvPtr;
+ int killer1 = killers[ply][0], killer2 = killers[ply][1], hashMove;
+ int bestNr, bestScore, startAlpha, startScore, resultDepth, iterDepth=0;
+ int hit, hashKeyH;
+ int curEval, score;
+
+ // legality
+ int earlyGen = (ff->toPiece == stm+31 || ff->toSqr != ff->captSqr); // King was moved, or e.p.
+if(ply > 90) Dump("maxply");
+ f.xking = location[stm+31]; // opponent King, as stm not yet toggled
+ if(!earlyGen) { // if other piece was moved, abort with +INF score if it was pinned
+ if(ff->mutation > 0) { // exclude drops
+ int vec = f.xking - ff->fromSqr;
+ int match = captCode[vec] & pinCodes;
+ if(match) {
+ int x = f.xking, v = deltaVec[vec];
+ while(board[x-=v] == 0) {}
+ if(!(board[x] & stm) && captCode[f.xking-x] & pieceCode[board[x]]) return INF; // guards & counters tests as own piece!
+ }
+ }
+ }
+
+ // some housekeeping
+ stm ^= COLOR;
+ f.hashKey = ff->newKey;
+ f.pstEval = -ff->newEval;
+ f.rights = ff->rights | spoiler[ff->toSqr] | spoiler[ff->fromSqr];
+ m.epSqr = ff->epSqr; // put in m, because MoveGen needs it
+if(PATH)printf("%d:%d Hash Probe %016llx\n",ply,depth,f.hashKey);
+ // hash probe
+ hashKeyH = f.hashKey >> 32;
+ entry = hashTable + (f.hashKey + (stm + 9849)*(m.epSqr + 51451) & hashMask);
+ if(entry->lock == hashKeyH || (++entry)->lock == hashKeyH || (++entry)->lock == hashKeyH || (++entry)->lock == hashKeyH) { // 4 possible entries
+ int score = entry->score, d = entry->depth;
+ f.checker = entry->checker; f.checkDist = 0;
+ if(f.checker != CK_NONE) { // in check; restore info needed in evasion test
+ if(sqr2file[f.checker] != 12) f.checkDir = 0; else { // off-board represents on-board distant check
+ int vec = location[stm+31] - (f.checker -= 11);
+ f.checkDir = deltaVec[vec];
+ f.checkDist = dist[vec];
+ }
+ reduction = 0; // checks are not reduced
+ }
+if(PATH)printf(" Hit, d=%d, checker = %x\n",entry->depth,f.checker);
+ if((entry->flags & H_LOWER || entry->score <= alpha) && (entry->flags & H_UPPER || entry->score >= beta)) { // compatible bound
+ d += (score >= beta)*reduction; // fail highs need to satisfy reduced depth only, so we fake higher depth than actually found
+ if(score > alpha && d >= depth || d >= maxDepth) { // sufficient depth
+ ff->depth = d + 1; return entry->score; // depth was sufficient, take hash cutoff
+ }
+ }
+ hashMove = entry->move;
+if(hashMove && board[hashMove>>8&255] == 0) {char s[100];sprintf(s,"bad hash move %16llx: %s\n", f.hashKey, MoveToText(hashMove)); Dump(s); }
+ hit = 1;
+ } else hit = hashMove = 0, f.checker = CK_UNKNOWN;
+
+ moveSP += 48; // create space for non-captures
+ if(earlyGen) { // last moved piece was King
+ if(MoveGen(stm, &m)) { moveSP = oldSP; return INF; } // make sure we detect if he moved into check
+ }
+
+ if((++nodeCount & 0xFFF) == 0) abortFlag |= TimeIsUp(3); // check time limit every 4K nodes
+ curEval = f.pstEval + Evaluate(stm);
+ alpha -= (alpha < curEval); //pre-compensate delayed-loss bonus
+ beta -= (beta <= curEval);
+ killers[ply+1][0] = killers[ply+1][1] = 0;
+ if(-INF >= beta) { moveSP = oldSP; return -INF+1; }
+
+
+ // check test
+ if(f.checker == CK_UNKNOWN) { // hash did not supply it
+ int king = location[stm+31]; // own King
+ int vec = king - ff->toSqr;
+ int match = captCode[vec] & pieceCode[ff->toPiece];
+ f.checker = CK_NONE; f.checkDist = 0; // assume not in check
+ if(match & C_DISTANT) { // moving piece is aligned
+ int x = ff->toSqr, v = deltaVec[vec];
+ while(board[x+=v] == 0) {} // scan ray
+ if(x == king) f.checker = ff->toSqr, f.checkDir = v, f.checkDist = dist[vec]; // ray is clear, distant check
+ } else if(match & C_CONTACT) f.checker = ff->toSqr, f.checkDir = 0; // contact check
+ if(ff->mutation != -1) { // board move (no drop)
+ vec = king - ff->fromSqr;
+ match = captCode[vec] & pinCodes;
+ if(match) { // from-square is aligned
+ int x = king, v = deltaVec[vec];
+ while(board[x-=v] == 0) {} // scan ray
+ if(f.checker != x && !(board[x] & stm) && captCode[king-x] & pieceCode[board[x]]) { // discovered check
+ if(f.checker != CK_NONE) f.checker = CK_DOUBLE;
+ else f.checker = x, f.checkDir = v, f.checkDist = dist[x-king];
+ }
+ }
+ if(board[ff->captSqr] == 0) { // e.p. capture can discover check as well
+ vec = king - ff->captSqr;
+ match = captCode[vec] & pinCodes;
+ if(match) { // from-square is aligned
+ int x = king, v = deltaVec[vec];
+ while(board[x-=v] == 0) {} // scan ray
+ if(f.checker != x && !(board[x] & stm) && captCode[king-x] & pieceCode[board[x]]) { // discovered check
+ if(f.checker != CK_NONE) f.checker = CK_DOUBLE;
+ else f.checker = x, f.checkDir = v, f.checkDist = dist[x-king];
+ }
+ }
+ }
+ }
+ }
+ if(f.checker != CK_NONE) depth++, maxDepth++, reduction = 0; // extend check evasions
+ else if(depth > 3) {
+ if(depth - reduction < 3) reduction = depth - 3; // never reduce to below 3 ply
+ depth -= reduction;
+ } else reduction = 0;
+if(PATH)printf("%d:%d {%d,%d} max=%d eval=%d check=%02x,%d,%d\n",ply,depth,alpha,beta,maxDepth,curEval,f.checker,f.checkDir,f.checkDist);
+ // stand pat or null move
+ startAlpha = alpha; startScore = -INF;
+ if(depth <= 0) { // QS
+ if(curEval > alpha) {
+ if(curEval >= beta) { ff->depth = 1; moveSP = oldSP; return curEval; } // stand-pat cutoff
+ alpha = startScore = curEval; maxDepth = 0; // we will not fail low, so no extra iterations
+ }
+ if(maxDepth <= 0 && board[toDecode[hashMove&255]] == 0) hashMove = 0;
+ } else if(curEval >= beta && f.checker == CK_NONE) {
+ int nullDepth = (depth > 3 ? depth - 3 : 0);
+ f.mutation = -1; // kludge to suppress testing for discovered check
+ f.newEval = f.pstEval;
+ f.newKey = f.hashKey;
+ deprec[ply] = maxDepth << 16 | depth << 8; path[ply++] = 0;
+ score = -Search(stm, -alpha-1, -alpha, &f, nullDepth, 0, nullDepth);
+ ply--;
+ if(score >= beta) { ff->depth = f.depth + 2; moveSP = oldSP; return beta + 1; }
+ }
+
+ // move generation
+ if(!earlyGen) MoveGen(stm, &m); // generate moves if we had not done so yet
+ if(hashMove) moveStack[--m.firstMove] = hashMove; // put hash move in front of list (duplicat!)
+
+ do { // IID loop
+ int curMove, highDepth;
+ iterDepth++;
+if(PATH)printf("%d:%d:%d new iter moveStack[%d..%d]\n",ply,depth,iterDepth,m.firstMove,moveSP);
+ highDepth = (iterDepth > depth ? iterDepth : depth) - 1; // reply depth for high-failing moves
+ alpha = startAlpha;
+ pvPtr = pvStart; *pvPtr++ = 0; // empty PV
+ bestScore = startScore; bestNr = 0; // kludge: points to 0 entry in moveStack
+ resultDepth = MAXPLY;
+ m.stage &= 3;
+ for(curMove=m.firstMove; m.stage<4; curMove++) {
+ int score;
+
+ // sort section
+ if(curMove >= m.unsorted) {
+ if(curMove < m.nonCapts) { // captures: extract best
+ unsigned int i, bestNr = curMove, bestCapt = moveStack[curMove];
+ for(i=curMove+1; i<m.nonCapts; i++) if(moveStack[i] > bestCapt) bestCapt = moveStack[bestNr=i]; // find best
+ moveStack[bestNr] = moveStack[curMove]; moveStack[curMove] = bestCapt; // swap it to front
+ m.unsorted = curMove + 1; // sorted set now includes move
+ } else {
+ if(maxDepth <= 0) { resultDepth = 0; if(bestScore < curEval) bestScore = curEval; break; } // in QS we stop after captures
+ switch(m.stage) { // we reached non-captures
+ case 0:
+ if(PseudoLegal(stm, killer1)) moveStack[moveSP++] = moveStack[m.late], moveStack[m.late++] = killer1; // insert killers
+ if(PseudoLegal(stm, killer2)) moveStack[moveSP++] = moveStack[m.late], moveStack[m.late++] = killer2; // (original goes to end)
+ m.drops = moveSP;
+ // here we can sort based on history
+ m.stage = 1; if(moveSP > curMove) break;
+ case 1:
+ if(f.checker != CK_NONE) {
+ m.stage |= 4; // when in check we stop after evasion drops
+ if(f.checkDist == 0) continue; // but there cannot be any for contact/double checks
+ EvasionDrops(stm, &f);
+ if(moveSP <= curMove) continue; // no avail
+ m.stage = 3; break;
+ }
+ CheckDrops(stm, f.xking);
+ m.stage = 2; if(moveSP > curMove) break;
+ case 2:
+ if(iterDepth > 1) { // quiet drops only at depth >= 2
+ AllDrops(stm);
+ m.stage = 3; if(moveSP > curMove) break;
+ }
+ case 3:
+ m.stage |= 4; continue; // this value of m.stage terminates the loop over moves
+ }
+ m.unsorted = moveSP; // set to return here when done with the current list
+ }
+ }
+
+ // make move
+ if(MakeMove(&f, moveStack[curMove])) { // aborts if fails to evade existing check
+
+ // repetition checking
+ int index = (unsigned int)f.newKey >> 24 ^ stm << 2; // uses high byte of low (= hands-free) key
+ while(repKey[index] && (repKey[index] ^ (int)f.newKey) & 0x1FFFFF) index += 2;
+ if(repKey[index]) { // key present in table: (quasi-)repetition
+ int gain = (f.newEval << 21) - (repKey[index] & 0xFFE00000);
+ if(gain == 0) { // true repeat
+ score = 0; // draw score *** HERE WE SHOULD TEST FOR PERPETUALS IN SHOGI ***
+ } else if(gain == pawn || gain > (450<<21)) score = INF; // quasi-repeat with extra piece in hand
+ else if(gain == -pawn || gain < (-450<<21)) score = -INF; // or with one piece less
+ else goto search;// traded one hand piece for another; could still lead somewhere
+ } else { // not a repeat: search it
+ int lmr;
+ search:
+ lmr = (curMove >= m.late) + (curMove >= m.drops);
+ repKey[index] = (int)f.newKey & 0x1FFFFF | f.newEval << 21; // remember position
+ // recursion
+ deprec[ply] = (f.checker != CK_NONE ? f.checker : 0)<<24 | maxDepth<<16 | depth<< 8 | iterDepth; path[ply++] = moveStack[curMove];
+ score = -Search(stm, -beta, -alpha, &f, iterDepth-1, lmr, highDepth);
+ ply--;
+
+ repKey[index] = 0;
+ }
+
+ // unmake
+ UnMake(&f);
+
+ } else score = -INF, f.depth = MAXPLY;
+if(PATH){
+int m=moveStack[curMove];
+printf("%d:%d:%d %2d. %08x %c%d%c%d %6d %6d %6d\n",ply,depth,iterDepth,curMove,m,(m>>8&255)%22+'a',(m>>8&255)/22+1,toDecode[m&255]%22+'a',toDecode[m&255]/22+1,f.pstEval,score,bestScore);
+}
+
+ if(abortFlag) { moveSP = oldSP; return -INF; }
+
+ // minimaxing
+ if(f.depth < resultDepth) resultDepth = f.depth;
+
+ if(score > bestScore) {
+ bestScore = score; bestNr = curMove;
+ if(score > alpha) {
+ int *tail;
+ alpha = score;
+ if(score >= beta) { // beta cutoff
+ if(curMove >= m.nonCapts && moveStack[curMove] != killers[ply][1])
+ killers[ply][0] = killers[ply][1], killers[ply][1] = moveStack[curMove];
+ resultDepth = f.depth;
+ goto cutoff; // done with this node
+ }
+ tail = pvPtr; pvPtr = pvStart; *pvPtr++ = moveStack[curMove]; // alpha < score < beta: move starts new PV
+ while(*pvPtr++ = *tail++); // copy PV of daughter node behind it (including 0 sentinel)
+ if(ply == 0) { // in root we print this PV
+ printf("%d %d %d %d", iterDepth, score, ReadClock(0)/10, nodeCount);
+ for(tail=pvStart; *tail; tail++) printf(" %s", MoveToText(*tail));
+ printf("\n"); fflush(stdout);
+ }
+ }
+ }
+ } // move loop
+
+ // stalemate correction
+
+ // self-deepening
+ if(resultDepth > iterDepth) iterDepth = resultDepth; // unexpectedly deep result (from hashed daughters?)
+ if(reduction && iterDepth == depth) depth += reduction, reduction = 0; // no fail high, start unreduced re-search on behalf of parent
+if(PATH)printf("%d:%d:%d iter end, max=%d, alpha=%d start=%d\n", ply, depth,iterDepth, maxDepth, alpha, startAlpha);
+ if(iterDepth >= depth && alpha > startAlpha ) break; // move is PV; nominal depth suffices
+ alpha = startAlpha; // reset alpha for next iteration
+
+ // put best in front
+ ff->move = moveStack[bestNr];
+ if(bestNr > m.firstMove) {
+ int bestMove = moveStack[bestNr];
+ if(bestNr == m.firstMove+1) moveStack[bestNr] = moveStack[m.firstMove]; else m.firstMove--; // swap first two, or prepend duplicat
+ moveStack[bestNr = m.firstMove] = bestMove;
+ } else m.late += (m.late == bestNr); // if best already in front (or non-existing), just make sure it is not reduced
+
+ } while(iterDepth < maxDepth && (ply || !TimeIsUp(1))); // IID loop
+
+ cutoff:
+ // delayed-loss bonus
+ bestScore += (bestScore < curEval);
+ resultDepth -= (f.checker != CK_NONE); // store unextended depth
+
+ // hash store
+ if(!hit) { // replacement
+// if(searchNr - entry[-3].age > 2) entry -= 3; else { // replace primary hit if stale
+ {
+ HashEntry *entry2 = entry - 3;
+ entry2 += (entry2[0].depth > entry2[1].depth);
+ entry -= (entry[0].depth > entry[-1].depth);
+ if(entry->depth > entry2->depth) entry = entry2;
+ }
+ }
+ entry->lock = hashKeyH;
+ entry->move = moveStack[bestNr]; // if no move was found, bestNr = 0, and moveStack[0] contains INVALID
+ entry->score = bestScore;
+ entry->depth = resultDepth;
+ entry->flags = (bestScore > alpha)*H_LOWER + (bestScore < beta)*H_UPPER;
+ entry->checker = f.checker + 11*(f.checkDist != 0); // encode distant check as off-board checker
+if(PATH || f.hashKey == 0x1348708590)printf("%d:%d Hash store %16llx, d=%d, checker=%x move=%s\n",ply,depth,f.hashKey,resultDepth,entry->checker,MoveToText(entry->move));
+
+ // return results
+ moveSP = oldSP; pvPtr = pvStart;
+ ff->depth = resultDepth + 1 + reduction; // report valid depth as seen from parent
+ return bestScore;
+}
+
+int moveNr; // part of game state; incremented by MakeMove
+int gameMove[MAXMOVES]; // holds the game history
+int stm = WHITE;
+
+// Some routines your engine should have to do the various essential things
+void PonderUntilInput(int stm); // Search current position for stm, deepening forever until there is input.
+
+int
+TimeIsUp (int mode)
+{ // determine if we should stop, depending on time already used, TC mode, time left on clock and from where it is called ('mode')
+ int t = ReadClock(0), targetTime, panicTime;
+ if(timePerMove >= 0) { // fixed time per move
+ targetTime = panicTime = 10*timeLeft - 30;
+ } else if(mps) { // classical TC
+ int movesLeft = -(moveNr >> 1);
+ while(movesLeft <= 0) movesLeft += mps;
+ targetTime = 10*(timeLeft - 30) / (movesLeft + 2);
+ panicTime = 50*(timeLeft - 30) / (movesLeft + 4);
+ } else { // incremental TC
+ targetTime = 10*timeLeft / 40 + inc;
+ panicTime = 5*targetTime;
+ }
+ switch(mode) {
+ case 1: return (t > 0.6*targetTime); // for starting new root iteration
+ case 2: return (t > targetTime); // for searching new move in root
+ case 3: return (t > panicTime); // during search
+ }
+ return 0; // unreachable; added to silence warning
+}
+
+StackFrame undoInfo;
+
+int
+Setup (char *fen)
+{ // very flaky FEN parser
+ static char castle[] = "QqKk-", startFEN[200];
+ int pstEval, rights, stm = WHITE, i, p, sqr = 22*(nrRanks-1); // upper-left corner
+ ClearBoard();
+ if(!fen) fen = startFEN; else strcpy(startFEN, fen); // remember start position, or use remembered one if not given
+ rights = 15; pstEval = 0; // no castling rights, balance score
+ hashKey = pawnKey = 0; // clear hash keys
+ while(*fen) { // parse board-field of FEN
+ if(*fen == ' ' || *fen == '[') break;
+ if(*fen == '/') sqr = 22*(sqr/22) - 22; else // skip to (start of) next rank
+ if(*fen <= '9') {
+ int n = atoi(fen); sqr += n; fen += (n > 9); // skip given number of squares (and second digit of 10 or 11)
+ } else {
+ int color, prom, n;
+ fen += prom = (*fen == '+'); // remember promotion prefix
+ p = *fen & ~32; color = *fen - p + WHITE; // convert to upper case and determine color
+ prom |= n = (fen[1] == '~'); fen += n; // remember promotion suffix
+ i = 0; while(pieces[i] && pieces[i] != p) i++; // identify piece type
+ if(p == 'K') i = 31; // K is not in list, and (royal) piece 31 in any variant
+ if(p == 'Q' && *fen == '~') i = 0; // Q~ is +P, not +Q
+ i |= color + 16*prom; // adjust type for color and promotion
+ board[sqr] = i; location[i] = sqr; // place piece
+ hashKey += KEY(i, sqr); // update hash key
+ pstEval += (color & WHITE ? 1 : -1)*(PST[i][sqr]+pieceValues[i]);// update PST eval (white POV)
+ pawnCount[sqr2file[sqr]] += pawnBulk[i]; // Pawn occupancy per file
+ sqr++;
+ }
+ fen++;
+ }
+ while(*fen == ' ') fen++; // skip white
+ if(*fen == '[') { // holdings
+ if(*++fen == '-') fen++; else
+ while(*fen && (p = *fen) != ']') {
+ int color;
+ p &= ~32; color = *fen++ - p + WHITE;
+ i = 0; while(pieces[i] && pieces[i] != p) i++; // identify piece type
+ i |= color; // adjust type for color
+ sqr = handSlot[i ^ COLOR]; // determine counter location
+ board[sqr]--; // count piece in hand
+ hashKey += handKey[i ^ COLOR]; // update hash key
+ pstEval += (color & WHITE ? 1 : -1)*(handVal[i] - pieceValues[i]); // update PST eval (white POV)
+ }
+ fen++; // skip closing bracket
+ }
+ if(*++fen == 'b') stm = BLACK, pstEval *= -1; // black to move; flip eval
+ while(*++fen == ' ');
+ while(*fen) {
+ i=0; while(castle[i] && castle[i] != *fen) i++;
+ if(i > 3) break;
+ rights &= ~(1<<i);
+ fen++;
+ }
+
+ undoInfo.rights = rights;
+ undoInfo.newEval = (stm == WHITE ? pstEval : -pstEval);
+ undoInfo.newKey = hashKey;
+
+ lastGameMove = 0; // TODO: use FEN e.p. rights to fake double-push here
+ return stm;
+}
+
+char *
+MoveToText (int move)
+{
+ static char buf[20], pieceID[] = "+nbrq";
+ int promo = '\0', from = move >> 8 & 0xFF, to = move & 0xFF;
+ int inc = promoInc[to];
+ to = toDecode[to];
+ if(inc > 0) promo = pieceID[inc - 16]; // move is promotion
+ else if(inc < 0) to = 2*to - from; // move is castling, and 'to' indicates Rook; calculate King to-square
+ if(promo == '+' && promoCode[WHITE] & Z_FIDE) promo = 'q';
+ if(from%22 > 10) sprintf(buf, "%c@%c%d", pieces[dropType[from]-1&~COLOR], 'a'+(to%22), 1+(to/22)); // move is drop
+ else sprintf(buf, "%c%d%c%d%c", 'a'+(from%22), 1+(from/22), 'a'+(to%22), 1+(to/22), promo);
+ return buf;
+}
+
+int
+ParseMove(int stm, char *s)
+{
+ char prom = 0, f, f2, piece;
+ int m, r, r2, i, from;
+ if(sscanf(s, "%c@%c%d", &piece, &f, &r) == 3) { // drop
+ f -= 'a', r--;
+ m = 22*r + f;
+ for(i=0; pieces[i]; i++) if(pieces[i] == piece) break;
+ m += handSlot[COLOR - stm + i] << 8;
+ } else {
+ sscanf(s, "%c%d%c%d%c", &f2, &r2, &f, &r, &prom);
+ f -= 'a'; f2 -= 'a'; r--, r2--;
+ m = 22*r + f + ((from = 22*r2 + f2) << 8);
+ if(prom == '\n') prom = 0;
+ if(prom) { // promotion suffix
+ int in = (stm == WHITE ? -22 : 22); // inward board step
+ m += 11; // move to-square off-board on same rank (good for '+' and 'q')
+ switch(prom) { // under-promotions must encode choice by rank (they always occur on last rank)
+ case 'n': m += in; break;
+ case 'b': m += 2*in; break;
+ case 'r': m += 3*in; break;
+ }
+ } else if(board[from] == stm + 31 && (f - f2 > 1 || f2 - f > 1)) { // castling
+ m = 22*8+10 + (f > f2 ? 0 : 11) + 22*(r2 == 7) + (from << 8);
+ } else if(board[from] == stm) { // Pawn moves
+ if(!(r - r2 & 1)) m += 5*22; // steps even nr of ranks, so must be double push
+ else if(f - f2 && !board[22*r+f]) m += 22*((r^1) - r + 5) + 11; // diagonal to empty: e.p.
+ }
+ }
+printf("# move = %08x\n", m);
+ return m;
+}
+
+// Some global variables that control your engine's behavior
+int ponder;
+int randomize;
+int resign; // engine-defined option
+int contemptFactor; // likewise
+
+#ifdef WIN32
+# include <windows.h>
+# define CPUtime 1000.*clock
+#else
+# include <sys/time.h>
+# include <sys/times.h>
+# include <unistd.h>
+ int GetTickCount() // with thanks to Tord
+ { struct timeval t;
+ gettimeofday(&t, NULL);
+ return t.tv_sec*1000 + t.tv_usec/1000;
+ }
+#endif
+
+int
+ReadClock (int start)
+{
+ static int startTime;
+ int t = GetTickCount();
+ if(start) startTime = t;
+ return t - startTime; // msec
+}
+
+int
+SetMemorySize (int n)
+{
+ static int oldSize;
+ if(n == oldSize) return 0; // nothing to do
+ oldSize = n; // remember current size
+ if(hashTable) free(hashTable); // throw away old table
+ for(hashMask = (1<<24)-1; hashMask*sizeof(HashEntry) > n*1024*1024; hashMask >>= 1); // round down nr of buckets to power of 2
+ hashTable = (HashEntry*) calloc(hashMask+4, sizeof(HashEntry));
+printf("# memory allocated\n");
+ return !hashTable; // return TRUE if alocation failed
+}
+
+int
+RootMakeMove(int move)
+{
+ int index;
+ // irreversibly adopt incrementally updated values from last move as new starting point
+ undoInfo.pstEval = -undoInfo.newEval; // (like we initialize new Stackframe in daughter node)
+ undoInfo.hashKey = undoInfo.newKey;
+ undoInfo.rights |= spoiler[undoInfo.fromSqr] | spoiler[undoInfo.toSqr];
+ undoInfo.checker = CK_NONE;
+ MakeMove(&undoInfo, move);
+ // store in game history hash table
+ index = (unsigned int)undoInfo.newKey >> 24 ^ stm << 2; // uses high byte of low (= hands-free) key
+ while(repKey[index] && (repKey[index] ^ (int)undoInfo.newKey) & 0x1FFFFF) index += 2; // find empty slot
+ repKey[index] = (int)undoInfo.newKey & 0x1FFFFF | undoInfo.newEval << 21; // remember position
+ stm ^= COLOR;
+ return 1;
+}
+
+void
+TakeBack (int n)
+{ // reset the game and then replay it to the desired point
+ int last;
+ stm = Setup(NULL); // uses FEN saved during previous Setup
+ last = moveNr - n; if(last < 0) last = 0;
+ for(moveNr=0; moveNr<last; moveNr++) RootMakeMove(gameMove[moveNr]);
+}
+
+void PrintResult(int stm, int score)
+{
+ if(score == 0) printf("1/2-1/2\n");
+ if(score > 0 && stm == WHITE || score < 0 && stm == BLACK) printf("1-0\n");
+ else printf("0-1\n");
+}
+
+int engineSide=NONE; // side played by engine
+int ponderMove;
+char inBuf[80];
+
+void
+ReadLine ()
+{
+ int i, c;
+ if(inBuf[0]) return; // buffer already holds a backlogged command;
+ for(i = 0; (c = getchar()) != EOF && (inBuf[i++] = c) != '\n'; );
+ inBuf[i] = 0;
+}
+
+int
+DoCommand (int searching)
+{
+ char command[80];
+
+ while(1) { // usually we break out of this loop after treating one command
+
+ ReadLine(); // read one line into inBuf (or retrieve backlogged)
+printf("# command: %s\n", inBuf);
+ if(!*inBuf) exit(0); // EOF, terminate
+ sscanf(inBuf, "%s", command); // extract the first word
+ *inBuf = 0; // and already mark the buffer as empty
+
+ // recognize and execute 'easy' commands, i.e those that can be executed during search
+ if(!strcmp(command, "quit")) { exit(0); } // exit immediately
+ if(!strcmp(command, "otim")) { continue; } // move will follow immediately, wait for it
+ if(!strcmp(command, "time")) { sscanf(inBuf+4, "%d", &timeLeft); continue; }
+ if(!strcmp(command, "easy")) { ponder = OFF; return 0; }
+ if(!strcmp(command, "hard")) { ponder = ON; return 0; }
+ if(!strcmp(command, "post")) { postThinking = ON; return 0; }
+ if(!strcmp(command, "nopost")) { postThinking = OFF;return 0; }
+ if(!strcmp(command, "random")) { randomize = ON; return 0; }
+ if(!strcmp(command, ".")) { return 0; } // periodic update request; ignore for now
+ if(!strcmp(command, "option")) { // setting of engine-define option; find out which
+ if(sscanf(inBuf+7, "Resign=%d", &resign) == 1) return 0;
+ if(sscanf(inBuf+7, "Contempt=%d", &contemptFactor) == 1) return 0;
+ return 1;
+ }
+
+ if(searching) {
+ if(!strcmp(command, "usermove")) { return 1; } // TODO during search we just test for ponder hit
+ *inBuf = *command; // backlog command (by repairing inBuf)
+ return 1; // and request search abort
+ }
+
+ // the following commands can (or need) only be done when not searching
+ if(!strcmp(command, "force")) { engineSide = NONE; return 1; }
+ if(!strcmp(command, "analyze")) { engineSide = ANALYZE; return 1; }
+ if(!strcmp(command, "exit")) { engineSide = NONE; return 1; }
+ if(!strcmp(command, "level")) {
+ int min, sec=0;
+ sscanf(inBuf, "level %d %d %d", &mps, &min, &inc) == 3 || // if this does not work, it must be min:sec format
+ sscanf(inBuf, "level %d %d:%d %d", &mps, &min, &sec, &inc);
+ timeControl = 60*min + sec; timePerMove = -1;
+ return 1;
+ }
+ if(!strcmp(command, "protover")){
+ printf("feature ping=1 setboard=1 colors=0 usermove=1 memory=1 debug=1 reuse=0 sigint=0 sigterm=0 myname=\"CrazyWa " VERSION "\"\n");
+ printf("feature variants=\"crazyhouse,shogi,minishogi,judkinshogi,torishogi,euroshogi,crazywa\"\n");
+ printf("feature option=\"Resign -check 0\"\n"); // example of an engine-defined option
+ printf("feature option=\"Contempt -spin 0 -200 200\"\n"); // and another one
+ printf("feature done=1\n");
+ return 1;
+ }
+ if(!strcmp(command, "sd")) { sscanf(inBuf+2, "%d", &maxDepth); return 1; }
+ if(!strcmp(command, "st")) { sscanf(inBuf+2, "%d", &timePerMove); return 1; }
+ if(!strcmp(command, "memory")) { if(SetMemorySize(atoi(inBuf+7))) printf("tellusererror Not enough memory\n"), exit(-1); return 1; }
+ if(!strcmp(command, "ping")) { printf("pong%s", inBuf+4); return 1; }
+// if(!strcmp(command, "")) { sscanf(inBuf, " %d", &); return 1; }
+ if(!strcmp(command, "new")) { engineSide = BLACK; stm = WHITE; maxDepth = MAXPLY; randomize = OFF; return 1; }
+ if(!strcmp(command, "variant")) { GameInit(inBuf + 8); Setup(startPos); return 1; }
+ if(!strcmp(command, "setboard")){ engineSide = NONE; stm = Setup(inBuf+9); return 1; }
+ if(!strcmp(command, "undo")) { TakeBack(1); return 1; }
+ if(!strcmp(command, "remove")) { TakeBack(2); return 1; }
+ if(!strcmp(command, "go")) { engineSide = stm; return 1; }
+ if(!strcmp(command, "hint")) { if(ponderMove != INVALID) printf("Hint: %s\n", MoveToText(ponderMove)); return 1; }
+ if(!strcmp(command, "book")) { return 1; }
+ // completely ignored commands:
+ if(!strcmp(command, "xboard")) { return 1; }
+ if(!strcmp(command, "computer")){ return 1; }
+ if(!strcmp(command, "name")) { return 1; }
+ if(!strcmp(command, "ics")) { return 1; }
+ if(!strcmp(command, "accepted")){ return 1; }
+ if(!strcmp(command, "rejected")){ return 1; }
+ if(!strcmp(command, "?")) { return 1; } // 'move now'
+ if(!strcmp(command, "p")) { Debug(); return 1; }
+
+ if(!strcmp(command, "b")) { PrintDBoard("board:", board, " ", 11); return 1; }
+ if(!strcmp(command, "")) { return 1; }
+ if(!strcmp(command, "usermove")){
+ int move = ParseMove(stm, inBuf+9);
+ if(move == INVALID) printf("Illegal move\n");
+ else if(!RootMakeMove(move)) printf("Illegal move\n");
+ else {
+ ponderMove = INVALID;
+ }
+ return 1;
+ }
+ printf("Error: unknown command\n");
+ }
+ return 0;
+}
+
+int
+main ()
+{
+ int score;
+
+ EngineInit(); SetMemorySize(1); // reserve minimal hash to prevent crash if GUI sends no 'memory' command
+ GameInit("zh"); Setup(startPos); // to facilitate debugging from command line
+
+ while(1) { // infinite loop
+
+ fflush(stdout); // make sure everything is printed before we do something that might take time
+
+ if(stm == engineSide) { // if it is the engine's turn to move, set it thinking, and let it move
+
+ nodeCount = forceMove = undoInfo.move = abortFlag = 0; ReadClock(1);
+ score = Search(stm^COLOR, -INF, INF, &undoInfo, maxDepth, 0, maxDepth);
+
+ if(!undoInfo.move) { // no move, game apparently ended
+ engineSide = NONE; // so stop playing
+ PrintResult(stm, score);
+ } else {
+ RootMakeMove(undoInfo.move); // perform chosen move (stores it in lastGameMove and changes stm)
+ printf("move %s\n", MoveToText(undoInfo.move)); // and output it
+ }
+ }
+
+ fflush(stdout); // make sure everything is printed before we do something that might take time
+#if 0
+ // now it is not our turn (anymore)
+ if(engineSide == ANALYZE) { // in analysis, we always ponder the position
+ PonderUntilInput(stm);
+ } else
+ if(engineSide != NONE && ponder == ON && moveNr != 0) { // ponder while waiting for input
+ if(ponderMove == INVALID) { // if we have no move to ponder on, ponder the position
+ PonderUntilInput(stm);
+ } else {
+ int newStm = MakeMove(stm, ponderMove);
+ PonderUntilInput(newStm);
+ UnMake(ponderMove);
+ }
+ }
+#endif
+ DoCommand(0);
+ }
+ return 0;
+}