[net.sources.mac] Sources to Go program for the Macintosh

LOGANJ@BYUVAX.BITNET (03/11/86)

The following text contains 1 document file and 7 files of source code for
the Go program posted 24 Feb 86, as promised.  Each source file begins with
"/* File filename" and you will need to break the files up to compile.  The
programs were written for Megamax C.

The core programs (in goprocs.c) have been through extensive testing,
everything else is in the process of conceptualization and development.

These programs are provided as a simple skeleton go program.  Some code was
removed from the 'bestmoves' routine for this posting, to keep it simple.
Since code was removed, the following programs will not play as well as the
executable version that was posted 24 Feb 1986.

This is not to be taken as an example of great coding technique - this is a
first effort.

Known problems:  The last time that I adjusted the weights in the
move selection routine (bestmoves) to play a better game against Nemesis I
made a mistake, so presently the executable version of the program will
attempt an illegal move in some situations.

Also, I do not have access to a Mac XL, so I can not insure that the
program runs properly there.  As far as I know, the code follows the
toolbox interface conventions - nothing tricky there.

Will this kind posting create more interest in Programs that play go?
Or will it set the programming of go back ten years by undermining support
for more professional efforts?  Other?

Thanks to those of you who have responded with problems and suggestions!
Correspondence and gratuities of any kind (verbal, code, financial, ...)
can be sent to:

Lynn Beus & Jim Logan
BYU Computer Science
232 TMCB
Provo, Utah  84602

or email:  loganj@byuvax.bitnet

Phone: (801) 378-3617
___________________________________________________________________________

26 February 1986

Go Utility Programs Description

Developer:  James R. Logan Jr. (BYU)
Advisor:  Dr. Lynn H. Beus (BYU)
BYU Computer Science
232 TMCB
Provo, Utah  84602
email address:  loganj@byuvax.bitnet
phone:  (801) 378-3617

This document describes procedures, variables, arrays, and structures
that maintain a 1-dimensional representation of the Oriental game of Go.
The present implementation of the programs, for lack of development
time, has the following arbitrary limitations:

  -  No count is maintained of the number of eyes that each army has
     (an easy thing to implement?),  but only the number of individual
     liberties that each army has.

  -  No count is maintained of the number of neighboring armies to an
     army.

  -  The maximum allowable board size is 19 by 19 lines, or 361 positions.

  -  The maximum allowable number of armies is 362 (compile time
     parameter).

  -  The game board must be square.

The 'who' variable in the game structure (game.who) designates black's
or white's turn to play, and is an input parameter to some of these
procedures.  The user is required to set this variable to White or Black
before each procedure call.

All 1-dimensional representations of the Go board are defined in C as
char [361].  The Go Programs consist of twelve main procedures.  The
calling conventions and purpose of these twelve procedures is described
as follows:

-initialize
Initializes the specified game structure by clearing all stones from the
various arrays and building the initial blank army.  Initialize must be
called for a newly allocated game structure.  For example, to initialize
a 5 by 5 game board the initialize routine should be called as follows:

  g->width = 5;
  initialize(g);
  where g is a game structure pointer previously allocated by the user.

-addastone
Adds a stone of color g.who to the specified 1-dimensional position and
updates the specified game structure.  If the specified position is
already occupied then no update occurs.  For example, to add a stone to
position 9 the addastone routine should be called as follows:

  addastone(g,9);
  where g is an initialized game structure pointer.

-removeastone
Removes the specified stone and updates the specified game structure.
If the specified stone represents an unoccupied position then no update
occurs.  For example, to remove a stone at position 9 the removeastone
routine should be called as follows:

  removeastone(g,9,status);
  where g is a game structure pointer,
  and status is a movestatus (mstatus) structure.

-getneighbors
Returns the number of immediate non-blank neighbors to a position, and
returns the 1 dimensional board position of each non-blank neighbor in a
stone[1..4] array sorted by army number.  For example, to get all
neighboring positions (white, black, and blank) to position 9 the
getneighbors routine should be called as follows:

  getneighbors(g,n,9);
  where g is a game structure pointer,
  and n is a nabor structure pointer.

The next two procedures will not normally be needed by the users program,
but they are included for completeness.

-capture
Removes the army represented by the specified stone and updates the
appropriate data structures.  If the specified stone represents a blank
army then no update occurs.  For example, to remove an army that
contains a stone at position 9 the capture routine should be called as
follows:

  capture(g,9);
  where g is a game structure pointer.

-uncapture
Uncaptures the army represented by the specified position and updates
the appropriate data structures.  If the specified postion is occupied
then no update occurs.  For example, to uncapture an army that contains
a stone at position 9 the uncapture routine should be called as follows:

  uncapture(g,9,color);
  where g is a game structure pointer,
  and color is the color of the army (White or Black) being uncaptured.
_________________________________________________________________________

The following routines print the values of game structures upside down
from the game board displayed on the screen.

-moveboard
This procedure prints the game.board array on the screen, for debugging.
For example, to display game.board call moveboard as follows:

  moveboard(g);
  where g is a game structure pointer.

-armyboard
This procedure prints the game.armymen array on the screen, for debugging.
For example, to display game.armymen call armyboard as follows:

  armyboard(g);
  where g is a game structure pointer.

-armyinfo
This procedure prints the game.army structure on the screen, for debugging.
For example, to display game.army call armyinfo as follows:

  armyinfo(g);
  where g is a game structure pointer.

linkinfo
This procedure prints the game.armylnk array on the screen, for debugging.
For example, to display game.armylnk call linkinfo as follows:

  linkinfo(g);
  where g is a game structure pointer.

-pictinfo
This procedure prints the game.cpict and game.opict arrays on the screen
in hex, for debugging.  For example, to display game.cpict and
game.opict call pictinfo as follows:

  pictinfo(g);
  where g is a game structure pointer.

-neighborinfo
This procedure prints the game.neibrd array on the screen in hex, for
debugging.  For example, to display game.neibrd call neighborinfo as
follows:

  neighborinfo(g);
  where g is a game structure pointer.
________________________________________________________________________

These Go Programs use one main data structure to represent a game of Go,
as follows:

game - This structure contains some basic structures for the analysis of
a single board position.

The Game structure contains the following variables, arrays, and
structures:

who - This variable represents the color of the player making the
current move, and is set to White for one player, and Black for the
other player.  This variable must be set before a call to the addastone
procedure, because the addastone procedure uses this variable to set the
board array value (stone color).

avail - This variable is the next available army number in a linked list
of army numbers. Do not change this variable.

maxstone - This variable indicates the 1-dimensional size of the game
board being used.  It is computed by the initialize procedure.  Do not
change this variable.

width - This variable indicates the width of the game board.  The
initialize procedure sets this variable to the maximum column number.
Do not change this variable.

movecount - This variable indicates the number of stones played in the
game structure.  This variable is cleared by the initialize procedure,
incremented by calls to the addastone procedure, and decremented by
calls to the removeastone and capture procedures. Do not change this
variable.

movestatus - This structure contains ko information and capture
information based on the last stone played on the board.  This structure
should be pushed on the stack during tree searches, because it is used
to remove a play and restore the previous game position.  Do not change
the variables in this structure.

The ko position (movestatus.kospot) is set by the capture procedure to
the 1-dimensional position of a 1-man army that was just captured.  If
no army was captured by the last move then this variable is set to -1.
Also if the size of the captured army is greater than one then this
variable is set to -1.  The addastone procedure sets this variable to
-1, but the addastone procedure calls the capture procedure if a capture
is made by a move.

The capture information (movestatus.captures) is set by the addastone
procedure to reflect which of the four possible armies surrounding the
new stone was captured.  This variable type (movestatus.captures) is bit
encoded, where the least significant 4 bits determine which direction an
army was captured in (so that the army can be restored if the capturing
stone is removed).

debug - This BITSET variable represents various debug states.  Bits 0-5
are used in these programs, and bits 6-15 are available for any use.

board - This 1-dimensional array contains the position of black and
white stones on the Go Board.  One player's stones are represented by a
value of White, and opponent stones are represented by a value of Black.
Do not change this array.

rowbrd - This 1-dimensional array corresponds exactly to the board array
above and indicates the 2-dimensional row number of each position.  Do
not change this array.

colbrd - This 1-dimensional array corresponds exactly to the board array
above and indicates the 2-dimensional column number of each position.
Do not change this array.

armymen - This 1-dimensional array corresponds exactly to the Board
structure (above). Each board position in this array contains the army
number of the army that occupies this position.  The army number is used
to access information about the army.

armylnk - This 1-dimensional array corresponds exactly to the Board
structure (above). Each position in this array contains the number of
the next man in the army.  This array is used for processing every man
in an army, which occurs when an army is being built, when an army is
captured, and when army neighbors need to be counted.

whitepats - This 1-dimensional array corresponds exactly to the Board
structure (above). Each position in this array contains a binary
representation of the white stones in a 5 by 5 diamond shape around the
position.  This binary picture can be used to test for a specific
geometic arrangement of the white player's stones around a given
position.  See the diagram below.

blackpats - This 1-dimensional array corresponds exactly to the Board
structure (above). Each position in this array contains a binary
representation of the black stones in a 5 by 5 diamond shape around the
position.  This binary picture can be used to test for a specific
geometic arrangement of the black player's stones around a given
position.  See the diagram below.

Whitepats and blackpats (above) contain 32 bit values that represent
stone patterns in a 5 by 5 diamond shape, centered around a given board
position (S).  Only 29 bits are actually used, 25 bits for a 5 by 5
square and 4 bits at the center of each outside edge.  The area covered
and bits represented are as follows:

    Shape              Bit numbers

      *                    28
  * * * * *           5  4  3  2  1
  * * * * *          10  9  8  7  6
* * * S * * *     27 15 14 13 12 11 26
  * * * * *          20 19 18 17 16
  * * * * *          25 24 23 22 21
      *                    29

neibrd - This 1-dimensional array corresponds exactly to the Board
structure (above).  The purpose of this array is to test a 1-dimensional
position and determine if the position is on one edge or corner of the
board.  This array can also be used to test for a position on row 1, 2,
3, or 4 from any side of the board.

army - This structure is indexed by army number, and contains
information about each army such as size, color, first (first army man's
board position), last (last army man's board position), fp (forward
pointer to the next army), bp (backward pointer to the previous army),
lib (liberties), friend (number of bordering friendly stones), enemy
(number of bordering enemy stones), armies (number of neighboring
non-blank armies -- not implemented yet), and rollcall (scratchpad
variable).  For blank armies the number of liberties is not used, the
number of friendly stones is the number of neighboring white stones, and
the number of enemy stones is the number of neighboring black stones.
For non-blank armies the number of friendly stones is not used.
________________________________________________________________________

The following additional data structures are used by these programs and
also made available for convenience, as follows:

neighbors - This structure contains the output of the getneighbors
procedure:  the number and position of non-blank neighbors to a
specified 1-dimensional position, sorted by army number (which makes it
easier to tell if different neighbors are in the same army).

neighborcount - This array is used with the game.neibrd array to quickly
determine how many valid neighboring positions there are to a given
stone position.  This array, neighborcount[0..15], merely translates a
binary number from 0 to 15 into the number of bits that are set in that
binary number.

mstatus - This structure contains ko information and capture information
based on the last stone played on the board.

