Subversion Repositories Games.Chess Giants

Rev

Rev 33 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "chess.h"
  2. #include "data.h"
  3. /* last modified 02/24/14 */
  4. /*
  5.  *******************************************************************************
  6.  *                                                                             *
  7.  *   InputMove() is responsible for converting a move from a text string to    *
  8.  *   the internal move format.  It allows the so-called "reduced algebraic     *
  9.  *   move format" which makes the origin square optional unless required for   *
  10.  *   clarity.  It also accepts as little as required to remove ambiguity from  *
  11.  *   the move, by using GenerateMoves() to produce a set of legal moves that   *
  12.  *   the text can be applied against to eliminate those moves not intended.    *
  13.  *   Hopefully, only one move will remain after the elimination and legality   *
  14.  *   checks.                                                                   *
  15.  *                                                                             *
  16.  *******************************************************************************
  17.  */
  18. int InputMove(TREE * RESTRICT tree, int ply, int wtm, int silent,
  19.     int ponder_list, char *text) {
  20.   unsigned moves[220], *mv, *mvp, *goodmove = 0;
  21.   int piece = -1, capture, promote, give_check;
  22.   int ffile, frank, tfile, trank;
  23.   int current, i, nleft;
  24.   char *goodchar, *tc;
  25.   char movetext[128];
  26.   static const char pieces[15] =
  27.       { ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r',
  28.     'Q', 'q', 'K', 'k', '\0'
  29.   };
  30.   static const char pro_pieces[15] =
  31.       { ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r', 'Q', 'q',
  32.     'K', 'k', '\0'
  33.   };
  34. /*
  35.  ************************************************************
  36.  *                                                          *
  37.  *  First, we need to strip off the special characters for  *
  38.  *  check, mate, bad move, good move, and such that might   *
  39.  *  come from a PGN input file.                             *
  40.  *                                                          *
  41.  ************************************************************
  42.  */
  43.   if ((tc = strchr(text, '!')))
  44.     *tc = 0;
  45.   if ((tc = strchr(text, '?')))
  46.     *tc = 0;
  47. /*
  48.  ************************************************************
  49.  *                                                          *
  50.  *  Check for full coordinate input (f1e1) and handle that  *
  51.  *  if needed.                                              *
  52.  *                                                          *
  53.  ************************************************************
  54.  */
  55.   if (strlen(text) == 0)
  56.     return 0;
  57.   if ((text[0] >= 'a') && (text[0] <= 'h') && (text[1] >= '1')
  58.       && (text[1] <= '8') && (text[2] >= 'a') && (text[2] <= 'h')
  59.       && (text[3] >= '1') && (text[3] <= '8'))
  60.     return InputMoveICS(tree, ply, wtm, silent, ponder_list, text);
  61. /*
  62.  ************************************************************
  63.  *                                                          *
  64.  *  Initialize move structure.  If we discover a parsing    *
  65.  *  error, this will cause us to return a move of "0" to    *
  66.  *  indicate some sort of error was detected.               *
  67.  *                                                          *
  68.  ************************************************************
  69.  */
  70.   tree->status[MAXPLY] = tree->status[ply];
  71.   strcpy(movetext, text);
  72.   moves[0] = 0;
  73.   piece = 0;
  74.   capture = 0;
  75.   promote = 0;
  76.   give_check = 0;
  77.   frank = -1;
  78.   ffile = -1;
  79.   trank = -1;
  80.   tfile = -1;
  81.   goodchar = strchr(movetext, '#');
  82.   if (goodchar)
  83.     *goodchar = 0;
  84. /*
  85.  ************************************************************
  86.  *                                                          *
  87.  *  First we look for castling moves which are a special    *
  88.  *  case with an unusual syntax compared to normal moves.   *
  89.  *                                                          *
  90.  ************************************************************
  91.  */
  92.   if (!strcmp(movetext, "o-o") || !strcmp(movetext, "o-o+")
  93.       || !strcmp(movetext, "O-O") || !strcmp(movetext, "O-O+")
  94.       || !strcmp(movetext, "0-0") || !strcmp(movetext, "0-0+")) {
  95.     piece = king;
  96.     if (wtm) {
  97.       ffile = 4;
  98.       frank = 0;
  99.       tfile = 6;
  100.       trank = 0;
  101.     } else {
  102.       ffile = 4;
  103.       frank = 7;
  104.       tfile = 6;
  105.       trank = 7;
  106.     }
  107.   } else if (!strcmp(movetext, "o-o-o") || !strcmp(movetext, "o-o-o+")
  108.       || !strcmp(movetext, "O-O-O") || !strcmp(movetext, "O-O-O+")
  109.       || !strcmp(movetext, "0-0-0") || !strcmp(movetext, "0-0-0+")) {
  110.     piece = king;
  111.     if (wtm) {
  112.       ffile = 4;
  113.       frank = 0;
  114.       tfile = 2;
  115.       trank = 0;
  116.     } else {
  117.       ffile = 4;
  118.       frank = 7;
  119.       tfile = 2;
  120.       trank = 7;
  121.     }
  122.   } else {
  123. /*
  124.  ************************************************************
  125.  *                                                          *
  126.  *  OK, it is not a castling move.  Check for two "b"       *
  127.  *  characters which might be a piece (bishop) and a file   *
  128.  *  (b-file).  The first "b" should be "B" but we allow     *
  129.  *  this to make typing input simpler.                      *
  130.  *                                                          *
  131.  ************************************************************
  132.  */
  133.     if ((movetext[0] == 'b') && (movetext[1] == 'b'))
  134.       movetext[0] = 'B';
  135. /*
  136.  ************************************************************
  137.  *                                                          *
  138.  *  Check to see if there is a "+" character which means    *
  139.  *  that this move is a check.  We can use this to later    *
  140.  *  eliminate all non-checking moves as possibilities.      *
  141.  *                                                          *
  142.  ************************************************************
  143.  */
  144.     if (strchr(movetext, '+')) {
  145.       *strchr(movetext, '+') = 0;
  146.       give_check = 1;
  147.     }
  148. /*
  149.  ************************************************************
  150.  *                                                          *
  151.  *  If this is a promotion, indicated by an "=" in the      *
  152.  *  text, we can pick up the promote-to piece and save it   *
  153.  *  to use later when eliminating moves.                    *
  154.  *                                                          *
  155.  ************************************************************
  156.  */
  157.     if (strchr(movetext, '=')) {
  158.       goodchar = strchr(movetext, '=');
  159.       goodchar++;
  160.       promote = (strchr(pro_pieces, *goodchar) - pro_pieces) >> 1;
  161.       *strchr(movetext, '=') = 0;
  162.     }
  163. /*
  164.  ************************************************************
  165.  *                                                          *
  166.  *  Now for a kludge.  ChessBase (and others) can't follow  *
  167.  *  the PGN standard of bxc8=Q for promotion, and instead   *
  168.  *  will produce "bxc8Q" omitting the PGN-standard "="      *
  169.  *  character.  We handle that here so that we can read     *
  170.  *  their non-standard moves.                               *
  171.  *                                                          *
  172.  ************************************************************
  173.  */
  174.     else {
  175.       char *prom = strchr(pro_pieces, movetext[strlen(movetext) - 1]);
  176.  
  177.       if (prom) {
  178.         promote = (prom - pro_pieces) >> 1;
  179.         movetext[strlen(movetext) - 1] = 0;
  180.       }
  181.     }
  182. /*
  183.  ************************************************************
  184.  *                                                          *
  185.  *  Next we extract the last rank/file designators from the *
  186.  *  text, since the destination is required for all valid   *
  187.  *  non-castling moves.  Note that we might not have both a *
  188.  *  rank and file but we must have at least one.            *
  189.  *                                                          *
  190.  ************************************************************
  191.  */
  192.     current = strlen(movetext) - 1;
  193.     trank = movetext[current] - '1';
  194.     if ((trank >= 0) && (trank <= 7))
  195.       movetext[current] = 0;
  196.     else
  197.       trank = -1;
  198.     current = strlen(movetext) - 1;
  199.     tfile = movetext[current] - 'a';
  200.     if ((tfile >= 0) && (tfile <= 7))
  201.       movetext[current] = 0;
  202.     else
  203.       tfile = -1;
  204.     if (strlen(movetext)) {
  205. /*
  206.  ************************************************************
  207.  *                                                          *
  208.  *  The first character is the moving piece, unless it is a *
  209.  *  pawn.  In this case, the moving piece is omitted and we *
  210.  *  know what it has to be.                                 *
  211.  *                                                          *
  212.  ************************************************************
  213.  */
  214.       if (strchr("  PpNnBBRrQqKk", *movetext)) {
  215.         piece = (strchr(pieces, movetext[0]) - pieces) >> 1;
  216.         for (i = 0; i < (int) strlen(movetext); i++)
  217.           movetext[i] = movetext[i + 1];
  218.       }
  219. /*
  220.  ************************************************************
  221.  *                                                          *
  222.  *  It is also possible that this move is a capture, which  *
  223.  *  is indicated by a "x" between either the source and     *
  224.  *  destination squares, or between the moving piece and    *
  225.  *  the destination.                                        *
  226.  *                                                          *
  227.  ************************************************************
  228.  */
  229.       if ((strlen(movetext)) && (movetext[strlen(movetext) - 1] == 'x')) {
  230.         capture = 1;
  231.         movetext[strlen(movetext) - 1] = 0;
  232.       } else
  233.         capture = 0;
  234. /*
  235.  ************************************************************
  236.  *                                                          *
  237.  *  It is possible to have no source square, but we could   *
  238.  *  have a complete algebraic square designation, or just   *
  239.  *  rank or file, needed to disambiguate the move.          *
  240.  *                                                          *
  241.  ************************************************************
  242.  */
  243.       if (strlen(movetext)) {
  244.         ffile = movetext[0] - 'a';
  245.         if ((ffile < 0) || (ffile > 7)) {
  246.           ffile = -1;
  247.           frank = movetext[0] - '1';
  248.           if ((frank < 0) || (frank > 7))
  249.             piece = -1;
  250.         } else {
  251.           if (strlen(movetext) == 2) {
  252.             frank = movetext[1] - '1';
  253.             if ((frank < 0) || (frank > 7))
  254.               piece = -1;
  255.           }
  256.         }
  257.       }
  258.     }
  259.   }
  260. /*
  261.  ************************************************************
  262.  *                                                          *
  263.  *  Now for the easy part.  We first generate all moves if  *
  264.  *  not pondering, or else use a pre-computed list of moves *
  265.  *  (if pondering) since the board position is not correct  *
  266.  *  for move input analysis.  We then loop through the list *
  267.  *  of moves, using the information we extracted previously *
  268.  *  , and eliminate all moves that are (a) the wrong piece  *
  269.  *  type;  (b) wrong source or destination square;          *
  270.  *  (c) wrong promotion type;  (d) should be a capture,     *
  271.  *  check or promotion but is not, or vice-versa.           *
  272.  *                                                          *
  273.  ************************************************************
  274.  */
  275.   if (!piece)
  276.     piece = 1;
  277.   if (!ponder_list) {
  278.     mvp = GenerateCaptures(tree, MAXPLY, wtm, moves);
  279.     mvp = GenerateNoncaptures(tree, MAXPLY, wtm, mvp);
  280.   } else {
  281.     for (i = 0; i < num_ponder_moves; i++)
  282.       moves[i] = ponder_moves[i];
  283.     mvp = moves + num_ponder_moves;
  284.   }
  285.   for (mv = &moves[0]; mv < mvp; mv++) {
  286.     if (piece && (Piece(*mv) != piece))
  287.       *mv = 0;
  288.     if ((ffile >= 0) && (File(From(*mv)) != ffile))
  289.       *mv = 0;
  290.     if (capture && (!Captured(*mv)))
  291.       *mv = 0;
  292.     if (promote && (Promote(*mv) != promote))
  293.       *mv = 0;
  294.     if ((frank >= 0) && (Rank(From(*mv)) != frank))
  295.       *mv = 0;
  296.     if ((tfile >= 0) && (File(To(*mv)) != tfile))
  297.       *mv = 0;
  298.     if ((trank >= 0) && (Rank(To(*mv)) != trank))
  299.       *mv = 0;
  300.     if (!ponder_list && *mv) {
  301.       MakeMove(tree, MAXPLY, wtm, *mv);
  302.       if (Check(wtm) || (give_check && !Check(Flip(wtm)))) {
  303.         UnmakeMove(tree, MAXPLY, wtm, *mv);
  304.         *mv = 0;
  305.       } else
  306.         UnmakeMove(tree, MAXPLY, wtm, *mv);
  307.     }
  308.   }
  309. /*
  310.  ************************************************************
  311.  *                                                          *
  312.  *  Once we have completed eliminating incorrect moves, we  *
  313.  *  hope to have exactly one move left.  If none or left,   *
  314.  *  the entered move is illegal.  If more than one is left, *
  315.  *  the move entered is ambiguous.  If appropriate, we      *
  316.  *  output some sort of diagnostic message and then return. *
  317.  *                                                          *
  318.  ************************************************************
  319.  */
  320.   nleft = 0;
  321.   for (mv = &moves[0]; mv < mvp; mv++) {
  322.     if (*mv) {
  323.       nleft++;
  324.       goodmove = mv;
  325.     }
  326.   }
  327.   if (nleft == 1)
  328.     return *goodmove;
  329.   if (!silent) {
  330.     if (nleft == 0)
  331.       Print(4095, "Illegal move: %s\n", text);
  332.     else if (piece < 0)
  333.       Print(4095, "Illegal move (unrecognizable): %s\n", text);
  334.     else
  335.       Print(4095, "Illegal move (ambiguous): %s\n", text);
  336.   }
  337.   return 0;
  338. }
  339.  
  340. /* last modified 02/24/14 */
  341. /*
  342.  *******************************************************************************
  343.  *                                                                             *
  344.  *   InputMoveICS() is responsible for converting a move from the ics format   *
  345.  *   [from][to][promote] to the program's internal format.                     *
  346.  *                                                                             *
  347.  *******************************************************************************
  348.  */
  349. int InputMoveICS(TREE * RESTRICT tree, int ply, int wtm, int silent,
  350.     int ponder_list, char *text) {
  351.   unsigned moves[220], *mv, *mvp, *goodmove = 0;
  352.   int piece = -1, promote;
  353.   int ffile, frank, tfile, trank;
  354.   int i, nleft;
  355.   char movetext[128];
  356.   static const char pieces[15] =
  357.       { ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r',
  358.     'Q', 'q', 'K', 'k', '\0'
  359.   };
  360. /*
  361.  ************************************************************
  362.  *                                                          *
  363.  *  Initialize move structure.  If we discover a parsing    *
  364.  *  error, this will cause us to return a move of "0" to    *
  365.  *  indicate some sort of error was detected.               *
  366.  *                                                          *
  367.  ************************************************************
  368.  */
  369.   if (strlen(text) == 0)
  370.     return 0;
  371.   tree->status[MAXPLY] = tree->status[ply];
  372.   strcpy(movetext, text);
  373.   moves[0] = 0;
  374.   promote = 0;
  375. /*
  376.  ************************************************************
  377.  *                                                          *
  378.  *  First we look for castling moves which are a special    *
  379.  *  case with an unusual syntax compared to normal moves.   *
  380.  *                                                          *
  381.  ************************************************************
  382.  */
  383.   if (!strcmp(movetext, "o-o") || !strcmp(movetext, "O-O")
  384.       || !strcmp(movetext, "0-0")) {
  385.     piece = king;
  386.     if (wtm) {
  387.       ffile = 4;
  388.       frank = 0;
  389.       tfile = 6;
  390.       trank = 0;
  391.     } else {
  392.       ffile = 4;
  393.       frank = 7;
  394.       tfile = 6;
  395.       trank = 7;
  396.     }
  397.   } else if (!strcmp(movetext, "o-o-o") || !strcmp(movetext, "O-O-O")
  398.       || !strcmp(movetext, "0-0-0")) {
  399.     piece = king;
  400.     if (wtm) {
  401.       ffile = 4;
  402.       frank = 0;
  403.       tfile = 2;
  404.       trank = 0;
  405.     } else {
  406.       ffile = 4;
  407.       frank = 7;
  408.       tfile = 2;
  409.       trank = 7;
  410.     }
  411.   } else {
  412. /*
  413.  ************************************************************
  414.  *                                                          *
  415.  *  Next we extract both rank/file designators from the     *
  416.  *  text, since the destination is required for all valid   *
  417.  *  non-castling moves.                                     *
  418.  *                                                          *
  419.  ************************************************************
  420.  */
  421.     ffile = movetext[0] - 'a';
  422.     frank = movetext[1] - '1';
  423.     tfile = movetext[2] - 'a';
  424.     trank = movetext[3] - '1';
  425. /*
  426.  ************************************************************
  427.  *                                                          *
  428.  *  If this is a promotion, indicated by an "=" in the      *
  429.  *  text, we can pick up the promote-to piece and save it   *
  430.  *  to use later when eliminating moves.                    *
  431.  *                                                          *
  432.  ************************************************************
  433.  */
  434.     if (movetext[4] == '=')
  435.       promote = (strchr(pieces, movetext[5]) - pieces) >> 1;
  436.     else if ((movetext[4] != 0) && (movetext[4] != ' '))
  437.       promote = (strchr(pieces, movetext[4]) - pieces) >> 1;
  438.   }
  439. /*
  440.  ************************************************************
  441.  *                                                          *
  442.  *  Now for the easy part.  We first generate all moves if  *
  443.  *  not pondering, or else use a pre-computed list of moves *
  444.  *  (if pondering) since the board position is not correct  *
  445.  *  for move input analysis.  We then loop through the list *
  446.  *  of moves, using the information we extracted previously *
  447.  *  and eliminate all moves that are (a) the wrong piece    *
  448.  *  type;  (b) wrong source or destination square;          *
  449.  *  (c) wrong promotion type;  (d) should be a capture,     *
  450.  *  check or promotion but is not or vice-versa.            *
  451.  *                                                          *
  452.  ************************************************************
  453.  */
  454.   if (!ponder_list) {
  455.     mvp = GenerateCaptures(tree, MAXPLY, wtm, moves);
  456.     mvp = GenerateNoncaptures(tree, MAXPLY, wtm, mvp);
  457.   } else {
  458.     for (i = 0; i < num_ponder_moves; i++)
  459.       moves[i] = ponder_moves[i];
  460.     mvp = moves + num_ponder_moves;
  461.   }
  462.   for (mv = &moves[0]; mv < mvp; mv++) {
  463.     if (Promote(*mv) != promote)
  464.       *mv = 0;
  465.     if (Rank(From(*mv)) != frank)
  466.       *mv = 0;
  467.     if (File(From(*mv)) != ffile)
  468.       *mv = 0;
  469.     if (Rank(To(*mv)) != trank)
  470.       *mv = 0;
  471.     if (File(To(*mv)) != tfile)
  472.       *mv = 0;
  473.     if (!ponder_list && *mv) {
  474.       MakeMove(tree, MAXPLY, wtm, *mv);
  475.       if (Check(wtm)) {
  476.         UnmakeMove(tree, MAXPLY, wtm, *mv);
  477.         *mv = 0;
  478.       } else
  479.         UnmakeMove(tree, MAXPLY, wtm, *mv);
  480.     }
  481.   }
  482. /*
  483.  ************************************************************
  484.  *                                                          *
  485.  *  Once we have completed eliminating incorrect moves, we  *
  486.  *  hope to have exactly one move left.  If none or left,   *
  487.  *  the entered move is illegal.  If more than one is left, *
  488.  *  the move entered is ambiguous.  If appropriate, we      *
  489.  *  output some sort of diagnostic message and then return. *
  490.  *                                                          *
  491.  ************************************************************
  492.  */
  493.   nleft = 0;
  494.   for (mv = &moves[0]; mv < mvp; mv++) {
  495.     if (*mv) {
  496.       nleft++;
  497.       goodmove = mv;
  498.     }
  499.   }
  500.   if (nleft == 1)
  501.     return *goodmove;
  502.   if (!silent) {
  503.     if (nleft == 0)
  504.       Print(4095, "Illegal move: %s\n", text);
  505.     else if (piece < 0)
  506.       Print(4095, "Illegal move (unrecognizable): %s\n", text);
  507.     else
  508.       Print(4095, "Illegal move (ambiguous): %s\n", text);
  509.   }
  510.   return 0;
  511. }
  512.