Version 0.16: King neighborhood, Kylin promotion eval terms
[hachu.git] / hachu.c
diff --git a/hachu.c b/hachu.c
index 41af7d7..51beb0e 100644 (file)
--- a/hachu.c
+++ b/hachu.c
@@ -11,7 +11,7 @@
 // promotions by pieces with Lion power stepping in & out the zone in same turn\r
 // promotion on capture\r
 \r
-#define VERSION "0.14e"\r
+#define VERSION "0.16 k+ky"\r
 \r
 #define PATH level==0 /*|| path[0] == 0x3490a &&  (level==1 || path[1] == 0x285b3 && (level == 2 || path[2] == 0x8710f && (level == 3 /*|| path[3] == 0x3e865 && (level == 4 || path[4] == 0x4b865 && (level == 5)))))*/\r
 //define PATH 0\r
 #define LIONTRAP\r
 #define WINGS\r
 #define KINGSAFETY\r
+#define KSHIELD\r
 #define XFORTRESS\r
 #define PAWNBLOCK\r
+#define KYLIN 100 /* extra end-game value of Kylin for promotability */\r
+#define PROMO 0 /* extra bonus for 'vertical' piece when it actually promotes (diagonal pieces get half) */\r
 \r
 #include <stdio.h>\r
 #include <stdlib.h>\r
@@ -462,6 +465,7 @@ Vector direction[] = { // clockwise!
 \r
 int epList[96], ep2List[96], toList[96], reverse[96];  // decoding tables for double and triple moves\r
 int kingStep[10], knightStep[10];         // raw tables for step vectors (indexed as -1 .. 8)\r
+int neighbors[9];   // similar to kingStep, but starts with null-step\r
 char fireFlags[10]; // flags for Fire-Demon presence (last two are dummies, which stay 0, for compactify)\r
 #define kStep (kingStep+1)\r
 #define nStep (knightStep+1)\r
@@ -582,7 +586,7 @@ int attackMaps[200*BSIZE], *attacks = attackMaps;
 char distance[2*BSIZE]; // distance table\r
 char promoBoard[BSIZE]; // flags to indicate promotion zones\r
 char rawFire[BSIZE+2*BWMAX]; // flags to indicate squares controlled by Fire Demons\r
-signed char PST[4*BSIZE];\r
+signed char PST[5*BSIZE];\r
 \r
 #define board     (rawBoard + 6*BHMAX + 3)\r
 #define fireBoard (rawFire + BWMAX + 1)\r
@@ -667,7 +671,9 @@ int
 EasyProm (signed char *r)\r
 {\r
   int i;\r
-  return r[0] == X || r[1] == X || r[7] == X;\r
+  if(r[0] == X) return 30 + PROMO*((unsigned int)(r[1] | r[2] | r[3] | r[5] | r[6] | r[7]) <= 1);\r
+  if(r[1] == X || r[7] == X) return 30 + PROMO/2;\r
+  return 0;\r
 }\r
 \r
 int\r
@@ -829,9 +835,11 @@ SetUp(char *array, int var)
   }\r
   for(i=0; i<BH; i++) for(j=0; j<BH; j++) board[BW*i+j] = EMPTY;\r
   for(i=WHITE+2; i<=last[WHITE]; i+=2) if(p[i].pos != ABSENT) {\r
-    if((j = p[i].promo) > 0 && p[i].promoGain)\r
-      p[i].promoGain = (p[j].value - p[i].value - 30)*1.25, p[i].value = p[j].value - 30;\r
+    int g = p[i].promoGain;\r
+    if((j = p[i].promo) > 0 && g)\r
+      p[i].promoGain = (p[j].value - p[i].value - g)*1.25, p[i].value = p[j].value - g;\r
     else p[i].promoGain = 0;\r
+    if(i == kylin[WHITE]) p[i].promoGain = 1.25*KYLIN, p[i].value += KYLIN;\r
     if(j > 0 && p[i].pst == BH) p[i].pst = 3*BW*BH;      // use white pre-prom bonus\r
     board[p[i].pos] = i;\r
     rootEval += p[i].value + PST[p[i].pst + p[i].pos];\r
@@ -839,10 +847,13 @@ SetUp(char *array, int var)
     filling += p[i].bulk;\r
   } else p[i].promoGain = 0;\r
   for(i=BLACK+2; i<=last[BLACK]; i+=2) if(p[i].pos != ABSENT) {\r
-    if((j = p[i].promo) > 0 && p[i].promoGain)\r
-      p[i].promoGain = (p[j].value - p[i].value - 30)*1.25, p[i].value = p[j].value - 30;\r
+    int g = p[i].promoGain;\r
+    if((j = p[i].promo) > 0 && g)\r
+      p[i].promoGain = (p[j].value - p[i].value - g)*1.25, p[i].value = p[j].value - g;\r
     else p[i].promoGain = 0;\r
+    if(i == kylin[BLACK]) p[i].promoGain = 1.25*KYLIN, p[i].value += KYLIN;\r
     if(j > 0 && p[i].pst == BH) p[i].pst = 3*BW*BH + BH; // use black pre-prom bonus\r
+    if(j > 0 && p[i].pst == bsize) p[i].pst = 4*BW*BH;      // use white pre-prom bonus\r
     board[p[i].pos] = i;\r
     rootEval -= p[i].value + PST[p[i].pst + p[i].pos];\r
     promoDelta -= p[i].promoGain;\r
@@ -879,6 +890,7 @@ Init (int var)
     kStep[i] = STEP(direction[i&7].x,   direction[i&7].y);       // King\r
     nStep[i] = STEP(direction[(i&7)+8].x, direction[(i&7)+8].y); // Knight\r
   }\r
