Implement S-Chess
[capablanca.git] / lasker-2.2.3 / src / movecheck.c
index 81f2d37..6996d09 100644 (file)
 
 #include "includes.h"
 
+// [HGM] some explanation of the parser code:
+// The routine parse_move() calls is_move() to recognize some simple cases,
+// like OO-castle and long algebraic with or without dash. What does not fall
+// in this class is handed to alg_is_move(), whch is really a continuation
+// of is_move(), to recognize SAN.
+// Depending on the type of move syntax, parse_move can extract from- and to-square
+// immediately, or transate the OO-castling to internal from-to representation.
+// Only for SAN syntax the routine alg_pars_move() is called to extract the
+// given elements, (through get_move_info(), which is the same as alg_is_move(),
+// xcept that it does not discard the value of the elements), and disambiguate 
+// the move (i.e. determines the from-square) by looking for a piece of the given
+// type on the board for which the move is pseudo-legal (using legal_move() ).
+// Th
+
 /* Simply tests if the input string is a move or not. */
 /* If it matches patterns below */
 /* Add to this list as you improve the move parser */
@@ -99,14 +113,14 @@ int is_move(const char *mstr)
 }
 
 
-int NextPieceLoop(board_t b, int *f, int *r, int color)
+int NextPieceLoop(board_t b, int *f, int *r, int color, int w, int h)
 {
   for (;;) {
     (*r) = (*r) + 1;
-    if (*r > 7) {
+    if (*r >= h) {
       *r = 0;
       *f = *f + 1;
-      if (*f > 7)
+      if (*f >= w)
        break;
     }
     if ((b[*f][*r] != NOPIECE) && iscolor(b[*f][*r], color))
@@ -127,21 +141,21 @@ 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) {
-    if (gs->board[tf][tr] != NOPIECE) return 0;
+    if (gs->board[tf][tr] != NOPIECE && !gs->palace && gs->promoType != 3) return 0; // [HGM] XQ and Shogi pawns can capture straight ahead
     if (gs->onMove == WHITE) {
       if (tr - fr == 1) return 1;
-      if ((fr == 1) && (tr - fr == 2) && gs->board[ff][2]==NOPIECE) return 1;
+      if ((fr <= gs->pawnDblStep) && (tr - fr == 2) && gs->board[ff][fr+1]==NOPIECE) return 1;
     } else {
       if (fr - tr == 1) return 1;
-      if ((fr == 6) && (fr - tr == 2) && gs->board[ff][5]==NOPIECE) return 1;
+      if ((fr >= gs->ranks - 1 - gs->pawnDblStep) && (fr - tr == 2) && gs->board[ff][fr-1]==NOPIECE) return 1;
     }
     return 0;
   }
-  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 ((fr - tr != 1) && (tr - fr != 1)) return 0;
     if (gs->onMove == WHITE) {
-      if (fr > tr) return 0;
+      if(gs->palace) return (fr >= gs->ranks/2 && fr == tr); // [HGM] XQ promoted pawns
+      if (tr != fr+1) return 0;
       if ((gs->board[tf][tr] != NOPIECE) && iscolor(gs->board[tf][tr],BLACK))
         return 1;
       if (gs->ep_possible[0][ff] == 1) {
@@ -150,7 +164,8 @@ static int legal_pawn_move( struct game_state_t *gs, int ff, int fr, int tf, int
         if ((tf==ff-1) && (gs->board[ff-1][fr] == B_PAWN)) return 1;
       }
     } else {
-      if (tr > fr) return 0;
+      if(gs->palace) return (fr < gs->ranks/2 && fr == tr); // [HGM] XQ promoted pawns
+      if (tr != fr-1) return 0;
       if ((gs->board[tf][tr] != NOPIECE) && iscolor(gs->board[tf][tr],WHITE))
         return 1;
       if (gs->ep_possible[1][ff] == 1) {
@@ -180,6 +195,36 @@ static int legal_knight_move(struct game_state_t * gs, int ff, int fr, int tf, i
   return 0;
 }
 
+static int legal_horse_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  int dx, dy;
+
+  dx = ff - tf;
+  dy = fr - tr;
+  if (abs(dx) == 2) {
+    if (abs(dy) == 1 && gs->board[(ff+tf)/2][fr] == NOPIECE)
+      return 1;
+  }
+  if (abs(dy) == 2) {
+    if (abs(dx) == 1 && gs->board[ff][(fr+tr)/2] == NOPIECE)
+      return 1;
+  }
+  return 0;
+}
+
+static int legal_honorablehorse_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  int dx, dy;
+
+  dx = ff - tf;
+  dy = fr - tr;
+  if (dy == (gs->onMove == WHITE ? -2 : 2)) {
+    if (abs(dx) == 1)
+      return 1;
+  }
+  return 0;
+}
+
 static int legal_bishop_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
 {
   int dx, dy, x, y;
@@ -255,11 +300,82 @@ static int legal_rook_move(struct game_state_t * gs, int ff, int fr, int tf, int
   }
 }
 
+static int legal_cannon_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  int i, cnt=0;
+  int start, stop;
+
+  if (ff == tf) {
+    if (fr < tr) {
+      start = fr + 1;
+      stop = tr - 1;
+    } else {
+      start = tr + 1;
+      stop = fr - 1;
+    }
+    for (i = start; i <= stop; i++) {
+      if (gs->board[ff][i] != NOPIECE) cnt++;
+    }
+    return (cnt == 0 && gs->board[tf][tr] == NOPIECE) ||
+          (cnt == 1 && gs->board[tf][tr] != NOPIECE);
+  } else if (fr == tr) {
+    if (ff < tf) {
+      start = ff + 1;
+      stop = tf - 1;
+    } else {
+      start = tf + 1;
+      stop = ff - 1;
+    }
+    for (i = start; i <= stop; i++) {
+      if (gs->board[i][fr] != NOPIECE) cnt++;
+    }
+    return (cnt == 0 && gs->board[tf][tr] == NOPIECE) ||
+          (cnt == 1 && gs->board[tf][tr] != NOPIECE);
+  } else {
+    return 0;
+  }
+}
+
+static int legal_lance_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  int i;
+  int start, stop;
+
+  if (ff == tf) {
+    if (abs(fr - tr) == 1)
+      return 1;
+    if (fr < tr) {
+      if(gs->onMove != WHITE) return 0;
+      start = fr + 1;
+      stop = tr - 1;
+    } else {
+      if(gs->onMove == WHITE) return 0;
+      start = tr + 1;
+      stop = fr - 1;
+    }
+    for (i = start; i <= stop; i++) {
+      if (gs->board[ff][i] != NOPIECE)
+       return 0;
+    }
+    return 1;
+  }
+}
+
 static int legal_queen_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
 {
   return legal_rook_move(gs, ff, fr, tf, tr) || legal_bishop_move(gs, ff, fr, tf, tr);
 }
 
+static int legal_cardinal_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_knight_move(gs, ff, fr, tf, tr) || legal_bishop_move(gs, ff, fr, tf, tr);
+}
+
+static int legal_marshall_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_rook_move(gs, ff, fr, tf, tr) || legal_knight_move(gs, ff, fr, tf, tr);
+}
+
 /* Ckeck, if square (kf,kr) is attacked by enemy piece.
  * Used in castling from/through check testing.
  */
