Implement S-Chess
[capablanca.git] / lasker-2.2.3 / src / algcheck.c
index 32bdba1..aa7d5af 100644 (file)
@@ -96,7 +96,7 @@ static int get_move_info(const char *str, int *piece, int *ff, int *fr, int *tf,
     for (j = len - 1; j >= 0; j--) {
       switch (alg_list[i][j]) {
       case 'f':
-       if ((tmp[j] < 'a') || (tmp[j] > 'h'))
+       if ((tmp[j] < 'a') || (tmp[j] > 'l')) // [HGM] upto l-file
          goto nomatch;
        if (ltf == ALG_UNKNOWN)
          ltf = tmp[j] - 'a';
@@ -104,12 +104,12 @@ static int get_move_info(const char *str, int *piece, int *ff, int *fr, int *tf,
          lff = tmp[j] - 'a';
        break;
       case 'r':
-       if ((tmp[j] < '1') || (tmp[j] > '8'))
+       if ((tmp[j] < '0') || (tmp[j] > '9')) // [HGM] also match 0- and 9-rank
          goto nomatch;
        if (ltr == ALG_UNKNOWN)
-         ltr = tmp[j] - '1';
+         ltr = tmp[j] - '0'; // [HGM] allow 0-rank for Xiangqi, correct later
        else
-         lfr = tmp[j] - '1';
+         lfr = tmp[j] - '0';
        break;
       case 'p':
        if (isupper(tmp[j]))
@@ -118,8 +118,32 @@ static int get_move_info(const char *str, int *piece, int *ff, int *fr, int *tf,
          c = tmp[j];
        if (c == 'k')
          lpiece = KING;
+       else if (c == 'e')   // [HGM] note that som piece indicators are ambiguous,
+         lpiece = ELEPHANT; //       and their true meaning depends on the variant,
+       else if (c == 'v')   //       which we do not know at this point.
+         lpiece = CENTAUR;
+       else if (c == 's')
+         lpiece = SILVER;
+       else if (c == 'g')
+         lpiece = GOLD;
+       else if (c == 'l')
+         lpiece = LANCE;
+       else if (c == 'f')
+         lpiece = FERZ;
+       else if (c == 'h')
+         lpiece = HORSE;
+       else if (c == 'w')
+         lpiece = WAZIR;
+       else if (c == 'o')
+         lpiece = SQUIRREL;
        else if (c == 'q')
          lpiece = QUEEN;
+       else if (c == 'c')
+         lpiece = MARSHALL;
+       else if (c == 'a')
+         lpiece = CARDINAL;
+       else if (c == 'm')
+         lpiece = MAN;
        else if (c == 'r')
          lpiece = ROOK;
        else if (c == 'b')
@@ -128,6 +152,8 @@ static int get_move_info(const char *str, int *piece, int *ff, int *fr, int *tf,
          lpiece = KNIGHT;
        else if (c == 'p')
          lpiece = PAWN;
+       else if (c == 'd')
+         lpiece = DRAGONKING;
        else
          goto nomatch;
        break;
@@ -173,6 +199,7 @@ static int get_move_info(const char *str, int *piece, int *ff, int *fr, int *tf,
     matchVal = i;
 nomatch:;
   }
+
   if (matchVal != -1)
     return MS_ALG;
   else
@@ -190,23 +217,62 @@ int alg_is_move(const char *mstr)
 static void add_promotion(struct game_state_t *gs, const char *mstr, struct move_t * mt)
 {
        char *s;
-       int piece;
+       int piece, i;
        s = strchr(mstr, '=');
        if (s == NULL) {
                return;
        }
-       
+       if(gs->promoType == 3) { // handle Shogi promotions
+               piece = gs->board[mt->fromFile][mt->fromRank];
+               if(colorval(piece) == WHITE && mt->fromRank < gs->ranks - gs->ranks/3
+                                           && mt->toRank   < gs->ranks - gs->ranks/3 ) return;
+               if(colorval(piece) == BLACK && mt->fromRank >= gs->ranks/3
+                                           && mt->toRank   >= gs->ranks/3 ) return;
+                switch(piecetype(piece)) {
+                   case PAWN:
+                   case LANCE:
+                   case HONORABLEHORSE:
+                   case SILVER:
+                       if(s[1] != '+' && s[1] != '^' && s[1] != 'G' && s[1] != 'g') return;
+                       piece = GOLD; break;
+                   case BISHOP:
+                       if(s[1] != '+' && s[1] != '^' && s[1] != 'H' && s[1] != 'h') return;
+                       piece = DRAGONHORSE; break;
+                   case ROOK:
+                       if(s[1] != '+' && s[1] != '^' && s[1] != 'D' && s[1] != 'd') return;
+                       piece = DRAGONKING; break;
+                   default: return; // others do not promote, so ignore
+               }
+               mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
+               return;
+       }
+
        if (piecetype(gs->board[mt->fromFile][mt->fromRank]) != PAWN) {
                return;
        }
-       if (mt->toRank != 7 && mt->toRank != 0) {
+       if (mt->toRank < gs->ranks - gs->promoZone && mt->toRank >= gs->promoZone) {
                return;
        }
 
        switch (tolower(s[1])) {
+       case 'f':
+               piece = FERZ2;
+               break;
        case 'q':
                piece = QUEEN;
                break;
+       case 'c':
+               if(!gs->capablancaPieces) return; // [HGM] should make variant-dependent piece mask
+               piece = MARSHALL;
+               break;
+       case 'a':
+               if(!gs->capablancaPieces) return;
+               piece = CARDINAL;
+               break;
+       case 'm':
+               if(!gs->royalKnight) return; // [HGM] only in knightmate
+               piece = MAN;
+               break;
        case 'r':
                piece = ROOK;
                break;
@@ -214,11 +280,38 @@ static void add_promotion(struct game_state_t *gs, const char *mstr, struct move
                piece = BISHOP;
                break;
        case 'n':
+               if(gs->royalKnight) return; // [HGM] not in knightmate
                piece = KNIGHT;
                break;
+       // Superchess promotons: filtered out later by promoType
+       case 'g':
+               piece = MASTODON;
+               break;
+       case 'o':
+               piece = SQUIRREL;
+               break;
+       case 'w':
+               piece = WOODY;
+               break;
+       case 'v':
+               piece = CENTAUR;
+               break;
+       case 'e':
+               piece = gs->drops == 2 ? SELEPHANT : EMPRESS; // for Seirawan
+               break;
+       case 's':
+               piece = PRINCESS;
+               break;
+       case 'h':
+               if(gs->drops != 2) return;
+               piece = HAWK;
+               break;
        default:
                return;
        }
+       i = colorval(gs->board[mt->fromFile][mt->fromRank]) == WHITE ? 0 : 1;
+       if(gs->promoType == 2 && gs->holding[i][piece-1] == 0) return; // only if piece was captured
+       if(piece >= WOODY && piece < KING && (gs->promoType != 2 || gs->promoZone == 3)) return; // reserved for Superchess
 
        mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
 }
@@ -233,6 +326,56 @@ int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
     d_printf( "CHESSD: Shouldn't try to algebraicly parse non-algabraic move string.\n");
     return MOVE_ILLEGAL;
   }
+  // [HGM] check if move does not stray off board
+  if(gs->ranks < 10) { 
+    if(tr == 0 || fr == 0) return MOVE_ILLEGAL; // used nonexistent 0-rank
+    if(tr != ALG_UNKNOWN) tr--; if(fr != ALG_UNKNOWN) fr--; // shift to lowest rank = 1
+  }
+  if(tr >= gs->ranks || fr >= gs->ranks || tf >= gs->files || ff >= gs->files)
+    return MOVE_ILLEGAL;
+
+  // [HGM] resolve ambiguity in piece, type based on variant
+  switch(piece) {
+    case ELEPHANT:
+      if(strstr(gs->variant, "super"))   piece = EMPRESS; else
+      if(strstr(gs->variant, "great"))   piece = MODERNELEPHANT; else
+      if(strstr(gs->variant, "courier")) piece = ALFIL2;
+      break;
+    case CARDINAL:
+      if(strstr(gs->variant, "super")) piece = AMAZON; else
+      if(strstr(gs->variant, "xiangqi")) piece = MANDARIN;
+      break;
+    case MARSHALL:
+      if(strstr(gs->variant, "xiangqi")) piece = CANNON;
+      break;
+    case SILVER:
+      if(strstr(gs->variant, "super")) piece = PRINCESS;
+      if(strstr(gs->variant, "great")) piece = MAN2;
+      break;
+    case BISHOP:
+      if(strstr(gs->variant, "shatranj")) piece = ALFIL;
+      break;
+    case QUEEN:
+      if(strstr(gs->variant, "shatranj")) piece = FERZ;
+      break;
+    case WAZIR:
+      if(strstr(gs->variant, "super")) piece = WOODY;
+      break;
+    case KNIGHT:
+      if(strstr(gs->variant, "shogi")) piece = HONORABLEHORSE;
+      break;
+    case MAN:
+      if(strstr(gs->variant, "great")) piece = MINISTER;
+      break;
+    case HORSE:
+      if(strstr(gs->variant, "great")) piece = PRIESTESS;
+      if(strstr(gs->variant, "shogi")) piece = DRAGONHORSE;
+      break;
+    case GOLD:
+      if(strstr(gs->variant, "great")) piece = MASTODON;
+      break;
+  }
+
   /* Resolve ambiguities in to-ness */
   if (tf == ALG_UNKNOWN) {
          d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
@@ -250,7 +393,7 @@ int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
     }
     /* Need to find pawn on ff that can take to tf and fill in ranks */
     for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
-        NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+        NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
       if ((ff != ALG_UNKNOWN) && (ff != f))
        continue;
       if (piecetype(gs->board[f][r]) != piece)
@@ -286,25 +429,26 @@ int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
     ff = ALG_UNKNOWN;
     fr = ALG_UNKNOWN;
     for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
-        NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
-           if ((piecetype(gs->board[f][r]) != PAWN) && (piecetype(gs->board[f][r]) != BISHOP)) {
+        NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
+           if ((piecetype(gs->board[f][r]) != PAWN) && (piecetype(gs->board[f][r]) != piece)) {
+                   // note that the interpretation Bxc4 is matched last, and has set piece to BISHOP
                    continue;
            }
            if (legal_andcheck_move(gs, f, r, tf, tr)) {
-                   if ((piecetype(gs->board[f][r]) == PAWN) && (f != 1)) {
+                   if ((piecetype(gs->board[f][r]) == PAWN) && (f != tolower(mstr[0]) - 'a')) {
                            continue;
                    }
 
                    /* if its a lowercase 'b' then prefer the pawn move if there is one */
                    if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
-                       piecetype(gs->board[f][r]) == PAWN && mstr[0] == 'b') {
+                       piecetype(gs->board[f][r]) == PAWN && mstr[0] >= 'a') {
                            ff = f;
                            fr = r;
                            continue;
                    }
 
                    if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
-                       piecetype(gs->board[ff][fr]) == PAWN && mstr[0] == 'b') {
+                       piecetype(gs->board[ff][fr]) == PAWN && mstr[0] >= 'a') {
                            continue;
                    }
 
@@ -322,7 +466,7 @@ int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
     if ((ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) {
       /* Need to find a piece that can go to tf, tr */
       for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
-          NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+          NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
        if ((ff != ALG_UNKNOWN) && (ff != f))
          continue;
        if ((fr != ALG_UNKNOWN) && (fr != r))
@@ -381,11 +525,11 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
     piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
   }
 
-  if ((piece == KING) && ((mt->fromFile == 4) && (mt->toFile == 6))) {
+  if ((mt->fromFile == ALG_CASTLE) && (mt->toFile > mt->toRank)) { // [HGM] castle: K ends right of R
     strcpy(mStr, "O-O");
     goto check;
   }
-  if ((piece == KING) && ((mt->fromFile == 4) && (mt->toFile == 2))) {
+  if ((mt->fromFile == ALG_CASTLE) && (mt->toFile < mt->toRank)) { // [HGM] castle: K ends left of R
     strcpy(mStr, "O-O-O");
     goto check;
   }
@@ -394,7 +538,8 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
   case PAWN:
     if (mt->fromFile == ALG_DROP) {
       strcpy(mStr,"P");
-    } else if (mt->fromFile != mt->toFile) {
+    } else if (mt->fromFile != mt->toFile 
+           || gs->board[mt->toFile][mt->toRank] != NOPIECE) { // [HGM] XQ: forward captures as "axa6"
       sprintf(tmp, "%c", mt->fromFile + 'a');
       strcpy(mStr, tmp);
     }
@@ -408,9 +553,68 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
   case ROOK:
     strcpy(mStr, "R");
     break;
+  case ALFIL2:
+  case AMAZON:
+  case CARDINAL:
+    strcpy(mStr, "A");
+    break;
+  case CANNON:
+  case MARSHALL:
+    strcpy(mStr, "C");
+    break;
+  case MAN:
+    strcpy(mStr, "M");
+    break;
+  case FERZ:
   case QUEEN:
     strcpy(mStr, "Q");
     break;
+  case EMPRESS:
+  case ELEPHANT:
+    strcpy(mStr, "E");
+    break;
+  case ALFIL:
+    strcpy(mStr, "B");
+    break;
+  case FERZ2:
+    strcpy(mStr, "F");
+    break;
+  case WOODY:
+  case WAZIR:
+    strcpy(mStr, "W");
+    break;
+  case SQUIRREL:
+    strcpy(mStr, "O");
+    break;
+  case CENTAUR:
+    strcpy(mStr, "V");
+    break;
+  case HORSE:
+    strcpy(mStr, "H");
+    break;
+  case HONORABLEHORSE:
+    strcpy(mStr, "N");
+    break;
+  case DRAGONKING:
+    strcpy(mStr, "D");
+    break;
+  case DRAGONHORSE:
+    strcpy(mStr, "H");
+    break;
+  case LANCE:
+    strcpy(mStr, "L");
+    break;
+  case PRINCESS:
+  case SILVER:
+    strcpy(mStr, "S");
+    break;
+  case MASTODON:
+  case GOLD:
+    strcpy(mStr, "G");
+    break;
+  case MANDARIN:
+    strcpy(mStr, "A");
+    break;
   case KING:
     strcpy(mStr, "K");
     break;
@@ -425,8 +629,8 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
   /* Checks for ambiguity in short notation ( Ncb3, R8e8 or so) */
   if (piece != PAWN) {
     ambig = r_ambig = f_ambig = 0;
-    for (r = 0; r < 8; r++)
-      for (f = 0; f < 8; f++) {
+    for (r = 0; r < gs->ranks; r++)
+      for (f = 0; f < gs->files; f++) {
        if ((gs->board[f][r] != NOPIECE) && iscolor(gs->board[f][r], gs->onMove)
            && (piecetype(gs->board[f][r]) == piece) &&
            ((f != mt->fromFile) || (r != mt->fromRank))) {
@@ -467,10 +671,10 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
        sprintf(tmp, "%c", mt->fromFile + 'a');
        strcat(mStr, tmp);
       } else if (r_ambig == 0) {
-       sprintf(tmp, "%d", mt->fromRank + 1);
+       sprintf(tmp, "%d", mt->fromRank + 1 - (gs->ranks > 9));
        strcat(mStr, tmp);
       } else {
-       sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1);
+       sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1 - (gs->ranks > 9));
        strcat(mStr, tmp);
       }
     }
@@ -480,10 +684,10 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
     strcat(mStr, "x");
   }
   }
-  sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1);
+  sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));
   strcat(mStr, tmp);
 
-  if ((piece == PAWN) && (mt->piecePromotionTo != NOPIECE)) {
+  if ((piece == PAWN || gs->promoType == 3) && (mt->piecePromotionTo != NOPIECE)) {
     strcat(mStr, "=");         /* = before promoting piece */
     switch (piecetype(mt->piecePromotionTo)) {
     case KNIGHT:
@@ -495,9 +699,50 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
     case ROOK:
       strcat(mStr, "R");
       break;
+    case CARDINAL:
+      strcat(mStr, "A");
+      break;
+    case MARSHALL:
+      strcat(mStr, "C");
+      break;
+    case MAN:
+      strcat(mStr, "M");
+      break;
     case QUEEN:
       strcat(mStr, "Q");
       break;
+    case FERZ2:
+      strcat(mStr, "F");
+      break;
+    case WOODY:
+      strcat(mStr, "W");
+      break;
+    case SELEPHANT:
+    case EMPRESS:
+      strcat(mStr, "E");
+      break;
+    case CENTAUR:
+      strcat(mStr, "V");
+      break;
+    case PRINCESS:
+      strcat(mStr, "S");
+      break;
+    case SQUIRREL:
+      strcat(mStr, "O");
+      break;
+    case MASTODON:
+      strcat(mStr, "G");
+      break;
+    case GOLD: // [HGM] Shogi promotions: avoid use of '+'
+      strcat(mStr, "G");
+      break;
+    case HAWK:
+    case DRAGONHORSE:
+      strcat(mStr, "H");
+      break;
+    case DRAGONKING:
+      strcat(mStr, "D");
+      break;
     default:
       break;
     }