ftp.nice.ch/pub/next/games/network/NeXTGo.2.7.NIHS.bs.gnutar.gz#/NeXTGo/Board.m

This is Board.m in view mode; [Download] [Up]

#include "comment.header"

/* $Id: Board.m,v 1.3 1997/07/06 19:37:55 ergo Exp $ */

/*
 * $Log: Board.m,v $
 * Revision 1.3  1997/07/06 19:37:55  ergo
 * actual version
 *
 * Revision 1.5  1997/06/03 23:01:55  ergo
 * *** empty log message ***
 *
 * Revision 1.4  1997/05/30 18:44:13  ergo
 * Added an Inspector
 *
 * Revision 1.3  1997/05/04 18:56:50  ergo
 * added time control for moves
 *
 */

#import "Board.h"
#import "gnugo.h"
#include "igs.h"
#include "MiscString.h"

#import <libc.h>
#import <math.h>
#import <sys/time.h>
#import <dpsclient/wraps.h>	// PSxxx functions
#import <soundkit/Sound.h>
#import <appkit/appkit.h>

#define EMPTY		0
#define WHITESTONE	1
#define BLACKSTONE	2
#define NEUTRAL_TERR	3
#define WHITE_TERR	4
#define BLACK_TERR	5
#define SPECIAL_CHAR    6
#define KOMI            5.5

// The following values are the default sizes for the various pieces. 
  
#define RADIUS		14.5 			// Stone radius
#define STONEWIDTH	29.0			// Stone width
#define STONEHEIGHT	29.0			// Stone height
  
  // SHADOWOFFSET defines the amount the shadow is offset from the piece. 
  
#define SHADOWOFFSET 2.0
  
#define BASEBOARDX 19.0
#define BASEBOARDY 19.0
#define WINDOWOFFSETX 12.0
#define WINDOWOFFSETY 12.0
  
#define gameSize  bounds.size

#define PSLine(a, b, x, y)	PSmoveto(a, b); PSlineto(x, y)

float stoneX, stoneY;
int blackStones, whiteStones;
char currentCharacter;
unsigned char oldBoard[19][19];

void setStoneLoc(int x, int y)
{
  stoneX = ((19.0 - MAXX)/2.0)*STONEWIDTH + BASEBOARDX - RADIUS + (x*STONEWIDTH);
  stoneY = BASEBOARDY - RADIUS + ((18 - y)*STONEHEIGHT) - ((19.0 - MAXY)/2.0)*STONEHEIGHT;
}  

void TEHandler(DPSTimedEntry teNumber, double now, void* userdata) {
	id obj  = (id)userdata;
	TimeStruct*	ts = [obj ts];

	char buf[256];
	int myTime;
	
	if ([obj startZeit] == 0.0)
		[obj setStartZeit:now];
	myTime = ts->time - (now - [obj startZeit]);
	if (myTime < 0) {
		if (ts->byo == -1 || 		/* player is in first byo-yomi time */
			ts->byo == 25) { 		/* player is in byo-yomi but did 	*/
									/* not yet move */
			myTime += [obj ByoTime] * 60;
			ts->byo = 25;
		}
	}
    sprintf(buf, "%d:%02d", myTime / 60, myTime % 60);
    if (ts->byo != -1)
		sprintf(buf, "%s, %d", buf, ts->byo);
	[ts->timeToHandle setStringValue:buf];
	[ts->timeToHandle display];
}

@implementation GoView
  