@@ -268,9 +384,10 @@ static int legal_queen_move(struct game_state_t * gs, int ff, int fr, int tf, in
 static int is_square_attacked (struct game_state_t *gs, int kf, int kr)
 {
   struct game_state_t fakeMove;
+  int oldk = gs->onMove == WHITE ? gs->wkmoved : gs->bkmoved;
 
   fakeMove = *gs;
-  fakeMove.board[4][kr] = NOPIECE;
+  fakeMove.board[oldk][kr] = NOPIECE; // [HGM] castle: this routine is called only when King has not moved
   fakeMove.board[kf][kr] = KING | fakeMove.onMove;
   fakeMove.onMove = CToggle (fakeMove.onMove);
   if (in_check(&fakeMove)) return 1;
@@ -284,7 +401,7 @@ static int is_square_attacked(struct game_state_t * gs, int kf, int kr)
   gs->onMove = CToggle(gs->onMove);
 
   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 (legal_move(gs, f, r, kf, kr)) {
       gs->onMove = CToggle(gs->onMove);
       return 1;
@@ -295,46 +412,201 @@ static int is_square_attacked(struct game_state_t * gs, int kf, int kr)
 }
 */
 
+static int legal_man_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  if (abs(ff - tf) > 1)
+    return 0;
+  if (abs(fr - tr) > 1)
+    return 0;
+  return 1;
+}
+
+static int legal_wazir_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  if(gs->palace && (tr > gs->palace && tr < gs->ranks - gs->palace ||
+     tf < (gs->files - gs->palace)/2 || tf >= (gs->files + gs->palace)/2))
+    return 0;
+  if (abs(ff - tf) == 1 && fr == tr)
+    return 1;
+  if (abs(fr - tr) == 1 && ff == tf)
+    return 1;
+  return 0;
+}
+
+static int legal_dababba_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  if (abs(ff - tf) == 2 && fr == tr)
+    return 1;
+  if (abs(fr - tr) == 2 && ff == tf)
+    return 1;
+  return 0;
+}
+
+static int legal_ferz_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  if (abs(ff - tf) != 1)
+    return 0;
+  if (abs(fr - tr) != 1)
+    return 0;
+  return 1;
+}
+
+static int legal_mandarin_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  if(gs->palace && (tr > gs->palace && tr < gs->ranks - gs->palace ||
+     tf < (gs->files - gs->palace)/2 || tf >= (gs->files + gs->palace)/2))
+    return 0;
+  if (abs(ff - tf) != 1)
+    return 0;
+  if (abs(fr - tr) != 1)
+    return 0;
+  return 1;
+}
+
+static int legal_alfil_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  if (abs(ff - tf) != 2)
+    return 0;
+  if (abs(fr - tr) != 2)
+    return 0;
+  return 1;
+}
+
+static int legal_elephant_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  if (abs(ff - tf) != 2)
+    return 0;
+  if (abs(fr - tr) != 2)
+    return 0;
+  if(gs->board[(ff+tf)/2][(fr+tr)/2] != NOPIECE) return 0; // blocked
+  if((tr >= gs->ranks/2) != (fr >= gs->ranks/2)) return 0; // do not cross river
+  return 1;
+}
+
+static int legal_gold_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_wazir_move(gs, ff, fr, tf, tr) || (abs(ff-tf) == 1 && tr == fr + (gs->onMove==WHITE ? 1 : -1));
+}
+
+static int legal_silver_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_ferz_move(gs, ff, fr, tf, tr) || (tf == ff && tr == fr + (gs->onMove==WHITE ? 1 : -1) );
+}
+
+static int legal_woody_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_wazir_move(gs, ff, fr, tf, tr) || legal_dababba_move(gs, ff, fr, tf, tr);
+}
+
+static int legal_squirrel_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_alfil_move(gs, ff, fr, tf, tr) || legal_dababba_move(gs, ff, fr, tf, tr) 
+                                                       || legal_knight_move(gs, ff, fr, tf, tr);
+}
+
+static int legal_mastodon_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_man_move(gs, ff, fr, tf, tr) || legal_alfil_move(gs, ff, fr, tf, tr)
+                                                       || legal_dababba_move(gs, ff, fr, tf, tr);
+}
+
+static int legal_centaur_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_man_move(gs, ff, fr, tf, tr) || legal_knight_move(gs, ff, fr, tf, tr);
+}
+
+static int legal_amazon_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_queen_move(gs, ff, fr, tf, tr) || legal_knight_move(gs, ff, fr, tf, tr);
+}
+
+static int legal_dragonking_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_rook_move(gs, ff, fr, tf, tr) || legal_ferz_move(gs, ff, fr, tf, tr);
+}
+
+static int legal_dragonhorse_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_bishop_move(gs, ff, fr, tf, tr) || legal_wazir_move(gs, ff, fr, tf, tr);
+}
+
+static int legal_modernelephant_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_ferz_move(gs, ff, fr, tf, tr) || legal_alfil_move(gs, ff, fr, tf, tr);
+}
+
+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)
+                                               || legal_alfil_move(gs, ff, fr, tf, tr);
+}
+
+static int legal_minister_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
+{
+  return legal_knight_move(gs, ff, fr, tf, tr) || legal_wazir_move(gs, ff, fr, tf, tr)
+                                               || legal_dababba_move(gs, ff, fr, tf, tr);
+}
+
 static int legal_king_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
 {
+  int result;
+
+  // [HGM] castle: test first if valid as regular King move; result = 1 or 0
+  if(gs->royalKnight)
+    result = legal_knight_move(gs, ff, fr, tf, tr);
+  else if(gs->palace) {
+    result = legal_wazir_move(gs, ff, fr, tf, tr);
+    if(!result && ff == tf && piecetype(gs->board[tf][tr]) == KING) { // XQ regicide
+      int i, d = (tr>fr ? 1 : -1);
+      for(i=fr+d; i!=tr; i+=d) 
+       if(gs->board[ff][i] != NOPIECE) return 0; // line of sight blocked
+      return 1;
+    }
+  } else
+    result = legal_man_move(gs, ff, fr, tf, tr);
+
+  if(result) return 1;
+  // [HGM] castle: orthodox legal castlings given as King move return 2
+
   if (gs->onMove == WHITE) {
     /* King side castling */
-    if ((fr == 0) && (tr == 0) && (ff == 4) && (tf == 6) && !gs->wkmoved
-       && (!gs->wkrmoved) && (gs->board[5][0] == NOPIECE) &&
-       (gs->board[6][0] == NOPIECE) && (gs->board[7][0] == W_ROOK) &&
-       (!is_square_attacked(gs, 4, 0)) && (!is_square_attacked(gs, 5, 0))) {
-      return 1;
+    if ((fr == 0) && (tr == 0) && (ff == gs->files/2) && (tf == gs->files-2) && (gs->wkmoved >= 0)
+       && (gs->wkrmoved >= 0) && (gs->board[gs->files-3][0] == NOPIECE) &&
+       (gs->board[gs->files-2][0] == NOPIECE) && (gs->board[gs->files-1][0] == W_ROOK) &&
+       (gs->board[gs->files/2+1][0] == NOPIECE) && (!is_square_attacked(gs, gs->files/2+1, 0)) &&
+       (!is_square_attacked(gs, gs->files/2, 0)) && (!is_square_attacked(gs, gs->files-3, 0))) {
+      return 2;
     }
     /* Queen side castling */
-    if ((fr == 0) && (tr == 0) && (ff == 4) && (tf == 2) && !gs->wkmoved
-       && (!gs->wqrmoved) && (gs->board[3][0] == NOPIECE) &&
+    if ((fr == 0) && (tr == 0) && (ff == gs->files/2) && (tf == 2) && (gs->wkmoved >= 0)
+       && (gs->wqrmoved >= 0) && (gs->board[3][0] == NOPIECE) &&
        (gs->board[2][0] == NOPIECE) && (gs->board[1][0] == NOPIECE) &&
        (gs->board[0][0] == W_ROOK) &&
-       (!is_square_attacked(gs, 4, 0)) && (!is_square_attacked(gs, 3, 0))) {
-      return 1;
+       (gs->board[gs->files/2-1][0] == NOPIECE) && (!is_square_attacked(gs, gs->files/2-1, 0)) &&
+       (!is_square_attacked(gs, gs->files/2, 0)) && (!is_square_attacked(gs, 3, 0))) {
+      return 2;
     }
   } else {                     /* Black */
     /* King side castling */
-    if ((fr == 7) && (tr == 7) && (ff == 4) && (tf == 6) && !gs->bkmoved
-       && (!gs->bkrmoved) && (gs->board[5][7] == NOPIECE) &&
-       (gs->board[6][7] == NOPIECE) && (gs->board[7][7] == B_ROOK) &&
-       (!is_square_attacked(gs, 4, 7)) && (!is_square_attacked(gs, 5, 7))) {
-      return 1;
+    if ((fr == gs->ranks-1) && (tr == gs->ranks-1) && (ff == gs->files/2) && (tf == gs->files-2) && (gs->bkmoved >= 0)
+       && (gs->bkrmoved >= 0) && (gs->board[gs->files-3][7] == NOPIECE) &&
+       (gs->board[gs->files-2][gs->ranks-1] == NOPIECE) && (gs->board[gs->files-1][gs->ranks-1] == B_ROOK) &&
+       (gs->board[gs->files/2+1][gs->ranks-1] == NOPIECE) && (!is_square_attacked(gs, gs->files/2+1, gs->ranks-1)) &&
+       (!is_square_attacked(gs, gs->files/2, gs->ranks-1)) && (!is_square_attacked(gs, gs->files-3, gs->ranks-1))) {
+      return 2;
     }
     /* Queen side castling */
-    if ((fr == 7) && (tr == 7) && (ff == 4) && (tf == 2) && (!gs->bkmoved)
-       && (!gs->bqrmoved) && (gs->board[3][7] == NOPIECE) &&
-       (gs->board[2][7] == NOPIECE) && (gs->board[1][7] == NOPIECE) &&
-       (gs->board[0][7] == B_ROOK) &&
-       (!is_square_attacked(gs, 4, 7)) && (!is_square_attacked(gs, 3, 7))) {
-      return 1;
+    if ((fr == gs->ranks-1) && (tr == gs->ranks-1) && (ff == gs->files/2) && (tf == 2) && (gs->bkmoved >= 0)
+       && (gs->bqrmoved >= 0) && (gs->board[3][gs->ranks-1] == NOPIECE) &&
+       (gs->board[2][gs->ranks-1] == NOPIECE) && (gs->board[1][gs->ranks-1] == NOPIECE) &&
+       (gs->board[0][gs->ranks-1] == B_ROOK) &&
+       (gs->board[gs->files/2-1][gs->ranks-1] == NOPIECE) && (!is_square_attacked(gs, gs->files/2-1, gs->ranks-1)) &&
+       (!is_square_attacked(gs, gs->files/2, gs->ranks-1)) && (!is_square_attacked(gs, 3, gs->ranks-1))) {
+      return 2;
     }
   }
-  if (abs(ff - tf) > 1)
-    return 0;
-  if (abs(fr - tr) > 1)
-    return 0;
-  return 1;
+
+  return 0; // neither regular King move nor castling
 }
 
 static void add_pos(int tof, int tor, int *posf, int *posr, int *numpos)
@@ -349,36 +621,53 @@ static void possible_pawn_moves(struct game_state_t * gs,
                                  int *posf, int *posr, int *numpos)
 {
   if (gs->onMove == WHITE) {
-    if (gs->board[onf][onr + 1] == NOPIECE) {
+    if (gs->board[onf][onr + 1] == NOPIECE || gs->palace || gs->promoType == 3) {
       add_pos(onf, onr + 1, posf, posr, numpos);
-      if ((onr == 1) && (gs->board[onf][onr + 2] == NOPIECE))
+      if ((onr <= gs->pawnDblStep) && (gs->board[onf][onr + 2] == NOPIECE))
        add_pos(onf, onr + 2, posf, posr, numpos);
     }
-    if ((onf > 0) && (gs->board[onf - 1][onr + 1] != NOPIECE) &&
-       (iscolor(gs->board[onf - 1][onr + 1], BLACK)))
-      add_pos(onf - 1, onr + 1, posf, posr, numpos);
-    if ((onf < 7) && (gs->board[onf + 1][onr + 1] != NOPIECE) &&
-       (iscolor(gs->board[onf + 1][onr + 1], BLACK)))
-      add_pos(onf + 1, onr + 1, posf, posr, numpos);
+    if (onf > 0) {
+      if (gs->board[onf - 1][onr + 1] != NOPIECE &&
+         iscolor(gs->board[onf - 1][onr + 1], BLACK) &&
+          !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi
+        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
+    }
+    if (onf < gs->files-1) {
+      if (gs->board[onf + 1][onr + 1] != NOPIECE &&
+         iscolor(gs->board[onf + 1][onr + 1], BLACK) &&
+          !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi
+       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
+    }
     if (gs->ep_possible[0][onf] == -1)
       add_pos(onf - 1, onr + 1, posf, posr, numpos);
     if (gs->ep_possible[0][onf] == 1)
       add_pos(onf + 1, onr + 1, posf, posr, numpos);
   } else {
-    if (gs->board[onf][onr - 1] == NOPIECE) {
+    if (gs->board[onf][onr - 1] == NOPIECE || gs->palace || gs->promoType == 3) {
       add_pos(onf, onr - 1, posf, posr, numpos);
-      if ((onr == 6) && (gs->board[onf][onr - 2] == NOPIECE))
+      if ((onr >= gs->ranks - gs->pawnDblStep - 1) && (gs->board[onf][onr - 2] == NOPIECE))
        add_pos(onf, onr - 2, posf, posr, numpos);
     }
-    if ((onf > 0) && (gs->board[onf - 1][onr - 1] != NOPIECE) &&
-       (iscolor(gs->board[onf - 1][onr - 1], WHITE)))
-      add_pos(onf - 1, onr - 1, posf, posr, numpos);
-/* loon: changed what looks like a typo, here's the original line:
-      add_pos(onf - 1, onr + 1, posf, posr, numpos);
-*/
-    if ((onf < 7) && (gs->board[onf + 1][onr - 1] != NOPIECE) &&
-       (iscolor(gs->board[onf + 1][onr - 1], WHITE)))
-      add_pos(onf + 1, onr - 1, posf, posr, numpos);
+    if (onf > 0) {
+      if (gs->board[onf - 1][onr - 1] != NOPIECE &&
+         iscolor(gs->board[onf - 1][onr - 1], WHITE) &&
+          !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi
+       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
+    }
+    if (onf < gs->files-1) {
+      if (gs->board[onf + 1][onr - 1] != NOPIECE &&
+         iscolor(gs->board[onf + 1][onr - 1], WHITE) &&
+          !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi
+       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
+    }
     if (gs->ep_possible[1][onf] == -1)
       add_pos(onf - 1, onr - 1, posf, posr, numpos);
     if (gs->ep_possible[1][onf] == 1)
@@ -398,9 +687,9 @@ static void possible_knight_moves(struct game_state_t * gs,
   for (j = 0; j < 8; j++) {
     f = knightJumps[j][0] + onf;
     r = knightJumps[j][1] + onr;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
       continue;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       continue;
     if ((gs->board[f][r] == NOPIECE) ||
        (iscolor(gs->board[f][r], CToggle(gs->onMove))))
@@ -408,6 +697,28 @@ static void possible_knight_moves(struct game_state_t * gs,
   }
 }
 
+static void possible_horse_moves(struct game_state_t * gs,
+                                   int onf, int onr,
+                                   int *posf, int *posr, int *numpos)
+{
+  static int knightJumps[8][4] = {{-1, 2, 0, 1}, {1, 2, 0, 1}, {2, -1, 1, 0}, {2, 1, 1, 0},
+  {-1, -2, 0, -1}, {1, -2, 0, -1}, {-2, 1, -1, 0}, {-2, -1, -1, 0}};
+  int f, r;
+  int j;
+
+  for (j = 0; j < 8; j++) {
+    f = knightJumps[j][0] + onf;
+    r = knightJumps[j][1] + onr;
+    if ((f < 0) || (f >= gs->files))
+      continue;
+    if ((r < 0) || (r >= gs->ranks))
+      continue;
+    if ((gs->board[knightJumps[j][2] + onf][knightJumps[j][3] + onr] == NOPIECE) && 
+       ((gs->board[f][r] == NOPIECE) || (iscolor(gs->board[f][r], CToggle(gs->onMove)))))
+      add_pos(f, r, posf, posr, numpos);
+  }
+}
+
 static void possible_bishop_moves(struct game_state_t * gs,
                                    int onf, int onr,
                                    int *posf, int *posr, int *numpos)
@@ -420,9 +731,9 @@ static void possible_bishop_moves(struct game_state_t * gs,
   for (;;) {
     f--;
     r++;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
       break;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       break;
     if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
       break;
@@ -436,9 +747,9 @@ static void possible_bishop_moves(struct game_state_t * gs,
   for (;;) {
     f++;
     r++;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
       break;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       break;
     if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
       break;
@@ -452,9 +763,9 @@ static void possible_bishop_moves(struct game_state_t * gs,
   for (;;) {
     f--;
     r--;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
       break;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       break;
     if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
       break;
@@ -468,9 +779,9 @@ static void possible_bishop_moves(struct game_state_t * gs,
   for (;;) {
     f++;
     r--;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
       break;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       break;
     if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
       break;
@@ -491,9 +802,9 @@ static void possible_rook_moves(struct game_state_t * gs,
   r = onr;
   for (;;) {
     f--;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
       break;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       break;
     if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
       break;
@@ -506,9 +817,9 @@ static void possible_rook_moves(struct game_state_t * gs,
   r = onr;
   for (;;) {
     f++;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
       break;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       break;
     if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
       break;
@@ -521,9 +832,9 @@ static void possible_rook_moves(struct game_state_t * gs,
   r = onr;
   for (;;) {
     r++;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
       break;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       break;
     if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
       break;
@@ -536,9 +847,123 @@ static void possible_rook_moves(struct game_state_t * gs,
   r = onr;
   for (;;) {
     r--;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
+      break;
+    if ((r < 0) || (r >= gs->ranks))
+      break;
+    if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+      break;
+    add_pos(f, r, posf, posr, numpos);
+    if (gs->board[f][r] != NOPIECE)
+      break;
+  }
+}
+
+static void possible_cannon_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  int f, r, i;
+
+  /* Left */
+  f = onf;
+  r = onr;
+  for (i=0;;) {
+    f--;
+    if ((f < 0) || (f >= gs->files))
+      break;
+    if ((r < 0) || (r >= gs->ranks))
+      break;
+    if ((gs->board[f][r] != NOPIECE) && i++ == 0) continue;
+    if(i == 0)
+       add_pos(f, r, posf, posr, numpos); // no hop: non-capt
+    else if(i == 2 && !iscolor(gs->board[f][r], gs->onMove)) 
+       add_pos(f, r, posf, posr, numpos); // hop: capt
+    if (gs->board[f][r] != NOPIECE)
+      break;
+  }
+  /* Right */
+  f = onf;
+  r = onr;
+  for (i=0;;) {
+    f++;
+    if ((f < 0) || (f >= gs->files))
+      break;
+    if ((r < 0) || (r >= gs->ranks))
+      break;
+    if ((gs->board[f][r] != NOPIECE) && i++ == 0) continue;
+    if(i == 0)
+       add_pos(f, r, posf, posr, numpos); // no hop: non-capt
+    else if(i == 2 && !iscolor(gs->board[f][r], gs->onMove)) 
+       add_pos(f, r, posf, posr, numpos); // hop: capt
+    if (gs->board[f][r] != NOPIECE)
+      break;
+  }
+  /* Up */
+  f = onf;
+  r = onr;
+  for (i=0;;) {
+    r++;
+    if ((f < 0) || (f >= gs->files))
+      break;
+    if ((r < 0) || (r >= gs->ranks))
+      break;
+    if ((gs->board[f][r] != NOPIECE) && i++ == 0) continue;
+    if(i == 0)
+       add_pos(f, r, posf, posr, numpos); // no hop: non-capt
+    else if(i == 2 && !iscolor(gs->board[f][r], gs->onMove)) 
+       add_pos(f, r, posf, posr, numpos); // hop: capt
+    if (gs->board[f][r] != NOPIECE)
+      break;
+  }
+  /* Down */
+  f = onf;
+  r = onr;
+  for (i=0;;) {
+    r--;
+    if ((f < 0) || (f >= gs->files))
+      break;
+    if ((r < 0) || (r >= gs->ranks))
+      break;
+    if ((gs->board[f][r] != NOPIECE) && i++ == 0) continue;
+    if(i == 0)
+       add_pos(f, r, posf, posr, numpos); // no hop: non-capt
+    else if(i == 2 && !iscolor(gs->board[f][r], gs->onMove)) 
+       add_pos(f, r, posf, posr, numpos); // hop: capt
+    if (gs->board[f][r] != NOPIECE)
+      break;
+  }
+}
+
+static void possible_lance_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  int f, r;
+
+  /* Up */
+  f = onf;
+  r = onr;
+  for (;gs->onMove == WHITE;) {
+    r++;
+    if ((f < 0) || (f >= gs->files))
+      break;
+    if ((r < 0) || (r >= gs->ranks))
+      break;
+    if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
+      break;
+    add_pos(f, r, posf, posr, numpos);
+    if (gs->board[f][r] != NOPIECE)
+      break;
+  }
+  /* Down */
+  f = onf;
+  r = onr;
+  for (;gs->onMove == BLACK;) {
+    r--;
+    if ((f < 0) || (f >= gs->files))
       break;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       break;
     if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
       break;
@@ -548,6 +973,22 @@ static void possible_rook_moves(struct game_state_t * gs,
   }
 }
 
+static void possible_cardinal_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_knight_moves(gs, onf, onr, posf, posr, numpos);
+  possible_bishop_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_marshall_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_rook_moves(gs, onf, onr, posf, posr, numpos);
+  possible_knight_moves(gs, onf, onr, posf, posr, numpos);
+}
+
 static void possible_queen_moves(struct game_state_t * gs,
                                   int onf, int onr,
                                   int *posf, int *posr, int *numpos)
@@ -556,21 +997,110 @@ static void possible_queen_moves(struct game_state_t * gs,
   possible_bishop_moves(gs, onf, onr, posf, posr, numpos);
 }
 
-static void possible_king_moves(struct game_state_t * gs,
+static void possible_alfil_moves(struct game_state_t * gs,
                                  int onf, int onr,
                                  int *posf, int *posr, int *numpos)
 {
-  static int kingJumps[8][2] = {{-1, -1}, {0, -1}, {1, -1}, {-1, 1},
-  {0, 1}, {1, 1}, {-1, 0}, {1, 0}};
+  static int kingJumps[4][2] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
   int f, r;
   int j;
 
-  for (j = 0; j < 8; j++) {
+  for (j = 0; j < 4; j++) {
+    f = 2*kingJumps[j][0] + onf;
+    r = 2*kingJumps[j][1] + onr;
+    if ((f < 0) || (f >= gs->files))
+      continue;
+    if ((r < 0) || (r >= gs->ranks))
+      continue;
+    if ((gs->board[f][r] == NOPIECE) ||
+       (iscolor(gs->board[f][r], CToggle(gs->onMove))))
+      add_pos(f, r, posf, posr, numpos);
+  }
+}
+
+static void possible_ferz_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  static int kingJumps[4][2] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
+  int f, r;
+  int j;
+
+  for (j = 0; j < 4; j++) {
+    f = kingJumps[j][0] + onf;
+    r = kingJumps[j][1] + onr;
+    if ((f < 0) || (f >= gs->files))
+      continue;
+    if ((r < 0) || (r >= gs->ranks))
+      continue;
+    if ((gs->board[f][r] == NOPIECE) ||
+       (iscolor(gs->board[f][r], CToggle(gs->onMove))))
+      add_pos(f, r, posf, posr, numpos);
+  }
+}
+
+static void possible_mandarin_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  static int kingJumps[4][2] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
+  int f, r;
+  int j;
+
+  for (j = 0; j < 4; j++) {
+    f = kingJumps[j][0] + onf;
+    r = kingJumps[j][1] + onr;
+    if ((f < 0) || (f >= gs->files))
+      continue;
+    if ((r < 0) || (r >= gs->ranks))
+      continue;
+    if(gs->palace && (r >= gs->palace && r < gs->ranks - gs->palace ||
+       f < (gs->files - gs->palace)/2 || f >= (gs->files + gs->palace)/2))
+      continue;
+    if ((gs->board[f][r] == NOPIECE) ||
+       (iscolor(gs->board[f][r], CToggle(gs->onMove))))
+      add_pos(f, r, posf, posr, numpos);
+  }
+}
+
+static void possible_wazir_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  static int kingJumps[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
+  int f, r;
+  int j;
+
+  for (j = 0; j < 4; j++) {
     f = kingJumps[j][0] + onf;
     r = kingJumps[j][1] + onr;
-    if ((f < 0) || (f > 7))
+    if ((f < 0) || (f >= gs->files))
+      continue;
+    if ((r < 0) || (r >= gs->ranks))
+      continue;
+    if(gs->palace && (r >= gs->palace && r < gs->ranks - gs->palace ||
+       f < (gs->files - gs->palace)/2 || f >= (gs->files + gs->palace)/2))
+      continue;
+    if ((gs->board[f][r] == NOPIECE) ||
+       (iscolor(gs->board[f][r], CToggle(gs->onMove))))
+      add_pos(f, r, posf, posr, numpos);
+  }
+}
+
+static void possible_dababba_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  static int kingJumps[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
+  int f, r;
+  int j;
+
+  for (j = 0; j < 4; j++) {
+    f = 2*kingJumps[j][0] + onf;
+    r = 2*kingJumps[j][1] + onr;
+    if ((f < 0) || (f >= gs->files))
       continue;
-    if ((r < 0) || (r > 7))
+    if ((r < 0) || (r >= gs->ranks))
       continue;
     if ((gs->board[f][r] == NOPIECE) ||
        (iscolor(gs->board[f][r], CToggle(gs->onMove))))
@@ -578,6 +1108,184 @@ static void possible_king_moves(struct game_state_t * gs,
   }
 }
 
+static void possible_man_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
+  possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_dragonking_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_rook_moves(gs, onf, onr, posf, posr, numpos);
+  possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_dragonhorse_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
+  possible_bishop_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_centaur_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_man_moves(gs, onf, onr, posf, posr, numpos);
+  possible_knight_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_woody_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
+  possible_dababba_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_squirrel_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_alfil_moves(gs, onf, onr, posf, posr, numpos);
+  possible_dababba_moves(gs, onf, onr, posf, posr, numpos);
+  possible_knight_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_mastodon_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_man_moves(gs, onf, onr, posf, posr, numpos);
+  possible_alfil_moves(gs, onf, onr, posf, posr, numpos);
+  possible_dababba_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_amazon_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_queen_moves(gs, onf, onr, posf, posr, numpos);
+  possible_knight_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_modernelephant_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
+  possible_alfil_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_priestess_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
+  possible_alfil_moves(gs, onf, onr, posf, posr, numpos);
+  possible_knight_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_minister_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
+  possible_dababba_moves(gs, onf, onr, posf, posr, numpos);
+  possible_knight_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_gold_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
+  if(gs->onMove == WHITE) {
+    if(onr < gs->ranks-1)
+      if(onf > 0 && !iscolor(gs->board[onf-1][onr+1], WHITE))
+       add_pos(onf-1, onr+1, posf, posr, numpos);
+      if(onf < gs->files-1 && !iscolor(gs->board[onf+1][onr+1], WHITE))
+       add_pos(onf+1, onr+1, posf, posr, numpos);
+  } else {
+    if(onr > 0)
+      if(onf > 0 && !iscolor(gs->board[onf-1][onr-1], BLACK))
+       add_pos(onf-1, onr-1, posf, posr, numpos);
+      if(onf < gs->files-1 && !iscolor(gs->board[onf+1][onr-1], BLACK))
+       add_pos(onf+1, onr-1, posf, posr, numpos);
+  }
+}
+
+static void possible_silver_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
+  if(gs->onMove == WHITE) {
+    if(onr < gs->ranks-1 && !iscolor(gs->board[onf][onr+1], WHITE))
+      add_pos(onf, onr+1, posf, posr, numpos);
+  } else {
+    if(onr > 0 && !iscolor(gs->board[onf][onr-1], BLACK))
+      add_pos(onf, onr-1, posf, posr, numpos);
+  }
+}
+
+static void possible_honorablehorse_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  int f, r = onr + (gs->onMove == WHITE ? 2 : -2);
+
+  if(r < 0 || r >= gs->ranks) return;
+  if(onf > 0) {
+    if ((gs->board[onf-1][r] == NOPIECE) ||
+       (iscolor(gs->board[onf-1][r], CToggle(gs->onMove))))
+      add_pos(onf - 1, r, posf, posr, numpos);
+  }
+  if(onf < gs->files - 1) {
+    if ((gs->board[onf+1][r] == NOPIECE) ||
+       (iscolor(gs->board[onf+1][r], CToggle(gs->onMove))))
+      add_pos(onf + 1, r, posf, posr, numpos);
+  }
+}
+
+static void possible_king_moves(struct game_state_t * gs,
+                                 int onf, int onr,
+                                 int *posf, int *posr, int *numpos)
+{
+  if(gs->royalKnight)
+    possible_knight_moves(gs, onf, onr, posf, posr, numpos);
+  else if(gs->palace)
+    possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
+  else
+    possible_man_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+static void possible_elephant_moves(struct game_state_t * gs,
+                                  int onf, int onr,
+                                  int *posf, int *posr, int *numpos)
+{
+  static int kingJumps[4][2] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
+  int f, r;
+  int j;
+
+  for (j = 0; j < 4; j++) {
+    f = 2*kingJumps[j][0] + onf;
+    r = 2*kingJumps[j][1] + onr;
+    if ((f < 0) || (f >= gs->files))
+      continue;
+    if ((r < 0) || (r >= gs->ranks))
+      continue;
+    if ((gs->board[(f+onf)/2][(r+onr)/2] == NOPIECE) && ((gs->board[f][r] == NOPIECE) ||
+       (iscolor(gs->board[f][r], CToggle(gs->onMove)))))
+      add_pos(f, r, posf, posr, numpos);
+  }
+}
+
 /* Doesn't check for check */
 int legal_move(struct game_state_t * gs,
               int fFile, int fRank,
@@ -588,15 +1296,72 @@ int legal_move(struct game_state_t * gs,
 
   if (fFile == ALG_DROP) {
     move_piece = fRank;
+    if(gs->drops != 1) return 0; // [HGM] variants: no drops in this variant
     if (move_piece == KING)
       return 0;
     if (gs->holding[gs->onMove==WHITE ? 0 : 1][move_piece-1] == 0)
       return 0;
     if (gs->board[tFile][tRank] != NOPIECE)
       return 0;
-    if (move_piece == PAWN && (tRank == 0 || tRank == 7))
+    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;
+  } else if(fFile == ALG_CASTLE) {
+       // [HGM] castle: this code can handle any type of free castling
+       // it does not check if Rook and King from squares correspond to the rights, as the
+       // user never enters such squares, but they are copied from the rights on entering o-o-o
+       int backRank, kRook, qRook, fKing, leftEmpty, rightEmpty, leftCheck, rightCheck, f;
+       if(!gs->castlingStyle) return 0;   // no castling in this variant
+       if(gs->onMove == WHITE) {
+               if(gs->wkmoved < 0) return 0; // King moved
+               fKing = gs->wkmoved;
+               backRank = 0;
+               kRook = gs->wkrmoved;
+               qRook = gs->wqrmoved;
+       } else {
+               if(gs->bkmoved < 0) return 0; // King moved
+               fKing = gs->bkmoved;
+               backRank = gs->ranks-1;
+               kRook = gs->bkrmoved;
+               qRook = gs->bqrmoved;
+       }
+       if((tRank > tFile ? qRook : kRook) < 0) return 0; // Rook moved
+       // here we verified rights do exist, so from squares (fRank and fKing) must be valid
+       if(gs->board[fRank][backRank] != (ROOK | gs->onMove) ) return 0; // only with own Rook
+       if(gs->board[fKing][backRank] != (KING | gs->onMove) ) return 0; // only with own King
+
+       // by now we know that K and R are in correct position, and still have rights
+       if(tRank > tFile) { // R ends right of K: q-side
+               leftEmpty  = fRank < tFile ? fRank+1 : tFile+1;
+               rightEmpty = tRank < fKing ? fKing-1 : tRank-1;
+       } else { // k-side
+               leftEmpty  = tRank < fKing ? tRank+1 : fKing+1;
+               rightEmpty = fRank < tFile ? fRank-1 : tFile-1;
+       }
+       for(f=leftEmpty; f<=rightEmpty; f++) // check if other pieces block castling
+               if(f != fRank && f != fKing && gs->board[f][backRank] != NOPIECE) return 0;
+
+       leftCheck  = fKing < tFile ? fKing : tFile+1;
+       rightCheck = fKing < tFile ? tFile-1 : fKing;
+       for(f=leftCheck; f<=rightCheck; f++) // check if King passes attacked square or was in check
+               if(is_square_attacked(gs, f, backRank)) return 0;
+
+       return 1; // passed all tests
   } else {
     move_piece = piecetype(gs->board[fFile][fRank]);
   }
@@ -622,9 +1387,88 @@ int legal_move(struct game_state_t * gs,
   case ROOK:
     legal = legal_rook_move(gs, fFile, fRank, tFile, tRank);
     break;
+  case HAWK:
+  case CARDINAL:
+  case PRINCESS:
+    legal = legal_cardinal_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case SELEPHANT:
+  case MARSHALL:
+  case EMPRESS:
+    legal = legal_marshall_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case MAN:
+  case MAN2:
+    legal = legal_man_move(gs, fFile, fRank, tFile, tRank);
+    break;
   case QUEEN:
     legal = legal_queen_move(gs, fFile, fRank, tFile, tRank);
     break;
+  case ELEPHANT:
+    legal = legal_elephant_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case AMAZON:
+    legal = legal_amazon_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case WOODY:
+    legal = legal_woody_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case SQUIRREL:
+    legal = legal_squirrel_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case MASTODON:
+    legal = legal_mastodon_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case CENTAUR:
+    legal = legal_centaur_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case HORSE:
+    legal = legal_horse_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case FERZ:
+  case FERZ2:
+    legal = legal_ferz_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case MANDARIN:
+    legal = legal_mandarin_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case WAZIR:
+    legal = legal_wazir_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case ALFIL:
+  case ALFIL2:
+    legal = legal_alfil_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case MODERNELEPHANT:
+    legal = legal_modernelephant_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case PRIESTESS:
+    legal = legal_priestess_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case MINISTER:
+    legal = legal_minister_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case SILVER:
+    legal = legal_silver_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case GOLD:
+    legal = legal_gold_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case LANCE:
+    legal = legal_lance_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case CANNON:
+    legal = legal_cannon_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case DRAGONHORSE:
+    legal = legal_dragonhorse_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case DRAGONKING:
+    legal = legal_dragonking_move(gs, fFile, fRank, tFile, tRank);
+    break;
+  case HONORABLEHORSE:
+    legal = legal_honorablehorse_move(gs, fFile, fRank, tFile, tRank);
+    break;
   case KING:
     legal = legal_king_move(gs, fFile, fRank, tFile, tRank);
     break;
@@ -642,6 +1486,7 @@ int legal_move(struct game_state_t * gs,
 static int move_calculate(struct game_state_t * gs, struct move_t * mt, int promote)
 {
   struct game_state_t fakeMove;
+  int gating = 0;
 
   mt->pieceCaptured = gs->board[mt->toFile][mt->toRank];
   mt->enPassant = 0;           /* Don't know yet, let execute move take care
@@ -651,12 +1496,66 @@ static int move_calculate(struct game_state_t * gs, struct move_t * mt, int prom
     sprintf(mt->moveString, "%s/%c%c-%c%d",
            wpstring[mt->fromRank],
                DROP_CHAR, DROP_CHAR,
-           mt->toFile + 'a', mt->toRank + 1);
+           mt->toFile + 'a', mt->toRank + 1 - (gs->ranks>9));
+  } else if(mt->fromFile == ALG_CASTLE) { 
+       // [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 ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == PAWN) &&
-      ((mt->toRank == 0) || (mt->toRank == 7))) {
-    mt->piecePromotionTo = promote |
-      (colorval(gs->board[mt->fromFile][mt->fromRank]));
+  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]));
+  } else
+  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))) {
+    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=PIECES-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 promotion was explicitly specified!
+    if(!gs->pawnDblStep && promote == PRINCESS) promote = MAN2;
+    if(!gs->pawnDblStep && promote != FERZ2 && promote != MAN2) promote = FERZ; // [HGM] kludge to recognize shatranj
+    // 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 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
+    // now we must test virginity of the moved piece. Yegh!
+    for (i = g->numHalfMoves-2; i > 0; i -= 2) {
+      if (g->moveList[i].toFile == mt->fromFile && g->moveList[i].toRank == mt->fromRank) return MOVE_ILLEGAL;
+    }
+    gating = 1; // gating OK; remember we did it for check test
   } else {
     mt->piecePromotionTo = NOPIECE;
   }
@@ -666,17 +1565,23 @@ static int move_calculate(struct game_state_t * gs, struct move_t * mt, int prom
   } else {
     mt->doublePawn = -1;
   }
+#if 0
   if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) &&
-      (mt->fromFile == 4) && (mt->toFile == 2)) {
+      (mt->fromFile == gs->files/2) && (mt->toFile == 2) &&
+       mt->fromRank == mt->toRank) {
     sprintf(mt->moveString, "o-o-o");
   } else if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) &&
-            (mt->fromFile == 4) && (mt->toFile == 6)) {
+            (mt->fromFile == gs->files/2) && (mt->toFile == gs->files-2) &&
+               mt->fromRank == mt->toRank) {
     sprintf(mt->moveString, "o-o");
   } else {
+#else
+  {
+#endif
     sprintf(mt->moveString, "%s/%c%d-%c%d",
            wpstring[piecetype(gs->board[mt->fromFile][mt->fromRank])],
-           mt->fromFile + 'a', mt->fromRank + 1,
-           mt->toFile + 'a', mt->toRank + 1);
+           mt->fromFile + 'a', mt->fromRank + 1 - (gs->ranks>9),
+           mt->toFile + 'a', mt->toRank + 1 - (gs->ranks>9));
   }
   }
   /* Replace this with an algabraic de-parser */
@@ -685,6 +1590,7 @@ static int move_calculate(struct game_state_t * gs, struct move_t * mt, int prom
   fakeMove = *gs;
   /* Calculates enPassant also */
   execute_move(&fakeMove, mt, 0);
+  if(gating) fakeMove.board[mt-fromFile][mt->fromRank] = NOPIECE; // [HGM] gating is only legal if non-gating move was (weird, but true)
 
   /* Does making this move leave ME in check? */
   if (in_check(&fakeMove))
@@ -699,6 +1605,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;
@@ -707,7 +1614,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;
@@ -722,15 +1629,15 @@ int in_check(struct game_state_t * gs)
 
   /* Find the king */
   if (gs->onMove == WHITE) {
-    for (f = 0; f < 8 && kf < 0; f++)
-      for (r = 0; r < 8 && kf < 0; r++)
+    for (f = 0; f < gs->files && kf < 0; f++)
+      for (r = 0; r < gs->ranks && kf < 0; r++)
        if (gs->board[f][r] == B_KING) {
          kf = f;
          kr = r;
        }
   } else {
-    for (f = 0; f < 8 && kf < 0; f++)
-      for (r = 0; r < 8 && kf < 0; r++)
+    for (f = 0; f < gs->files && kf < 0; f++)
+      for (r = 0; r < gs->ranks && kf < 0; r++)
        if (gs->board[f][r] == W_KING) {
          kf = f;
          kr = r;
@@ -741,7 +1648,7 @@ int in_check(struct game_state_t * gs)
     return 0;
   }
   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 (legal_move(gs, f, r, kf, kr)) {        /* In Check? */
       return 1;
     }
@@ -758,7 +1665,7 @@ int has_legal_move(struct game_state_t * gs)
   int numpossible = 0;
 
   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);) {
     switch (piecetype(gs->board[f][r])) {
     case PAWN:
       possible_pawn_moves(gs, f, r, possiblef, possibler, &numpossible);
@@ -772,9 +1679,88 @@ int has_legal_move(struct game_state_t * gs)
     case ROOK:
       possible_rook_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
+    case HAWK:
+    case CARDINAL:
+    case PRINCESS:
+      possible_cardinal_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case SELEPHANT:
+    case MARSHALL:
+    case EMPRESS:
+      possible_marshall_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case MAN:
+    case MAN2:
+      possible_man_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
     case QUEEN:
       possible_queen_moves(gs, f, r, possiblef, possibler, &numpossible);
       break;
+    case ELEPHANT:
+      possible_elephant_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case AMAZON:
+      possible_amazon_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case WOODY:
+      possible_woody_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case SQUIRREL:
+      possible_squirrel_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case MASTODON:
+      possible_mastodon_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case CENTAUR:
+      possible_centaur_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case HORSE:
+      possible_horse_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case FERZ:
+    case FERZ2:
+      possible_ferz_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case MANDARIN:
+      possible_mandarin_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case WAZIR:
+      possible_wazir_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case ALFIL:
+    case ALFIL2:
+      possible_alfil_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case MODERNELEPHANT:
+      possible_modernelephant_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case PRIESTESS:
+      possible_priestess_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case MINISTER:
+      possible_minister_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case SILVER:
+      possible_silver_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case GOLD:
+      possible_gold_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case CANNON:
+      possible_cannon_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case LANCE:
+      possible_lance_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case DRAGONHORSE:
+      possible_dragonhorse_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case DRAGONKING:
+      possible_dragonking_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
+    case HONORABLEHORSE:
+      possible_honorablehorse_moves(gs, f, r, possiblef, possibler, &numpossible);
+      break;
     case KING:
       kf = f;
       kr = r;
@@ -791,14 +1777,22 @@ int has_legal_move(struct game_state_t * gs)
   }
 
   /* IanO:  if we got here, then kf and kr must be set */
-  if (gs->gameNum >=0 && game_globals.garray[gs->gameNum].link >= 0) {
+  if (gs->gameNum >=0 && game_globals.garray[gs->gameNum].link >= 0
+       || gs->holdings) { // [HGM] zh: also in 2-player games with drops
     /* bughouse: potential drops as check interpositions */
     gs->holding[gs->onMove==WHITE ? 0 : 1][QUEEN - 1]++;
     for (f=kf-1; f<=kf+1; f++) for (r=kr-1; r<=kr+1; r++) {
-      if (f>=0 && f<8 && r>=0 && r<8 && gs->board[f][r] == NOPIECE) {
+      if (f>=0 && f<gs->files && r>=0 && r<gs->ranks && gs->board[f][r] == NOPIECE) {
        /* try a drop next to the king */
        if (legal_andcheck_move(gs, ALG_DROP, QUEEN, f, r)) {
          gs->holding[gs->onMove==WHITE ? 0 : 1][QUEEN - 1]--;
+         // OK, so we have an interposing drop. But do we have something to drop?
+         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 (legal_andcheck_move(gs, ALG_DROP, i, f, r)) return 1;
+               return 0;
+         }
          return 1;
        }
       }
@@ -823,38 +1817,53 @@ int parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt, int pro
     break;
   case MS_COMP:
     mt->fromFile = mstr[0] - 'a';
-    mt->fromRank = mstr[1] - '1';
+    mt->fromRank = mstr[1] - '1' + (gs->ranks>9);
     mt->toFile = mstr[2] - 'a';
-    mt->toRank = mstr[3] - '1';
+    mt->toRank = mstr[3] - '1' + (gs->ranks>9);
     break;
   case MS_COMPDASH:
     mt->fromFile = mstr[0] - 'a';
-    mt->fromRank = mstr[1] - '1';
+    mt->fromRank = mstr[1] - '1' + (gs->ranks>9);
     mt->toFile = mstr[3] - 'a';
-    mt->toRank = mstr[4] - '1';
+    mt->toRank = mstr[4] - '1' + (gs->ranks>9);
     break;
   case MS_KCASTLE:
-    mt->fromFile = 4;
-    mt->toFile = 6;
+#if 0
+    mt->fromFile = gs->files/2;
+    mt->toFile = gs->files-2;
     if (gs->onMove == WHITE) {
       mt->fromRank = 0;
       mt->toRank = 0;
     } else {
-      mt->fromRank = 7;
-      mt->toRank = 7;
+      mt->fromRank = gs->ranks-1;
+      mt->toRank = gs->ranks-1;
     }
     break;
+#endif
+    // [HGM] castle: for now always assume Fischer-type castling (of which normal castling is a special case).
+    mt->fromFile = ALG_CASTLE;
+    mt->toFile = gs->files-2;
+    mt->fromRank = gs->onMove == WHITE ? gs->wkrmoved : gs->bkrmoved;
+    mt->toRank = mt->toFile-1; // R next to K
+    break;    
   case MS_QCASTLE:
-    mt->fromFile = 4;
+#if 0
+    mt->fromFile = gs->files/2;
     mt->toFile = 2;
     if (gs->onMove == WHITE) {
       mt->fromRank = 0;
       mt->toRank = 0;
     } else {
-      mt->fromRank = 7;
-      mt->toRank = 7;
+      mt->fromRank = gs->ranks-1;
+      mt->toRank = gs->ranks-1;
     }
     break;
+#endif
+    mt->fromFile = ALG_CASTLE;
+    mt->toFile = 2;
+    mt->fromRank = gs->onMove == WHITE ? gs->wqrmoved : gs->bqrmoved;
+    mt->toRank = mt->toFile+1;
+    break;
   case MS_ALG:
     /* Fills in the mt structure */
     if ((result = alg_parse_move(mstr, gs, mt)) != MOVE_OK)
@@ -864,13 +1873,28 @@ int parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt, int pro
     return MOVE_ILLEGAL;
     break;
   }
-  if (!legal_move(gs, mt->fromFile, mt->fromRank, mt->toFile, mt->toRank)) {
+  if((mt->fromRank >= gs->ranks || mt->fromRank < 0 || mt->fromFile >= gs->files) &&
+     mt->fromFile != ALG_DROP && mt->fromFile != ALG_CASTLE
+     || mt->toRank < 0 || mt->toRank >= gs->ranks || mt->toFile >= gs->files)
+    return MOVE_ILLEGAL; // [HGM] make sure move stays on board
+
+  if (!(result = legal_move(gs, mt->fromFile, mt->fromRank, mt->toFile, mt->toRank)))
     return MOVE_ILLEGAL;
-  }
+
+  if(result == 2) { // [HGM] castle: orthodox castling was given as King move; convert it to new format
+       if(mt->fromFile - mt->toFile > 1) { // Q-side
+               mt->fromRank = 0; 
+               mt->toRank   = mt->toFile+1;
+       } else if(mt->toFile - mt->fromFile > 1) { // K-side
+               mt->fromRank = gs->files-1;
+               mt->toRank   = mt->toFile-1;
+       }
+       mt->fromFile = ALG_CASTLE;
+    }
 
   if (mt->piecePromotionTo != NOPIECE) {
          promote = piecetype(mt->piecePromotionTo);
-  }
+  } else if(gs->promoType == 3 && promote == MASTODON) promote = GOLD;
 
   return move_calculate(gs, mt, promote);
 }
@@ -881,7 +1905,7 @@ int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_st
 {
   int movedPiece;
   int tookPiece;
-  int i, j, foobar;
+  int i, j, foobar, wCnt, bCnt, king, rook;
 
   if (mt->fromFile == ALG_DROP) {
     movedPiece = mt->fromRank;
@@ -890,23 +1914,50 @@ int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_st
     gs->board[mt->toFile][mt->toRank] = movedPiece | gs->onMove;
     if (gs->gameNum >= 0)
       gs->lastIrreversable = game_globals.garray[gs->gameNum].numHalfMoves;
+  } else if(mt->fromFile == ALG_CASTLE) {
+    int backRank, fKing;
+    // [HGM] castle: perform castling
+    if(gs->onMove == WHITE) {
+       backRank = 0;
+       fKing = gs->wkmoved;
+       gs->wkmoved = -gs->wkmoved-2;
+    } else {
+       backRank = gs->ranks-1;
+       fKing = gs->bkmoved;
+       gs->bkmoved = -gs->bkmoved-2;
+    }
+    // move Rook & King, in a way that is resistant to ending where they started (for FRC!)
+    rook = gs->board[mt->fromRank][backRank];    // first remember
+    king = gs->board[fKing][backRank];
+    gs->board[mt->fromRank][backRank] = NOPIECE; // then erase
+    gs->board[fKing][backRank] = NOPIECE;
+    gs->board[mt->toRank][backRank] = rook;      // then put back
+    gs->board[mt->toFile][backRank] = king;
   } else {
   movedPiece = gs->board[mt->fromFile][mt->fromRank];
   tookPiece = gs->board[mt->toFile][mt->toRank];
-  if (mt->piecePromotionTo == NOPIECE) {
+  if(gs->drops == 2 && mt->piecePromotionTo != NOPIECE && pieceType(movedPiece) != PAWN) { // [HGM] Seirawan-style gating
     gs->board[mt->toFile][mt->toRank] = gs->board[mt->fromFile][mt->fromRank];
+    gs->board[mt->fromFile][mt->fromRank] = mt->piecePromotionTo | gs->onMove;;
+    gs->holding[gs->onMove==WHITE ? 0 : 1][mt->piecePromotionTo-1]--; // remove gated piece from holdings
   } else {
-    gs->board[mt->toFile][mt->toRank] = mt->piecePromotionTo | gs->onMove;
+    if (mt->piecePromotionTo == NOPIECE) {
+      gs->board[mt->toFile][mt->toRank] = gs->board[mt->fromFile][mt->fromRank];
+    } else {
+      gs->board[mt->toFile][mt->toRank] = mt->piecePromotionTo | gs->onMove;
+      if(gs->promoType == 2) gs->holding[gs->onMove][mt->piecePromotionTo-1]--;
+    }
+    gs->board[mt->fromFile][mt->fromRank] = NOPIECE;
   }
-  gs->board[mt->fromFile][mt->fromRank] = NOPIECE;
   /* Check if irreversable */
-  if ((piecetype(movedPiece) == PAWN) || (tookPiece != NOPIECE)) {
+  if ((piecetype(movedPiece) == PAWN) && (mt->fromRank != mt->toRank) // [HGM] XQ: sideway Pawn move reversible!
+                       || (tookPiece != NOPIECE)) {
     if (gs->gameNum >= 0)
       gs->lastIrreversable = game_globals.garray[gs->gameNum].numHalfMoves;
   }
   /* Check if this move is en-passant */
   if ((piecetype(movedPiece) == PAWN) && (mt->fromFile != mt->toFile) &&
-      (tookPiece == NOPIECE)) {
+      (tookPiece == NOPIECE) && !gs->palace) { // [HGM] XQ: no e.p. in sideway xiangqi moves
     if (gs->onMove == WHITE) {
       mt->pieceCaptured = B_PAWN;
     } else {
@@ -920,7 +1971,7 @@ int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_st
     gs->board[mt->toFile][mt->fromRank] = NOPIECE;
   }
   /* Check en-passant flags for next moves */
-  for (i = 0; i < 8; i++) {
+  for (i = 0; i < gs->files; i++) {
     gs->ep_possible[0][i] = 0;
     gs->ep_possible[1][i] = 0;
   }
@@ -944,49 +1995,53 @@ int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_st
    ((mt->fromRank == mt->toRank + 2) || (mt->fromRank + 2 == mt->toRank))) {
     /* Should turn on enpassent flag if possible */
     if (gs->onMove == WHITE) {
-      if ((mt->toFile < 7) && gs->board[mt->toFile + 1][3] == B_PAWN) {
+      if ((mt->toFile < gs->files-1) && gs->board[mt->toFile + 1][mt->toRank] == B_PAWN) {
        gs->ep_possible[1][mt->toFile + 1] = -1;
       }
-      if ((mt->toFile - 1 >= 0) && gs->board[mt->toFile - 1][3] == B_PAWN) {
+      if ((mt->toFile - 1 >= 0) && gs->board[mt->toFile - 1][mt->toRank] == B_PAWN) {
        gs->ep_possible[1][mt->toFile - 1] = 1;
       }
     } else {
-      if ((mt->toFile < 7) && gs->board[mt->toFile + 1][4] == W_PAWN) {
+      if ((mt->toFile < gs->files-1) && gs->board[mt->toFile + 1][mt->toRank] == W_PAWN) {
        gs->ep_possible[0][mt->toFile + 1] = -1;
       }
-      if ((mt->toFile - 1 >= 0) && gs->board[mt->toFile - 1][4] == W_PAWN) {
+      if ((mt->toFile - 1 >= 0) && gs->board[mt->toFile - 1][mt->toRank] == W_PAWN) {
        gs->ep_possible[0][mt->toFile - 1] = 1;
       }
     }
   }
-  if ((piecetype(movedPiece) == ROOK) && (mt->fromFile == 0)) {
-    if ((mt->fromRank == 0) && (gs->onMove == WHITE))
-      gs->wqrmoved = 1;
-    if ((mt->fromRank == 7) && (gs->onMove == BLACK))
-      gs->bqrmoved = 1;
+  if ((piecetype(movedPiece) == ROOK) && (mt->fromRank == 0) && (gs->onMove == WHITE)) {
+    if (mt->fromFile == gs->wqrmoved) // [HGM] castle: flip w.r.t. -1 to remember original
+      gs->wqrmoved = -gs->wqrmoved-2;
+    if (mt->fromFile == gs->wkrmoved)
+      gs->wkrmoved = -gs->wkrmoved-2;
   }
-  if ((piecetype(movedPiece) == ROOK) && (mt->fromFile == 7)) {
-    if ((mt->fromRank == 0) && (gs->onMove == WHITE))
-      gs->wkrmoved = 1;
-    if ((mt->fromRank == 7) && (gs->onMove == BLACK))
-      gs->bkrmoved = 1;
+  if ((piecetype(movedPiece) == ROOK) && (mt->fromRank == gs->ranks-1) && (gs->onMove == BLACK)) {
+    if (mt->fromFile == gs->bqrmoved)
+      gs->bqrmoved = -gs->bqrmoved-2;
+    if (mt->fromFile == gs->bkrmoved)
+      gs->bkrmoved = -gs->bkrmoved-2;
   }
   if (piecetype(movedPiece) == KING) {
-    if (gs->onMove == WHITE)
-      gs->wkmoved = 1;
-    else
-      gs->bkmoved = 1;
+    if ((gs->onMove == WHITE) && (mt->fromFile == gs->wkmoved))
+      gs->wkmoved = -gs->wkmoved-2;
+    if ((gs->onMove == BLACK) && (mt->fromFile == gs->bkmoved))
+      gs->bkmoved = -gs->bkmoved-2;
   }
+#if 0
   if ((piecetype(movedPiece) == KING) &&
-      ((mt->fromFile == 4) && ((mt->toFile == 6)))) {  /* Check for KS castling */
-    gs->board[5][mt->toRank] = gs->board[7][mt->toRank];
-    gs->board[7][mt->toRank] = NOPIECE;
+      ((mt->fromFile == gs->files/2) && (mt->toFile == gs->files-2)) &&
+       mt->fromRank == mt->toRank) {   /* Check for KS castling */
+    gs->board[gs->files-3][mt->toRank] = gs->board[gs->files-1][mt->toRank];
+    gs->board[gs->files-1][mt->toRank] = NOPIECE;
   }
   if ((piecetype(movedPiece) == KING) &&
-      ((mt->fromFile == 4) && ((mt->toFile == 2)))) {  /* Check for QS castling */
+      ((mt->fromFile == gs->files/2) && (mt->toFile == 2)) &&
+       mt->fromRank == mt->toRank) {   /* Check for QS castling */
     gs->board[3][mt->toRank] = gs->board[0][mt->toRank];
     gs->board[0][mt->toRank] = NOPIECE;
   }
+#endif
   }
   if (gs->onMove == BLACK)
     gs->moveNum++;
@@ -1002,12 +2057,14 @@ int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_st
       /* Check for stalemate */
       gs->onMove = CToggle(gs->onMove);
       if (!has_legal_move(gs))
-       return MOVE_STALEMATE;
+       return gs->stalemate ? MOVE_STALEMATE : MOVE_CHECKMATE; // [HGM] in XQ and shatranj stalemate loses
+    }
 /* loon: check for insufficient mating material, first try */
-      foobar = 0;
-      for (i=0; i<8; i++) {
-        for (j=0; j<8; j++) {
-          switch(piecetype(gs->board[i][j])) {
+      foobar = wCnt = bCnt = 0;
+      for (i=0; i<gs->files; i++) {
+        for (j=0; j<gs->ranks; j++) {
+         int p = gs->board[i][j];
+          switch(piecetype(p)) {
             case KNIGHT:
             case BISHOP:
               foobar++;
@@ -1019,14 +2076,20 @@ int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_st
               foobar = 2;
               break;
           }
+         if(p != NOPIECE && iscolor(p, WHITE)) wCnt++;
+         if(iscolor(p, BLACK)) bCnt++;
         }
       }
-      if (foobar < 2)
+      if(gs->bareKingLoses) { // [HGM] with bare-King-loses rule only KK is insuff. material
+       if(gs->onMove == BLACK && wCnt == 1 && bCnt > 1) return MOVE_BARE;
+       if(gs->onMove == WHITE && bCnt == 1 && wCnt > 1) return MOVE_BARE;
+       if(bCnt == 1 && wCnt == 1) return MOVE_NOMATERIAL;
+      } else if (foobar < 2)
         return MOVE_NOMATERIAL;
-    }
   } else {
     gs->onMove = CToggle(gs->onMove);
   }
+
   return MOVE_OK;
 }
 
@@ -1046,6 +2109,31 @@ int backup_move(int g, int mode)
   if (m->toFile < 0) {
     return MOVE_ILLEGAL;
   }
+  if(m->fromFile == ALG_CASTLE) {
+    // [HGM] castling in new format. Derive K and R moves
+    int rank, kingFromFile;
+    if(m->color == WHITE) {
+      rank = 0;
+      kingFromFile = -gs->wkmoved-2;
+      if(kingFromFile<0) kingFromFile = -kingFromFile-2; // safety catch; should never happen?
+      gs->wkmoved = kingFromFile;
+      if(m->toRank > m->toFile) gs->wqrmoved = m->fromRank;
+      else gs->wkrmoved = m->fromRank;
+    } else {
+      rank = gs->ranks-1;
+      kingFromFile = -gs->bkmoved-2;
+      if(kingFromFile<0) kingFromFile = -kingFromFile-2; // safety catch; should never happen?
+      gs->bkmoved = kingFromFile;
+      if(m->toRank > m->toFile) gs->bqrmoved = m->fromRank;
+      else gs->bkrmoved = m->fromRank;
+    }
+    // remove first, as one might come back to a square the other left
+    gs->board[m->toFile  ][rank] = NOPIECE; // King toSqr
+    gs->board[m->toRank  ][rank] = NOPIECE; // Rook toSqr
+    gs->board[m->fromRank][rank] = ROOK | m->color; // Rook fromSqr
+    gs->board[kingFromFile][rank] = KING | m->color; // King fromSquare
+    goto cleanupMove;
+  }
   gs->board[m->fromFile][m->fromRank] = gs->board[m->toFile][m->toRank];
   if (m->piecePromotionTo != NOPIECE) {
     gs->board[m->fromFile][m->fromRank] = PAWN |
@@ -1058,48 +2146,49 @@ int backup_move(int g, int mode)
   *******************/
   if (piecetype(gs->board[m->fromFile][m->fromRank]) == ROOK) {
     if (m->color == WHITE) {
-      if ((m->fromFile == 0) && (m->fromRank == 0)) {
+      if ((m->fromFile == -gs->wqrmoved-2) && (m->fromRank == 0)) {
        for (i = 2; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
          m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
-         if ((m1->fromFile == 0) && (m1->fromRank == 0))
+         if ((m1->fromFile == -gs->wqrmoved-2) && (m1->fromRank == 0))
            break;
        }
        if (i == game_globals.garray[g].numHalfMoves - 1)
-         gs->wqrmoved = 0;
+         gs->wqrmoved = m->fromFile;
       }
-      if ((m->fromFile == 7) && (m->fromRank == 0)) {
+      if ((m->fromFile == -gs->wkrmoved-2) && (m->fromRank == 0)) {
        for (i = 2; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
          m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
-         if ((m1->fromFile == 7) && (m1->fromRank == 0))
+         if ((m1->fromFile == -gs->wkrmoved-2) && (m1->fromRank == 0))
            break;
        }
        if (i == game_globals.garray[g].numHalfMoves - 1)
-         gs->wkrmoved = 0;
+         gs->wkrmoved = m->fromFile;
       }
     } else {
-      if ((m->fromFile == 0) && (m->fromRank == 7)) {
+      if ((m->fromFile == -gs->bqrmoved-2) && (m->fromRank == gs->ranks-1)) {
        for (i = 3; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
          m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
-         if ((m1->fromFile == 0) && (m1->fromRank == 0))
+         if ((m1->fromFile == -gs->bkrmoved-2) && (m1->fromRank == gs->ranks-1))
            break;
        }
        if (i == game_globals.garray[g].numHalfMoves - 1)
-         gs->bqrmoved = 0;
+         gs->bqrmoved = m->fromFile;
       }
-      if ((m->fromFile == 7) && (m->fromRank == 7)) {
+      if ((m->fromFile == -gs->bkrmoved-2) && (m->fromRank == gs->ranks-1)) {
        for (i = 3; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
          m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
-         if ((m1->fromFile == 7) && (m1->fromRank == 0))
+         if ((m1->fromFile == -gs->wkrmoved-2) && (m1->fromRank == gs->ranks-1))
            break;
        }
        if (i == game_globals.garray[g].numHalfMoves - 1)
-         gs->bkrmoved = 0;
+         gs->bkrmoved = m->fromFile;
       }
     }
   }
   if (piecetype(gs->board[m->fromFile][m->fromRank]) == KING) {
     gs->board[m->toFile][m->toRank] = m->pieceCaptured;
-
+#if 0
+    /* [HGM] castlings are already intercepted due to new format; this code wrecks knightmate! */
     if (m->toFile - m->fromFile == 2) {
       gs->board[7][m->fromRank] = ROOK |
        colorval(gs->board[m->fromFile][m->fromRank]);
@@ -1136,6 +2225,7 @@ int backup_move(int g, int mode)
       }
       goto cleanupMove;
     }
+#endif
     /******************
        When takeback a _first_ move of king (not the castling),
        the ?kmoved variable must be cleared . To check, if the move is first,
@@ -1144,24 +2234,24 @@ int backup_move(int g, int mode)
 
     if (m->color == WHITE) {
 
-      if ((m->fromFile == 4) && (m->fromRank == 0)) {
+      if ((m->fromFile == -gs->wkmoved-2) && (m->fromRank == 0)) {
        for (i = 2; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
          m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
-         if ((m1->fromFile == 4) && (m1->fromRank == 0))
+         if ((m1->fromFile == gs->wkmoved-2) && (m1->fromRank == 0))
            break;
        }
        if (i == game_globals.garray[g].numHalfMoves - 1)
-         gs->wkmoved = 0;
+         gs->wkmoved = m->fromFile;
       }
     } else {
-      if ((m->fromFile == 4) && (m->fromRank == 7)) {
+      if ((m->fromFile == -gs->bkmoved-2) && (m->fromRank == gs->ranks-1)) {
        for (i = 3; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
          m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
-         if ((m1->fromFile == 4) && (m1->fromRank == 7))
+         if ((m1->fromFile == -gs->bkmoved-2) && (m1->fromRank == gs->ranks-1))
            break;
        }
        if (i == game_globals.garray[g].numHalfMoves - 1)
-         gs->bkmoved = 0;
+         gs->bkmoved = m->fromFile;
       }
     }
   }
@@ -1173,6 +2263,9 @@ int backup_move(int g, int mode)
     goto cleanupMove;
   }
   gs->board[m->toFile][m->toRank] = m->pieceCaptured;
+  if(gs->board[m->fromFile][m->fromRank] != NOPIECE) { // [HGM] from-square occupied, 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!)
+  }
 cleanupMove:
   if (game_globals.garray[g].status != GAME_EXAMINE) {
     game_update_time(g);
@@ -1248,18 +2341,18 @@ cleanupMove:
                             &game_globals.garray[g].examMoveList[game_globals.garray[g].numHalfMoves - 1];
     if (piecetype(gs->board[m1->toFile][m1->toRank]) == PAWN) {
       if ((m1->toRank - m1->fromRank) == 2) {
-       if ((m1->toFile < 7) && gs->board[m1->toFile + 1][3] == B_PAWN) {
+       if ((m1->toFile < gs->files-1) && gs->board[m1->toFile + 1][m1->toRank] == B_PAWN) {
          gs->ep_possible[1][m1->toFile + 1] = -1;
        }
-       if ((m1->toFile - 1 >= 0) && gs->board[m1->toFile - 1][3] == B_PAWN) {
+       if ((m1->toFile - 1 >= 0) && gs->board[m1->toFile - 1][m1->toRank] == B_PAWN) {
          gs->ep_possible[1][m1->toFile - 1] = 1;
        }
       }
       if ((m1->toRank - m1->fromRank) == -2) {
-       if ((m1->toFile < 7) && gs->board[m1->toFile + 1][4] == W_PAWN) {
+       if ((m1->toFile < gs->files-1) && gs->board[m1->toFile + 1][m1->toRank] == W_PAWN) {
          gs->ep_possible[0][m1->toFile + 1] = -1;
        }
-       if ((m1->toFile - 1 >= 0) && gs->board[m1->toFile - 1][4] == W_PAWN) {
+       if ((m1->toFile - 1 >= 0) && gs->board[m1->toFile - 1][m1->toRank] == W_PAWN) {
          gs->ep_possible[0][m1->toFile - 1] = 1;
        }
       }