Seirawan gatings on castling
authorH.G. Muller <h.g.muller@hccnet.nl>
Thu, 1 Dec 2011 11:49:36 +0000 (12:49 +0100)
committerH.G. Muller <h.g.muller@hccnet.nl>
Thu, 1 Dec 2011 18:52:31 +0000 (19:52 +0100)
Castlings can now have promo suffix in any move format. RxK is understood
as castling, and in combination with promosuffix will cause gating at
the Rook square. For now it is printed as h1e1=h in SAN.

lasker-2.2.3/src/algcheck.c
lasker-2.2.3/src/board.c
lasker-2.2.3/src/eco.c
lasker-2.2.3/src/gamedb.c
lasker-2.2.3/src/gameproc.c
lasker-2.2.3/src/movecheck.c

index 1c1ff75..33f6b28 100644 (file)
@@ -529,12 +529,18 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
     piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
   }
 
     piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
   }
 
-  if ((mt->fromFile == ALG_CASTLE) && (mt->toFile > mt->toRank)) { // [HGM] castle: K ends right of R
-    strcpy(mStr, "O-O");
-    goto check;
-  }
-  if ((mt->fromFile == ALG_CASTLE) && (mt->toFile < mt->toRank)) { // [HGM] castle: K ends left of R
-    strcpy(mStr, "O-O-O");
+  if (mt->fromFile == ALG_CASTLE) {
+    int r = gs->onMove == WHITE ? 1 : gs->ranks;
+    if(mt->toFile > mt->toRank) { // [HGM] castle: K ends right of R
+      strcpy(mStr, "O-O");
+    }
+    if (mt->toFile < mt->toRank) { // [HGM] castle: K ends left of R
+      strcpy(mStr, "O-O-O");
+    }
+    if(gs->drops == 2) {
+       if(mt->piecePromotionTo < 0) snprintf(mStr, 20, "%c%de%d", mt->fromRank + 'a', r, r);
+       goto suffix; // [HGM] in Seirawan castling can have gating suffix
+    }
     goto check;
   }
   strcpy(mStr, "");
     goto check;
   }
   strcpy(mStr, "");
@@ -690,10 +696,10 @@ char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
   }
   sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));
   strcat(mStr, tmp);
   }
   sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));
   strcat(mStr, tmp);
