home *** CD-ROM | disk | FTP | other *** search
- /* Go started 4/17/88 by Todd R. Johnson */
- /* 8/8/89 cleaned up for first release */
- /* Public Domain */
-
- #include "go.h"
-
- /* From amigainterface.c */
- extern void amigainit();
- extern void amigaexit();
- extern void getinput( short*, short*, short*, short );
- extern void placestone();
- extern void intrMoveReport( enum bVal, char*, char * );
- extern void intrPrisonerReport( short, short );
- extern void intrInitScratchArea();
- extern void intrPuts( char * );
- extern void intrPutshort( short );
- extern void intrPutLn();
- extern void intrPass( enum bVal );
-
- /* From goplayer.c */
- extern genMove();
- extern char *playReason;
- extern short playLevel, showTrees;
-
- /* Procedures from this file */
- short Connect( enum bVal, short, short, short[4], short[4], short *, short * );
- short Maxlibs( short, short );
- short Suicide( enum bVal, short, short );
- short StoneLibs( short, short );
- void EraseMarks();
- short GoPlaceStone( enum bVal, short, short );
- void GoRemoveStone( short, short );
- void MergeGroups( short, short );
- void DeleteGroup( short );
- void ReEvalGroups( enum bVal, short, short, short );
- void GroupCapture( short );
- void FixLibs( enum bVal, short, short, short );
- void main();
- void goRestart();
- void RelabelGroups();
- short CountAndMarkLibs( short, short );
- void CountLiberties( short );
- void CheckForEye( short, short, short[4], short, short * );
- void CountEyes();
- void printGroupReport( short, short );
- void goCoord( short, short, char[] );
-
- struct bRec goboard[19][19]; /* The main go board */
-
- struct Group GroupList[MAXGROUPS]; /* The list of Groups */
- short DeletedGroups[4]; /* Codes of deleted groups */
-
- short GroupCount = 0; /* The total number of groups */
- short DeletedGroupCount; /* The total number of groups deleted
- on a move */
- short ko, koX, koY;
- short blackPrisoners, whitePrisoners;
- short showMoveReason = FALSE, groupInfo = FALSE,
- computerBlack = TRUE, computerWhite = FALSE,
- whitePassed = FALSE, blackPassed = FALSE;
-
- enum bVal color; /* The color to move next */
-
- /* Arrays for use when checking around a point */
- short xVec[4] = {0, 1, 0, -1};
- short yVec[4] = {-1, 0, 1, 0};
-
- short member( group, grouplist, cnt )
- short group, grouplist[4], cnt;
- {
- unsigned short i;
- for (i = 0; i < cnt; i++)
- if (grouplist[i] == group) return TRUE;
- return FALSE;
- }
-
- /* Does a stone at x, y connect to any groups of color? */
- short Connect( color, x, y, fGroups, fCnt, eGroups, eCnt )
- enum bVal color;
- short x, y;
- short fGroups[4], eGroups[4];
- short *fCnt, *eCnt;
- {
- unsigned short point = 0;
- short tx, ty, total = 0;
- enum bVal opcolor = WHITE;
- *fCnt = 0;
- *eCnt = 0;
- if (color == WHITE) opcolor = BLACK;
- for (point = 0; point <= 3; point++ )
- {
- tx = x + xVec[point];
- ty = y + yVec[point];
- if (tx >= 0 && tx <= 18 && ty >= 0 && ty <= 18)
- if (goboard[tx][ty].Val == color
- && ! member( goboard[tx][ty].GroupNum, fGroups, *fCnt ))
- {
- fGroups[(*fCnt)++] = goboard[tx][ty].GroupNum;
- total += 1;
- }
- else if (goboard[tx][ty].Val == opcolor
- && ! member( goboard[tx][ty].GroupNum, eGroups, *eCnt ))
- {
- eGroups[(*eCnt)++] = goboard[tx][ty].GroupNum;
- total += 1;
- }
- }
- return total;
- }
-
- /* Returns the maximum number of liberties for a given intersection */
- short Maxlibs( x, y )
- short x, y;
- {
- short cnt = 4;
- if (x == 0 || x == 18) cnt = cnt - 1;
- if (y == 0 || y == 18) cnt = cnt - 1;
- return cnt;
- }
-
- /* Determine whether x, y is suicide for color */
- short Suicide( color, x, y )
- enum bVal color;
- short x, y;
- {
- enum bVal opcolor = BLACK;
- short friendlycnt, friendlygroups[4], enemycnt, enemygroups[4], total;
- short maxlibs, i, libcnt = 0;
- if (color == BLACK) opcolor = WHITE;
- maxlibs = Maxlibs( x, y );
- total = Connect( color, x, y, friendlygroups, &friendlycnt,
- enemygroups, &enemycnt);
- if (total < maxlibs) return FALSE;
- /* Check for a capture */
- for (i = 0; i < enemycnt; i++ )
- if (GroupList[enemygroups[i]].liberties == 1) return FALSE;
- for (i = 0; i < friendlycnt; i++ )
- libcnt += GroupList[friendlygroups[i]].liberties - 1;
- if (libcnt != 0) return FALSE;
- return TRUE;
- }
-
- /* Returns the number of liberties for x, y */
- short StoneLibs( x, y )
- short x, y;
- {
- short cnt = 0, tx, ty;
- unsigned short point;
- for (point = 0; point <= 3; point++)
- {
- tx = x + xVec[point];
- ty = y + yVec[point];
- if (tx >= 0 && tx <= 18 && ty >= 0 && ty <= 18 &&
- goboard[tx][ty].Val == EMPTY)
- cnt++;
- }
- return cnt;
- }
-
- void EraseMarks()
- {
- short x, y;
- for (y = 0; y <= 18; y++)
- for (x = 0; x <= 18; x++)
- goboard[x][y].marked = FALSE;
- }
-
- /* Place a stone of color at x, y */
- short GoPlaceStone( color, x, y )
- enum bVal color;
- short x, y;
- {
- short fgroups[4], egroups[4]; /* group codes surrounding stone */
- short fcnt, ecnt, i;
- short lowest = GroupCount + 1;
-
- DeletedGroupCount = 0;
- if (goboard[x][y].Val != EMPTY) return FALSE;
- if (Suicide( color, x, y )) return FALSE; /* Illegal move */
- if (ko && koX == x && koY == y) return FALSE;
- ko = FALSE;
- placestone( color, x, y );
- goboard[x][y].Val = color;
- /* Does the new stone connect to any friendly stone(s)? */
- Connect( color, x, y, fgroups, &fcnt, egroups, &ecnt );
- if (fcnt)
- {
- /* Find the connecting friendly group with the lowest code */
- for ( i = 0; i < fcnt; i++ )
- if (fgroups[i] <= lowest) lowest = fgroups[i];
- /* Renumber resulting group */
- /* Raise the stone count of the lowest by one to account for new
- stone */
- goboard[x][y].GroupNum = lowest;
- GroupList[lowest].count += 1;
- for ( i = 0; i < fcnt; i++ )
- if (fgroups[i] != lowest)
- MergeGroups( lowest, fgroups[i] );
- /* Fix the liberties of the resulting group */
- CountLiberties( lowest ); /* Fix up liberties for group */
- }
- else
- {
- /* Isolated stone. Create new group. */
- GroupCount += 1;
- lowest = GroupCount;
- GroupList[ lowest ].color = color;
- GroupList[ lowest ].count = 1;
- GroupList[ lowest ].internal = 0;
- GroupList[ lowest ].external = StoneLibs( x, y );
- GroupList[ lowest ].liberties = GroupList[ lowest ].external;
- GroupList[ lowest ].eyes = 0;
- GroupList[ lowest ].alive = 0;
- GroupList[ lowest ].territory = 0;
- goboard[x][y].GroupNum = lowest;
- }
- /* Now fix the liberties of enemy groups adjacent to played stone */
- FixLibs( color, x, y, PLACED ); /* Fix the liberties of opcolor */
- ReEvalGroups( color, x, y, lowest );
- RelabelGroups();
- return TRUE;
- }
-
- /* Remove a stone from the board */
- void GoRemoveStone( x, y )
- short x, y;
- {
- goboard[x][y].Val = EMPTY;
- goboard[x][y].GroupNum = 0;
- removestone( x, y );
- }
-
- /* Merges two groups -- Renumbers stones and deletes second group from
- list. Fixes stone count of groups. This does not fix anything else.
- FixLibs must be called to fix liberties, etc. */
- void MergeGroups( g1, g2 )
- short g1, g2;
- {
- short x, y;
- for (y = 0; y <= 18; y++)
- for (x = 0; x <= 18; x++)
- if (goboard[x][y].GroupNum == g2) goboard[x][y].GroupNum = g1;
- GroupList[g1].count += GroupList[g2].count;
- DeleteGroup( g2 ); /* Removes group from GroupList */
- }
-
- /* Stores a group code to be deleted */
- void DeleteGroup( code )
- short code;
- {
- DeletedGroups[DeletedGroupCount++] = code;
- }
-
- /* Re-evaluate the groups given the last move. This assumes that the
- last move has been merged into adjoining groups and all liberty counts
- are correct. Handles capture. Checks for Ko. Keeps track of captured
- stones. code is the group number of the stone just played. */
- void ReEvalGroups( color, x, y, code )
- enum bVal color;
- short x, y, code;
- {
- short fgroups[4], egroups[4], fcnt, ecnt, i, killcnt = 0, count = 0;
- enum bVal opcolor = BLACK;
- if (color == BLACK) opcolor = WHITE;
- /* Check for capture */
- Connect( color, x, y, fgroups, &fcnt, egroups, &ecnt );
- if (ecnt)
- {
- /* See if any of the groups have no liberties */
- for (i = 0; i < ecnt; i++ )
- if (GroupList[egroups[i]].liberties == 0)
- {
- killcnt += 1;
- count = GroupList[egroups[i]].count;
- GroupCapture( egroups[i] );
- }
- }
- /* Check for ko. koX and koY are set in GroupCapture above. */
- if (killcnt == 1 && count == 1
- && GroupList[ code ].count == 1
- && GroupList[ code ].liberties == 1)
- ko = TRUE;
- if (killcnt) intrPrisonerReport( blackPrisoners, whitePrisoners );
- /* Set eye count for groups */
- CountEyes();
- #ifdef DEBUG
- PrintGroupInfo();
- #endif
- }
-
- /* Remove a captured group from the board and fix the liberties of any
- adjacent groups. Fixes prisoner count. Sets KoX and KoY */
- void GroupCapture( code )
- short code;
- {
- short x, y;
- if (GroupList[code].color == BLACK)
- blackPrisoners += GroupList[code].count;
- else
- whitePrisoners += GroupList[code].count;
- for (y = 0; y <= 18; y++)
- for( x = 0; x <= 18; x++ )
- if (goboard[x][y].GroupNum == code)
- {
- FixLibs( GroupList[code].color, x, y, REMOVED );
- GoRemoveStone( x, y );
- koX = x; koY = y;
- }
- DeleteGroup( code );
- }
-
- /* Fix the liberties of groups adjacent to x, y. move indicates
- whether a stone of color was placed or removed at x, y
- This does not change liberty counts of friendly groups when a stone
- is placed. Does not do captures. */
- void FixLibs( color, x, y, move )
- enum bVal color;
- short x, y, move;
- {
- short fgroups[4], fcnt, egroups[4], ecnt, i;
- enum bVal opcolor = BLACK;
- if (color == BLACK) opcolor = WHITE;
- Connect( color, x, y, fgroups, &fcnt, egroups, &ecnt );
- if (move == PLACED)
- for (i = 0; i < ecnt; i++)
- GroupList[egroups[i]].liberties -= 1;
- else /* Stone removed so increment opcolor */
- for (i = 0; i < ecnt; i++)
- GroupList[egroups[i]].liberties += 1;
- }
-
- void goRestart()
- {
- short x, y;
-
- color = BLACK;
- ko = FALSE;
- blackPrisoners = 0;
- whitePrisoners = 0;
- intrPrisonerReport( 0, 0 );
- intrInitScratchArea();
- intrCredits();
- for (y = 0; y <= 18; y++)
- for (x = 0; x <= 18; x++)
- if (goboard[x][y].Val != EMPTY)
- {
- goboard[x][y].Val = EMPTY;
- removestone( x, y );
- }
- } /* goRestart */
-
- void main()
- {
- short msg, x, y, playing = FALSE;
- char *coord = " ";
-
- playLevel = 7;
- showTrees = FALSE;
- amigainit();
- for (y = 0; y <= 18; y++)
- for (x = 0; x <= 18; x++)
- goboard[x][y].Val = EMPTY;
- goRestart();
-
- while (TRUE)
- {
- while (! playing)
- {
- getinput( &msg, &x, &y, FALSE );
- switch (msg)
- {
- case INTERSECTIONMSG:
- {
- if (goboard[x][y].Val == EMPTY)
- GoPlaceStone( BLACK, x, y );
- else if (goboard[x][y].Val == BLACK)
- {
- GoRemoveStone( x, y );
- GoPlaceStone( WHITE, x, y );
- }
- else
- GoRemoveStone( x, y );
- break;
- }
- case RESTARTMSG:
- {
- goRestart();
- break;
- }
- case QUITMSG:
- {
- amigaexit();
- return;
- }
- case PLAYMSG:
- {
- playing = TRUE;
- intrInitScratchArea(); /* Clear initial message */
- break;
- }
- } /* switch */
- }
- while (playing)
- {
- /* Get a move from one of the players */
- if ((color == BLACK && computerBlack) ||
- (color == WHITE && computerWhite))
- /* First see if the user has generated any messages. */
- /* This is needed for Amiga vs. Amiga games */
- {
- getinput( &msg, &x, &y, TRUE );
- if (msg)
- switch( msg )
- {
- case RESTARTMSG:
- {
- playing = FALSE;
- goRestart();
- continue;
- }
- case QUITMSG:
- {
- amigaexit();
- return;
- }
- } /* switch */
- if (genMove( color, &x, &y ))
- {
- msg = INTERSECTIONMSG;
- goCoord( x, y, coord );
- intrMoveReport( color, coord, playReason );
- }
- else
- msg = PASSMSG;
- }
- else getinput( &msg, &x, &y, FALSE );
-
- /* Handle the move */
- switch (msg)
- {
- case INTERSECTIONMSG:
- {
- if (goboard[x][y].Val != EMPTY)
- {
- printGroupReport(x, y);
- break;
- }
- if (GoPlaceStone( color, x, y ))
- if (color == BLACK)
- {
- color = WHITE;
- blackPassed = FALSE;
- }
- else
- {
- color = BLACK;
- whitePassed = FALSE;
- }
- break;
- }
- case PASSMSG:
- if (color == WHITE)
- {
- intrPass( color );
- whitePassed = TRUE;
- if (blackPassed)
- {
- playing = FALSE;
- intrPutLn(); intrPuts( "Game Over" );
- break;
- }
- color = BLACK;
- continue;
- }
- else
- {
- intrPass( color );
- blackPassed = TRUE;
- if (whitePassed)
- {
- playing = FALSE;
- intrPutLn(); intrPuts( "Game Over" );
- break;
- }
- color = WHITE;
- continue;
- }
- case RESTARTMSG:
- {
- playing = FALSE;
- goRestart();
- break;
- }
- case QUITMSG:
- {
- amigaexit();
- return;
- }
- }
- }
- }
- }
-
- /* if any groups have been deleted as a result of the last move, this
- routine will delete the old group numbers from GroupList and
- reassign group numbers. */
- void RelabelGroups()
- {
- unsigned short i, j, x, y;
- for (i = 0; i < DeletedGroupCount; i++)
- {
- /* Relabel all higher groups */
- for (y = 0; y <= 18; y++)
- for (x = 0; x <= 18; x++)
- if (goboard[x][y].GroupNum > DeletedGroups[i])
- goboard[x][y].GroupNum = goboard[x][y].GroupNum - 1;
- /* Move the groups down */
- for (y = DeletedGroups[i]; y < GroupCount; y++)
- GroupList[y] = GroupList[y+1];
- /* fix the group numbers stored in the deleted list */
- for (j = i+1; j < DeletedGroupCount; j++)
- if (DeletedGroups[j] > DeletedGroups[i])
- DeletedGroups[j] -= 1;
- GroupCount -= 1;
- }
- }
-
- /* Returns liberty count for x, y intersection. Sets marked to true
- for each liberty */
- short CountAndMarkLibs( x, y )
- short x, y;
- {
- short cnt = 0;
- if ((x > 0) && (goboard[x-1][y].Val == EMPTY)
- && (goboard[x-1][y].marked == FALSE))
- { cnt++; goboard[x-1][y].marked = TRUE; }
- if ((x < 18) && (goboard[x+1][y].Val == EMPTY)
- && (goboard[x+1][y].marked == FALSE))
- { cnt++; goboard[x+1][y].marked = TRUE; }
- if ((y > 0) && (goboard[x][y-1].Val == EMPTY)
- && (goboard[x][y-1].marked == FALSE))
- { cnt++; goboard[x][y-1].marked = TRUE; }
- if ((y < 18) && (goboard[x][y+1].Val == EMPTY)
- && (goboard[x][y+1].marked == FALSE))
- { cnt++; goboard[x][y+1].marked = TRUE; }
- return cnt;
- }
-
- /* Determine the number of liberties for a group given the group code
- num */
- void CountLiberties( code )
- short code;
- {
- short x, y, libcnt = 0;
- for (y = 0; y <= 18; y++)
- for (x = 0; x <= 18; x++)
- if (goboard[x][y].GroupNum == code)
- libcnt += CountAndMarkLibs( x, y );
- EraseMarks();
- GroupList[code].liberties = libcnt;
- }
-
- void CheckForEye( x, y, groups, cnt, recheck )
- short x, y, groups[4], cnt, *recheck;
- {
- short i;
- for (i = 0; i < (cnt-1); i++)
- if (groups[i] != groups[i+1])
- {
- /* Mark liberty for false eye check */
- goboard[x][y].marked = TRUE;
- (*recheck)++;
- return;
- }
- /* It is an eye */
- GroupList[groups[i]].eyes += 1;
- }
-
- /* Set the eye count for the groups */
- void CountEyes()
- {
- short i, x, y, wgroups[4], bgroups[4], wcnt, bcnt, max, cnt, recheck = 0,
- eye;
- for (i = 1; i <= GroupCount; i++)
- GroupList[i].eyes = 0;
- for (y = 0; y <= 18; y++)
- for (x = 0; x <= 18; x++)
- {
- if (goboard[x][y].Val != EMPTY) continue;
- cnt = Connect( WHITE, x, y, wgroups, &wcnt, bgroups, &bcnt );
- max = Maxlibs( x, y );
- if (cnt == max && wcnt == 1 && bcnt == 0)
- GroupList[wgroups[0]].eyes += 1;
- else if (cnt == max && bcnt == 1 && wcnt == 0)
- GroupList[bgroups[0]].eyes += 1;
- else if (cnt == max && ( bcnt == 0 || wcnt == 0 ))
- {
- goboard[x][y].marked = TRUE;
- recheck++;
- }
- }
- /* Now recheck marked liberties to see if two or more one eye
- groups contribute to a false eye */
- if (recheck == 0) return;
- for (y = 0; y <= 18; y++)
- for (x = 0; x <= 18; x++)
- if (goboard[x][y].marked)
- {
- recheck--;
- goboard[x][y].marked = FALSE;
- Connect( WHITE, x, y, wgroups, &wcnt, bgroups, &bcnt );
- /* If all the groups have at least one eye then all the
- groups are safe from capture because of the common
- liberty at x, y */
- eye = TRUE;
- for (i = 0; i < wcnt; i++)
- if (GroupList[wgroups[i]].eyes == 0) eye = FALSE;
- if (eye)
- for (i = 0; i < wcnt; i++)
- GroupList[wgroups[i]].eyes += 1;
- for (i = 0; i < bcnt; i++)
- if (GroupList[bgroups[i]].eyes == 0) eye = FALSE;
- if (eye)
- for (i = 0; i < bcnt; i++)
- GroupList[bgroups[i]].eyes += 1;
- if (recheck == 0) return;
- }
- }
-
- #ifdef DEBUG
- /* Print group information */
- PrintGroupInfo()
- {
- short i;
- for (i = 1; i <= GroupCount; i++)
- printf( "Group number %d\n\tEyes: %d\nLibs: %d\n", i, GroupList[i].eyes,
- GroupList[i].liberties );
- }
- #endif
-
- void printGroupReport(x, y)
- short x, y;
- {
- short g;
- if (! groupInfo) return;
- g = goboard[x][y].GroupNum;
- intrInitScratchArea();
- intrPuts( "Group " ); intrPutshort( g ); intrPutLn();
- intrPuts( "Stones: " );
- intrPutshort( GroupList[g].count ); intrPutLn();
- intrPuts( "Libs: " );
- intrPutshort( GroupList[g].liberties );
- intrPutLn();
- intrPuts( "Eyes: " );
- intrPutshort( GroupList[g].eyes );
- }
-
- char horizontal[] = "191817161514131211109 8 7 6 5 4 3 2 1 ";
- char vertical[] = "ABCDEFGHJKLMNOPQRST";
-
- /* Takes an x, y board intersection and returns the algebraic name */
- void goCoord( x, y, coord )
- short x, y;
- char coord[];
- {
- coord[0] = vertical[x];
- coord[1] = horizontal[2 * y];
- coord[2] = horizontal[2 * y + 1];
- }
-
-