Fix takeback of S-Chess gatings
[capablanca.git] / lasker-2.2.3 / src / movecheck.c
index 61bc64c..d575915 100644 (file)
@@ -178,6 +178,36 @@ static int legal_pawn_move( struct game_state_t *gs, int ff, int fr, int tf, int
   return 0;
 }
 
+static int legal_hoplite_move( struct game_state_t *gs, int ff, int fr, int tf, int tr )
+{
+  if (ff == tf) { /* Capture ? */
+    if (gs->board[tf][tr] == NOPIECE) return 0;
+    if (gs->onMove == WHITE) {
+      if(iscolor(gs->board[tf][tr],WHITE)) return 0;
+      if (tr - fr == 1) return 1;
+    } else {
+      if(iscolor(gs->board[tf][tr],BLACK)) return 0;
+      if (fr - tr == 1) return 1;
+    }
+    return 0;
+  }
+  if (ff != tf) {
+    if (gs->board[tf][tr] != NOPIECE) return 0;
+    if (gs->onMove == WHITE) {
+      if (tr == fr+1 && abs(tf-ff) == 1) return 1;
+      if (tr != fr+2 || abs(tf-ff) != 2) return 0;
+      if (fr > gs->pawnDblStep) return 0;
+        return 1;
+    } else {
+      if (tr == fr-1 && abs(tf-ff) == 1) return 1;
+      if (tr != fr-2 || abs(tf-ff) != 2) return 0;
+      if (fr < gs->ranks - 1 - gs->pawnDblStep) return 0;
+        return 1;
+    }
+  }
+  return 0;
+}
+
 static int legal_knight_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
 {
   int dx, dy;
@@ -535,6 +565,12 @@ static int legal_modernelephant_move(struct game_state_t * gs, int ff, int fr, i
   return legal_ferz_move(gs, ff, fr, tf, tr) || legal_alfil_move(gs, ff, fr, tf, tr);
 }
 
+static int legal_lieutenant_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_modernelephant_move(gs, ff, fr, tf, tr) ||
+        fr == tr && abs(ff - tf) == 1 && gs->board[tf][tr] == NOPIECE;
+}
+
 static int legal_priestess_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
 {
   return legal_knight_move(gs, ff, fr, tf, tr) || legal_ferz_move(gs, ff, fr, tf, tr)
@@ -679,6 +715,51 @@ static void possible_pawn_moves(struct game_state_t * gs,
   }
 }
 
+static void possible_hoplite_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  if (gs->onMove == WHITE) { // in Spartan Chess there are no white hoplites...
+    if (gs->board[onf][onr + 1] != NOPIECE &&
+         iscolor(gs->board[onf][onr + 1], BLACK)) {
+      add_pos(onf, onr + 1, posf, posr, numpos);
+    }
+    if (onf > 0) {
+      if (gs->board[onf - 1][onr + 1] == NOPIECE) {
+        add_pos(onf - 1, onr + 1, posf, posr, numpos);
+        if (onf > 1 && (onr <= gs->pawnDblStep) /*&& (gs->board[onf-2][onr + 2] == NOPIECE)*/)
+         add_pos(onf - 2, onr + 2, posf, posr, numpos);
+      }
+    }
+    if (onf < gs->files-1) {
+      if (gs->board[onf + 1][onr + 1] == NOPIECE) {
+       add_pos(onf + 1, onr + 1, posf, posr, numpos);
+        if (onf < gs->files-2 && (onr <= gs->pawnDblStep) /*&& (gs->board[onf+2][onr + 2] == NOPIECE)*/)
+         add_pos(onf + 2, onr + 2, posf, posr, numpos);
+      }
+    }
+  } else {
+    if (gs->board[onf][onr - 1] != NOPIECE &&
+         iscolor(gs->board[onf][onr - 1], WHITE)) {
+      add_pos(onf, onr - 1, posf, posr, numpos);
+    }
+    if (onf > 0) {
+      if (gs->board[onf - 1][onr - 1] == NOPIECE) {
+       add_pos(onf - 1, onr - 1, posf, posr, numpos);
+        if (onf > 1 && (onr >= gs->ranks - gs->pawnDblStep - 1) /*&& (gs->board[onf - 2][onr - 2] == NOPIECE)*/)
+         add_pos(onf - 2, onr - 2, posf, posr, numpos);
+      }
+    }
+    if (onf < gs->files-1) {
+      if (gs->board[onf + 1][onr - 1] == NOPIECE) {
+       add_pos(onf + 1, onr - 1, posf, posr, numpos);
+        if (onf < gs->files-2 && (onr >= gs->ranks - gs->pawnDblStep - 1) /*&& (gs->board[onf + 2][onr - 2] == NOPIECE)*/)
+         add_pos(onf + 2, onr - 2, posf, posr, numpos);
+      }
+    }
+  }
+}
+
 static void possible_knight_moves(struct game_state_t * gs,
                                    int onf, int onr,
                                    int *posf, int *posr, int *numpos)