The ko position (movestatus.kospot) is set by the capture procedure to
the 1-dimensional position of a 1-man army that was just captured.  If
no army was captured by the last move then this variable is set to -1.
Also if the size of the captured army is greater than one then this
variable is set to -1.  The addastone procedure sets this variable to
-1, but the addastone procedure calls the capture procedure if a capture
is made by a move.

The capture information (movestatus.captures) is set by the addastone
procedure to reflect which of the four possible armies surrounding the
new stone was captured.  This variable is bit encoded.
_________________________________________________________________________

To use all of the above utility programs and data structures the
following C code is necessary:

#include <goprocs.h>
#include <gomoves.h>

The following C code creates/allocates a game structure called g at
compile time (this is the responsibility of the user):

  game g;

Black, White, and Blank are C parameters that define the only valid
states of a board position.  Black and White define the only valid
stone colors and player designations.  The following C code changes
the designated player in the game structure pointed to by g:

  g->who = otherside[g->who];

The following C code will walk through all armies (black,white, and blank),
given a game structure pointer (g):

  int armynumber;
  armynumber = mygame.army[mygame.avail].bp; /* back pointer is first army */
  while (armynumber > 0) {
    if (mygame.army[armynumber].size > 0) {
      (* code to process each army goes here *)
    }
    armynumber = mygame.army[armynumber].bp;
  }

The following C code will walk through an army, given a game structure
pointer (g) and the position of any stone in the army (s):

  int stone;
  stone = g->army[g->armymen[s]].first; /* first man in army */
  while (stone >= 0) {
    (* code to process each army man goes here *)
    stone = g->armylnk[stone]; /* next man in army */
  }

The following C code will obtain the positions of the neighbors to a
given stone position (s), given a game structure pointer (g) and visit
each of the non-blank neighboring positions in order of their army number:

  neighbors nabor; int n;
  getneighbors(g,&nabor,s);     /* get the neighbors */
  for (n = 0; n < nabor.neis; n++) {  /* go through neighbors */
    if (g->board[s] != Blank) {
      (* code to process non-blank neighbors to stone s goes here *)
  } }
________________________________end of document___________________________

/* File goprocs.h
   Developer:  James R. Logan Jr. (BYU), Advisor:  Dr. Lynn H. Beus (BYU)
   email address:  loganj@byuvax.bitnet
   (801) 378-3617

   Structures required by the go program utilities.
   This code is not machine dependent.
*/

#define firstelement 0
#define brdsize 19
#define maxstones 362
#define maxarmies 362
#define White 0
#define Black 1
#define Blank 2

typedef struct {
  int
  color,   /* army color */
  size,    /* army size  */
  first,   /* first armyman's position */
  last,    /* last armyman's position */
  fp,      /* forward pointer to next army */
  bp,      /* backward pointer to last army */
  lib,     /* number of liberties, for nonblank armies only */
  friend,  /* number of neighboring White stones, for blank armies only */
  enemy,   /* number of enemy stones for nonblank armies, or Black stones */
  armies,  /* number of neighboring armies */
  rollcall;/* scratch pad variable */
} armystruct;

typedef struct {
  int kospot;    /* valid ko position, or -1, result of last move */
  long captures; /* bit 1 is capture on the west (left)
                    bit 2 is capture on the east (right)
                    bit 3 is capture on the south (down)
                    bit 4 is capture on the north (up) */
} mstatus;

typedef struct {
  int
  who,      /* White for computer's level, Black for opponent */
  avail,    /* next available army number */
  maxstone, /* size of 1-dimensional board */
  width,    /* width of 1-dimensional board */
  movecount,/* number of stones played on the board */
  deadwhite,/* number of captured white stones */
  deadblack,/* number of captured black stones */
  debug;    /* debug flags */
  char
  board[maxstones],    /* computer = White, opponent = Black */
  rowbrd[maxstones],   /* translates 1-dimension row to 2-dimension */
  colbrd[maxstones],   /* translates 1-dimension col to 2-dimension */
  whitecount[maxstones],/* count of white stones in whitepats */
  blackcount[maxstones];/* count of black stones in blackpats */
  int
  armymen[maxstones],  /* men of same army have same num */
  armylnk[maxstones];  /* link to next army member */
  long
  whitepats[maxstones],/* bit pattern of white stones in 5 by 5 square */
  blackpats[maxstones],/* bit pattern of black stones in 5 by 5 square */
  neibrd[maxstones];   /* n by n bit encoded valid neighbors array */
  mstatus movestatus;  /* ko position and capture info */
  armystruct army[maxarmies];
} game;

typedef struct {
  int
  neis,
  stone[4],
  direction[4];
        /* bit 1 is neighbor on the west (left)
           bit 2 is capture on the east (right)
           bit 3 is capture on the south (down)
           bit 4 is capture on the north (up) */
} neighbors;
/* File gomoves.h
   Developer:  James R. Logan Jr. (BYU), Advisor:  Dr. Lynn H. Beus (BYU)
   email address:  loganj@byuvax.bitnet
   (801) 378-3617
*/

#define maxsons 5  /* maximum depth of alpha-beta tree */
#define maxlevs 5  /* must be odd, maximizing level for best odds? */

typedef struct {
  int s,pv;       /* alpha beta stone position and board value */
} nodestruct;

typedef struct {  /* children of node in alpha-beta tree */
  int nodepnt,nodecnt;
  mstatus status; /* board status resulting from stone */
  nodestruct nodes[maxsons];
} sons;
/* File gomain.c
   Developer:  James R. Logan Jr. (BYU), Advisor:  Dr. Lynn H. Beus (BYU)
   email address:  loganj@byuvax.bitnet
   (801) 378-3617

   This procedure provides the interface between the Mac and the operator.
   This code is machine dependent.
*/

#include <qd.h>
#include <win.h>
#include <menu.h>
#include <event.h>
#include <te.h>
#include <stdio.h>
#include <goprocs.h>

#define lastmenu 5
#define applemenu 1
#define filemenu 256
#define gamemenu 257
#define playermenu 258
#define displaymenu 259

FILE *fp,*gp;
windowptr whichwindow;
menuhandle mymenus[lastmenu+1];
boolean doneflag,temp;
eventrecord myevent;
long blinktime;
int code,refnum;
int themenu,theitem,
  activeplayer,blinkstate,blinkcolor,blinkstone,drawnumber,
  lookahead,play,playfile,stonestate;

game mygame;

extern initgame();
extern int addastone(),moves();
extern char coltable[19];
extern int otherside[2];
extern windowptr mewindow,mywindow;

