summaryrefslogtreecommitdiff
path: root/noncore/games/go/amigo.c
Side-by-side diff
Diffstat (limited to 'noncore/games/go/amigo.c') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/games/go/amigo.c656
1 files changed, 656 insertions, 0 deletions
diff --git a/noncore/games/go/amigo.c b/noncore/games/go/amigo.c
new file mode 100644
index 0000000..cd61013
--- a/dev/null
+++ b/noncore/games/go/amigo.c
@@ -0,0 +1,656 @@
+/* Go started 4/17/88 by Todd R. Johnson */
+/* 8/8/89 cleaned up for first release */
+/* Public Domain */
+
+#include "go.h"
+#include "goplayutils.h"
+#include "amigo.h"
+
+
+extern char *playReason;
+extern short playLevel, showTrees;
+
+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 blackTerritory,whiteTerritory;
+short blackPrisoners, whitePrisoners;
+short showMoveReason = FALSE,
+ groupInfo = FALSE,
+ whitePassed = FALSE,
+ blackPassed = FALSE;
+
+
+/* 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;
+ short grouplist[4];
+ short 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 (!LegalPoint(tx,ty))
+ continue;
+ if (goboard[tx][ty].Val == color)
+ {
+ total++;
+ if (!member(goboard[tx][ty].GroupNum, fGroups, *fCnt))
+ fGroups[(*fCnt)++] = goboard[tx][ty].GroupNum;
+ }
+ else if (goboard[tx][ty].Val == opcolor)
+ {
+ total++;
+ if (!member(goboard[tx][ty].GroupNum, eGroups, *eCnt))
+ eGroups[(*eCnt)++] = goboard[tx][ty].GroupNum;
+ }
+ }
+ 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--;
+ if (y == 0 || y == 18)
+ cnt--;
+ return cnt;
+}
+
+DeleteGroupFromStone(x,y)
+ short x,y;
+{
+ if (goboard[x][y].Val != EMPTY)
+ GroupCapture(goboard[x][y].GroupNum);
+}
+
+/* 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 (LegalPoint(tx,ty) && goboard[tx][ty].Val == EMPTY)
+ cnt++;
+ }
+ return cnt;
+}
+
+void
+EraseMarks()
+{
+ register short i;
+ register struct bRec *gpt = &goboard[0][0];
+
+
+ for (i=0; i<361; gpt++,i++)
+ gpt->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 || Suicide(color,x,y))
+ return FALSE;
+
+ 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++;
+ for (i = 0; i < fcnt; i++)
+ if (fgroups[i] != lowest)
+ MergeGroups(lowest, fgroups[i]);
+ /* Fix the liberties of the resulting group */
+ CountLiberties(lowest);
+ }
+ else
+ {
+ /* Isolated stone. Create new group. */
+ GroupCount++;
+ 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;
+
+
+ ForeachPoint(y,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++;
+ 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();
+}
+
+/* Remove a captured group from the board and fix the liberties of any
+ adjacent groups. Fixes prisoner count. Sets KoX and KoY */
+/*-- update display of captured stones -neilb --*/
+void
+GroupCapture(code)
+ short code;
+{
+ short x, y;
+
+ if (GroupList[code].color == BLACK)
+ blackPrisoners += GroupList[code].count;
+ else
+ whitePrisoners += GroupList[code].count;
+ intrPrisonerReport(blackPrisoners, whitePrisoners);
+ ForeachPoint(y,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--;
+ else /* Stone removed so increment opcolor */
+ for (i = 0; i < ecnt; i++)
+ GroupList[egroups[i]].liberties++;
+}
+
+void
+goSetHandicap(handicap)
+ int handicap;
+{
+ if (handicap < 2)
+ return;
+
+ GoPlaceStone(BLACK,3,3);
+ GoPlaceStone(BLACK,15,15);
+
+ if (handicap >= 3)
+ GoPlaceStone(BLACK,15,3);
+ if (handicap >= 4)
+ GoPlaceStone(BLACK,3,15);
+ if (handicap == 5 || handicap == 7 || handicap == 9)
+ GoPlaceStone(BLACK,9,9);
+ if (handicap >= 6)
+ {
+ GoPlaceStone(BLACK,15,9);
+ GoPlaceStone(BLACK,3,9);
+ }
+ if (handicap >= 8)
+ {
+ GoPlaceStone(BLACK,9,15);
+ GoPlaceStone(BLACK,9,3);
+ }
+}
+
+void
+goRestart(handicap)
+ int handicap;
+{
+ register short i;
+ register struct bRec *gpt = &goboard[0][0];
+
+
+ GroupCount = 0;
+ ko = FALSE;
+ blackPrisoners = whitePrisoners = 0;
+ intrPrisonerReport(0, 0);
+ for (i=0; i<361; gpt++,i++)
+ {
+ gpt->Val = EMPTY;
+ gpt->GroupNum = 0;
+ }
+ goSetHandicap(handicap);
+}
+
+
+/* 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 */
+ ForeachPoint(y,x)
+ if (goboard[x][y].GroupNum > DeletedGroups[i])
+ goboard[x][y].GroupNum--;
+ /* 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]--;
+ GroupCount--;
+ }
+}
+
+/* Returns liberty count for x, y intersection. Sets marked to true
+ for each liberty */
+short
+CountAndMarkLibs( x, y)
+ short x, y;
+{
+ short tx,ty,i;
+ short cnt = 0;
+
+
+ for (i=0;i<4;i++)
+ {
+ tx = x + xVec[i];
+ ty = y + yVec[i];
+ if (LegalPoint(tx,ty) && goboard[tx][ty].Val == EMPTY
+ && goboard[tx][ty].marked == FALSE)
+ {
+ cnt++;
+ goboard[tx][ty].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;
+
+ ForeachPoint(y,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;
+
+ ForeachPoint(y,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++;
+ else if (cnt == max && bcnt == 1 && wcnt == 0)
+ GroupList[bgroups[0]].eyes++;
+ 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;
+
+ ForeachPoint(y,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++;
+ 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++;
+ if (recheck == 0)
+ return;
+ }
+}
+
+
+short foo[19][19];
+
+/*----------------------------------------------------------------
+-- CountUp() --
+-- Count up final scores at the end of the game. --
+----------------------------------------------------------------*/
+CountUp( wtotal, btotal )
+ int *wtotal, *btotal;
+{
+ short x,y;
+ short CountFromPoint();
+ short vv;
+ char buff[512];
+
+
+ blackTerritory = whiteTerritory = 0;
+ ForeachPoint(y,x)
+ {
+ goboard[x][y].marked = FALSE;
+ foo[x][y] = CNT_UNDECIDED;
+ }
+ ForeachPoint(y,x)
+ if (goboard[x][y].Val==EMPTY && foo[x][y]==CNT_UNDECIDED)
+ {
+ FillPoints(x,y,CountFromPoint(x,y));
+ }
+
+ *wtotal = whiteTerritory + blackPrisoners;
+ *btotal = blackTerritory + whitePrisoners;
+ /*
+ sprintf(buff,"White : %3d territory + %3d prisoners = %d\n\
+Black : %3d territory + %3d prisoners = %d\n\n%s.\n",
+ whiteTerritory,blackPrisoners,*wtotal,
+ blackTerritory,whitePrisoners,*btotal,
+ (*btotal>*wtotal?"Black wins":(*wtotal>*btotal?"White wins":
+ "A draw")));
+
+
+
+ XtVaSetValues(message,XtNstring,buff,0);
+ printf( "CountUp() %s", buff );
+ */
+}
+
+FillPoints(x,y,val)
+ short x,y,val;
+{
+ int i;
+ short tx,ty;
+
+
+ if ((foo[x][y] = val) == CNT_BLACK_TERR)
+ blackTerritory++;
+ else if (val == CNT_WHITE_TERR)
+ whiteTerritory++;
+ for (i=0;i<4;i++)
+ {
+ tx = x + xVec[i];
+ ty = y + yVec[i];
+ if (!LegalPoint(tx,ty))
+ continue;
+ if (goboard[tx][ty].Val==EMPTY && foo[tx][ty]==CNT_UNDECIDED)
+ FillPoints(tx,ty,val);
+ }
+}
+
+short
+CountFromPoint(x,y)
+ short x,y;
+{
+ int i;
+ short tx,ty;
+ short blkcnt=0,whtcnt=0;
+ short baz;
+
+
+ goboard[x][y].marked = TRUE;
+ for (i=0;i<4;i++)
+ {
+ tx = x + xVec[i];
+ ty = y + yVec[i];
+ if (!LegalPoint(tx,ty))
+ continue;
+ if (goboard[tx][ty].Val == BLACK)
+ blkcnt++;
+ else if (goboard[tx][ty].Val == WHITE)
+ whtcnt++;
+ else
+ {
+ if (goboard[tx][ty].marked)
+ continue;
+ baz = CountFromPoint(tx,ty);
+ if (baz == CNT_NOONE)
+ return CNT_NOONE;
+ else if (baz == CNT_BLACK_TERR)
+ blkcnt++;
+ else if (baz == CNT_WHITE_TERR)
+ whtcnt++;
+ }
+ if (blkcnt && whtcnt)
+ return CNT_NOONE;
+ }
+ if (blkcnt && !whtcnt)
+ return CNT_BLACK_TERR;
+ else if (whtcnt && !blkcnt)
+ return CNT_WHITE_TERR;
+ else
+ return CNT_UNDECIDED;
+}