+  for(i=0; i<8; i++) neighbors[i+1] = kStep[i];\r
 \r
   for(i=0; i<8; i++) { // Lion double-move decoding tables\r
     for(j=0; j<8; j++) {\r
@@ -927,16 +939,23 @@ Init (int var)
   for(j=0; j<BH; j++) {\r
    for(i=0; i<BH; i++) {\r
     int s = BW*i + j, d = BH*(BH-2) - abs(2*i - BH + 1)*(BH-1) - (2*j - BH + 1)*(2*j - BH + 1);\r
-    PST[s] = 2*(i==0 | i==BH-1) + (i==1 | i==BH-2); // last-rank markers in null table\r
-    PST[BH+s] = d/4 - (i == 0 || i == BH-1 ? 5 : 0) - (j == 0 || j == BH-1 ? 5 : 0)\r
-                    + 2*(i==zone || i==BH-zone-1);  // stepper centralization\r
-    PST[BH*BW+s] = d/6;                             // double-stepper centralization\r
-    PST[BH*BW+BH+s] = d/12;                         // slider centralization\r
+    PST[s] = 2*(i==0 | i==BH-1) + (i==1 | i==BH-2);      // last-rank markers in null table\r
+    PST[BH+s] = d/4 - (i < 2 || i > BH-3 ? 3 : 0) - (j == 0 || j == BH-1 ? 5 : 0)\r
+                    + 3*(i==zone || i==BH-zone-1);       // stepper centralization\r
+    PST[BH*BW+s] = d/6;                                  // double-stepper centralization\r
+    PST[BH*BW+BH+s] = d/12 - 5*(i==BH/2 || i==(BH-1)/2); // slider centralization\r
     PST[2*BH*BW+s] = j < 3 || j > BH-4 ? (i < 3 ? 7 : i == 3 ? 4 : i == 4 ? 2 : 0) : 0;\r
     PST[2*BH*BW+BH+s] = ((BH-1)*(BH-1) - (2*i - BH + 1)*(2*i - BH + 1) - (2*j - BH + 1)*(2*j - BH + 1))/6;\r
-    PST[3*BH*BW+s] = PST[3*BH*BW+BH+s] = PST[BH+s]; // as stepper, but with pre-promotion bonus W/B\r
+    PST[3*BH*BW+s] = PST[3*BH*BW+BH+s] = PST[BH+s];      // as stepper, but with pre-promotion bonus W/B\r
+    PST[4*BH*BW+s] = PST[BW*BH+s];                       // as jumper, but with pre-promotion bonus B\r
+    PST[4*BH*BW+BH+s] = BW*(zone - 1 - i);               // board step to enter promo zone black\r
    }\r
-   if(zone > 1) PST[3*BW*BH+BW*(BH-1-zone) + j] += 10, PST[3*BW*BH+BH + BW*zone + j] += 10;\r
+   if(zone > 0) PST[3*BW*BH+BW*(BH-1-zone) + j] += 10, PST[3*BW*BH+BH + BW*zone + j] += 10;\r
+#if KYLIN\r
+   // pre-promotion bonuses for jumpers\r
+   if(zone > 0) PST[BW*BH + BW*(BH-2-zone) + j] = PST[4*BW*BH + BW*(zone+1) + j] = 100,\r
+                PST[BW*BH + BW*(BH-1-zone) + j] = PST[4*BW*BH + BW*zone + j] = 200;\r
+#endif\r
   }\r
 \r
   p[EDGE].qval = 5; // tenjiku jump-capturer sentinel\r
@@ -1717,6 +1736,20 @@ Fortress (int forward, int king, int lion)
 }\r
 \r
 int\r
+Surround (int stm, int king, int start)\r
+{\r
+  int i, s=0;\r
+  for(i=start; i<9; i++) {\r
+    int v, piece, sq = king + neighbors[i];\r
+    if((piece = board[sq]) == EDGE || !piece || piece&1^stm) continue;\r
+    if(p[piece].promoGain) continue;\r
+    v = p[piece].value;\r
+    s += -(v > 70) & v;\r
+  }\r
+  return (s > 512 ? 512 : s);\r
+}\r
+\r
+int\r
 Ftest (int side)\r
 {\r
   int lion = ABSENT, king;\r
@@ -1779,27 +1812,46 @@ Evaluate (int difEval)
   if(wLion != ABSENT) f -= Fortress(-BW, bKing, wLion);\r
   score += (filling < 96 ? f : f*(224 - filling) >> 7); // build up slowly\r
 # endif\r
+\r
+# ifdef KSHIELD\r
+  score += Surround(WHITE, wKing, 1) - Surround(BLACK, bKing, 1) >> 3;\r
+# endif\r
+\r
 #endif\r
 \r
-#ifdef KYLIN\r
-  // bonus for having Kylin in late end-game, where it could promote to Lion\r
-  if((filling < 64) {\r
-    if((wLion = kylin[WHITE]) && p[wLion].pos != ABSENT)\r
-      score += (64 - filling)*kylinProm[wLion];\r
-    if((filling < 64 && (bLion = kylin[BLACK]) && p[bLion].pos != ABSENT)\r
-      score -= (64 - filling)*kylinProm[bLion];\r
+#if KYLIN\r
+  // bonus for having Kylin in end-game, where it could promote to Lion\r
+  // depends on board population, defenders around zone entry and proximity to zone\r
+  if(filling < 128) {\r
+    int sq;\r
+    if((wLion = kylin[WHITE]) && (sq = p[wLion].pos) != ABSENT) {\r
+      int anchor = sq - PST[5*BW*BH - 1 - sq];\r
+      score += (512 - Surround(BLACK, anchor, 0))*(128 - filling)*PST[p[wLion].pst + sq] >> 15;\r
+    }\r
+    if((bLion = kylin[BLACK]) && (sq = p[bLion].pos) != ABSENT) {\r
+      int anchor = sq + PST[4*BW*BH + BH + sq];\r
+      score -= (512 - Surround(WHITE, anchor, 0))*(128 - filling)*PST[p[bLion].pst + sq] >> 15;\r
+    }\r
   }\r
 #endif\r
 \r
 #ifdef PAWNBLOCK\r
   // penalty for blocking own P or GB: 20 by slider, 10 by other, but 50 if only retreat mode is straight back\r
   for(i=last[WHITE]; i > 1 && p[i].value<=50; i-=2) {\r
-    if((f = p[i].pos) != ABSENT && (j = board[f + BW])&1) // P present, square before it white (odd) piece\r
-      score -= 10 + 10*(p[j].promoGain > 0) + 30*!(p[j].range[3] || p[j].range[5] || p[j].value==50);\r
+    if((f = p[i].pos) != ABSENT) { // P present,\r
+      if((j = board[f + BW])&1) // square before it white (odd) piece\r
+       score -= 10 + 10*(p[j].promoGain > 0) + 30*!(p[j].range[3] || p[j].range[5] || p[j].value==50);\r
+      if((j = board[f - BW])&1) // square behind it white (odd) piece\r
+       score += 7*(p[j].promoGain == 0 & p[j].value<=151);\r
+    }\r
   }\r
   for(i=last[BLACK]; i > 1 && p[i].value<=50; i-=2) {\r
-    if((f = p[i].pos) != ABSENT && (j = board[f - BW]) && !(j&1)) // P present, square before non-empty and even (black)\r
-      score += 10 + 10*(p[j].promoGain > 0) + 30*!(p[j].range[1] || p[j].range[7] || p[j].value==50);\r
+    if((f = p[i].pos) != ABSENT) { // P present,\r
+      if((j = board[f - BW]) && !(j&1)) // square before non-empty and even (black)\r
+       score += 10 + 10*(p[j].promoGain > 0) + 30*!(p[j].range[1] || p[j].range[7] || p[j].value==50);\r
+      if((j = board[f + BW]) && !(j&1)) // square behind non-empty and even (black)\r
+       score -= 7*(p[j].promoGain == 0 & p[j].value<=151);\r
+    }\r
   }\r
 #endif\r
 \r
@@ -2349,7 +2401,8 @@ Convert (char *fen)
     if(isalpha(*fen)) {\r
       char *table = fenNames;\r
       n = *fen > 'Z' ? 'a' - 'A' : 0;\r
-      if((currentVariant == V_CHESS || currentVariant == V_SHATRANJ || currentVariant == V_MAKRUK) && *fen - n == 'N' // In Chess N is Knight, not Lion\r
+      if((currentVariant == V_CHESS || currentVariant == V_SHATRANJ ||\r
+          currentVariant == V_MAKRUK || currentVariant == V_SHO) && *fen - n == 'N' // In Chess N is Knight, not Lion\r
            || table[2* (*fen - 'A' - n)] == '.') *p++ = *fen; else {\r
         *p++ = ':';\r
         *p++ = table[2* (*fen - 'A' - n)] + n;\r
@@ -2515,8 +2568,13 @@ Highlight(char *coords)
   for(i=listStart; i<listEnd; i++) {\r
     if(sqr == (moveStack[i]>>SQLEN & SQUARE)) {\r
       int t = moveStack[i] & SQUARE;\r
-      if(t >= SPECIAL) continue;\r
-      b[t] = (!boardCopy[t] ? 'Y' : 'R'); cnt++;\r
+      if(t >= SPECIAL) {\r
+       int e = sqr + epList[t - SPECIAL]; // decode\r
+       b[e] = 'C';\r
+       continue;\r
+      }\r
+      if(!b[t]) b[t] = (!boardCopy[t] ? 'Y' : 'R'); cnt++;\r
+      if(moveStack[i] & PROMOTE) b[t] = 'M';\r
     }\r
   }\r
   if(!cnt) { // no moves from given square\r
@@ -2594,9 +2652,9 @@ printf("# ponder=%s\n", MoveToText(pv[1],0));
     int TakeBack(int n)\r
     { // reset the game and then replay it to the desired point\r
       int last, stm;\r
+      last = moveNr - n; if(last < 0) last = 0;\r
       Init(currentVariant); stm = Setup2(startPos);\r
 printf("# setup done");fflush(stdout);\r
-      last = moveNr - n; if(last < 0) last = 0;\r
       for(moveNr=0; moveNr<last; moveNr++) stm = MakeMove2(stm, gameMove[moveNr]),printf("make %2d: %x\n", moveNr, gameMove[moveNr]);\r
       return stm;\r
     }\r