Add null move
[hachu.git] / hachu.c
diff --git a/hachu.c b/hachu.c
index 5b93431..fd6cbfe 100644 (file)
--- a/hachu.c
+++ b/hachu.c
@@ -46,7 +46,7 @@
 #define EMPTY      0\r
 #define EDGE   (1<<11)\r
 #define TYPE   (WHITE|BLACK|EDGE)\r
-#define ABSENT  4095\r
+#define ABSENT  2047\r
 #define INF     8000\r
 #define NPIECES 2000               /* length of piece list    */\r
 \r
@@ -82,12 +82,12 @@ typedef struct {
 } PieceDesc;\r
 \r
 typedef struct {\r
-  int from, to, piece, victim, new, booty, epSquare, epVictim, ep2Square, ep2Victim, savKeyL, savKeyH;\r
+  int from, to, piece, victim, new, booty, epSquare, epVictim, ep2Square, ep2Victim, revMoveCount, savKeyL, savKeyH;\r
 } UndoInfo;\r
 \r
-int stm, xstm, hashKeyH, hashKeyL, framePtr, msp, nonCapts, rootEval, retMSP, retFirst, pvPtr, level, chuFlag=1;\r
+int stm, xstm, hashKeyH, hashKeyL, framePtr, msp, nonCapts, rootEval, retMSP, retFirst, pvPtr, level, cnt50, chuFlag=1;\r
 int nodes, startTime, tlim1, tlim2;\r
-Move retMove, moveStack[10000], path[100], pv[1000];\r
+Move retMove, moveStack[10000], path[100], repStack[300], pv[1000];\r
 \r
 #define X 36 /* slider              */\r
 #define J -1 /* jump                */\r
@@ -1004,6 +1004,7 @@ MakeMove(Move m, UndoInfo *u)
   u->piece = board[u->from];\r
   board[u->from] = EMPTY;\r
   u->booty = 0;\r
+  u->revMoveCount = cnt50++;\r
   u->savKeyL = hashKeyL;\r
   u->savKeyH = hashKeyH;\r
 \r
@@ -1015,6 +1016,7 @@ MakeMove(Move m, UndoInfo *u)
       p[u->piece].pos = ABSENT;\r
       u->new = p[u->piece].promo;\r
       u->booty = p[u->new].value - p[u->piece].value;\r
+      cnt50 = 0; // promotion irreversible\r
     }\r
   } else u->new = u->piece;\r
 \r
@@ -1040,12 +1042,13 @@ MakeMove(Move m, UndoInfo *u)
     hashKeyL ^= p[u->ep2Victim].pieceKey * squareKey[u->ep2Square];\r
     hashKeyH ^= p[u->ep2Victim].pieceKey * squareKey[u->ep2Square+BH];\r
     if(p[u->piece].value != 1000 && p[u->epVictim].value == 1000) deferred |= PROMOTE; // flag non-Lion x Lion\r
+    cnt50 = 0; // double capture irreversible\r
   } else u->epVictim = EMPTY;\r
 \r
   u->victim = board[u->to];\r
   p[u->victim].pos = ABSENT;\r
   u->booty += p[u->victim].value + PST[p[u->victim].pst + u->to];\r
-//  if(p[u->victim].value == 1000 && p[u->piece].value != 1000) deferred |= PROMOTE; // flag non-Lion x Lion\r
+  if(u->victim != EMPTY) cnt50 = 0; // capture irreversible\r
 \r
   p[u->new].pos = u->to;\r
   board[u->to] = u->new;\r
@@ -1077,6 +1080,7 @@ UnMake(UndoInfo *u)
   p[u->piece].pos = u->from; // this can be the same as above\r
   board[u->from] = u->piece;\r
 \r
+  cnt50 = u->revMoveCount;\r
   hashKeyL = u->savKeyL;\r
   hashKeyH = u->savKeyH;\r
 }\r
@@ -1285,6 +1289,11 @@ if(flag && depth>= 0) printf("phase=%d: first/curr/last = %d / %d / %d\n", phase
            break;\r
          case 7: // bad captures\r
          case 8: // PV null move\r
+           phase = 9;\r
+           if(nullMove != ABSENT) {\r
+             moveStack[msp++] = nullMove + (nullMove << SQLEN) | DEFER; // kludge: setting DEFER guarantees != 0, and has no effect\r
+           }\r
+printf("# %d. sqr = %08x null = %08x\n", msp, nullMove, moveStack[msp-1]);\r
          case 9:\r
            goto cutoff;\r
        }\r
@@ -1304,10 +1313,18 @@ if(flag & depth >= 0) printf("%2d:%d extract %d/%d\n", depth, iterDep, curMove,
        if(move == 0) continue; // skip invalidated move\r
       }\r
 if(flag & depth >= 0) printf("%2d:%d found %d/%d\n", depth, iterDep, curMove, msp);\r
+\r
       // RECURSION\r
       stm ^= WHITE;\r