setupmenus() {
int i;
char appletitle[2];
  initmenus();
  appletitle[0] = applemark; appletitle[1] = 0;
  mymenus[1] = newmenu(applemenu, appletitle);
  addresmenu(mymenus[1], "DRVR");
  mymenus[2] = newmenu(filemenu, "Directives");
  appendmenu(mymenus[2], "Pass;Change Sides;Log to File;Play Log File;Quit");
  mymenus[3] = newmenu(gamemenu, "Game");
  appendmenu(mymenus[3], "Initialize;Begin or Resume;Pause");
  mymenus[4] = newmenu(playermenu, "Players");
  appendmenu(mymenus[4], "Computer Vs Computer;Computer Vs Human;Human Vs Human"
);
  mymenus[5] = newmenu(displaymenu, "Display");
  appendmenu(mymenus[5],
    "Look Ahead Moves;Stone Numbers;Procedures;Stones;Stone Armies;Stone Links;S
tone Patterns;Position Values;Army Info");
  for (i=1; i<=lastmenu; i++) insertmenu(mymenus[i], 0);
  drawmenubar();
  drawnumber = 1;
  checkitem(mymenus[5],2,1);
  lookahead = 0;
  activeplayer = 1;
  checkitem(mymenus[4],activeplayer,1);
}

docommand(themenu, theitem) int themenu, theitem; {
char name[256];
int i;
  switch (themenu) {
  case applemenu:
    getitem(mymenus[1], theitem, name);
    refnum = opendeskacc(name);
    break;
  case filemenu:
    switch (theitem) {
    case 1: /* pass */
      if (play == 0) {
        if ((activeplayer == 2) || (activeplayer == 3)) {
          mygame.who = otherside[mygame.who];
          if (activeplayer == 2) {
            play = 1;
          } else {
            if (mygame.who == White) printf(" White's turn ...\n");
            else printf(" Black's turn ...\n");
          }
        }
      }
      break;
    case 2: /* change sides */
      mygame.who = otherside[mygame.who];
      if (mygame.who == White) printf(" White's turn ...\n");
      else printf(" Black's turn ...\n");
      break;
    case 3:
      if (fp == NULL) {
        checkitem(mymenus[2],3,1);
        createfile();
      } else {
        checkitem(mymenus[2],3,0);
        fclose(fp); fp = NULL;
      }
      break;
    case 4:
      if (gp == 0) {
        if (activeplayer == 2) blinkoff();
        replay();
        if (gp != 0) {
          checkitem(mymenus[2],4,1);
          play = 1;
          playfile = 1;
        }
      } else {
        checkitem(mymenus[2],4,0);
        if (gp != NULL) fclose(gp); gp = NULL;
        play = 0;
        playfile = 0;
        mygame.who = otherside[mygame.who];
        if (mygame.who == White) printf(" White's turn ...\n");
        else printf(" Black's turn ...\n");
        if (activeplayer == 2) blinkon();
      }
      break;
    case 5:
      if (fp != NULL) { fclose(fp); fp = NULL; }
      if (gp != NULL) { fclose(gp); gp = NULL; }
      doneflag = 1;
      break;
    }
    break;
  case gamemenu:
    switch (theitem) {
    case  1:
      play = 0;
      if (playfile != 0) {
        checkitem(mymenus[2],4,0);
        if (gp != NULL) fclose(gp); gp = NULL;
        playfile = 0;
      }
      blinkoff();
      boardsize(&mygame);
      if (activeplayer == 1) {
        if (mygame.who == White) printf(" White's turn ...\n");
        else printf(" Black's turn ...\n");
      } else if (activeplayer == 2) {
        blinkon();
        printf(" Your turn ...\n");
      } else if (activeplayer == 3) printf(" Black's turn ...\n");
      break;
    case  2:
      if ((playfile != 0) || (activeplayer == 1)) play = 1;
      else if (activeplayer == 2) printf(" Your turn ...\n");
      else if (mygame.who == White) printf(" White's turn ...\n");
      else printf(" Black's turn ...\n");
      break;
    case  3: play = 0; break;
    }
    break;
  case playermenu:
    switch (theitem) {
    case  1:
      if (activeplayer == 2) blinkoff();
      checkitem(mymenus[4],activeplayer,0);
      activeplayer = 1;
      checkitem(mymenus[4],activeplayer,1);
      break;
    case  2:
      play = 0;
      blinkstone = -1;
      checkitem(mymenus[4],activeplayer,0);
      activeplayer = 2;
      checkitem(mymenus[4],activeplayer,1);
      break;
    case  3:
      play = 0;
      if (activeplayer == 2) blinkoff();
      checkitem(mymenus[4],activeplayer,0);
      activeplayer = 3;
      checkitem(mymenus[4],activeplayer,1);
      break;
    }
    break;
  case displaymenu:
    switch (theitem) {
    case  1:
      lookahead = 1 - lookahead;
      checkitem(mymenus[5],1,lookahead);
      break;
    case  2:
      drawnumber = 1 - drawnumber;
      checkitem(mymenus[5],2,drawnumber);
      break;
    case  3:
      if ((mygame.debug & 1) == 0) {
        mygame.debug |= 1;
        checkitem(mymenus[5],3,1);
      } else {
        mygame.debug &= ~1;
        checkitem(mymenus[5],3,0);
      }
      break;
    case  4: moveboard(&mygame); break;
    case  5: armyboard(&mygame); break;
    case  6: linkinfo(&mygame); break;
    case  7: pictinfo(&mygame); break;
    case  8: valueboard(&mygame); break;
    case  9: armyinfo(&mygame); break;
  } }
  hilitemenu(0);
}

stoneon() {
  if ((stonestate == 0) && (blinkstone >= 0))
    drawstone(&mygame,blinkstone,blinkcolor);
  stonestate = 1;
}

stoneoff() {
  if ((stonestate != 0) && (blinkstone >= 0))
    undrawstone(&mygame,blinkstone);
  stonestate = 0;
}

blinkon() {
/* Turn on the blink'n blinker if no look ahead in progress. */
  if (lookahead == 0) {
    blinkstate = 1;
    blinktime = tickcount();
} }

blinkoff() { blinkstate = 0; stoneon(); }

blink() {
/* This procedure does the actual blinking of the last stone played */
long now;
  if (blinkstate != 0) {
    now = tickcount();
    if (now > blinktime + 20) {
      if (stonestate != 0) stoneoff(); else stoneon();
      blinktime = now;
} } }

main() {
/* This procedure provides the interface between the Mac and the
   operator */
int i,s;
extern struct qdvar { char dummy[202]; grafptr qdtheport;} qdvars;
#define theport (qdvars.qdtheport)

  openresfile("clock");
  initgraf(&theport);
  initfonts();
  flushevents(everyevent, 0);
  initwindows();
  setupmenus();
  initdialogs(0L);
  initcursor();

  doneflag = 0; play = 0; playfile = 0; blinkstone = -1;
  mygame.debug = 0;
  mygame.width = 19;
  fp = NULL; gp = NULL;
  initgame(&mygame);

  do {
    systemtask();
    blink();
    temp = getnextevent(everyevent, &myevent);
    switch (myevent.what) {
    case mousedown:
      code = findwindow(&myevent.where, &whichwindow);
      switch (code) {
      case inmenubar:
        docommand(menuselect(&myevent.where)); break;
      case insyswindow:
        systemclick(&myevent, whichwindow); break;
      case indrag:
        break;
      case ingrow:
      case incontent:
        if ((play == 0) &&
          ((playfile != 0) || (activeplayer == 2) || (activeplayer == 3))) {
          if (whichwindow == mywindow) {
            blinkoff();
            setport(mywindow);
            globaltolocal(&myevent.where);
            setport(mewindow);
/* place a stone on the board */
            s = (mygame.width - 1 - ((myevent.where.a.v - 2) / 16));
            if (s < 0) s = 0;
            if (s >= mygame.width) s = mygame.width - 1;
            s = s * mygame.width;
            i = (myevent.where.a.h - 2) / 16;
            if (i < 0) i = 0;
            if (i >= mygame.width) i = mygame.width - 1;
            s = s + i;
            s = addastone(&mygame,s);
            if (s >= 0) {
              if (mygame.who == White) printf(" White to %c,%d\n",
                coltable[mygame.colbrd[s]],mygame.rowbrd[s]);
              else printf(" Black to %c,%d\n",
                coltable[mygame.colbrd[s]],mygame.rowbrd[s]);
              if (activeplayer == 2) play = 1;
              mygame.who = otherside[mygame.who];
              if (mygame.who == White) printf(" White's turn ...\n");
              else printf(" Black's turn ...\n");
            } else {
              if (mygame.who == White) printf(" Still White's turn ...\n");
              else printf(" Still Black's turn ...\n");
            }
          }
        }
        break;
/* goaway box is on the modify window only, so save the changes to the
   letter being modified and take down the modify window */
      case ingoaway: break;
      }
      break;
    case mouseup: break;
    case keydown:
    case autokey:
/*      draw_text((char) (255 & myevent.message)); */
      break;
    case activateevt: break;
    case updateevt:
/*      setport(mywindow);
      beginupdate(mywindow);
      endupdate(mywindow);*/
      break;
    }
    if (doneflag == 0) {
      if (gp == NULL) {
        if (playfile != 0) {
          playfile = 0;
          checkitem(mymenus[2],4,0);
        }
        if (((activeplayer == 1) || (activeplayer == 2)) && (play != 0)) {
          blinkcolor = mygame.who;
          blinkstone = moves(&mygame);
          if (mygame.who == White) printf(" White's turn ...\n");
          else printf(" Black's turn ...\n");
          if (activeplayer == 2) {
            play = 0;
            blinkon();
        } }
      } else if (play != 0) replay();
    }
  } while (doneflag == 0);
}
/* File go.c
   Developer:  James R. Logan Jr. (BYU), Advisor:  Dr. Lynn H. Beus (BYU)
   email address:  loganj@byuvax.bitnet
   (801) 378-3617

   These routines and the routines in gomoves.c determine the strategy of
   the go program.  This code is not machine dependent.
*/

#include <qd.h>
#include <pack.h>
#include <win.h>
#include <dialog.h>
#include <stdio.h>
#include <goprocs.h>
#include <gomoves.h>

extern char coltable[19];
extern int activeplayer,blinkstone,looking,play,otherside[2];
extern game mygame;
extern int addastone();
extern initialize(),removeastone(),capture(),uncapture(),
  getneighbors(),allarmies(),linkarmies(),buildarmies(),
  armymerge(),topicture(),frompicture(),moveboard(),
  armyboard(),linkinfo(),armyinfo(),pictinfo(),neighborinfo();

overlay "gamestuff"

#define allyweight 4
#define blankweight 1
#define enemyweight 2
#define libertiesweight 1
#define squareweight 4

#define edgevalue 1
#define openvalue 2  /* ordinary unoccupied square, not on edge */
#define cornervalue 10
#define connectvalue 3
#define midneivalue 6
#define midrowvalue 8
#define row3value 4
#define row5value 3

pattern greypat = {0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92};

rect screenrect;
windowrecord merecord,myrecord,drecord;
windowptr mewindow,mywindow = {0L},dummywindow;

sons root[maxlevs];

mstatus status;

int
  init[maxstones],value[maxstones],  /* initial position values */
  color,stone,nextmove,passcount,
  searchdepth = {1},searchwidth = {1},work;

initgame(g) game *g; {
int i,j,brdsze;
char dummyscreen[20000];
  if (mywindow == 0L) {
/* calculate the size of the window for the game board, and display */
    brdsze = 16 + 8 * (g->width - 1);
    setrect(&screenrect,351-brdsze,181-brdsze,351+brdsze,181+brdsze);
    mywindow = newwindow(&myrecord, &screenrect, "", 1, 2,
      (long)-1, 0, (long)0);
    dummywindow = newwindow(&drecord, &screenrect, "", 0, 2,
      (long)-1, 1, (long)0);
    drecord.port.portbits.baseaddr = &dummyscreen;
    setrect(&screenrect, 0, 21, 188, 340);
    mewindow = newwindow(&merecord, &screenrect, "", 1, 2,
      (long)-1, 0, (long)0);
  } else {
    disposewindow(mywindow);
    brdsze = 16 + 8 * (g->width - 1);
    setrect(&screenrect,351-brdsze,181-brdsze,351+brdsze,181+brdsze);
    mywindow = newwindow(&myrecord, &screenrect, "Go", 1, 3,
                     (long)-1, 0, (long)0);
  }
  if ((g->debug & 1) != 0) { printf("Initgame: \n"); }
  setport(mywindow);
  textmode(srcxor);
  backpat(&greypat);
  setorigin(-12,0);
  brdsze = 4 + 16 * g->width;
  setrect(&screenrect,0,0,400,brdsze);
  eraserect(&screenrect);
  setport(mewindow);
  textsize(9);
  initialize(g);  /* initialize the game structure for the go procs */
  g->who = Black; /* first move is black's */
  passcount = 0;  /* number of consecutive passes in the game */
  blinkstone = -1; /* indicate no stone to blink yet */
/* set the initial value of each position, first the whole board */
  for (i = firstelement; i < g->maxstone; i++) { init[i] = openvalue; }
/* set the initial value of the edges of the board */
  j = firstelement;
  for (i = firstelement; i < g->width; i++) {
    init[j] = edgevalue; init[j+g->width-1] = edgevalue;
    init[i] = edgevalue; init[g->maxstone-g->width+i] = edgevalue;
    j = j + g->width;
  }
/* Set the initial value for row 3 all around the board */
  if (g->width >= 7) {
    j = 2*g->width+3;
    for (i = firstelement; i < g->width-6; i++) {
      init[j] = row3value; init[g->maxstone-j-1] = row3value;
      j = j + 1; }
    j = 3*g->width+2;
    for (i = firstelement; i < g->width-6; i++) {
      init[j] = row3value; init[j+g->width-5] = row3value;
      j = j + g->width; }
  }
/* Set the value for the middle of row 3 around the edge of the board */
  if (g->width >= 9) {
    j = (2*g->width)+(g->width/2);
    init[j] = midrowvalue;
    init[g->maxstone-1-j] = midrowvalue;
    j = ((g->width / 2) * g->width) + 2;
    init[j] = midrowvalue;
    init[g->maxstone-1-j] = midrowvalue;
  }
/* Set the initial value for row 5 all around the board, but not in 5 corner */
  if (g->width >= 11) {
    j = 4*g->width+5;
    for (i = firstelement; i < g->width-10; i++) {
      init[j] = row5value; init[g->maxstone-j-1] = row5value;
      j = j + 1; }
    j = 5*g->width+4;
    for (i = firstelement; i < g->width-10; i++) {
      init[j] = row5value; init[j+g->width-9] = row5value;
      j = j + g->width; }
  }
/* Set the initial value for 3 rows in from each corner */
  if (g->width >= 5) {
    init[2*g->width+2] = cornervalue;
    init[3*g->width-3] = cornervalue;
    init[g->maxstone-2*g->width-3] = cornervalue;
    init[g->maxstone-3*g->width+2] = cornervalue;
  }
/* Set the initial value for 2 jump neighbors from mid row 3 */
  if (g->width == 19) {
    init[44] = midneivalue; init[50] = midneivalue;
    init[116] = midneivalue; init[130] = midneivalue;
    init[230] = midneivalue; init[244] = midneivalue;
    init[310] = midneivalue; init[316] = midneivalue;
  }
  init[0] = 0; init[g->width-1] = 0;
  init[g->maxstone-g->width] = 0; init[g->maxstone-1] = 0;
  if ((g->debug & 1) != 0) { printf("Initgame end.\n"); }
}

int moves(g) game *g; {
/* This procedure receives control when the computer needs to make a move,
   and returns the 1-dimensional position selected by the computer.
   If g->who == White a move is computed for the computer, else a move is
   computed for the opponent.  A legal move is always added to the board */
int s; sons *sonsp;
  alphabeta(); /* look ahead for the best move */
  if (nextmove >= 0) {
    s = addastone(g,nextmove); /* add this move to the game board */
    if (s >= 0) {
      if (g->who == White) printf(" White to %c,%d\n",
          coltable[g->colbrd[s]],g->rowbrd[s]);
      else printf(" Black to %c,%d\n",
          coltable[g->colbrd[s]],g->rowbrd[s]);
    }
    passcount = 0;
  } else {
    s = -1;
    if (activeplayer == 1) passcount = passcount + 1;
    if (g->who == White) printf(" White passes\n");
    else printf(" Black passes\n");
  }
  g->who = otherside[g->who];
  if ((activeplayer == 1) && (passcount >= 2)) {
    play = 0;
    printf(" Play stopped after two passes!\n");
  }
  return s;
}

alphabeta() {
/* This procedure is the top level of the alpha-beta cutoff look ahead
   procedure.  Alpha and beta are initialized here. */
int a,b,dummy; int sonsp;
  looking = 1;   /* indicate look ahead in progress */
  nextmove = -1; /* default next move is a pass */
  sonsp = 0;     /* point to root of tree */
  a = -9999; b = 9999; /* alpha-beta initialization */
  dummy = abcontrol (sonsp,a,b,mygame.who);
  looking = 0;
}

int abcontrol (tr,a,b,wh) int tr,a,b,wh; {
/* This procedure performs the actual alpha-beta search.  Inputs are
   tree level, Alpha, Beta, and who's move it is.  The present
   implementation does not dynamically allocate the tree. */
int ta,tb,ev,junk,r,c,v;
  if (tr >= searchdepth) {   /* last level of tree? */
    ev = eval(searchdepth);    /* yes, compute the position value */
    if ((mygame.debug & 0xF) != 0) {
      printf("\nabcontrol - level, node, value: %d %d %d\n",
        searchdepth-1,root[searchdepth-1].nodepnt-1,ev);
    }
    return (ev);        /* return the best position value */
  } else if (wh == mygame.who) {    /* for maximizing level */
    bestmoves(&mygame,tr);   /* fill in the best moves */
    while ((root[tr].nodepnt < root[tr].nodecnt) && (a < b)) {
      junk = addastone(&mygame,root[tr].nodes[root[tr].nodepnt].s);
      root[tr].status = mygame.movestatus;
      root[tr].nodepnt = root[tr].nodepnt + 1;
      ta = abcontrol (tr + 1, a, b, otherside[wh]);
      if (ta > a) {
        a = ta; /* new alpha value */
        if (tr <= 0) { nextmove = root[tr].nodes[root[tr].nodepnt-1].s; }
      }
      removeastone(&mygame,root[tr].nodes[root[tr].nodepnt-1].s,root[tr].status)
;
    }
    return (a);
  } else {   /* for minimizing level */
    bestmoves(&mygame,tr);   /* fill in the best moves */
    while ((root[tr].nodepnt < root[tr].nodecnt) && (a < b)) {
      mygame.who = wh;                      /* change sides */
      junk = addastone(&mygame,root[tr].nodes[root[tr].nodepnt].s);
      root[tr].status = mygame.movestatus;
      mygame.who = otherside[mygame.who];   /* change sides back */
      root[tr].nodepnt = root[tr].nodepnt + 1;
      tb = abcontrol (tr + 1, a, b, otherside[wh]);
      if (tb < b) { b = tb; } /* new beta value */
      removeastone(&mygame,root[tr].nodes[root[tr].nodepnt-1].s,root[tr].status)
;
    }
    return (b);
  }
}

int eval(sonsp) int sonsp; {
/* This procedure evaluates the worth of look ahead moves during the alpha
   beta search - not a well defined process.  Need a lot of work here!
   The game structure fields don't seem to represent enough higher level
   (human) abstractions to play a good game yet.  The evaluation here
   is based on the number of armies, eyes, and total number of liberties
   for both sides.  Variables beginning with an 'a' are ally values, 'e' are
   enemy values. */
int n,v,pv,allies,enemies,alibs,elibs,wh,tb;
  if ((mygame.debug & 1) != 0) { printf("eval: \n"); }
  pv = 0; allies = 0; enemies = 0; alibs = 0; elibs = 0;
  wh = 1;
  for (tb = 0; tb < sonsp; tb++) {    /* traverse the tree */
    pv = pv + root[tb].nodes[root[tb].nodepnt-1].pv * wh;
    tb = tb + 1;
    wh = -wh;     /* switch players at each level of the tree */
  }
  n = mygame.army[mygame.avail].bp;   /* visit each army */
  while (n > 0) {
    if (mygame.army[n].size > 0) {
      if (mygame.army[n].color == Blank) {   /* blank army is an eye? */
        if (mygame.army[n].enemy <= 0) {
          if (mygame.who == White) allies = allies + 1; /* ally eyes */
          else enemies = enemies + 1;                   /* enemy eyes */
        } else if (mygame.army[n].friend <= 0) {
          if (mygame.who == White) enemies = enemies + 1;/* ally eyes */
          else allies = allies + 1;                     /* enemy eyes */
        }
      } else if (mygame.army[n].color == mygame.who) {
        alibs = alibs + mygame.army[n].lib;
      } else {
        elibs = elibs + mygame.army[n].lib;
      }
    }
    n = mygame.army[n].bp;
  }
  v = allies * allyweight - enemies * enemyweight +
       libertiesweight * alibs - libertiesweight * elibs +
       pv * squareweight;
  if ((mygame.debug & 1) != 0) { printf("eval.\n"); }
  return (v);
}

boardsize(g) game *g; {
/* This procedure handles the game initialization dialog */
int the_item,item_type,value;
handle item_handle;
rect drect,item_box;
char item_text[64];
dialogptr mydialog;
  setrect(&drect,-16,28,440,200);
  copybits(&mywindow->portbits,&dummywindow->portbits,
        &drect,&drect,srccopy,(long)0);
  mydialog = getnewdialog(9507,(long) 0,(long)-1);
  getditem(mydialog,3,&item_type,&item_handle,&item_box);
  sprintf(item_text,"%d",g->width);
  setitext(item_handle,item_text);
  getditem(mydialog,4,&item_type,&item_handle,&item_box);
  sprintf(item_text,"%d",searchwidth);
  setitext(item_handle,item_text);
  getditem(mydialog,5,&item_type,&item_handle,&item_box);
  sprintf(item_text,"%d",searchdepth);
  setitext(item_handle,item_text);
  do {
    modaldialog((long) 0,&the_item);
    if (the_item == 1) {
      getditem(mydialog,3,&item_type,&item_handle,&item_box);
      getitext(item_handle,&item_text);
      sscanf(item_text,"%d",&value);
      if ((value > 2) && (value < 20)) g->width = value;
      getditem(mydialog,4,&item_type,&item_handle,&item_box);
      getitext(item_handle,&item_text);
      sscanf(item_text,"%d",&value);
      if ((value > 0) && (value <= maxsons)) searchwidth = value;
      getditem(mydialog,5,&item_type,&item_handle,&item_box);
      getitext(item_handle,&item_text);
      sscanf(item_text,"%d",&value);
      if ((value > 0) && (value <= maxlevs)) searchdepth = value;
    }
  } while ((the_item != 1) && (the_item != 2));
  closedialog(mydialog);
  copybits(&dummywindow->portbits,&mywindow->portbits,
        &drect,&drect,srccopy,(long)0);
  if (the_item == 1) initgame(g);
}

valueboard(g) game *g; {
int i,s;
  s = 0;
  while (s < g->maxstone) {
    for (i = firstelement; i < g->width; i++) { printf("%3d",value[s]); s = s +
1; }
    printf("\n");
} }
/* File gomoves.c
   Developer:  James R. Logan Jr. (BYU), Advisor:  Dr. Lynn H. Beus (BYU)
   email address:  loganj@byuvax.bitnet
   (801) 378-3617

   This routine determines the best n moves available when a move is
   being selected by the computer.  This code is not machine dependent.
*/

#include <goprocs.h>
#include <gomoves.h>

#define maxsons 5
#define maxlevs 5     /* must be odd, maximizing level for best odds */

#define allyweight 4
#define blankweight 1
#define enemyweight 2
#define libertiesweight 1
#define squareweight 4

/* The following 32 bit values represent patterns in a 5 by 5 diamond shape,
   centered around a given board position (s).  Only 29 bits are actually
   used, 25 bits for a 5 by 5 square and 4 bits at the center of each
   outside edge.  The area covered and bits represented are as follows:

      *                    28
  * * * * *           5  4  3  2  1
  * * * * *          10  9  8  7  6
* * * S * * *     27 15 14 13 12 11 26
  * * * * *          20 19 18 17 16
  * * * * *          25 24 23 22 21
      *                    29
*/
#define square3 0x739C0L
#define square5 0x0E8C62EL
#define diamond5 0x477DC4L
#define circle7 0x1F100011L

#define firstmoves 17 /* number of moves before middle game */

#define attackvalue 14
#define circle7value 2
#define edgevalue 1
#define edgecutvalue 10
#define edgejoinvalue 16
#define openvalue 2  /* ordinary unoccupied square, not on edge */
#define capturevalue 24
#define cornervalue 10
#define connectvalue 3
#define cutvalue 3
#define hitarivalue 18
#define invadevalue 20 /* must be 4 less than capture value */
#define midneivalue 6

#define midrowvalue 8
#define protvalue 24
#define retreatsize 18
#define row3value 4
#define row5value 3
#define square5value 1
#define runtoedgevalue 12
#define territoryvalue 4

extern sons root[maxlevs];
extern int
  otherside[2],
  init[maxstones],value[maxstones],  /* initial position values */
  searchdepth,searchwidth;

bestmoves(g,sonsp) game *g; int sonsp; {
/* This procedure determines the best n moves available using a point
   scheme with weights for different patterns and situations.  This
   routine does not explicitly check the validity of each move (bad
   moves are possible if the weights are not properly set. */
char *enmcount,*fricount;
int a1,a2,blankfris,blankenms,frilibs,lastus,lastthem,
  ha,pa,snapstone,snaplibs,i,n,s,s1,us,them,
  captsize,protsize,totallibs,hitarisize,hitarisave,neiarms,neienms,node;
long fripats,enmpats,neipats;
neighbors nabor,nextnabor;
/* First initialize all the best moves to pass */
  if ((g->debug & 1) != 0) { printf("bestmoves: \n"); }
  root[sonsp].nodecnt = 0; root[sonsp].nodepnt = 0;
  for (node = firstelement; node < searchwidth; node++) {
    root[sonsp].nodes[node].pv = 0;
    root[sonsp].nodes[node].s = -1;   /* default is to pass */
  }
/* Evaluate each square of the board and assign a value to it */
  for (i = firstelement; i < g->maxstone; i++) {
    value[i] = 0;          /* default is not blank, so no value */
    if ((g->board[i] == Blank) && (i != g->movestatus.kospot)) {
      neipats = g->neibrd[i];
      a1 = g->armymen[i];    /* blank army number */
      if (g->who == White) {
        fricount = &g->whitecount[0]; enmcount = &g->blackcount[0];
        fripats = g->whitepats[i]; enmpats = g->blackpats[i];
        blankfris = g->army[a1].friend; blankenms = g->army[a1].enemy;
      } else {
        fricount = &g->blackcount[0]; enmcount = &g->whitecount[0];
        fripats = g->blackpats[i]; enmpats = g->whitepats[i];
        blankfris = g->army[a1].enemy; blankenms = g->army[a1].friend;
      }
      getneighbors(g,&nabor,i);  /* get the neighbors */
      if ((g->army[a1].size > g->maxstone-4) ||
         ((blankfris > 0) && (blankenms > 0))) {
        if ((init[i] > openvalue) &&
          (((fripats & square3) != 0) || ((enmpats & square3) != 0)) ) {
          value[i] = openvalue;
        } else {
          value[i] = init[i];      /* default value */
        }
      }
/* territory value, more territory value for empty diamonds
   some code was removed here too */
      if (((fripats & diamond5) == 0) &&
          ((enmpats & diamond5) == 0) &&
          ((neipats & 0x0F0) == 0x0F0)) {
            value[i] = value[i] + territoryvalue;
      } else {
        us = 0; them = 0; captsize = 0; protsize = 0;
        lastus = -1; lastthem = -1; totallibs = g->army[a1].size - 1;
        frilibs = 0; hitarisize = 0; hitarisave = 0;
        neiarms = 0; neienms = 0; ha = -1; pa = -1;
        for (n = firstelement; n < nabor.neis; n++) { /* count new army neighbor
s */
          s = nabor.stone[n];              /* neighbors row & column */
          a2 = g->armymen[s];              /* neighbor army number */
          if (g->board[s] == Blank) {
            frilibs = frilibs + 1;
          } else {
            if (g->army[a2].color == g->who) {
              frilibs = frilibs + g->army[a2].lib - 1;
              totallibs = totallibs + g->army[a2].lib - 1;
            }
            if (g->army[a2].lib == 1) {
              if (g->army[a2].color == g->who) {
                protsize = protsize + g->army[a2].size;
                pa = a2;
              } else {
                captsize = captsize + g->army[a2].size;
              }
/* Check for hitari ...
   If the enemy can be hitari'd add in the points, if we can be hitari'd
   add in the points if we can protect against hitari without using up
   our eyes and if the blank army has more of our stones as neighbors */
            } else if (g->army[a2].lib == 2) {
              if (g->army[a2].color == otherside[g->who]) {
                hitarisize = hitarisize + g->army[a2].size;
              } else if ((g->army[a2].color == g->who) &&
                (g->army[a1].enemy > 0) && (blankfris > 1)) {
                hitarisave = hitarisave + g->army[a2].size;
                ha = a2;
              }
            }
/* Don't count neighbors in the same amry more than once in neiarms,
   and don't count armies in trouble */
            if (g->board[s] == g->who) {
              us = us + 1;
              if (((lastus < 0) || (a2 != lastus)) &&
                   (a2 != ha)) {
                neiarms = neiarms + 1;
                lastus = a2;
              }
            } else {
              them = them + 1;
              if ((lastthem < 0) || (a2 != lastthem)) {
                neienms = neienms + 1;
                lastthem = a2;
              }
            }
          }
        }
/* This is the place!  I cut lots of strategic code from here.  If you want
   to improve the ability of this program to play go, this is the place to
   do it. */


/* lower the value of a square controlled */
        if (((neipats & 0xF00) != 0xF00) &&    /* row 3 test */
            ((neipats & 0x0F0) == 0x0F0) &&
            (them > 0) && ((fripats & diamond5) == 0)) value[i] = 0;
        if ((us > 2) && (them <= 0)) { value[i] = 0; }
        if ((us > 1) && (neiarms < us)) { value[i] = 0; }
        if ((them > 2) && (us <= 0)) { value[i] = 0; }
/* lower the value of a square that can be hitari'd on the next move */
        if ((us <= 0) && (them >= nabor.neis-2)) {
          value[i] = (value[i] / 2) - 1;
        }
/* increase the value of a position that protects us from hitari, unless
   that position is already controlled by us */
        if ((hitarisave > 0) && (nabor.neis > 3) && (frilibs > 1) &&
            ((us < 3) || (them > 0))) {
          value[i] = value[i] + hitarisave + retreatsize + neiarms - us;
        }
/* increase the value of a square that hitari's the enemy */
        if (hitarisize > 0) {
          if (frilibs > 1) {
            value[i] = value[i] + hitarisize - us;
            if (nabor.neis > 3) {
              value[i] = value[i] + hitarivalue;
            }
          } else if ((snaplibs > 0) && (snaplibs < 3)) {
              value[i] = value[i] + hitarisize + capturevalue +
                hitarivalue + neiarms + snaplibs;
        } }
/* protect if we pick up at least 3 liberties, or 2 liberties without
   having to move on the edge of the board and without having to move
   into our eyes */
        if ((protsize > 0) && (blankfris > 1) &&
            ((frilibs > 2) || ((frilibs > 1) && (nabor.neis > 3)))) {
          value[i] = value[i] + protsize + protvalue - us;
        }
/* don't move to the edge unless someone else is there */
        if (((fripats & square3) == 0) && (nabor.neis < 4)) { value[i] = 0; }
/* lower the value of a square we are next to if the enemy is not near */
        if (((enmpats & square3) == 0) && ((fripats & square3) != 0)) {
          if (nabor.neis > 3) value[i] = value[i] / 2; else value[i] = 0;
        }
/* Increase the value of moves that extend influence into unoccupied territory *
/
        if ((g->movecount >= firstmoves) &&
            ((fripats & square3) == 0) && ((enmpats & square3) == 0)) {
          if (((fripats & square5) != 0) && ((enmpats & square5) != 0)) {
            value[i] = value[i] + square5value;
          } else if (((fripats & square5) == 0) && ((enmpats & square5) == 0)) {
            if (((fripats & circle7) != 0) && ((enmpats & circle7) != 0))
              value[i] = value[i] + circle7value;
          } }
/* don't move to a square surrounded by enemy unless we have a capture */
        if (captsize > 0) {
          value[i] = value[i] + capturevalue + captsize;
        } else if (them >= nabor.neis) {
          value[i] = 0;
        }
      }
/* There are three situations we move into:  1 is during the first
   moves of the game when a large blank army occupies most of the
   board; 2 is into a blank army that borders on computer and enemy
   stones; 3 is into an eye if the value high enough to indicate a
   capture or protection required, if not a KO situation. */
      if (value[i] > 0) {
        if ((i != g->movestatus.kospot) &&
            ((g->army[a1].size > g->maxstone-4) ||
            ((blankfris > 0) && (blankenms > 0)) ||
            (value[i] > invadevalue))) {
          s = i; node = firstelement;
          while ((node < searchwidth) && (s >= 0)) {
            if (value[s] > root[sonsp].nodes[node].pv) {
              root[sonsp].nodes[node].pv = value[s];
              s1 = root[sonsp].nodes[node].s;   /* save the old value */
              root[sonsp].nodes[node].s = s;    /* use the better value */
              s = s1;                      /* continue with old value */
            }
            node = node + 1;
          }
          root[sonsp].nodecnt = root[sonsp].nodecnt + 1;
        }
      }
    }
  }
  if (root[sonsp].nodecnt > searchwidth) { root[sonsp].nodecnt = searchwidth; }
  if ((g->debug & 1) != 0) { printf("bestmoves end.\n"); }
}
/* File goprocs.c
   Developer:  James R. Logan Jr. (BYU), Advisor:  Dr. Lynn H. Beus (BYU)
   BYU Computer Science
   232 TMCB
   Provo, Utah  84602
   email address:  loganj@byuvax.bitnet
   (801) 378-3617

   These procedures are go playing utility procedures that can be used by
   a go playing progam to do all the house keeping during a game of go.
   These procedures a based on a 1-dimensional representation of the game.
   This code is not machine dependent, except for the drawing routines at
   the end.
*/

#include <qd.h>
#include <win.h>
#include <stdio.h>
#include <goprocs.h>

#define patwidth 5

extern FILE *fp;
extern int drawnumber,lookahead,otherside[2];
extern windowptr mewindow,mywindow;

overlay "gostuff"

char coltable[19] = {'A','B','C','D','E','F','G','H','J','K','L','M','N','O','P'
,'Q','R','S','T'};
int picset[29] =
  {0x50,0x41,0x40,0x42,0x60,
   0x14,0x05,0x04,0x06,0x24,
   0x10,0x01,0x00,0x02,0x20,
   0x18,0x09,0x08,0x0A,0x28,
   0x90,0x81,0x80,0x82,0xA0,
   0x100,0x200,0x400,0x800};
pattern whitepat = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
pattern blackpat = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};

