home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / Apps / Games / NeXTGo / Source / Board.m < prev    next >
Encoding:
Text File  |  1993-07-10  |  28.0 KB  |  1,136 lines

  1. /*
  2.                 GNU GO - the game of Go (Wei-Chi)
  3.                 Version 1.1   last revised 3-1-89
  4.            Copyright (C) Free Software Foundation, Inc.
  5.                       written by Man L. Li
  6.                       modified by Wayne Iba
  7.                     documented by Bob Webber
  8.                     NeXT version by John Neil
  9. */
  10. /*
  11. This program is free software; you can redistribute it and/or modify
  12. it under the terms of the GNU General Public License as published by
  13. the Free Software Foundation - version 1.
  14.  
  15. This program is distributed in the hope that it will be useful,
  16. but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. GNU General Public License in file COPYING for more details.
  19.  
  20. You should have received a copy of the GNU General Public License
  21. along with this program; if not, write to the Free Software
  22. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  
  24. Please report any bug/fix, modification, suggestion to
  25.  
  26. mail address:   Man L. Li
  27.                 Dept. of Computer Science
  28.                 University of Houston
  29.                 4800 Calhoun Road
  30.                 Houston, TX 77004
  31.  
  32. e-mail address: manli@cs.uh.edu         (Internet)
  33.                 coscgbn@uhvax1.bitnet   (BITNET)
  34.                 70070,404               (CompuServe)
  35.  
  36. For the NeXT version, please report any bug/fix, modification, suggestion to
  37.  
  38. mail address:   John Neil
  39.                 Mathematics Department
  40.                 Portland State University
  41.                 PO Box 751
  42.                 Portland, OR  97207
  43.  
  44. e-mail address: neil@math.mth.pdx.edu  (Internet)
  45.                 neil@psuorvm.bitnet    (BITNET)
  46. */
  47.  
  48. #import "Board.h"
  49. #import "gnugo.h"
  50.  
  51. #import <libc.h>
  52. #import <math.h>
  53. #import <dpsclient/wraps.h>    // PSxxx functions
  54. #import <appkit/color.h>    // For background colors of images
  55. #import <defaults/defaults.h>    // For writing/reading high score
  56. #import <appkit/Application.h>    // For NXApp
  57. #import <appkit/Button.h>
  58. #import <appkit/Control.h>
  59. #import <appkit/NXImage.h>
  60. #import <appkit/OpenPanel.h>
  61. #import <appkit/Panel.h>
  62. #import <appkit/Window.h>
  63. #import <appkit/Menu.h>
  64. #import <appkit/Matrix.h>
  65.  
  66. #define EMPTY        0
  67. #define WHITESTONE    1
  68. #define BLACKSTONE    2
  69. #define KOMI            5.5
  70.  
  71. // The following values are the default sizes for the various pieces. 
  72.   
  73. #define RADIUS        14.5             // Stone radius
  74. #define STONEWIDTH    29.0            // Stone width
  75. #define STONEHEIGHT    29.0            // Stone height
  76.   
  77.   // SHADOWOFFSET defines the amount the shadow is offset from the piece. 
  78.   
  79. #define SHADOWOFFSET 2.0
  80.   
  81. #define BASEBOARDX 19.0
  82. #define BASEBOARDY 19.0
  83. #define WINDOWOFFSETX 12.0
  84. #define WINDOWOFFSETY 100.0
  85.   
  86. #define gameSize  bounds.size
  87.  
  88. #define PSLine(a, b, x, y)    PSmoveto(a, b); PSlineto(x, y)
  89.  
  90. float stoneX, stoneY;
  91.  
  92. void setStoneLoc(int x, int y)
  93. {
  94.   stoneX = ((19.0 - MAXX)/2.0)*STONEWIDTH + BASEBOARDX - RADIUS + (x*STONEWIDTH);
  95.   stoneY = BASEBOARDY - RADIUS + ((18 - y)*STONEHEIGHT) - ((19.0 - MAXY)/2.0)*STONEHEIGHT;
  96. }  
  97.   
  98.   
  99. @implementation GoView
  100.   
  101.   - initFrame:(const NXRect *)frm
  102. {
  103.   NXSize stoneSize;
  104.   
  105.   stoneSize.width = STONEWIDTH;
  106.   stoneSize.height = STONEHEIGHT;
  107.   
  108.   [super initFrame:frm];
  109.   
  110.   [self allocateGState];    // For faster lock/unlockFocus
  111.     
  112.   [(blackStone = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  113.   [blackStone useDrawMethod:@selector(drawBlackStone:) inObject:self];
  114.   [blackStone setSize:&stoneSize];
  115.   
  116.   [(whiteStone = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  117.   [whiteStone useDrawMethod:@selector(drawWhiteStone:) inObject:self];
  118.   [whiteStone setSize:&stoneSize];
  119.   
  120.   [(grayStone = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  121.   [grayStone useDrawMethod:@selector(drawGrayStone:) inObject:self];
  122.   [grayStone setSize:&stoneSize];
  123.   
  124.   [(upperLeft = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  125.   [upperLeft useDrawMethod:@selector(drawUpperLeft:) inObject:self];
  126.   [upperLeft setSize:&stoneSize];
  127.   
  128.   [(upperRight = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  129.   [upperRight useDrawMethod:@selector(drawUpperRight:) inObject:self];
  130.   [upperRight setSize:&stoneSize];
  131.   
  132.   [(lowerLeft = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  133.   [lowerLeft useDrawMethod:@selector(drawLowerLeft:) inObject:self];
  134.   [lowerLeft setSize:&stoneSize];
  135.   
  136.   [(lowerRight = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  137.   [lowerRight useDrawMethod:@selector(drawLowerRight:) inObject:self];
  138.   [lowerRight setSize:&stoneSize];
  139.   
  140.   [(midLeft = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  141.   [midLeft useDrawMethod:@selector(drawMidLeft:) inObject:self];
  142.   [midLeft setSize:&stoneSize];
  143.   
  144.   [(midRight = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  145.   [midRight useDrawMethod:@selector(drawMidRight:) inObject:self];
  146.   [midRight setSize:&stoneSize];
  147.   
  148.   [(midTop = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  149.   [midTop useDrawMethod:@selector(drawMidTop:) inObject:self];
  150.   [midTop setSize:&stoneSize];
  151.   
  152.   [(midBottom = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  153.   [midBottom useDrawMethod:@selector(drawMidBottom:) inObject:self];
  154.   [midBottom setSize:&stoneSize];
  155.   
  156.   [(innerSquare = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  157.   [innerSquare useDrawMethod:@selector(drawInnerSquare:) inObject:self];
  158.   [innerSquare setSize:&stoneSize];
  159.   
  160.   [(innerHandicap = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  161.   [innerHandicap useDrawMethod:@selector(drawInnerHandicap:) inObject:self];
  162.   [innerHandicap setSize:&stoneSize];
  163.   
  164.   [self setBackgroundFile:NXGetDefaultValue([NXApp appName], "BackGround") 
  165.  andRemember:NO];
  166.   
  167.   [self startNewGame];
  168.   
  169.   return self;
  170. }
  171.  
  172. // free simply gets rid of everything we created for MainGoView, including
  173.   // the instance of MainGoView itself. This is how nice objects clean up.
  174.   
  175.   - free
  176. {
  177.   [backGround free];
  178.   return [super free];
  179. }
  180.  
  181.  
  182.  
  183. // This methods allows changing the file used to paint the background of the
  184.   // playing field. Set fileName to NULL to revert to the default. Set
  185.   // remember to YES if you wish the write the value out in the defaults.
  186.   
  187.   - setBackgroundFile:(const char *)fileName andRemember:(BOOL)remember
  188. {
  189.   [backGround free];
  190.   backGround = [[NXImage allocFromZone:[self zone]] initSize:&gameSize];
  191.   if (fileName) {
  192.     [backGround useFromFile:fileName];
  193.     if (remember) {
  194.       NXWriteDefault ([NXApp appName], "Background", fileName);
  195.     }
  196.   } else {
  197.     [backGround useFromSection:"Background.tiff"];
  198.     if (remember) {
  199.       NXRemoveDefault ([NXApp appName], "Background");
  200.     }
  201.   }
  202.   [backGround setBackgroundColor:NX_COLORWHITE];
  203.   [backGround setScalable:NO];
  204.   [self display];
  205.   
  206.   return self;   
  207. }
  208.  
  209. // The following two methods allow changing the background image from
  210.   // menu items or buttons.
  211.   
  212.   - changeBackground:sender
  213. {
  214.   const char *const types[] = {"tiff", "eps", NULL};  
  215.   
  216.   if ([[OpenPanel new] runModalForTypes:types]) {
  217.     [self setBackgroundFile:[[OpenPanel new] filename] andRemember:YES];
  218.     [self display];
  219.   }
  220.   
  221.   return self;
  222. }
  223.  
  224. - revertBackground:sender
  225. {
  226.   [self setBackgroundFile:NULL andRemember:YES];
  227.   [self display];
  228.   return self;
  229. }
  230.  
  231. // The following method will initialize all the variables for a new game.
  232.   
  233.   - startNewGame
  234. {
  235.   int i, j;
  236.   
  237.   gameRunning = NO;
  238.   finished = NO;
  239.   
  240.   blackCaptured = whiteCaptured = 0;
  241.   
  242.   seed(&rd);
  243.   
  244.   for (i = 0; i < MAXX; i++)
  245.     for (j = 0; j < MAXY; j++)
  246.       p[i][j] = 0;
  247.   
  248.   for (i = 0; i < 9; i++)
  249.     opn[i] = 1;
  250.   opn[4] = 0;
  251.   
  252.   sethand(handicap);
  253.   currentStone = (handicap == 0)?BLACKSTONE:WHITESTONE;
  254.   opposingStone = (currentStone == BLACKSTONE)?WHITESTONE:BLACKSTONE;
  255.   
  256.   if (currentStone == BLACKSTONE)
  257.     [gameMessage setStringValue:"Black's Turn"];
  258.   else
  259.     [gameMessage setStringValue:"White's Turn"];
  260.     
  261.   if (bothSides)
  262.     [passButton setEnabled: NO];
  263.   else
  264.     [passButton setEnabled: YES];
  265.   [passButton display];
  266.   
  267.   if (neitherSide) {
  268.     [startButton setEnabled: NO];
  269.     [stopButton setEnabled: NO];
  270.     [startButton display];
  271.     [stopButton display];
  272.   } else {
  273.     [startButton setEnabled: YES];
  274.     [stopButton setEnabled: YES];
  275.     [startButton display];
  276.     [stopButton display];
  277.   }
  278.   
  279.   if (((currentStone == BLACKSTONE) && (blackSide == 1)) ||
  280.       ((currentStone == WHITESTONE) && (whiteSide == 1)))  
  281.     [gameMessage2 setStringValue:"Press START to begin..."];
  282.   else
  283.     [gameMessage2 setStringValue:"You move first..."];
  284.   
  285.   return self;
  286. }
  287.  
  288. // The stop method will pause a running game. The go method will start it up
  289.   // again.
  290.   
  291.   - go:sender
  292. {
  293.   if ((gameRunning == 0) && (finished == 0)) {
  294.     gameRunning = YES;
  295.     [self step];
  296.   }
  297.   return self;
  298. }
  299.  
  300. - stop:sender
  301. {
  302.   id menuList = [mainMenu itemList];
  303.   int i, r, c;
  304.   
  305.   if (gameRunning) {
  306.     [menuList getNumRows: &r numCols: &c];
  307.     for( i = 0; i < r; i++ )
  308.       [[menuList cellAt: i :0] setEnabled: YES];
  309.     [mainMenu display];
  310.     gameRunning = NO;
  311.   }
  312.   return self;
  313. }
  314.  
  315. - mouseDown:(NXEvent *)event
  316. {
  317.   NXPoint pickedP;
  318.   
  319.   if (((currentStone == BLACKSTONE) && (blackSide == 1)) ||
  320.       ((currentStone == WHITESTONE) && (whiteSide == 1)))
  321.     return self;
  322.     
  323.   if ((!gameRunning) && (!finished))
  324.     gameRunning = YES;
  325.   
  326.   if (!finished) {
  327.     int x, y;
  328.     pickedP = event->location;
  329.     
  330.     x = floor((pickedP.x - ((19.0 - MAXX)/2.0)*STONEWIDTH - BASEBOARDX - WINDOWOFFSETX + RADIUS)/STONEWIDTH);
  331.     y = 18 - floor((pickedP.y - BASEBOARDY - WINDOWOFFSETY + RADIUS + ((19.0 - MAXY)/2.0)*STONEHEIGHT)/STONEHEIGHT);
  332.     
  333.     if (x < 0) x = 0;
  334.     if (x > MAXX - 1) x = MAXX - 1;
  335.     if (y < 0) y = 0;
  336.     if (y > MAXY - 1) y = MAXY - 1;
  337.     
  338.     if ((p[x][y] == 0) && (!suicide(x,y))) {
  339.       p[x][y] = currentStone;
  340.       if (currentStone == BLACKSTONE)
  341.     blackPassed = 0;
  342.       else
  343.     whitePassed = 0;
  344.       
  345.       setStoneLoc(x,y);
  346.   //    stoneX = BASEBOARDX - RADIUS + (x*STONEWIDTH);
  347.   //    stoneY = BASEBOARDY - RADIUS + ((18 - y)*STONEHEIGHT);
  348.       
  349.       //        sprintf(s,"%d %d: %f %f",x,y,pickedP.x,pickedP.y);
  350.       //        [gameMessage setStringValue:s];
  351.       
  352.       [self lockFocus];
  353.       
  354.       switch (p[x][y]) {
  355.       case WHITESTONE: [self showWhiteStone];
  356.     break;
  357.       case BLACKSTONE: [self showBlackStone];
  358.     break;
  359.       default: break;
  360.       }
  361.       
  362.       [self unlockFocus];
  363.       [self updateInfo];
  364.       if (!neitherSide)
  365.         [self step];
  366.       
  367.     } 
  368.   } else {
  369.     [self go:self];
  370.   }
  371.   
  372.   return self;
  373. }
  374.  
  375. - passMove
  376. {
  377.   if (((currentStone == BLACKSTONE) && (blackSide == 1)) ||
  378.       ((currentStone == WHITESTONE) && (whiteSide == 1)))
  379.     return self;
  380.  
  381.   if (currentStone == BLACKSTONE)
  382.     blackPassed = 1;
  383.   else
  384.     whitePassed = 1;
  385.     
  386.   [self updateInfo];
  387.   if (!neitherSide)
  388.     [self step];
  389.     
  390.   return self;
  391. }
  392.  
  393. - updateInfo
  394. {
  395.   char s[35], s2[150];
  396.   int oldBlackPrisoners, oldWhitePrisoners;
  397.   
  398.   oldBlackPrisoners = blackCaptured;
  399.   oldWhitePrisoners = whiteCaptured;
  400.   
  401.   examboard(opposingStone);
  402.   
  403.   if (currentStone == BLACKSTONE) {
  404.     opposingStone = BLACKSTONE;
  405.     currentStone = WHITESTONE;
  406.     [gameMessage setStringValue:"White's Turn"];
  407.   } else {
  408.     opposingStone = WHITESTONE;
  409.     currentStone = BLACKSTONE;
  410.     [gameMessage setStringValue:"Black's Turn"];
  411.   }
  412.   
  413.   sprintf(s,"%d",blackCaptured);
  414.   [blackPrisoners setStringValue:s];
  415.   
  416.   sprintf(s,"%d",whiteCaptured);
  417.   [whitePrisoners setStringValue:s];
  418.   
  419.   if ((oldBlackPrisoners != blackCaptured) ||
  420.       (oldWhitePrisoners != whiteCaptured))
  421.     {
  422.       [self lockFocus];
  423.       [[self window] flushWindow];
  424.       [self drawSelf:&bounds :0];
  425.       [self display];
  426.       [self unlockFocus];
  427.     }
  428.   
  429.   if ((blackPassed) && (opposingStone == BLACKSTONE))
  430.     [gameMessage2 setStringValue:"Black has Passed."];
  431.     
  432.   if ((whitePassed) && (opposingStone == WHITESTONE))
  433.     [gameMessage2 setStringValue:"White has Passed."];
  434.     
  435.   if ((!blackPassed) && (!whitePassed))
  436.     [gameMessage2 setStringValue:""];
  437.   
  438.   if ((blackPassed) && (whitePassed)) {
  439.     [self lockFocus];
  440.     [[self window] flushWindow];
  441.     [gameMessage setStringValue:"Scoring Game, Please Wait"];
  442.     [gameMessage2 setStringValue:"Removing Dead Groups..."];
  443.     [self display];
  444.     [self unlockFocus];
  445.     finished = 1;
  446.     [self scoreGame];
  447.     black_Score = (float)blackTerritory - (float)blackCaptured;
  448.     white_Score = (float)whiteTerritory - (float)whiteCaptured;
  449.     white_Score += (handicap == 0)?KOMI:0.5;
  450.     if (black_Score > white_Score)
  451.       sprintf(s, "Black has won by %3.1f points.", black_Score - white_Score);
  452.     if (white_Score > black_Score)
  453.       sprintf(s, "White has won by %3.1f points.", white_Score - black_Score);
  454.     if (black_Score == white_Score)
  455.       sprintf(s, "The game was a tie.");
  456.     [gameMessage2 setStringValue:s];
  457.     [gameMessage setStringValue:"Game Over"];
  458.     [self lockFocus];
  459.     [self display];
  460.     [self unlockFocus];
  461.     sprintf(s2, "Scoring:  Territory - Captured + Komi = Score\nBlack          %5d           %5d          %3.1f        %3.1f\nWhite          %5d          %5d           %3.1f        %3.1f", blackTerritory, blackCaptured, 0.0, black_Score, whiteTerritory, whiteCaptured, (handicap == 0)?KOMI:0.5, white_Score);
  462.     NXRunAlertPanel("Scoring Summary", s2, "OK", NULL, NULL);
  463.   }
  464.   
  465.   return self;
  466. }
  467.  
  468. - scoreGame
  469. {
  470.   int i, j, k, l, changes = 1, num_in_pattern;
  471.  
  472.   for (i = 0; i < MAXX; i++)
  473.     for (j = 0; j < MAXY; j++)
  474.       scoringmat[i][j] = 0;
  475.       
  476.   while (changes)
  477.     {
  478.       changes = 0;
  479.       find_owner();
  480.  
  481.       for (i = 0; i < MAXX; i++)
  482.     for (j = 0; j < MAXY; j++)
  483.       if ((p[i][j] != 0) && (scoringmat[i][j] == 0))
  484.         {
  485.           if (surrounds_territory(i, j))
  486.         {
  487.           find_pattern_in_board(i, j);
  488.  
  489.           for (k = 0; k < MAXX; k++)
  490.             for (l = 0; l < MAXY; l++)
  491.               if (patternmat[k][l])
  492.             scoringmat[k][l] = p[k][l];
  493.         }
  494.           else
  495.         {
  496.           find_pattern_in_board(i, j);
  497.           set_temp_to_p();
  498.           num_in_pattern = 0;
  499.  
  500.           for (k = 0; k < MAXX; k++)
  501.             for (l = 0; l < MAXY; l++)
  502.               if (patternmat[k][l])
  503.             {
  504.               p[k][l] = 0;
  505.               [self flashStone:k:l];
  506.               num_in_pattern++;
  507.             }
  508.  
  509.           find_owner();
  510.  
  511.           if ((ownermat[i][j] != -1) && (ownermat[i][j] != tempmat[i][j]))
  512.             {
  513.               if (tempmat[i][j] == 2)
  514.             blackCaptured += num_in_pattern;
  515.               else
  516.             whiteCaptured += num_in_pattern;
  517.               changes++;
  518.               [self lockFocus];
  519.               [[self window] flushWindow];
  520.               [self drawSelf:&bounds :0];
  521.               [self display];
  522.               [self unlockFocus];
  523.             }
  524.           else
  525.             {
  526.               set_p_to_temp();
  527.               find_owner();
  528.             }
  529.         }
  530.         }
  531.     }
  532.  
  533.   blackTerritory = 0;
  534.   whiteTerritory = 0;
  535.  
  536.   [self lockFocus];
  537.   for (i = 0; i < MAXX; i++)
  538.     for (j = 0; j < MAXY; j++)
  539.       {
  540.     if (ownermat[i][j] == BLACKSTONE)
  541.       blackTerritory++;
  542.     if (ownermat[i][j] == WHITESTONE)
  543.       whiteTerritory++;
  544.     if (ownermat[i][j] == -1)
  545.       [self flashStone:i:j];
  546.       }
  547.   [self unlockFocus];
  548.  
  549.   return self;
  550. }
  551.  
  552. // The following methods draw the pieces.
  553.   
  554.   - drawBlackStone:imageRep 
  555. {
  556.   //    PSscale (1.0, 1.0);
  557.   
  558.   // First draw the shadow under the stone.
  559.     
  560. //    PSarc (RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET/2, 
  561. //       RADIUS-SHADOWOFFSET, 0.0, 360.0);
  562. //  PSsetgray (NX_DKGRAY);
  563. //  if (NXDrawingStatus == NX_DRAWING) {
  564. //    PSsetalpha (0.666);
  565. //  }
  566. //  PSfill ();
  567.   if (NXDrawingStatus == NX_DRAWING) {
  568.     PSsetalpha (1.0);
  569.   }
  570.   
  571.   // Draw the stone.
  572.     
  573.     PSarc (RADIUS, RADIUS, 
  574.        RADIUS, 0.0, 360.0);
  575.   PSsetgray (NX_BLACK);
  576.   PSfill ();
  577.   
  578.   // And the lighter & darker spots on the stone...
  579.     
  580.     PSarcn (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  581.         RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
  582.   PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  583.      RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
  584.   PSsetgray (NX_DKGRAY);
  585.   PSfill ();
  586.   PSarcn (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  587.       RADIUS-SHADOWOFFSET-2.0, 350.0, 280.0);
  588.   PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  589.      RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
  590.   PSsetgray (NX_LTGRAY);
  591.   PSfill ();
  592.   
  593.   return self;
  594. }
  595.  
  596. - drawWhiteStone:imageRep 
  597. {
  598.   //    PSscale (1.0, 1.0);
  599.   
  600.   // First draw the shadow under the stone.
  601.     
  602. //    PSarc (RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET/2, 
  603. //       RADIUS-SHADOWOFFSET, 0.0, 360.0);
  604. //  PSsetgray (NX_DKGRAY);
  605. //  if (NXDrawingStatus == NX_DRAWING) {
  606. //    PSsetalpha (0.666);
  607. //  }
  608. //  PSfill ();
  609.   if (NXDrawingStatus == NX_DRAWING) {
  610.     PSsetalpha (1.0);
  611.   }
  612.   
  613.   // Draw the stone.
  614.     
  615.     PSarc (RADIUS, RADIUS, 
  616.        RADIUS, 0.0, 360.0);
  617.   PSsetgray (NX_WHITE);
  618.   PSfill ();
  619.   
  620.   // And the lighter & darker spots on the stone...
  621.     
  622.     PSarcn (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  623.         RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
  624.   PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  625.      RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
  626.   PSsetgray (NX_LTGRAY);
  627.   PSfill ();
  628.   PSarcn (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  629.       RADIUS-SHADOWOFFSET-2.0, 350.0, 280.0);
  630.   PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  631.      RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
  632.   PSsetgray (NX_DKGRAY);
  633.   PSfill ();
  634.   
  635.   return self;
  636. }
  637.  
  638. - drawGrayStone:imageRep 
  639. {
  640.   //    PSscale (1.0, 1.0);
  641.   
  642.   // First draw the shadow under the stone.
  643.     
  644. //    PSarc (RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET/2, 
  645. //       RADIUS-SHADOWOFFSET, 0.0, 360.0);
  646. //  PSsetgray (NX_DKGRAY);
  647. //  if (NXDrawingStatus == NX_DRAWING) {
  648. //    PSsetalpha (0.666);
  649. //  }
  650. //  PSfill ();
  651.   if (NXDrawingStatus == NX_DRAWING) {
  652.     PSsetalpha (1.0);
  653.   }
  654.   
  655.   // Draw the stone.
  656.     
  657. //    PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  658. //       RADIUS-SHADOWOFFSET, 0.0, 360.0);
  659.     PSarc (RADIUS, RADIUS, 
  660.        RADIUS, 0.0, 360.0);
  661.   PSsetgray (NX_DKGRAY);
  662.   PSfill ();
  663.   
  664.   // And the lighter & darker spots on the stone...
  665.     
  666.     PSarcn (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  667.         RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
  668.   PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  669.      RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
  670.   PSsetgray (NX_LTGRAY);
  671.   PSfill ();
  672.   PSarcn (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  673.       RADIUS-SHADOWOFFSET-2.0, 350.0, 280.0);
  674.   PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
  675.      RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
  676.   PSsetgray (NX_WHITE);
  677.   PSfill ();
  678.   
  679.   return self;
  680. }
  681.  
  682. - drawUpperLeft:imageRep
  683. {
  684.   PSsetgray(NX_BLACK);
  685.   PSsetlinewidth(0.0);
  686.   if (NXDrawingStatus == NX_DRAWING) {
  687.     PSsetalpha (1.0);
  688.   }
  689.  
  690.   PSnewpath();
  691.   PSmoveto(RADIUS, RADIUS);
  692.   PSlineto(bounds.size.width,RADIUS);
  693.   PSmoveto(RADIUS, RADIUS);
  694.   PSlineto(RADIUS, 0.0);
  695.   PSmoveto(RADIUS-SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  696.   PSlineto(bounds.size.width, RADIUS+SHADOWOFFSET);
  697.   PSmoveto(RADIUS-SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  698.   PSlineto(RADIUS-SHADOWOFFSET, 0.0);
  699.   PSstroke();
  700.  
  701.   return self;
  702. }
  703.  
  704. - drawUpperRight:imageRep
  705. {
  706.   PSsetgray(NX_BLACK);
  707.   PSsetlinewidth(0.0);
  708.   if (NXDrawingStatus == NX_DRAWING) {
  709.     PSsetalpha (1.0);
  710.   }
  711.  
  712.   PSnewpath();
  713.   PSmoveto(RADIUS, RADIUS);
  714.   PSlineto(0.0,RADIUS);
  715.   PSmoveto(RADIUS, RADIUS);
  716.   PSlineto(RADIUS, 0.0);
  717.   PSmoveto(RADIUS+SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  718.   PSlineto(0.0, RADIUS+SHADOWOFFSET);
  719.   PSmoveto(RADIUS+SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  720.   PSlineto(RADIUS+SHADOWOFFSET, 0.0);
  721.   PSstroke();
  722.  
  723.   return self;
  724. }
  725.  
  726. - drawLowerLeft:imageRep
  727. {
  728.   PSsetgray(NX_BLACK);
  729.   PSsetlinewidth(0.0);
  730.   if (NXDrawingStatus == NX_DRAWING) {
  731.     PSsetalpha (1.0);
  732.   }
  733.  
  734.   PSnewpath();
  735.   PSmoveto(RADIUS, RADIUS);
  736.   PSlineto(bounds.size.width,RADIUS);
  737.   PSmoveto(RADIUS, RADIUS);
  738.   PSlineto(RADIUS, bounds.size.height);
  739.   PSmoveto(RADIUS-SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  740.   PSlineto(bounds.size.width, RADIUS-SHADOWOFFSET);
  741.   PSmoveto(RADIUS-SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  742.   PSlineto(RADIUS-SHADOWOFFSET, bounds.size.height);
  743.   PSstroke();
  744.  
  745.   return self;
  746. }
  747.  
  748. - drawLowerRight:imageRep
  749. {
  750.   PSsetgray(NX_BLACK);
  751.   PSsetlinewidth(0.0);
  752.   if (NXDrawingStatus == NX_DRAWING) {
  753.     PSsetalpha (1.0);
  754.   }
  755.  
  756.   PSnewpath();
  757.   PSmoveto(RADIUS, RADIUS);
  758.   PSlineto(0.0,RADIUS);
  759.   PSmoveto(RADIUS, RADIUS);
  760.   PSlineto(RADIUS, bounds.size.height);
  761.   PSmoveto(RADIUS+SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  762.   PSlineto(0.0, RADIUS-SHADOWOFFSET);
  763.   PSmoveto(RADIUS+SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  764.   PSlineto(RADIUS+SHADOWOFFSET, bounds.size.height);
  765.   PSstroke();
  766.  
  767.   return self;
  768. }
  769.  
  770. - drawMidLeft:imageRep
  771. {
  772.   PSsetgray(NX_BLACK);
  773.   PSsetlinewidth(0.0);
  774.   if (NXDrawingStatus == NX_DRAWING) {
  775.     PSsetalpha (1.0);
  776.   }
  777.  
  778.   PSnewpath();
  779.   PSmoveto(RADIUS, RADIUS);
  780.   PSlineto(bounds.size.width,RADIUS);
  781.   PSmoveto(RADIUS, bounds.size.height);
  782.   PSlineto(RADIUS, 0.0);
  783.   PSmoveto(RADIUS-SHADOWOFFSET, bounds.size.height);
  784.   PSlineto(RADIUS-SHADOWOFFSET, 0.0);
  785.   PSstroke();
  786.  
  787.   return self;
  788. }
  789.  
  790. - drawMidRight:imageRep
  791. {
  792.   PSsetgray(NX_BLACK);
  793.   PSsetlinewidth(0.0);
  794.   if (NXDrawingStatus == NX_DRAWING) {
  795.     PSsetalpha (1.0);
  796.   }
  797.  
  798.   PSnewpath();
  799.   PSmoveto(RADIUS, RADIUS);
  800.   PSlineto(0.0,RADIUS);
  801.   PSmoveto(RADIUS, bounds.size.height);
  802.   PSlineto(RADIUS, 0.0);
  803.   PSmoveto(RADIUS+SHADOWOFFSET, bounds.size.height);
  804.   PSlineto(RADIUS+SHADOWOFFSET, 0.0);
  805.   PSstroke();
  806.  
  807.   return self;
  808. }
  809.  
  810. - drawMidTop:imageRep
  811. {
  812.   PSsetgray(NX_BLACK);
  813.   PSsetlinewidth(0.0);
  814.   if (NXDrawingStatus == NX_DRAWING) {
  815.     PSsetalpha (1.0);
  816.   }
  817.  
  818.   PSnewpath();
  819.   PSmoveto(RADIUS, RADIUS);
  820.   PSlineto(RADIUS,0.0);
  821.   PSmoveto(0.0, RADIUS);
  822.   PSlineto(bounds.size.width, RADIUS);
  823.   PSmoveto(0.0, RADIUS+SHADOWOFFSET);
  824.   PSlineto(bounds.size.width, RADIUS+SHADOWOFFSET);
  825.   PSstroke();
  826.  
  827.   return self;
  828. }
  829.  
  830. - drawMidBottom:imageRep
  831. {
  832.   PSsetgray(NX_BLACK);
  833.   PSsetlinewidth(0.0);
  834.   if (NXDrawingStatus == NX_DRAWING) {
  835.     PSsetalpha (1.0);
  836.   }
  837.  
  838.   PSnewpath();
  839.   PSmoveto(RADIUS, RADIUS);
  840.   PSlineto(RADIUS,bounds.size.height);
  841.   PSmoveto(0.0, RADIUS);
  842.   PSlineto(bounds.size.width, RADIUS);
  843.   PSmoveto(0.0, RADIUS-SHADOWOFFSET);
  844.   PSlineto(bounds.size.width, RADIUS-SHADOWOFFSET);
  845.   PSstroke();
  846.  
  847.   return self;
  848. }
  849.  
  850. - drawInnerSquare:imageRep
  851. {
  852.   PSsetgray(NX_BLACK);
  853.   PSsetlinewidth(0.0);
  854.   if (NXDrawingStatus == NX_DRAWING) {
  855.     PSsetalpha (1.0);
  856.   }
  857.  
  858.   PSnewpath();
  859.   PSmoveto(0.0, RADIUS);
  860.   PSlineto(bounds.size.width,RADIUS);
  861.   PSmoveto(RADIUS, bounds.size.height);
  862.   PSlineto(RADIUS, 0.0);
  863.   PSstroke();
  864.  
  865.   return self;
  866. }
  867.  
  868. - drawInnerHandicap:imageRep
  869. {
  870.   PSsetgray(NX_BLACK);
  871.   PSsetlinewidth(0.0);
  872.   if (NXDrawingStatus == NX_DRAWING) {
  873.     PSsetalpha (1.0);
  874.   }
  875.  
  876.   PSnewpath();
  877.   PSmoveto(0.0, RADIUS);
  878.   PSlineto(bounds.size.width,RADIUS);
  879.   PSmoveto(RADIUS, bounds.size.height);
  880.   PSlineto(RADIUS, 0.0);
  881.   PSstroke();
  882.   
  883.   PSarc(RADIUS, RADIUS, SHADOWOFFSET, 0.0, 360.0);
  884.   PSfill();
  885.  
  886.   return self;
  887. }
  888.  
  889. // The following methods show or erase the stones from the board.
  890.   
  891.   - showBlackStone 
  892. {
  893.   NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
  894.               {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  895.   [blackStone composite:NX_SOVER toPoint:&tmpRect.origin];
  896.   return self;
  897. }
  898.  
  899. - showWhiteStone
  900. {
  901.   NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
  902.               {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  903.   [whiteStone composite:NX_SOVER toPoint:&tmpRect.origin];
  904.   return self;
  905. }
  906.  
  907. - showGrayStone
  908. {
  909.   NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
  910.               {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  911.   [grayStone composite:NX_SOVER toPoint:&tmpRect.origin];
  912.   return self;
  913. }
  914.  
  915. - eraseStone
  916. {
  917.   NXRect tmpRect = {{floor(stoneX), floor(stoneY)}, {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  918.   return [self drawBackground:&tmpRect];
  919. }
  920.  
  921. // drawBackground: just draws the specified piece of the background by
  922.   // compositing from the background image.
  923.  
  924.   - showBackgroundPiece: (int)x: (int)y
  925. {
  926.   int q;
  927.   NXRect tmpRect = {{floor(stoneX), floor(stoneY)}, {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  928.  
  929.   if ((x == 0) && (y == 0))
  930.     [upperLeft composite:NX_SOVER toPoint:&tmpRect.origin];
  931.   
  932.   if ((x == 0) && (y == MAXY - 1))
  933.     [lowerLeft composite:NX_SOVER toPoint:&tmpRect.origin];
  934.   
  935.   if ((x == MAXX - 1) && (y == 0))
  936.     [upperRight composite:NX_SOVER toPoint:&tmpRect.origin];
  937.   
  938.   if ((x == MAXX - 1) && (y == MAXY - 1))
  939.     [lowerRight composite:NX_SOVER toPoint:&tmpRect.origin];
  940.   
  941.   if ((x == 0) && (y > 0) && (y < MAXY - 1))
  942.     [midLeft composite:NX_SOVER toPoint:&tmpRect.origin];
  943.   
  944.   if ((x == MAXX - 1) && (y > 0) && (y < MAXY - 1))
  945.     [midRight composite:NX_SOVER toPoint:&tmpRect.origin];
  946.   
  947.   if ((x > 0) && (x < MAXX - 1) && (y == 0))
  948.     [midTop composite:NX_SOVER toPoint:&tmpRect.origin];
  949.   
  950.   if ((x > 0) && (x < MAXX - 1) && (y == MAXY - 1))
  951.     [midBottom composite:NX_SOVER toPoint:&tmpRect.origin];
  952.   
  953.   if ((x > 0) && (x < MAXX - 1) && (y > 0) && (y < MAXY - 1))
  954.     [innerSquare composite:NX_SOVER toPoint:&tmpRect.origin];
  955.     
  956.   if (MAXX < 13)
  957.     q = 2;
  958.   else
  959.     q = 3;
  960.   
  961.   if (((x == q) && (y == q)) || ((x == q) && (y == MAXY/2)) ||
  962.       ((x == q) && (y == MAXY-q-1)) || ((x == MAXX/2) && (y == q)) ||
  963.       ((x == MAXX/2) && (y == MAXY/2)) || ((x == MAXX/2) && (y == MAXY-q-1)) ||
  964.       ((x == MAXX-q-1) && (y == q)) || ((x == MAXX-q-1) && (y == MAXY/2)) ||
  965.       ((x == MAXX-q-1) && (y == MAXY-q-1)))
  966.     [innerHandicap composite:NX_SOVER toPoint:&tmpRect.origin];
  967.  
  968.   return self;
  969. }
  970.   
  971.   - drawBackground:(NXRect *)rect
  972. {
  973.   NXRect tmpRect = *rect;
  974.   
  975.   NX_X(&tmpRect) = floor(NX_X(&tmpRect));
  976.   NX_Y(&tmpRect) = floor(NX_Y(&tmpRect));
  977.   if (NXDrawingStatus == NX_DRAWING) {
  978.     PSsetgray (NX_WHITE);
  979.     PScompositerect (NX_X(&tmpRect), NX_Y(&tmpRect),
  980.              NX_WIDTH(&tmpRect), NX_HEIGHT(&tmpRect), NX_COPY);
  981.   }
  982.   [backGround composite:NX_SOVER fromRect:&tmpRect toPoint:&tmpRect.origin];
  983.   return self;
  984. }
  985.  
  986. // drawSelf::, a method every decent View should have, redraws the game
  987.   // in its current state. This allows us to print the game very easily.
  988.   
  989.   - drawSelf:(NXRect *)rects :(int)rectCount 
  990. {
  991.   int xcnt, ycnt;
  992.   
  993.   [self drawBackground:(rects ? rects : &bounds)];
  994.   
  995.   for (xcnt = 0; xcnt < MAXX; xcnt++) { 
  996.     for (ycnt = 0; ycnt < MAXY; ycnt++) {
  997.       setStoneLoc(xcnt, ycnt);
  998.  //     stoneX = BASEBOARDX - RADIUS + (xcnt*STONEWIDTH);
  999.  //     stoneY = BASEBOARDY - RADIUS + ((18 - ycnt)*STONEHEIGHT);
  1000.       switch (p[xcnt][ycnt]) {
  1001.       case EMPTY: [self showBackgroundPiece: xcnt: ycnt];
  1002.     break;
  1003.       case WHITESTONE: [self showWhiteStone];
  1004.     break;
  1005.       case BLACKSTONE: [self showBlackStone];
  1006.     break;
  1007.       default: break;
  1008.       }
  1009.     }
  1010.   }
  1011.   
  1012.   return self;
  1013. }
  1014.  
  1015. - print:sender
  1016. {
  1017.   return [self printPSCode:sender];
  1018. }
  1019.  
  1020. - step
  1021. {
  1022.   id menuList = [mainMenu itemList];
  1023.   int i, r, c;
  1024.   
  1025.   if (neitherSide)
  1026.     return self;
  1027.     
  1028.   if (((currentStone == BLACKSTONE) && (blackSide == 0)) ||
  1029.       ((currentStone == WHITESTONE) && (whiteSide == 0)))
  1030.     return self;
  1031.   
  1032.   if (bothSides) {
  1033.     while ((gameRunning) && (!finished)) {
  1034.       [self selectMove];
  1035.       NXPing();
  1036.       [self updateInfo];
  1037.     }
  1038.   
  1039.     NXPing();
  1040.  
  1041.     [menuList getNumRows: &r numCols: &c];
  1042.     for( i = 0; i < r; i++ )
  1043.       [[menuList cellAt: i :0] setEnabled: YES];
  1044.     [mainMenu display];
  1045.   } else {
  1046.     [passButton setEnabled: NO];
  1047.     [passButton display];
  1048.     [self selectMove];
  1049.     [self updateInfo];
  1050.     [passButton setEnabled: YES];
  1051.     [passButton display];
  1052.   }
  1053.   return self;
  1054. }
  1055.  
  1056. - selectMove
  1057. {
  1058.   id menuList = [mainMenu itemList];
  1059.   int i, j, r, c;
  1060.   
  1061.   NXPing();
  1062.   
  1063.   [menuList getNumRows: &r numCols: &c];
  1064.   for( i = 0; i < r-1; i++ )
  1065.     [[menuList cellAt: i :0] setEnabled: NO];
  1066.   [mainMenu display];
  1067.   if( !bothSides )
  1068.     [stopButton setEnabled: NO];
  1069.   else
  1070.     [stopButton setEnabled: YES];
  1071.   
  1072.   genmove( &i, &j );
  1073.   if (i >= 0)
  1074.     {
  1075.       p[i][j] = currentStone;
  1076.       [self flashStone: i: j];
  1077.     }
  1078.  
  1079.   [self selectMoveEnd];
  1080.   
  1081.   if (i >= 0)
  1082.     {
  1083.       [self lockFocus];
  1084.       if (currentStone == BLACKSTONE)
  1085.         [self showBlackStone];
  1086.       else
  1087.         [self showWhiteStone];
  1088.       [self unlockFocus];
  1089.     }
  1090.   
  1091.   NXPing();
  1092.   return self;
  1093. }
  1094.  
  1095. - selectMoveEnd
  1096. {
  1097.   id menuList = [mainMenu itemList];
  1098.   int i, r, c;
  1099.   NXEvent peek_ev, *get_ev;
  1100.   
  1101.   NXPing();
  1102.  
  1103.   [menuList getNumRows: &r numCols: &c];
  1104.   if( !bothSides )
  1105.     for( i = 0; i < r; i++ )
  1106.       [[menuList cellAt: i :0] setEnabled: YES];
  1107.   [mainMenu display];
  1108.   [startButton setEnabled: YES];
  1109.   [stopButton setEnabled: YES];
  1110.   if( [NXApp peekNextEvent: NX_MOUSEDOWNMASK into: &peek_ev] ){
  1111.     get_ev = [NXApp getNextEvent: NX_MOUSEDOWNMASK];
  1112.     [NXApp sendEvent: get_ev];
  1113.   }
  1114.   NXPing();
  1115.  
  1116.   return self;
  1117. }
  1118.  
  1119. - flashStone: (int)x :(int)y
  1120. {
  1121.   
  1122.   setStoneLoc(x, y);
  1123. //  stoneX = BASEBOARDX - RADIUS + (x*STONEWIDTH);
  1124. //  stoneY = BASEBOARDY - RADIUS + ((18 - y)*STONEHEIGHT);
  1125.   
  1126.   [self lockFocus];
  1127.   [self showGrayStone];
  1128.   [self unlockFocus];
  1129.   
  1130.   return self;
  1131. }
  1132.  
  1133.  
  1134. @end
  1135.  
  1136.