-      defer = MakeMove(moveStack[curMove], &tb);\r
-path[level++] = moveStack[curMove];\r
+      defer = MakeMove(move, &tb);\r
+\r
+      for(i=2; i<=cnt50; i+=2) if(repStack[level-i+200] == hashKeyH) {\r
+       moveStack[curMove] = 0; // erase forbidden move\r
+       score = -INF; goto repetition;\r
+      }\r
+      repStack[level+200] = hashKeyH;\r
+\r
+path[level++] = move;\r
 attacks += 2*BSIZE;\r
 MapFromScratch(attacks); // for as long as incremental update does not work.\r
 if(PATH) pmap(attacks, stm);\r
@@ -1320,7 +1337,7 @@ if(PATH) pmap(attacks, stm);
          if(promoSuppress & PROMOTE) score = -INF; // non-Lion captures Lion after opponent did same\r
          defer |= PROMOTE;                         // if we started, flag  he cannot do it in reply\r
        }\r
-        if(score == -INF) { moveStack[curMove] = 0; goto abortMove; }\r
+        if(score == -INF) { moveStack[curMove] = 0; goto abortMove; } // zap illegal moves\r
       }\r
 #if 1\r
       score = -Search(-beta, -iterAlpha, -difEval - tb.booty, replyDep, promoSuppress & ~PROMOTE, defer);\r
@@ -1328,14 +1345,14 @@ if(PATH) pmap(attacks, stm);
       score = 0;\r
 #endif\r
     abortMove:\r
-\r
-\r
 attacks -= 2*BSIZE;\r
 level--;\r
+    repetition:\r
       UnMake(&tb);\r
       xstm = stm; stm ^= WHITE;\r
 #if 1\r
 if(PATH) printf("%d:%2d:%d %3d %6x %-10s %6d %6d\n", level, depth, iterDep, curMove, moveStack[curMove], MoveToText(moveStack[curMove], 0), score, bestScore);\r
+\r
       // ALPHA-BETA STUFF\r
       if(score > bestScore) {\r
        bestScore = score; bestMoveNr = curMove;\r
@@ -1482,17 +1499,23 @@ int lastLift, lastPut;
 int\r
 MakeMove2 (int stm, MOVE move)\r
 {\r
+  int i;\r
   sup0 = sup1; sup1 = sup2;\r
   sup2 = MakeMove(move, &undoInfo);\r
   rootEval = -rootEval - undoInfo.booty;\r
+  for(i=0; i<200; i++) repStack[i] = repStack[i+1];\r
+  repStack[199] = hashKeyH;\r
+printf("# makemove %08x %c%d %c%d\n", move, sup1%BW+'a', sup1/BW, sup2%BW+'a', sup2/BW);\r
   return stm ^ WHITE;\r
 }\r
 \r
 void\r
 UnMake2 (MOVE move)\r
 {\r
+  int i;\r
   rootEval = -rootEval - undoInfo.booty;\r
   UnMake(&undoInfo);\r
+  for(i=200; i>0; i--) repStack[i] = repStack[i-1];\r
   sup2 = sup1; sup1 = sup0;\r
 }\r
 \r
@@ -1501,7 +1524,7 @@ Setup2 (char *fen)
 {\r
   SetUp(chuArray, chuPieces);\r
   sup0 = sup1 = sup2 = ABSENT;\r
-  rootEval = hashKeyH = hashKeyL = 0;\r
+  rootEval = cnt50 = hashKeyH = hashKeyL = 0;\r
   return WHITE;\r
 }\r
 \r
@@ -1515,6 +1538,7 @@ MoveToText (MOVE move, int multiLine)
 {\r
   static char buf[50];\r
   int f = move>>SQLEN & SQUARE, g = f, t = move & SQUARE;\r
+  if(f == t) { sprintf(buf, "@@@@"); return buf; } // null-move notation in WB protocol\r
   buf[0] = '\0';\r
   if(t >= SPECIAL) { // kludgy! Print as side effect non-standard WB command to remove victims from double-capture (breaks hint command!)\r
     int e = f + epList[t - SPECIAL];\r
@@ -1547,6 +1571,7 @@ MOVE
 ParseMove (char *moveText)\r
 {\r
   int i, j, f, t, t2, e, ret, deferred=0;\r
+  char c = moveText[0];\r
   moveText += ReadSquare(moveText, &f);\r
   moveText += ReadSquare(moveText, &t); t2 = t;\r
   if(*moveText == ',') {\r
@@ -1563,9 +1588,12 @@ ParseMove (char *moveText)
   }\r
   ret = f<<SQLEN | t2;\r
   if(*moveText == '+') ret |= PROMOTE;\r
+printf("# suppress = %c%d\n", sup1%BW+'a', sup1/BW);\r
 MapFromScratch(attacks);\r
   Search(-INF-1, INF+1, 0, 1, sup1, sup2);\r
   for(i=retFirst; i<retMSP; i++) {\r
+    if(moveStack[i] == INVALID) continue;\r
+    if(c == '@' && (moveStack[i] & SQUARE) == (moveStack[i] >> SQLEN & SQUARE)) break; // any null move matches @@@@\r
     if((moveStack[i] & (PROMOTE | DEFER-1)) == ret) break;\r
     if((moveStack[i] & DEFER-1) == ret) deferred = i; // promoted version of entered non-promotion is legal\r
   }\r