int looking,stonenumber,otherside[2],
  wrkbrd[maxstones];

initialize (g) game *g; {
/* This procedure initializes the game structure supplied by the caller */
int i,j,r1,c1,w1,w2,w3,w4,w5,w6,w7,w8;
  if ((g->debug & 1) != 0) printf("initializing, board size %d.\n",g->width);
/* initialize other side table */
  otherside[White] = Black; otherside[Black] = White;
/* initialize game parameters */
  g->movecount = 0;
  g->deadwhite = 0; g->deadblack = 0;
  w1 = g->width; w2 = w1 + w1; w3 = w2 + w1; w4 = w3 + w1;
  w5 = w4 + w1; w6 = w5 + w1; w7 = w6 + 1; w8 = w7 + 1;
  g->maxstone = w1 * w1; g->movestatus.kospot = -1;
/* initialize the neighbor board */
  for (i = firstelement; i < g->maxstone; i++) g->neibrd[i] = 0x0FFFFFFFFL;
  j = firstelement;
  for (i = firstelement; i < g->width; i++) {
    g->neibrd[j] = g->neibrd[j] & 0x0EEEEEEEEL;
    g->neibrd[j+w1-1] = g->neibrd[j+w1-1] & 0x0DDDDDDDDL;
    g->neibrd[i] = g->neibrd[i] & 0x0BBBBBBBBL;
    g->neibrd[g->maxstone-w1+i] = g->neibrd[g->maxstone-w1+i] & 0x077777777L;
    if (w1 > 1) {
      g->neibrd[j+1] = g->neibrd[j+1] & 0x0EEEEEEEFL;
      g->neibrd[j+w1-2] = g->neibrd[j+w1-2] & 0x0DDDDDDDFL;
      g->neibrd[i+w1] = g->neibrd[i+w1] & 0x0BBBBBBBFL;
      g->neibrd[g->maxstone-w2+i] = g->neibrd[g->maxstone-w2+i] & 0x07777777FL;
    }
    if (w1 > 2) {
      g->neibrd[j+2] = g->neibrd[j+2] & 0x0EEEEEEFFL;
      g->neibrd[j+w1-3] = g->neibrd[j+w1-3] & 0x0DDDDDDFFL;
      g->neibrd[i+w2] = g->neibrd[i+w2] & 0x0BBBBBBFFL;
      g->neibrd[g->maxstone-w3+i] = g->neibrd[g->maxstone-w3+i] & 0x0777777FFL;
    }
    if (w1 > 3) {
      g->neibrd[j+3] = g->neibrd[j+3] & 0x0EEEEEFFFL;
      g->neibrd[j+w1-4] = g->neibrd[j+w1-4] & 0x0DDDDDFFFL;
      g->neibrd[i+w3] = g->neibrd[i+w3] & 0x0BBBBBFFFL;
      g->neibrd[g->maxstone-w4+i] = g->neibrd[g->maxstone-w4+i] & 0x077777FFFL;
    }
    if (w1 > 4) {
      g->neibrd[j+4] = g->neibrd[j+4] & 0x0EEEEFFFFL;
      g->neibrd[j+w1-5] = g->neibrd[j+w1-5] & 0x0DDDDFFFFL;
      g->neibrd[i+w4] = g->neibrd[i+w4] & 0x0BBBBFFFFL;
      g->neibrd[g->maxstone-w5+i] = g->neibrd[g->maxstone-w5+i] & 0x07777FFFFL;
    }
    if (w1 > 5) {
      g->neibrd[j+5] = g->neibrd[j+5] & 0x0EEEFFFFFL;
      g->neibrd[j+w1-6] = g->neibrd[j+w1-6] & 0x0DDDFFFFFL;
      g->neibrd[i+w5] = g->neibrd[i+w5] & 0x0BBBFFFFFL;
      g->neibrd[g->maxstone-w6+i] = g->neibrd[g->maxstone-w6+i] & 0x0777FFFFFL;
    }
    if (w1 > 6) {
      g->neibrd[j+6] = g->neibrd[j+6] & 0x0EEFFFFFFL;
      g->neibrd[j+w1-7] = g->neibrd[j+w1-7] & 0x0DDFFFFFFL;
      g->neibrd[i+w6] = g->neibrd[i+w6] & 0x0BBFFFFFFL;
      g->neibrd[g->maxstone-w7+i] = g->neibrd[g->maxstone-w7+i] & 0x077FFFFFFL;
    }
    if (w1 > 7) {
      g->neibrd[j+7] = g->neibrd[j+7] & 0x0EFFFFFFFL;
      g->neibrd[j+w1-8] = g->neibrd[j+w1-8] & 0x0DFFFFFFFL;
      g->neibrd[i+w7] = g->neibrd[i+w7] & 0x0BFFFFFFFL;
      g->neibrd[g->maxstone-w8+i] = g->neibrd[g->maxstone-w8+i] & 0x07FFFFFFFL;
    }
    j = j + g->width;
  }
/* initialize the row and column arrays */
  i = 0;
  r1 = 1;
  while (i < g->maxstone) {
    for (c1 = 0; c1 < g->width; c1++)
      { g->rowbrd[i] = r1; g->colbrd[i] = c1; i = i + 1; }
    r1 = r1 + 1;
  }
  for (i = firstelement; i < g->maxstone; i++) {
    g->board[i] = Blank; g->whitepats[i] = 0; g->blackpats[i] = 0;
    g->whitecount[i] = 0; g->blackcount[i] = 0;
  }
  allarmies(g);  /* Create the armies */
  drawboard(g);  /* display the game board */
  looking = 0;   /* indicate not in look ahead now */
  stonenumber = 0;
  if ((g->debug & 1) != 0) printf("initialize end.\n");
}

