home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #3 / amigamamagazinepolishissue1998.iso / szachy / gnu / amyboard-3.2.pl2 / moves.c < prev    next >
C/C++ Source or Header  |  1995-03-08  |  24KB  |  863 lines

  1. /*
  2.  * moves.c - Move generation and checking
  3.  * $Id: moves.c,v 1.6 1995/03/09 00:43:28 mann Exp $
  4.  *
  5.  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
  6.  * Enhancements Copyright 1992-95 Free Software Foundation, Inc.
  7.  *
  8.  * The following terms apply to Digital Equipment Corporation's copyright
  9.  * interest in XBoard:
  10.  * ------------------------------------------------------------------------
  11.  * All Rights Reserved
  12.  *
  13.  * Permission to use, copy, modify, and distribute this software and its
  14.  * documentation for any purpose and without fee is hereby granted,
  15.  * provided that the above copyright notice appear in all copies and that
  16.  * both that copyright notice and this permission notice appear in
  17.  * supporting documentation, and that the name of Digital not be
  18.  * used in advertising or publicity pertaining to distribution of the
  19.  * software without specific, written prior permission.
  20.  *
  21.  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  22.  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  23.  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  24.  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  25.  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  26.  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  27.  * SOFTWARE.
  28.  * ------------------------------------------------------------------------
  29.  *
  30.  * The following terms apply to the enhanced version of XBoard distributed
  31.  * by the Free Software Foundation:
  32.  * ------------------------------------------------------------------------
  33.  * This program is free software; you can redistribute it and/or modify
  34.  * it under the terms of the GNU General Public License as published by
  35.  * the Free Software Foundation; either version 2 of the License, or
  36.  * (at your option) any later version.
  37.  *
  38.  * This program is distributed in the hope that it will be useful,
  39.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  40.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  41.  * GNU General Public License for more details.
  42.  *
  43.  * You should have received a copy of the GNU General Public License
  44.  * along with this program; if not, write to the Free Software
  45.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  46.  * ------------------------------------------------------------------------
  47.  */
  48.  
  49. #include <stdio.h>
  50. #if HAVE_STRING_H
  51. # include <string.h>
  52. #else /* not HAVE_STRING_H */
  53. # include <strings.h>
  54. #endif /* not HAVE_STRING_H */
  55. #include "common.h"
  56. #include "backend.h" 
  57. #include "moves.h"
  58. #include "parser.h"
  59.  
  60. int WhitePiece(piece)
  61.      ChessSquare piece;
  62. {
  63.     return (int) piece >= (int) WhitePawn && (int) piece <= (int) WhiteKing;
  64. }
  65.  
  66. int BlackPiece(piece)
  67.      ChessSquare piece;
  68. {
  69.     return (int) piece >= (int) BlackPawn && (int) piece <= (int) BlackKing;
  70. }
  71.  
  72. int SameColor(piece1, piece2)
  73.      ChessSquare piece1, piece2;
  74. {
  75.     return ((int) piece1 >= (int) WhitePawn &&
  76.         (int) piece1 <= (int) WhiteKing &&
  77.         (int) piece2 >= (int) WhitePawn &&
  78.         (int) piece2 <= (int) WhiteKing)
  79.       ||   ((int) piece1 >= (int) BlackPawn &&
  80.         (int) piece1 <= (int) BlackKing &&
  81.         (int) piece2 >= (int) BlackPawn &&
  82.         (int) piece2 <= (int) BlackKing);
  83. }
  84.  
  85. ChessSquare PromoPiece(moveType)
  86.      ChessMove moveType;
  87. {
  88.     switch (moveType) {
  89.       default:
  90.     return EmptySquare;
  91.       case WhitePromotionQueen:
  92.     return WhiteQueen;
  93.       case BlackPromotionQueen:
  94.     return BlackQueen;
  95.       case WhitePromotionRook:
  96.     return WhiteRook;
  97.       case BlackPromotionRook:
  98.     return BlackRook;
  99.       case WhitePromotionBishop:
  100.     return WhiteBishop;
  101.       case BlackPromotionBishop:
  102.     return BlackBishop;
  103.       case WhitePromotionKnight:
  104.     return WhiteKnight;
  105.       case BlackPromotionKnight:
  106.     return BlackKnight;
  107.     }
  108. }
  109.  
  110. ChessMove PromoCharToMoveType(whiteOnMove, promoChar)
  111.      int whiteOnMove;
  112.      int promoChar;
  113. {
  114.     if (whiteOnMove) {
  115.     switch (promoChar) {
  116.       case 'n':
  117.       case 'N':
  118.         return WhitePromotionKnight;
  119.       case 'b':
  120.       case 'B':
  121.         return WhitePromotionBishop;
  122.       case 'r':
  123.       case 'R':
  124.         return WhitePromotionRook;
  125.       case 'q':
  126.       case 'Q':
  127.       case NULLCHAR:
  128.         return WhitePromotionQueen;
  129.       default:
  130.         return BadMove;
  131.     }
  132.     } else {
  133.     switch (promoChar) {
  134.       case 'n':
  135.       case 'N':
  136.         return BlackPromotionKnight;
  137.       case 'b':
  138.       case 'B':
  139.         return BlackPromotionBishop;
  140.       case 'r':
  141.       case 'R':
  142.         return BlackPromotionRook;
  143.       case 'q':
  144.       case 'Q':
  145.       case NULLCHAR:
  146.         return BlackPromotionQueen;
  147.       default:
  148.         return BadMove;
  149.     }
  150.     }
  151. }
  152.  
  153. char pieceToChar[] = {
  154.     'P', 'R', 'N', 'B', 'Q', 'K',
  155.     'p', 'r', 'n', 'b', 'q', 'k', '.'
  156.   };
  157.  
  158. char PieceToChar(p)
  159.      ChessSquare p;
  160. {
  161.     return pieceToChar[(int) p];
  162. }
  163.  
  164. ChessSquare CharToPiece(c)
  165.      int c;
  166. {
  167.     switch (c) {
  168.       default:
  169.       case '.':    return EmptySquare;
  170.       case 'P':    return WhitePawn;
  171.       case 'R':    return WhiteRook;
  172.       case 'N':    return WhiteKnight;
  173.       case 'B':    return WhiteBishop;
  174.       case 'Q':    return WhiteQueen;
  175.       case 'K':    return WhiteKing;
  176.       case 'p':    return BlackPawn;
  177.       case 'r':    return BlackRook;
  178.       case 'n':    return BlackKnight;
  179.       case 'b':    return BlackBishop;
  180.       case 'q':    return BlackQueen;
  181.       case 'k':    return BlackKing;
  182.     }
  183. }
  184.  
  185. void CopyBoard(to, from)
  186.      Board to, from;
  187. {
  188.     int i, j;
  189.     
  190.     for (i = 0; i < BOARD_SIZE; i++)
  191.       for (j = 0; j < BOARD_SIZE; j++)
  192.     to[i][j] = from[i][j];
  193. }
  194.  
  195. int CompareBoards(board1, board2)
  196.      Board board1, board2;
  197. {
  198.     int i, j;
  199.     
  200.     for (i = 0; i < BOARD_SIZE; i++)
  201.       for (j = 0; j < BOARD_SIZE; j++) {
  202.       if (board1[i][j] != board2[i][j])
  203.         return FALSE;
  204.     }
  205.     return TRUE;
  206. }
  207.  
  208.  
  209. /* Call callback once for each pseudo-legal move in the given
  210.    position, except castling moves. A move is pseudo-legal if it is
  211.    legal, or if it would be legal except that it leaves the king in
  212.    check.  In the arguments, epfile is EP_NONE if the previous move
  213.    was not a double pawn push, or the file 0..7 if it was, or
  214.    EP_UNKNOWN if we don't know and want to allow all e.p. captures.
  215.    Promotion moves generated are to Queen only.
  216. */
  217. void GenPseudoLegal(board, flags, epfile, callback, closure)
  218.      Board board;
  219.      int flags;
  220.      int epfile;
  221.      MoveCallback callback;
  222.      VOIDSTAR closure;
  223. {
  224.     int rf, ff;
  225.     int i, j, d, s, fs, rs, rt, ft;
  226.  
  227.     for (rf = 0; rf <= 7; rf++) 
  228.       for (ff = 0; ff <= 7; ff++) {
  229.       if (flags & F_WHITE_ON_MOVE) {
  230.           if (!WhitePiece(board[rf][ff])) continue;
  231.       } else {
  232.           if (!BlackPiece(board[rf][ff])) continue;
  233.       }
  234.       switch (board[rf][ff]) {
  235.         case EmptySquare:
  236.         default:
  237.           /* can't happen */
  238.           break;
  239.  
  240.         case WhitePawn:
  241.           if (rf < 7 && board[rf + 1][ff] == EmptySquare) {
  242.           callback(board, flags,
  243.                rf == 6 ? WhitePromotionQueen : NormalMove,
  244.                rf, ff, rf + 1, ff, closure);
  245.           }
  246.           if (rf == 1 && board[2][ff] == EmptySquare &&
  247.           board[3][ff] == EmptySquare) {
  248.           callback(board, flags, NormalMove,
  249.                rf, ff, 3, ff, closure);
  250.           }
  251.           for (s = -1; s <= 1; s += 2) {
  252.           if (rf < 7 && ff + s >= 0 && ff + s <= 7 &&
  253.               BlackPiece(board[rf + 1][ff + s])) {
  254.               callback(board, flags, 
  255.                    rf == 6 ? WhitePromotionQueen : NormalMove,
  256.                    rf, ff, rf + 1, ff + s, closure);
  257.           }
  258.           if (rf == 4) {
  259.               if (ff + s >= 0 && ff + s <= 7 &&
  260.               (epfile == ff + s || epfile == EP_UNKNOWN) &&
  261.               board[4][ff + s] == BlackPawn &&
  262.               board[5][ff + s] == EmptySquare) {
  263.               callback(board, flags, WhiteCapturesEnPassant,
  264.                    rf, ff, 5, ff + s, closure);
  265.               }
  266.           }
  267.           }            
  268.           break;
  269.  
  270.         case BlackPawn:
  271.           if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
  272.           callback(board, flags, 
  273.                rf == 1 ? BlackPromotionQueen : NormalMove,
  274.                rf, ff, rf - 1, ff, closure);
  275.           }
  276.           if (rf == 6 && board[5][ff] == EmptySquare &&
  277.           board[4][ff] == EmptySquare) {
  278.           callback(board, flags, NormalMove,
  279.                rf, ff, 4, ff, closure);
  280.           }
  281.           for (s = -1; s <= 1; s += 2) {
  282.           if (rf > 0 && ff + s >= 0 && ff + s <= 7 &&
  283.               WhitePiece(board[rf - 1][ff + s])) {
  284.               callback(board, flags, 
  285.                    rf == 1 ? BlackPromotionQueen : NormalMove,
  286.                    rf, ff, rf - 1, ff + s, closure);
  287.           }
  288.           if (rf == 3) {
  289.               if (ff + s >= 0 && ff + s <= 7 &&
  290.               (epfile == ff + s || epfile == EP_UNKNOWN) &&
  291.               board[3][ff + s] == WhitePawn &&
  292.               board[2][ff + s] == EmptySquare) {
  293.               callback(board, flags, BlackCapturesEnPassant,
  294.                    rf, ff, 2, ff + s, closure);
  295.               }
  296.           }
  297.           }            
  298.           break;
  299.  
  300.         case WhiteKnight:
  301.         case BlackKnight:
  302.           for (i = -1; i <= 1; i += 2)
  303.         for (j = -1; j <= 1; j += 2)
  304.           for (s = 1; s <= 2; s++) {
  305.               rt = rf + i*s;
  306.               ft = ff + j*(3-s);
  307.               if (rt < 0 || rt > 7 || ft < 0 || ft > 7) continue;
  308.               if (SameColor(board[rf][ff], board[rt][ft])) continue;
  309.               callback(board, flags, NormalMove,
  310.                    rf, ff, rt, ft, closure);
  311.           }
  312.           break;
  313.  
  314.         case WhiteBishop:
  315.         case BlackBishop:
  316.           for (rs = -1; rs <= 1; rs += 2) 
  317.         for (fs = -1; fs <= 1; fs += 2)
  318.           for (i = 1;; i++) {
  319.               rt = rf + (i * rs);
  320.               ft = ff + (i * fs);
  321.               if (rt < 0 || rt > 7 || ft < 0 || ft > 7) break;
  322.               if (SameColor(board[rf][ff], board[rt][ft])) break;
  323.               callback(board, flags, NormalMove,
  324.                    rf, ff, rt, ft, closure);
  325.               if (board[rt][ft] != EmptySquare) break;
  326.           }
  327.           break;
  328.  
  329.         case WhiteRook:
  330.         case BlackRook:
  331.           for (d = 0; d <= 1; d++)
  332.         for (s = -1; s <= 1; s += 2)
  333.           for (i = 1;; i++) {
  334.               rt = rf + (i * s) * d;
  335.               ft = ff + (i * s) * (1 - d);
  336.               if (rt < 0 || rt > 7 || ft < 0 || ft > 7) break;
  337.               if (SameColor(board[rf][ff], board[rt][ft])) break;
  338.               callback(board, flags, NormalMove,
  339.                    rf, ff, rt, ft, closure);
  340.               if (board[rt][ft] != EmptySquare) break;
  341.           }
  342.           break;
  343.  
  344.         case WhiteQueen:
  345.         case BlackQueen:
  346.           for (rs = -1; rs <= 1; rs++) 
  347.         for (fs = -1; fs <= 1; fs++) {
  348.             if (rs == 0 && fs == 0) continue;
  349.             for (i = 1;; i++) {
  350.             rt = rf + (i * rs);
  351.             ft = ff + (i * fs);
  352.             if (rt < 0 || rt > 7 || ft < 0 || ft > 7) break;
  353.             if (SameColor(board[rf][ff], board[rt][ft])) break;
  354.             callback(board, flags, NormalMove,
  355.                  rf, ff, rt, ft, closure);
  356.             if (board[rt][ft] != EmptySquare) break;
  357.             }
  358.         }
  359.           break;
  360.  
  361.         case WhiteKing:
  362.         case BlackKing:
  363.           for (i = -1; i <= 1; i++)
  364.         for (j = -1; j <= 1; j++) {
  365.             if (i == 0 && j == 0) continue;
  366.             rt = rf + i;
  367.             ft = ff + j;
  368.             if (rt < 0 || rt > 7 || ft < 0 || ft > 7) continue;
  369.             if (SameColor(board[rf][ff], board[rt][ft])) continue;
  370.             callback(board, flags, NormalMove,
  371.                  rf, ff, rt, ft, closure);
  372.         }
  373.           break;
  374.       }
  375.       }
  376. }
  377.  
  378.  
  379. typedef struct {
  380.     MoveCallback cb;
  381.     VOIDSTAR cl;
  382. } GenLegalClosure;
  383.  
  384. void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
  385.      Board board;
  386.      int flags;
  387.      ChessMove kind;
  388.      int rf, ff, rt, ft;
  389.      VOIDSTAR closure;
  390. {
  391.     register GenLegalClosure *cl = (GenLegalClosure *) closure;
  392.  
  393.     if (CheckTest(board, flags, rf, ff, rt, ft,
  394.           kind == WhiteCapturesEnPassant ||
  395.           kind == BlackCapturesEnPassant)) return;
  396.     cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
  397. }
  398.  
  399.  
  400. /* Like GenPseudoLegal, but include castling moves and omit moves that
  401.    would leave the king in check.  The CASTLE_OK flags are true if
  402.    castling is not yet ruled out by a move of the king or rook.
  403. */
  404. int GenLegal(board, flags, epfile, callback, closure)
  405.      Board board;
  406.      int flags;
  407.      int epfile;
  408.      MoveCallback callback;
  409.      VOIDSTAR closure;
  410. {
  411.     GenLegalClosure cl;
  412.     int ff;
  413.  
  414.     cl.cb = callback;
  415.     cl.cl = closure;
  416.     GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
  417.  
  418.     if (CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
  419.  
  420.     /* Generate castling moves */
  421.     for (ff = 4; ff >= 3; ff-- /*ics wild 1*/) {
  422.     if ((flags & F_WHITE_ON_MOVE) &&
  423.         (flags & F_WHITE_KCASTLE_OK) &&
  424.         board[0][ff] == WhiteKing &&
  425.         board[0][ff + 1] == EmptySquare &&
  426.         board[0][ff + 2] == EmptySquare &&
  427.         board[0][6] == EmptySquare &&
  428.         board[0][7] == WhiteRook &&
  429.         !CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
  430.         !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)) {
  431.  
  432.         callback(board, flags,
  433.              ff==4 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
  434.              0, ff, 0, ff + 2, closure);
  435.     }
  436.     if ((flags & F_WHITE_ON_MOVE) &&
  437.         (flags & F_WHITE_QCASTLE_OK) &&
  438.         board[0][ff] == WhiteKing &&
  439.         board[0][ff - 1] == EmptySquare &&
  440.         board[0][ff - 2] == EmptySquare &&
  441.         board[0][1] == EmptySquare &&
  442.         board[0][0] == WhiteRook &&
  443.         !CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
  444.         !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)) {
  445.  
  446.         callback(board, flags,
  447.              ff==4 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
  448.              0, ff, 0, ff - 2, closure);
  449.     }
  450.     if (!(flags & F_WHITE_ON_MOVE) &&
  451.         (flags & F_BLACK_KCASTLE_OK) &&
  452.         board[7][ff] == BlackKing &&
  453.         board[7][ff + 1] == EmptySquare &&
  454.         board[7][ff + 2] == EmptySquare &&
  455.         board[7][6] == EmptySquare &&
  456.         board[7][7] == BlackRook &&
  457.         !CheckTest(board, flags, 7, ff, 7, ff + 1, FALSE) &&
  458.         !CheckTest(board, flags, 7, ff, 7, ff + 2, FALSE)) {
  459.  
  460.         callback(board, flags,
  461.              ff==4 ? BlackKingSideCastle : BlackKingSideCastleWild,
  462.              7, ff, 7, ff + 2, closure);
  463.     }
  464.     if (!(flags & F_WHITE_ON_MOVE) &&
  465.         (flags & F_BLACK_QCASTLE_OK) &&
  466.         board[7][ff] == BlackKing &&
  467.         board[7][ff - 1] == EmptySquare &&
  468.         board[7][ff - 2] == EmptySquare &&
  469.         board[7][1] == EmptySquare &&
  470.         board[7][0] == BlackRook &&
  471.         !CheckTest(board, flags, 7, ff, 7, ff - 1, FALSE) &&
  472.         !CheckTest(board, flags, 7, ff, 7, ff - 1, FALSE)) {
  473.  
  474.         callback(board, flags,
  475.              ff==4 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
  476.              7, ff, 7, ff - 2, closure);
  477.     }
  478.     }
  479.  
  480.     return FALSE;
  481. }
  482.  
  483.  
  484. typedef struct {
  485.     int rking, fking;
  486.     int check;
  487. } CheckTestClosure;
  488.  
  489.  
  490. void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
  491.      Board board;
  492.      int flags;
  493.      ChessMove kind;
  494.      int rf, ff, rt, ft;
  495.      VOIDSTAR closure;
  496. {
  497.     register CheckTestClosure *cl = (CheckTestClosure *) closure;
  498.  
  499.     if (rt == cl->rking && ft == cl->fking) cl->check++;
  500. }
  501.  
  502.  
  503. /* If the player on move were to move from (rf, ff) to (rt, ft), would
  504.    he leave himself in check?  Or if rf == -1, is the player on move
  505.    in check now?  enPassant must be TRUE if the indicated move is an
  506.    e.p. capture.  The possibility of castling out of a check along the
  507.    back rank is not accounted for (i.e., we still return nonzero), as
  508.    this is illegal anyway.  Return value is the number of times the
  509.    king is in check. */ 
  510. int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
  511.      Board board;
  512.      int flags;
  513.      int rf, ff, rt, ft, enPassant;
  514. {
  515.     CheckTestClosure cl;
  516.     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
  517.     ChessSquare captured;
  518.  
  519.     if (rf >= 0) {
  520.     if (enPassant) {
  521.         captured = board[rf][ft];
  522.         board[rf][ft] = EmptySquare;
  523.     } else {
  524.         captured = board[rt][ft];
  525.     }
  526.     board[rt][ft] = board[rf][ff];
  527.     board[rf][ff] = EmptySquare;
  528.     }
  529.  
  530.     /* For compatibility with ICS wild 9, we scan the board in the
  531.        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
  532.        and we test only whether that one is in check. */
  533.     cl.check = 0;
  534.     for (cl.fking = 0; cl.fking <= 7; cl.fking++)
  535.     for (cl.rking = 0; cl.rking <= 7; cl.rking++) {
  536.       if (board[cl.rking][cl.fking] == king) {
  537.           GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
  538.                  CheckTestCallback, (VOIDSTAR) &cl);
  539.           goto undo_move;  /* 2-level break */
  540.       }
  541.       }
  542.  
  543.   undo_move:
  544.  
  545.     if (rf >= 0) {
  546.     board[rf][ff] = board[rt][ft];
  547.     if (enPassant) {
  548.         board[rf][ft] = captured;
  549.         board[rt][ft] = EmptySquare;
  550.     } else {
  551.         board[rt][ft] = captured;
  552.     }
  553.     }
  554.  
  555.     return cl.check;
  556. }
  557.  
  558.  
  559. typedef struct {
  560.     int rf, ff, rt, ft;
  561.     ChessMove kind;
  562. } LegalityTestClosure;
  563.  
  564. void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
  565.      Board board;
  566.      int flags;
  567.      ChessMove kind;
  568.      int rf, ff, rt, ft;
  569.      VOIDSTAR closure;
  570. {
  571.     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
  572.  
  573.     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
  574.       cl->kind = kind;
  575. }
  576.  
  577. ChessMove LegalityTest(board, flags, epfile, rf, ff, rt, ft, promoChar)
  578.      Board board;
  579.      int flags, epfile;
  580.      int rf, ff, rt, ft, promoChar;
  581. {
  582.     LegalityTestClosure cl;
  583.     
  584.     cl.rf = rf;
  585.     cl.ff = ff;
  586.     cl.rt = rt;
  587.     cl.ft = ft;
  588.     cl.kind = BadMove;
  589.     GenLegal(board, flags, epfile, LegalityTestCallback, (VOIDSTAR) &cl);
  590.     if (promoChar != NULLCHAR) {
  591.     if (cl.kind == WhitePromotionQueen ||
  592.         cl.kind == BlackPromotionQueen) {
  593.         cl.kind = PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
  594.                       promoChar);
  595.     } else {
  596.         cl.kind = BadMove;
  597.     }
  598.     }
  599.     return cl.kind;
  600. }
  601.  
  602. typedef struct {
  603.     int count;
  604. } MateTestClosure;
  605.  
  606. void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
  607.      Board board;
  608.      int flags;
  609.      ChessMove kind;
  610.      int rf, ff, rt, ft;
  611.      VOIDSTAR closure;
  612. {
  613.     register MateTestClosure *cl = (MateTestClosure *) closure;
  614.  
  615.     cl->count++;
  616. }
  617.  
  618. /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
  619. int MateTest(board, flags, epfile)
  620.      Board board;
  621.      int flags, epfile;
  622. {
  623.     MateTestClosure cl;
  624.     int inCheck;
  625.  
  626.     cl.count = 0;
  627.     inCheck = GenLegal(board, flags, epfile, MateTestCallback, (VOIDSTAR) &cl);
  628.     if (cl.count > 0) {
  629.     return inCheck ? MT_CHECK : MT_NONE;
  630.     } else {
  631.     return inCheck ? MT_CHECKMATE : MT_STALEMATE;
  632.     }
  633. }
  634.  
  635.      
  636. void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
  637.      Board board;
  638.      int flags;
  639.      ChessMove kind;
  640.      int rf, ff, rt, ft;
  641.      VOIDSTAR closure;
  642. {
  643.     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
  644.  
  645.     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]) &&
  646.     (cl->rfIn == -1 || cl->rfIn == rf) &&
  647.     (cl->ffIn == -1 || cl->ffIn == ff) &&
  648.     (cl->rtIn == -1 || cl->rtIn == rt) &&
  649.     (cl->ftIn == -1 || cl->ftIn == ft)) {
  650.  
  651.     cl->count++;
  652.     cl->piece = board[rf][ff];
  653.     cl->rf = rf;
  654.     cl->ff = ff;
  655.     cl->rt = rt;
  656.     cl->ft = ft;
  657.     cl->kind = kind;
  658.     }
  659. }
  660.  
  661. void Disambiguate(board, flags, epfile, closure)
  662.      Board board;
  663.      int flags, epfile;
  664.      DisambiguateClosure *closure;
  665. {
  666.     closure->count = 0;
  667.     closure->rf = closure->ff = closure->rt = closure->ft = 0;
  668.     closure->kind = BadMove;
  669.     GenLegal(board, flags, epfile, DisambiguateCallback, (VOIDSTAR) closure);
  670.     if (closure->promoCharIn != NULLCHAR) {
  671.     if (closure->promoCharIn != NULLCHAR) {
  672.         if (closure->kind == WhitePromotionQueen ||
  673.         closure->kind == BlackPromotionQueen) {
  674.         closure->kind =
  675.           PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
  676.                       closure->promoCharIn);
  677.         } else {
  678.         closure->kind = BadMove;
  679.         }
  680.     }
  681.     if (closure->kind == BadMove) closure->count = 0;
  682.     }
  683.     closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));
  684.     if (closure->promoChar == '.') closure->promoChar = NULLCHAR;
  685.     if (closure->count > 1) closure->kind = AmbiguousMove;
  686. }
  687.  
  688.  
  689. typedef struct {
  690.     /* Input */
  691.     ChessSquare piece;
  692.     int rf, ff, rt, ft;
  693.     /* Output */
  694.     ChessMove kind;
  695.     int rank;
  696.     int file;
  697.     int either;
  698. } CoordsToAlgebraicClosure;
  699.  
  700. void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
  701.      Board board;
  702.      int flags;
  703.      ChessMove kind;
  704.      int rf, ff, rt, ft;
  705.      VOIDSTAR closure;
  706. {
  707.     register CoordsToAlgebraicClosure *cl =
  708.       (CoordsToAlgebraicClosure *) closure;
  709.  
  710.     if (rt == cl->rt && ft == cl->ft &&
  711.     board[rf][ff] == cl->piece) {
  712.     if (rf == cl->rf) {
  713.         if (ff == cl->ff) {
  714.         cl->kind = kind; /* this is the move we want */
  715.         } else {
  716.         cl->file++; /* need file to rule out this move */
  717.         }
  718.     } else {
  719.         if (ff == cl->ff) {
  720.         cl->rank++; /* need rank to rule out this move */
  721.         } else {
  722.         cl->either++; /* rank or file will rule out this move */
  723.         }
  724.     }        
  725.     }
  726. }
  727.  
  728. /* Convert coordinates to normal algebraic notation.
  729.    promoChar must be NULLCHAR or '.' if not a promotion.
  730. */
  731. ChessMove CoordsToAlgebraic(board, flags, epfile,
  732.                 rf, ff, rt, ft, promoChar, out)
  733.      Board board;
  734.      int flags, epfile;
  735.      int rf, ff, rt, ft;
  736.      int promoChar;
  737.      char out[MOVE_LEN];
  738. {
  739.     ChessSquare piece;
  740.     ChessMove ret;
  741.     char *outp = out;
  742.     CoordsToAlgebraicClosure cl;
  743.     
  744.     if (promoChar == '.') promoChar = NULLCHAR;
  745.     piece = board[rf][ff];
  746.     switch (piece) {
  747.       case WhitePawn:
  748.       case BlackPawn:
  749.     /* Pawn move */
  750.     *outp++ = ff + 'a';
  751.     if (ff == ft) {
  752.         /* Non-capture; use style "e5" */
  753.         *outp++ = rt + '1';
  754.     } else {
  755.         /* Capture; use style "exd5" */
  756.         *outp++ = 'x';
  757.         *outp++ = ft + 'a';
  758.         *outp++ = rt + '1';
  759.     }
  760.     /* Use promotion suffix style "=Q" */
  761.     if (promoChar != NULLCHAR) {
  762.         *outp++ = '=';
  763.         *outp++ = ToUpper(promoChar);
  764.     }
  765.     *outp = NULLCHAR;
  766.     
  767.     /* This notation is always unambiguous if the move is legal.
  768.        More code would be needed if we wanted the style "ed" for
  769.        captures, since that can be ambiguous.
  770.     */
  771.     ret = LegalityTest(board, flags, epfile,
  772.                rf, ff, rt, ft, promoChar);
  773.     break;
  774.     
  775.       case WhiteKing:
  776.       case BlackKing:
  777.     /* Test for castling or ICS wild castling */
  778.     /* Use style "O-O" (oh-oh) for PGN compatibility */
  779.     if (rf == rt &&
  780.         rf == ((piece == WhiteKing) ? 0 : 7) &&
  781.         ((ff == 4 && (ft == 2 || ft == 6)) ||
  782.          (ff == 3 && (ft == 1 || ft == 5)))) {
  783.         switch (ft) {
  784.           case 1:
  785.           case 6:
  786.         strcpy(out, "O-O");
  787.         break;
  788.           case 2:
  789.           case 5:
  790.         strcpy(out, "O-O-O");
  791.         break;
  792.         }
  793.         /* This notation is always unambiguous, unless there are
  794.            kings on both the d and e files, with "wild castling"
  795.            possible for the king on the d file and normal castling
  796.            possible for the other.  ICS rules for wild 9
  797.            effectively make castling illegal for either king in
  798.            this situation.  So I am not going to worry about it;
  799.            I'll just generate an ambiguous O-O in this case.
  800.         */
  801.         ret = LegalityTest(board, flags, epfile,
  802.                    rf, ff, rt, ft, promoChar);
  803.         break;
  804.     }
  805.     /* else fall through */
  806.     
  807.       default:
  808.     /* Piece move */
  809.     cl.rf = rf;
  810.     cl.ff = ff;
  811.     cl.rt = rt;
  812.     cl.ft = ft;
  813.     cl.piece = piece;
  814.     cl.kind = BadMove;
  815.     cl.rank = cl.file = cl.either = 0;
  816.     GenLegal(board, flags, epfile,
  817.          CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
  818.  
  819.     ret = cl.kind;
  820.     if (ret == BadMove) break;
  821.  
  822.     /* Style is "Nf3" or "Nxf7" if this is unambiguous,
  823.        else "Ngf3" or "Ngxf7",
  824.        else "N1f3" or "N5xf7",
  825.        else "Ng1f3" or "Ng5xf7".
  826.     */
  827.     *outp++ = ToUpper(PieceToChar(piece));
  828.     
  829.     if (cl.file || (cl.either && !cl.rank)) {
  830.         *outp++ = ff + 'a';
  831.     }
  832.     if (cl.rank) {
  833.         *outp++ = rf + '1';
  834.     }
  835.  
  836.     if(board[rt][ft] != EmptySquare)
  837.       *outp++ = 'x';
  838.  
  839.     *outp++ = ft + 'a';
  840.     *outp++ = rt + '1';
  841.     *outp = NULLCHAR;
  842.     break; 
  843.     
  844.       case EmptySquare:
  845.     /* Illegal move; use coordinate notation */
  846.     ret = BadMove;
  847.     break;
  848.     }
  849.     
  850.     if (ret == BadMove) {
  851.     /* Illegal move; use coordinate notation */
  852.     outp = out;
  853.     *outp++ = ff + 'a';
  854.     *outp++ = rf + '1';
  855.     *outp++ = ft + 'a';
  856.     *outp++ = rt + '1';
  857.     *outp++ = ToUpper(promoChar);
  858.     *outp = NULLCHAR;
  859.     }
  860.  
  861.     return ret;
  862. }
  863.