@@ -1186,6 +1267,17 @@ static void possible_modernelephant_moves(struct game_state_t * gs,
   possible_alfil_moves(gs, onf, onr, posf, posr, numpos);
 }
 
+static void possible_lieutenant_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  possible_modernelephant_moves(gs, onf, onr, posf, posr, numpos);
+      if (onf < gs->files-1 && (gs->board[onf+1][onr] == NOPIECE))
+        add_pos(onf + 1, onr, posf, posr, numpos);
+    if (onf > 0 && (gs->board[onf-1][onr] == NOPIECE))
+        add_pos(onf - 1, onr, posf, posr, numpos);
+}
+
 static void possible_priestess_moves(struct game_state_t * gs,
                                   int onf, int onr,
                                   int *posf, int *posr, int *numpos)
@@ -1388,6 +1480,9 @@ int legal_move(struct game_state_t * gs,
   case PAWN:
     legal = legal_pawn_move(gs, fFile, fRank, tFile, tRank);
     break;
+  case HOPLITE:
+    legal = legal_hoplite_move(gs, fFile, fRank, tFile, tRank);
+    break;
   case KNIGHT:
     legal = legal_knight_move(gs, fFile, fRank, tFile, tRank);
     break;
@@ -1397,6 +1492,7 @@ int legal_move(struct game_state_t * gs,
   case ROOK:
     legal = legal_rook_move(gs, fFile, fRank, tFile, tRank);
     break;
+  case WARLORD:
   case HAWK:
   case CARDINAL:
   case PRINCESS:
@@ -1420,6 +1516,7 @@ int legal_move(struct game_state_t * gs,
   case AMAZON:
     legal = legal_amazon_move(gs, fFile, fRank, tFile, tRank);
     break;
+  case CAPTAIN:
   case WOODY:
     legal = legal_woody_move(gs, fFile, fRank, tFile, tRank);
     break;
@@ -1445,6 +1542,9 @@ int legal_move(struct game_state_t * gs,
   case WAZIR:
     legal = legal_wazir_move(gs, fFile, fRank, tFile, tRank);
     break;
+  case LIEUTENANT:
+    legal = legal_lieutenant_move(gs, fFile, fRank, tFile, tRank);
+    break;
   case ALFIL:
   case ALFIL2:
     legal = legal_alfil_move(gs, fFile, fRank, tFile, tRank);
@@ -1473,6 +1573,7 @@ int legal_move(struct game_state_t * gs,
   case DRAGONHORSE:
     legal = legal_dragonhorse_move(gs, fFile, fRank, tFile, tRank);
     break;
+  case GENERAL:
   case DRAGONKING:
     legal = legal_dragonking_move(gs, fFile, fRank, tFile, tRank);
     break;
@@ -1552,6 +1653,7 @@ static int move_calculate(struct game_state_t * gs, struct move_t * mt, int prom
   if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == PAWN) && 
        !gs->palace && // [HGM] XQ: no promotions in xiangqi
       ((mt->toRank < gs->promoZone) || (mt->toRank >= gs->ranks - gs->promoZone))) {
+    if(promote == KING) return MOVE_ILLEGAL; // no king in normal chess
     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=PIECES-1; promote>PAWN; promote--) if(gs->holding[stm == BLACK][promote-1]) break;
@@ -1563,6 +1665,44 @@ static int move_calculate(struct game_state_t * gs, struct move_t * mt, int prom
     // 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
+    if(promote == KNIGHT && gs->royalKnight) return MOVE_ILLEGAL; // Knight not allowed in Knightmate
+    if(gs->promoType != 2 && promote > QUEEN) { // for promoType != 2 we must check explicitly if the requested pieceis compatible with the variant
+       switch(promote) {
+         case HAWK:
+         case SELEPHANT:
+           if(gs->drops != 2) return MOVE_ILLEGAL; // allowed only in S-Chess
+           break;
+         case MARSHALL:
+         case CARDINAL:
+           if(!gs->capablancaPieces) return MOVE_ILLEGAL; // allowed when flagged so
+           break;
+         case FERZ:
+         case FERZ2:
+           if(!gs->pawnDblStep) return MOVE_ILLEGAL; // allowed in Shatranj and Courier
+           break;
+         case MAN2:
+           if(!gs->royalKnight) return MOVE_ILLEGAL; // allowed only in Knightmate
+           break;
+         default:
+           return MOVE_ILLEGAL;
+       }
+    }
+  } else
+  if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == HOPLITE) && 
+      ((mt->toRank < gs->promoZone) || (mt->toRank >= gs->ranks - gs->promoZone))) {
+    if(!promote || promote == KING) {
+       int f, r, k=0, king = gs->onMove == WHITE ? W_KING : B_KING;
+       for(r=0; r<gs->ranks;r++) for(f=0; f<gs->files; f++) k += (gs->board[f][r] == king);
+       if(k > 1) { // we already have two kings
+         if(promote == KING) return MOVE_ILLEGAL; // three kings not allowed
+         promote = WARLORD;   // use strongest piece as default
+       } else promote = KING; // if no promo-piece given, this could be mate test, so test if promoting to King evades
+    } else
+    if(promote == MASTODON) promote = GENERAL; else
+    if(promote == WOODY)    promote = WARLORD; else
+    if(promote == MARSHALL) promote = CAPTAIN; else
+    if(promote != LIEUTENANT) return MOVE_ILLEGAL;
+    mt->piecePromotionTo = (promote | stm);
   } else if(gs->drops == 2 && promote && mt->fromRank == (stm == WHITE ? 0 : gs->ranks-1)) { // [HGM] Seirawan-style gating
     int i; struct game *g = &game_globals.garray[gs->gameNum];
     if(!gs->holding[stm == BLACK][promote-1]) return MOVE_ILLEGAL; // unavailable piece specified
@@ -1663,12 +1803,20 @@ int in_check(struct game_state_t * gs)
        }
   }
   if (kf < 0) {
-    d_printf( "CHESSD: Error game with no king!\n");
-    return 0;
+//    d_printf( "CHESSD: Error game with no king!\n");
+    return -1;
   }
   for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
        NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
     if (legal_move(gs, f, r, kf, kr)) {        /* In Check? */
+      if(gs->onMove == WHITE && !strcmp(gs->variant, "spartan")) { // first king is in check, but we might have spare
+//printf("spartan K-capt %c%d%c%d\n",f+'a',r+1,kf+'a',kr+1);
+       gs->board[kf][kr] = B_MAN; // temporarily cure the check on the first King by replacing the latter;
+       r = in_check(gs);
+       gs->board[kf][kr] = B_KING; // and put it back
+//printf("duple = %d\n",r);
+       return r != 0; // if we have no second king (r = -1) or the second is also attacked (r = 1) we are in check.
+      }
       return 1;
     }
   }
