Implement Grand Chess
[capablanca.git] / lasker-2.2.3 / src / movecheck.c
old mode 100755 (executable)
new mode 100644 (file)
index 953298c..30405fa
@@ -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
@@ -218,7 +218,7 @@ static int legal_honorablehorse_move(struct game_state_t * gs, int ff, int fr, i
 
   dx = ff - tf;
   dy = fr - tr;
-  if (dy == gs->onMove == WHITE ? 2 : -2) {
+  if (dy == (gs->onMove == WHITE ? -2 : 2)) {
     if (abs(dx) == 1)
       return 1;
   }
@@ -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
@@ -1299,6 +1303,21 @@ int legal_move(struct game_state_t * gs,
       return 0;
     if (gs->board[tFile][tRank] != NOPIECE)
       return 0;
+    if (gs->promoType == 3) { // Shogi
+      int r;
+      switch(move_piece) {
+       case PAWN:  // check for own Pawn in same file
+         for(r=0; r<gs->ranks; r++) if(gs->board[tFile][r] == (gs->onMove|PAWN)) return 0;
+       case LANCE: // Pawns and Lances not on last rank
+         if(gs->onMove == WHITE && tRank >= gs->ranks-1) return 0;
+         if(gs->onMove == BLACK && tRank < 1) return 0;
+         break;
+       case HONORABLEHORSE: // Knights at least two ranks from edge
+         if(gs->onMove == WHITE && tRank >= gs->ranks-2) return 0;
+         if(gs->onMove == BLACK && tRank < 2) return 0;
+       default: ;
+      }
+    } else
     if (move_piece == PAWN && (tRank == 0 || tRank == gs->ranks-1))
       return 0;
     return 1;
@@ -1479,13 +1498,53 @@ 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 HONORABLEHORSE:
+        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 HONORABLEHORSE:
+          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))) {
+      ((mt->toRank < gs->promoZone) || (mt->toRank >= gs->ranks - gs->promoZone))) {
+    int stm = colorval(gs->board[mt->fromFile][mt->fromRank]);
+    if(!promote && (mt->toRank == 0 || mt->toRank == gs->ranks-1)) { // promotion obligatory, but not specified
+       if(gs->promoType != 2) promote = QUEEN; else { // choose a default
+           for(promote=KING-1; promote>PAWN; promote--) if(gs->holding[stm == BLACK][promote-1]) break;
+           if(promote == PAWN) return MOVE_ILLEGAL; // nothing available
+       }
+    } // if not obligatory, we defer unless promoton was explicitly specified!
     if(!gs->pawnDblStep && promote == PRINCESS) promote = MAN2;
     if(!gs->pawnDblStep && promote != FERZ2 && promote != MAN2) promote = FERZ; // [HGM] kludge to recognize shatranj
-    mt->piecePromotionTo = promote |\r
-      (colorval(gs->board[mt->fromFile][mt->fromRank]));\r
+    // non-promotion can still be an option for deeper promotion zones
+    mt->piecePromotionTo = promote ? (promote | stm) : NOPIECE;
+    if(promote && gs->promoType == 2 && !gs->holding[stm == BLACK][promote-1]) return MOVE_ILLEGAL; // unavailable piece specified
   } else {\r
     mt->piecePromotionTo = NOPIECE;\r
   }\r
@@ -1518,7 +1577,7 @@ static int move_calculate(struct game_state_t * gs, struct move_t * mt, int prom
 \r
   sprintf(mt->algString, alg_unparse(gs, mt));\r
   fakeMove = *gs;\r
-  /* Calculates enPassant also */\r
+  /* Calculates enPassant also */
   execute_move(&fakeMove, mt, 0);\r
 \r
   /* Does making this move leave ME in check? */\r
@@ -1534,6 +1593,7 @@ int legal_andcheck_move(struct game_state_t * gs,
                        int tFile, int tRank)
 {
   struct move_t mt;
+
   if (!legal_move(gs, fFile, fRank, tFile, tRank))
     return 0;
   mt.color = gs->onMove;
@@ -1542,7 +1602,7 @@ int legal_andcheck_move(struct game_state_t * gs,
   mt.toFile = tFile;
   mt.toRank = tRank;
   /* This should take into account a pawn promoting to another piece */
-  if (move_calculate(gs, &mt, QUEEN) == MOVE_OK)
+  if (move_calculate(gs, &mt, NOPIECE) == MOVE_OK) 
     return 1;
   else
     return 0;
@@ -1577,7 +1637,7 @@ int in_check(struct game_state_t * gs)
   }\r
   for (InitPieceLoop(gs->board, &f, &r, gs->onMove);\r
        NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {\r
-    if (legal_move(gs, f, r, kf, kr)) {        /* In Check? */\r
+    if (legal_move(gs, f, r, kf, kr)) {        /* In Check? */
       return 1;\r
     }\r
   }\r
@@ -1716,10 +1776,8 @@ int has_legal_move(struct game_state_t * gs)
          if(game_globals.garray[gs->gameNum].link < 0) {
                // we have no partner, so we must have something to drop now
                for(i=QUEEN; i>=PAWN; i--)
-                       if(gs->holding[gs->onMove==WHITE ? 0 : 1][i-1]) break;
-               if(i > PAWN) return 1; // we have a non-Pawn to drop
-               // We have a Pawn, but check if it legal to drop it
-               if(i == PAWN && r != 0 && r != gs->ranks-1) return 1; // [HGM] todo: for Shogi there are extra conditions on Pawn drops!
+                       if (legal_andcheck_move(gs, ALG_DROP, i, f, r)) return 1;\r
+               return 0;
          }\r
          return 1;\r
        }\r
@@ -1820,7 +1878,7 @@ 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
   }\r
 \r
@@ -1894,7 +1952,7 @@ int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_st
   for (i = 0; i < gs->files; i++) {\r
     gs->ep_possible[0][i] = 0;\r
     gs->ep_possible[1][i] = 0;\r
-  }\r
+  }
 /* Added by Sparky 3/16/95
 
    From soso@Viktoria.drp.fmph.uniba.sk Thu Mar 16 13:08:51 1995
@@ -2008,7 +2066,8 @@ int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_st
         return MOVE_NOMATERIAL;\r
   } else {\r
     gs->onMove = CToggle(gs->onMove);\r
-  }\r
+  }
+\r
   return MOVE_OK;\r
 }