Implement Kyoto Shogi
authorH.G.Muller <hgm@hgm-xboard.(none)>
Tue, 10 Apr 2018 17:25:24 +0000 (19:25 +0200)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Tue, 10 Apr 2018 17:25:24 +0000 (19:25 +0200)
Previous commits have created all infra-structure needed to handle
Kyoto Shogi. This patch adds Kyoto Shogi to the list of variants,
plus the required info (setup FEN, piece IDs and moves, pieceToCharTable).
The strongest member of each alternating pair is defined as the promoted
piece, so that P, L and N will be subjected to the drop restrictions
(applied only to unpromoted pieces). As usual P and L will be piece 0 and 1,
while N will be the last unpromoted piece. A compacted version of the
piece-square data is included as initialized data, as the non-ergodic pieces
require very unusual and specific tables. This data is used to create a
separate PST for each piece type. (Two 5x5 tables packed into one normal
11x11 space to not exceed the normal number of tables.) The (common) promoGain
of all hand pieces had to be set to a non-zero value in order to prevent
overflow in the had PST, as the difference between hand value and value of
the unpromoted piece is unusually large (as promoted dropping is possible).

dropper.c

index 6ec2a14..8bfb7e4 100644 (file)
--- a/dropper.c
+++ b/dropper.c
@@ -300,6 +300,7 @@ shogiValues[]   = { 30, 150, 240, 270, 390, 450, 180, 200, -1, 330, 288, 276, 27
 miniValues[]    = { 60, 110, 195, 237, 243, 330,      -1, 297, 245, 240, 237, 375, 420,      -1, 120, 200, 243, 252, 315, 390, -1 },
 judkinValues[]  = { 50, 115, 240, 270, 325, 390, 180, -1, 330, 245, 276, 270, 420, 480, 282, -1,  60, 220, 300, 330, 450, 510, 285, -1 },
 toriValues[]  = { 60, 100, 150, 150, 237, 300, -1, 90, 0, 0, 0, 0, 0, 500, -1, 65, 150, 200, 200, 300, 400, -1 },
+kyotoValues[] = { 115, 117, 304, 100, -1, 275, 198, 346, 172, -1, 360, 317, 378, 301, -1 },
 waValues[]  = { 30, 210, 210, 180, 210, 210, 210, 240, 270, 160, 175, 270, 300, 360, 540, 480, -1,
                330, 360, 480, 360, 240, 270, 270, 300, 330, 330, 270, 360, 540, 540, -1,
                60, 240, 300, 270, 255, 270, 240, 285, 330, 270, 285, 345, 480, 480, 630, 480, -1 };
@@ -310,6 +311,7 @@ shogiDirs[] = { 40, 69, 97, 87, 57, 35, 26, 186, 227, 255, 57, 57, 57, 57, 31, 2
                40, 62, 94, 43, 64, 35, 26, 10, 232, 255, 64, 64, 64, 64, 31, 22, 64, 57, 255 },
 toriDirs[]  = { 40, 69, 189, 197, 201, 42, 79, 255, 112, 0, 0, 0, 0, 168, 255,   // Ph, S, Pt, Ql, Qr, Cr, Fa / G - - - - Ea
                 40, 62, 193, 205, 209, 42, 71, 255, 108, 0, 0, 0, 0, 177, 255 },
+kyotoDirs[] = { 40, 69, 97, 87, 186, 255, 26, 57, 35, 57, 255, 40, 62, 94, 43, 10, 255, 26, 64, 35, 64, 255 }, // K,P,L,S,N / R,T,B,G
 waDirs[] = { 40, 69, 97, 67, 67, 74, 59, 59, 73, 87, 57, 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, 40,135,150,213, 87,116, 57, 79, 40,  8,  26,  99, 141, 255,          //    GB,FF,CE,RF,VS,SW,VW,RB,BE,PO,HH,GS,Tr,TF
              40, 62, 94, 60, 60, 82, 66, 66, 81, 43, 64, 93, 116, 128,  34, 99, 159, 255,
@@ -320,6 +322,7 @@ chessIDs[] = "PNBRQ",
 shogiIDs[] = "PLSGBRN",
 euroIDs[]  = "PLSGBR?N",
 toriIDs[]  = "SPLRCF",
+kyotoIDs[] = "PLSN............RTBG", // R, T, B, G are synonyms for +P, +L, +S and +N (written and recognized in drop moves)
 waIDs[]    = "POULCMGDVWHSRFXE",
 
 chessFEN[] = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -",
@@ -329,6 +332,7 @@ waFEN[]    = "hmlcvkwgudo/1e3s3f1/ppprpppxppp/3p3p3/11/11/11/3P3P3/PPPXPPPRPPP/1
 miniFEN[]  = "rbsgk/4p/5/P4/KGSBR w",
 judkinFEN[]= "rbnsgk/5p/6/6/P5/KGSNBR w",
 euroFEN[]  = "1nbgkgn1/1r4b1/pppppppp/8/8/PPPPPPPP/1B4R1/1NGKGBN1 w",
+kyotoFEN[] = "pgkst/5/5/5/TSKGP w",
 
 // promotion codes for unpromoted pieces. Will be ANDed with Z_WHITE or Z_BLACK to fill promoCode[] table
 
@@ -342,12 +346,14 @@ euroFEN[]  = "1nbgkgn1/1r4b1/pppppppp/8/8/PPPPPPPP/1B4R1/1NGKGBN1 w",
 chessProms[16] = { Z_MUST },
 shogiProms[16] = { Z_MUST, Z_2ND, COLOR, 0, Z_MUST, Z_MUST, Z_2ND, Z_2ND },
 toriProms[16]  = { Z_MUST, 0, 0, 0, 0, Z_MUST },
+kyotoProms[16] = { Z_MUST, Z_MUST, Z_MUST, Z_MUST },
 waProms[16]    = { Z_MUST, Z_2ND, Z_MUST, Z_MUST, Z_MUST, COLOR, COLOR, Z_MUST, Z_MUST, Z_MUST, COLOR, Z_MUST, COLOR, Z_MUST };
 
-int lances[] = { 1, 3, 0103, 0103, 1, 3, 3, 1 }; // bitmap indicating piece types with drop restriction (LSB = Pawn) in various variants
+int lances[] = { 1, 013, 3, 0103, 0103, 1, 3, 3, 1 }; // bitmap indicating piece types with drop restriction (LSB = Pawn) in various variants
 
 char *betza[] = { // piece defs for sending to GUI
   NULL, // suppresses setup command
+  NULL,
   "",
   "",
   NULL,
@@ -360,6 +366,7 @@ char *betza[] = { // piece defs for sending to GUI
 
 char *ptc[] = { // XBoard 4.9 piece-to-char table
   NULL,
+  NULL,
   "P.BR.S...G.+.++.+Kp.br.s...g.+.++.+k",
   "PNBR.S...G.++++.+Kpnbr.s...g.++++.+k",
   NULL,
@@ -372,6 +379,7 @@ char *ptc[] = { // XBoard 4.9 piece-to-char table
 
 char *pstType[] = {
   "",
+  "",
   "307716 777718",
   "3077160 7777187",
   "3077160 7777187",
@@ -391,6 +399,7 @@ typedef struct {
 
 VariantDesc variants[] = {
   {  8,  8,  5, 1, "crazyhouse\n",  chessIDs, chessFEN,  chessDirs, chessProms, chessCodes, chessValues },
+  {  5,  5,  4, 5, "kyotoshogi\n",  kyotoIDs, kyotoFEN,  kyotoDirs, kyotoProms, shogiCodes, kyotoValues },
   {  5,  5,  5, 1, "minishogi\n",   shogiIDs, miniFEN,   shogiDirs, shogiProms, shogiCodes, miniValues },
   {  6,  6,  6, 2, "judkinshogi\n", shogiIDs, judkinFEN, shogiDirs, shogiProms, shogiCodes, judkinValues },
   {  9,  9,  7, 3, "shogi\n",       shogiIDs, shogiFEN,  shogiDirs, shogiProms, shogiCodes, shogiValues },
@@ -400,9 +409,10 @@ VariantDesc variants[] = {
   {  7,  7,  6, 2, "torishogi\n",   toriIDs,  toriFEN,   toriDirs,  toriProms,  toriCodes,  toriValues  },
 };
 
-#define TORI_NR  7 /* must correspond to index of torishogi (which must be last) in table above! */
-#define MINI_NR  1
-#define KNIGHTLESS 4  /* first variant without a Knight (except mini-, which has no 2-rank zone) */
+#define TORI_NR  8 /* must correspond to index of torishogi (which must be last) in table above! */
+#define KYOTO_NR 1
+#define MINI_NR  2
+#define KNIGHTLESS 5  /* first variant without a Knight (except mini-, which has no 2-rank zone) */
 
 // info per piece type. sometimes indexed by negated holdings count instead of piece
 #define pieceKey   (rawKey+1)
@@ -424,6 +434,17 @@ unsigned int squareKey[22*11];
 
 signed char pstData[22*11*9];  // actual tables (for now 9 pairs)
 
+signed char kyotoPST[8][15] = { // 3 values per rank (a-c file)
+  { -114,-114,-114,    48,  59,  62,   82, 97,102,   95, 110, 115,   95, 110, 115 }, // P-R
+  { -117,-117,-117,   -31, -15, -15,   17, 46, 46,   52,  86,  88,   79, 115, 118 }, // L-T
+  {  -64, -45, -32,   -19,  48,  50,  -10, 54, 65,  -14,  56,  62,  -46,  -6,   4 }, // S-B
+  { -100,-100,-100,  -100,-100,-100,  -10, 11, 21,   53,  67,  85,   69,  83, 101 }, // N-G
+  {  -55, -37, -31,     2,  26,  33,   22, 47, 55,   23,  48,  56,    6,  30,  38 }, // R-P
+  { -107, -80, -80,   -33,  30,  31,    9, 74, 98,   35, 101, 108,   20,  84,  92 }, // T-L
+  {  -57, -49, -40,   -51,   0,   9,  -39, 14, 58,  -49,   4,  17,  -57, -46, -37 }, // B-S
+  { -107, -75, -75,   -30,  31,  32,   -3, 58, 81,   37, 105, 108,   25,  93,  95 }, // G-N
+};
+
 int
 MyRandom ()
 {
@@ -551,7 +572,7 @@ GameInit (char *name)
        PST[WHITE+4] = PST[BLACK+4] = kingPST+11; // Queens
        frontier = 2*22; killZone = 3*22; impasse = boardEnd;
     } else {
-       pawnBulk[WHITE] = (nrRanks == 7 ? 1 : 2); // board file considered full when total reaches 2
+       pawnBulk[WHITE] = (maxDrop == 3 || 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
@@ -643,6 +664,23 @@ GameInit (char *name)
                     kingPST[2*22] = kingPST[22*(nrRanks-3) + nrFiles - 1] = 0;
 
     for(f=0,p=pstType[v]; *p; p++,f++) if(*p == ' ') f = 15; else  PST[BLACK+f] = (PST[WHITE+f] = pstData + 22*11*(*p - '0')) + 11*(*p > '2'); 
+    if(v == KYOTO_NR) { // initialize Kyoto-Shogi PST from packed initialized data
+       static int bulk[4] = { 1, 3, 2, 3 };
+       for(i=0; i<4; i++) {
+           PST[BLACK+i] = (PST[WHITE+i] = pstData + 11*22*(i+3)) + 11; // assign PST to pieces
+           PST[BLACK+i+16] = (PST[WHITE+i+16] = pstData + 11*22*(i+3) + 5*22) + 11; // hide two 5x5 PST in one 11x11 table
+           for(r=0; r<5; r++) for(f=0; f<3; f++) {
+               PST[BLACK+i][22*r+f] = PST[BLACK+i][22*r+4-f] = kyotoPST[i][3*r+f];
+               PST[WHITE+i][22*r+f] = PST[WHITE+i][22*r+4-f] = kyotoPST[i][3*(4-r)+f];
+               PST[BLACK+i+16][22*r+f] = PST[BLACK+i+16][22*r+4-f] = kyotoPST[i+4][3*r+f];
+               PST[WHITE+i+16][22*r+f] = PST[WHITE+i+16][22*r+4-f] = kyotoPST[i+4][3*(4-r)+f];
+            }
+           promoCode[i+16] = promoCode[i]; promoCode[i+16+WHITE] = promoCode[i+WHITE]; // let promoted types demote
+           PST[-1][handSlot[WHITE+i]] -= 125; PST[-1][handSlot[BLACK+i]] -= 125;
+           handBulk[handSlot[WHITE+i]] = handBulk[handSlot[BLACK+i]] = bulk[i]; // base bulk on promoted type
+       }
+       promoDrops = 1; promoGain[-1] = 125;
+    }
 
     InitCaptureCodes(variants[v].codes);
     pinCodes = (v == TORI_NR ? 0xFF2C : 0xFF1F); // rays along which pinning is possible
@@ -1816,8 +1854,8 @@ printf("# command: %s\n", inBuf);
     }
     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,"
-                               "5x5+5_shogi,6x6+6_shogi,7x7+6_shogi,11x17+16_chu\"\n");
+      printf("feature variants=\"crazyhouse,shogi,minishogi,judkinshogi,torishogi,euroshogi,crazywa,kyotoshogi,"
+                               "5x5+4_shogi,5x5+5_shogi,6x6+6_shogi,7x7+6_shogi,11x17+16_chu\"\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");