// TODO: trap ctrl-c // TODO: trap ctrl-d // TODO: taktak #include #include #include #include #include #include #include "tak.h" #define MAXSTACK 10 #define MAXW 8 #define MAXH 8 #define MAX_CMD_LEN 100 #define MAXPLAYERS 2 #define FORMAT_ROWHEAD "%c " #define FORMAT_ROWTAIL "| %c" #define FORMAT_COLHEAD "| %c " #define FORMAT_COLTAIL "| %c " //#define FORMAT_OWNER "| %c%c |" //#define FORMAT_STACK "|[%-10s]|" #define FORMAT_OWNER "| %s%c%c%s " #define FORMAT_STACK "| %-10s " #define FORMAT_AIVAL "| %s%5d%s /%2d " #define LINEPART "--------------" #define TRUE (-1) #define FALSE (0) #define ALL (-1) #define ANY (-1) #define NOBOLD (0) #define BLACK "\e[0;30m" #define BLUE "\e[0;36m" #define RED "\e[0;31m" #define YELLOW "\e[0;33m" #define BOLD "\e[1m" //#define BLUE "\e[0;34m" #define GREEN "\e[0;32m" #define reset "\e[0m" char board[MAXW*MAXH][MAXBOARDSTRLEN]; int lastmove[MAXW*MAXH]; // how many tiles were moved here last turn int aivalue[MAXW*MAXH]; int aidef[MAXW*MAXH]; #define BLOCKED -999 char cmdbuf[MAX_CMD_LEN]; char aireason[MAXW*MAXH][2048]; int tilesleft[MAXPLAYERS]; int capsleft[MAXPLAYERS]; int hasroad[MAXPLAYERS]; int gamepoints[MAXPLAYERS]; int roundpoints[MAXPLAYERS]; int ai[MAXPLAYERS]; // for game log char topofmove = ' '; int flattened = FALSE; int tak[MAXPLAYERS]; char movelog[256]; int debug = FALSE; int xmod[]={0, 1, 0, -1}; /// nesw int ymod[]={1, 0, -1, 0}; /// nesw enum direction {NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3}; int size=5; int w=5; int h=5; int curround, numrounds; int turnnumber = 0; int firstturn = 0; // who goes first int gameover = 0; int turn = 0; int winner = -1; void init(void) { int x,y; for (y=0; y < h; y++) { for (x=0; x < w; x++) { strcpy(board[y*w+x], ""); } } } void setlastmove(int x,int y,int ntiles) { lastmove[y*w+x] = ntiles; } int setupround(void) { int i; int x,y; int starttiles,startcaps; for (y=0; y < h; y++) { for (x=0; x < w; x++) { strcpy(board[y*w+x], ""); setlastmove(x,y,0); } } gameover = 0; turn = firstturn; turnnumber = 1; starttiles = getstarttiles(size); startcaps = getstartcapstones(size); for (i=0; i 8) || (sz == 7)) { printf("error - board size must be between 3 and 8 (but not 7).\n"); return TRUE; } w = h = size = sz; for (i=0; i=0; pos--) { if (!ismod(stack[pos])) break; } if (pos < 0) return ' '; return stack[pos]; } int isdir(char ch) { switch (ch) { case '>': case '<': case '+': case '-': return TRUE; } return FALSE; } int ismod(char ch) { switch (ch) { case 'C': case 'k': case '^': case 'S': case 's': case '|': return TRUE; } return FALSE; } char *getdirname(int dir) { switch (dir) { case '>': return "right"; case '<': return "left"; case '+': return "up"; case '-': return "down"; } return "UNKNOWN_DIR"; } char *getmodname(int mod) { switch (mod) { case 'C': case 'k': case '^': return "capstone"; case 'S': case '|': return "standing stone"; } return "flatstone"; } char *readcmd(char *cmd) { char turntext[64]; int x,y; // clear out last move for (y=0; y < h; y++) { for (x=0; x < w; x++) { setlastmove(x,y,0); } } if (turnnumber == 1) { sprintf(turntext, "SETUP: %s%s%s placing %s%s's%s initial tile", getpcol(turn),getpname(turn), reset, getpcol(1-turn),getpname(1-turn),reset); printf("R%d/%d-T%d. %s %s==> ",curround,numrounds,turnnumber, turntext, getpcol(turn)); } else { sprintf(turntext, "%s%s's%s turn", getpcol(turn),getpname(turn),reset); printf("R%d/%d-T%d. %s (%s%sstones:%s%s%d %scapstones%s%s:%d%s) %s==> ",curround,numrounds,turnnumber, turntext, getpcol(turn), BOLD, reset, getpcol(turn), tilesleft[turn], BOLD, reset,getpcol(turn),capsleft[turn],reset, getpcol(turn)); } printf("%s",BOLD); if (ai[turn]) { genaicmd(cmd, TRUE, (turnnumber == 1) ? TRUE : FALSE); printf("%s\n",cmd); } else { fgets(cmd ,MAX_CMD_LEN, stdin); if (cmd) { cmd[strlen(cmd)-1] = 0; } else { clearerr(stdin); } } printf("%s",reset); return cmd; } int rowhasmod(int sx, int sy, char mod, int who) { int count=0; int x; if (!isvalidxy(sx,sy)) { return 0; } for (x=0; x < w; x++) { if (getmodxy(x,sy) == mod) { if (who == ANY || getownernumxy(x,sy) == who) { count++; } } } return count; } int colhasmod(int sx, int sy, char mod, int who) { int count=0; int y; if (!isvalidxy(sx,sy)) { return 0; } for (y=0; y < h; y++) { if (getmodxy(sx,y) == mod) { if (who == ANY || getownernumxy(sx,y) == who) { count++; } } } return count; } int countownedinrow(int sx, int sy, int who) { int count=0; int x; char *s; if (!isvalidxy(sx,sy)) { return 0; } for (x=0; x < w; x++) { if (x != sx) { s = board[sy*w+x]; if (getpnum(getowner(s)) == who) { count++; } } } return count; } int countownedincol(int sx, int sy, int who) { int count=0; int y; char *s; if (!isvalidxy(sx,sy)) { return 0; } for (y=0; y < h; y++) { if (y != sy) { s = board[y*w+sx]; if (getpnum(getowner(s)) == who) { count++; } } } return count; } int countadjacent(int x, int y, int who) { int count=0,d; int newx,newy; char *s; if (!isvalidxy(x,y)) { return 0; } for (d=0; d <=3; d++) { newx = x + xmod[d]; newy = y + ymod[d]; //printf("* checking %d,%d for spaces owned by who=%d\n",newx,newy,who); if (isvalidxy(newx,newy)) { //printf("* %d,%d is valid\n",newx,newy); s = board[newy*w+newx]; //printf("* %d,%d is owned by: %c (player %d)\n",newx,newy,getowner(s),getpnum(getowner(s))); if (getpnum(getowner(s)) == who) { count++; } } } return count; } void aiupdatetile(int x, int y) { int multi; int xpos,ypos; int idx = y*w+x; int n,a,b,c; char temp[1024]; xytopos(x,y,&xpos,&ypos); if (!canplacetile(turn, x, y, ' ', NULL) && !canmoveanyto(turn, x, y, NULL, TRUE)) { aivalue[idx] = BLOCKED; } else if (xywouldgivewinto(turn, x, y)) { // would make us win modaivalue(x, y, 999, "would make us win"); modaivalue_around(x, y, 10, "would make us win"); //adjust surrounding modaidef_around(x, y, 1); modaidef(x, y, -999); } else if (xywouldgivewinto(1-turn, x, y)) { // would make other player win modaivalue(x, y, 800, "would give win to other player"); modaivalue_around(x, y, 12, "would give win to other player"); //adjust surrounding modaidef(x, y, 10); modaidef_around(x, y, 2); } // if we already own it, probably dont want to go there if (getownernumxy(x, y) == turn) { modaivalue(x,y, -15, "we own this space already"); /* } else { n = countadjacent(x,y,turn); // modify based on adjacent controlled locations snprintf(temp, 1024, "we own %d adjacent cells", n); modaivalue(x,y, n * 1, temp); // controlled by us */ } if (getownernumxy(x, y) == turn) { multi = 1; } else { multi = 2; } n = countadjacent(x,y,1 - turn); snprintf(temp, 1024, "other player owns %d adjacent cells", n); modaivalue(x,y, n * multi, temp); // controlled by other player modaidef_around(x, y, countadjacent(x,y,1 - turn) * 1); // If we DONT own this location, consider other stones in the same row/col if (getownernumxy(x, y) != turn) { // walls actively count against value // row n = countownedinrow(x,y,turn); snprintf(temp, 1024, "we own %d cells in this row", n); modaivalue(x,y, n * 6, temp); n = rowhasmod(x,y,'|',turn) + rowhasmod(x,y,'|',1-turn); snprintf(temp, 1024, "%d walls in this row", n); modaivalue(x,y, -(n * 7), temp); n = rowhasmod(x,y,'^',1-turn); snprintf(temp, 1024, "%d opposing capstones in this row", n); modaivalue(x,y, -(n * 10), temp); // col n = countownedincol(x,y,turn); snprintf(temp, 1024, "we own %d cells in this col", n); modaivalue(x,y, n * 6, temp); n = colhasmod(x,y,'|',ANY); snprintf(temp, 1024, "%d walls in this col", n); modaivalue(x,y, -(n * 7), temp); n = colhasmod(x,y,'^',1-turn); snprintf(temp, 1024, "%d opposing capstones in this col", n); modaivalue(x,y, -(n * 10), temp); // opponent tiles in row, offset by our walls/caps multi = 2; a = rowhasmod(x,y,'^',turn); multi -= a; b = rowhasmod(x,y,'|',turn); multi -= b; if (getownernumxy(x, y) == ' ') { // cells noone owns are more valuable c = 1; multi += 1; } else { c=0; } n = countownedinrow(x,y,1-turn) - rowhasmod(x,y,'|',1 - turn); // don't include their walls snprintf(temp, 1024, "other player has %d cells in row (we have %d caps + %d walls == %d multiplier)", n, a, b, multi); if (multi > 0) { modaivalue(x,y, n * multi, temp); } modaidef(x,y, n * 1); // opponent tiles in col, offset by our walls/caps multi = 2; a = colhasmod(x,y,'^',turn); multi -= a; b = colhasmod(x,y,'|',turn); multi -= b; if (getownernumxy(x, y) == ' ') { // cells noone owns are more valuable c = 1; multi += 1; } else { c=0; } n = countownedincol(x,y,1-turn) - colhasmod(x,y,'|',1 - turn); // don't include their walls snprintf(temp, 1024, "other player has %d cells in col (empty=%d, we have %d caps + %d walls == %d multiplier)", n, c, a, b, multi); if (multi > 0) { modaivalue(x,y, n * multi, temp); } modaidef(x,y, n * 1); } // reduce defence required for each or our caps in this row/col a = colhasmod(x,y,'^',turn); b = rowhasmod(x,y,'^',turn); modaidef(x,y, (a+b) * -1); a = colhasmod(x,y,'|',turn); b = rowhasmod(x,y,'|',turn); modaidef(x,y, (a+b) * -1); } char *genaicmd(char *cmd, int showdb, int useworst) { int x,y,best; int ncandidates; int candidate[MAXW*MAXH]; int sel, xsel, ysel,xpossel,ypossel; // zero all priorities for (y=0; y < h; y++) { for (x=0; x < w; x++) { aivalue[y*w+x] = 500; aidef[y*w+x] = 0; strcpy(aireason[y*w+x], ""); } } // mark anywhere we definitely cannot go for (y=0; y < h; y++) { for (x=0; x < w; x++) { aiupdatetile(x,y); } } // determine the best priority if (useworst) best=99999; else best = -1; for (y=0; y < h; y++) { for (x=0; x < w; x++) { if (aivalue[y*w+x] != BLOCKED) { if (useworst) { if (aivalue[y*w+x] < best) best = aivalue[y*w+x]; } else { if (aivalue[y*w+x] > best) best = aivalue[y*w+x]; } } } } if (showdb) { showaivalues(best); } //printf("*** best value is = %d\n", best); // pick randomly from the best priority ncandidates = 0; for (y=0; y < h; y++) { for (x=0; x < w; x++) { if (aivalue[y*w+x] == best) { candidate[ncandidates++] = y*w+x; } } } //printf("*** ncandidates = %d\n", ncandidates); if (ncandidates == 0) { sprintf(cmd, "\\r"); } else { char str[256]; sel = rand() % ncandidates; idxtoxy(candidate[sel], &xsel, &ysel); xytopos(xsel,ysel,&xpossel, &ypossel); // we place a tile here or move here? if (canplacetile(turn, xsel, ysel, ' ', NULL)) { char mod = ' '; int chance = aidef[ysel*w+xsel]*10; // wall or cap? if (pctchance(chance)) { if ((capsleft[turn] > 0) && pctchance(chance) && canplacetile(turn, xsel, ysel, '^', NULL)) { mod = '^'; } else if (canplacetile(turn, xsel, ysel, '|', NULL)) { mod = '|'; } } // place if (mod == ' ') { sprintf(cmd, "%c%c", xpossel,ypossel); } else { sprintf(cmd, "%c%c%c", mod,xpossel,ypossel); } } else if (canmoveanyto(turn, xsel, ysel, str, TRUE)) { // move sprintf(cmd, "%s", str); } } return cmd; } int pctchance(int num) { if (rand() % 100 < num) { return TRUE; } return FALSE; } int xywouldgivewinto(int who, int tx, int ty) { char board2[MAXW*MAXH][MAXBOARDSTRLEN]; int x,y,wouldwin = FALSE; // take copy of board for (y=0; y < h; y++) { for (x=0; x < w; x++) { strcpy(board2[y*w+x], board[y*w+x]); } } // if player COULD go there, // force owner of given position to given player if (canplacetile(who, tx, ty, ' ', NULL) || canmoveanyto(who, tx, ty, NULL, FALSE)) { strcat(board2[ty*w+tx], getpstr(who)); if ( playerhasroads(who, board2)) { wouldwin = TRUE; } } return wouldwin; } void idxtoxy(int idx, int *x, int *y) { if (x) *x = idx % w; if (y) *y = idx / w; } int modaidef(int x,int y,int amt) { int idx=y*w+x; if (!isvalidxy(x,y)) { return TRUE; } aidef[idx] += amt; return FALSE; } int modaivalue(int x,int y,int amt, char *reason) { char temp[512]; int idx=y*w+x; if (!isvalidxy(x,y)) { return TRUE; } if (aivalue[idx] == BLOCKED) { return TRUE; } aivalue[idx] += amt; snprintf(temp, 512, " [%s%-3d] %s\n", (amt > 0) ? "+" : "", amt, reason ? reason : ""); strcat(aireason[idx], temp); return FALSE; } int modaivalue_around(int x,int y,int amt, char *reason) { int newx,newy,d; int xpos,ypos; char localreason[1024]; if (!isvalidxy(x,y)) { return TRUE; } xytopos(x,y,&xpos,&ypos); snprintf(localreason, 1024, "(from adjacent tile %c%c): %s", xpos,ypos, reason); for (d=0; d <=3; d++) { newx = x + xmod[d]; newy = y + ymod[d]; modaivalue(newx, newy, amt, localreason); } return FALSE; } int modaidef_around(int x,int y,int amt) { int newx,newy,d; if (!isvalidxy(x,y)) { return TRUE; } for (d=0; d <=3; d++) { newx = x + xmod[d]; newy = y + ymod[d]; modaidef(newx, newy, amt); } return FALSE; } int isboardfull() { int x,y; for (y=0; y < h; y++) { for (x=0; x < w; x++) { if (!counttiles(board[y*w+x])) return FALSE; } } return TRUE; } void updateroadstatus(void) { int i; // update road status for each player for (i=0; i origcount) { printf("* %s can't move %s tiles %s from %c%c - only %d there\n",getpname(turn),countstr,getdirname(dir), xch,ych,origcount); return TRUE; } dirtoxy(dir,&xmodamt,&ymodamt); numtomove = count; // First pass: validate each step for (pass=0; pass <= 1; pass++) { int remaining,numtodrop; if (pass == 1) { if (debug) printf("- %s%s%s is moving %d tiles %s from %c%c, dropping %s\n", getpcol(turn), getpname(turn), reset, count, getdirname(dir), xch, ych, strlen(dropstr) ? dropstr : "*"); } //printf("PASS %d\n",pass); postoxy(xch,ych,&x, &y); // pick up stack of tiles to move p = bpos + strlen(bpos)-1; //printf("debug: bpos is: %s\n", bpos); //printf("debug: p is: %s\n", p); if (ismod(*p)) p--; p -= (count-1) ; //printf("debug: new p is: %s\n", p); strcpy(moving, p); if (pass == 1) { // actually remove tiles from source location *p = 0; } // begin at start of drop string dp = dropstr; // start dropping from bottom of moving stack mp = moving; newxch = xch; newych = ych; while ((remaining = counttiles(mp))) { //printf("remaining in mp: %d\n",remaining);; //printf("move %s from %d,%d (%c%c)", getdirname(dir), x,y,newxch,newych); x += xmodamt; y += ymodamt; // still on the board? if (!isvalidxy(x,y)) { printf("* %s can't move %s tiles %s from %c%c - would move off board with %d tiles remaining\n",getpname(turn),countstr,getdirname(dir), xch,ych, remaining); return TRUE; } xytopos(x, y, &newxch, &newych); newpos = y*w+x; newbpos = board[newpos]; //printf("now at %d,%d (%c%c)", x,y,newxch,newych); mod = getmod(newbpos); if (!canmoveto(turn, x, y, err)) { printf("* %s can't move %s tiles %s from %c%c - %s\n",getpname(turn),countstr,getdirname(dir), xch,ych, err); return TRUE; } else if ((mod == '|') && (remaining > 1)) { // stack of > 1 moving onto standing stone printf("* %s can't move %s tiles %s from %c%c - %d tiles blocked by %s standing stone at %c%c\n",getpname(turn),countstr,getdirname(dir), xch,ych, remaining, getpname(getowner(newbpos)), newxch,newych); return TRUE; } else if ((mod == '|') && (getmod(mp) != '^')) { // non-capstone moving onto standing stone printf("* %s can't move %s tiles %s from %c%c - only capstone can move onto %s standing stone at %c%c\n",getpname(turn),countstr,getdirname(dir), xch,ych, getpname(getowner(newbpos)), newxch,newych); return TRUE; } else if ((mod == '|') && (remaining == 1) && (getmod(mp) == '^')) { // single capstone moving onto a standing stone if (pass == 1) { if (debug) printf(" - %s%s%s capstone flattens %s%s%s standing stone at %c%c!\n",getpcol(turn),getpname(turn),reset, getpcol(getowner(newbpos)),getpname(getowner(newbpos)), reset, newxch,newych); newbpos[strlen(newbpos)-1] = 0;; flattened = TRUE; } } // drop tiles if (!dp || *dp == 0) { numtodrop = remaining; } else { numtodrop = *dp - '0'; } if (numtodrop > remaining) { printf("* %s can't move %s tiles %s from %c%c with '%s' - want to drop %d tiles at %c%c but only %d remain\n",getpname(turn),countstr,getdirname(dir), xch,ych, dropstr, numtodrop, newxch,newych, remaining); return TRUE; } // construct string of tiles to drop strncpy(dropping, mp, numtodrop); dropping[numtodrop] = 0; // retain top capstone/standingstone if (ismod(*(mp+numtodrop))) { strncat(dropping, mp+numtodrop, 1); mp++; // havk } if (pass == 1) { char colorstr[MAXSTACK*30]; // add ansi color codes makecolorstr(dropping, colorstr, NOBOLD); if (debug) { printf(" - dropping '%s' (%d) at %c%c. remaining: '%s' (%d)\n", colorstr, numtodrop, newxch,newych, (mp+numtodrop), counttiles(mp+numtodrop)); } // actually drop tiles here strcat(newbpos, dropping); if (numtodrop > 0) { setlastmove(x,y,numtodrop); } } // remove dropped tiles from moving stack mp += numtodrop; // advance to next drop count entry dp++; } } return FALSE; } void dirtoxy(char dir, int *x, int *y) { *x = 0; *y = 0; switch (dir) { case '>': *x = 1; *y = 0; break; case '<': *x = -1; *y = 0; break; case '+': *x = 0; *y = 1; break; case '-': *x = 0; *y = -1; break; } } int placetile(int who,char xch,char ych, char mod) { int pos,x,y; char *bpos; char err[256]; if (mod) mod = modprintable(mod); postoxy(xch,ych,&x, &y); pos = y*w+x; bpos = board[pos]; if (!canplacetile(who, x, y, mod, err)) { // is this space available? printf("* %s\n",err); return TRUE; } if (debug) printf("-- placing %s %s at %c%c\n", getpname(who), getmodname(mod), xch, ych); strcat(bpos, getpstr(who)); if (mod) { strncat(bpos, &mod, 1); } if (mod == '^') { capsleft[who]--; } else { tilesleft[who]--; } setlastmove(x,y,1); return FALSE; } int canplacetile(int who, int x, int y, int mod, char *why) { int idx,xch,ych; char *bpos; xytopos(x,y,&xch, &ych); idx = y*w+x; bpos = board[idx]; if (why) strcpy(why,""); // placing capstone on first turn? if (turnnumber == 1 && ((mod == '|') || (mod == '^'))) { if (why) sprintf(why,"can't place %s %s at %c%c - can only place flat tiles during setup, mod is '%c'\n",getpname(who),getmodname(mod),xch,ych,mod); return FALSE; } if ((mod == '^') && (capsleft[who] <= 0)) { if (why) sprintf(why, "can't place %s %s at %c%c - no capstones left\n",getpname(who),getmodname(mod),xch,ych); return FALSE; } if (strlen(bpos) > 0) { if (why) sprintf(why, "can't place %s %s at %c%c - not free\n",getpname(who),getmodname(mod),xch,ych); return FALSE; } return TRUE; } int canmoveanyto(int who, int tx, int ty, char *why, int safe) { int x,y; if (!canmoveto(who, tx, ty, why)) return FALSE; if (why) strcpy(why,""); // for each tile we control, see if it can move to given spot for (y=0; y < h; y++) { for (x=0; x < w; x++) { int ok = TRUE; int count; if( (x == tx) && (y == ty)) { ok = FALSE; } if (ok && (x != tx) && (y != ty)) { // same row/col ok = FALSE; } if (ok) { if (safe) { if (!willmovefrom(who, x, y, why)) { // got somethong which is safe to move here ok = FALSE; } } else { if (!canmovefrom(who, x, y, why)) { // got somethong to move here ok = FALSE; } } } count = counttiles(board[y*w+x]); if (ok && (x == tx) && abs(tx - x) > count) { // distance ok? ok = FALSE; } if (ok && (y == ty) && abs(ty - y) > count) { // distance ok? ok = FALSE; } // can this stack get one of our color to there??? if (ok && canmakedropstr(who, x,y,tx,ty,why)) { return TRUE; } } } return FALSE; } int canmakedropstr(int who, int sx, int sy, int dx, int dy, char *str) { // return TRUE if we succeeded int count,maxtry,trystacksize, d; char moving[MAXSTACK*2]; char *mp; char *p; char *spos; int smod; char localstr[256]; int db = FALSE; int sizes[3][MAXSTACK]; int scount[3]; int tryidx,n,i; spos=board[sy*w+sx]; smod = getmod(spos); count = counttiles(board[sy*w+sx]); if (count < 1) { return FALSE; } if (count > size) maxtry = size; else maxtry = count; // which dir? if ((sx == dx) && (sy < dy)) { d = 0; } else if ((sx == dx) && (sy > dy)) { d = 2; } else if ((sy == dy) && (sx > dx)) { d = 3; } else if ((sy == dy) && (sx < dx)) { d = 1; } else { return FALSE; } // try sizes that leave us in control of the initial tile // first. for (i=0; i<= 2; i++) { // 0 good, 1 bad, 2 good then bad scount[i]=0; } if (db) { printf("db: ** maxtry is.%d\n",maxtry); } for (trystacksize = 1; trystacksize <= maxtry; trystacksize++) { p = spos + strlen(spos)-1; if (ismod(*p)) p--; p -= (trystacksize-1) ; // we're on the 1st tile that's moving if (p>spos && (*(p-1) == getpchar(who))) { // we own top of remaining stack sizes[0][scount[0]++] = trystacksize; } else { // the other player owns it sizes[1][scount[1]++] = trystacksize; } } // combine lists, first good then bad for (i=0;i<=1;i++) { for (n=0;n= scount[0] ? 'b' : 'g' ); } printf("\n"); } // try each stack size for (tryidx = 0; tryidx < scount[2]; tryidx++) { int x,y; int stillgood = TRUE; int xch,ych; trystacksize = sizes[2][tryidx]; // pick up stack of tiles to move p = spos + strlen(spos)-1; if (ismod(*p)) p--; p -= (trystacksize-1) ; strcpy(moving, p); mp = moving; // max distance we can move before hitting wall/cap/edge? x = sx; y = sy; xytopos(x, y, &xch, &ych); stillgood = TRUE; sprintf(localstr, "%d%c%c%c", trystacksize,xch,ych,getdirchar(d)); while (stillgood) { // move one cell toward dst x += xmod[d]; y += ymod[d]; if (!isvalidxy(x,y) || getmodxy(x,y) == '^' || (getmodxy(x,y) == '|' && (smod != '^' || counttiles(mp) != 1)) ) { // hit a blocker - stop stillgood = FALSE; } else { // are we there yet if (x == dx && y == dy) { char temp[64]; // dump the rest here and we're done sprintf(temp, "%1d",counttiles(mp)); strcat(localstr, temp); if (str) strcpy(str,localstr); return TRUE; // TODO: go further to control more locations } else { char *z; // not there yet. // must drop at least one // TODO: figure out how many we can drop and // still reach dst //z = strchr(mp, getpchar(who)); // stone to drop. z = mp; // drop 1 if (z) { // if match, drop so it's on top int numtodrop = (z - moving) + 1; int didsomething = TRUE; char temp[10],owner; // would dropping this amt let the // other player win? while (didsomething) { didsomething = FALSE; owner = *(z+numtodrop-1); if (getpnum(owner) == 1-who) { if (xywouldgivewinto(1-who, x, y)) { if (++numtodrop >= counttiles(mp)) { break; } didsomething = TRUE; } } } // no more tiles in moving stack?? if (numtodrop >= counttiles(mp)) { // this stack size. stillgood = FALSE; } else { sprintf(temp, "%1d",numtodrop); strcat(localstr, temp); if (db) printf("db: ** numtodrop: %d. tiles in stack: %d (%s)\n", numtodrop, counttiles(mp), mp); // remove dropped tiles from moving stack mp += numtodrop; if (db) printf("db: ** new tiles in stack: %d (%s)\n", counttiles(mp), mp); } } else { // if not, fail this stack size. stillgood = FALSE; } } } } } return FALSE; } char getdirchar(int dir) { switch (dir) { case 1: return '>'; case 3: return '<'; case 0: return '+'; case 2: return '-'; } return '='; } int willmovefrom(int who, int x, int y, char *why) { // would moving away from here make the other player win? if (xywouldgivewinto(who, x, y)) { if (why) sprintf(why, "Moving away would open up a win to other player"); return FALSE; } return canmovefrom(who, x, y, why); } int canmovefrom(int who, int x, int y, char *why) { int mod; int idx,xch,ych; char *bpos; xytopos(x,y,&xch, &ych); idx = y*w+x; bpos = board[idx]; mod = getmod(bpos); if (why) strcpy(why,""); if (turnnumber == 1) { if (why) sprintf(why, "cant move on first setup turn.\n"); return FALSE; } // is there anything at the source location? if (!strlen(bpos) ) { if (why) sprintf(why, "empty source location\n"); return FALSE; } // does the other player own the source location? if (getowner(bpos) == getpchar(1 - who)) { if (why) sprintf(why, "not owner of source location\n"); return FALSE; } return TRUE; } int canmoveto(int who, int x, int y, char *why) { int mod; int idx,xch,ych; char *bpos; xytopos(x,y,&xch, &ych); idx = y*w+x; bpos = board[idx]; mod = getmod(bpos); if (why) strcpy(why,""); if (turnnumber == 1) { if (why) sprintf(why, "cant move on first setup turn.\n"); return FALSE; } if (mod == '^') { // capstone? if (why) sprintf(why, "%s capstone at %c%c\n",getpname(getowner(bpos)), xch,ych); return FALSE; } return TRUE; } int isvalidxy(int x, int y) { if (x >= 0 && x < w && y >= 0 && y < w) { return TRUE; } return FALSE; } int isvalidxypos(char x, char y) { char firstx = 'a'; char lastx = 'a' + w - 1; char firsty = '1'; char lasty = '1'+h-1; if (x >= firstx && x <= lastx && y >= firsty && y <= lasty) { return TRUE; } return FALSE; } int parsecmd(char *cmd) { int err = FALSE; int mod=0; char *p=cmd; if (!p) return TRUE; if (!strcmp(cmd, "\\r")) { // resign winner = 1-turn; logmove(turn, cmd, ' '); return FALSE; } else if (strstr(cmd, "\\W ")) { // check if loc would give us a win int xpos,ypos,x,y,wouldwin; p = cmd + 3; xpos = p[0]; ypos = p[1]; postoxy(xpos,ypos,&x,&y); wouldwin = xywouldgivewinto(turn, x, y); printf(" *** would %c%c give us a win? %s\n", xpos,ypos, wouldwin ? "YES" : "no"); return TRUE; } else if (!strcmp(cmd, "\\b")) { showboard(); return TRUE; } else if (strstr(cmd, "\\w ")) { int xpos,ypos,x,y; p = cmd + 3; xpos = p[0]; ypos = p[1]; postoxy(xpos,ypos,&x,&y); printf("%sAI thoughts for %s%c%c%s%s: [%s%d%s%s]\n",YELLOW,BOLD,xpos,ypos,reset,YELLOW, BOLD,aivalue[y*w+x],reset,YELLOW); printf("%s%s\n",aireason[y*w+x],reset); return TRUE; } else if (!strcmp(cmd, "\\s")) { showscores("Current"); return TRUE; } else if (strstr(cmd, "\\m ")) { // check if we can move to loc char temp[256]; int xpos,ypos,x,y; p = cmd + 3; xpos = p[0]; ypos = p[1]; postoxy(xpos,ypos,&x,&y); if (canmoveanyto(turn, x, y, temp, FALSE)) { printf(" *** %c%c is reachable: %s\n", xpos,ypos, temp); } else { printf(" *** can't move to %c%c\n", xpos,ypos); } return TRUE; } else if (strstr(cmd, "\\c ")) { // count adjacent tiles to loc int xpos,ypos,x,y,num,i; p = cmd + 3; xpos = p[0]; ypos = p[1]; postoxy(xpos,ypos,&x,&y); for (i = 0; i <= 1; i++) { num = countadjacent(x,y,i); printf("%c%c has %d adjacent %s%s%s controlled locations.\n",xpos,ypos,num,getpcol(i),getpname(i),reset); } return TRUE; } else if (!strcmp(cmd, "\\a")) { // simlulate AI move for us, with debug info char cmd[256]; genaicmd(cmd, TRUE, FALSE); printf("%sAI suggested move for %s%s%s%s: %s%s%s%s\n", YELLOW, getpcol(turn),getpname(turn),reset,YELLOW,getpcol(turn), BOLD, cmd, reset); return TRUE; } if (ismod(p[0])) { mod = p[0]; p++; } if (strchr(p, 'q')) { gameover = TRUE; } else if (isalpha(p[0]) && isdigit(p[1]) && isdir(p[2])) { // move all tiles in direction int xpos = p[0]; int ypos = p[1]; int dir = p[2]; if (movetile(ALL,xpos,ypos,dir,p+3)) err = TRUE; } else if (isdigit(p[0]) && isalpha(p[1]) && isdigit(p[2]) && isdir(p[3]) ) { // move x tiles in direction int count = p[0] - '0'; int xpos = p[1]; int ypos = p[2]; int dir = p[3]; if (count > size) { printf("* can't move more than %d tiles\n",size); err = TRUE; } else { if (movetile(count,xpos,ypos,dir,p+4)) err = TRUE; } } else if ((strlen(p) == 2) && isalpha(p[0]) && isdigit(p[1]) ) { // place tile int xpos = p[0]; int ypos = p[1]; if (isvalidxypos(xpos,ypos)) { if (placetile((turnnumber == 1) ? (1-turn) : turn,xpos,ypos,mod)) err = TRUE; } else { printf("* invalid cell reference '%c%c'\n",xpos,ypos); err = TRUE; } } else { printf("* bad command '%s'\n",cmd); err = TRUE; } if (err && ai[turn]) { char temp[10]; printf("...ai made an error?!"); fgets(temp ,MAX_CMD_LEN, stdin); exit(1); } if (!err) { checkfortak(turn); checkfortak(1-turn); logmove(turn, cmd, topofmove); } return err; } char *makecolorstr(char *orig, char *newstr, int lastmovecount) { char *p, *curcol, *boldstart = NULL; if (lastmovecount > 0) { int count = 0; for (p = orig+strlen(orig)-1; p >= orig; p--) { if (*p == getpchar(0) || *p == getpchar(1)) { count++; if (count == lastmovecount) { boldstart = p; break; } } } } // printf("\nmcs(): got lmc = %d, boldidx = %d\n",lastmovecount, boldidx); strcpy(newstr, ""); for (p = orig; *p; p++) { if (!ismod(*p)) { curcol = getpcol(getpchar(*p)); strcat(newstr, curcol); } if (boldstart && p >= boldstart && *p != ' ') { strcat(newstr, BOLD); } strncat(newstr, p, 1); } strcat(newstr, reset); return newstr; } void showaivalues(int best) { int x,y; char ch; printf("\n"); printf(FORMAT_ROWHEAD, ' '); ch = 'a'; for (x=0; x < w; x++) { printf(FORMAT_COLHEAD, ch++); } printf(FORMAT_ROWTAIL, ' '); printf("\n"); line(); ch = '1' + h - 1; for (y=h-1; y >= 0; y--) { // show ai priority values printf(FORMAT_ROWHEAD, ch); for (x=0; x < w; x++) { int val = aivalue[y*w+x]; int def = aidef[y*w+x]; char *col; if (val == best) { col = GREEN; } else if (val == BLOCKED) { col = RED; } else { col = YELLOW; } printf(FORMAT_AIVAL, col, val, reset, def); } printf(FORMAT_ROWTAIL, ch); printf("\n"); // TODO: move this to a function // show board stack showboardrow(board, y); ch--; } printf(FORMAT_ROWHEAD, ' '); ch = 'a'; for (x=0; x < w; x++) { printf(FORMAT_COLHEAD, ch++); } printf(FORMAT_ROWTAIL, ' '); printf("\n"); /* for (i=0;i= 0; y--) { showboardrow(board, y); ch--; } printf(FORMAT_ROWHEAD, ' '); ch = 'a'; for (x=0; x < w; x++) { printf(FORMAT_COLHEAD, ch++); } printf(FORMAT_ROWTAIL, ' '); printf("\n"); /* for (i=0;i= 0; y--) { line[y] = 0; for (x=0; x < w; x++) { char *str = board[y*w+x]; if ((getmod(str) != '|') && getowner(str) != ' ' && (getpnum(getowner(str)) == pnum)) { int this; this = 1 << xtoleftshift(x); line[y] |= this; } } printbinaryline(ch--, line[y], TRUE); } //printbinaryline('X', vline); for (y=h-1; y >= 0; y--) { hline |= line[y]; vline &= line[y]; //printbinaryline('x', vline); } /* printf("raw:\n"); ch = '1' + h - 1; for (y=h-1; y >= 0; y--) { line[y] = 0; printf("%c - ", ch--); // show line for (x=0; x < w; x++) { char *str = board[y*w+x]; if ((getmod(str) != '|') && getowner(str) != ' ' && (getpnum(getowner(str)) == pnum)) { printf("%d",1); } else { printf("%d",0); } } printf("\n"); } */ printf("horizontal line:\n"); printbinaryline('H', hline, FALSE); printf(" - %s\n",(hline == allones) ? "YES" : ""); // all 1s = yes printf("vertical line:\n"); printbinaryline('V', vline, FALSE); printf(" - %s\n",(vline) ? "YES" : ""); // all 1s = yes } void roadlog(char *dname, int dir, int xch, int ych, int newx,int newy) { int newxch,newych; xytopos(newx, newy, &newxch, &newych); fprintf(stderr,"** db: Checking %s (dir %d xm=%d,ym=%d) from %c%c (%c%c == %d,%d)\n", dname, dir, xmod[dir],ymod[dir],xch,ych,newxch,newych,newx,newy); } int xyvalidforroad(char (*b)[MAXBOARDSTRLEN], int who, int x, int y) { char *cell; cell = b[y*w+x]; if (isvalidxy(x,y) && (getpnum(getowner(cell)) == who) && (getmod(cell) != '|')) { return TRUE; } return FALSE; } int checkxyforroad(char (*b)[MAXBOARDSTRLEN], int who, int sx, int sy, int sd) { int x,y,newx,newy,d,xch,ych,newxch,newych; int startxch,startych; int startdir,done,turncount; int db = 0; int visited[MAXW*MAXH]; // mark everywhere as not visited for (y=0; y < h; y++) { for (x=0; x < w; x++) { visited[y*w+x] = 0; } } x=sx; y=sy; d=sd; startdir=d; xytopos(x, y, &xch, &ych); startxch = xch; startych = ych; if (db) fprintf(stderr,"** db: looking for %s roads from %c%c (owned by %c / %d)\n", getpname(who), xch,ych, getowner(b[sy*w+sx]), getpnum(getowner(b[sy*w+sx]))); // check initial cell if (!xyvalidforroad(b, who,x,y)) { if (db) fprintf(stderr,"** db: %c%c not valid for road - stopping\n", xch,ych); return FALSE; } if (getpnum(getowner(b[sy*w+sx])) != who) { if (db) fprintf(stderr,"** db: %s doesn't own %c%c - stopping\n", getpname(who), xch,ych); return FALSE; } done = FALSE; turncount = 0; while (!done) { int moved = FALSE; int leftdir,rightdir; // figure out where left/right is. leftdir = (d - 1); if (leftdir < 0) leftdir = 3; rightdir = (d + 1); if (rightdir >= 4) rightdir = 0; // is there a valid cell to the left? newx = x + xmod[leftdir]; newy = y + ymod[leftdir]; if (db) roadlog("left", leftdir, xch,ych,newx,newy); if (xyvalidforroad(b, who,newx,newy)) { x = newx; y = newy; d = leftdir; moved = TRUE; } if (!moved) { // forward? newx = x + xmod[d]; newy = y + ymod[d]; if (db) roadlog("forward", d, xch,ych,newx,newy); if (xyvalidforroad(b, who,newx,newy)) { x = newx; y = newy; moved = TRUE; } } if (!moved) { // right newx = x + xmod[rightdir]; newy = y + ymod[rightdir]; if (db) roadlog("right", rightdir, xch,ych,newx,newy); if (xyvalidforroad(b, who,newx,newy)) { x = newx; y = newy; d = rightdir; moved = TRUE; } } if (!moved) { // reverse direction if (--d < 0) d = 3; if (--d < 0) d = 3; newx = x + xmod[d]; newy = y + ymod[d]; if (db) roadlog("backward", d, xch,ych,newx,newy); if (xyvalidforroad(b, who,newx,newy)) { x = newx; y = newy; moved = TRUE; } } if (!moved) { if (db) fprintf(stderr,"** db: Couldn't turn around - no roads.\n"); } if (moved) { xytopos(x, y, &newxch, &newych); if (db) fprintf(stderr,"** db: %c%c is owned by us, moving there.\n", newxch,newych); xch = newxch; ych = newych; turncount = 0; // back to start? if ((x == sx) && (y == sy)) { if (db) fprintf(stderr,"** db: We're back at the start (%c%c) - no roads.\n", startxch,startych); done = TRUE; // other side? } else if ((sx == 0) && (x == w-1)) { if (db) fprintf(stderr,"** db: Horizontal w->e path from from %c%c to %c%c!\n", startxch,startych,xch,ych); return TRUE; } else if ((sx == w-1) && (x == 0)) { if (db) fprintf(stderr,"** db: Horizontal e->w path from from %c%c to %c%c!\n", startxch,startych,xch,ych); return TRUE; } else if ((sy == 0) && (y == h-1)) { if (db) fprintf(stderr,"** db: Vertical s->n path from from %c%c to %c%c!\n", startxch,startych,xch,ych); return TRUE; } else if ((sy == h-1) && (y == 0)) { if (db) fprintf(stderr,"** db: Vertical n->s path from from %c%c to %c%c!\n", startxch,startych,xch,ych); return TRUE; } } else { // didn't move? done = TRUE; } } if (db) fprintf(stderr,"** db: No roads found. Returning false.\n"); return FALSE; } int playerhasroads(int pnum, char (*b)[]) { int x,y; // check for roads from bottom to top for (x=0; x < w; x++) { if (checkxyforroad(b, pnum, x, 0, NORTH)) return TRUE; } // check for roads from left to right for (y=h-1; y >= 0; y--) { if (checkxyforroad(b, pnum, 0, y, EAST)) return TRUE; } return FALSE; } void printbinaryline(char prefix, int num, int wantcr) { int x; // show line number printf("%c - ", prefix); // show line for (x=0; x < w; x++) { if (num & (1 << xtoleftshift(x))) { printf("%d",1); } else { printf("%d",0); } } if (wantcr) printf("\n"); } void line(void) { int x; for (x=0; x < w; x++) { printf("%s", LINEPART); } printf("\n"); } int gotwinner(void) { if (winner >= 0) { return TRUE; } return FALSE; } void determinefinalwinner(void) { if (gamepoints[0] > gamepoints[1]) { winner = 0; } else if (gamepoints[1] > gamepoints[0]) { winner = 1; } else { winner = -1; } if (winner == -1) { } else { printf("%s** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **%s\n", getpcol(winner), reset); printf("%s ***** %s%s%s is the victor! %s*****%s\n", getpcol(winner), BOLD,getpname(winner), reset, getpcol(winner), reset); printf("%s** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **%s\n", getpcol(winner), reset); } // show totals showscores("Final"); // TODO: bold this printf("\n%s--- Press ENTER to end ---%s\n", BOLD, reset); // wait for key fgets(cmdbuf ,MAX_CMD_LEN, stdin); } void determinewinner(void) { int x,y,i; int flatstones[MAXPLAYERS]; int stonepoints,sizepoints; // win by roads? if (!gotwinner()) { if (hasroad[0] && hasroad[1]) { // current turn wins a tie winner = turn; } else if (hasroad[0]) { winner = 0; } else if (hasroad[1]) { winner = 1; } } if (!gotwinner()) { // count flatstones for (y=0; y < h; y++) { for (x=0; x < w; x++) { if (!getmod(board[y*w+x])) { flatstones[getpnum(getowner(board[y*w+x]))]++; } } } if (flatstones[0] > flatstones[1]) { winner = 0; } else if (flatstones[0] < flatstones[1]) { winner = 0; } else { // current turn wins a tie winner = turn; } } if (!gotwinner()) { // shouldn't be possible... winner = turn; } // only winner gets points // 1 point for every board square (size*size) // 1 point for each of their remaining tiles sizepoints = (size * size); stonepoints = tilesleft[winner]; roundpoints[winner] = sizepoints + stonepoints; printf("%s*** %s%s%s%s%s wins round %d and earns %d points (%d from board size, %d from remaining tiles)%s\n", BOLD, getpcol(winner), BOLD, getpname(winner), reset, BOLD, curround, roundpoints[winner], sizepoints, stonepoints,reset); // update totals for (i=0; i