- initFrame:(const NXRect *)frm
{
  NXSize stoneSize;
  
  stoneSize.width = STONEWIDTH;
  stoneSize.height = STONEHEIGHT;
  
  te = 0;
  startZeit = 0;
  
  [super initFrame:frm];
  
  [self allocateGState];	// For faster lock/unlockFocus
    
  [(blackStone = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [blackStone useDrawMethod:@selector(drawBlackStone:) inObject:self];
  [blackStone setSize:&stoneSize];
  
  [(whiteStone = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [whiteStone useDrawMethod:@selector(drawWhiteStone:) inObject:self];
  [whiteStone setSize:&stoneSize];
  
  [(grayStone = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [grayStone useDrawMethod:@selector(drawGrayStone:) inObject:self];
  [grayStone setSize:&stoneSize];
  
  [(upperLeft = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [upperLeft useDrawMethod:@selector(drawUpperLeft:) inObject:self];
  [upperLeft setSize:&stoneSize];
  
  [(upperRight = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [upperRight useDrawMethod:@selector(drawUpperRight:) inObject:self];
  [upperRight setSize:&stoneSize];
  
  [(lowerLeft = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [lowerLeft useDrawMethod:@selector(drawLowerLeft:) inObject:self];
  [lowerLeft setSize:&stoneSize];
  
  [(lowerRight = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [lowerRight useDrawMethod:@selector(drawLowerRight:) inObject:self];
  [lowerRight setSize:&stoneSize];
  
  [(midLeft = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [midLeft useDrawMethod:@selector(drawMidLeft:) inObject:self];
  [midLeft setSize:&stoneSize];
  
  [(midRight = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [midRight useDrawMethod:@selector(drawMidRight:) inObject:self];
  [midRight setSize:&stoneSize];
  
  [(midTop = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [midTop useDrawMethod:@selector(drawMidTop:) inObject:self];
  [midTop setSize:&stoneSize];
  
  [(midBottom = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [midBottom useDrawMethod:@selector(drawMidBottom:) inObject:self];
  [midBottom setSize:&stoneSize];
  
  [(innerSquare = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [innerSquare useDrawMethod:@selector(drawInnerSquare:) inObject:self];
  [innerSquare setSize:&stoneSize];
  
  [(innerHandicap = [[NXImage allocFromZone:[self zone]] init]) setScalable:NO];
  [innerHandicap useDrawMethod:@selector(drawInnerHandicap:) inObject:self];
  [innerHandicap setSize:&stoneSize];
  
  [self setBackgroundFile:NXGetDefaultValue([NXApp appName], "BackGround") 
 andRemember:NO];
  
  [self startNewGame];

  historyFont = [Font newFont:"Helvetica" size:9.0 matrix:NX_IDENTITYMATRIX];
  blackTerrFont = [Font newFont:"Helvetica" size:25.0 matrix:NX_IDENTITYMATRIX];
  whiteTerrFont = [Font newFont:"Helvetica" size:22.5 matrix:NX_IDENTITYMATRIX];
  stoneClick = [Sound findSoundFor:"Pop"];
  
	{
		struct timeval tp;
    	struct timezone tzp;
	    gettimeofday(&tp, &tzp);
		time = tp.tv_sec;
	}
  return self;
}

// free simply gets rid of everything we created for MainGoView, including
  // the instance of MainGoView itself. This is how nice objects clean up.
  
- free {
	[backGround free];
	return [super free];
}



// This methods allows changing the file used to paint the background of the
  // playing field. Set fileName to NULL to revert to the default. Set
  // remember to YES if you wish the write the value out in the defaults.
  
- setBackgroundFile:(const char *)fileName andRemember:(BOOL)remember {
	[backGround free];
	backGround = [[NXImage allocFromZone:[self zone]] initSize:&gameSize];
	if (fileName) {
    	[backGround useFromFile:fileName];
    	if (remember) 
      		NXWriteDefault ([NXApp appName], "Background", fileName);
  	} else {
    	[backGround useFromSection:"Background.tiff"];
    	if (remember)
      		NXRemoveDefault ([NXApp appName], "Background");
  	}
	[backGround setBackgroundColor:NX_COLORWHITE];
	[backGround setScalable:NO];
	[self display];
  
	return self;   
}

// The following two methods allow changing the background image from
// menu items or buttons.
  
- changeBackground:sender {
	
	const char *const types[] = {"tiff", "eps", NULL};  
  
	if ([[OpenPanel new] runModalForTypes:types]) {
		[self setBackgroundFile:[[OpenPanel new] filename] andRemember:YES];
		[self display];
	}
  
	return self;
}

- revertBackground:sender
{
  [self setBackgroundFile:NULL andRemember:YES];
  [self display];
  return self;
}

- resetButtons
{
  if (SmartGoGameFlag)
    {
      [startButton setEnabled: NO];
      [stopButton setEnabled: NO];
      [passButton setEnabled: NO];

      return self;
    }
    
 if (bothSides)
    [passButton setEnabled: NO];
  else
    [passButton setEnabled: YES];
 
  if (neitherSide)
    {
      [startButton setEnabled: NO];
      [stopButton setEnabled: NO];
    }
  else
    {
      [startButton setEnabled: YES];
      [stopButton setEnabled: YES];
    }

  return self;
}

// The following method will initialize all the variables for a new game.
  
- startNewGame {
	
	int i, j;
  
	gameRunning = NO;
	finished = NO;
	gameScored = NO;
	resultsDisplayed = NO;
	scoringGame = NO;
	lastMove = 0;
  	blackCaptured = whiteCaptured = 0;
	manualScoring = manScoreTemp;
  
	seed(&rd);
  
	for (i = 0; i < MAXX; i++)
    	for (j = 0; j < MAXY; j++)
      		oldBoard[i][j] = p[i][j] = hist[i][j] = 0;
  
  	for (i = 0; i < 9; i++)
    	opn[i] = 1;
  	opn[4] = 0;


  	if (gameType == LOCAL) {
		sethand(handicap);
      	currentStone = (handicap == 0)?BLACKSTONE:WHITESTONE;
      	opposingStone = (currentStone == BLACKSTONE)?WHITESTONE:BLACKSTONE;
  
      	if (currentStone == BLACKSTONE)
			[gameMessage setStringValue:"Black's Turn"];
      	else
			[gameMessage setStringValue:"White's Turn"];

      	[self resetButtons];
  
      	if (((currentStone == BLACKSTONE) && (blackSide == 1)) ||
	  		((currentStone == WHITESTONE) && (whiteSide == 1)))  
			[gameMessage2 setStringValue:"Press START to begin..."];
      	else
			[gameMessage2 setStringValue:"You move first..."];
    }
  	else {
		[gameMessage2 setStringValue:"Internet Go Server"];
      	[gameMessage setStringValue:""];
      	[self setblacksPrisoners:0];
      	[self setwhitesPrisoners:0];
      	[passButton setEnabled: YES];
      	[startButton setEnabled: NO];
      	[stopButton setEnabled: NO];
		[self removeTE];
		{
			struct timeval tp;
    		struct timezone tzp;
    		gettimeofday(&tp, &tzp);
			time = tp.tv_sec;
		}	
	}
    
  	[ScoringWindow close];
  	NXPing();
    
  	return self;
}

// The stop method will pause a running game. The go method will start it up
  // again.
  
- go:sender
{
  if (gameType == IGSGAME)
    return self;
    
  if ((scoringGame) && (manualScoring))
    {
      int i, j;

      find_owner();
      blackTerritory = 0;
      whiteTerritory = 0;

      for (i = 0; i < MAXX; i++)
	for (j = 0; j < MAXY; j++)
	  {
	    if (ownermat[i][j] == BLACKSTONE)
	      {
		blackTerritory++;
		p[i][j] = BLACK_TERR;
	      }
	    if (ownermat[i][j] == WHITESTONE)
	      {
		whiteTerritory++;
		p[i][j] = WHITE_TERR;
	      }
	    if (ownermat[i][j] == NEUTRAL_TERR)
	      {
		p[i][j] = NEUTRAL_TERR;
	      }
	  }

      gameScored = YES;
      [self displayScoringInfo];
      NXPing();
    }

  if ((gameRunning == 0) && (finished == 0)) {
    gameRunning = YES;
    [self step];
  }
  return 0;
}

- stop:sender {
	if (gameType == IGSGAME)
		return self;
      
	if (gameRunning) 
		gameRunning = NO;
	return self;
}

- showLastMove:sender
{
  int i;

  if (SmartGoGameFlag)
    return self;

  if (finished)
    {
      NXRunAlertPanel("NeXTGo", "The game has concluded.  The last move was\n\
the scoring.", "OK", 0, 0);

      return self;
    }

  if (lastMove == 0)
    {
      NXRunAlertPanel("NeXTGo", "The game has not yet started.", "OK", 0, 0);

      return self;
    }

  for (i = 0; i < gameMoves[lastMove-1].numchanges; i++)
    {
      if (gameMoves[lastMove-1].changes[i].x < 0)
	{
	  NXRunAlertPanel("NeXTGo", "The last move was a pass.", "OK", 0, 0);

	  return self;
	}
    }
    
  [self lockFocus];
  for (i = 0; i < gameMoves[lastMove-1].numchanges; i++)
    {
      if (gameMoves[lastMove-1].changes[i].added)
	{
	  setStoneLoc(gameMoves[lastMove-1].changes[i].x,
		      gameMoves[lastMove-1].changes[i].y);
	  [self showGrayStone];
	}
    }
  [self unlockFocus];
  NXPing();
  
  [self lockFocus];
  [[self window] flushWindow];
  [self drawSelf:&bounds :0];
  [self display];
  [self unlockFocus];
  NXPing();
  
  return self;
}

- undo
{
  int i, j, x, y;

  if (finished)
    return self;
  
  if (lastMove == 1)
    {
      [self startNewGame];
      [self display];

      return self;
    }
    
  if (lastMove > 0)
    {
      lastMove--;

      for (i = 0; i < MAXX; i++)
	for (j = 0; j < MAXY; j++)
	  p[i][j] = oldBoard[i][j];
      blackCaptured = gameMoves[lastMove].blackCaptured;
      whiteCaptured = gameMoves[lastMove].whiteCaptured;

      for (i = 0; i < gameMoves[lastMove-1].numchanges; i++)
	{
	  x = gameMoves[lastMove-1].changes[i].x;
	  y = gameMoves[lastMove-1].changes[i].y;
	  if (gameMoves[lastMove-1].changes[i].added)
	    {
	      oldBoard[x][y] = EMPTY;
	    }
	  else
	    {
	      oldBoard[x][y] = gameMoves[lastMove-1].changes[i].color;
	    }
	}
      [self refreshIO];

      currentStone = opposingStone;
      opposingStone = (currentStone == BLACKSTONE)?WHITESTONE:BLACKSTONE;

      if (gameType == LOCAL)
	{
	  [gameMessage setStringValue:(currentStone == BLACKSTONE)?
	   "Black's Turn":"White's Turn"];
	  
	  if ((bothSides) || (((currentStone == BLACKSTONE) && (blackSide)) ||
			      ((currentStone == WHITESTONE) && (whiteSide))))
	    {
	      [gameMessage2 setStringValue:"Press START to continue..."];
	      gameRunning = 0;
	    }
	  else
	    [gameMessage2 setStringValue:"Your move..."];
	}
    }

  return self;
}

- undoLastMove:sender
{
  if (SmartGoGameFlag)
    return self;

  if (gameType == LOCAL)
    {
      [self undo];
    }
  else
    {
      sendstr("undo\n");
    }

  return self;
}

- toggleShowHistFlag:sender
{
  [self lockFocus];
  [self display];
  [self unlockFocus];

  return self;
}

- toggleSound:sender
{

  return self;
}

- doClick
{
  if ([playSounds intValue])
    [stoneClick play];
  NXPing();

  return self;
}

- toggleCoords:sender
{
  [self lockFocus];
  [self display];
  [self unlockFocus];

  return self;
}

- mouseDown:(NXEvent *)event
{
  	NXPoint pickedP;

	if (gameType == LOCAL) {
      	if ((((currentStone == BLACKSTONE) && (blackSide == 1)) ||
	   		((currentStone == WHITESTONE) && (whiteSide == 1))) &&
	  		(!scoringGame) && (!manualScoring))
			return self;

      	if (SmartGoGameFlag)
			return self;
    
      	if ((!gameRunning) && (!finished))
			gameRunning = YES;
  
      	if (!finished) {
			int i, j, x, y;

			pickedP = event->location;
    
			x = floor((pickedP.x - ((19.0 - MAXX)/2.0)*STONEWIDTH - BASEBOARDX 	
								  - WINDOWOFFSETX + RADIUS)/STONEWIDTH);
			y = 18 - floor((pickedP.y - BASEBOARDY - WINDOWOFFSETY + RADIUS +
			((19.0 - MAXY)/2.0)*STONEHEIGHT)/STONEHEIGHT);
    
			if (x < 0) x = 0;
			if (x > MAXX - 1) x = MAXX - 1;
			if (y < 0) y = 0;
			if (y > MAXY - 1) y = MAXY - 1;
    
			if ((p[x][y] == 0) && (!suicide(x,y))) {
	  			for (i = 0; i < MAXX; i++)
	    			for (j = 0; j < MAXY; j++)
	      				oldBoard[i][j] = p[i][j];

				p[x][y] = currentStone;
	  			if (currentStone == BLACKSTONE)
	    			blackPassed = 0;
	  			else
	    			whitePassed = 0;

	  			setStoneLoc(x,y);
      
	  			[self lockFocus];
	  			switch (p[x][y]) {
	    			case WHITESTONE: 
						[self showWhiteStone];
	      				break;
	    			case BLACKSTONE: 
						[self showBlackStone];
	     			 	break;
	    			default: 
						break;
	    		}
	  			[self unlockFocus];

	  			[self doClick];

		  		[self updateInfo];

		  		[self addMoveToGameMoves: currentStone: x: y];

		  		if ([showHistFlag intValue]) {
		      		NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
							{floor(STONEWIDTH), floor(STONEHEIGHT)}};

	      			[self lockFocus];
	      			[self drawSelf:&tmpRect :0];
	      			[self unlockFocus];
	    		}

	  			if (!neitherSide)
	    			[self step];
      			[self update];
			} 
		} 
		else {
			if ((scoringGame) && (manualScoring) && (!gameScored)) {
	    	int x, y;

	    	pickedP = event->location;
    
	    	x = floor((pickedP.x - ((19.0 - MAXX)/2.0)*STONEWIDTH - BASEBOARDX
		       - WINDOWOFFSETX + RADIUS)/STONEWIDTH);
	    	y = 18 - floor((pickedP.y - BASEBOARDY - WINDOWOFFSETY + RADIUS +
			    ((19.0 - MAXY)/2.0)*STONEHEIGHT)/STONEHEIGHT);
    
	    	if (x < 0) x = 0;
	    	if (x > MAXX - 1) x = MAXX - 1;
	    	if (y < 0) y = 0;
	    	if (y > MAXY - 1) y = MAXY - 1;

	    	if (p[x][y] != EMPTY) {
				int k, l;

				currentStone = p[x][y];

				find_pattern_in_board(x, y);
				for (k = 0; k < MAXX; k++)
		  			for (l = 0; l < MAXY; l++)
		    			if (patternmat[k][l]) {
							p[k][l] = EMPTY;
							if (currentStone == BLACKSTONE)
			  					blackCaptured++;
							else
			  					whiteCaptured++;
		      			}
		
				[self setblacksPrisoners:blackCaptured];
				[self setwhitesPrisoners:whiteCaptured];

				[self update];
	      	}
	  		}
  		}
  	}
  	else {
      	int x, y;
      	char s[50], n[50];
      	extern int observing, ingame;

      	if (observing || (ingame == -1)) {
	  		NXRunAlertPanel("IGS Error", "You cannot make a move unless you are playing.", "OK", 0, 0);

	  		return self;
		}

      	pickedP = event->location;
    
      	x = floor((pickedP.x - ((19.0 - MAXX)/2.0)*STONEWIDTH - BASEBOARDX -
		 	WINDOWOFFSETX + RADIUS)/STONEWIDTH);
      	y = 18 - floor((pickedP.y - BASEBOARDY - WINDOWOFFSETY + RADIUS +
		      ((19.0 - MAXY)/2.0)*STONEHEIGHT)/STONEHEIGHT);

      	if (x < 0) x = 0;
      	if (x > MAXX - 1) x = MAXX - 1;
      	if (y < 0) y = 0;
      	if (y > MAXY - 1) y = MAXY - 1;

      	s[0] = x + 'a';
      	if (x > 7)
        	s[0] = x + 'b';
      	s[1] = 0;
      	sprintf(n, "%d", MAXY-y);
      	strcat(s, n);
		{
			struct timeval tp;
    		struct timezone tzp;
	    	gettimeofday(&tp, &tzp);
			time = tp.tv_sec - time;
		
		}

 		sprintf(n, " %d", ingame); 	
      	strcat(s, n);
 
		sprintf(n, " %ld", time); 	
      	strcat(s, n);
      	strcat(s, "\n");
      	sendstr(s);
    }

  return self;
}


- passMove
{
	if (gameType == LOCAL) {
		if (((currentStone == BLACKSTONE) && (blackSide == 1)) ||
	  		((currentStone == WHITESTONE) && (whiteSide == 1))) {
			return self;
		}
		if (currentStone == BLACKSTONE) {
	  		blackPassed = 1;
	  		if (AGAScoring) blackCaptured++;
		}
      	else {
	  		whitePassed = 1;
	  		if (AGAScoring) whiteCaptured++;
		}

      	[self updateInfo];

      	[self addMoveToGameMoves: currentStone: -1: -1];
    
      	if ((!neitherSide) && (!finished))
		[self step];
    }
  	else {
		sendstr("pass\n");
    }
    
  return self;
}

- refreshIO
{
  [self setblacksPrisoners:blackCaptured];
  [self setwhitesPrisoners:whiteCaptured];
  
  [self lockFocus];
  [[self window] flushWindow];
  [self drawSelf:&bounds :0];
  [self display];
  [self unlockFocus];
  
  NXPing();

  return self;
}

- addMoveToGameMoves: (int)color: (int)x: (int)y
{
	int i, j, k, numchanges;

  	numchanges = 0;
  	for (i = 0; i < MAXX; i++)
    	for (j = 0; j < MAXY; j++)
      		if (p[i][j] != oldBoard[i][j])
				numchanges++;
  	if (x < 0 || y < 0)
		numchanges++;
  	gameMoves[lastMove].numchanges = numchanges;
  	gameMoves[lastMove].changes = (struct change *)
    	malloc((size_t)sizeof(struct change)*numchanges);
  	k = 0;
  	if (x < 0 || y < 0) {
      	gameMoves[lastMove].changes[0].added = NO;
      	gameMoves[lastMove].changes[0].x = x;
		gameMoves[lastMove].changes[0].y = y;
		gameMoves[lastMove].changes[0].color = color;
      	k++;
    }
  	for (i = 0; i < MAXX; i++)
    	for (j = 0; j < MAXY; j++)
      		if (p[i][j] != oldBoard[i][j]) {
	  			gameMoves[lastMove].changes[k].x = i;
	  			gameMoves[lastMove].changes[k].y = j;
	  			if (p[i][j] != EMPTY) {
	      			gameMoves[lastMove].changes[k].added = YES;
	      			gameMoves[lastMove].changes[k].color = p[i][j];
	    		}
	  			else {
	      			gameMoves[lastMove].changes[k].added = NO;
	      			gameMoves[lastMove].changes[k].color = oldBoard[i][j];
				}
	  			k++;
			}
  	gameMoves[lastMove].blackCaptured = blackCaptured;
  	gameMoves[lastMove].whiteCaptured = whiteCaptured;

  	lastMove++;

  	if (x >= 0)
		hist[x][y] = lastMove;

  	return self;
}

- makeMove: (int)color: (int)x: (int)y {
	int oldwhitesPrisoners, oldblacksPrisoners, i, j;

	currentStone = color;
	opposingStone = (currentStone == BLACKSTONE)?WHITESTONE:BLACKSTONE;

	if ((x >= 0) && (y >= 0)) {
		for (i = 0; i < MAXX; i++)
        	for (j = 0; j < MAXX; j++)
          		oldBoard[i][j] = p[i][j];

      	p[x][y] = color;
      
      	setStoneLoc(x,y);

	    [self lockFocus];
      	switch (p[x][y]) {
        	case WHITESTONE: 
				[self showWhiteStone];
          		break;
        	case BLACKSTONE: 
				[self showBlackStone];
          		break;
        	default: 
				break;
        }
      	[self unlockFocus];

      	[self doClick];

      	oldblacksPrisoners = blackCaptured;
      	oldwhitesPrisoners = whiteCaptured;
      
      	examboard(opposingStone);
  
      	[self setblacksPrisoners:blackCaptured];
      	[self setwhitesPrisoners:whiteCaptured];
  
      	if (((oldblacksPrisoners != blackCaptured) ||
           (oldwhitesPrisoners != whiteCaptured))) {
        	[self lockFocus];
	  		[self display];
          	[self unlockFocus];
        
		}

      	if ([showHistFlag intValue]) {
          	NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
	    		      {floor(STONEWIDTH), floor(STONEHEIGHT)}};

          	[self lockFocus];
          	[self drawSelf:&tmpRect :0];
          	[self display];
			[self unlockFocus];
		}
		
		if (blackPassed) {
			blackPassed = 0;
			[gameMessage2 setStringValue:""];
		}
		
		if (whitePassed) {
			whitePassed = 0;
			[gameMessage2 setStringValue:""];
		}
	}
	[self addMoveToGameMoves: color: x: y];
	
	[gameMessage setStringValue:(opposingStone == BLACKSTONE)?"Black's Turn":
       "White's Turn"];
	
	if ((-1 == x) && (-1 == y)) {		/* opponent has passed */
		if (currentStone == BLACKSTONE) {
			blackPassed = 1;
    		[gameMessage2 setStringValue:"Black has passed."];
		}
		else {
 			whitePassed = 1;
    		[gameMessage2 setStringValue:"White has passed."];
		}
    }
	{
		struct timeval tp;
    	struct timezone tzp;
    	gettimeofday(&tp, &tzp);
		time = tp.tv_sec;
	}	
	[self doClick];
	[self update];
	return self;
}

- makeMoveSilent: (int)color: (int)x: (int)y
{
  int i, j;
      
  if ((x >= 0) && (y >= 0))
    {
      for (i = 0; i < MAXX; i++)
	for (j = 0; j < MAXY; j++)
	  oldBoard[i][j] = p[i][j];

      p[x][y] = color;

      currentStone = color;
      opposingStone = (currentStone == BLACKSTONE)?WHITESTONE:BLACKSTONE;

      examboard(opposingStone);

      [self addMoveToGameMoves: color: x: y];
    }
  
  return self;
}

- setTimeAndByo: (int)btime: (int)bbyo: (int)wtime: (int)wbyo
{
	ts.caller = self;
	if (bTime != -1) {
		if (bTime < 0)
			bTime = 0;
		if (wTime < 0)
			wTime = 0;
		if (currentStone == WHITESTONE) {		/* Black moved */
			[self removeTE];
			startZeit = 0.0;
			ts.byo = bbyo;
			ts.time = btime;
			ts.timeToHandle = blackTime;
			te = DPSAddTimedEntry(0.2, TEHandler, (void*)self,
								   NX_BASETHRESHOLD);
		} 
		else {						/* White moved */
			[self removeTE];
			startZeit = 0.0;
			ts.byo = wbyo;
			ts.time = wtime;
			ts.timeToHandle = whiteTime;
			te = DPSAddTimedEntry(0.2, TEHandler, (void*)self,
								   NX_BASETHRESHOLD);
		}
		bTime = btime;
		bByo = bbyo;
		wTime = wtime;
		wByo = wbyo;
	}
	return self;
}

- dispTime
{
	char bltime[25], whtime[25];

    sprintf(bltime, "%d:%02d", bTime / 60, bTime % 60);
    if (bByo != -1)
		sprintf(bltime, "%s, %d", bltime, bByo);
    sprintf(whtime, "%d:%02d", wTime / 60, wTime % 60);
    if (wByo != -1)
		sprintf(whtime, "%s, %d", whtime, wByo);
	[blackTime setStringValue:bltime];
	[blackTime display];
	[whiteTime setStringValue:whtime];
	[whiteTime display];
	return self;
}

- setGameNumber: (int)n
{
  [IGSGameNumber setIntValue:n];
  [IGSGameNumber display];
  
  return self;
}

- updateTitle {
	id buf = [ [MiscString alloc] initString:[IGSBlackPlayer stringValue]];
	[buf cat:" - "];
	[buf cat:[IGSWhitePlayer stringValue]];
	[[self window] setTitle:[buf stringValue]];
	[buf free];
	
	return self;	
}

- setWhiteName: (char *)wname {
	
	[IGSWhitePlayer setStringValue:wname];
	[IGSWhitePlayer display];
	[self updateTitle];

	return self;
}

- setBlackName: (char *)bname {

	[IGSBlackPlayer setStringValue:bname];
	[IGSBlackPlayer display];
	[self updateTitle];

	return self;
}

- setIGSHandicap: (int)h
{
  [IGShandicap setIntValue:h];
  [IGShandicap display];

  return self;
}

- setIGSKomi: (char *)k
{
  [IGSkomi setStringValue:k];
  [IGSkomi display];

  return self;
}

- setByoTime: (int)aByoTime {
	ByoTime = aByoTime;
	
	return self;
}

- (int) ByoTime {
	return ByoTime;
}

- updateInfo
{
  int oldblacksPrisoners, oldwhitesPrisoners, i, j;
  
  if (finished && gameScored && resultsDisplayed)
    {
      [startButton setEnabled: NO];
      [stopButton setEnabled: NO];
      [passButton setEnabled: NO];
      return self;
    }
  
  oldblacksPrisoners = blackCaptured;
  oldwhitesPrisoners = whiteCaptured;
      
  examboard(opposingStone);
  
  	if (currentStone == BLACKSTONE) {
  		opposingStone = BLACKSTONE;
    	currentStone = WHITESTONE;
    	[gameMessage setStringValue:"White's Turn"];
  	} else {
    	opposingStone = WHITESTONE;
    	currentStone = BLACKSTONE;
    	[gameMessage setStringValue:"Black's Turn"];
  	}
  
	[self setblacksPrisoners:blackCaptured];
	[self setwhitesPrisoners:whiteCaptured];
  
	if (((oldblacksPrisoners != blackCaptured) ||
      	(oldwhitesPrisoners != whiteCaptured))) {
      	[self lockFocus];
      	for (i = 0; i < MAXX; i++)
			for (j = 0; j < MAXX; j++)
	  			if ((oldBoard[i][j] != EMPTY) && (p[i][j] == EMPTY)) {
	      			setStoneLoc(i, j);
	      			[self eraseStone];
	      			[self showBackgroundPiece: i: j];
	    		}
    	[self unlockFocus];
  	}

  if ([showHistFlag intValue])
    {
      NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
			  {floor(STONEWIDTH), floor(STONEHEIGHT)}};

      [self lockFocus];
      [self drawSelf:&tmpRect :0];
      [self display];
      [self unlockFocus];
    }
  
  if ((blackPassed) && (opposingStone == BLACKSTONE))
    [gameMessage2 setStringValue:"Black has passed."];
    
  if ((whitePassed) && (opposingStone == WHITESTONE))
    [gameMessage2 setStringValue:"White has passed."];
    
  if ((!blackPassed) && (!whitePassed))
    [gameMessage2 setStringValue:""];
  
  if ((blackPassed) && (whitePassed) && (!manualScoring) && (!gameScored))
    {
      [self lockFocus];
      [[self window] flushWindow];
      [gameMessage setStringValue:"Scoring Game, Please Wait"];
      [gameMessage2 setStringValue:"Removing Dead Groups..."];
      [self display];
      [self unlockFocus];
      finished = 1;
      score_game();
//      [self scoreGame];
      manualScoring = 1;
    }
  if ((blackPassed) && (whitePassed) && (manualScoring) && (!gameScored))
    {
      [self lockFocus];
      [[self window] flushWindow];
      [gameMessage setStringValue:"Please remove dead groups"];
      [gameMessage2 setStringValue:"When finished, press Start..."];
      [self display];
      [self unlockFocus];
      [passButton setEnabled:NO];
      [stopButton setEnabled:NO];
      finished = 1;
      scoringGame = YES;
    }

  return self;

}

- displayScoringInfo
{
  char s[35];
  int i, j;

  if (gameScored)
    {
      resultsDisplayed = YES;
      if (typeOfScoring == 0)
	{
	  black_Score = (float)blackTerritory - (float)blackCaptured;
	  white_Score = (float)whiteTerritory - (float)whiteCaptured;
	  white_Score += (handicap == 0)?KOMI:0.5;
	  [TypeOfScoring setStringValue:"Japanese Scoring Method"];
	  [BlackTerrString setStringValue:"Territory"];
	  [WhiteTerrString setStringValue:"Territory"];
	  [BlackTerrValue setIntValue:blackTerritory];
	  [WhiteTerrValue setIntValue:whiteTerritory];
	  [BlackPrisonString setStringValue:"Prisoners"];
	  [WhitePrisonString setStringValue:"Prisoners"];
	  [BlackPrisonValue setIntValue:blackCaptured];
	  [WhitePrisonValue setIntValue:whiteCaptured];
	  [BlackTotalValue setFloatValue:black_Score];
	  [WhiteTotalValue setFloatValue:white_Score];
	}
      else
	{
	  blackStones = whiteStones = 0;
	  for (i = 0; i < MAXX; i++)
	    for (j = 0; j < MAXY; j++)
	      {
		if (p[i][j] == BLACKSTONE) blackStones++;
		if (p[i][j] == WHITESTONE) whiteStones++;
	      }
	  black_Score = (float)blackTerritory + (float)blackStones;
	  white_Score = (float)whiteTerritory + (float)whiteStones;
	  white_Score += (handicap == 0)?KOMI:0.5;
	  [TypeOfScoring setStringValue:"Chinese Scoring Method"];
	  [BlackTerrString setStringValue:"Territory"];
	  [WhiteTerrString setStringValue:"Territory"];
	  [BlackTerrValue setIntValue:blackTerritory];
	  [WhiteTerrValue setIntValue:whiteTerritory];
	  [BlackPrisonString setStringValue:"Stones"];
	  [WhitePrisonString setStringValue:"Stones"];
	  [BlackPrisonValue setIntValue:blackStones];
	  [WhitePrisonValue setIntValue:whiteStones];
	  [BlackTotalValue setFloatValue:black_Score];
	  [WhiteTotalValue setFloatValue:white_Score];
	}
      if (black_Score > white_Score)
	sprintf(s, "Result:  Black wins by %3.1f points.", black_Score - white_Score);
      if (white_Score > black_Score)
	sprintf(s, "Result:  White wins by %3.1f points.", white_Score - black_Score);
      if (black_Score == white_Score)
	sprintf(s, "Result:  The game was a tie.");
      [KomiValue setFloatValue:((handicap == 0)?KOMI:0.5)];
      [GameResult setStringValue:s];
      [ScoringWindow makeKeyAndOrderFront:self];
      [gameMessage setStringValue:"Game Over"];
      [self lockFocus];
      [self display];
      [self unlockFocus];
    }
  
  return self;
}

- scoreGame
{
  int i, j, k, l, changes = 1, num_in_pattern;

  for (i = 0; i < MAXX; i++)
    for (j = 0; j < MAXY; j++)
      scoringmat[i][j] = 0;
      
  while (changes)
    {
      changes = 0;
      find_owner();

      for (i = 0; i < MAXX; i++)
	for (j = 0; j < MAXY; j++)
	  if ((p[i][j] != 0) && (scoringmat[i][j] == 0))
	    {
	      if (surrounds_territory(i, j))
		{
		  find_pattern_in_board(i, j);

		  for (k = 0; k < MAXX; k++)
		    for (l = 0; l < MAXY; l++)
		      if (patternmat[k][l])
			scoringmat[k][l] = p[k][l];
		}
	      else
		{
		  find_pattern_in_board(i, j);
		  set_temp_to_p();
		  num_in_pattern = 0;

		  for (k = 0; k < MAXX; k++)
		    for (l = 0; l < MAXY; l++)
		      if (patternmat[k][l])
			{
			  p[k][l] = EMPTY;
			  [self flashStone:k:l];
			  num_in_pattern++;
			}

		  find_owner();

		  if ((ownermat[i][j] != NEUTRAL_TERR) &&
		      (ownermat[i][j] != tempmat[i][j]))
		    {
		      if (tempmat[i][j] == BLACKSTONE)
			blackCaptured += num_in_pattern;
		      else
			whiteCaptured += num_in_pattern;
		      changes++;
		      [self lockFocus];
		      [self display];
		      [self unlockFocus];
		    }
		  else
		    {
		      set_p_to_temp();
		      find_owner();
		    }
		}
	    }
    }

/*  blackTerritory = 0;
  whiteTerritory = 0;

  [self lockFocus];
  for (i = 0; i < MAXX; i++)
    for (j = 0; j < MAXY; j++)
      {
	if (ownermat[i][j] == BLACKSTONE)
	  {
	    blackTerritory++;
	    p[i][j] = BLACK_TERR;
	  }
	if (ownermat[i][j] == WHITESTONE)
	  {
	    whiteTerritory++;
	    p[i][j] = WHITE_TERR;
	  }
	if (ownermat[i][j] == NEUTRAL_TERR)
	  {
	    [self flashStone:i:j];
	    p[i][j] = NEUTRAL_TERR;
	  }
      }
  [self unlockFocus];   */

  return self;
}

// The following methods draw the pieces.
  
  - drawBlackStone:imageRep 
{
  //    PSscale (1.0, 1.0);
  
  // First draw the shadow under the stone.
    
//    PSarc (RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET/2, 
//	   RADIUS-SHADOWOFFSET, 0.0, 360.0);
//  PSsetgray (NX_DKGRAY);
//  if (NXDrawingStatus == NX_DRAWING) {
//    PSsetalpha (0.666);
//  }
//  PSfill ();
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }
  
  // Draw the stone.
    
    PSarc (RADIUS, RADIUS, 
	   RADIUS, 0.0, 360.0);
  PSsetgray (NX_BLACK);
  PSfill ();
  
  // And the lighter & darker spots on the stone...
    
    PSarcn (RADIUS, RADIUS, 
	    RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
  PSsetgray (NX_DKGRAY);
  PSfill ();
  PSarcn (RADIUS, RADIUS, 
	  RADIUS-SHADOWOFFSET-3.0, 350.0, 280.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
  PSsetgray (NX_LTGRAY);
  PSfill ();
  
  return self;
}

- drawWhiteStone:imageRep 
{
  //    PSscale (1.0, 1.0);
  
  // First draw the shadow under the stone.
    
//    PSarc (RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET/2, 
//	   RADIUS-SHADOWOFFSET, 0.0, 360.0);
//  PSsetgray (NX_DKGRAY);
//  if (NXDrawingStatus == NX_DRAWING) {
//    PSsetalpha (0.666);
//  }
//  PSfill ();
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }
  
  // Draw the stone.
    
    PSarc (RADIUS, RADIUS, 
	   RADIUS, 0.0, 360.0);
  PSsetgray (NX_WHITE);
  PSfill ();
  
  // And the lighter & darker spots on the stone...
    
    PSarcn (RADIUS, RADIUS, 
	    RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
  PSsetgray (NX_LTGRAY);
  PSfill ();
  PSarcn (RADIUS, RADIUS, 
	  RADIUS-SHADOWOFFSET-3.0, 350.0, 280.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
  PSsetgray (NX_DKGRAY);
  PSfill ();
  
  return self;
}

- drawGrayStone:imageRep 
{
  //    PSscale (1.0, 1.0);
  
  // First draw the shadow under the stone.
    
//    PSarc (RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET/2, 
//	   RADIUS-SHADOWOFFSET, 0.0, 360.0);
//  PSsetgray (NX_DKGRAY);
//  if (NXDrawingStatus == NX_DRAWING) {
//    PSsetalpha (0.666);
//  }
//  PSfill ();
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }
  
  // Draw the stone.
    
//    PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, 
//	   RADIUS-SHADOWOFFSET, 0.0, 360.0);
    PSarc (RADIUS, RADIUS, 
	   RADIUS, 0.0, 360.0);
  PSsetgray (NX_DKGRAY);
  PSfill ();
  
  // And the lighter & darker spots on the stone...
    
    PSarcn (RADIUS, RADIUS, 
	    RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
  PSsetgray (NX_LTGRAY);
  PSfill ();
  PSarcn (RADIUS, RADIUS, 
	  RADIUS-SHADOWOFFSET-3.0, 350.0, 280.0);
  PSarc (RADIUS, RADIUS, 
	 RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
  PSsetgray (NX_WHITE);
  PSfill ();
  
  return self;
}

- drawUpperLeft:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS, 0.0);
  PSmoveto(RADIUS-SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  PSlineto(bounds.size.width, RADIUS+SHADOWOFFSET);
  PSmoveto(RADIUS-SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  PSlineto(RADIUS-SHADOWOFFSET, 0.0);
  PSstroke();

  return self;
}

- drawUpperRight:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(0.0,RADIUS);
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS, 0.0);
  PSmoveto(RADIUS+SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  PSlineto(0.0, RADIUS+SHADOWOFFSET);
  PSmoveto(RADIUS+SHADOWOFFSET, RADIUS+SHADOWOFFSET);
  PSlineto(RADIUS+SHADOWOFFSET, 0.0);
  PSstroke();

  return self;
}

- drawLowerLeft:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS, bounds.size.height);
  PSmoveto(RADIUS-SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  PSlineto(bounds.size.width, RADIUS-SHADOWOFFSET);
  PSmoveto(RADIUS-SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  PSlineto(RADIUS-SHADOWOFFSET, bounds.size.height);
  PSstroke();

  return self;
}

- drawLowerRight:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(0.0,RADIUS);
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS, bounds.size.height);
  PSmoveto(RADIUS+SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  PSlineto(0.0, RADIUS-SHADOWOFFSET);
  PSmoveto(RADIUS+SHADOWOFFSET, RADIUS-SHADOWOFFSET);
  PSlineto(RADIUS+SHADOWOFFSET, bounds.size.height);
  PSstroke();

  return self;
}

- drawMidLeft:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, bounds.size.height);
  PSlineto(RADIUS, 0.0);
  PSmoveto(RADIUS-SHADOWOFFSET, bounds.size.height);
  PSlineto(RADIUS-SHADOWOFFSET, 0.0);
  PSstroke();

  return self;
}

- drawMidRight:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(0.0,RADIUS);
  PSmoveto(RADIUS, bounds.size.height);
  PSlineto(RADIUS, 0.0);
  PSmoveto(RADIUS+SHADOWOFFSET, bounds.size.height);
  PSlineto(RADIUS+SHADOWOFFSET, 0.0);
  PSstroke();

  return self;
}

- drawMidTop:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS,0.0);
  PSmoveto(0.0, RADIUS);
  PSlineto(bounds.size.width, RADIUS);
  PSmoveto(0.0, RADIUS+SHADOWOFFSET);
  PSlineto(bounds.size.width, RADIUS+SHADOWOFFSET);
  PSstroke();

  return self;
}

- drawMidBottom:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(RADIUS, RADIUS);
  PSlineto(RADIUS,bounds.size.height);
  PSmoveto(0.0, RADIUS);
  PSlineto(bounds.size.width, RADIUS);
  PSmoveto(0.0, RADIUS-SHADOWOFFSET);
  PSlineto(bounds.size.width, RADIUS-SHADOWOFFSET);
  PSstroke();

  return self;
}

- drawInnerSquare:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(0.0, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, bounds.size.height);
  PSlineto(RADIUS, 0.0);
  PSstroke();

  return self;
}

- drawInnerHandicap:imageRep
{
  PSsetgray(NX_BLACK);
  PSsetlinewidth(0.0);
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetalpha (1.0);
  }

  PSnewpath();
  PSmoveto(0.0, RADIUS);
  PSlineto(bounds.size.width,RADIUS);
  PSmoveto(RADIUS, bounds.size.height);
  PSlineto(RADIUS, 0.0);
  PSstroke();
  
  PSarc(RADIUS, RADIUS, SHADOWOFFSET, 0.0, 360.0);
  PSfill();

  return self;
}

// The following methods show or erase the stones from the board.
  
  - showBlackStone 
{
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
		      {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  [blackStone composite:NX_SOVER toPoint:&tmpRect.origin];
  return self;
}

- showWhiteStone
{
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
		      {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  [whiteStone composite:NX_SOVER toPoint:&tmpRect.origin];
  return self;
}

- showGrayStone
{
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
		      {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  [grayStone composite:NX_SOVER toPoint:&tmpRect.origin];
  return self;
}

- eraseStone
{
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)}, {floor(STONEWIDTH), floor(STONEHEIGHT)}};
  return [self drawBackground:&tmpRect];
}

// drawBackground: just draws the specified piece of the background by
// compositing from the background image.

- showBackgroundPiece: (int)x: (int)y {
  int q;
  NXRect tmpRect = {{floor(stoneX), floor(stoneY)}, {floor(STONEWIDTH), floor(STONEHEIGHT)}};

  if ((x == 0) && (y == 0))
    [upperLeft composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == 0) && (y == MAXY - 1))
    [lowerLeft composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == MAXX - 1) && (y == 0))
    [upperRight composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == MAXX - 1) && (y == MAXY - 1))
    [lowerRight composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == 0) && (y > 0) && (y < MAXY - 1))
    [midLeft composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x == MAXX - 1) && (y > 0) && (y < MAXY - 1))
    [midRight composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x > 0) && (x < MAXX - 1) && (y == 0))
    [midTop composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x > 0) && (x < MAXX - 1) && (y == MAXY - 1))
    [midBottom composite:NX_SOVER toPoint:&tmpRect.origin];
  
  if ((x > 0) && (x < MAXX - 1) && (y > 0) && (y < MAXY - 1))
    [innerSquare composite:NX_SOVER toPoint:&tmpRect.origin];
    
  if (MAXX < 13)
    q = 2;
  else
    q = 3;
  
  if (((x == q) && (y == q)) || ((x == q) && (y == MAXY/2)) ||
      ((x == q) && (y == MAXY-q-1)) || ((x == MAXX/2) && (y == q)) ||
      ((x == MAXX/2) && (y == MAXY/2)) || ((x == MAXX/2) && (y == MAXY-q-1)) ||
      ((x == MAXX-q-1) && (y == q)) || ((x == MAXX-q-1) && (y == MAXY/2)) ||
      ((x == MAXX-q-1) && (y == MAXY-q-1)))
    [innerHandicap composite:NX_SOVER toPoint:&tmpRect.origin];

  return self;
}
  
  - drawBackground:(NXRect *)rect
{
  NXRect tmpRect = *rect;
  
  NX_X(&tmpRect) = floor(NX_X(&tmpRect));
  NX_Y(&tmpRect) = floor(NX_Y(&tmpRect));
  if (NXDrawingStatus == NX_DRAWING) {
    PSsetgray (NX_WHITE);
    PScompositerect (NX_X(&tmpRect), NX_Y(&tmpRect),
		     NX_WIDTH(&tmpRect), NX_HEIGHT(&tmpRect), NX_COPY);
  }
  [backGround composite:NX_SOVER fromRect:&tmpRect toPoint:&tmpRect.origin];
  return self;
}

// drawSelf::, a method every decent View should have, redraws the game
// in its current state. This allows us to print the game very easily.
  
  - drawSelf:(NXRect *)rects :(int)rectCount 
{
  int xcnt, ycnt;
  char s[5], specialChar;
  
  [self drawBackground:(rects ? rects : &bounds)];

  specialChar = 'a';

  for (xcnt = 0; xcnt < MAXX; xcnt++)
    {
      for (ycnt = 0; ycnt < MAXY; ycnt++)
	{
	  setStoneLoc(xcnt, ycnt);

	  switch (p[xcnt][ycnt])
	    {
	    case EMPTY: currentCharacter = 0;
	      [self showBackgroundPiece: xcnt: ycnt];
	      break;
	    case WHITESTONE: [self showWhiteStone];
	      if ([showHistFlag intValue])
		{
		  char s[5];
		  
		  [historyFont set];
		  PSsetgray(NX_BLACK);
		  PSmoveto(stoneX+RADIUS -
			   (floor(log(hist[xcnt][ycnt]+0.5)/log(10))+1.0)*3,
			   stoneY+RADIUS - 4);
		  if (hist[xcnt][ycnt] > 0)
		    {
		      sprintf(s, "%d", hist[xcnt][ycnt]);
		      PSshow(s);
		    }
		  else
		    {
		      PSmoveto(stoneX + RADIUS - 4, stoneY + RADIUS - 4);
		      PSshow("H");
		    }
		}
	      break;
	    case BLACKSTONE: [self showBlackStone];
	      if ([showHistFlag intValue])
		{
		  char s[5];

		  [historyFont set];
		  PSsetgray(NX_WHITE);
		  PSmoveto(stoneX+RADIUS -
			   (floor(log(hist[xcnt][ycnt]+0.5)/log(10))+1.0)*3,
			   stoneY+RADIUS - 4);
		  if (hist[xcnt][ycnt] > 0)
		    {
		      sprintf(s, "%d", hist[xcnt][ycnt]);
		      PSshow(s);
		    }
		  else
		    {
		      PSmoveto(stoneX + RADIUS - 4, stoneY + RADIUS - 4);
		      PSshow("H");
		    }
		}
	      break;
	    case NEUTRAL_TERR: [self showGrayStone];
	      break;
	    case WHITE_TERR: [self showBackgroundPiece: xcnt: ycnt];
	      [whiteTerrFont set];
	      PSsetgray(NX_WHITE);
	      PSmoveto(stoneX+RADIUS/3, stoneY+RADIUS/3+2);
	      PSshow("W");
	      break;
	    case BLACK_TERR: [self showBackgroundPiece: xcnt: ycnt];
	      [blackTerrFont set];
	      PSsetgray(NX_DKGRAY);
	      PSmoveto(stoneX+RADIUS/3+1, stoneY+RADIUS/3);
	      PSshow("B");
	      break;
	    case SPECIAL_CHAR: [self showBackgroundPiece: xcnt: ycnt];
	      PSselectfont("Helvetica", 25.0);
	      PSsetgray(NX_DKGRAY);
	      PSmoveto(stoneX+RADIUS/3+1, stoneY+RADIUS/3);
	      sprintf(s,"%c",specialChar);
	      specialChar++;
	      PSshow(s);
	      break;
	    default: currentCharacter = 0;
	      [self showBackgroundPiece: xcnt: ycnt];
	      break;
	    }
	}
    }

  if ([showCoords intValue])
    {
      for (xcnt = 0; xcnt < MAXX; xcnt++)
	{
	  setStoneLoc(xcnt, 0);

	  [historyFont set];
	  PSsetgray(NX_DKGRAY);
	  PSmoveto(stoneX + RADIUS - 3, stoneY + RADIUS + 11);
	  s[0] = 'A' + xcnt;
	  if (xcnt > 7) s[0]++;
	  s[1] = 0;
	  PSshow(s);

          setStoneLoc(xcnt, MAXY - 1);
          PSmoveto(stoneX + RADIUS - 3, stoneY - 3);
	  PSshow(s);
	}
      for (ycnt = 0; ycnt < MAXX; ycnt++)
	{
	  setStoneLoc(0, ycnt);

	  [historyFont set];
	  PSsetgray(NX_DKGRAY);
	  PSmoveto(stoneX - 4, stoneY + RADIUS - 4);
	  sprintf(s, "%d", MAXY-ycnt);
	  PSshow(s);

	  setStoneLoc(MAXX - 1, ycnt);
	  if (xcnt < 10)
	    {
	      PSmoveto(stoneX + STONEWIDTH, stoneY + RADIUS - 4);
	    }
	  else
	    {
	      PSmoveto(stoneX + STONEWIDTH - 6, stoneY + RADIUS - 4);
	    }
	  PSshow(s);
	}
    }
  
  return self;
}

- print:sender
{
  return [self printPSCode:sender];
}

- step
{
  NXEvent peek_ev, *get_ev;
  
  if (gameType == IGSGAME)
    {
      return self;
    }
  
  if (neitherSide)
    return self;
    
  if (((currentStone == BLACKSTONE) && (blackSide == 0)) ||
      ((currentStone == WHITESTONE) && (whiteSide == 0)))
    return self;
  
  if (bothSides) {
    while ((gameRunning) && (!finished)) {
      [self selectMove];
      NXPing();

      if( [NXApp peekNextEvent: NX_MOUSEDOWNMASK into: &peek_ev] ){
	get_ev = [NXApp getNextEvent: NX_MOUSEDOWNMASK];
	[NXApp sendEvent: get_ev];
      }
    }
  
    NXPing();
  } else {
    [passButton setEnabled: NO];
    [self selectMove];

    NXPing();

    NXPing();
    [passButton setEnabled: YES];
    NXPing();
  }
  return self;
}

- selectMove
{
  int i, j;
  
  NXPing();

  if( !bothSides )
    [stopButton setEnabled: NO];
  else
    [stopButton setEnabled: YES];

  for (i = 0; i < MAXX; i++)
    for (j = 0; j < MAXY; j++)
      oldBoard[i][j] = p[i][j];
  
  genmove( &i, &j );
  if (i >= 0)
    {
      p[i][j] = currentStone;

      [self flashStone: i: j];
    }

  if (((i < 0) || (j < 0)) && (AGAScoring))
    {
      if (currentStone == BLACKSTONE)
        blackCaptured++;
      else
        whiteCaptured++;
    }
  
  [self selectMoveEnd];
    
  if (i >= 0)
    {
      [self lockFocus];
      if (currentStone == BLACKSTONE)
        [self showBlackStone];
      else
        [self showWhiteStone];
      [self unlockFocus];

      [self doClick];
    }

  [self updateInfo];
    
  [self addMoveToGameMoves: currentStone: i: j];
  
  if ([showHistFlag intValue])
    {
      NXRect tmpRect = {{floor(stoneX), floor(stoneY)},
			  {floor(STONEWIDTH), floor(STONEHEIGHT)}};

      [self lockFocus];
      [self drawSelf:&tmpRect :0];
      [self display];
      [self unlockFocus];
    }

  NXPing();
  return self;
}

- selectMoveEnd
{
  NXPing();

  [startButton setEnabled: YES];
  [stopButton setEnabled: YES];
  NXPing();

  return self;
}

- flashStone: (int)x :(int)y
{
  
  setStoneLoc(x, y);
  
  [self lockFocus];
  [self showGrayStone];
  [self unlockFocus];
  
  return self;
}

- setMess1:(char *)s
{
  [gameMessage setStringValue:s];
  [gameMessage display];

  return self;
}

- setMess2:(char *)s
{
  [gameMessage2 setStringValue:s];
  [gameMessage2 display];

  return self;
}

- setblacksPrisoners:(int)bp
{
  [blacksPrisoners setIntValue:bp];
  [blacksPrisoners display];

  return self;
}

- setwhitesPrisoners:(int)wp
{
  [whitesPrisoners setIntValue:wp];
  [whitesPrisoners display];

  return self;
}

- (float)startZeit {
	return startZeit;
}

- setStartZeit:(float)aTime {
	startZeit = aTime;
	return self;
}

- (int)bByo {
	return bByo;
}

- (TimeStruct*)ts {
	return &ts;
}

- gameCompleted {
	
	[self removeTE];	
  	[self setblacksPrisoners:0];
  	[self setwhitesPrisoners:0];
	[IGSGameNumber setStringValue:""]; 
	[IGSBlackPlayer setStringValue:""]; 
	[IGSWhitePlayer setStringValue:""]; 
	[IGShandicap setStringValue:""]; 
	[IGSkomi setStringValue:""];
	[blackTime setStringValue:""];
	[whiteTime setStringValue:""];

	
	return self;
}

- removeTE {
	if (te) 
		DPSRemoveTimedEntry(te);
	te = 0;
	return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Marcel Waldvogel and Netfuture.ch.