tak/tak.c

2167 lines
57 KiB
C
Raw Permalink Normal View History

2021-02-16 11:56:34 +11:00
// TODO: trap ctrl-c
2021-02-17 23:30:12 +11:00
// TODO: trap ctrl-d
// TODO: taktak
2021-02-16 11:56:34 +11:00
#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 "
2021-02-17 21:50:31 +11:00
#define FORMAT_AIVAL "| %s%5d%s /%2d "
2021-02-16 11:56:34 +11:00
#define LINEPART "--------------"
#define TRUE (-1)
#define FALSE (0)
#define ALL (-1)
2021-02-17 23:30:12 +11:00
#define ANY (-1)
2021-02-21 17:19:03 +11:00
#define NOBOLD (0)
2021-02-16 11:56:34 +11:00
#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];
2021-02-21 17:19:03 +11:00
int lastmove[MAXW*MAXH]; // how many tiles were moved here last turn
2021-02-16 11:56:34 +11:00
int aivalue[MAXW*MAXH];
2021-02-17 21:50:31 +11:00
int aidef[MAXW*MAXH];
#define BLOCKED -999
2021-02-16 11:56:34 +11:00
char cmdbuf[MAX_CMD_LEN];
2021-02-17 21:50:31 +11:00
char aireason[MAXW*MAXH][2048];
2021-02-16 11:56:34 +11:00
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;
2021-02-17 21:50:31 +11:00
int tak[MAXPLAYERS];
2021-02-16 11:56:34 +11:00
char movelog[256];
int debug = FALSE;
2021-02-17 21:50:31 +11:00
int xmod[]={0, 1, 0, -1}; /// nesw
int ymod[]={1, 0, -1, 0}; /// nesw
2021-02-16 11:56:34 +11:00
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], "");
}
}
}
2021-02-21 17:19:03 +11:00
void setlastmove(int x,int y,int ntiles) {
lastmove[y*w+x] = ntiles;
}
2021-02-16 11:56:34 +11:00
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], "");
2021-02-21 17:19:03 +11:00
setlastmove(x,y,0);
2021-02-16 11:56:34 +11:00
}
}
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;
2021-02-17 21:50:31 +11:00
tak[i] = FALSE;
2021-02-16 11:56:34 +11:00
}
ai[0] = FALSE;
2021-02-17 21:50:31 +11:00
//ai[1] = FALSE;
ai[1] = TRUE;
2021-02-16 11:56:34 +11:00
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 ' ';
}
2021-02-21 17:19:03 +11:00
// boolean
int hasmod(char *stack) {
if (getmod(stack) == ' ') {
return FALSE;
}
return TRUE;
}
2021-02-16 11:56:34 +11:00
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;
}
2021-02-17 21:50:31 +11:00
int getownernumxy(int x, int y) {
return getpnum(getownerxy(x,y));
}
char getownerxy(int x, int y) {
char *s;
s = board[y*w+x];
return getowner(s);
}
2021-02-16 11:56:34 +11:00
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];
2021-02-21 17:19:03 +11:00
int x,y;
// clear out last move
for (y=0; y < h; y++) {
for (x=0; x < w; x++) {
setlastmove(x,y,0);
}
}
2021-02-16 11:56:34 +11:00
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]) {
2021-02-17 21:50:31 +11:00
genaicmd(cmd, TRUE, (turnnumber == 1) ? TRUE : FALSE);
2021-02-16 11:56:34 +11:00
printf("%s\n",cmd);
} else {
fgets(cmd ,MAX_CMD_LEN, stdin);
if (cmd) {
cmd[strlen(cmd)-1] = 0;
2021-02-21 17:19:03 +11:00
} else {
clearerr(stdin);
2021-02-16 11:56:34 +11:00
}
}
printf("%s",reset);
return cmd;
}
2021-02-17 21:50:31 +11:00
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) {
2021-02-17 23:30:12 +11:00
if (who == ANY || getownernumxy(x,sy) == who) {
count++;
}
2021-02-17 21:50:31 +11:00
}
}
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) {
2021-02-17 23:30:12 +11:00
if (who == ANY || getownernumxy(sx,y) == who) {
count++;
}
2021-02-17 21:50:31 +11:00
}
}
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");
2021-02-21 17:19:03 +11:00
/*
2021-02-17 21:50:31 +11:00
} 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
2021-02-21 17:19:03 +11:00
*/
2021-02-17 21:50:31 +11:00
}
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) {
2021-02-17 23:30:12 +11:00
// walls actively count against value
// row
n = countownedinrow(x,y,turn);
2021-02-17 21:50:31 +11:00
snprintf(temp, 1024, "we own %d cells in this row", n);
modaivalue(x,y, n * 6, temp);
2021-02-21 17:19:03 +11:00
n = rowhasmod(x,y,'|',turn) + rowhasmod(x,y,'|',1-turn);
2021-02-17 23:30:12 +11:00
snprintf(temp, 1024, "%d walls in this row", n);
modaivalue(x,y, -(n * 7), temp);
2021-02-21 17:19:03 +11:00
n = rowhasmod(x,y,'^',1-turn);
2021-02-17 23:30:12 +11:00
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);
2021-02-17 21:50:31 +11:00
modaivalue(x,y, n * 6, temp);
2021-02-17 23:30:12 +11:00
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);
2021-02-17 21:50:31 +11:00
// 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) {
2021-02-16 11:56:34 +11:00
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;
2021-02-17 21:50:31 +11:00
aidef[y*w+x] = 0;
strcpy(aireason[y*w+x], "");
2021-02-16 11:56:34 +11:00
}
}
// mark anywhere we definitely cannot go
for (y=0; y < h; y++) {
for (x=0; x < w; x++) {
2021-02-17 21:50:31 +11:00
aiupdatetile(x,y);
2021-02-16 11:56:34 +11:00
}
}
// determine the best priority
2021-02-17 21:50:31 +11:00
if (useworst) best=99999;
else best = -1;
2021-02-16 11:56:34 +11:00
for (y=0; y < h; y++) {
for (x=0; x < w; x++) {
2021-02-17 21:50:31 +11:00
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];
}
2021-02-16 11:56:34 +11:00
}
}
}
if (showdb) {
showaivalues(best);
}
2021-02-17 21:50:31 +11:00
//printf("*** best value is = %d\n", best);
2021-02-16 11:56:34 +11:00
// 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;
}
}
}
2021-02-17 21:50:31 +11:00
//printf("*** ncandidates = %d\n", ncandidates);
2021-02-16 11:56:34 +11:00
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?
2021-02-17 21:50:31 +11:00
if (canplacetile(turn, xsel, ysel, ' ', NULL)) {
char mod = ' ';
2021-02-21 17:19:03 +11:00
int chance = aidef[ysel*w+xsel]*10;
2021-02-17 21:50:31 +11:00
// 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)) {
2021-02-16 11:56:34 +11:00
// move
sprintf(cmd, "%s", str);
}
}
return cmd;
}
2021-02-17 21:50:31 +11:00
int pctchance(int num) {
if (rand() % 100 < num) {
return TRUE;
}
return FALSE;
}
2021-02-16 11:56:34 +11:00
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]);
}
}
2021-02-21 17:19:03 +11:00
// if player COULD go there,
2021-02-16 11:56:34 +11:00
// force owner of given position to given player
2021-02-21 17:19:03 +11:00
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;
}
2021-02-16 11:56:34 +11:00
}
return wouldwin;
}
void idxtoxy(int idx, int *x, int *y) {
if (x) *x = idx % w;
if (y) *y = idx / w;
}
2021-02-17 21:50:31 +11:00
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];
2021-02-16 11:56:34 +11:00
int idx=y*w+x;
2021-02-17 21:50:31 +11:00
if (!isvalidxy(x,y)) {
return TRUE;
}
2021-02-16 11:56:34 +11:00
if (aivalue[idx] == BLOCKED) {
return TRUE;
}
aivalue[idx] += amt;
2021-02-17 21:50:31 +11:00
snprintf(temp, 512, " [%s%-3d] %s\n", (amt > 0) ? "+" : "", amt, reason ? reason : "<unspecified>");
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);
}
2021-02-16 11:56:34 +11:00
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;
}
2021-02-17 21:50:31 +11:00
2021-02-16 11:56:34 +11:00
return FALSE;
}
2021-02-17 21:50:31 +11:00
void checkfortak(int who) {
int x,y;
// check for 'tak' condition
tak[who] = FALSE;
for (y=0; y < h; y++) {
for (x=0; x < w; x++) {
if (xywouldgivewinto(who, x, y)) {
2021-02-17 23:30:12 +11:00
if (canplacetile(who, x, y, ' ', NULL) ||
canmoveanyto(who, x, y, NULL, FALSE)) {
tak[who] = TRUE;
}
2021-02-17 21:50:31 +11:00
}
}
}
}
2021-02-16 11:56:34 +11:00
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];
2021-02-21 17:19:03 +11:00
char moving[MAXSTACK*30];
char dropping[MAXSTACK*30];
2021-02-16 11:56:34 +11:00
char *bpos,*newbpos,*p,*dp,*mp;
int pass;
2021-02-17 21:50:31 +11:00
int xmodamt,ymodamt;
2021-02-16 11:56:34 +11:00
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;
}
2021-02-17 21:50:31 +11:00
dirtoxy(dir,&xmodamt,&ymodamt);
2021-02-16 11:56:34 +11:00
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);
2021-02-17 21:50:31 +11:00
x += xmodamt;
y += ymodamt;
2021-02-16 11:56:34 +11:00
// 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) {
2021-02-21 17:19:03 +11:00
char colorstr[MAXSTACK*30];
2021-02-16 11:56:34 +11:00
// add ansi color codes
2021-02-21 17:19:03 +11:00
makecolorstr(dropping, colorstr, NOBOLD);
2021-02-16 11:56:34 +11:00
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);
2021-02-21 17:19:03 +11:00
if (numtodrop > 0) {
setlastmove(x,y,numtodrop);
}
2021-02-16 11:56:34 +11:00
}
// remove dropped tiles from moving stack
mp += numtodrop;
// advance to next drop count entry
dp++;
}
}
2021-02-21 17:19:03 +11:00
2021-02-16 11:56:34 +11:00
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]--;
}
2021-02-21 17:19:03 +11:00
setlastmove(x,y,1);
2021-02-16 11:56:34 +11:00
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;
}
2021-02-17 21:50:31 +11:00
int canmoveanyto(int who, int tx, int ty, char *why, int safe) {
2021-02-16 11:56:34 +11:00
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;
}
2021-02-17 21:50:31 +11:00
if (ok) {
if (safe) {
2021-02-21 17:19:03 +11:00
if (!willmovefrom(who, x, y, why)) { // got somethong which is safe to move here
2021-02-17 21:50:31 +11:00
ok = FALSE;
}
} else {
2021-02-21 17:19:03 +11:00
if (!canmovefrom(who, x, y, why)) { // got somethong to move here
2021-02-17 21:50:31 +11:00
ok = FALSE;
}
}
2021-02-16 11:56:34 +11:00
}
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???
2021-02-17 21:50:31 +11:00
if (ok && canmakedropstr(who, x,y,tx,ty,why)) {
2021-02-16 11:56:34 +11:00
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];
2021-02-17 23:30:12 +11:00
int db = FALSE;
int sizes[3][MAXSTACK];
int scount[3];
int tryidx,n,i;
2021-02-16 11:56:34 +11:00
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;
}
2021-02-17 23:30:12 +11:00
// 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);
}
2021-02-16 11:56:34 +11:00
for (trystacksize = 1; trystacksize <= maxtry; trystacksize++) {
2021-02-17 23:30:12 +11:00
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[i];n++) {
sizes[2][scount[2]++] = sizes[i][n];
}
}
if (db) {
printf("db: ** stack sizes to try: ");
for (tryidx = 0; tryidx < scount[2]; tryidx++) {
printf("%d(%c) ", sizes[2][tryidx],
tryidx >= scount[0] ? 'b' : 'g' );
}
printf("\n");
}
// try each stack size
for (tryidx = 0; tryidx < scount[2]; tryidx++) {
2021-02-16 11:56:34 +11:00
int x,y;
int stillgood = TRUE;
int xch,ych;
2021-02-17 23:30:12 +11:00
trystacksize = sizes[2][tryidx];
2021-02-16 11:56:34 +11:00
// 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;
2021-02-17 21:50:31 +11:00
// TODO: go further to control more locations
2021-02-16 11:56:34 +11:00
} else {
char *z;
// not there yet.
// must drop at least one
2021-02-17 21:50:31 +11:00
// TODO: figure out how many we can drop and
// still reach dst
2021-02-16 11:56:34 +11:00
//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;
2021-02-17 21:50:31 +11:00
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;
}
}
}
2021-02-16 11:56:34 +11:00
// no more tiles in moving stack??
if (numtodrop >= counttiles(mp)) {
// this stack size.
stillgood = FALSE;
} else {
2021-02-17 21:50:31 +11:00
sprintf(temp, "%1d",numtodrop);
strcat(localstr, temp);
if (db) printf("db: ** numtodrop: %d. tiles in stack: %d (%s)\n", numtodrop, counttiles(mp), mp);
2021-02-16 11:56:34 +11:00
// 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 '=';
}
2021-02-17 21:50:31 +11:00
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);
}
2021-02-16 11:56:34 +11:00
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,"");
2021-02-17 21:50:31 +11:00
if (turnnumber == 1) {
if (why) sprintf(why, "cant move on first setup turn.\n");
return FALSE;
}
2021-02-16 11:56:34 +11:00
// is there anything at the source location?
if (!strlen(bpos) ) {
2021-02-17 21:50:31 +11:00
if (why) sprintf(why, "empty source location\n");
2021-02-16 11:56:34 +11:00
return FALSE;
}
// does the other player own the source location?
if (getowner(bpos) == getpchar(1 - who)) {
2021-02-17 21:50:31 +11:00
if (why) sprintf(why, "not owner of source location\n");
2021-02-16 11:56:34 +11:00
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,"");
2021-02-17 21:50:31 +11:00
if (turnnumber == 1) {
if (why) sprintf(why, "cant move on first setup turn.\n");
return FALSE;
}
2021-02-16 11:56:34 +11:00
if (mod == '^') {
// capstone?
2021-02-17 21:50:31 +11:00
if (why) sprintf(why, "%s capstone at %c%c\n",getpname(getowner(bpos)), xch,ych);
2021-02-16 11:56:34 +11:00
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;
2021-02-17 21:50:31 +11:00
} else if (strstr(cmd, "\\W ")) { // check if loc would give us a win
2021-02-16 11:56:34 +11:00
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;
2021-02-17 21:50:31 +11:00
} 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;
2021-02-16 11:56:34 +11:00
} 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);
2021-02-17 21:50:31 +11:00
if (canmoveanyto(turn, x, y, temp, FALSE)) {
2021-02-16 11:56:34 +11:00
printf(" *** %c%c is reachable: %s\n", xpos,ypos, temp);
} else {
printf(" *** can't move to %c%c\n", xpos,ypos);
}
2021-02-17 21:50:31 +11:00
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);
}
2021-02-16 11:56:34 +11:00
return TRUE;
} else if (!strcmp(cmd, "\\a")) { // simlulate AI move for us, with debug info
char cmd[256];
2021-02-17 21:50:31 +11:00
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);
2021-02-16 11:56:34 +11:00
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) {
2021-02-17 21:50:31 +11:00
checkfortak(turn);
2021-02-17 23:30:12 +11:00
checkfortak(1-turn);
2021-02-16 11:56:34 +11:00
logmove(turn, cmd, topofmove);
}
return err;
}
2021-02-21 17:19:03 +11:00
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);
2021-02-16 11:56:34 +11:00
strcpy(newstr, "");
for (p = orig; *p; p++) {
if (!ismod(*p)) {
curcol = getpcol(getpchar(*p));
strcat(newstr, curcol);
}
2021-02-21 17:19:03 +11:00
if (boldstart && p >= boldstart && *p != ' ') {
strcat(newstr, BOLD);
}
2021-02-16 11:56:34 +11:00
strncat(newstr, p, 1);
}
strcat(newstr, reset);
return newstr;
}
void showaivalues(int best) {
int x,y;
char ch;
2021-02-17 21:50:31 +11:00
printf("\n");
2021-02-16 11:56:34 +11:00
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];
2021-02-17 21:50:31 +11:00
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);
2021-02-16 11:56:34 +11:00
}
printf(FORMAT_ROWTAIL, ch);
printf("\n");
// TODO: move this to a function
// show board stack
2021-02-21 17:19:03 +11:00
showboardrow(board, y);
2021-02-16 11:56:34 +11:00
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<MAXPLAYERS; i++) {
showboardbinary(i);
}
*/
}
2021-02-21 17:19:03 +11:00
void showboardrow(char (*b)[MAXBOARDSTRLEN], int y) {
int x,ch;
xytopos(0, y, NULL, &ch);
printf(FORMAT_ROWHEAD, ch);
for (x=0; x < w; x++) {
char *str = board[y*w+x];
// show stack
if (str) {
char origstr[MAXSTACK*30];
char colorstr[MAXSTACK*30];
sprintf(origstr,FORMAT_STACK, str ? str : " ");
// add ansi color codes
makecolorstr(origstr, colorstr, lastmove[y*w+x]);
printf("%s", colorstr);
} else {
printf(FORMAT_STACK, " ");
}
}
printf(FORMAT_ROWTAIL, ch);
printf("\n");
line();
}
2021-02-16 11:56:34 +11:00
void showboard(void) {
2021-02-17 23:30:12 +11:00
int x,y,i;
2021-02-16 11:56:34 +11:00
char ch;
2021-02-17 23:30:12 +11:00
printf("\n%sRound %d - Turn %d (%s%s%s%s):", BOLD, curround, turnnumber, getpcol(turn), BOLD, getpname(turn), reset);
for (i=0; i<MAXPLAYERS; i++) {
if (tak[i]) {
printf(" %s---Tak:%s%s%s",getpcol(i), BOLD,getpname(i),reset);
}
}
printf("\n");
2021-02-16 11:56:34 +11:00
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--) {
2021-02-21 17:19:03 +11:00
showboardrow(board, y);
2021-02-16 11:56:34 +11:00
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<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
}
2021-02-17 21:50:31 +11:00
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);
}
2021-02-16 11:56:34 +11:00
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])));
2021-02-17 21:50:31 +11:00
// 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;
}
2021-02-16 11:56:34 +11:00
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;
2021-02-17 21:50:31 +11:00
// 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);
2021-02-16 11:56:34 +11:00
if (xyvalidforroad(b, who,newx,newy)) {
x = newx;
y = newy;
2021-02-17 21:50:31 +11:00
d = leftdir;
2021-02-16 11:56:34 +11:00
moved = TRUE;
2021-02-17 21:50:31 +11:00
}
if (!moved) { // forward?
newx = x + xmod[d];
newy = y + ymod[d];
2021-02-16 11:56:34 +11:00
2021-02-17 21:50:31 +11:00
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];
2021-02-16 11:56:34 +11:00
2021-02-17 21:50:31 +11:00
if (db) roadlog("backward", d, xch,ych,newx,newy);
2021-02-16 11:56:34 +11:00
if (xyvalidforroad(b, who,newx,newy)) {
x = newx;
y = newy;
moved = TRUE;
}
}
2021-02-17 21:50:31 +11:00
if (!moved) {
if (db) fprintf(stderr,"** db: Couldn't turn around - no roads.\n");
}
2021-02-16 11:56:34 +11:00
if (moved) {
2021-02-17 21:50:31 +11:00
xytopos(x, y, &newxch, &newych);
2021-02-16 11:56:34 +11:00
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;
}
2021-02-17 21:50:31 +11:00
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);
}
2021-02-16 11:56:34 +11:00
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
2021-02-17 21:50:31 +11:00
showscores("Current");
2021-02-16 11:56:34 +11:00
// TODO: bold this
printf("\n%s--- Press ENTER for next round ---%s\n", BOLD, reset);
// wait for key
fgets(cmdbuf ,MAX_CMD_LEN, stdin);
}
2021-02-17 21:50:31 +11:00
void showscores(char *str) {
2021-02-16 11:56:34 +11:00
int i;
char temp[64];
2021-02-17 21:50:31 +11:00
printf("%s scores:\n", str);
2021-02-16 11:56:34 +11:00
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, "*");
}
2021-02-17 21:50:31 +11:00
if (tak[who]) {
strcat(temp, "'");
}
2021-02-16 11:56:34 +11:00
// first player's turn?
if (who == firstturn) {
sprintf(movelog, "%s", temp);
2021-02-17 21:50:31 +11:00
printf("%s%s%d.%s%s %s ...%s\n", YELLOW, BOLD, turnnumber, reset, YELLOW, movelog,reset);
2021-02-16 11:56:34 +11:00
} 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();
}
2021-02-17 21:50:31 +11:00
determinefinalwinner();
2021-02-16 11:56:34 +11:00
}