int addastone(g,newstone) game *g; int newstone; {
/* Clear the blank army for rebuilding, update any neighboring enemy
   armies directly, and do nothing for neighboring friendly armies.
   Friendly armies are merged and updated by linkarmies at the end. */
int a1,a2,first,playedstone,n,s; neighbors nabor; mstatus oldstatus;
  playedstone = -1;
  a2 = -1;
  if ((newstone >= 0) && (newstone < g->maxstone) &&
      (newstone != g->movestatus.kospot) && (g->board[newstone] == Blank)) {
    g->movecount = g->movecount + 1;
    if (looking == 0) stonenumber = stonenumber + 1;
    oldstatus = g->movestatus;  /* save the old status */
    g->board[newstone] = g->who;
    drawstone(g,newstone,g->who);
    g->movestatus.kospot = -1;  /* clear the ko position */
    g->movestatus.captures = 0; /* clear captures */
    topicture(g,newstone);
    a1 = g->armymen[newstone]; /* old blank army number */
    first = g->army[a1].first; /* create link list of armies to relink */
    getneighbors(g,&nabor,newstone);
    a2 = -1;
    for (n = firstelement; n < nabor.neis; n++) {
      s = nabor.stone[n];
      if (g->board[s] != Blank) {
        a1 = g->armymen[s];
        if ((a1 != a2) && (g->army[a1].color == otherside[g->who])) {
          g->army[a1].enemy = g->army[a1].enemy + 1;
          g->army[a1].lib = g->army[a1].lib - 1;
          if (g->army[a1].lib <= 0) {
            capture(g,g->army[a1].first);
            g->movestatus.captures = g->movestatus.captures | nabor.direction[n]
;
          }
        }
        a2 = a1;
    } }
    linkarmies(g,first);
/* If the added stone was illegal for lack of liberties then remove it */
    if (g->army[g->armymen[newstone]].lib <= 0) {
      if (g->who == White) printf(" Bad add by white at %c,%d\n",
          coltable[g->colbrd[newstone]],g->rowbrd[newstone]);
      else printf(" Bad add by black at %c,%d\n",
          coltable[g->colbrd[newstone]],g->rowbrd[newstone]);
      removeastone(g,newstone,g->movestatus);
      g->movestatus = oldstatus;
      if (looking == 0) stonenumber = stonenumber - 1;
    } else {
/* If the added stone merged into another army then there is no ko spot */
      a1 = g->armymen[newstone]; /* old blank army number */
      if (g->army[a1].size > 1) g->movestatus.kospot = -1;
      if (looking == 0) {
        playedstone = newstone;
        if (fp != NULL) {
          if (g->who == White) fprintf(fp,"add w %c,%d\n",
            coltable[g->colbrd[newstone]],g->rowbrd[newstone]);
          else fprintf(fp,"add b %c,%d\n",
            coltable[g->colbrd[newstone]],g->rowbrd[newstone]);
    } } }
  } else {
    if (g->who == White) printf(" Bad add by white at %c,%d\n",
          coltable[g->colbrd[newstone]],g->rowbrd[newstone]);
    else printf(" Bad add by black at %c,%d\n",
          coltable[g->colbrd[newstone]],g->rowbrd[newstone]);
  }
  return playedstone;
}

