Finish implementation of Shogi
authorH.G. Muller <h.g.muller@hccnet.nl>
Sun, 10 Oct 2010 10:38:56 +0000 (12:38 +0200)
committerH.G. Muller <h.g.muller@hccnet.nl>
Sun, 10 Oct 2010 10:38:56 +0000 (12:38 +0200)
Shogi promotions were implemented by introducing a new promoType
value (3), which can be set y 'S' in the board status line.
Demotion of pieces takes place on capture, by tracing them back with
was_promoted() to their original, which now is not automatically Pawn,
but derived from the piece mentioned in the move string.

Like in Bughouse a promoted Queen is not distinguished from a
primordial one on the board, Golds which are promoted Pawns / Lances /
Knights / Silvers all look the same as a primordial Gold. The Dragon
Horse and Draon King look different from Bishop and Rook, though, and
are indicated by the letters I and J on the board.

The promotion is indicated in the SAN move by suffix =G, =I or =J.
Deferral is not indicated. On input == is understood as deferral, =+ or
=^ (or in fact =<ANY PIECE> as promotion).

lasker-2.2.3/data/boards/shogi/0
lasker-2.2.3/src/algcheck.c
lasker-2.2.3/src/board.c
lasker-2.2.3/src/gamedb_old.c
lasker-2.2.3/src/gameproc.c
lasker-2.2.3/src/movecheck.c

index f8312fb..68f0338 100644 (file)
@@ -1,3 +1,3 @@
-S 9x9 dh
+S 9x9 dhS
 W: P a3 b3 c3 d3 e3 f3 g3 h3 i3 n b1 h1 S c1 g1 G d1 f1 K e1 L a1 i1 R h2 B b2
 B: P a7 b7 c7 d7 e7 f7 g7 h7 i7 n b9 h9 S c9 g9 G d9 f9 K e9 L a9 i9 R b8 B h8
index d043445..c7b6906 100644 (file)
@@ -656,8 +656,8 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
   }\r
   sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));\r
   strcat(mStr, tmp);\r
