2143 lines
56 KiB
C
2143 lines
56 KiB
C
// TODO: why isnt this working: never move away from place that would give a win to other p
|
|
// TODO: trap ctrl-c
|
|
// TODO: trap ctrl-d
|
|
// TODO: taktak
|
|
#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%5d%s /%2d "
|
|
|
|
#define LINEPART "--------------"
|
|
#define TRUE (-1)
|
|
#define FALSE (0)
|
|
#define ALL (-1)
|
|
#define ANY (-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];
|
|
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], "");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
tak[i] = FALSE;
|
|
}
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
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, TRUE, (turnnumber == 1) ? TRUE : FALSE);
|
|
printf("%s\n",cmd);
|
|
} else {
|
|
fgets(cmd ,MAX_CMD_LEN, stdin);
|
|
if (cmd) {
|
|
cmd[strlen(cmd)-1] = 0;
|
|
}
|
|
}
|
|
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]*15;
|
|
// 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]);
|
|
}
|
|
}
|
|
// 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 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 : "<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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)) {
|
|
if (canplacetile(who, x, y, ' ', NULL) ||
|
|
canmoveanyto(who, x, y, NULL, FALSE)) {
|
|
tak[who] = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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 xmodamt,ymodamt;
|
|
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,&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*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 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 (!canmovefrom(who, x, y, why)) { // got somethong to move here
|
|
ok = FALSE;
|
|
}
|
|
} else {
|
|
if (!willmovefrom(who, x, y, why)) { // got somethong which is safe 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[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++) {
|
|
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
|
|
// 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;
|
|
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) {
|
|
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("\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
|
|
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,i;
|
|
char ch;
|
|
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");
|
|
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
|
|
|
|
}
|
|
|
|
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<MAXPLAYERS; i++) {
|
|
gamepoints[i] += roundpoints[i];
|
|
}
|
|
|
|
// show totals
|
|
showscores("Current");
|
|
// 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(char *str) {
|
|
int i;
|
|
char temp[64];
|
|
printf("%s scores:\n", str);
|
|
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, "*");
|
|
}
|
|
if (tak[who]) {
|
|
strcat(temp, "'");
|
|
}
|
|
|
|
// first player's turn?
|
|
if (who == firstturn) {
|
|
sprintf(movelog, "%s", temp);
|
|
printf("%s%s%d.%s%s %s ...%s\n", YELLOW, BOLD, turnnumber, reset, YELLOW, movelog,reset);
|
|
} 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();
|
|
}
|
|
determinefinalwinner();
|
|
}
|