Fix rep-draw detection
[capablanca.git] / lasker-2.2.3 / src / gameproc.c
index ec6918c..295aed6 100644 (file)
@@ -144,6 +144,11 @@ void game_ended(int g, int winner, int why)
     strcpy(EndSymbol, "Bar");
     rate_change = 1;
     break;
+  case END_PERPETUAL:
+    sprintf(tmp, "%s perpetually checking} %s", NameOfLoser, winSymbol);
+    strcpy(EndSymbol, "Per");
+    rate_change = 1;
+    break;
   case END_RESIGN:
     sprintf(tmp, "%s resigns} %s", NameOfLoser, winSymbol);
     strcpy(EndSymbol, "Res");
@@ -416,7 +421,7 @@ static int was_promoted(struct game *g, int f, int r)
       if (g->moveList[i].piecePromotionTo) {
        switch(g->moveList[i].moveString[0]) { // [HGM] return original piece type rather than just TRUE
           case 'P': return PAWN;
-          case 'N': return KNIGHT;
+          case 'N': return HONORABLEHORSE; // !!! this is Shogi, so no KNIGHT !!!
           case 'B': return BISHOP;
           case 'R': return ROOK;
           case 'L': return LANCE;
@@ -525,7 +530,7 @@ void process_move(int p, char *command)
 {
   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;
 
@@ -561,9 +566,9 @@ void process_move(int p, char *command)
   }
   pp->promote = NOPIECE; // [HGM] this seemed to be uninitialized, which caused spurious promotion in Shogi
   if ((len = strlen(command)) > 1) {
-    if (command[len - 2] == '=') {
+    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;
@@ -608,7 +613,19 @@ printf("promo '%s'\n", command);
       case 'g':
        pp->promote = MASTODON;
        break;
+      case 'l':
+       pp->promote = LIEUTENANT;
+       break;
+      case 'k':
+       pp->promote = KING;
+       break;
       // Shogi promotions
+      case 'h':
+       pp->promote = DRAGONHORSE;
+       break;
+      case 'd':
+       pp->promote = DRAGONKING;
+       break;
       case '^':
       case '+':
        pp->promote = GOLD;
@@ -870,6 +887,26 @@ static int Check50MoveRule (int p, int g)
   return 0;
 }
 
+static int perp_check(struct game g, int first, int third)
+{
+  struct game_state_t gs = g.game_state; // current position, both first and last of loop
+  int half_move, no_perp = 0;
+printf("perp %d %d\n",first,third);
+  for(half_move=first+1; half_move<third; half_move++) {
+    gs.onMove = CToggle(gs.onMove);
+    if(!in_check(&gs)) no_perp |= (half_move&1) + 1; // 1 = white not in check, 2 = black not in check
+    gs.onMove = CToggle(gs.onMove);
+printf("move%d, p=%d\n",half_move,no_perp);
+    if(no_perp == 3) break;
+    execute_move(&gs, &g.moveList[half_move], 0);
+  }
+  if(no_perp == (third&1) + 1) return END_NOTENDED;  // stm was checking, other not: defer judgement
+  if(no_perp == 2 - (third&1)) return END_PERPETUAL; // stm was not checking, other was: stm wins
+  if(no_perp == 0) return END_REPETITION; // mutual perpertual check, draw
+  // here we should check for chasing
+  return END_REPETITION;
+}
+
 static char *GetFENpos (int g, int half_move)
 {
   if (half_move < 0)
@@ -881,29 +918,47 @@ static int CheckRepetition (int p, int g)
 {
   struct player *pp = &player_globals.parray[p];
   struct pending* pend;
-  int move_num;
+  int move_num, s1, s2, result = END_REPETITION;
   int flag1 = 1, flag2 = 1;
-  char *pos1 = GetFENpos (g, game_globals.garray[g].numHalfMoves - 1);
-  char *pos2 = GetFENpos (g, game_globals.garray[g].numHalfMoves);
+  int numPly = game_globals.garray[g].numHalfMoves;
+  char *pos1 = GetFENpos (g, numPly - 1); // current position
+  char *pos2 = "";
   char *pos;
+  int  turn = numPly - 1;
 
-  if (game_globals.garray[g].numHalfMoves < 8)  /* can't have three repeats any quicker. */
+  if (numPly < 8)  /* can't have three repeats any quicker. */
     return 0;
 
-  for (move_num = game_globals.garray[g].game_state.lastIrreversable;
-       move_num < game_globals.garray[g].numHalfMoves - 1; move_num++) {
+  if((game_globals.garray[g].white == p) != (numPly&1)) { // claimer has the move
+    pos2 = pos1;
+    pos1 = GetFENpos (g, turn = numPly - 2); // also check position before opponent's move (which could have pre-empted him)
+  } // pos1 is now always a position where the opponent has the move
+
+  for (move_num = numPly - 3; // [HGM] FEN stored in moveList[numHalfMoves-1] !
+       move_num >= game_globals.garray[g].game_state.lastIrreversable - 1; move_num--) {
     pos = GetFENpos (g, move_num);
-    if (strlen(pos1) == strlen(pos) && !strcmp(pos1, pos))
-      flag1++;
-    if (strlen(pos2) == strlen(pos) && !strcmp(pos2, pos))
-      flag2++;
+    if (!(turn - move_num & 1) && strlen(pos1) == strlen(pos) && !strcmp(pos1, pos))
+      flag1++ == 2 && (s1 = move_num);
+    if ( (turn - move_num & 1) && strlen(pos2) == strlen(pos) && !strcmp(pos2, pos))
+      flag2++ == 2 && (s2 = move_num); // remember start of last two loops
+printf("%2d. %d-%d '%s' '%s' '%s'\n", move_num, flag1, flag2, pos1,pos2,pos);
   }
   if (flag1 >= 3 || flag2 >= 3) {
     if ((pend = find_pend(pp->opponent, p, PEND_DRAW)) != NULL) {
       delete_pending(pend);
       decline_withdraw_offers(p, -1, -1,DO_DECLINE);
     }
-    game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_REPETITION);
+    if(game_globals.garray[g].game_state.palace) { // [HGM] in Xiangqi we have to test for perpetuals to determine the outcome
+      if(flag2 >= 3) result = perp_check(game_globals.garray[g], s2, numPly);
+      else  result = perp_check(game_globals.garray[g], s1, numPly - (pos2[0] != 0));
+      if(result == END_NOTENDED) {
+       pprintf(p, "Perpetuals can be claimed only during the turn of the winner\n");
+       return 1;
+      }
+      game_ended(g, (numPly&1) ? BLACK : WHITE, result); // stm wins
+      return 1;
+    }
+    game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, result);
     return 1;
   }
   else return 0;