removeastone(g,newstone,status) game *g; int newstone; mstatus status; {
/* Clear the blank army for rebuilding, update any neighboring enemy
   armies directly, and do nothing for neighboring friendly armies.
   Friendly armies are merged and updated by linkarmies at the end. */
int a1,a2,first,n,s,color,wh; neighbors nabor;
  if ((g->debug & 1) != 0) printf("removeastone at: %d\n",newstone);
  if ((newstone >= 0) && (newstone < g->maxstone) &&
      (g->board[newstone] != Blank)) {
    g->movecount = g->movecount - 1;
    g->movestatus = status;    /* set the new board status */
    wh = g->board[newstone];   /* save the old color */
    color = otherside[wh];
    frompicture(g,newstone);   /* remove stone from picture */
    g->board[newstone] = Blank;/* remove the stone */
    if (wh == White) g->deadwhite = g->deadwhite + 1;
    else g->deadblack = g->deadblack + 1;
    undrawstone(g,newstone);
    a1 = g->armymen[newstone]; /* old army number */
    first = g->army[a1].first; /* first man in army to be relinked */
    getneighbors(g,&nabor,newstone);
    a2 = 0;
    for (n = firstelement; n < nabor.neis; n++) { /* for neighbors */
      a1 = g->armymen[nabor.stone[n]];
      if ((g->army[a1].color == Blank) &&
          ((nabor.direction[n] & g->movestatus.captures) != 0)) {
        uncapture(g,nabor.stone[n],color);
        g->army[a1].lib = 1;   /* set 1 liberty for the removed stone */
      } else if ((a1 != a2) && (g->army[a1].color == otherside[wh])) {
        g->army[a1].enemy = g->army[a1].enemy - 1;
        g->army[a1].lib = g->army[a1].lib + 1;
      }
      a2 = a1;
    }
    linkarmies(g,first);
  } else {
    if (g->who == White) printf(" Bad remove by white at %c,%d\n",
          coltable[g->colbrd[newstone]],g->rowbrd[newstone]);
    else printf(" Bad remove by black at %c,%d\n",
          coltable[g->colbrd[newstone]],g->rowbrd[newstone]);
  }
  if ((g->debug & 1) != 0) printf("removeastone, end.\n");
}

capture(g,s) game *g; int s; {
/* Call this routine to remove an army that has been captured.  The
   army remains intact, just changes to a blank army.  Neighboring
   enemy armies have their liberties updated by buildarmies. */
int a1,a2,s1,n,wh; neighbors nabor;
  if ((g->debug & 1) != 0) printf("capture:\n");
  if ((s >= 0) && (s < g->maxstone)) {
    a1 = g->armymen[s];       /* get the army number */
    if (g->army[a1].color != Blank) {
      if (g->army[a1].size == 1) {
        g->movestatus.kospot = g->army[a1].first;
      } else {
        g->movestatus.kospot = -1;
      }
      g->movecount = g->movecount - g->army[a1].size;
      wh = g->army[a1].color;     /* save the army color */
      if (wh == White) g->deadwhite = g->deadwhite + g->army[a1].size;
      else g->deadblack = g->deadblack + g->army[a1].size;
      g->army[a1].color = Blank;  /* change army color to blank */
      g->army[a1].size = 0;   /* causes army to be rebuilt by buildarmies */
      s1 = g->army[a1].first; /* first man in army */
      while (s1 >= 0) {
        frompicture(g,s1);    /* take out of picture */
        undrawstone(g,s1);
        g->board[s1] = Blank;     /* change stone color to blank */
/* clear the size in neighboring enemy armies so they will be rebuilt */
        getneighbors(g,&nabor,s1);
        for (n = firstelement; n < nabor.neis; n++) {   /* for neighbors */
          a2 = g->armymen[nabor.stone[n]];
          if (a2 > 0) {
            if (g->army[a2].color == otherside[wh]) {
              g->army[a2].size = 0; }
          }
        }
/* get the next man in the army */
        s1 = g->armylnk[s1];  /* row link to next army man   */
      }
      buildarmies(g);
    } else {
      printf(" Bad capture at: %c,%d\n",coltable[g->colbrd[s]],g->rowbrd[s]);
    }
  } else {
    printf(" Bad capture at: %c,%d\n",coltable[g->colbrd[s]],g->rowbrd[s]);
  }
  if ((g->debug & 1) != 0) printf("capture, end.\n");
}

uncapture(g,s,wh) game *g; int s,wh; {
/* Call this routine to uncapture an army that had been captured.  The
   army remains intact, just changes to a non-blank army.  Neighboring
   enemy armies have their liberties updated by buildarmies. */
int a1,a2,s1,n; neighbors nabor;
  if ((g->debug & 1) != 0) printf("uncapture:\n");
  if ((s >= 0) && (s < g->maxstone)) {
    a1 = g->armymen[s];     /* get the army number */
    if (g->army[a1].color == Blank) {
      g->movecount = g->movecount + g->army[a1].size;
      if (wh == White) g->deadwhite = g->deadwhite - g->army[a1].size;
      else g->deadblack = g->deadblack - g->army[a1].size;
      g->army[a1].color = wh; /* change army color to non-blank */
      g->army[a1].size = 0;   /* causes army to be rebuilt by buildarmies */
      s1 = g->army[a1].first; /* first man in army */
      while (s1 >= 0) {
        g->board[s1] = wh;  /* change stone color to non-blank */
        drawstone(g,s1,wh);
        topicture(g,s1);    /* add to picture */
/* clear the size in neighboring enemy armies so they will be rebuilt */
        getneighbors(g,&nabor,s1);
        for (n = firstelement; n < nabor.neis; n++) {   /* for neighbors */
          a2 = g->armymen[nabor.stone[n]];
          if (g->army[a2].color != wh) g->army[a2].size = 0;
        }
/* get the next man in the army */
        s1 = g->armylnk[s1];  /* row link to next army man */
      }
      buildarmies(g);
    }
  } else {
    printf("Bad uncapture an army at: %c,%d\n",
      coltable[g->colbrd[s]],g->rowbrd[s]);
  }
  if ((g->debug & 1) != 0) printf(" uncapture, end.\n");
}

