home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / Kibitz / Chess.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-02  |  59.1 KB  |  2,181 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        chess.c
  5. ** Written by:  Eric Soldan
  6. **
  7. ** Copyright © 1990-1992 Apple Computer, Inc.
  8. ** All rights reserved. */
  9.  
  10.  
  11.  
  12. /*****************************************************************************/
  13.  
  14.  
  15.  
  16. #include "Kibitz.h"                /* Get the Kibitz includes/typedefs, etc.    */
  17. #include "KibitzCommon.h"        /* Get the stuff in common with rez.        */
  18. #include "Kibitz.protos"        /* Get the prototypes for Kibitz.            */
  19.  
  20. #ifndef __ERRORS__
  21. #include <Errors.h>
  22. #endif
  23.  
  24. #ifndef __FONTS__
  25. #include <Fonts.h>
  26. #endif
  27.  
  28. #ifndef __TEXTEDITCONTROL__
  29. #include <TextEditControl.h>
  30. #endif
  31.  
  32. #ifndef __TOOLUTILS__
  33. #include <ToolUtils.h>
  34. #endif
  35.  
  36. #ifndef __STDIO__
  37. #include <StdIO.h>
  38. #endif
  39.  
  40. #ifndef __STRING__
  41. #include <String.h>
  42. #endif
  43.  
  44. #ifndef THINK_C
  45. #ifndef __STRINGS__
  46. #include <Strings.h>
  47. #endif
  48. #endif
  49.  
  50. #ifndef __UTILITIES__
  51. #include <Utilities.h>
  52. #endif
  53.  
  54.  
  55.  
  56. /*****************************************************************************/
  57.  
  58.  
  59.  
  60. extern Boolean    gComputerResigns;
  61.  
  62. short    gPieceLoc;
  63.  
  64. #define kLastNode         6
  65. #define kComputerResigns -9999
  66.  
  67. GameListHndl            gGenMovesHndl;
  68.  
  69. static unsigned long    idleTick;
  70. static MoveListHndl        gNodeHndl[kLastNode + 1];
  71.  
  72. static short    gNumPieces, gPosReps;
  73. static long    gTreeValue, gWhiteTotal, gBlackTotal;
  74. static long    gTreePieceValues[13] = {
  75.     -0x40000000L, -0x00090000L, -0x00050000L, -0x00030000L, -0x00030000L, -0x00010000L,
  76.      0x00000000L,
  77.      0x00010000L,  0x00030000L,  0x00030000L,  0x00050000L,  0x00090000L,  0x40000000L,
  78. };
  79.  
  80. static short    distance[10] = {0, 1, 1, 7, 7, 7, 1};
  81.     /* How far a piece can move.                        */
  82.     /* The double-pawn-push is handled as an exception. */
  83.     /* Castling is handled as an exception.                */
  84.  
  85. static short    direction[10][9] = {
  86.       0,   0,   0,   0,   0,   0,   0,   0,   0,
  87.      10,   9,  11,   0,   0,   0,   0,   0,   0,    /* Pawn moves.     */
  88.     -21, -19, -12,  -8,   8,  12,  19,  21,   0,    /* Knight moves. */
  89.     -11,  -9,   9,  11,   0,   0,   0,   0,   0,    /* Bishop moves. */
  90.     -10,  -1,   1,  10,   0,   0,   0,   0,   0,    /* Rook moves.     */
  91.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* Queen moves.     */
  92.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* King moves.     */
  93. };
  94.  
  95. static short    gPieceColor[13] = {WHITE, WHITE, WHITE, WHITE, WHITE, WHITE,
  96.                                    EMPTY,
  97.                                    BLACK, BLACK, BLACK, BLACK, BLACK, BLACK};
  98.  
  99. static short    gPieceKind[13]  = {KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN,
  100.                                    EMPTY,
  101.                                    PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING};
  102.  
  103.  
  104.  
  105. /*****************************************************************************/
  106.  
  107.  
  108.  
  109. TheDoc    newDocData
  110. #ifndef __PPCC__
  111.     /*
  112.         Work around compiler bug with aggregate initializers of
  113.         mixed alignment structures (68k structs containing PowerPC
  114.         structs).
  115.     */
  116.  
  117.  = {
  118.     kVersion,        /* File format version.                             */
  119.  
  120.     false,            /* Flag indicating print record is current.         */
  121.     {                /* Space for print record.                         */
  122.         0,
  123.         {0, 0, 0,{0, 0, 0, 0},},
  124.         {0, 0, 0, 0},
  125.         {0, 0, 0, 0, 0},
  126.         {0, 0, 0,{0, 0, 0, 0},},
  127.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  128.         {0, 0, 0, 0, 0, nil, nil, 0, 0, 0},
  129.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  130.     },
  131.  
  132.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  133.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  134.     OBNDS,  BR,    BN,    BB,    BQ,    BK,    BKB,   BKN,   BKR,  OBNDS,
  135.     OBNDS,  BP,    BP,    BP,    BP,    BP,    BP,    BP,    BP,   OBNDS,
  136.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  137.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  138.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  139.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  140.     OBNDS,  WP,    WP,    WP,    WP,    WP,    WP,    WP,    WP,   OBNDS,
  141.     OBNDS,  WR,    WN,    WB,    WQ,    WK,    WKB,   WKN,   WKR,  OBNDS,
  142.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  143.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  144.     {
  145.         WKPOS, 0,    /* White king position, king-moved count.         */
  146.         0,            /* White queen-rook-moved count.                 */
  147.         0,            /* White king-rook-moved count.                     */
  148.         BKPOS, 0,    /* Black king position, king-moved count.         */
  149.         0,            /* Black queen-rook-moved count.                 */
  150.         0            /* Black king-rook-moved count.                     */
  151.     },
  152.     0,                /* En-passant opportunity location.                 */
  153.     0,                /* En-passant opportunity pawn-to-take location. */
  154.     0,                /* Arranged en-passant opportunity loc.             */
  155.     0,                /* Arranged en-passant opp. pawn-to-take loc.     */
  156.     0,                /* Number of legal moves in move list.             */
  157.     0,                /* Index into record of game.                     */
  158.     0,                /* Number of moves in game.                         */
  159.     0,                /* My color (0 = white).                         */
  160.     0,                /* True if black started game.                     */
  161.     0,                /* True if in arrange-board mode.                 */
  162.     WP,                /* Piece hilited in arrange-board palette.         */
  163.     18000L,            /* 5 minute default time for white.                 */
  164.     18000L,            /* 5 minute default time for black.                 */
  165.     -1L,            /* Ticks remaining for white. (-1, no clock)     */
  166.     -1L,            /* Ticks remaining for black. (-1, no clock)     */
  167.     false,            /* Display board normal (not inverted.)             */
  168.     0,                /* Above info is saved to disk.                     */
  169.  
  170.     "\0",            /* Space for opponent zone.                         */
  171.     "\0",            /* Space for opponent machine.                     */
  172.     "\0",            /* Space for opponent kibitz full path.             */
  173.     "\0",            /* Space for opponent kibitz filename.             */
  174.     false,            /* Boolean for just board window.                 */
  175.     false,            /* Boolean for document is template.             */
  176.     false,            /* Document saved while computer moves white.     */
  177.     false,            /* Document saved while computer moves black.     */
  178.     0,                /* Above is new version info saved to disk.         */
  179.  
  180.     false,            /* Flag indicating existence of opponent.         */
  181.     0L,                /* ID assigned by me for this game.                 */
  182.     0L,                /* ID assigned by opponent for this game.         */
  183.     0,                /* Reason for sending the game.                     */
  184.     0,                /* State of the draw button.                      */
  185.     {
  186.         0L,            /* AEAddressDesc of opponent.                     */
  187.         nil
  188.     },
  189.     0,                /* Above is send game info.                         */
  190.  
  191.     0,                /* State for receiving AppleEvents.                 */
  192.     false,            /* Flag indicating if we originated game.         */
  193.     -1L,            /* Ticks remaining displayed for white.             */
  194.     -1L,            /* Ticks remaining displayed for black.             */
  195.     -1L,            /* Ticks for freeze clock for white.             */
  196.     -1L,            /* Ticks for freeze clock for black.             */
  197.     0L,                /* Reference tick for timer.                     */
  198.     0L,                /* Tick when computer moved last.                 */
  199.     0L,                /* Tick when received last info from opponent.     */
  200.     false,            /* Flag indicating computer moves white pieces.     */
  201.     false,            /* Flag indicating computer moves black pieces.     */
  202.     "\0",            /* Space for opponent name.                         */
  203.     "\0",            /* Space for opponent zone.                         */
  204.     "\0",            /* Space for opponent machine.                     */
  205.     0,                /* Time that last move/message received.         */
  206.     0,                /* Above info is for one machine only.             */
  207.  
  208.     false,            /* Flag indicating color change has been posted. */
  209.     0,                /* New color from config.                         */
  210.     false,            /* Flag indicating time change has been posted.     */
  211.     -1L,            /* New white time from config.                     */
  212.     -1L,            /* New black time from config.                     */
  213.     0,                /* Above info is config setting which will         */
  214.                     /* not be applied until a NULL event.             */
  215.  
  216.     nil,            /* Handle to legal move list.                     */
  217.     nil,            /* Handle to game record.                         */
  218.     nil,            /* Handle to incoming message.                     */
  219.     nil,            /* Handle to outgoing message.                     */
  220.     nil,            /* Handle to recorded sound, if any.             */
  221.     false,            /* Boolean stating if we say stuff.                 */
  222.     {
  223.         0L,            /* Default voice.                                 */
  224.         0L
  225.     },
  226.     nil,            /* Handle to send button control.                 */
  227.     nil,            /* Handle to move notify control.                 */
  228.     nil,            /* Handle to message notify control.             */
  229.     nil,            /* Handle to game-slider control.                 */
  230.     nil,            /* Handle to white-starts radio button.             */
  231.     nil,            /* Handle to black-starts radio button.             */
  232.     nil,            /* Handle to resign button.                         */
  233.     nil,            /* Handle to draw button.                         */
  234.     nil,            /* Handle to record sound button.                 */
  235.     nil,            /* Handle to send sound button.                     */
  236.     0,                /* Above info is reference to controls.             */
  237. }
  238. #endif /* __PPCC__ */
  239. ;
  240.  
  241. /*****************************************************************************/
  242.  
  243. #ifdef __PPCC__
  244.     /*
  245.         Work around compiler bug with aggregate initializers of
  246.         mixed alignment structures (68k structs containing PowerPC
  247.         structs).
  248.     */
  249. void InitNewDocData(void)
  250. {
  251.     int i,j;
  252.     
  253.     newDocData.version = kVersion;
  254.     
  255.     newDocData.printRecValid = false;
  256.     newDocData.print.iPrVersion = 0;
  257.     newDocData.print.prInfo.iDev = 0;
  258.     newDocData.print.prInfo.iVRes = 0;
  259.     newDocData.print.prInfo.iHRes = 0;
  260.     newDocData.print.prInfo.rPage.top = 0;
  261.     newDocData.print.prInfo.rPage.left = 0;
  262.     newDocData.print.prInfo.rPage.bottom = 0;
  263.     newDocData.print.prInfo.rPage.right = 0;
  264.     newDocData.print.rPaper.top = 0;
  265.     newDocData.print.rPaper.left = 0;
  266.     newDocData.print.rPaper.bottom = 0;
  267.     newDocData.print.rPaper.right = 0;
  268.     newDocData.print.prStl.wDev = 0;
  269.     newDocData.print.prStl.iPageV = 0;
  270.     newDocData.print.prStl.iPageH = 0;
  271.     newDocData.print.prStl.bPort = 0;
  272.     newDocData.print.prStl.feed = 0;
  273.     newDocData.print.prInfoPT.iDev = 0;
  274.     newDocData.print.prInfoPT.iVRes = 0;
  275.     newDocData.print.prInfoPT.iHRes = 0;
  276.     newDocData.print.prInfoPT.rPage.top = 0;
  277.     newDocData.print.prInfoPT.rPage.left = 0;
  278.     newDocData.print.prInfoPT.rPage.bottom = 0;
  279.     newDocData.print.prInfoPT.rPage.right = 0;
  280.     newDocData.print.prXInfo.iRowBytes = 0;
  281.     newDocData.print.prXInfo.iBandV = 0;
  282.     newDocData.print.prXInfo.iBandH = 0;
  283.     newDocData.print.prXInfo.iDevBytes = 0;
  284.     newDocData.print.prXInfo.iBands = 0;
  285.     newDocData.print.prXInfo.bPatScale = 0;
  286.     newDocData.print.prXInfo.bUlThick = 0;
  287.     newDocData.print.prXInfo.bUlOffset = 0;
  288.     newDocData.print.prXInfo.bUlShadow = 0;
  289.     newDocData.print.prXInfo.scan = 0;
  290.     newDocData.print.prXInfo.bXInfoX = 0;
  291.     newDocData.print.prJob.iFstPage = 0;
  292.     newDocData.print.prJob.iLstPage = 0;
  293.     newDocData.print.prJob.iCopies = 0;
  294.     newDocData.print.prJob.bJDocLoop = 0;
  295.     newDocData.print.prJob.fFromUsr = 0;
  296.     newDocData.print.prJob.pIdleProc = nil;
  297.     newDocData.print.prJob.pFileName = nil;
  298.     newDocData.print.prJob.iFileVol = 0;
  299.     newDocData.print.prJob.bFileVers = 0;
  300.     newDocData.print.prJob.bJobX = 0;
  301.     for (i=0; i < 19; ++i)
  302.         newDocData.print.printX[i] = 0;
  303.  
  304.     for ( i = 0; i < 120; ++i )
  305.        newDocData.theBoard[i] = OBNDS;
  306.     newDocData.theBoard[21] = BR;
  307.     newDocData.theBoard[22] = BN;
  308.     newDocData.theBoard[23] = BB;
  309.     newDocData.theBoard[24] = BQ;
  310.     newDocData.theBoard[25] = BK;
  311.     newDocData.theBoard[26] = BKB;
  312.     newDocData.theBoard[27] = BKN;
  313.     newDocData.theBoard[28] = BKR;
  314.     newDocData.theBoard[31] = BP;
  315.     newDocData.theBoard[32] = BP;
  316.     newDocData.theBoard[33] = BP;
  317.     newDocData.theBoard[34] = BP;
  318.     newDocData.theBoard[35] = BP;
  319.     newDocData.theBoard[36] = BP;
  320.     newDocData.theBoard[37] = BP;
  321.     newDocData.theBoard[38] = BP;
  322.     for ( i = 40; i <= 70; i += 10 )
  323.        for (j = 1; j<= 8; ++j)
  324.           newDocData.theBoard[i+j] = 0;
  325.     newDocData.theBoard[81] = WP;
  326.     newDocData.theBoard[82] = WP;
  327.     newDocData.theBoard[83] = WP;
  328.     newDocData.theBoard[84] = WP;
  329.     newDocData.theBoard[85] = WP;
  330.     newDocData.theBoard[86] = WP;
  331.     newDocData.theBoard[87] = WP;
  332.     newDocData.theBoard[88] = WP;
  333.     newDocData.theBoard[91] = WR;
  334.     newDocData.theBoard[92] = WN;
  335.     newDocData.theBoard[93] = WB;
  336.     newDocData.theBoard[94] = WQ;
  337.     newDocData.theBoard[95] = WK;
  338.     newDocData.theBoard[96] = WKB;
  339.     newDocData.theBoard[97] = WKN;
  340.     newDocData.theBoard[98] = WKR;
  341.  
  342.     newDocData.king[0].kingLoc = WKPOS;
  343.     newDocData.king[0].kingMoves = 0;
  344.     newDocData.king[0].rookMoves[0] = 0;
  345.     newDocData.king[0].rookMoves[1] = 0;
  346.     newDocData.king[1].kingLoc = BKPOS;
  347.     newDocData.king[1].kingMoves = 0;
  348.     newDocData.king[1].rookMoves[0] = 0;
  349.     newDocData.king[1].rookMoves[1] = 0;
  350.     
  351.     newDocData.enPasMove = 0;
  352.     newDocData.enPasPawnLoc = 0;
  353.     newDocData.arngEnPasMove = 0;
  354.     newDocData.arngEnPasPawnLoc = 0;
  355.     newDocData.numLegalMoves = 0;        
  356.     newDocData.gameIndex = 0;        
  357.     newDocData.numGameMoves = 0;            
  358.     newDocData.myColor = 0;            
  359.     newDocData.startColor = 0;                
  360.     newDocData.arrangeBoard = 0;                
  361.     newDocData.palettePiece = WP;            
  362.     newDocData.defaultTime[0] = 18000L;    
  363.     newDocData.defaultTime[1] = 18000L;        
  364.     newDocData.timeLeft[0] = -1L;        
  365.     newDocData.timeLeft[1] = -1L;            
  366.     newDocData.invertBoard = false;        
  367.     newDocData.endFileInfo1 = 0;
  368.     
  369.     ccpy(newDocData.reconnectZone,"\0");            
  370.     ccpy(newDocData.reconnectMachine,"\0");            
  371.     ccpy(newDocData.reconnectPath,"\0");    
  372.     ccpy(newDocData.reconnectApp,"\0");
  373.     
  374.     newDocData.justBoardWindow = false;
  375.     newDocData.docIsTemplate = false;
  376.     newDocData.keepCMWhite = false;
  377.     newDocData.keepCMBlack = false;
  378.     newDocData.endFileInfo2 = 0;                
  379.  
  380.     newDocData.twoPlayer = false;            
  381.     newDocData.gameID_0 = 0L;                
  382.     newDocData.gameID_1 = 0L;                
  383.     newDocData.sendReason = 0;                
  384.     newDocData.drawBtnState = 0;    
  385.     
  386.     newDocData.locOfOpponent.descriptorType = 0L;
  387.     newDocData.locOfOpponent.dataHandle = nil;
  388.     newDocData.endSendInfo = 0;
  389.     
  390.     newDocData.resync = 0;
  391.     newDocData.creator = false;
  392.     newDocData.displayTime[0] = -1L;            
  393.     newDocData.displayTime[1] = -1L;            
  394.     newDocData.freezeTime[0] = -1L;            
  395.     newDocData.freezeTime[1] = -1L;            
  396.     newDocData.timerRefTick = 0L;
  397.     newDocData.compMoveTick = 0L;
  398.     newDocData.gotUpdateTick = 0L;
  399.     newDocData.compMovesWhite = false;
  400.     newDocData.compMovesBlack = false;
  401.     
  402.     ccpy(newDocData.opponentName,"\0");            
  403.     ccpy(newDocData.opponentZone,"\0");            
  404.     ccpy(newDocData.opponentMachine,"\0");
  405.     
  406.     newDocData.timeLastReceive = 0;
  407.     newDocData.endLocalInfo = 0;
  408.     
  409.     newDocData.configColorChange = false;
  410.     newDocData.configColor = 0;
  411.     newDocData.configTimeChange = false;
  412.     newDocData.configTime[0] = -1L;
  413.     newDocData.configTime[1] = -1L;
  414.     newDocData.endConfigInfo = 0;
  415.     
  416.     newDocData.legalMoves = nil;
  417.     newDocData.gameMoves = nil;
  418.     newDocData.message[0] = nil;
  419.     newDocData.message[1] = nil;
  420.     newDocData.sound = nil;
  421.     newDocData.doSpeech = false;
  422.     newDocData.theVoice.creator = 0L;
  423.     newDocData.theVoice.id = 0L;
  424.     
  425.     newDocData.sendMessage = nil;
  426.     newDocData.beepOnMove = nil;
  427.     newDocData.beepOnMssg = nil;
  428.     newDocData.gameSlider = nil;
  429.     newDocData.wbStart[0] = nil;
  430.     newDocData.wbStart[1] = nil;
  431.     newDocData.resign = nil;
  432.     newDocData.draw = nil;
  433.     newDocData.record = nil;
  434.     newDocData.sendSnd = nil;
  435.     newDocData.endControls = 0;
  436. }
  437. #endif /* __PPCC__ */
  438.  
  439.  
  440. /*****************************************************************************/
  441.  
  442.  
  443.  
  444. #pragma segment Chess
  445. OSErr    InitLogic(void)
  446. {
  447.     short    i;
  448.  
  449.     if (!(gGenMovesHndl = (GameListHndl)NewHandle(0))) return(memFullErr);
  450.  
  451.     for (i = 0; i <= kLastNode; ++i)
  452.         if (!(gNodeHndl[i] = (MoveListHndl)NewHandle(0))) return(memFullErr);
  453.  
  454.     return(noErr);
  455. }
  456.  
  457.  
  458.  
  459. /*****************************************************************************/
  460.  
  461.  
  462.  
  463. #pragma segment Chess
  464. void    NewGame(FileRecHndl game)
  465. {
  466.     TheDocPtr    docPtr;
  467.  
  468.     docPtr = &(*game)->doc;
  469.  
  470.     newDocData.legalMoves = docPtr->legalMoves;
  471.     newDocData.gameMoves  = docPtr->gameMoves;
  472.  
  473.     *docPtr = newDocData;
  474.  
  475.     newDocData.legalMoves = nil;
  476.     newDocData.gameMoves  = nil;
  477. }
  478.  
  479.  
  480.  
  481. /*****************************************************************************/
  482.  
  483.  
  484.  
  485. #pragma segment Chess
  486. void    GenerateLegalMoves(FileRecHndl game)
  487. {
  488.     short            gameIndex, numGameMoves, square, piece;
  489.     short            color, pieceColor;
  490.     short            row, dirNum, dir, dist;
  491.     short            s, d, dest, destColor, epLoc;
  492.     long            size;
  493.     Boolean            docDirty, check, kspiece, dkspiece;
  494.     GameListHndl    gameMoves;
  495.  
  496.     gameIndex    = (*game)->doc.gameIndex;
  497.     gameMoves    = (*game)->doc.gameMoves;
  498.     numGameMoves = (*game)->doc.numGameMoves;
  499.     docDirty     = (*game)->fileState.docDirty;
  500.  
  501.     (*game)->doc.numLegalMoves = 0;            /* Start the list over. */
  502.  
  503.     if ((gameIndex) && (gameIndex == numGameMoves))
  504.         if (!(**gameMoves)[gameIndex - 1].moveFrom) return;
  505.             /* Resignation or agreed-upon draw recorded.
  506.             ** Game over, so no legal moves. */
  507.  
  508.     SetHandleSize((Handle)gGenMovesHndl, size = GetHandleSize((Handle)gameMoves));
  509.     BlockMove(*(Handle)gameMoves, *(Handle)gGenMovesHndl, size);
  510.     (*game)->doc.gameMoves = gGenMovesHndl;            /* Protect the game moves list. */
  511.  
  512.     color = WhosMove(game);                        /* Who's move it is. */
  513.  
  514.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  515.         /* Scan for pieces of correct color. */
  516.  
  517.         if ((piece = (*game)->doc.theBoard[square]) == EMPTY) continue;
  518.             /* Empty square, so next square, please. */
  519.  
  520.         if (piece == OBNDS) continue;
  521.             /* Out of bounds. */
  522.  
  523.         kspiece = false;
  524.         if (piece < WK) {
  525.             kspiece = true;
  526.             piece += KSIDEPIECE;
  527.         }
  528.         if (piece > BK) {
  529.             kspiece = true;
  530.             piece -= KSIDEPIECE;
  531.         }
  532.  
  533.         pieceColor = BLACK;
  534.         if (piece < 0) {
  535.             pieceColor = WHITE;
  536.             piece = -piece;
  537.         }
  538.  
  539.         if (pieceColor != color) continue;
  540.             /* Not our piece. */
  541.  
  542.         row = square / 10;
  543.  
  544.         for (dirNum = 0; (dir = direction[piece][dirNum]) != 0; ++dirNum) {
  545.             /* The direction we will move a piece.  This is correct in all
  546.             ** cases except for white pawns.  Without an adjustment, they
  547.             ** would move in the direction of black pawns, i.e., backwards.
  548.             */
  549.  
  550.             dist = distance[piece];
  551.                 /* The distance a piece can move, in all cases except a
  552.                 ** double-pawn-push and castling.
  553.                 */
  554.  
  555.             if (piece == PAWN) {
  556.                 if (color == WHITE) dir = -dir;
  557.                     /* White pawns will now move forwards. */
  558.  
  559.                 if ((!dirNum) && ((row == 3) || (row == 8))) dist = 2;
  560.                     /* In the case of a pawn, the first direction we check
  561.                     ** is forwards, so if dirNum is 0, we are pushing pawns.
  562.                     **
  563.                     ** Allow double-pawn-push if pawn is on correct row.  We don't
  564.                     ** have to worry about which color the pawn is if the pawn
  565.                     ** has advanced to the other double-push row.  It will
  566.                     ** double-push itself out of bounds.
  567.                     */
  568.             }
  569.  
  570.             for (s = square, d = 1; d <= dist; ++d) {
  571.  
  572.                 s += dir;
  573.                 dest = (*game)->doc.theBoard[s];
  574.                 if (dest == OBNDS) break;    /* Can't go this direction anymore. */
  575.  
  576.                 dkspiece = false;
  577.                 if (dest < WK) {
  578.                     dkspiece = true;
  579.                     dest += KSIDEPIECE;
  580.                 }
  581.                 if (dest > BK) {
  582.                     dkspiece = true;
  583.                     dest -= KSIDEPIECE;
  584.                 }
  585.  
  586.                 destColor = BLACK;
  587.                 if (dest < 0) {
  588.                     destColor = WHITE;
  589.                     dest = -dest;
  590.                 }
  591.  
  592.                 if ((dest) && (destColor == color)) break;
  593.                     /* Ran into our own piece, so can't go this direction anymore. */
  594.  
  595.                 if (dest == KING) break;
  596.                     /* Never allow the king to be taken. */
  597.  
  598.                 if (piece == PAWN) {
  599.                     if (!dirNum) {                /* If pawn push... */
  600.                         if (dest) break;        /* Can't take on a pawn-push. */
  601.                     }
  602.                     else {
  603.                         if (dest == EMPTY) {    /* If possible en-passant... */
  604.                             if ((*game)->doc.enPasMove != s) break;
  605.                                 /* Not en-passant pawn capture. */
  606.                             epLoc = (*game)->doc.enPasPawnLoc;
  607.                             dest  = (*game)->doc.theBoard[epLoc];
  608.                             destColor = BLACK;
  609.                             if (dest < 0) {
  610.                                 destColor = WHITE;
  611.                                 dest = -dest;
  612.                             }
  613.                             if (destColor == color) break;
  614.                                 /* We can't en-passant our own piece. */
  615.                             if (dest != PAWN) break;
  616.                                 /* We can only en-passant pawns. */
  617.                         }
  618.                     }
  619.                 }
  620.  
  621.                 MakeMove(game, square, s, QUEEN);
  622.                 check = SquareAttacked(game,
  623.                     (*game)->doc.king[color].kingLoc, color);
  624.                 UnmakeMove(game);
  625.                 if (!check) AddLegalMove(game, square, s);
  626.                     /* Move didn't put (or leave) king in check, so it is a
  627.                     ** valid move.  Since it is valid, record it. */
  628.  
  629.                 if (dest) break;    /* Once we hit a piece, we are
  630.                                     ** done in this direction. */
  631.             }
  632.         }
  633.     }
  634.  
  635.     square = (*game)->doc.king[color].kingLoc;
  636.     if (CastleOkay(game, QSIDE)) AddLegalMove(game, square, square - 2);
  637.     if (CastleOkay(game, KSIDE)) AddLegalMove(game, square, square + 2);
  638.         /* If castling possible, add it to move list. */
  639.  
  640.     (*game)->doc.gameMoves      = gameMoves;
  641.     (*game)->doc.numGameMoves   = numGameMoves;
  642.     (*game)->fileState.docDirty = docDirty;
  643.         /* Restore things the way we were.  We are done with MakeMove services. */
  644. }
  645.  
  646.  
  647.  
  648. /*****************************************************************************/
  649.  
  650.  
  651.  
  652. #pragma segment Chess
  653. void    AddLegalMove(FileRecHndl game, short from, short to)
  654. {
  655.     MoveListHndl    lglMoves;
  656.     short            numLglMoves;
  657.     long            newHndlSize, oldHndlSize;
  658.  
  659.     numLglMoves = (*game)->doc.numLegalMoves;
  660.     lglMoves    = (*game)->doc.legalMoves;
  661.  
  662.     oldHndlSize = GetHandleSize((Handle)lglMoves);
  663.     newHndlSize = ((numLglMoves | 0x3F) + 1) * sizeof(MoveElement);
  664.     if (newHndlSize != oldHndlSize)
  665.         SetHandleSize((Handle)lglMoves, newHndlSize);
  666.  
  667.     (**lglMoves)[numLglMoves].moveFrom = from;
  668.     (**lglMoves)[numLglMoves].moveTo   = to;
  669.     (**lglMoves)[numLglMoves].value    = 0;
  670.  
  671.     ++(*game)->doc.numLegalMoves;
  672. }
  673.  
  674.  
  675.  
  676. /*****************************************************************************/
  677.  
  678.  
  679.  
  680. #pragma segment Chess
  681. Boolean    CastleOkay(FileRecHndl game, short castleSide)
  682. {
  683.     short    color;
  684.     short    castleDir, kingLoc, rookLoc;
  685.     short    i, j, piece, pieceColor;
  686.     Boolean    kspiece;
  687.  
  688.     color = WhosMove(game);            /* Who's move it is. */
  689.  
  690.     if ((*game)->doc.king[color].kingMoves) return(false);
  691.         /* Can't castle.  King has already moved. */
  692.  
  693.     rookLoc = (kingLoc = (*game)->doc.king[color].kingLoc) + 3;
  694.     if ((castleDir = castleSide) == QSIDE) {
  695.         rookLoc -= 7;
  696.         --castleDir;
  697.     }
  698.  
  699.     for (i = kingLoc, j = 3; j; i += castleDir, --j)
  700.         if (SquareAttacked(game, i, color)) return(false);
  701.             /* Can't castle out of, through, or into check. */
  702.  
  703.     if ((*game)->doc.king[color].rookMoves[castleSide]) return(false);
  704.         /* Rook trying to castle with has already moved,
  705.         ** or has been taken.
  706.         */
  707.  
  708.     piece = (*game)->doc.theBoard[rookLoc];
  709.     kspiece = false;
  710.     if (piece < WK) {
  711.         kspiece = true;
  712.         piece += KSIDEPIECE;
  713.     }
  714.     if (piece > BK) {
  715.         kspiece = true;
  716.         piece -= KSIDEPIECE;
  717.     }
  718.  
  719.     pieceColor = BLACK;
  720.     if (piece < 0) {
  721.         pieceColor = WHITE;
  722.         piece = -piece;
  723.     }
  724.     if (color != pieceColor) return(false);
  725.     if (piece != ROOK)         return(false);
  726.         /* These deviant conditions can occur if user arranged the board. */
  727.  
  728.     /* So far, everything is cool.  The only remaining possible problem is
  729.     ** that there is a piece (or more) between the king and the rook. */
  730.  
  731.     while (kingLoc += castleDir, kingLoc != rookLoc)
  732.         if ((*game)->doc.theBoard[kingLoc]) return(false);
  733.             /* There is a piece in the way, so we can't castle. */
  734.  
  735.     return(true);        /* The castling move is okay. */
  736. }
  737.  
  738.  
  739.  
  740. /*****************************************************************************/
  741.  
  742.  
  743.  
  744. #pragma segment Chess
  745. void    MakeMove(FileRecHndl game, short moveFrom, short moveTo, short promoteTo)
  746. {
  747.     GameListHndl    gameMoves;
  748.     long            newHndlSize, oldHndlSize;
  749.     short            gameIndex, numGameMoves, color, rank, i;
  750.     short            pieceMoved, pieceCaptured, pieceCapturedFrom;
  751.     short            absPieceMoved, delta, middle, oldRookLoc;
  752.     Boolean            modifyGame;
  753.  
  754.     if (moveFrom == -1) {
  755.         UnmakeMove(game);
  756.         return;
  757.     }
  758.  
  759.     gameIndex    = (*game)->doc.gameIndex;
  760.     numGameMoves = (*game)->doc.numGameMoves;
  761.     gameMoves    = (*game)->doc.gameMoves;
  762.     color        = WhosMove(game);
  763.  
  764.     i = (gameIndex > numGameMoves) ? gameIndex : numGameMoves;
  765.     newHndlSize = ((i | 0x3F) + 1) * sizeof(GameElement);
  766.     oldHndlSize = GetHandleSize((Handle)gameMoves);
  767.     if (newHndlSize != oldHndlSize)
  768.         SetHandleSize((Handle)gameMoves, newHndlSize);
  769.  
  770.     modifyGame = true;
  771.  
  772.     if (moveFrom == 1) {
  773.         if (gameIndex >= numGameMoves) return;
  774.             /* Already positioned at the end of the game. */
  775.         moveFrom   = (**gameMoves)[gameIndex].moveFrom;
  776.         moveTo     = (**gameMoves)[gameIndex].moveTo;
  777.         promoteTo  = (**gameMoves)[gameIndex].promoteTo;
  778.         modifyGame = false;
  779.     }
  780.  
  781.     if (!moveFrom) {        /* Draw agreed upon, or player resigned. */
  782.         if (gameIndex)
  783.             if (!(**gameMoves)[gameIndex - 1].moveFrom) --gameIndex;
  784.                 /* In case there is a race condition, only allow one game-ending
  785.                 ** "move" to occur. */
  786.         (**gameMoves)[gameIndex].moveFrom          = 0;
  787.         (**gameMoves)[gameIndex].moveTo            = moveTo;
  788.         (**gameMoves)[gameIndex].pieceCaptured     = 0;
  789.         (**gameMoves)[gameIndex].pieceCapturedFrom = 0;
  790.         (**gameMoves)[gameIndex].promoteTo         = 0;
  791.         (*game)->doc.gameIndex = ++gameIndex;
  792.         (*game)->doc.numGameMoves   = gameIndex;
  793.         (*game)->fileState.docDirty = true;
  794.         (*game)->doc.resync = kResync;
  795.         return;
  796.     }
  797.  
  798.     pieceMoved        = (*game)->doc.theBoard[moveFrom];
  799.     pieceCaptured     = (*game)->doc.theBoard[moveTo];
  800.     pieceCapturedFrom = moveTo;
  801.  
  802.     absPieceMoved = (pieceMoved < 0) ? -pieceMoved : pieceMoved;
  803.  
  804.     if (absPieceMoved == PAWN) {
  805.  
  806.         rank = moveTo / 10;
  807.         if ((rank == 2) || (rank == 9)) {
  808.             if (promoteTo < 0) promoteTo = -promoteTo;
  809.             pieceMoved *= promoteTo;
  810.             promoteTo = pieceMoved;
  811.         }
  812.         else promoteTo = 0;
  813.  
  814.         if (moveTo == (*game)->doc.enPasMove) {
  815.             pieceCaptured     = -pieceMoved;
  816.             pieceCapturedFrom = (*game)->doc.enPasPawnLoc;
  817.         }        /* If pawn move is onto en-passant move square, then the
  818.                 ** capture is from a square other than moveTo.
  819.                 */
  820.     }
  821.     else promoteTo = 0;
  822.  
  823.     (**gameMoves)[gameIndex].moveFrom          = moveFrom;
  824.     (**gameMoves)[gameIndex].moveTo            = moveTo;
  825.     (**gameMoves)[gameIndex].pieceCaptured     = pieceCaptured;
  826.     (**gameMoves)[gameIndex].pieceCapturedFrom = pieceCapturedFrom;
  827.     (**gameMoves)[gameIndex].promoteTo         = promoteTo;
  828.     (*game)->doc.gameIndex = ++gameIndex;
  829.  
  830.     if (modifyGame) {
  831.         (*game)->doc.numGameMoves   = gameIndex;
  832.         (*game)->fileState.docDirty = true;
  833.     }
  834.  
  835.     /* The move has now been recorded in the move list.  Now make the move. */
  836.  
  837.     (*game)->doc.theBoard[pieceCapturedFrom] = 0;
  838.     (*game)->doc.theBoard[moveTo]            = pieceMoved;
  839.     (*game)->doc.theBoard[moveFrom]          = 0;
  840.         /* The move is now made, except for the rook if castling. */
  841.  
  842.     delta  = moveTo - moveFrom;
  843.     middle = (moveTo + moveFrom) / 2;
  844.  
  845.     if (absPieceMoved == KING) {
  846.         oldRookLoc = 0;
  847.         if (delta == -2) oldRookLoc = moveFrom - 4;
  848.         if (delta == 2)  oldRookLoc = moveFrom + 3;
  849.         if (oldRookLoc) {
  850.             (*game)->doc.theBoard[middle] = (*game)->doc.theBoard[oldRookLoc];
  851.             (*game)->doc.theBoard[oldRookLoc] = 0;
  852.         }
  853.     }        /* Castling (and therefore move) now complete. */
  854.  
  855.  
  856.     /* All that remains is some information updating for castling, king
  857.     ** position, and en-passant. */
  858.  
  859.     if (absPieceMoved == KING) {
  860.         (*game)->doc.king[color].kingLoc = moveTo;
  861.         ++(*game)->doc.king[color].kingMoves;
  862.     }
  863.  
  864.     if ((moveFrom==21) || (moveTo==21)) ++(*game)->doc.king[BLACK].rookMoves[QSIDE];
  865.     if ((moveFrom==28) || (moveTo==28)) ++(*game)->doc.king[BLACK].rookMoves[KSIDE];
  866.     if ((moveFrom==91) || (moveTo==91)) ++(*game)->doc.king[WHITE].rookMoves[QSIDE];
  867.     if ((moveFrom==98) || (moveTo==98)) ++(*game)->doc.king[WHITE].rookMoves[KSIDE];
  868.         /* This accounts for all rook moves/captures, other than castling.
  869.         ** This is necessary to keep track of rook moves/captures to determine
  870.         ** if castling is allowed.  Rook moves when castling don't have to be
  871.         ** accounted for, since the king move in the castle will prevent
  872.         ** any more castling.
  873.         */
  874.  
  875.     /* If the move was a double-pawn-push, then we have some en-passant
  876.     ** information to record.  Otherwise, we need to zero-out these values. */
  877.  
  878.     if (absPieceMoved == PAWN) {
  879.  
  880.         if ((delta != -20) && (delta != 20))
  881.             middle = moveTo = 0;
  882.                 /* If not a double-pawn-push, then record 0's for the
  883.                 ** en-passant information.  This will prevent en-passant
  884.                 ** moves from being generated. */
  885.  
  886.     }
  887.     else middle = moveTo = 0;
  888.  
  889.     (*game)->doc.enPasMove    = middle;
  890.     (*game)->doc.enPasPawnLoc = moveTo;
  891.         /* Record the en-passant information.  These values are
  892.         ** non-zero if a pawn was double-pushed. */
  893.  
  894. }
  895.  
  896.  
  897.  
  898. /*****************************************************************************/
  899.  
  900.  
  901.  
  902. #pragma segment Chess
  903. void    UnmakeMove(FileRecHndl game)
  904. {
  905.     GameListHndl    gameMoves;
  906.     short            gameIndex, numGameMoves;
  907.     short            moveFrom, moveTo, pieceCaptured, pieceCapturedFrom;
  908.     short            promoteTo, pieceMoved, color, delta, oldRookLoc, middle;
  909.  
  910.     gameIndex    = (*game)->doc.gameIndex;
  911.     numGameMoves = (*game)->doc.numGameMoves;
  912.     gameMoves    = (*game)->doc.gameMoves;
  913.  
  914.     if (!gameIndex) return;
  915.     --gameIndex;
  916.  
  917.     moveFrom          = (**gameMoves)[gameIndex].moveFrom;
  918.     moveTo            = (**gameMoves)[gameIndex].moveTo;
  919.     pieceCaptured     = (**gameMoves)[gameIndex].pieceCaptured;
  920.     pieceCapturedFrom = (**gameMoves)[gameIndex].pieceCapturedFrom;
  921.     promoteTo         = (**gameMoves)[gameIndex].promoteTo;
  922.  
  923.     if (moveFrom) {        /* The "move" could be a resign or draw.  Make sure it isn't. */
  924.         pieceMoved = (*game)->doc.theBoard[moveTo];
  925.         if (promoteTo) pieceMoved = (pieceMoved < 0) ? -1 : 1;
  926.  
  927.         (*game)->doc.theBoard[moveFrom]          = pieceMoved;
  928.         (*game)->doc.theBoard[moveTo]            = 0;
  929.         (*game)->doc.theBoard[pieceCapturedFrom] = pieceCaptured;
  930.             /* Any move now undone, except for castling.  The rook still has
  931.             ** to be put back. */
  932.  
  933.         if ((pieceMoved == KING) || (pieceMoved == -KING)) {
  934.  
  935.             color = ((gameIndex + (*game)->doc.startColor) & 0x01);        /* Who's move it is. */
  936.             (*game)->doc.king[color].kingLoc = moveFrom;
  937.             --(*game)->doc.king[color].kingMoves;
  938.  
  939.             delta  = moveTo - moveFrom;
  940.             oldRookLoc = 0;
  941.             if (delta == -2) oldRookLoc = moveFrom - 4;
  942.             if (delta == 2)  oldRookLoc = moveFrom + 3;
  943.             if (oldRookLoc) {
  944.                 middle = (moveTo + moveFrom) / 2;
  945.                 (*game)->doc.theBoard[oldRookLoc] = (*game)->doc.theBoard[middle];
  946.                 (*game)->doc.theBoard[middle] = 0;
  947.             }
  948.         }        /* Castling now completely undone. */
  949.  
  950.         if ((moveFrom == 21) || (moveTo == 21)) --(*game)->doc.king[BLACK].rookMoves[QSIDE];
  951.         if ((moveFrom == 28) || (moveTo == 28)) --(*game)->doc.king[BLACK].rookMoves[KSIDE];
  952.         if ((moveFrom == 91) || (moveTo == 91)) --(*game)->doc.king[WHITE].rookMoves[QSIDE];
  953.         if ((moveFrom == 98) || (moveTo == 98)) --(*game)->doc.king[WHITE].rookMoves[KSIDE];
  954.             /* Undo any rook move/capture accounting.  This info is used when
  955.             ** determining of castling is allowed.
  956.             */
  957.  
  958.         (*game)->doc.enPasMove = (*game)->doc.enPasPawnLoc = 0;
  959.             /* Assume move previous to the one we just undid was not a
  960.             ** double-pawn-push.  If it was not a double-pawn-push, then
  961.             ** en-passant moves are not possible at the move number we
  962.             ** just undid.
  963.             */
  964.     }
  965.  
  966.     if (gameIndex) {        /* Restore en-passant possibilities. */
  967.  
  968.         moveFrom   = (**gameMoves)[--gameIndex].moveFrom;
  969.         moveTo     = (**gameMoves)[gameIndex++].moveTo;
  970.         pieceMoved = (*game)->doc.theBoard[moveTo];
  971.  
  972.         if ((pieceMoved == PAWN) || (pieceMoved == -PAWN)) {
  973.             delta = moveTo - moveFrom;
  974.             if ((delta == -20) || (delta == 20)) {
  975.                 (*game)->doc.enPasMove    = (moveTo + moveFrom) / 2;
  976.                 (*game)->doc.enPasPawnLoc = moveTo;
  977.             }
  978.         }
  979.     }
  980.     else {
  981.         (*game)->doc.enPasMove    = (*game)->doc.arngEnPasMove;
  982.         (*game)->doc.enPasPawnLoc = (*game)->doc.arngEnPasPawnLoc;
  983.     }
  984.  
  985.     (*game)->doc.gameIndex = gameIndex;
  986. }
  987.  
  988.  
  989.  
  990. /*****************************************************************************/
  991.  
  992.  
  993.  
  994. #pragma segment Chess
  995. short    SquareAttacked(FileRecHndl game, short square, short color)
  996. {
  997.     short    destColor;
  998.     short    kind;
  999.     short    dirNum, dir;
  1000.     short    s, dist, maxDist, dest, taker, takerLoc;
  1001.  
  1002.     taker    = KING + 1;        /* This is a no-take flag. */
  1003.     takerLoc = 0;
  1004.  
  1005.     for (kind = KNIGHT; kind <= QUEEN; kind += (QUEEN - KNIGHT)) {
  1006.         /* Check in the knight and queen directions. */
  1007.  
  1008.         for (dirNum = 0; (dir = direction[kind][dirNum]) != 0; ++dirNum) {
  1009.             /* The direction we will scan for an attack, for the most part. */
  1010.  
  1011.             maxDist = (kind == KNIGHT) ? 1 : 7;
  1012.             for (s = square, dist = 1; dist <= maxDist; ++dist) {
  1013.  
  1014.                 s += dir;
  1015.                 if ((dest = (*game)->doc.theBoard[s]) == EMPTY) continue;
  1016.                     /* Empty square, so keep looking. */
  1017.  
  1018.                 if (dest == OBNDS) break;
  1019.                     /* Can't be attacked from this direction anymore. */
  1020.  
  1021.                 destColor = BLACK;
  1022.                 if (dest < 0) {
  1023.                     destColor = WHITE;
  1024.                     dest = -dest;
  1025.                 }
  1026.                 if (dest > BK) dest -= KSIDEPIECE;
  1027.  
  1028.                 if (destColor == color) break;
  1029.                     /* Ran into our own piece, so no attack
  1030.                     ** from this direction.
  1031.                     */
  1032.  
  1033.                 if (dest >= taker) continue;
  1034.  
  1035.                 if (kind == KNIGHT)    {        /* If we are looking for knights... */
  1036.                     if (dest == KNIGHT) {
  1037.                         taker    = KNIGHT;
  1038.                         takerLoc = s;
  1039.                         dirNum   = 7;        /* Since there is 'an' attack by a knight,
  1040.                                             ** we don't care if there is another.
  1041.                                             ** We have already established a knight
  1042.                                             ** take, so we can skip the rest of the
  1043.                                             ** knight directions. */
  1044.                     }
  1045.                     continue;        /* Only knights can take in this direction. */
  1046.                 }
  1047.  
  1048.                 if (dest == KING) {
  1049.                     if (dist == 1) {
  1050.                         taker    = KING;
  1051.                         takerLoc = s;
  1052.                     }
  1053.                     break;
  1054.                 }            /* We are looking in the non-knight move directions
  1055.                             ** for attackers.  If a king is found as the possible
  1056.                             ** attacker, make sure it is in range (one square away)
  1057.                             ** before counting it as an attack.  If it isn't one
  1058.                             ** square away, then it serves to prevent any other
  1059.                             ** attack from this direction.
  1060.                             */
  1061.  
  1062.                 if (dest == QUEEN) {
  1063.                     taker    = QUEEN;
  1064.                     takerLoc = s;
  1065.                     break;
  1066.                 }            /* If the potential attacker is a queen, then it is
  1067.                             ** a valid attacker.
  1068.                             */
  1069.  
  1070.                 if (dest == ROOK) {
  1071.                     if (dirNum > 3) {
  1072.                         taker    = ROOK;
  1073.                         takerLoc = s;
  1074.                     }
  1075.                     break;
  1076.                 }            /* If the potential attacker is a rook, and we are
  1077.                             ** examining a rank or file, then count it as an attacker.
  1078.                             ** Otherwise, the rook serves to prevent any other
  1079.                             ** attack from this direction.
  1080.                             */
  1081.  
  1082.                 if (dest == BISHOP) {
  1083.                     if (dirNum < 4) {
  1084.                         taker    = BISHOP;
  1085.                         takerLoc = s;
  1086.                     }
  1087.                     break;
  1088.                 }            /* If the potential attacker is a bishop, and we are
  1089.                             ** examining a diagonal, then count it as an attacker.
  1090.                             ** Otherwise, the bishop serves to prevent any other
  1091.                             ** attack from this direction.
  1092.                             */
  1093.  
  1094.                 if (dest == PAWN) {
  1095.                     if (destColor == BLACK) {
  1096.                         if ((dirNum < 2) && (dist == 1)) {
  1097.                             taker    = PAWN;
  1098.                             takerLoc = s;
  1099.                         }
  1100.                     }
  1101.                     else
  1102.                         if (
  1103.                             (dirNum > 1) && 
  1104.                             (dirNum < 4) && 
  1105.                             (dist == 1)
  1106.                         ) {
  1107.                             taker    = PAWN;
  1108.                             takerLoc = s;
  1109.                         }
  1110.                     break;
  1111.                 }
  1112.  
  1113.                 break;
  1114.                     /* Final case is a knight in a non-knight direction. */
  1115.             }
  1116.         }
  1117.     }
  1118.  
  1119.     return(takerLoc);
  1120. }
  1121.  
  1122.  
  1123.  
  1124. /*****************************************************************************/
  1125.  
  1126.  
  1127.  
  1128. #pragma segment Chess
  1129. void    EndTheGame(FileRecHndl game, short endReason)
  1130. {
  1131.     WindowPtr    oldPort;
  1132.  
  1133.     MakeMove(game, 0, endReason, 0);
  1134.         /* Record who resigned or draw agreement. */
  1135.  
  1136.     oldPort = SetFilePort(game);
  1137.     ImageDocument(game, true);
  1138.     AdjustGameSlider(game);
  1139.     UpdateGameStatus(game);
  1140.     SetPort(oldPort);
  1141. }
  1142.  
  1143.  
  1144.  
  1145. /*****************************************************************************/
  1146.  
  1147.  
  1148.  
  1149. #pragma segment Chess
  1150. short    WhosMove(FileRecHndl game)
  1151. {
  1152.     short    color;
  1153.  
  1154.     color  = ((*game)->doc.gameIndex ^ (*game)->doc.startColor);
  1155.     return(color & 0x01);
  1156. }
  1157.  
  1158.  
  1159.  
  1160. /*****************************************************************************/
  1161.  
  1162.  
  1163.  
  1164. #pragma segment Chess
  1165. short    GameStatus(FileRecHndl game)
  1166. {
  1167.     short            i, color, kingLoc;
  1168.     short            board[120], *boardPtr;
  1169.     short            origGameIndex, gameIndex;
  1170.     short            rep, back, pieceMoved, gameStat;
  1171.     GameListHndl    gameMoves;
  1172.  
  1173.     gPosReps = 0;
  1174.  
  1175.     if ((*game)->doc.arrangeBoard) return(kGameContinues);
  1176.  
  1177.     for (i = 0; i < 2; ++i)
  1178.         if (!(*game)->doc.timeLeft[i])
  1179.             return(kYouLoseOnTime - (i ^ (*game)->doc.myColor));
  1180.  
  1181.     GenerateLegalMoves(game);
  1182.  
  1183.     gameMoves     = (*game)->doc.gameMoves;
  1184.     origGameIndex = (*game)->doc.gameIndex;
  1185.  
  1186.     if (!(*game)->doc.numLegalMoves) {
  1187.  
  1188.         color = WhosMove(game);            /* Who's move it is. */
  1189.  
  1190.         if ((origGameIndex) && (origGameIndex == (*game)->doc.numGameMoves))
  1191.             if (!(**gameMoves)[origGameIndex - 1].moveFrom)
  1192.                 return((**gameMoves)[origGameIndex - 1].moveTo);
  1193.  
  1194.         kingLoc = (*game)->doc.king[color].kingLoc;
  1195.  
  1196.         if (SquareAttacked(game, kingLoc, color)) {        /* Checkmated. */
  1197.             if (color == (*game)->doc.myColor) return(kYouLose);
  1198.             else return(kYouWin);
  1199.         }
  1200.  
  1201.         return(kStalemate);
  1202.     }
  1203.  
  1204.     BlockMove((Ptr)&(*game)->doc.theBoard[0], (Ptr)&board[0], 120 * sizeof(short));
  1205.  
  1206.     rep = 2;            /* 2 matching current position makes 3 that match. */
  1207.     back = 100;
  1208.  
  1209.     gameIndex = (*game)->doc.gameIndex;
  1210.     while ((rep) && (back) && (gameIndex)) {
  1211.  
  1212.         if ((**gameMoves)[--gameIndex].pieceCaptured) break;
  1213.  
  1214.         pieceMoved = (*game)->doc.theBoard[(**gameMoves)[gameIndex].moveTo];
  1215.         if (pieceMoved < 0) pieceMoved = -pieceMoved;
  1216.         if (pieceMoved == PAWN) break;
  1217.  
  1218.         UnmakeMove(game);
  1219.         --back;
  1220.  
  1221.         boardPtr = &(*game)->doc.theBoard[0];
  1222.         for (i = START_IBNDS; i < END_IBNDS; ++i) if (boardPtr[i] != board[i]) break;
  1223.  
  1224.         if (i == END_IBNDS) {
  1225.             ++gPosReps;
  1226.             if (!(--rep)) break;
  1227.         }
  1228.     }
  1229.  
  1230.     while ((*game)->doc.gameIndex != origGameIndex) MakeMove(game, 1, 0, 0);
  1231.  
  1232.     gameStat = kGameContinues;
  1233.     if (!rep)  gameStat = kDrawByRep;
  1234.     if (!back) gameStat = kDrawBy50;
  1235.  
  1236.     if (gameStat) (*game)->doc.numLegalMoves = 0;
  1237.  
  1238.     return(gameStat);
  1239. }
  1240.  
  1241.  
  1242.  
  1243. /*****************************************************************************/
  1244.  
  1245.  
  1246.  
  1247. #pragma segment Chess
  1248. short    UpdateTime(FileRecHndl game, Boolean canLose)
  1249. {
  1250.     FileRecPtr    frPtr;
  1251.     short        moveColor, myColor;
  1252.     long        timeLeft, opponentTimeLeft, oldTimeLeft, diff;
  1253.  
  1254.     frPtr = *game;
  1255.     moveColor        = WhosMove(game);
  1256.     myColor          = frPtr->doc.myColor;
  1257.     timeLeft         = frPtr->doc.timeLeft[moveColor];
  1258.     opponentTimeLeft = frPtr->doc.timeLeft[moveColor ^ 1];
  1259.  
  1260.     if (!frPtr->doc.twoPlayer) myColor = moveColor;
  1261.         /* Since we are not playing over the net, both sides can lose 
  1262.         ** due to time on this machine. */
  1263.  
  1264.     if ((timeLeft > 0) && (opponentTimeLeft > 0)) {
  1265.         oldTimeLeft = timeLeft;
  1266.         diff = TickCount() - frPtr->doc.timerRefTick;
  1267.         if (diff < 0) {
  1268.             frPtr->doc.timerRefTick = TickCount();
  1269.             diff = 0;
  1270.         }
  1271.         diff /= 60;
  1272.         diff *= 60;
  1273.         if (diff >= 60) {
  1274.             if (!GameStatus(game)) {
  1275.                 timeLeft -= diff;
  1276.                 if (timeLeft < 60) timeLeft = 0;
  1277.                 if (!timeLeft)
  1278.                     if ((myColor != moveColor) || (!canLose)) timeLeft = 60;
  1279.                 frPtr->doc.timeLeft[moveColor] = timeLeft;
  1280.                 frPtr->doc.timerRefTick += diff;
  1281.                 if (!timeLeft) return(2);
  1282.                 if (timeLeft != oldTimeLeft) return(1);
  1283.             }
  1284.             else (*game)->doc.timerRefTick = TickCount();
  1285.                 /* Someone has already lost, so no time change. */
  1286.         }
  1287.     }
  1288.     else (*game)->doc.timerRefTick = TickCount();
  1289.  
  1290.     return(0);
  1291. }
  1292.  
  1293.  
  1294.  
  1295. /*****************************************************************************/
  1296.  
  1297.  
  1298.  
  1299. #pragma segment Chess
  1300. void    UpdateGameStatus(FileRecHndl game)
  1301. {
  1302.     WindowPtr        oldPort;
  1303.     ControlHandle    draw, resign;
  1304.     short            status, myColor;
  1305.     Rect            drawRect, resignRect, workRect;
  1306.     Point            endOfText;
  1307.     Boolean            hideEm, hidden;
  1308.     Str255            reasonText;
  1309.  
  1310.     if ((*game)->doc.arrangeBoard) return;
  1311.  
  1312.     draw   = (*game)->doc.draw;
  1313.     resign = (*game)->doc.resign;
  1314.  
  1315.     if (!draw) return;
  1316.  
  1317.     oldPort = SetFilePort(game);
  1318.  
  1319.     drawRect   = (*draw)->contrlRect;
  1320.     resignRect = (*resign)->contrlRect;
  1321.  
  1322.     hideEm = false;
  1323.     status = GameStatus(game);
  1324.     if (status) hideEm = true;
  1325.  
  1326.     hidden = false;
  1327.     if (drawRect.top & 0x4000) hidden = true;
  1328.  
  1329.     workRect = drawRect;
  1330.     if (hidden) OffsetRect(&workRect, 0, -0x4000);
  1331.     workRect.right = resignRect.right;
  1332.  
  1333.     if (hideEm != hidden) {
  1334.         EraseRect(&workRect);
  1335.         MoveControl(draw,   drawRect.left,   drawRect.top ^ 0x4000);
  1336.         MoveControl(resign, resignRect.left, resignRect.top ^ 0x4000);
  1337.     }
  1338.  
  1339.     if (hideEm) {
  1340.         myColor = (*game)->doc.myColor;
  1341.         switch (status) {
  1342.             case kYouWin:
  1343.             case kYouWinOnTime:
  1344.                 status = kWhiteWins + myColor;
  1345.                 break;
  1346.             case kYouLose:
  1347.             case kYouLoseOnTime:
  1348.                 status = kBlackWins - myColor;
  1349.                 break;
  1350.         }
  1351.  
  1352.         TextMode(srcCopy);
  1353.         TextFont(systemFont);
  1354.         TextSize(0);
  1355.         GetIndString(reasonText, rGameStat, status);
  1356.         MoveTo(workRect.left, workRect.top + 14);
  1357.         DrawString(reasonText);
  1358.         TextMode(srcOr);
  1359.         GetPen(&endOfText);
  1360.         workRect.left = endOfText.h;
  1361.         EraseRect(&workRect);
  1362.     }
  1363.  
  1364.     SetPort(oldPort);
  1365. }
  1366.  
  1367.  
  1368.  
  1369. /*****************************************************************************/
  1370.  
  1371.  
  1372.  
  1373. #pragma segment Chess
  1374. void    DrawButtonTitle(FileRecHndl game, short newVal)
  1375. {
  1376.     WindowPtr        oldPort;
  1377.     ControlHandle    draw;
  1378.     Rect            drawRect;
  1379.     Str255            drawButtonText;
  1380.  
  1381.     if (newVal != (*game)->doc.drawBtnState) {
  1382.  
  1383.         oldPort = SetFilePort(game);
  1384.  
  1385.         GetIndString(drawButtonText, rGameStat, kDrawButtonText + newVal);
  1386.  
  1387.         draw = (*game)->doc.draw;
  1388.         SetControlTitle(draw, drawButtonText);
  1389.         (*game)->doc.drawBtnState = newVal;
  1390.  
  1391.         drawRect = (*draw)->contrlRect;
  1392.         ValidRect(&drawRect);
  1393.  
  1394.         SetPort(oldPort);
  1395.     }
  1396. }
  1397.  
  1398.  
  1399.  
  1400. /*****************************************************************************/
  1401. /*****************************************************************************/
  1402.  
  1403.  
  1404.  
  1405. #pragma segment Chess
  1406. Boolean    ComputerMove(FileRecHndl game)
  1407. {
  1408.     OSErr            err;
  1409.     FileRecHndl        workGame;
  1410.     short            numLegalMoves, moveNum, fromSq, toSq, i;
  1411.     short            wtotal, btotal, color, update;
  1412.     WindowPtr        window;
  1413.     Boolean            compMoved;
  1414.     MoveListHndl    legalMoves;
  1415.  
  1416.     compMoved = false;
  1417.  
  1418.     IncNewFileNum(false);
  1419.     err = AppDuplicateDocument(game, &workGame);
  1420.     IncNewFileNum(true);
  1421.     if (err) return(false);
  1422.  
  1423.     for (i = 0; i < 2; ++i) (*workGame)->doc.timeLeft[i] = (*game)->doc.timeLeft[i];
  1424.  
  1425.     GetPort(&window);
  1426.     (*workGame)->fileState.window = window;
  1427.  
  1428.     GenerateLegalMoves(game);
  1429.     GenerateLegalMoves(workGame);
  1430.  
  1431.     numLegalMoves = (*workGame)->doc.numLegalMoves;
  1432.     legalMoves    = (*workGame)->doc.legalMoves;
  1433.  
  1434.     if (numLegalMoves) {        /* If there is a move, pick one, any one. */
  1435.         if (numLegalMoves == 1) moveNum = 0;
  1436.         else {
  1437.             moveNum = CheckForMate(workGame, 0, 2);
  1438.             if (moveNum == -1) {
  1439.                 CalcPositionValues(workGame);
  1440.                 wtotal = gWhiteTotal >> 16;
  1441.                 btotal = gBlackTotal >> 16;
  1442.                 color  = WhosMove(workGame);
  1443.                 for (;;) {
  1444.                     if (gNumPieces == 1) {
  1445.                         if (
  1446.                             ((color == WHITE) && (wtotal == 9)) ||
  1447.                             ((color == BLACK) && (btotal == 9))
  1448.                         ) {
  1449.                             moveNum = QueenMate(workGame);
  1450.                             break;
  1451.                         }
  1452.                         if (
  1453.                             ((color == WHITE) && (wtotal == 5)) ||
  1454.                             ((color == BLACK) && (btotal == 5))
  1455.                         ) {
  1456.                             moveNum = RookMate(workGame);
  1457.                             break;
  1458.                         }
  1459.                     }
  1460.                     moveNum = BestMove(workGame);
  1461.                     break;
  1462.                 }
  1463.             }
  1464.         }
  1465.  
  1466.         if (moveNum > -1) {
  1467.             update = UpdateTime(game, true);
  1468.             if (update) DrawTime(game);
  1469.             if (update == 2) {
  1470.                 if ((*game)->doc.twoPlayer) SendGame(game, kIsMove, nil);
  1471.                 AlertIfGameOver(game);
  1472.             }
  1473.             else {
  1474.                 fromSq = (**legalMoves)[moveNum].moveFrom;
  1475.                 toSq   = (**legalMoves)[moveNum].moveTo;
  1476.                 SlideThePiece(game, fromSq, toSq);
  1477.                 MakeMove(game, fromSq, toSq, QUEEN);
  1478.                 compMoved = true;
  1479.             }
  1480.         }
  1481.         if (moveNum == kComputerResigns) {
  1482.             EndTheGame(game, kWhiteResigns + WhosMove(game));
  1483.             if ((*game)->doc.twoPlayer) SendGame(game, kIsMove, nil);
  1484.             gComputerResigns = true;
  1485.             AlertIfGameOver(game);
  1486.         }
  1487.     }
  1488.  
  1489.     AppDisposeDocument(workGame);
  1490.     return(compMoved);
  1491. }
  1492.  
  1493.  
  1494.  
  1495. /*****************************************************************************/
  1496.  
  1497.  
  1498.  
  1499. #pragma segment Chess
  1500. short    CheckForMate(FileRecHndl game, short nodeDepth, short maxDepth)
  1501. {
  1502.     short            num, ourMove, move;
  1503.     short            color, kingLoc, result;
  1504.     MoveListHndl    node;
  1505.     EventRecord        event;
  1506.  
  1507.     ourMove = -1;
  1508.     GenerateLegalMoves(game);
  1509.  
  1510.     num  = (*game)->doc.numLegalMoves;
  1511.     node = (*game)->doc.legalMoves;
  1512.  
  1513.     (*game)->doc.legalMoves = gNodeHndl[nodeDepth];
  1514.         /* Protect the list of legal moves for this level.  Put a handle
  1515.         ** into the game where moves for the next level can be placed. */
  1516.  
  1517.     color = WhosMove(game) ^ 1;
  1518.     if (!(nodeDepth & 0x01)) {
  1519.         for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1520.             MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN);
  1521.             kingLoc = (*game)->doc.king[color].kingLoc;
  1522.             if (SquareAttacked(game, kingLoc, color)) {        /* The move caused check. */
  1523.                 GenerateLegalMoves(game);
  1524.                 if (!(*game)->doc.numLegalMoves) {            /* If no way out of check... */
  1525.                     ourMove = move;                            /* ...it is checkmate. */
  1526.                 }
  1527.             }
  1528.             UnmakeMove(game);
  1529.         }
  1530.         if (idleTick + 10 < TickCount()) {
  1531.             idleTick = TickCount();
  1532.             if (EventAvail(everyEvent - highLevelEventMask, &event)) ourMove = -2;
  1533.             else {
  1534.                 CTEIdle();
  1535.                 DoIdleTasks(false);
  1536.             }
  1537.         }
  1538.     }
  1539.  
  1540.     if (nodeDepth < maxDepth) {
  1541.         if (ourMove == -1) {
  1542.             for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1543.                 MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN);
  1544.                 result = CheckForMate(game, nodeDepth + 1, maxDepth);
  1545.                 if (result == -1) {
  1546.                     ourMove = move;
  1547.                 }
  1548.                     /* If opponent can't find saving move, then this is the move we want. */
  1549.                 if (result == -2) ourMove = -2;
  1550.                     /* User interrupted search. */
  1551.                 UnmakeMove(game);
  1552.             }
  1553.         }
  1554.     }
  1555.  
  1556.     if (!nodeDepth) {
  1557.         if (ourMove > -1) {
  1558.             MakeMove(game, (**node)[ourMove].moveFrom, (**node)[ourMove].moveTo, QUEEN);
  1559.             kingLoc = (*game)->doc.king[color].kingLoc;
  1560.             if (!SquareAttacked(game, kingLoc, color)) {    /* It is not a mate in 1. */
  1561.                 GenerateLegalMoves(game);
  1562.                 if (!(*game)->doc.numLegalMoves) ourMove = -1;
  1563.                     /* It is a stalemate.  Throw it back. */
  1564.             }
  1565.             UnmakeMove(game);
  1566.         }
  1567.     }
  1568.  
  1569.     (*game)->doc.numLegalMoves = num;
  1570.     (*game)->doc.legalMoves    = node;
  1571.  
  1572.     return(ourMove);
  1573. }
  1574.  
  1575.  
  1576.  
  1577. /*****************************************************************************/
  1578.  
  1579.  
  1580.  
  1581. #pragma segment Chess
  1582. short    BestMove(FileRecHndl game)
  1583. {
  1584.     short            bestMove, keepBestMove, num;
  1585.     short            i, color, move, from, to;
  1586.     Boolean            playOn;
  1587.     long            value, max;
  1588.     MoveListHndl    node;
  1589.     EventRecord        event;
  1590.  
  1591.     bestMove = -1;
  1592.  
  1593.     num  = (*game)->doc.numLegalMoves;
  1594.     node = (*game)->doc.legalMoves;
  1595.  
  1596.     (*game)->doc.legalMoves = gNodeHndl[kLastNode];
  1597.         /* Protect the list of legal moves for this node.  Put a different handle
  1598.         ** into the game where moves for the next level can be placed. */
  1599.  
  1600.     color  = WhosMove(game);
  1601.     playOn = false;
  1602.     if ((i = (*game)->doc.timeLeft[1 - color]) >= 0)
  1603.         if (i < 7200)
  1604.             playOn = true;
  1605.  
  1606.     for (max = 0x80000000L, move = 0; move < num; ++move) {
  1607.         from = (**node)[move].moveFrom;
  1608.         to   = (**node)[move].moveTo;
  1609.         (**node)[move].value = value = OneDeepEval(game, from, to, color);
  1610.         if (max < value) {
  1611.             max = value;
  1612.             bestMove = move;        /* Best move so far. */
  1613.         }
  1614.         if (idleTick + 10 < TickCount()) {
  1615.             idleTick = TickCount();
  1616.             if (EventAvail(everyEvent - highLevelEventMask, &event)) {
  1617.                 bestMove = -2;
  1618.                 break;
  1619.             }
  1620.             CTEIdle();
  1621.             DoIdleTasks(false);
  1622.         }
  1623.     }
  1624.  
  1625.     if (bestMove > -1) {        /* Make sure we aren't getting mated in two. */
  1626.         for (keepBestMove = bestMove;;) {
  1627.             from = (**node)[bestMove].moveFrom;
  1628.             to   = (**node)[bestMove].moveTo;
  1629.             MakeMove(game, from, to, QUEEN);
  1630.             i = CheckForMate(game, 0, 2);    /* Check for mate in 2. */
  1631.             UnmakeMove(game);
  1632.             if (i == -2) {
  1633.                 bestMove = -2;
  1634.                 break;        /* User wants to do something, so interrupt. */
  1635.             }
  1636.             if (i == -1) {
  1637.                 max = (**node)[bestMove].value;
  1638.                 if (!playOn)
  1639.                     if (max < -0x00070000)
  1640.                         if (!(Random() & 0x03))
  1641.                             bestMove = kComputerResigns;    /* Resign sometimes. */
  1642.                 break;        /* Opponent has no mate in 2 against bestMove. */
  1643.             }
  1644.             (**node)[bestMove].value = max = 0x80000000L;    /* Getting mated is bad-bad. */
  1645.             for (i = 0; i < num; ++i) {        /* Try the next best move. */
  1646.                 if (max < (**node)[i].value) {
  1647.                     max = (**node)[i].value;
  1648.                     bestMove = i;
  1649.                 }
  1650.             }
  1651.             if (max == 0x80000000L) {
  1652.                 bestMove = keepBestMove;    /* We are going to get mated.  (Bummer.) */
  1653.                 if (!playOn)
  1654.                     if (!(Random() & 0x01))
  1655.                         bestMove = kComputerResigns;    /* Resign sometimes. */
  1656.                 break;
  1657.             }
  1658.         }
  1659.     }
  1660.  
  1661.     (*game)->doc.numLegalMoves = num;
  1662.     (*game)->doc.legalMoves    = node;
  1663.  
  1664.     return(bestMove);
  1665. }
  1666.  
  1667.  
  1668.  
  1669. /*****************************************************************************/
  1670.  
  1671.  
  1672.  
  1673. #pragma segment Chess
  1674. long    OneDeepEval(FileRecHndl game, short from, short to, short color)
  1675. {
  1676.     short            material, matBalance, xcngVal, posVal;
  1677.     short            take, takerLoc, retakerLoc;
  1678.     short            val, saveBoard;
  1679.     short            *boardPtr, keepBoard[120], square, j, k, xcolor;
  1680.     short            movedPiece, piece, pieceColor, r, c, cc, delta, xcng[64], xnum, loop;
  1681.     short            dirNum, dir, s, dist, numChecks;
  1682.     long            value, v1, v2, v3;
  1683.  
  1684.     MakeMove(game, from, to, QUEEN);
  1685.  
  1686.     boardPtr   = &(*game)->doc.theBoard[0];
  1687.     matBalance = xcngVal = posVal = 0;
  1688.  
  1689.     for (material = 0, square = START_IBNDS; square < END_IBNDS; ++square) {
  1690.         piece = boardPtr[square];
  1691.         if (piece) {
  1692.             if (piece != OBNDS) {
  1693.                 if (piece < 0) piece = -piece;
  1694.                 if (piece > BK) piece -= KSIDEPIECE;
  1695.                 if (piece == KING)   piece = 0;
  1696.                 if (piece == QUEEN)  piece = 9;
  1697.                 if (piece == ROOK)   piece = 5;
  1698.                 if (piece == KNIGHT) piece = 3;
  1699.                 material += piece;
  1700.             }
  1701.         }
  1702.     }
  1703.  
  1704.     movedPiece = boardPtr[to];
  1705.     if (movedPiece < 0) movedPiece = -movedPiece;
  1706.     if (movedPiece > BK) movedPiece -= KSIDEPIECE;
  1707.  
  1708.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  1709.  
  1710.         piece = boardPtr[square];
  1711.         if (piece) {        /* Evaluate value of this piece being here. */
  1712.  
  1713.             if (piece != OBNDS) {
  1714.  
  1715.                 pieceColor = BLACK;    /* Figure who's piece it is. */
  1716.                 if (piece < 0) {
  1717.                     pieceColor = WHITE;
  1718.                     piece = -piece;
  1719.                 }
  1720.                 if (piece > BK) piece -= KSIDEPIECE;
  1721.  
  1722.                 j = piece;
  1723.                 if (j == QUEEN)  j = 9;
  1724.                 if (j == ROOK)   j = 5;
  1725.                 if (j == KNIGHT) j = 3;
  1726.                 if (color == pieceColor) matBalance += j;
  1727.                 else                     matBalance -= j;
  1728.  
  1729.                 r = square / 10;    /* Get row and column of piece. */
  1730.                 c = square - 10 * r - 1;
  1731.                 r -= 2;
  1732.  
  1733.                 if (pieceColor == WHITE) r = 7 - r;        /* Flip rank for white. */
  1734.  
  1735.                 if (piece == PAWN) {            /* Weight the pawn position. */
  1736.                     if (r > 4) {
  1737.                         if (r == 6) r = 63;        /* Highly advanced pawns are nice. */
  1738.                         if (r == 5) r = 30;
  1739.                     }
  1740.                     else {
  1741.                         if (material > 40) {
  1742.                             cc = c;
  1743.                             if (cc > 3) cc = 7 - cc;
  1744.                             if (r < 2) cc = 0;
  1745.                             switch (cc) {
  1746.                                 case 0:
  1747.                                     r = 0;
  1748.                                     break;
  1749.                                 case 1:
  1750.                                     if (r > 2) r = -8;
  1751.                                     else       r = 0;
  1752.                                     break;
  1753.                                 case 2:
  1754.                                     if (r == 2) r = -2;
  1755.                                     if (r == 3) {
  1756.                                         r = 6;
  1757.                                         if (c < 5) r = 11;
  1758.                                     }
  1759.                                     break;
  1760.                                 case 3:
  1761.                                     if (r == 2) r = 6;
  1762.                                     if (r == 3) {
  1763.                                         r = 6;
  1764.                                         if (c < 5) r = 11;
  1765.                                     }
  1766.                                     break;
  1767.                             }
  1768.                         }
  1769.                         if (material < 26) r *= 4;
  1770.                     }
  1771.                     for (j = 9; j <= 11; j += 2) {    /* Give weight to pawn chains. */
  1772.                         k = boardPtr[square + j];
  1773.                         if (pieceColor == WHITE) k = -k;
  1774.                         if (k == PAWN) {
  1775.                             k = boardPtr[square - j];
  1776.                             if (pieceColor == WHITE) k = -k;
  1777.                             if (k == PAWN) ++r;
  1778.                         }
  1779.                     }
  1780.                     for (j = square;;) {    /* Give negative weight for doubled pawns. */
  1781.                         j += 10;
  1782.                         if (pieceColor == WHITE) j -= 20;
  1783.                         k = boardPtr[j];
  1784.                         if (k == OBNDS) break;
  1785.                         if (pieceColor == WHITE) k = -k;
  1786.                         if (k == PAWN) r -= 4;
  1787.                     }
  1788.                     if ((c == 3) || (c == 4)) {    /* Give negative weight for backward center pawns. */
  1789.                         for (j = square - 1;;) {
  1790.                             j += 10;
  1791.                             if (pieceColor == WHITE) j -= 20;
  1792.                             k = boardPtr[j];
  1793.                             if (k == OBNDS) break;
  1794.                             if (pieceColor == WHITE) k = -k;
  1795.                             if (k == PAWN) {
  1796.                                 r -= 2;
  1797.                                 break;
  1798.                             }
  1799.                         }
  1800.                         for (j = square + 1;;) {
  1801.                             j += 10;
  1802.                             if (pieceColor == WHITE) j -= 20;
  1803.                             k = boardPtr[j];
  1804.                             if (k == OBNDS) break;
  1805.                             if (pieceColor == WHITE) k = -k;
  1806.                             if (k == PAWN) {
  1807.                                 r -= 2;
  1808.                                 break;
  1809.                             }
  1810.                         }
  1811.                     }
  1812.                     for (j = square;;) {    /* Give negative weight for doubled pawns. */
  1813.                         j += 10;
  1814.                         if (pieceColor == WHITE) j -= 20;
  1815.                         k = boardPtr[j];
  1816.                         if (k == OBNDS) break;
  1817.                         if (pieceColor == WHITE) k = -k;
  1818.                         if (k == PAWN) r -= 4;
  1819.                     }
  1820.  
  1821.                     if ((*game)->doc.gameIndex == 2)
  1822.                         if (square == 54)
  1823.                             if ((boardPtr[63] == WP) || (boardPtr[65] == WP))
  1824.                                 r = -200;
  1825.                                     /* Special-case out center-counter. */
  1826.  
  1827.                     if ((*game)->doc.gameIndex == 4)
  1828.                         if (square == 45)
  1829.                             if (boardPtr[45] == BP)
  1830.                                 if (boardPtr[54] == BP)
  1831.                                     if (boardPtr[63] == WP)
  1832.                                         if (boardPtr[64] == WP)
  1833.                                             r += (0x00010000 >> 3);
  1834.                                                 /* Give weight to declining queen's gambit. */
  1835.  
  1836.                     if ((*game)->doc.gameIndex == 4)
  1837.                         if (square == 53)
  1838.                             if (boardPtr[53] == BP)
  1839.                                 if (boardPtr[43] == BN)
  1840.                                     if (boardPtr[65] == WP)
  1841.                                         if (boardPtr[76] == WKN)
  1842.                                             r += (0x00010000 >> 3);
  1843.                                                 /* Play a better e4,c5,Nf3. */
  1844.  
  1845.                     if ((*game)->doc.gameIndex == 5)
  1846.                         if (square == 54)
  1847.                             if (boardPtr[54] == WP)
  1848.                                 if (boardPtr[53] == BP)
  1849.                                     if (boardPtr[63] == WP)
  1850.                                         if (boardPtr[46] == BKN)
  1851.                                             r += (0x00010000 >> 2);
  1852.                                                 /* Play a better d4,Nf6,c4,c5. */
  1853.  
  1854.                     if ((*game)->doc.gameIndex == 6)
  1855.                         if (square == 64)
  1856.                             if (boardPtr[64] == BP)
  1857.                                 if (boardPtr[43] == BN)
  1858.                                     if (boardPtr[65] == WP)
  1859.                                         if (boardPtr[76] == WKN)
  1860.                                             r += (0x00010000 >> 2);
  1861.                                                 /* Play a better e4,c5,Nf3,Nc6,d4. */
  1862.  
  1863.                     if (color != pieceColor) r = -r;
  1864.                     posVal += r;
  1865.  
  1866.                 }
  1867.  
  1868.                 if (piece == KNIGHT) {        /* Give weight to centralized knights. */
  1869.                     if ((!r) || (r == 7)) {
  1870.                         r = -4;
  1871.                         if ((*game)->doc.gameIndex < 3) {
  1872.                             r = 200;
  1873.                             /* Special-case out knights early, 'cause it's really gross. */
  1874.                         }
  1875.                     }
  1876.                     else {
  1877.                         if (r == 4) r = 5;
  1878.                         if (r > 3) r = 7 - r;
  1879.                         if ((c < 2) || (c > 5))  r /= 2;
  1880.                         if ((c < 1) || (c > 6))  r /= 2;
  1881.                     }
  1882.                     if (color != pieceColor) r = -r;
  1883.                     posVal += r;
  1884.                 }
  1885.  
  1886.                 if (piece == BISHOP) {
  1887.                     j = r;
  1888.                     if (r > 5) r = 7 - r;
  1889.                         if (!r) r = -2;        /* Get those bishops developed. */
  1890.                     if ((c < 1) || (c > 6))  r /= 4;
  1891.                     if (j > 1) {        /* Give weight to knights before bishops past 2nd rank. */
  1892.                         c = (c < 4) ? 2 : 7;
  1893.                         k = boardPtr[90 + c - 70 * pieceColor];
  1894.                         if (pieceColor == WHITE) k = -k;
  1895.                         if (k < WK) k += KSIDEPIECE;
  1896.                         if (k > BK) k -= KSIDEPIECE;
  1897.                         if (k == KNIGHT) r = -1;
  1898.                     }
  1899.                     if (color != pieceColor) r = -r;
  1900.                     posVal += r;
  1901.                 }
  1902.  
  1903.                 if (piece == ROOK) {    /* Give weight to rooks on open files. */
  1904.                     if (material > 25) {
  1905.                         delta = (color == BLACK) ? 10 : -10;
  1906.                         if ((c) && (c < 7)) {
  1907.                             for (j = square + delta; !boardPtr[j]; j += delta, ++r) {};
  1908.                             if (color != pieceColor) r = -r;
  1909.                             posVal += r;
  1910.                         }
  1911.                     }
  1912.                 }
  1913.  
  1914.                 if (piece == QUEEN) {
  1915.                     if (color == pieceColor) {
  1916.                         if (movedPiece == QUEEN) {
  1917.                             if (material > 50) {
  1918.                                 r = -2;
  1919.                                 if (color != pieceColor) r = -r;
  1920.                                 posVal += r;
  1921.                             }        /* Keep the queen from moving too much in the beginning. */
  1922.                         }
  1923.                     }
  1924.                 }
  1925.                 if (piece == KING) {    /* Give weight to no king moves other than castling. */
  1926.                     j = 0;
  1927.                     if ((*game)->doc.king[color].kingMoves < 2) {
  1928.                         j = -10;
  1929.                         if (!r) {
  1930.                             if (c == 6) j = 6;
  1931.                             if (c == 2) j = 4;
  1932.                         }
  1933.                     }
  1934.                     if (color != pieceColor) j = -j;
  1935.                     posVal += j;
  1936.                 }
  1937.  
  1938.                 if (color == pieceColor) {
  1939.  
  1940.                     for (dirNum = 0; (dir = direction[piece][dirNum]) != 0; ++dirNum) {
  1941.                         for (s = square, dist = 1; dist <= distance[piece]; ++dist) {
  1942.                             if (piece == PAWN) {
  1943.                                 if (dirNum > 1) break;
  1944.                                 dir = direction[BISHOP][dirNum];
  1945.                                 if (color == BLACK) dir = -dir;
  1946.                             }
  1947.                             s += dir;
  1948.                             if ((take = (*game)->doc.theBoard[s]) == EMPTY) continue;
  1949.                             if (take == OBNDS) break;
  1950.                             xcolor = BLACK;
  1951.                             if (take < 0) {
  1952.                                 xcolor = WHITE;
  1953.                                 take = -take;
  1954.                             }
  1955.                             if (take > BK) take -= KSIDEPIECE;
  1956.                             if (xcolor == color) break;
  1957.                             switch (j = take) {
  1958.                                 case KNIGHT:
  1959.                                     j = 3;
  1960.                                     break;
  1961.                                 case ROOK:
  1962.                                     j = 5;
  1963.                                     break;
  1964.                                 case QUEEN:
  1965.                                     j = 9;
  1966.                                     break;
  1967.                                 case KING:
  1968.                                     j = material;
  1969.                                     if (j > 39) j = 60 - j;
  1970.                                     j /= 2;
  1971.                                     break;
  1972.                             }
  1973.                             switch (k = piece) {
  1974.                                 case KNIGHT:
  1975.                                     k = 3;
  1976.                                     break;
  1977.                                 case ROOK:
  1978.                                     k = 5;
  1979.                                     break;
  1980.                                 case QUEEN:
  1981.                                     k = 9;
  1982.                                     break;
  1983.                                 case KING:
  1984.                                     k = material;
  1985.                                     if (k > 39) k = 60 - k;
  1986.                                     k /= 2;
  1987.                                     break;
  1988.                             }
  1989.                             if (j > k) posVal += (j - k) * 5;
  1990.                             else if (!SquareAttacked(game, s, color)) posVal += (j + 4);
  1991.                             break;
  1992.                         }
  1993.                     }        /* The above code calculates the aggression factor of the board.
  1994.                             ** If the attacking piece is smaller than the piece attacked, then
  1995.                             ** we add the delta value as a plus.  This factor finds forks,
  1996.                             ** chases kings, queens, etc. */
  1997.  
  1998.                             /* The below code estimates the exchange value of takes.  This
  1999.                             ** is only an estimate, as it is not an actual move analysis
  2000.                             ** of all the possible exchanges.  We are only trying to get
  2001.                             ** an estimate of the board position in this function. */
  2002.  
  2003.                     takerLoc = SquareAttacked(game, square, color);
  2004.                     if (takerLoc) {
  2005.                         saveBoard = false;
  2006.                         xnum = val = 0;
  2007.                         for (xcolor = color ^ 1; takerLoc; xcolor ^= 1) {
  2008.                             take = boardPtr[square];
  2009.                             if (take < 0) take = -take;
  2010.                             if (take > BK) take -= KSIDEPIECE;
  2011.                             if (take == KING)   take = 512;
  2012.                             if (take == QUEEN)  take = 9;
  2013.                             if (take == ROOK)   take = 5;
  2014.                             if (take == KNIGHT) take = 3;
  2015.                             retakerLoc = SquareAttacked(game, square, xcolor);
  2016.                             if (xcolor == color) val += take;
  2017.                             else                 val -= take;
  2018.                             xcng[xnum++] = val;
  2019.                             if (retakerLoc) {
  2020.                                 if (!saveBoard) {
  2021.                                     saveBoard = true;
  2022.                                     for (j = START_IBNDS; j < END_IBNDS; ++j)
  2023.                                         keepBoard[j] = boardPtr[j];
  2024.                                 }
  2025.                                 boardPtr[square] = boardPtr[takerLoc];
  2026.                                 boardPtr[takerLoc] = 0;
  2027.                             }
  2028.                             takerLoc = retakerLoc;
  2029.                         }
  2030.                         if (saveBoard)
  2031.                             for (j = START_IBNDS; j < END_IBNDS; ++j)
  2032.                                 boardPtr[j] = keepBoard[j];
  2033.                         xcng[xnum] = xcng[xnum - 1];
  2034.                         xcolor = 1;        /* Check on opponent first. */
  2035.                         for (loop = true; loop; xcolor ^= 1) {
  2036.                             val  = xcng[xcolor];
  2037.                             k    = xnum;
  2038.                             loop = false;
  2039.                             for (j = xcolor + 2; j <= k; j += 2) {
  2040.                                 if (xcolor) {
  2041.                                     if (val > xcng[j]) {
  2042.                                         val = xcng[j];
  2043.                                         xnum = j - 1;
  2044.                                         loop = true;
  2045.                                     }
  2046.                                 }
  2047.                                 else {
  2048.                                     if (val < xcng[j]) {
  2049.                                         val = xcng[j];
  2050.                                         xnum = j - 1;
  2051.                                         loop = true;
  2052.                                     }
  2053.                                 }
  2054.                             }
  2055.                         }
  2056.                         if (xcngVal > val) xcngVal = val;
  2057.                     }
  2058.                 }
  2059.             }
  2060.         }
  2061.     }
  2062.  
  2063.     val = GameStatus(game);
  2064.     j = (100 - (*game)->doc.numLegalMoves) / 3;
  2065.     if (material > 60) j = 0;
  2066.  
  2067.     xcolor = color ^ 1;
  2068.     for (numChecks = 0;; ++numChecks) {
  2069.         if (!(SquareAttacked(game, (*game)->doc.king[xcolor].kingLoc, xcolor))) break;
  2070.         if ((*game)->doc.gameIndex < 2) break;
  2071.         UnmakeMove(game);
  2072.         UnmakeMove(game);
  2073.     }
  2074.     if (numChecks > 3) {
  2075.         j = 0;
  2076.     }
  2077.     for (; numChecks; --numChecks) {
  2078.         MakeMove(game, 1, 0, 0);
  2079.         MakeMove(game, 1, 0, 0);
  2080.     }
  2081.  
  2082.     posVal += j;
  2083.  
  2084.     if ((val >= kStalemate) && (val <= kDrawByRep)) matBalance = xcngVal = posVal = 0;
  2085.         /* Drawing may be our best move, so don't just eliminate this move. */
  2086.  
  2087.     if (gPosReps) matBalance = xcngVal = posVal = 0;
  2088.         /* Drawing may be our best move, so don't just eliminate this move. */
  2089.  
  2090.     UnmakeMove(game);
  2091.     GenerateLegalMoves(game);
  2092.  
  2093.     v1   = matBalance;
  2094.     v1  += xcngVal;
  2095.     v1 <<= 16;
  2096.     v2   = posVal;
  2097.     v2 <<= 3;
  2098.     v3   = Random() & 0x7F;
  2099.  
  2100.     value = v1 + v2;
  2101.     if (material > 29) value += v3;
  2102.  
  2103.     return(value);        
  2104. }
  2105.  
  2106.  
  2107.  
  2108. /*****************************************************************************/
  2109.  
  2110.  
  2111.  
  2112. #pragma segment Chess
  2113. void    SlideThePiece(FileRecHndl game, short fromSq, short toSq)
  2114. {
  2115.     short    fromRow, fromCol, toRow, toCol;
  2116.     Point    fromLoc, toLoc;
  2117.     Rect    fromRect;
  2118.  
  2119.     fromRow = (fromSq - START_IBNDS) / 10;
  2120.     fromCol = fromSq - START_IBNDS - 10 * fromRow;
  2121.     if ((*game)->doc.invertBoard) {
  2122.         fromRow = 7 - fromRow;
  2123.         fromCol = 7 - fromCol;
  2124.     }
  2125.     fromRect.top    = 1 + fromRow * kBoardSqSize;
  2126.     fromRect.left   = 1 + fromCol * kBoardSqSize;
  2127.     fromRect.bottom = fromRect.top  + 32;
  2128.     fromRect.right  = fromRect.left + 32;
  2129.  
  2130.     toRow = (toSq - START_IBNDS) / 10;
  2131.     toCol = toSq - START_IBNDS - 10 * toRow;
  2132.     if ((*game)->doc.invertBoard) {
  2133.         toRow = 7 - toRow;
  2134.         toCol = 7 - toCol;
  2135.     }
  2136.  
  2137.     fromLoc.v = fromRow * kBoardSqSize + kBoardVOffset + (kBoardSqSize / 2);
  2138.     fromLoc.h = fromCol * kBoardSqSize + kBoardHOffset + (kBoardSqSize / 2);
  2139.     toLoc.v   = toRow * kBoardSqSize + kBoardVOffset + (kBoardSqSize / 2);
  2140.     toLoc.h   = toCol * kBoardSqSize + kBoardHOffset + (kBoardSqSize / 2);
  2141.  
  2142.     MoveThePiece(game, fromSq, fromRect, fromLoc, &toLoc);
  2143. }
  2144.  
  2145.  
  2146.  
  2147. /*****************************************************************************/
  2148.  
  2149.  
  2150.  
  2151. #pragma segment Chess
  2152. void    CalcPositionValues(FileRecHndl game)
  2153. {
  2154.     short    *boardPtr, square, piece;
  2155.     long    val;
  2156.  
  2157.     boardPtr = &(*game)->doc.theBoard[0];
  2158.     gTreeValue = gWhiteTotal = gBlackTotal = gNumPieces = 0;
  2159.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  2160.         piece = boardPtr[square];
  2161.         if (piece) {
  2162.             if (piece != OBNDS) {
  2163.                 if (piece < WK) piece += KSIDEPIECE;
  2164.                 if (piece > BK) piece -= KSIDEPIECE;
  2165.                 val = gTreePieceValues[piece + KING];
  2166.                 gTreeValue += val;
  2167.                 if (piece < 0) piece = -piece;
  2168.                 if (piece != KING) {
  2169.                     gPieceLoc = square;
  2170.                     ++gNumPieces;
  2171.                     if (val < 0) gWhiteTotal -= val;
  2172.                     else         gBlackTotal += val;
  2173.                 }
  2174.             }
  2175.         }
  2176.     }
  2177. }
  2178.  
  2179.  
  2180.  
  2181.