-rw-r--r-- | noncore/games/go/amigo.c | 656 |
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; +} |