-
+suffix:
   if ((piece == PAWN || gs->promoType == 3 || gs->drops == 2) && (mt->piecePromotionTo != NOPIECE)) {
     strcat(mStr, "=");         /* = before promoting piece */
   if ((piece == PAWN || gs->promoType == 3 || gs->drops == 2) && (mt->piecePromotionTo != NOPIECE)) {
     strcat(mStr, "=");         /* = before promoting piece */
-    switch (piecetype(mt->piecePromotionTo)) {
+    switch (piecetype(abs(mt->piecePromotionTo))) {
     case KNIGHT:
       strcat(mStr, "N");
       break;
     case KNIGHT:
       strcat(mStr, "N");
       break;
index 8c92473..40e7037 100644 (file)
@@ -181,9 +181,6 @@ int board_init(int g,struct game_state_t *b, char *category, char *board)
        sprintf(b->variant, "wild/%d", wval);
     }
 
        sprintf(b->variant, "wild/%d", wval);
     }
 
-    if (board && !strcmp(board, "0"))
-       b->setup = 0; // [HGM] variant: any board in the default file "0" is supposed to be implied by the variant
-
     if (!strcmp(category, "knightmate")) {
       board_standard(b);
     } else if (!strcmp(category, "super")) {
     if (!strcmp(category, "knightmate")) {
       board_standard(b);
     } else if (!strcmp(category, "super")) {
@@ -386,6 +383,7 @@ char *board_to_string(char *wn, char *bn,
          game_globals.garray[b->gameNum].black_name);
   } else
     bstring[0] = '\0';
          game_globals.garray[b->gameNum].black_name);
   } else
     bstring[0] = '\0';
+
   if (bh && !IsMachineStyle(style))
     append_holding_display(bstring, b, orientation==BLACK);
 
   if (bh && !IsMachineStyle(style))
     append_holding_display(bstring, b, orientation==BLACK);
 
@@ -998,6 +996,9 @@ static int board_read_file(char *category, char *gname, struct game_state_t *gs)
 
   board_clear(gs);
   gs->setup = 1;
 
   board_clear(gs);
   gs->setup = 1;
+  if (gname && !strcmp(gname, "0"))
+       gs->setup = 0; // [HGM] variant: any board in the default file "0" is supposed to be implied by the variant
+
   while (!feof(fp)) {
     c = fgetc(fp);
     if (onNewLine) {
   while (!feof(fp)) {
     c = fgetc(fp);
     if (onNewLine) {
index 5d13738..5f98267 100644 (file)
@@ -48,66 +48,66 @@ static int ECO_entries, NIC_entries, LONG_entries;
 
 void FEN_to_board(char* FENpos, struct game_state_t* gs)
 {
 
 void FEN_to_board(char* FENpos, struct game_state_t* gs)
 {
-  int f,r;\r
-  char next;\r
-printf("FEN, var='%s'\n", gs->variant);\r
-  for (r=gs->ranks-1; r >=0; r--) {\r
-    f=0;\r
-    while (f<gs->files) {\r
-      next = *(FENpos++);\r
-      if (isalpha(next))\r
-        gs->board[f++][r] = CharToPiece(next, gs->variant);\r
-      else if (next != '/') {\r
-        int t = (next - '0');\r
-         if(*FENpos >= '0' && *FENpos <= '9') // [HGM] can be double-digit\r
-             t = 10*t + *(FENpos++) - '0';\r
-        do\r
-          gs->board[f++][r] = NOPIECE;\r
-        while (--t && f < gs->files);\r
-      }\r
-    }\r
-  }\r
-  if (*(FENpos + 1) == 'w') /* the char after the space */\r
-    gs->onMove = WHITE;\r
-  else\r
-    gs->onMove = BLACK;\r
+  int f,r;
+  char next;
+printf("FEN='%s', var='%s'\n", FENpos, gs->variant);
+  for (r=gs->ranks-1; r >=0; r--) {
+    f=0;
+    while (f<gs->files) {
+      next = *(FENpos++);
+      if (isalpha(next))
+        gs->board[f++][r] = CharToPiece(next, gs->variant);
+      else if (next != '/') {
+        int t = (next - '0');
+         if(*FENpos >= '0' && *FENpos <= '9') // [HGM] can be double-digit
+             t = 10*t + *(FENpos++) - '0';
+        do
+          gs->board[f++][r] = NOPIECE;
+        while (--t && f < gs->files);
+      }
+    }
+  }
+  if (*(FENpos + 1) == 'w') /* the char after the space */
+    gs->onMove = WHITE;
+  else
+    gs->onMove = BLACK;
 }
 
 /* converts a board to a FEN pos */
 
 static void board_to_FEN(char* FENpos, struct game_state_t* gs)
 {
 }
 
 /* converts a board to a FEN pos */
 
 static void board_to_FEN(char* FENpos, struct game_state_t* gs)
 {
-  int f,r,count;\r
-  char piece;  \r
-\r
-  for (r=gs->ranks-1; r>=0; r--) {\r
-    count = 0;\r
-    for (f=0;  f<gs->files; f++) {\r
-      if ((piece = PieceToChar(gs->board[f][r])) != ' ') {\r
-        if (count) { \r
-           if(count > 9) { count -= 10; *(FENpos++) = '1'; }\r
-          *(FENpos++) = count + '0';\r
-          count = 0;\r
-        }\r
-        *(FENpos++) = piece;\r
-      } else {\r
-        if (f == gs->files-1) {\r
-           if(count > 8) { count -= 10; *(FENpos++) = '1'; }\r
-          *(FENpos++) = count + '0' + 1;\r
-        } else\r
-          count++;\r
-      }\r
-    }\r
-    *(FENpos++) = '/';\r
-  }\r
-\r
-  *(--FENpos) = ' ';\r
-\r
-  if (gs->onMove == WHITE)\r
-    *(++FENpos) = 'w';\r
-  else\r
-    *(++FENpos) = 'b';\r
-  *(++FENpos) = '\0';\r
+  int f,r,count;
+  char piece;  
+
+  for (r=gs->ranks-1; r>=0; r--) {
+    count = 0;
+    for (f=0;  f<gs->files; f++) {
+      if ((piece = PieceToChar(gs->board[f][r])) != ' ') {
+        if (count) { 
+           if(count > 9) { count -= 10; *(FENpos++) = '1'; }
+          *(FENpos++) = count + '0';
+          count = 0;
+        }
+        *(FENpos++) = piece;
+      } else {
+        if (f == gs->files-1) {
+           if(count > 8) { count -= 10; *(FENpos++) = '1'; }
+          *(FENpos++) = count + '0' + 1;
+        } else
+          count++;
+      }
+    }
+    *(FENpos++) = '/';
+  }
+
+  *(--FENpos) = ' ';
+
+  if (gs->onMove == WHITE)
+    *(++FENpos) = 'w';
+  else
+    *(++FENpos) = 'b';
+  *(++FENpos) = '\0';
 }
 
 char *boardToFEN(int g)
 }
 
 char *boardToFEN(int g)
index 82eef2e..becb7a0 100644 (file)
@@ -588,6 +588,7 @@ char *movesToString(int g, int pgn)
       sprintf(tmp, "%d minutes, increment: %d seconds.\n\n", game_globals.garray[g].wInitTime / 600, game_globals.garray[g].wIncrement / 10);
     }
     strcat(gameString, tmp);
       sprintf(tmp, "%d minutes, increment: %d seconds.\n\n", game_globals.garray[g].wInitTime / 600, game_globals.garray[g].wIncrement / 10);
     }
     strcat(gameString, tmp);
+
     if(game_globals.garray[g].game_state.setup) { // [HGM] setup: print the initial position board
        char *q; struct game_state_t initial_gs; struct move_t ml[600]; int r, f;
 
     if(game_globals.garray[g].game_state.setup) { // [HGM] setup: print the initial position board
        char *q; struct game_state_t initial_gs; struct move_t ml[600]; int r, f;
 
@@ -607,7 +608,7 @@ char *movesToString(int g, int pgn)
         for (f = 0; f < 2; f++) {
           for (r = 0; r < initial_gs.files; r++)
              initial_gs.ep_possible[f][r] = 0;
         for (f = 0; f < 2; f++) {
           for (r = 0; r < initial_gs.files; r++)
              initial_gs.ep_possible[f][r] = 0;
-          for (r = PAWN; r <= QUEEN; r++)
+          for (r = PAWN; r <= PIECES-1; r++)
             initial_gs.holding[f][r-PAWN] = 0;
        }
        FEN_to_board(game_globals.garray[g].FENstartPos ,&initial_gs);
             initial_gs.holding[f][r-PAWN] = 0;
        }
        FEN_to_board(game_globals.garray[g].FENstartPos ,&initial_gs);
@@ -758,9 +759,15 @@ int CharToPiece(char c, char *variant)
   case 'q':
     return B_QUEEN;
   case 'E':
   case 'q':
     return B_QUEEN;
   case 'E':
+    if(!strcmp(variant, "seirawan")) return W_ELEPHANT;
     return W_ELEPHANT;
   case 'e':
     return W_ELEPHANT;
   case 'e':
+    if(!strcmp(variant, "seirawan")) return B_ELEPHANT;
     return B_ELEPHANT;
     return B_ELEPHANT;
+  case 'H':
+    return W_HAWK;
+  case 'h':
+    return B_HAWK;
   case 'K':
     return W_KING;
   case 'k':
   case 'K':
     return W_KING;
   case 'k':
@@ -814,9 +821,11 @@ char PieceToChar(int piece)
     return 'Q';
   case B_QUEEN:
     return 'q';
     return 'Q';
   case B_QUEEN:
     return 'q';
+  case W_SELEPHANT:
   case W_ELEPHANT:
   case W_EMPRESS:
     return 'E';
   case W_ELEPHANT:
   case W_EMPRESS:
     return 'E';
+  case B_SELEPHANT:
   case B_ELEPHANT:
   case B_EMPRESS:
     return 'e';
   case B_ELEPHANT:
   case B_EMPRESS:
     return 'e';
@@ -838,10 +847,12 @@ char PieceToChar(int piece)
   case B_WAZIR:
   case B_WOODY:
     return 'w';
   case B_WAZIR:
   case B_WOODY:
     return 'w';
+  case W_HAWK:
   case W_HORSE:
   case W_PRIESTESS:
   case W_NIGHTRIDER:
     return 'H';
   case W_HORSE:
   case W_PRIESTESS:
   case W_NIGHTRIDER:
     return 'H';
+  case B_HAWK:
   case B_HORSE:
   case B_PRIESTESS:
   case B_NIGHTRIDER:
   case B_HORSE:
   case B_PRIESTESS:
   case B_NIGHTRIDER:
index 4f50fa4..e6e59bd 100644 (file)
@@ -434,18 +434,6 @@ static int was_promoted(struct game *g, int f, int r)
   return 0;
 }
 
   return 0;
 }
 
-static int is_virgin(struct game *g, int f, int r)
-{
-  int i;
-
-  for (i = g->numHalfMoves-2; i > 0; i -= 2) {
-    if (g->moveList[i].toFile == f && g->moveList[i].toRank == r) {
-      return 0;
-    }
-  }
-  return 1;
-}
-
 int pIsPlaying (int p)
 {
        struct player *pp = &player_globals.parray[p];
 int pIsPlaying (int p)
 {
        struct player *pp = &player_globals.parray[p];
@@ -537,7 +525,7 @@ void process_move(int p, char *command)
 {
   struct player *pp = &player_globals.parray[p];
   struct game *gg;
 {
   struct player *pp = &player_globals.parray[p];
   struct game *gg;
-  int g, result, len, i;
+  int g, result, len, i, f;
   struct move_t move;
   unsigned now = 0;
 
   struct move_t move;
   unsigned now = 0;
 
@@ -575,7 +563,7 @@ void process_move(int p, char *command)
   if ((len = strlen(command)) > 1) {
     if (command[len - 2] == '=' || gg->game_state.drops == 2 && command[len - 2] == '/') { // [HGM] encode gating as promotion
 printf("promo '%s'\n", command);
   if ((len = strlen(command)) > 1) {
     if (command[len - 2] == '=' || gg->game_state.drops == 2 && command[len - 2] == '/') { // [HGM] encode gating as promotion
 printf("promo '%s'\n", command);
-      switch (tolower(command[strlen(command) - 1])) {
+      switch (tolower(command[len - 1])) {
       case 'n':
        pp->promote = KNIGHT;
        break;
       case 'n':
        pp->promote = KNIGHT;
        break;
index edeeea7..61bc64c 100644 (file)
 int is_move(const char *mstr)
 {
   int len = strlen(mstr);
 int is_move(const char *mstr)
 {
   int len = strlen(mstr);
-  if ((len > 3) && (mstr[len - 2] == '='))
-    len -= 2;
-
   /* remove the 'mates' marker */
   if (mstr[len - 1] == '#')
     len--;
 
   /* remove the 'mates' marker */
   if (mstr[len - 1] == '#')
     len--;
 
+  if ((len > 3) && (mstr[len - 2] == '=' || mstr[len - 2] == '/'))
+    len -= 2;
+
   if (len == 4) {              /* Test for e2e4 */
     if (isfile(mstr[0]) && isrank(mstr[1]) &&
        isfile(mstr[2]) && isrank(mstr[3])) {
   if (len == 4) {              /* Test for e2e4 */
     if (isfile(mstr[0]) && isrank(mstr[1]) &&
        isfile(mstr[2]) && isrank(mstr[3])) {
@@ -570,16 +570,18 @@ static int legal_king_move(struct game_state_t * gs, int ff, int fr, int tf, int
 
   if (gs->onMove == WHITE) {
     /* King side castling */
 
   if (gs->onMove == WHITE) {
     /* King side castling */
-    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) &&
+    if ((fr == 0) && (tr == 0) && ((ff == gs->files/2) && (tf == gs->files-2) ||
+                gs->drops == 2 && (tf == gs->files/2) && (ff == gs->files-1)) // [HGM] reverse Seirawan gating
+       && (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 */
        (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 == gs->files/2) && (tf == 2) && (gs->wkmoved >= 0)
-       && (gs->wqrmoved >= 0) && (gs->board[3][0] == NOPIECE) &&
+    if ((fr == 0) && (tr == 0) && ((ff == gs->files/2) && (tf == 2) ||
+                gs->drops == 2 && (tf == gs->files/2) && (ff == 0)) // [HGM] reverse Seirawan gating
+       && (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) &&
        (gs->board[gs->files/2-1][0] == NOPIECE) && (!is_square_attacked(gs, gs->files/2-1, 0)) &&
        (gs->board[2][0] == NOPIECE) && (gs->board[1][0] == NOPIECE) &&
        (gs->board[0][0] == W_ROOK) &&
        (gs->board[gs->files/2-1][0] == NOPIECE) && (!is_square_attacked(gs, gs->files/2-1, 0)) &&
@@ -588,16 +590,18 @@ static int legal_king_move(struct game_state_t * gs, int ff, int fr, int tf, int
     }
   } else {                     /* Black */
     /* King side castling */
     }
   } else {                     /* Black */
     /* King side castling */
-    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) &&
+    if ((fr == gs->ranks-1) && (tr == gs->ranks-1) && ((ff == gs->files/2) && (tf == gs->files-2) ||
+                                    gs->drops == 2 && (tf == gs->files/2) && (ff == gs->files-1)) // [HGM] reverse Seirawan gating
+       && (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 */
        (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 == 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) &&
+    if ((fr == gs->ranks-1) && (tr == gs->ranks-1) && ((ff == gs->files/2) && (tf == 2) ||
+                                    gs->drops == 2 && (tf == gs->files/2) && (ff == 0)) // [HGM] reverse Seirawan gating
+       && (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)) &&
        (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)) &&
@@ -1291,7 +1295,7 @@ int legal_move(struct game_state_t * gs,
               int fFile, int fRank,
               int tFile, int tRank)
 {
               int fFile, int fRank,
               int tFile, int tRank)
 {
-  int move_piece;
+  int move_piece, victim;
   int legal;
 
   if (fFile == ALG_DROP) {
   int legal;
 
   if (fFile == ALG_DROP) {
@@ -1369,9 +1373,15 @@ int legal_move(struct game_state_t * gs,
     return 0;
   if (!iscolor(gs->board[fFile][fRank], gs->onMove))   /* Wrong color */
     return 0;
     return 0;
   if (!iscolor(gs->board[fFile][fRank], gs->onMove))   /* Wrong color */
     return 0;
-  if ((gs->board[tFile][tRank] != NOPIECE) &&
-      iscolor(gs->board[tFile][tRank], gs->onMove))    /* Can't capture own */
-    return 0;
+  if (((victim = gs->board[tFile][tRank]) != NOPIECE) &&
+      iscolor(gs->board[tFile][tRank], gs->onMove)) {
+    if(piecetype(move_piece) == KING && piecetype(victim) == ROOK) { // [HGM] could be FRC castling
+    }
+    if(gs->drops== 2 && piecetype(move_piece) == ROOK && piecetype(victim) == KING) { // [HGM] could be Seirawan reverse gating
+       return legal_king_move(gs, fFile, fRank, tFile, tRank);
+    }
+    return 0;  /* Can't capture own */
+  }
   if ((fFile == tFile) && (fRank == tRank))    /* Same square */
     return 0;
   switch (move_piece) {
   if ((fFile == tFile) && (fRank == tRank))    /* Same square */
     return 0;
   switch (move_piece) {
@@ -1500,8 +1510,11 @@ static int move_calculate(struct game_state_t * gs, struct move_t * mt, int prom
   } 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(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");
-       if(gs->drops == 2 && promote && gs->holding[gs->onMove == BLACK][promote-1])
-           mt->piecePromotionTo = promote, gating = 1;
+       if(gs->drops == 2 && promote && gs->holding[gs->onMove == BLACK][abs(promote)-1]) { // promote can be flipped (reverse gating kludge)
+           int c = gs->onMove == WHITE ? 0 : gs->ranks-1;
+           mt->piecePromotionTo = promote; gating = 1;
+           if(promote < 0) sprintf(mt->moveString, "R/%c%d-e%d", mt->fromRank + 'a', c, c); // use RxK notation for Rook-square gatings
+       }
   } else {
   stm = colorval(gs->board[mt->fromFile][mt->fromRank]);
   if(gs->promoType == 3) { // Shogi-style promotions: not just Pawns, but many pieces can promote
   } else {
   stm = colorval(gs->board[mt->fromFile][mt->fromRank]);
   if(gs->promoType == 3) { // Shogi-style promotions: not just Pawns, but many pieces can promote
@@ -1556,7 +1569,9 @@ static int move_calculate(struct game_state_t * gs, struct move_t * mt, int prom
     // now we must test virginity of the moved piece. Yegh!
     for (i = g->numHalfMoves-1; i >= 0; i--) {
       if (g->moveList[i].fromFile == mt->fromFile && g->moveList[i].fromRank == mt->fromRank ||
     // now we must test virginity of the moved piece. Yegh!
     for (i = g->numHalfMoves-1; i >= 0; i--) {
       if (g->moveList[i].fromFile == mt->fromFile && g->moveList[i].fromRank == mt->fromRank ||
-          g->moveList[i].toFile   == mt->fromFile && g->moveList[i].toRank   == mt->fromRank) return MOVE_ILLEGAL;
+          g->moveList[i].toFile   == mt->fromFile && g->moveList[i].toRank   == mt->fromRank ||
+         g->moveList[i].fromFile == ALG_CASTLE && (gs->onMove == WHITE ? 0 : gs->ranks-1) == mt->fromRank &&
+                (g->moveList[i].fromRank == mt->fromFile || gs->files>>1 == mt->fromFile )) return MOVE_ILLEGAL;
     }
     mt->piecePromotionTo = promote; // gating OK
     gating = 1; // remember we did it for check test
     }
     mt->piecePromotionTo = promote; // gating OK
     gating = 1; // remember we did it for check test
@@ -1811,7 +1826,7 @@ int has_legal_move(struct game_state_t * gs)
 int parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt, int promote)
 {
   int type = is_move(mstr);
 int parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt, int promote)
 {
   int type = is_move(mstr);
-  int result;
+  int result, flipflag = 1;
 
   mt->piecePromotionTo = NOPIECE;
   mt->color = gs->onMove;
 
   mt->piecePromotionTo = NOPIECE;
   mt->color = gs->onMove;
@@ -1886,6 +1901,11 @@ int parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt, int pro
     return MOVE_ILLEGAL;
 
   if(result == 2) { // [HGM] castle: orthodox castling was given as King move; convert it to new format
     return MOVE_ILLEGAL;
 
   if(result == 2) { // [HGM] castle: orthodox castling was given as King move; convert it to new format
+       int ff=mt->fromFile, tf=mt->toFile;
+       if(piecetype(gs->board[tf][mt->toRank]) == KING) { // [HGM] RxK notation
+           mt->fromFile = tf; mt->toFile = ff > tf ? gs->files-2 : 2; // correct to coventional
+           flipflag = -1; // kludge: flip gated piece
+       }
        if(mt->fromFile - mt->toFile > 1) { // Q-side
                mt->fromRank = 0; 
                mt->toRank   = mt->toFile+1;
        if(mt->fromFile - mt->toFile > 1) { // Q-side
                mt->fromRank = 0; 
                mt->toRank   = mt->toFile+1;
@@ -1904,7 +1924,7 @@ int parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt, int pro
     if(gs->drops == 2 && promote == DRAGONHORSE) promote = HAWK;
   }
 
     if(gs->drops == 2 && promote == DRAGONHORSE) promote = HAWK;
   }
 
-  return move_calculate(gs, mt, promote);
+  return move_calculate(gs, mt, promote*flipflag);
 }
 
 /* Returns MOVE_OK, MOVE_NOMATERIAL, MOVE_CHECKMATE, or MOVE_STALEMATE */
 }
 
 /* Returns MOVE_OK, MOVE_NOMATERIAL, MOVE_CHECKMATE, or MOVE_STALEMATE */
@@ -1942,8 +1962,11 @@ int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_st
     gs->board[mt->toRank][backRank] = rook;      // then put back
     gs->board[mt->toFile][backRank] = king;
     if(gs->drops == 2 && mt->piecePromotionTo != NOPIECE) { // [HGM] Seirawan-style gating
     gs->board[mt->toRank][backRank] = rook;      // then put back
     gs->board[mt->toFile][backRank] = king;
     if(gs->drops == 2 && mt->piecePromotionTo != NOPIECE) { // [HGM] Seirawan-style gating
-      gs->board[fKing][backRank] = mt->piecePromotionTo | gs->onMove;   // for now always on King square
-      gs->holding[gs->onMove==WHITE ? 0 : 1][mt->piecePromotionTo-1]--; // remove gated piece from holdings
+      if(mt->piecePromotionTo > 0)
+       gs->board[fKing][backRank] = mt->piecePromotionTo | gs->onMove; // gate on King square
+      else
+       gs->board[mt->fromRank][backRank] = -mt->piecePromotionTo | gs->onMove; // gate on Rook square
+      gs->holding[gs->onMove==WHITE ? 0 : 1][abs(mt->piecePromotionTo)-1]--; // remove gated piece from holdings
     }
   } else {
   movedPiece = gs->board[mt->fromFile][mt->fromRank];
     }
   } else {
   movedPiece = gs->board[mt->fromFile][mt->fromRank];
@@ -2142,10 +2165,17 @@ int backup_move(int g, int mode)
     // 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
     // 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
+    if(gs->board[m->fromRank][rank] != NOPIECE)
+      gs->holding[gs->onMove==WHITE ? 1 : 0][piecetype(gs->board[m->fromRank][rank])-1]++; // put back in holdings (onMove not flipped yet!)
+    if(gs->board[kingFromFile][rank] != NOPIECE)
+      gs->holding[gs->onMove==WHITE ? 1 : 0][piecetype(gs->board[kingFromFile][rank])-1]++; // put back in holdings (onMove not flipped yet!)
     gs->board[m->fromRank][rank] = ROOK | m->color; // Rook fromSqr
     gs->board[kingFromFile][rank] = KING | m->color; // King fromSquare
     goto cleanupMove;
   }
     gs->board[m->fromRank][rank] = ROOK | m->color; // Rook fromSqr
     gs->board[kingFromFile][rank] = KING | m->color; // King fromSquare
     goto cleanupMove;
   }
+  if(gs->board[m->fromFile][m->fromRank] != NOPIECE) { // [HGM] from-square occupied; move must have been Seirawan-style gating
+    gs->holding[gs->onMove==WHITE ? 1 : 0][piecetype(gs->board[m->fromFile][m->fromRank])-1]++; // put back in holdings (onMove not flipped yet!)
+  }
   gs->board[m->fromFile][m->fromRank] = gs->board[m->toFile][m->toRank];
   if (m->piecePromotionTo != NOPIECE) {
     gs->board[m->fromFile][m->fromRank] = PAWN |
   gs->board[m->fromFile][m->fromRank] = gs->board[m->toFile][m->toRank];
   if (m->piecePromotionTo != NOPIECE) {
     gs->board[m->fromFile][m->fromRank] = PAWN |
@@ -2275,9 +2305,6 @@ int backup_move(int g, int mode)
     goto cleanupMove;
   }
   gs->board[m->toFile][m->toRank] = m->pieceCaptured;
     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);
 cleanupMove:
   if (game_globals.garray[g].status != GAME_EXAMINE) {
     game_update_time(g);