tak/tak.c

1630 lines
42 KiB
C
Raw Normal View History

2021-02-16 11:56:34 +11:00
// TODO: call tak
// TODO: trap ctrl-c
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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%d%s "
#define LINEPART "--------------"
#define TRUE (-1)
#define FALSE (0)
#define ALL (-1)
#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 aivalue[MAXW*MAXH];
#define BLOCKED -12345
char cmdbuf[MAX_CMD_LEN];
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;
char movelog[256];
int debug = FALSE;
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], "");
}
}
}
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], "");
}
}
gameover = 0;
turn = firstturn;
turnnumber = 1;
starttiles = getstarttiles(size);
startcaps = getstartcapstones(size);
for (i=0; i<MAXPLAYERS; i++) {
tilesleft[i] = starttiles;
capsleft[i] = startcaps;
hasroad[i] = FALSE;
roundpoints[i] = 0;
}
ai[0] = FALSE;
ai[1] = FALSE;
//ai[1] = TRUE;
for (i=0; i<MAXPLAYERS; i++) {
roundpoints[i] = 0;
}
winner = -1;
printf("%s=======================\n",BOLD);
printf("===== ROUND %2d/%2d =====\n", curround,numrounds);
printf("=======================%s\n\n",reset);
printf("%s%s%s%s%s is playing first this round.%s\n\n", getpcol(turn), BOLD, getpname(turn), reset,BOLD,reset);
return FALSE;
}
int setupgame(int sz, int nrounds) {
int i;
if ((sz < 3) || (sz > 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<MAXPLAYERS; i++) {
roundpoints[i] = 0;
gamepoints[i] = 0;
}
curround = 1;
numrounds = nrounds;
winner = -1;
return FALSE;
}
void cleanup(void) {
int x,y;
for (y=0; y < h; y++) {
for (x=0; x < w; x++) {
strcpy(board[y*w+x], "");
}
}
// fix colours
printf("%s",reset); fflush(stdout);
}
char getmodxy(int x, int y) {
return getmod(board[y*w+x]);
}
char getmod(char *stack) {
char ch = 0;
if (!stack) return ' ';
ch = stack[strlen(stack)-1];
if (ismod(ch)) {
return modprintable(ch);
}
return ' ';
}
char modprintable(char mod) {
switch (mod) {
case 'C':
case 'k':
mod = '^';
break;
case 'S':
case 's':
mod = '|';
break;
}
return mod;
}
char counttiles(char *stack) {
int pos,count=0;
if (!stack) return 0;
for (pos = 0; stack[pos]; pos++) {
if (!ismod(stack[pos])) count++;
}
return count;
}
char getowner(char *stack) {
int pos;
if (!stack) return ' ';
for (pos = strlen(stack)-1; pos>=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];
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, FALSE);
printf("%s\n",cmd);
} else {
fgets(cmd ,MAX_CMD_LEN, stdin);
if (cmd) {
cmd[strlen(cmd)-1] = 0;
}
}
printf("%s",reset);
return cmd;
}
char *genaicmd(char *cmd, int showdb) {
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;
}
}
// mark anywhere we definitely cannot go
for (y=0; y < h; y++) {
for (x=0; x < w; x++) {
int idx = y*w+x;
if (!canplacetile(turn, x, y, ' ', NULL) &&
!canmoveanyto(turn, x, y, NULL)) {
aivalue[idx] = BLOCKED;
} else if (xywouldgivewinto(1-turn, x, y)) {
modaivalue(x, y, 999);
}
}
}
// determine the best priority
best = -1;
for (y=0; y < h; y++) {
for (x=0; x < w; x++) {
if ((aivalue[y*w+x] != BLOCKED) &&
(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 (canmoveanyto(turn, x, y, str)) {
// move
sprintf(cmd, "%s", str);
} else {
// place
sprintf(cmd, "%c%c", xpossel,ypossel);
}
}
return cmd;
}
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]);
}
}
// force owner of given position to given player
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 modaivalue(int x,int y,int amt) {
int idx=y*w+x;
if (aivalue[idx] == BLOCKED) {
return TRUE;
}
aivalue[idx] += 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<MAXPLAYERS; i++) {
hasroad[i] = playerhasroads(i, board);
}
}
int checkendgame(void) {
int i;
// someone resigned?
if (gotwinner()) return TRUE;
// either player has a road?
for (i=0; i<MAXPLAYERS; i++) {
if (hasroad[i]) return TRUE;
}
// any player out of tiles? (most flatstones wins)
for (i=0; i<MAXPLAYERS; i++) {
if (tilesleft[i] < 0) return TRUE;
if (capsleft[i] < 0) return TRUE;
}
// board covered? (most flatstones wins)
if (isboardfull()) {
return TRUE;
}
return FALSE;
}
int getstartcapstones(int sz) {
switch (sz) {
case 3:
case 4:
return 0;
case 5:
case 6:
return 1;
case 8:
return 2;
}
return -1;
}
int getstarttiles(int sz) {
switch (sz) {
case 3:
return 10;
case 4:
return 15;
case 5:
return 21;
case 6:
return 30;
case 8:
return 50;
}
return -1;
}
int nextround(void) {
curround++;
// swap who goes first.
firstturn = 1 - firstturn;
return 0;
}
int nextturn(void) {
turn = 1 - turn;
if (turn == firstturn) {
turnnumber++;
}
// reset game log variables?
topofmove = ' ';
flattened = FALSE;
return 0;
}
char getpchar(int n) {
if ((n == 0) || (n == 'b')) {
return 'b';
} else {
return 'r';
}
}
char *getpcol(int pnum) {
switch (pnum) {
case 0:
case 'b':
return BLUE;
case 1:
case 'r':
return RED;
}
return reset;
}
char *getpstr(int n) {
if ((n == 0) || (n == 'b')) {
return "b";
} else {
return "r";
}
}
char *getpname(int n) {
if ((n == 0) || (n == 'b')) {
return "blue";
} else {
return "red";
}
}
int getpnum(int n) {
if ((n == 0) || (n == 'b')) {
return 0;
} else if ((n == 1) || (n == 'r')) {
return 1;
} else {
return -1;
}
}
void xytopos(int x, int y, int *xch, int *ych) {
if (xch) *xch = 'a' + x;
if (ych) *ych = '1' + y;
}
void postoxy(int xch, int ych, int *x, int *y) {
if (x) *x = xch - 'a';
if (y) *y = ych - '1';
}
int movetile(int count,char xch,char ych, char dir, char *dropstr) {
int pos,newpos,x,y,mod,origcount;
int newxch,newych;
char countstr[4];
char moving[MAXSTACK*2];
char dropping[MAXSTACK*2];
char *bpos,*newbpos,*p,*dp,*mp;
int pass;
int xmod,ymod;
int numtomove;
char err[256];
postoxy(xch,ych,&x, &y);
if (!isvalidxypos(xch, ych)) {
printf("* invalid cell reference '%c%c'\n",xch,ych);
return TRUE;
}
// remember the top tile mod, so we can log it in the game log
topofmove = getmodxy(x,y);
normalise(&topofmove);
pos = y*w+x;
bpos = board[pos];
origcount = counttiles(bpos);
if (count == ALL) {
// if no count specified, move max stack limit
if (origcount <= size) {
count = origcount;
strcpy(countstr, "ALL");
} else {
count = size;
sprintf(countstr, "%d", count);
}
} else {
sprintf(countstr, "%d", count);
}
if (!canmovefrom(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;
}
// validate count
if (count > 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,&xmod,&ymod);
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 += xmod;
y += ymod;
// 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
// TODO: can yoi flatten yourbown 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*10];
// add ansi color codes
makecolorstr(dropping, colorstr);
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);
}
// 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]--;
}
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 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 && !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 (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;
int xmod[]={0, 1, 0, -1}; /// nesw
int ymod[]={1, 0, -1, 0}; /// nesw
char localstr[256];
int db = TRUE;
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 each stack size
for (trystacksize = 1; trystacksize <= maxtry; trystacksize++) {
int x,y;
int stillgood = TRUE;
int xch,ych;
// 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 mlre locations
} else {
char *z;
// not there yet.
// must drop at least one
// TODO: drop more than 1, prefer our color
//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;
char temp[10];
sprintf(temp, "%1d",numtodrop);
strcat(localstr, temp);
if (db) printf("db: ** numtodrop: %d. tiles in stack: %d (%s)\n", numtodrop, counttiles(mp), mp);
// no more tiles in moving stack??
if (numtodrop >= counttiles(mp)) {
// this stack size.
stillgood = FALSE;
} else {
// 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 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,"");
// is there anything at the source location?
if (!strlen(bpos) ) {
sprintf(why, "empty source location\n");
return FALSE;
}
// does the other player own the source location?
if (getowner(bpos) == getpchar(1 - who)) {
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 (mod == '^') {
// capstone?
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 (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)) {
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 (!strcmp(cmd, "\\a")) { // simlulate AI move for us, with debug info
char cmd[256];
genaicmd(cmd, TRUE);
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) {
logmove(turn, cmd, topofmove);
}
return err;
}
char *makecolorstr(char *orig, char *newstr) {
char *p;
char *curcol;
strcpy(newstr, "");
for (p = orig; *p; p++) {
if (!ismod(*p)) {
curcol = getpcol(getpchar(*p));
strcat(newstr, curcol);
}
strncat(newstr, p, 1);
}
strcat(newstr, reset);
return newstr;
}
void showaivalues(int best) {
int x,y;
char ch;
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];
// show owner
printf(FORMAT_AIVAL, val == best ? GREEN : YELLOW, val, reset);
}
printf(FORMAT_ROWTAIL, ch);
printf("\n");
// TODO: move this to a function
// show board stack
printf(FORMAT_ROWHEAD, ch);
for (x=0; x < w; x++) {
char *str = board[y*w+x];
// show stack
if (str) {
char origstr[MAXSTACK*10];
char colorstr[MAXSTACK*10];
sprintf(origstr,FORMAT_STACK, str ? str : " ");
// add ansi color codes
makecolorstr(origstr, colorstr);
//strcpy(colorstr,origstr);
printf("%s", colorstr);
} else {
printf(FORMAT_STACK, " ");
}
}
printf(FORMAT_ROWTAIL, ch);
ch--;
printf("\n");
line();
}
printf(FORMAT_ROWHEAD, ' ');
ch = 'a';
for (x=0; x < w; x++) {
printf(FORMAT_COLHEAD, ch++);
}
printf(FORMAT_ROWTAIL, ' ');
printf("\n");
/*
for (i=0;i<MAXPLAYERS; i++) {
showboardbinary(i);
}
*/
}
void showboard(void) {
int x,y;
char ch;
printf("\n%sRound %d - Turn %d (%s%s%s%s):\n", BOLD, curround, turnnumber, getpcol(turn), BOLD, getpname(turn), reset);
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--) {
/*
printf(FORMAT_ROWHEAD, ch);
for (x=0; x < w; x++) {
char *str = board[y*w+x];
// show owner
printf(FORMAT_OWNER, getpcol(getowner(str)),getowner(str), getmod(str), reset);
}
printf(FORMAT_ROWTAIL, ch);
printf("\n");
*/
printf(FORMAT_ROWHEAD, ch);
for (x=0; x < w; x++) {
char *str = board[y*w+x];
// show stack
if (str) {
char origstr[MAXSTACK*10];
char colorstr[MAXSTACK*10];
sprintf(origstr,FORMAT_STACK, str ? str : " ");
// add ansi color codes
makecolorstr(origstr, colorstr);
//strcpy(colorstr,origstr);
printf("%s", colorstr);
} else {
printf(FORMAT_STACK, " ");
}
}
printf(FORMAT_ROWTAIL, ch);
ch--;
printf("\n");
line();
}
printf(FORMAT_ROWHEAD, ' ');
ch = 'a';
for (x=0; x < w; x++) {
printf(FORMAT_COLHEAD, ch++);
}
printf(FORMAT_ROWTAIL, ' ');
printf("\n");
/*
for (i=0;i<MAXPLAYERS; i++) {
showboardbinary(i);
}
*/
}
int xtoleftshift(int num) {
return (w-num-1);
}
void showboardbinary(int pnum) {
int x,y;
int line[h];
int allones = pow(2,w)-1;
int hline = 0,vline = allones;
char ch;
printf("Binary board for %s:\n", getpname(pnum));
// show top heading
printf(" - ");
ch = 'a';
for (x=0; x < w; x++) {
printf("%c",ch++);
}
printf("\n");
ch = '1' + h - 1;
for (y=h-1; y >= 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
}
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 xmod[]={0, 1, 0, -1}; /// nesw
int ymod[]={1, 0, -1, 0}; /// nesw
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])));
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 in the current dir?
newx = x + xmod[d];
newy = y + ymod[d];
xytopos(newx, newy, &newxch, &newych);
if (db) fprintf(stderr,"** db: Checking forward (dir %d) from %c%c (%c%c)\n", d, xch,ych,newxch,newych);
if (xyvalidforroad(b, who,newx,newy)) {
x = newx;
y = newy;
moved = TRUE;
} else { // turn left
newx = x + xmod[leftdir];
newy = y + ymod[leftdir];
xytopos(newx, newy, &newxch, &newych);
if (db) fprintf(stderr,"** db: Checking left (dir %d) from %c%c (%c%c)\n", leftdir, xch,ych,newxch,newych);
if (xyvalidforroad(b, who,newx,newy)) {
x = newx;
y = newy;
d = leftdir;
moved = TRUE;
} else { // turn right
newx = x + xmod[rightdir];
newy = y + ymod[rightdir];
xytopos(newx, newy, &newxch, &newych);
if (db) fprintf(stderr,"** db: Checking right (dir %d) from %c%c (%c%c)\n", rightdir, xch,ych,newxch,newych);
if (xyvalidforroad(b, who,newx,newy)) {
x = newx;
y = newy;
d = rightdir;
moved = TRUE;
} else {
// reverse direction
if (--d < 0) d = 3;
if (--d < 0) d = 3;
if (db) fprintf(stderr,"** db: Turning around (dir %d) from %c%c (%c%c)\n", d, xch,ych,newxch,newych);
if (xyvalidforroad(b, who,newx,newy)) {
x = newx;
y = newy;
moved = TRUE;
} else {
if (db) fprintf(stderr,"** db: Couldn't turn around - no roads.\n");
done = TRUE;
}
}
}
}
if (moved) {
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 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<MAXPLAYERS; i++) {
gamepoints[i] += roundpoints[i];
}
// show totals
showscores();
// TODO: bold this
printf("\n%s--- Press ENTER for next round ---%s\n", BOLD, reset);
// wait for key
fgets(cmdbuf ,MAX_CMD_LEN, stdin);
}
void showscores(void) {
int i;
char temp[64];
printf("Current scores:\n");
for (i=0; i<MAXPLAYERS; i++) {
sprintf(temp, "%s%12s%s",getpcol(i), getpname(i), reset);
printf(" %s: %s%d points%s\n", temp, getpcol(i),gamepoints[i], reset);
}
}
void pickfirstplayer() {
if (rand() % 2 == 0) firstturn = 0; else firstturn = 1;
}
int normalise(char *a) {
int donesomething = FALSE;
if (!a) return FALSE;
switch (*a) {
case '^':
case 'k':
*a = 'C'; donesomething = TRUE; break;
case 's':
case '|':
*a = 'S'; donesomething = TRUE; break;
}
return donesomething;
}
void logmove(int who, char *txt, char append) {
char temp[256];
char *p;
// use standard notation as per https://ustak.org/portable-tak-notation/
strcpy(temp, txt);
for (p = temp ; *p; p++) {
normalise(p);
}
if ((append == 'S') || (append == 'C')) {
strncat(temp, &append, 1);
}
if (flattened) {
strcat(temp, "*");
}
// first player's turn?
if (who == firstturn) {
sprintf(movelog, "%s", temp);
} else {
strcat(movelog, " ");
strcat(movelog, temp);
printf("%s%s%d.%s%s %s%s\n", YELLOW, BOLD, turnnumber, reset, YELLOW, movelog,reset);
}
}
int main(int argc, char *argv[]) {
init();
atexit(cleanup);
if (setupgame(5, 2)) {
exit(1);
}
srand(time(NULL));
pickfirstplayer(); // randomize 'firstturn'
while (curround <= numrounds) {
setupround();
while (!gameover) {
showboard();
for (readcmd(cmdbuf); parsecmd(cmdbuf); readcmd(cmdbuf));
updateroadstatus();
if (checkendgame()) {
showboard();
determinewinner();
gameover = 1;
}
nextturn();
}
nextround();
}
}