Initial version of engine
authorH.G.Muller <hgm@hgm-xboard.(none)>
Fri, 21 Oct 2016 12:38:05 +0000 (14:38 +0200)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Fri, 21 Oct 2016 12:47:05 +0000 (14:47 +0200)
Crazyhouse sort of works here.

dropper.c [new file with mode: 0644]

diff --git a/dropper.c b/dropper.c
new file mode 100644 (file)
index 0000000..2c5fbae
--- /dev/null
+++ b/dropper.c
@@ -0,0 +1,1559 @@
+#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;
+}