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); }