getneighbors(g,nabor,stone) game *g; neighbors *nabor; int stone; {
int b,i,j,s;
/* Establish the number and position of neighbors to a stone.  Gather
   the stone numbers of neighboring positions into the nabor.stone
   array sorted by army number.  The purpose of the sort is
   to group neighbors belonging to the same army together. */
  nabor->neis = firstelement;
  if ((g->neibrd[stone] & 1) != 0) {
    nabor->stone[firstelement] = stone - 1;
    nabor->direction[firstelement] = 1;
    nabor->neis = nabor->neis + 1; }
  if ((g->neibrd[stone] & 2) != 0) {
    nabor->stone[nabor->neis] = stone + 1;
    nabor->direction[nabor->neis] = 2;
    nabor->neis = nabor->neis + 1; }
  if ((g->neibrd[stone] & 4) != 0) {
    nabor->stone[nabor->neis] = stone - g->width;
    nabor->direction[nabor->neis] = 4;
    nabor->neis = nabor->neis + 1; }
  if ((g->neibrd[stone] & 8) != 0) {
    nabor->stone[nabor->neis] = stone + g->width;
    nabor->direction[nabor->neis] = 8;
    nabor->neis = nabor->neis + 1; }
  for (i = firstelement; i < nabor->neis-1; i++) {
    for (j = i+1; j < nabor->neis; j++) {
      if (g->armymen[nabor->stone[j]] < g->armymen[nabor->stone[i]]) {
        s = nabor->stone[j];
        nabor->stone[j] = nabor->stone[i]; nabor->stone[i] = s;
        b = nabor->direction[j];
        nabor->direction[j] = nabor->direction[i]; nabor->direction[i] = b;
} } } }

allarmies(g) game *g; {
/* Clear all army numbers in the army array, and call build armies */
int n,s;
  if ((g->debug & 1) != 0) printf("allarmies:\n");
  g->avail = 1;      /* next available army number */
  g->army[1].bp = 0;
  for (n = firstelement; n < maxarmies; n++) {
    g->army[n].first = -1;
    g->army[n].fp = n + 1;  /* initialize armies forward links */
  }
  for (s = firstelement; s < g->maxstone; s++) {
    g->armymen[s] = 0; g->armylnk[s] = s - 1;
  }
  linkarmies(g,g->maxstone-1);
  if ((g->debug & 1) != 0) printf(" allarmies.\n");
}

linkarmies(g,first) game *g; int first; {
/* Create a 1 man army for each square.  Then look at neighboring squares,
   and if any are the same color army then merge the new army into the
   neighboring army */
int n,s,s1,s2,new,old; neighbors nabor;
  if ((g->debug & 1) != 0) printf("linkarmies:\n");
  s = first;
  old = g->armymen[s];      /* save old army number */
  while (s >= 0) {
    s1 = g->armylnk[s];     /* save link to next old army man */
    new = g->avail;         /* get next available army number */
    g->avail = g->army[g->avail].fp;
    g->army[g->avail].bp = new;
    g->armymen[s] = new;    /* set new army number */
    g->armylnk[s] = -1;
    g->army[new].size = 0;  /* size is done by buildarmies */
    g->army[new].color = g->board[s];
    g->army[new].first = s; g->army[new].last = s;
    getneighbors(g,&nabor,s);     /* get the neighbors */
    for (n = firstelement; n < nabor.neis; n++) {  /* go through the neighbors *
/
      s2 = nabor.stone[n];
      if (g->armymen[s2] != old) { armymerge(g,s2,s); }
    }
    s = s1;  /* next man to be linked */
  }
/* make old army number available */
  if (old > 0) {
    if (g->army[old].fp != g->avail) {
      if (g->army[old].bp > 0) {
        g->army[g->army[old].bp].fp = g->army[old].fp; }
      g->army[g->army[old].fp].bp = g->army[old].bp;
      g->army[g->army[g->avail].bp].fp = old;
      g->army[old].fp = g->avail;
      g->army[old].bp = g->army[g->avail].bp;
      g->army[g->avail].bp = old;
    }
    g->army[old].first = -1; g->avail = old;
  }
  buildarmies(g);
  if ((g->debug & 1) != 0) printf("  linkarmies.\n");
}

buildarmies(g) game *g; {
/* Add up the number of computer and opponent stones that border on
   each army */
int a1,a2,n,s,s1; neighbors nabor;
  if ((g->debug & 1) != 0) printf("buildarmies:\n");
  for (s = firstelement; s < g->maxstone; s++) wrkbrd[s] = 0;
  a1 = g->army[g->avail].bp;
  while (a1 > 0) {                  /* go through each active army */
    if ((g->army[a1].size <= 0) && (g->army[a1].first >= 0)) {
      g->army[a1].lib = 0;
      g->army[a1].friend = 0;
      g->army[a1].enemy = 0;
      g->army[a1].armies = 0;
      s = g->army[a1].first;          /* first man in army */
      while (s >= 0) {
        g->army[a1].size = g->army[a1].size + 1;
        getneighbors(g,&nabor,s);     /* get the neighbors */
        for (n = firstelement; n < nabor.neis; n++) {  /* go through neighbors *
/
          s1 = nabor.stone[n];
          if (wrkbrd[s1] != a1) {
            wrkbrd[s1] = a1;        /* mark this neighbor counted */
            if (g->board[s] != Blank) {
              if (g->board[s1] == Blank) {
                g->army[a1].lib = g->army[a1].lib + 1;
              } else if (g->board[s1] != g->board[s]) {
                g->army[a1].enemy = g->army[a1].enemy + 1;
              }
            } else {
              if (g->board[s1] != Blank) {
                if (g->board[s1] == White) {
                  g->army[a1].friend = g->army[a1].friend + 1;
                } else {
                  g->army[a1].enemy = g->army[a1].enemy + 1;
                }
              }
            }
          }
        }
        s = g->armylnk[s]; /* link to next man */
      }
    }
    a1 = g->army[a1].bp;
  }
  if ((g->debug & 1) != 0) printf("  buildarmies.\n");
}

armymerge(g,s1,s2) game *g; int s1,s2; {
/* Merge two armies by merging the linked lists */
int a1,a2,i,s,s3;
  a1 = g->armymen[s1]; a2 = g->armymen[s2];
  if ((a1 > 0) && (a1 != a2) && (g->board[s1] == g->board[s2])) {
/* Change a2 number to a1 for all the a2 armymen */
    s = g->army[a2].first;  /* first man in army */
    while (s >= 0) { g->armymen[s] = a1; s = g->armylnk[s]; }
/* Link army a2 into army a1 */
    g->armylnk[g->army[a2].last] = g->army[a1].first;
    g->army[a1].first = g->army[a2].first;
    g->army[a2].first = -1; /* delete army a2 */
    g->army[a2].size = 0;
    g->army[a1].size = 0;   /* required for buildarmies */
    if (g->army[a2].fp != g->avail) {
      if (g->army[a2].bp > 0) {
        g->army[g->army[a2].bp].fp = g->army[a2].fp; }
      g->army[g->army[a2].fp].bp = g->army[a2].bp;
      g->army[g->army[g->avail].bp].fp = a2;
      g->army[a2].fp = g->avail;
      g->army[a2].bp = g->army[g->avail].bp;
      g->army[g->avail].bp = a2;
    }
    g->avail = a2;
} }

topicture(g,s) game *g; int s; {
/* Add the specified stone to the bitmap of the stones in the 5 by 5
   diamond centered about the specified stone */
int i,s1,s2,s3,w3; long bit,*p; char *c;
  i = - g->width - g->width - 2; bit = 1L; s1 = -1;
  if (g->who == White) { p = &g->whitepats[s]; c = &g->whitecount[s]; }
  else { p = &g->blackpats[s]; c = &g->blackcount[s]; }
  for (s2 = 0; s2 < patwidth; s2++) {
    for (s3 = 0; s3 < patwidth; s3++) {
      s1 = s1 + 1;
      if ((picset[s1] & g->neibrd[s]) == picset[s1])
        { *(p+i) = *(p+i) | bit; *(c+i) = *(c+i) + 1; }
      bit = bit + bit; i = i + 1;
    } i = i + g->width - patwidth;
  }
  w3 = g->width + g->width + g->width;
  if ((picset[25] & g->neibrd[s]) == picset[25])
    { *(p-3) = *(p-3) | 0x2000000L; *(c-3) = *(c-3) + 1; }
  if ((picset[26] & g->neibrd[s]) == picset[26])
    { *(p+3) = *(p+3) | 0x4000000L; *(c+3) = *(c+3) + 1; }
  if ((picset[27] & g->neibrd[s]) == picset[27])
    { *(p-w3) = *(p-w3) | 0x8000000L; *(c-w3) = *(c-w3) + 1; }
  if ((picset[28] & g->neibrd[s]) == picset[28])
    { *(p+w3) = *(p+w3) | 0x10000000L; *(c+w3) = *(c+w3) + 1; }
}

frompicture(g,s) game *g; int s; {
/* Remove the specified stone from the bitmap of the stones in the 5 by 5
   diamond centered about the specified stone. */
int i,s1,s2,s3,w3; long bit,*p; char *c;
  i = - g->width - g->width - 2; bit = 1; s1 = -1;
  if (g->who == White) { p = &g->whitepats[s]; c = &g->whitecount[s]; }
  else { p = &g->blackpats[s]; c = &g->blackcount[s]; }
  for (s2 = 0; s2 < patwidth; s2++) {
    for (s3 = 0; s3 < patwidth; s3++) {
      s1 = s1 + 1;
      if ((picset[s1] & g->neibrd[s]) == picset[s1])
        { *(p+i) = *(p+i) & ~bit; *(c+i) = *(c+i) - 1; }
      bit = bit + bit; i = i + 1;
    } i = i + g->width - patwidth;
  }
  w3 = g->width + g->width + g->width;
  if ((picset[25] & g->neibrd[s]) == picset[25])
    { *(p-3) = *(p-3) & ~0x2000000L; *(c-3) = *(c-3) - 1; }
  if ((picset[26] & g->neibrd[s]) == picset[26])
    { *(p+3) = *(p+3) & ~0x4000000L; *(c+3) = *(c+3) - 1; }
  if ((picset[27] & g->neibrd[s]) == picset[27])
    { *(p-w3) = *(p-w3) & ~0x8000000L; *(c-w3) = *(c-w3) - 1; }
  if ((picset[28] & g->neibrd[s]) == picset[28])
    { *(p+w3) = *(p+w3) & ~0x10000000L; *(c+w3) = *(c+w3) - 1; }
}

/* The following routines provide output for debugging */

moveboard(g) game *g; {
int i,s;
  s = 0;
  while (s < g->maxstone) {
    for (i = firstelement; i < g->width; i++) {
      if (g->board[s] == White) printf(" 1");
      else if (g->board[s] == Black) printf(" 2");
      else printf(" 0");
      s = s + 1;
    }
    printf("\n");
} }

armyboard(g) game *g; {
int i,s;
  s = 0;
  while (s < g->maxstone) {
    for (i = firstelement; i < g->width; i++) { printf("%3d",g->armymen[s]); s =
 s + 1; }
    printf("\n");
} }