@@ -1689,6 +1837,9 @@ int has_legal_move(struct game_state_t * gs)
     case PAWN:
       possible_pawn_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
+    case HOPLITE:
+      possible_hoplite_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
     case KNIGHT:
       possible_knight_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
@@ -1698,6 +1849,7 @@ int has_legal_move(struct game_state_t * gs)
     case ROOK:
       possible_rook_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
+    case WARLORD:
     case HAWK:
     case CARDINAL:
     case PRINCESS:
@@ -1721,6 +1873,7 @@ int has_legal_move(struct game_state_t * gs)
     case AMAZON:
       possible_amazon_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
+    case CAPTAIN:
     case WOODY:
       possible_woody_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
@@ -1753,6 +1906,9 @@ int has_legal_move(struct game_state_t * gs)
     case MODERNELEPHANT:
       possible_modernelephant_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
+    case LIEUTENANT:
+      possible_lieutenant_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
     case PRIESTESS:
       possible_priestess_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
@@ -1774,6 +1930,7 @@ int has_legal_move(struct game_state_t * gs)
     case DRAGONHORSE:
       possible_dragonhorse_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
+    case GENERAL:
     case DRAGONKING:
       possible_dragonking_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
@@ -2132,7 +2289,7 @@ int backup_move(int g, int mode)
 {
   struct game_state_t *gs;
   struct move_t *m, *m1;
-  int now, i;
+  int now, i, piece;
 
   if (game_globals.garray[g].link >= 0)        /*IanO: not implemented for bughouse yet */
     return MOVE_ILLEGAL;
@@ -2173,14 +2330,25 @@ int backup_move(int g, int mode)
     gs->board[kingFromFile][rank] = KING | m->color; // King fromSquare
     goto cleanupMove;
   }
+  piece = gs->board[m->toFile][m->toRank];
   if(gs->board[m->fromFile][m->fromRank] != NOPIECE) { // [HGM] from-square occupied; move must have been Seirawan-style gating
     gs->holding[gs->onMove==WHITE ? 1 : 0][piecetype(gs->board[m->fromFile][m->fromRank])-1]++; // put back in holdings (onMove not flipped yet!)
+  } else
+  if (m->piecePromotionTo != NOPIECE) { // it is a real promotion
+    switch(piecetype(m->piecePromotionTo)) { // Spartan pieces came from Hoplite, Shogi is problematic
+      case KING:
+      case CAPTAIN:
+      case LIEUTENANT:
+      case WARLORD:
+      case GENERAL: piece = HOPLITE; break;
+      case DRAGONHORSE: piece = BISHOP; break;
+      case DRAGONKING:  piece = ROOK;   break;
+      case GOLD: // TODO: figure out what original was
+      default: piece = PAWN;
+    }
+    piece |= colorval(gs->board[m->toFile][m->toRank]);
   }
-  gs->board[m->fromFile][m->fromRank] = gs->board[m->toFile][m->toRank];
-  if (m->piecePromotionTo != NOPIECE) {
-    gs->board[m->fromFile][m->fromRank] = PAWN |
-      colorval(gs->board[m->fromFile][m->fromRank]);
-  }
+  gs->board[m->fromFile][m->fromRank] = piece;
   /******************
      When takeback a _first_ move of rook, the ??rmoved variable
      must be cleared . To check, if the move is first, we should