-\r
-  if ((piece == PAWN) && (mt->piecePromotionTo != NOPIECE)) {\r
+
+  if ((piece == PAWN || gs->promoType == 3) && (mt->piecePromotionTo != NOPIECE)) {\r
     strcat(mStr, "=");         /* = before promoting piece */\r
     switch (piecetype(mt->piecePromotionTo)) {\r
     case KNIGHT:\r
@@ -702,6 +702,15 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
     case MASTODON:\r
       strcat(mStr, "G");\r
       break;\r
+    case GOLD: // [HGM] Shogi promotions: avoid use of '+'\r
+      strcat(mStr, "G");\r
+      break;\r
+    case DRAGONHORSE:\r
+      strcat(mStr, "I");\r
+      break;\r
+    case DRAGONKING:\r
+      strcat(mStr, "J");\r
+      break;\r
     default:\r
       break;\r
     }\r
index 5c974f1..315df34 100644 (file)
@@ -1032,6 +1032,9 @@ static int board_read_file(char *category, char *gname, struct game_state_t *gs)
                gs->promoType = 2; // only promote to captured pieces
                gs->holdings = 1;  // use holdings to hold own captured pieces
                break;
+           case 'S':
+               gs->promoType = 3; // Shogi-type promotions
+               break;
            case 'F':
                gs->castlingStyle = 2; // FRC castling
                break;
index 15b947e..dd435b4 100644 (file)
@@ -319,6 +319,10 @@ static void ReadOneV1Move(FILE * fp, struct move_t *m)
       m->algString[i++] = '1' + m->toRank;
       m->algString[i] = '\0';
     }
+    if (m->piecePromotionTo != 0) { // must be Shogi promotion
+       strcat(m->algString, "=+");
+      m->piecePromotionTo |= m->color;
+    }
   }
   if (m->algString[0] != 'O')
     sprintf(m->moveString, "%c/%c%d-%c%d", PieceChar, 'a' + m->fromFile,
index a0dba3f..ec6918c 100644 (file)
@@ -413,8 +413,17 @@ static int was_promoted(struct game *g, int f, int r)
 
   for (i = g->numHalfMoves-2; i > 0; i -= 2) {
     if (g->moveList[i].toFile == f && g->moveList[i].toRank == r) {
-      if (g->moveList[i].piecePromotionTo)
-       return 1;
+      if (g->moveList[i].piecePromotionTo) {
+       switch(g->moveList[i].moveString[0]) { // [HGM] return original piece type rather than just TRUE
+          case 'P': return PAWN;
+          case 'N': return KNIGHT;
+          case 'B': return BISHOP;
+          case 'R': return ROOK;
+          case 'L': return LANCE;
+          case 'S': return SILVER;
+          default:  return GOLD;
+        }
+      }
       if (g->moveList[i].fromFile == ALG_DROP)
        return 0;
       f = g->moveList[i].fromFile;
@@ -550,6 +559,7 @@ void process_move(int p, char *command)
       return;
     }
   }
+  pp->promote = NOPIECE; // [HGM] this seemed to be uninitialized, which caused spurious promotion in Shogi
   if ((len = strlen(command)) > 1) {
     if (command[len - 2] == '=') {
 printf("promo '%s'\n", command);
@@ -598,6 +608,14 @@ printf("promo '%s'\n", command);
       case 'g':
        pp->promote = MASTODON;
        break;
+      // Shogi promotions
+      case '^':
+      case '+':
+       pp->promote = GOLD;
+       break;
+      case '=':
+       pp->promote = NOPIECE;
+       break;
       default:
        pprintf(p, "Don't understand that move.\n");
        return;
@@ -675,14 +693,14 @@ printf("promo '%s'\n", command);
     if (result == MOVE_OK && (gg->link >= 0 || gg->game_state.holdings) && move.pieceCaptured != NOPIECE) {
       /* transfer captured piece to partner */
       /* check if piece reverts to a pawn */
-      int victim = move.pieceCaptured, partner = gg->link;
+      int victim = move.pieceCaptured, partner = gg->link, demoted;
       // [HGM] zh: if not Bughouse, the game_state.holdings field decides what happens
       if(gg->link < 0) { 
        partner = g; // pieces stay with current board
        if(gg->game_state.holdings == -1) victim ^= WHITE|BLACK; // flip color
       } 
-      if (was_promoted(&game_globals.garray[g], move.toFile, move.toRank))
-        update_holding(partner, colorval(victim) | PAWN); // [HGM] todo: for Shogi we would have to demote differently!
+      if (demoted = was_promoted(&game_globals.garray[g], move.toFile, move.toRank))
+        update_holding(partner, colorval(victim) | demoted); // [HGM] was_promoted now returns original piece type
       else
         update_holding(partner, victim);
     }
index 953298c..c0dd853 100644 (file)
@@ -141,7 +141,7 @@ int InitPieceLoop(board_t b, int *f, int *r, int color)
 static int legal_pawn_move( struct game_state_t *gs, int ff, int fr, int tf, int tr )
 {
   if (ff == tf) {\r
-    if (gs->board[tf][tr] != NOPIECE && !gs->palace) return 0; // [HGM] XQ pawns can capture straight ahead\r
+    if (gs->board[tf][tr] != NOPIECE && !gs->palace && gs->promoType != 3) return 0; // [HGM] XQ and Shogi pawns can capture straight ahead\r
     if (gs->onMove == WHITE) {\r
       if (tr - fr == 1) return 1;\r
       if ((fr <= gs->pawnDblStep) && (tr - fr == 2) && gs->board[ff][fr+1]==NOPIECE) return 1;
@@ -151,7 +151,7 @@ static int legal_pawn_move( struct game_state_t *gs, int ff, int fr, int tf, int
     }\r
     return 0;\r
   }\r
-  if (ff != tf) { /* Capture ? */
+  if (ff != tf && gs->promoType != 3) { /* Capture ? ([HGM] but not in Shogi) */
     if ((ff - tf != 1) && (tf - ff != 1)) return 0;
     if (gs->onMove == WHITE) {
       if(gs->palace) return (fr >= gs->ranks/2 && fr == tr); // [HGM] XQ promoted pawns\r
@@ -621,21 +621,23 @@ static void possible_pawn_moves(struct game_state_t * gs,
                                  int *posf, int *posr, int *numpos)
 {
   if (gs->onMove == WHITE) {\r
-    if (gs->board[onf][onr + 1] == NOPIECE || gs->palace) {\r
+    if (gs->board[onf][onr + 1] == NOPIECE || gs->palace || gs->promoType == 3) {\r
       add_pos(onf, onr + 1, posf, posr, numpos);\r
       if ((onr <= gs->pawnDblStep) && (gs->board[onf][onr + 2] == NOPIECE))\r
        add_pos(onf, onr + 2, posf, posr, numpos);\r
     }\r
     if (onf > 0) {
       if (gs->board[onf - 1][onr + 1] != NOPIECE &&\r
-         iscolor(gs->board[onf - 1][onr + 1], BLACK))\r
+         iscolor(gs->board[onf - 1][onr + 1], BLACK) &&
+          !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi\r
         add_pos(onf - 1, onr + 1, posf, posr, numpos);
       if(gs->palace && onr >= gs->ranks/2 && (gs->board[onf-1][onr] || iscolor(gs->board[onf-1][onr], BLACK)))
         add_pos(onf - 1, onr, posf, posr, numpos); // XQ promoted pawn
     }\r
     if (onf < gs->files-1) {
       if (gs->board[onf + 1][onr + 1] != NOPIECE &&\r
-         iscolor(gs->board[onf + 1][onr + 1], BLACK))\r
+         iscolor(gs->board[onf + 1][onr + 1], BLACK) &&
+          !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi\r
        add_pos(onf + 1, onr + 1, posf, posr, numpos);
       if(gs->palace && onr >= gs->ranks/2 && (gs->board[onf+1][onr] || iscolor(gs->board[onf+1][onr], BLACK)))
         add_pos(onf + 1, onr, posf, posr, numpos); // XQ promoted pawn
@@ -645,21 +647,23 @@ static void possible_pawn_moves(struct game_state_t * gs,
     if (gs->ep_possible[0][onf] == 1)\r
       add_pos(onf + 1, onr + 1, posf, posr, numpos);
   } else {\r
-    if (gs->board[onf][onr - 1] == NOPIECE || gs->palace) {\r
+    if (gs->board[onf][onr - 1] == NOPIECE || gs->palace || gs->promoType == 3) {\r
       add_pos(onf, onr - 1, posf, posr, numpos);\r
       if ((onr >= gs->ranks - gs->pawnDblStep - 1) && (gs->board[onf][onr - 2] == NOPIECE))\r
        add_pos(onf, onr - 2, posf, posr, numpos);\r
     }\r
     if (onf > 0) {
       if (gs->board[onf - 1][onr - 1] != NOPIECE &&\r
-         iscolor(gs->board[onf - 1][onr - 1], WHITE))\r
+         iscolor(gs->board[onf - 1][onr - 1], WHITE) &&\r
+          !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi\r
        add_pos(onf - 1, onr - 1, posf, posr, numpos);
       if(gs->palace && onr < gs->ranks/2 && !iscolor(gs->board[onf-1][onr], BLACK))
         add_pos(onf - 1, onr, posf, posr, numpos); // XQ promoted pawn
     }\r
     if (onf < gs->files-1) {
       if (gs->board[onf + 1][onr - 1] != NOPIECE &&\r
-         iscolor(gs->board[onf + 1][onr - 1], WHITE))\r
+         iscolor(gs->board[onf + 1][onr - 1], WHITE) &&\r
+          !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi\r
        add_pos(onf + 1, onr - 1, posf, posr, numpos);
       if(gs->palace && onr < gs->ranks/2 && !iscolor(gs->board[onf+1][onr], BLACK))
         add_pos(onf + 1, onr, posf, posr, numpos); // XQ promoted pawn
@@ -1479,6 +1483,38 @@ static int move_calculate(struct game_state_t * gs, struct move_t * mt, int prom
        // [HGM] castle: generalized castling, fr and tr give from and to file of Rook.
            sprintf(mt->moveString, mt->toRank > mt->toFile ? "o-o-o" : "o-o");
   } else {
+  if(gs->promoType == 3) { // Shogi-style promotions: not just Pawns, but many pieces can promote
+    int piece = gs->board[mt->fromFile][mt->fromRank];
+    mt->piecePromotionTo = NOPIECE;
+    if(colorval(piece) == WHITE && mt->fromRank < gs->ranks - gs->ranks/3
+                                && mt->toRank   < gs->ranks - gs->ranks/3 ||
+       colorval(piece) == BLACK && mt->fromRank >= gs->ranks/3
+                                && mt->toRank   >= gs->ranks/3 )
+        promote = NOPIECE; // suppress promotion outside zone
+    if(promote) { // promotion piece determined by original, no matter what was requested
+      switch(piecetype(piece)) {
+        case PAWN:
+        case LANCE:
+        case KNIGHT:
+        case SILVER:
+          promote = GOLD; break;
+        case BISHOP:
+          promote = DRAGONHORSE; break;
+        case ROOK:
+          promote = DRAGONKING; break;
+        default: promote = NOPIECE; // not a promotion
+      }
+    } else
+      switch(piecetype(piece)) { // force mandatory promotions
+        case KNIGHT:
+          if(mt->toRank == 1 || mt->toRank == gs->files-2) promote = GOLD;
+        case PAWN:
+        case LANCE:
+          if(mt->toRank == 0 || mt->toRank == gs->files-1) promote = GOLD;
+        default: break;
+      }
+    if(promote) mt->piecePromotionTo = promote | (colorval(gs->board[mt->fromFile][mt->fromRank]));\r
+  } else
   if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == PAWN) && 
        !gs->palace && // [HGM] XQ: no promotions in xiangqi\r
       ((mt->toRank == 0) || (mt->toRank == gs->ranks-1))) {
@@ -1820,8 +1856,9 @@ int parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt, int pro
        mt->fromFile = ALG_CASTLE;
     }\r
 \r
-  if (mt->piecePromotionTo != NOPIECE) {\r
+  if (mt->piecePromotionTo != NOPIECE) {
          promote = piecetype(mt->piecePromotionTo);\r
+printf("promotion piece = %d, type = %d",mt->piecePromotionTo, promote);\r
   }\r
 \r
   return move_calculate(gs, mt, promote);\r