armyinfo(g) game *g; {
int i,n; char qkey;
  qkey = 'g';
  printf("Total stones: %d\nAvail: %d\n",g->movecount,g->avail);
  n = 0;
  i = g->army[g->avail].bp;
  while (i > 0) { if (i > n) n = i; i = g->army[i].bp; }
  printf("Highest army number: %d\n",n);
  i = g->army[g->avail].bp;
  while ((qkey != 'q') && (i > 0)) {
    printf("Army %d\n",i);
    if (g->army[i].first >= 0) {
      printf("Color %d\nSize %d\nFirst %d\nLast %d\n",
        g->army[i].color,g->army[i].size,g->army[i].first,g->army[i].last);
      printf("FP %d\nBP %d\nLiberties %d\nFriends %d\nEnemies %d\n",
        g->army[i].fp,g->army[i].bp,g->army[i].lib,g->army[i].friend,g->army[i].
enemy);
    }
    i = g->army[i].bp;
    printf("q return to quit, space to continue\n"); qkey = getchar();
  }
}

linkinfo(g) game *g; {
int i,s;
  s = firstelement;
  while (s < g->maxstone) {
    for (i = firstelement; i < g->width; i++) { printf("%d ",g->armylnk[s]); s =
 s + 1; }
    printf("\n");
  }
}

pictinfo(g) game *g; {
int i,s; char qkey;
  s = 0; qkey = 'g';
  printf("White stone patterns:\n");
  while ((s < g->maxstone) && (qkey != 'q') && (qkey != 'n')) {
    for (i = firstelement; i < g->width; i++)
      { printf(" %lx\n",g->whitepats[s]); s = s + 1; }
    printf("q return to quit, space to continue\n"); qkey = getchar();
  } printf("Black stone patterns:\n");
  s = 0;
  while ((s < g->maxstone) && (qkey != 'q')) {
    for (i = firstelement; i < g->width; i++)
      { printf(" %lx\n",g->blackpats[s]); s = s + 1; }
    printf("q return to quit, space to continue\n"); qkey = getchar();
  }
}

neighborinfo(g) game *g; {
int i,s;
  s = 0;
  while (s < g->maxstone) {
    for (i = firstelement; i < g->width; i++)
      { printf("%4x ",g->neibrd[s]); s = s + 1; }
    printf("\n");
  }
}

drawboard(g) game *g; {
int i,linelength; rect srect; char num[4];
  setport(mywindow);
/* Draw the position letters and numbers */
  textsize(9);
  for (i = g->width; i > 0; i--) {
    moveto(-12,14 + 16 * (g->width - i));
    sprintf(num,"%2d", i);
    drawstring(num);
  }
  for (i = 0; i < g->width; i++) {
    moveto(7 + 16 * i,13 + 16 * g->width);
    drawchar(coltable[i]);
  }
/* Draw the board */
  textsize(7);
  linelength = 16 * (g->width - 1);
  moveto(10,10);
  for (i = 0; i < g->width; i++) {
    line(linelength,0);
    move(-linelength,16);
  }
  moveto(10,10);
  for (i = 0; i < g->width; i++) {
    line(0,linelength);
    move(16,-linelength);
  }
/* draw the handicap positions on a 19 X 19 board */
  if (g->width == 19) {
/* first draw the corner handicap positions */
    setrect(&srect,  55,  55,  61,  61); filloval(&srect,&blackpat);
    setrect(&srect,  55, 247,  61, 253); filloval(&srect,&blackpat);
    setrect(&srect, 247,  55, 253,  61); filloval(&srect,&blackpat);
    setrect(&srect, 247, 247, 253, 253); filloval(&srect,&blackpat);
/* now draw the other row 3 handicap positions */
    setrect(&srect,  55, 151,  61, 157); filloval(&srect,&blackpat);
    setrect(&srect, 151,  55, 157,  61); filloval(&srect,&blackpat);
    setrect(&srect, 247, 151, 253, 157); filloval(&srect,&blackpat);
    setrect(&srect, 151, 247, 157, 253); filloval(&srect,&blackpat);
/* finally draw the center handicap position */
    setrect(&srect, 151, 151, 157, 157); filloval(&srect,&blackpat);
  }
  setport(mewindow);
}

drawstone(g,s,wh) game *g; int s,wh; {
int w,x,y; rect srect; char num[8];
  if ((looking == 0) || (lookahead != 0)) {
    setport(mywindow);
    x = 10 + 16 * (s % g->width);
    y = 10 + 16 * ((g->maxstone - 1 - s) / g->width);
    setrect(&srect, x-8, y-8, x+8, y+8);
    if (wh == White) {
      filloval(&srect,&whitepat);
    } else {
      filloval(&srect,&blackpat);
    }
    if ((drawnumber != 0) && (looking == 0)) {
      sprintf(num,"%d",stonenumber);
      w = stringwidth(num) >> 1;
      moveto(x-w,y+3);
      drawstring(num);
    }
    setport(mewindow);
  }
}

undrawstone(g,s) game *g; int s; {
int x,y; rect srect;
  if ((looking == 0) || (lookahead != 0)) {
    setport(mywindow);
    x = 10 + 16 * (s % g->width);
    y = 10 + 16 * ((g->maxstone - 1 - s) / g->width);
    setrect(&srect, x-8, y-8, x+8, y+8);
    eraseoval(&srect);
    if ((g->neibrd[s] & 1) != 0) { moveto(x,y); line(-8,0); }
    if ((g->neibrd[s] & 2) != 0) { moveto(x,y); line(8,0); }
    if ((g->neibrd[s] & 4) != 0) { moveto(x,y); line(0,8); }
    if ((g->neibrd[s] & 8) != 0) { moveto(x,y); line(0,-8); }
/* draw the handicap positions on a 19 X 19 board */
    if (g->width == 19) {
/* first draw the corner handicap positions */
      if (s == 60) {
        setrect(&srect,  55, 247,  61, 253); filloval(&srect,&blackpat); }
      if (s == 72) {
        setrect(&srect, 247, 247, 253, 253); filloval(&srect,&blackpat); }
      if (s == 288) {
        setrect(&srect,  55,  55,  61,  61); filloval(&srect,&blackpat); }
      if (s == 300) {
        setrect(&srect, 247,  55, 253,  61); filloval(&srect,&blackpat); }
/* now draw the other row 3 handicap positions */
      if (s == 174) {
        setrect(&srect,  55, 151,  61, 157); filloval(&srect,&blackpat); }
      if (s == 294) {
        setrect(&srect, 151,  55, 157,  61); filloval(&srect,&blackpat); }
      if (s == 186) {
        setrect(&srect, 247, 151, 253, 157); filloval(&srect,&blackpat); }
      if (s == 66) {
        setrect(&srect, 151, 247, 157, 253); filloval(&srect,&blackpat); }
/* finally draw the center handicap position */
      if (s == 180) {
        setrect(&srect, 151, 151, 157, 157); filloval(&srect,&blackpat); }
    }
    setport(mewindow);
  }
}
/* File gofile.c
   Developer:  James R. Logan Jr. (BYU), Advisor:  Dr. Lynn H. Beus (BYU)
   email address:  loganj@byuvax.bitnet
   (801) 378-3617

   These routines handle the disk I/O for the go program's log file and
   replay feature.  This code is not machine dependent.
*/

#include <qd.h>
#include <pack.h>
#include <win.h>
#include <dialog.h>
#include <stdio.h>
#include <goprocs.h>

overlay "filestuff"

char prompt[2] = {' ','\0'},origname[2] = {' ','\0'},filename[32];
rect drect;

extern FILE *fp,*gp;
extern char coltable[19];
extern int play;
extern game mygame;
extern windowptr mywindow, dummywindow;
extern int addastone(),moves();

createfile() {
/* This procedure opens a log file for the game in progess */
sfreply reply; point pt; char volname[30]; int i,j,tint; long tlong;
  setrect(&drect,-16,28,440,200);
  copybits(&mywindow->portbits,&dummywindow->portbits,&drect,&drect,srccopy,(lon
g)0);
  setpt(&pt, 190, 75);
  sfputfile(&pt, &prompt[0], &origname[0], NULL, &reply);
  copybits(&dummywindow->portbits,&mywindow->portbits,&drect,&drect,srccopy,(lon
g)0);
  if (reply.good) {
    getvinfo(reply.vrefnum, volname, &tint, &tlong);
    strcpy(filename, volname); strcat(filename, ":"); strcat(filename, reply.fna
me);
    if (fp != NULL) fclose(fp);
    fp = fopen(filename,"w");
} }

replay() {
/* This procedure opens a saved game and replays the game.  The game file
   can be modified by an editor to change the sequence of play.  The first
   letter of each line determines the function.  Using log files is the
   fastest way to set up a specific board position.  */
char col,color,tline[128];
int c,i,row,s,size,tint; long tlong;
sfreply reply; point pt; char volname[30];
  if (gp == 0) {
    setrect(&drect,-16,28,440,200);
    copybits(&mywindow->portbits,&dummywindow->portbits,
          &drect,&drect,srccopy,(long)0);
    setpt(&pt, 186, 75);
    sfgetfile(&pt, NULL, NULL, 1, "TEXT", NULL, &reply);
    copybits(&dummywindow->portbits,&mywindow->portbits,
          &drect,&drect,srccopy,(long)0);
    if (reply.good) {
      getvinfo(reply.vrefnum, volname, &tint, &tlong);
      strcpy(filename, volname); strcat(filename, ":"); strcat(filename, reply.f
name);
      gp = fopen(filename,"r");
    }
  } else {
    c = NULL;
    for (i=0; (i<128) && ((c = getc(gp)) != EOF) && (c != 10); i++) tline[i] = c
;
    tline[i] = 0;
    switch (tline[0]) {
      case 'a': case 'A':
        sscanf(tline,"%*s %c %c,%d",&color,&col,&row);
        s = position(col,row);
        if ((color == 'w') || (color == 'W')) mygame.who = White;
        else if ((color == 'b') || (color == 'B')) mygame.who = Black;
        if (s >= 0) s = addastone(&mygame,s);
        break;
      case 'b': case 'B': mygame.who = Black; break;
      case 'c': case 'C':
        sscanf(tline,"%*s %c,%d",&col,&row);
        s = position(col,row);
        if (s >= 0) capture(&mygame,s);
        break;
      case 'i': case 'I':
        sscanf(tline,"%*s %d",&size);
        if ((size > 2) && (size < 20)) {
          mygame.width = size;
          initgame(&mygame);
        }
        break;
      case 'm': case 'M': s = moves(&mygame); break;
      case 'r': case 'R':
        sscanf(tline,"%*s %c,%d",&col,&row);
        s = position(col,row);
        if (s >= 0) removeastone(&mygame,s,mygame.movestatus);
        break;
      case 'u': case 'U':
        sscanf(tline,"%*s %c,%d,%c",&col,&row,&color);
        s = position(col,row);
        if (s >= 0) {
          if ((color == 'w') || (color == 'W')) uncapture(&mygame,s,White);
          else if ((color == 'b') || (color == 'B')) uncapture(&mygame,s,Black);

        }
        break;
      case 'w': case 'W': mygame.who = White; break;
      default:
    }
    if (c == EOF) { fclose(gp); gp = NULL; play = 0; }
} }

int position(col,row) char col; int row; {
/* This procedure converts from a 2-dimensional position used by the world,
   to the 1-dimensional position used in the game structures */
int i,s;
  s = -1;
  if ((row > 0) && (row <= mygame.width)) {
    for (i = 0; i < mygame.width; i++) if (col == coltable[i]) s = i;
    if ((s >= 0) && (s < mygame.width)) s = (row - 1) * mygame.width + s;
  }
  return(s);
}