1630 lines
42 KiB
C
1630 lines
42 KiB
C
|
// TODO: call tak
|
||
|
// TODO: trap ctrl-c
|
||
|
#include <ctype.h>
|
||
|
#include <math.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
#include "tak.h"
|
||
|
|
||
|
#define MAXSTACK 10
|
||
|
#define MAXW 8
|
||
|
#define MAXH 8
|
||
|
|
||
|
#define MAX_CMD_LEN 100
|
||
|
|
||
|
#define MAXPLAYERS 2
|
||
|
|
||
|
#define FORMAT_ROWHEAD "%c "
|
||
|
#define FORMAT_ROWTAIL "| %c"
|
||
|
#define FORMAT_COLHEAD "| %c "
|
||
|
#define FORMAT_COLTAIL "| %c "
|
||
|
|
||
|
//#define FORMAT_OWNER "| %c%c |"
|
||
|
//#define FORMAT_STACK "|[%-10s]|"
|
||
|
#define FORMAT_OWNER "| %s%c%c%s "
|
||
|
#define FORMAT_STACK "| %-10s "
|
||
|
#define FORMAT_AIVAL "| %s%d%s "
|
||
|
|
||
|
#define LINEPART "--------------"
|
||
|
#define TRUE (-1)
|
||
|
#define FALSE (0)
|
||
|
#define ALL (-1)
|
||
|
|
||
|
#define BLACK "\e[0;30m"
|
||
|
#define BLUE "\e[0;36m"
|
||
|
#define RED "\e[0;31m"
|
||
|
#define YELLOW "\e[0;33m"
|
||
|
#define BOLD "\e[1m"
|
||
|
//#define BLUE "\e[0;34m"
|
||
|
#define GREEN "\e[0;32m"
|
||
|
#define reset "\e[0m"
|
||
|
|
||
|
char board[MAXW*MAXH][MAXBOARDSTRLEN];
|
||
|
int aivalue[MAXW*MAXH];
|
||
|
#define BLOCKED -12345
|
||
|
char cmdbuf[MAX_CMD_LEN];
|
||
|
|
||
|
int tilesleft[MAXPLAYERS];
|
||
|
int capsleft[MAXPLAYERS];
|
||
|
int hasroad[MAXPLAYERS];
|
||
|
int gamepoints[MAXPLAYERS];
|
||
|
int roundpoints[MAXPLAYERS];
|
||
|
int ai[MAXPLAYERS];
|
||
|
|
||
|
// for game log
|
||
|
char topofmove = ' ';
|
||
|
int flattened = FALSE;
|
||
|
|
||
|
char movelog[256];
|
||
|
|
||
|
|
||
|
int debug = FALSE;
|
||
|
|
||
|
enum direction {NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3};
|
||
|
|
||
|
int size=5;
|
||
|
int w=5;
|
||
|
int h=5;
|
||
|
int curround, numrounds;
|
||
|
int turnnumber = 0;
|
||
|
int firstturn = 0; // who goes first
|
||
|
|
||
|
int gameover = 0;
|
||
|
int turn = 0;
|
||
|
int winner = -1;
|
||
|
|
||
|
void init(void) {
|
||
|
int x,y;
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
strcpy(board[y*w+x], "");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int setupround(void) {
|
||
|
int i;
|
||
|
int x,y;
|
||
|
int starttiles,startcaps;
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
strcpy(board[y*w+x], "");
|
||
|
}
|
||
|
}
|
||
|
gameover = 0;
|
||
|
turn = firstturn;
|
||
|
turnnumber = 1;
|
||
|
|
||
|
starttiles = getstarttiles(size);
|
||
|
startcaps = getstartcapstones(size);
|
||
|
for (i=0; i<MAXPLAYERS; i++) {
|
||
|
tilesleft[i] = starttiles;
|
||
|
capsleft[i] = startcaps;
|
||
|
hasroad[i] = FALSE;
|
||
|
roundpoints[i] = 0;
|
||
|
}
|
||
|
ai[0] = FALSE;
|
||
|
ai[1] = FALSE;
|
||
|
//ai[1] = TRUE;
|
||
|
|
||
|
for (i=0; i<MAXPLAYERS; i++) {
|
||
|
roundpoints[i] = 0;
|
||
|
}
|
||
|
winner = -1;
|
||
|
printf("%s=======================\n",BOLD);
|
||
|
printf("===== ROUND %2d/%2d =====\n", curround,numrounds);
|
||
|
printf("=======================%s\n\n",reset);
|
||
|
printf("%s%s%s%s%s is playing first this round.%s\n\n", getpcol(turn), BOLD, getpname(turn), reset,BOLD,reset);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
int setupgame(int sz, int nrounds) {
|
||
|
int i;
|
||
|
if ((sz < 3) || (sz > 8) || (sz == 7)) {
|
||
|
printf("error - board size must be between 3 and 8 (but not 7).\n");
|
||
|
return TRUE;
|
||
|
}
|
||
|
w = h = size = sz;
|
||
|
for (i=0; i<MAXPLAYERS; i++) {
|
||
|
roundpoints[i] = 0;
|
||
|
gamepoints[i] = 0;
|
||
|
}
|
||
|
curround = 1;
|
||
|
numrounds = nrounds;
|
||
|
winner = -1;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void cleanup(void) {
|
||
|
int x,y;
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
strcpy(board[y*w+x], "");
|
||
|
}
|
||
|
}
|
||
|
// fix colours
|
||
|
printf("%s",reset); fflush(stdout);
|
||
|
}
|
||
|
|
||
|
|
||
|
char getmodxy(int x, int y) {
|
||
|
return getmod(board[y*w+x]);
|
||
|
}
|
||
|
|
||
|
char getmod(char *stack) {
|
||
|
char ch = 0;
|
||
|
if (!stack) return ' ';
|
||
|
ch = stack[strlen(stack)-1];
|
||
|
if (ismod(ch)) {
|
||
|
return modprintable(ch);
|
||
|
}
|
||
|
return ' ';
|
||
|
}
|
||
|
|
||
|
char modprintable(char mod) {
|
||
|
switch (mod) {
|
||
|
case 'C':
|
||
|
case 'k':
|
||
|
mod = '^';
|
||
|
break;
|
||
|
case 'S':
|
||
|
case 's':
|
||
|
mod = '|';
|
||
|
break;
|
||
|
}
|
||
|
return mod;
|
||
|
}
|
||
|
|
||
|
char counttiles(char *stack) {
|
||
|
int pos,count=0;
|
||
|
if (!stack) return 0;
|
||
|
for (pos = 0; stack[pos]; pos++) {
|
||
|
if (!ismod(stack[pos])) count++;
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
char getowner(char *stack) {
|
||
|
int pos;
|
||
|
if (!stack) return ' ';
|
||
|
for (pos = strlen(stack)-1; pos>=0; pos--) {
|
||
|
if (!ismod(stack[pos])) break;
|
||
|
}
|
||
|
if (pos < 0) return ' ';
|
||
|
return stack[pos];
|
||
|
}
|
||
|
|
||
|
int isdir(char ch) {
|
||
|
switch (ch) {
|
||
|
case '>':
|
||
|
case '<':
|
||
|
case '+':
|
||
|
case '-':
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
int ismod(char ch) {
|
||
|
switch (ch) {
|
||
|
case 'C':
|
||
|
case 'k':
|
||
|
case '^':
|
||
|
case 'S':
|
||
|
case 's':
|
||
|
case '|':
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
char *getdirname(int dir) {
|
||
|
switch (dir) {
|
||
|
case '>':
|
||
|
return "right";
|
||
|
case '<':
|
||
|
return "left";
|
||
|
case '+':
|
||
|
return "up";
|
||
|
case '-':
|
||
|
return "down";
|
||
|
}
|
||
|
return "UNKNOWN_DIR";
|
||
|
}
|
||
|
|
||
|
char *getmodname(int mod) {
|
||
|
switch (mod) {
|
||
|
case 'C':
|
||
|
case 'k':
|
||
|
case '^':
|
||
|
return "capstone";
|
||
|
case 'S':
|
||
|
case '|':
|
||
|
return "standing stone";
|
||
|
}
|
||
|
return "flatstone";
|
||
|
}
|
||
|
|
||
|
char *readcmd(char *cmd) {
|
||
|
char turntext[64];
|
||
|
if (turnnumber == 1) {
|
||
|
sprintf(turntext, "SETUP: %s%s%s placing %s%s's%s initial tile", getpcol(turn),getpname(turn), reset, getpcol(1-turn),getpname(1-turn),reset);
|
||
|
printf("R%d/%d-T%d. %s %s==> ",curround,numrounds,turnnumber, turntext, getpcol(turn));
|
||
|
} else {
|
||
|
sprintf(turntext, "%s%s's%s turn", getpcol(turn),getpname(turn),reset);
|
||
|
printf("R%d/%d-T%d. %s (%s%sstones:%s%s%d %scapstones%s%s:%d%s) %s==> ",curround,numrounds,turnnumber,
|
||
|
turntext, getpcol(turn), BOLD, reset, getpcol(turn), tilesleft[turn], BOLD, reset,getpcol(turn),capsleft[turn],reset,
|
||
|
getpcol(turn));
|
||
|
}
|
||
|
printf("%s",BOLD);
|
||
|
if (ai[turn]) {
|
||
|
genaicmd(cmd, FALSE);
|
||
|
printf("%s\n",cmd);
|
||
|
} else {
|
||
|
fgets(cmd ,MAX_CMD_LEN, stdin);
|
||
|
if (cmd) {
|
||
|
cmd[strlen(cmd)-1] = 0;
|
||
|
}
|
||
|
}
|
||
|
printf("%s",reset);
|
||
|
return cmd;
|
||
|
}
|
||
|
|
||
|
char *genaicmd(char *cmd, int showdb) {
|
||
|
int x,y,best;
|
||
|
int ncandidates;
|
||
|
int candidate[MAXW*MAXH];
|
||
|
int sel, xsel, ysel,xpossel,ypossel;
|
||
|
// zero all priorities
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
aivalue[y*w+x] = 500;
|
||
|
}
|
||
|
}
|
||
|
// mark anywhere we definitely cannot go
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
int idx = y*w+x;
|
||
|
if (!canplacetile(turn, x, y, ' ', NULL) &&
|
||
|
!canmoveanyto(turn, x, y, NULL)) {
|
||
|
aivalue[idx] = BLOCKED;
|
||
|
} else if (xywouldgivewinto(1-turn, x, y)) {
|
||
|
modaivalue(x, y, 999);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// determine the best priority
|
||
|
best = -1;
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
if ((aivalue[y*w+x] != BLOCKED) &&
|
||
|
(aivalue[y*w+x] > best)) {
|
||
|
best = aivalue[y*w+x];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (showdb) {
|
||
|
showaivalues(best);
|
||
|
}
|
||
|
|
||
|
printf("*** best value is = %d\n", best);
|
||
|
// pick randomly from the best priority
|
||
|
ncandidates = 0;
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
if (aivalue[y*w+x] == best) {
|
||
|
candidate[ncandidates++] = y*w+x;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
printf("*** ncandidates = %d\n", ncandidates);
|
||
|
|
||
|
if (ncandidates == 0) {
|
||
|
sprintf(cmd, "\\r");
|
||
|
} else {
|
||
|
char str[256];
|
||
|
sel = rand() % ncandidates;
|
||
|
|
||
|
idxtoxy(candidate[sel], &xsel, &ysel);
|
||
|
xytopos(xsel,ysel,&xpossel, &ypossel);
|
||
|
|
||
|
// we place a tile here or move here?
|
||
|
if (canmoveanyto(turn, x, y, str)) {
|
||
|
// move
|
||
|
sprintf(cmd, "%s", str);
|
||
|
} else {
|
||
|
// place
|
||
|
sprintf(cmd, "%c%c", xpossel,ypossel);
|
||
|
}
|
||
|
}
|
||
|
return cmd;
|
||
|
}
|
||
|
|
||
|
int xywouldgivewinto(int who, int tx, int ty) {
|
||
|
char board2[MAXW*MAXH][MAXBOARDSTRLEN];
|
||
|
int x,y,wouldwin = FALSE;
|
||
|
|
||
|
// take copy of board
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
strcpy(board2[y*w+x], board[y*w+x]);
|
||
|
}
|
||
|
}
|
||
|
// force owner of given position to given player
|
||
|
strcat(board2[ty*w+tx], getpstr(who));
|
||
|
if ( playerhasroads(who, board2)) {
|
||
|
wouldwin = TRUE;
|
||
|
}
|
||
|
return wouldwin;
|
||
|
}
|
||
|
|
||
|
void idxtoxy(int idx, int *x, int *y) {
|
||
|
if (x) *x = idx % w;
|
||
|
if (y) *y = idx / w;
|
||
|
}
|
||
|
|
||
|
int modaivalue(int x,int y,int amt) {
|
||
|
int idx=y*w+x;
|
||
|
if (aivalue[idx] == BLOCKED) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
aivalue[idx] += amt;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int isboardfull() {
|
||
|
int x,y;
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
if (!counttiles(board[y*w+x])) return FALSE;
|
||
|
}
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void updateroadstatus(void) {
|
||
|
int i;
|
||
|
// update road status for each player
|
||
|
for (i=0; i<MAXPLAYERS; i++) {
|
||
|
hasroad[i] = playerhasroads(i, board);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int checkendgame(void) {
|
||
|
int i;
|
||
|
|
||
|
// someone resigned?
|
||
|
if (gotwinner()) return TRUE;
|
||
|
|
||
|
// either player has a road?
|
||
|
for (i=0; i<MAXPLAYERS; i++) {
|
||
|
if (hasroad[i]) return TRUE;
|
||
|
}
|
||
|
|
||
|
// any player out of tiles? (most flatstones wins)
|
||
|
for (i=0; i<MAXPLAYERS; i++) {
|
||
|
if (tilesleft[i] < 0) return TRUE;
|
||
|
if (capsleft[i] < 0) return TRUE;
|
||
|
}
|
||
|
|
||
|
// board covered? (most flatstones wins)
|
||
|
if (isboardfull()) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int getstartcapstones(int sz) {
|
||
|
switch (sz) {
|
||
|
case 3:
|
||
|
case 4:
|
||
|
return 0;
|
||
|
case 5:
|
||
|
case 6:
|
||
|
return 1;
|
||
|
case 8:
|
||
|
return 2;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int getstarttiles(int sz) {
|
||
|
switch (sz) {
|
||
|
case 3:
|
||
|
return 10;
|
||
|
case 4:
|
||
|
return 15;
|
||
|
case 5:
|
||
|
return 21;
|
||
|
case 6:
|
||
|
return 30;
|
||
|
case 8:
|
||
|
return 50;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int nextround(void) {
|
||
|
curround++;
|
||
|
// swap who goes first.
|
||
|
firstturn = 1 - firstturn;
|
||
|
return 0;
|
||
|
}
|
||
|
int nextturn(void) {
|
||
|
turn = 1 - turn;
|
||
|
if (turn == firstturn) {
|
||
|
turnnumber++;
|
||
|
}
|
||
|
// reset game log variables?
|
||
|
topofmove = ' ';
|
||
|
flattened = FALSE;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
char getpchar(int n) {
|
||
|
if ((n == 0) || (n == 'b')) {
|
||
|
return 'b';
|
||
|
} else {
|
||
|
return 'r';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
char *getpcol(int pnum) {
|
||
|
switch (pnum) {
|
||
|
case 0:
|
||
|
case 'b':
|
||
|
return BLUE;
|
||
|
case 1:
|
||
|
case 'r':
|
||
|
return RED;
|
||
|
}
|
||
|
|
||
|
return reset;
|
||
|
}
|
||
|
|
||
|
char *getpstr(int n) {
|
||
|
if ((n == 0) || (n == 'b')) {
|
||
|
return "b";
|
||
|
} else {
|
||
|
return "r";
|
||
|
}
|
||
|
}
|
||
|
char *getpname(int n) {
|
||
|
if ((n == 0) || (n == 'b')) {
|
||
|
return "blue";
|
||
|
} else {
|
||
|
return "red";
|
||
|
}
|
||
|
}
|
||
|
int getpnum(int n) {
|
||
|
if ((n == 0) || (n == 'b')) {
|
||
|
return 0;
|
||
|
} else if ((n == 1) || (n == 'r')) {
|
||
|
return 1;
|
||
|
} else {
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void xytopos(int x, int y, int *xch, int *ych) {
|
||
|
if (xch) *xch = 'a' + x;
|
||
|
if (ych) *ych = '1' + y;
|
||
|
}
|
||
|
|
||
|
void postoxy(int xch, int ych, int *x, int *y) {
|
||
|
if (x) *x = xch - 'a';
|
||
|
if (y) *y = ych - '1';
|
||
|
}
|
||
|
|
||
|
int movetile(int count,char xch,char ych, char dir, char *dropstr) {
|
||
|
int pos,newpos,x,y,mod,origcount;
|
||
|
int newxch,newych;
|
||
|
char countstr[4];
|
||
|
char moving[MAXSTACK*2];
|
||
|
char dropping[MAXSTACK*2];
|
||
|
char *bpos,*newbpos,*p,*dp,*mp;
|
||
|
int pass;
|
||
|
int xmod,ymod;
|
||
|
int numtomove;
|
||
|
char err[256];
|
||
|
postoxy(xch,ych,&x, &y);
|
||
|
if (!isvalidxypos(xch, ych)) {
|
||
|
printf("* invalid cell reference '%c%c'\n",xch,ych);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// remember the top tile mod, so we can log it in the game log
|
||
|
topofmove = getmodxy(x,y);
|
||
|
normalise(&topofmove);
|
||
|
|
||
|
pos = y*w+x;
|
||
|
bpos = board[pos];
|
||
|
|
||
|
origcount = counttiles(bpos);
|
||
|
if (count == ALL) {
|
||
|
// if no count specified, move max stack limit
|
||
|
if (origcount <= size) {
|
||
|
count = origcount;
|
||
|
strcpy(countstr, "ALL");
|
||
|
} else {
|
||
|
count = size;
|
||
|
sprintf(countstr, "%d", count);
|
||
|
}
|
||
|
} else {
|
||
|
sprintf(countstr, "%d", count);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!canmovefrom(turn, x, y, err)) {
|
||
|
printf("* %s can't move %s tiles %s from %c%c - %s\n",getpname(turn),countstr,getdirname(dir), xch,ych, err);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// validate count
|
||
|
if (count > origcount) {
|
||
|
printf("* %s can't move %s tiles %s from %c%c - only %d there\n",getpname(turn),countstr,getdirname(dir), xch,ych,origcount);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
dirtoxy(dir,&xmod,&ymod);
|
||
|
numtomove = count;
|
||
|
|
||
|
// First pass: validate each step
|
||
|
for (pass=0; pass <= 1; pass++) {
|
||
|
int remaining,numtodrop;
|
||
|
|
||
|
if (pass == 1) {
|
||
|
if (debug) printf("- %s%s%s is moving %d tiles %s from %c%c, dropping %s\n", getpcol(turn), getpname(turn), reset, count, getdirname(dir), xch, ych, strlen(dropstr) ? dropstr : "*");
|
||
|
}
|
||
|
//printf("PASS %d\n",pass);
|
||
|
postoxy(xch,ych,&x, &y);
|
||
|
|
||
|
// pick up stack of tiles to move
|
||
|
p = bpos + strlen(bpos)-1;
|
||
|
//printf("debug: bpos is: %s\n", bpos);
|
||
|
//printf("debug: p is: %s\n", p);
|
||
|
if (ismod(*p)) p--;
|
||
|
p -= (count-1) ;
|
||
|
//printf("debug: new p is: %s\n", p);
|
||
|
strcpy(moving, p);
|
||
|
|
||
|
if (pass == 1) {
|
||
|
// actually remove tiles from source location
|
||
|
*p = 0;
|
||
|
}
|
||
|
|
||
|
// begin at start of drop string
|
||
|
dp = dropstr;
|
||
|
|
||
|
// start dropping from bottom of moving stack
|
||
|
mp = moving;
|
||
|
|
||
|
newxch = xch;
|
||
|
newych = ych;
|
||
|
while ((remaining = counttiles(mp))) {
|
||
|
//printf("remaining in mp: %d\n",remaining);;
|
||
|
//printf("move %s from %d,%d (%c%c)", getdirname(dir), x,y,newxch,newych);
|
||
|
|
||
|
x += xmod;
|
||
|
y += ymod;
|
||
|
// still on the board?
|
||
|
if (!isvalidxy(x,y)) {
|
||
|
printf("* %s can't move %s tiles %s from %c%c - would move off board with %d tiles remaining\n",getpname(turn),countstr,getdirname(dir), xch,ych, remaining);
|
||
|
return TRUE;
|
||
|
}
|
||
|
xytopos(x, y, &newxch, &newych);
|
||
|
|
||
|
|
||
|
newpos = y*w+x;
|
||
|
newbpos = board[newpos];
|
||
|
//printf("now at %d,%d (%c%c)", x,y,newxch,newych);
|
||
|
|
||
|
|
||
|
mod = getmod(newbpos);
|
||
|
if (!canmoveto(turn, x, y, err)) {
|
||
|
printf("* %s can't move %s tiles %s from %c%c - %s\n",getpname(turn),countstr,getdirname(dir), xch,ych, err);
|
||
|
return TRUE;
|
||
|
} else if ((mod == '|') && (remaining > 1)) {
|
||
|
// stack of > 1 moving onto standing stone
|
||
|
printf("* %s can't move %s tiles %s from %c%c - %d tiles blocked by %s standing stone at %c%c\n",getpname(turn),countstr,getdirname(dir), xch,ych, remaining, getpname(getowner(newbpos)), newxch,newych);
|
||
|
return TRUE;
|
||
|
} else if ((mod == '|') && (getmod(mp) != '^')) {
|
||
|
// non-capstone moving onto standing stone
|
||
|
printf("* %s can't move %s tiles %s from %c%c - only capstone can move onto %s standing stone at %c%c\n",getpname(turn),countstr,getdirname(dir), xch,ych, getpname(getowner(newbpos)), newxch,newych);
|
||
|
return TRUE;
|
||
|
} else if ((mod == '|') && (remaining == 1) && (getmod(mp) == '^')) {
|
||
|
// single capstone moving onto a standing stone
|
||
|
// TODO: can yoi flatten yourbown standing stone?
|
||
|
if (pass == 1) {
|
||
|
if (debug) printf(" - %s%s%s capstone flattens %s%s%s standing stone at %c%c!\n",getpcol(turn),getpname(turn),reset,
|
||
|
getpcol(getowner(newbpos)),getpname(getowner(newbpos)), reset,
|
||
|
newxch,newych);
|
||
|
newbpos[strlen(newbpos)-1] = 0;;
|
||
|
|
||
|
flattened = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// drop tiles
|
||
|
if (!dp || *dp == 0) {
|
||
|
numtodrop = remaining;
|
||
|
} else {
|
||
|
numtodrop = *dp - '0';
|
||
|
}
|
||
|
if (numtodrop > remaining) {
|
||
|
printf("* %s can't move %s tiles %s from %c%c with '%s' - want to drop %d tiles at %c%c but only %d remain\n",getpname(turn),countstr,getdirname(dir), xch,ych, dropstr, numtodrop, newxch,newych, remaining);
|
||
|
return TRUE;
|
||
|
}
|
||
|
// construct string of tiles to drop
|
||
|
strncpy(dropping, mp, numtodrop);
|
||
|
dropping[numtodrop] = 0;
|
||
|
// retain top capstone/standingstone
|
||
|
if (ismod(*(mp+numtodrop))) {
|
||
|
strncat(dropping, mp+numtodrop, 1);
|
||
|
mp++; // havk
|
||
|
}
|
||
|
if (pass == 1) {
|
||
|
char colorstr[MAXSTACK*10];
|
||
|
// add ansi color codes
|
||
|
makecolorstr(dropping, colorstr);
|
||
|
|
||
|
if (debug) {
|
||
|
printf(" - dropping '%s' (%d) at %c%c. remaining: '%s' (%d)\n",
|
||
|
colorstr, numtodrop, newxch,newych,
|
||
|
(mp+numtodrop), counttiles(mp+numtodrop));
|
||
|
}
|
||
|
// actually drop tiles here
|
||
|
strcat(newbpos, dropping);
|
||
|
}
|
||
|
// remove dropped tiles from moving stack
|
||
|
mp += numtodrop;
|
||
|
// advance to next drop count entry
|
||
|
dp++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void dirtoxy(char dir, int *x, int *y) {
|
||
|
*x = 0;
|
||
|
*y = 0;
|
||
|
switch (dir) {
|
||
|
case '>':
|
||
|
*x = 1;
|
||
|
*y = 0;
|
||
|
break;
|
||
|
case '<':
|
||
|
*x = -1;
|
||
|
*y = 0;
|
||
|
break;
|
||
|
case '+':
|
||
|
*x = 0;
|
||
|
*y = 1;
|
||
|
break;
|
||
|
case '-':
|
||
|
*x = 0;
|
||
|
*y = -1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int placetile(int who,char xch,char ych, char mod) {
|
||
|
int pos,x,y;
|
||
|
char *bpos;
|
||
|
char err[256];
|
||
|
if (mod) mod = modprintable(mod);
|
||
|
|
||
|
postoxy(xch,ych,&x, &y);
|
||
|
|
||
|
pos = y*w+x;
|
||
|
bpos = board[pos];
|
||
|
|
||
|
if (!canplacetile(who, x, y, mod, err)) {
|
||
|
// is this space available?
|
||
|
printf("* %s\n",err);
|
||
|
return TRUE;
|
||
|
}
|
||
|
if (debug) printf("-- placing %s %s at %c%c\n", getpname(who), getmodname(mod), xch, ych);
|
||
|
strcat(bpos, getpstr(who));
|
||
|
if (mod) {
|
||
|
strncat(bpos, &mod, 1);
|
||
|
}
|
||
|
if (mod == '^') {
|
||
|
capsleft[who]--;
|
||
|
} else {
|
||
|
tilesleft[who]--;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int canplacetile(int who, int x, int y, int mod, char *why) {
|
||
|
int idx,xch,ych;
|
||
|
char *bpos;
|
||
|
xytopos(x,y,&xch, &ych);
|
||
|
idx = y*w+x;
|
||
|
bpos = board[idx];
|
||
|
|
||
|
if (why) strcpy(why,"");
|
||
|
// placing capstone on first turn?
|
||
|
if (turnnumber == 1 && ((mod == '|') || (mod == '^'))) {
|
||
|
if (why) sprintf(why,"can't place %s %s at %c%c - can only place flat tiles during setup, mod is '%c'\n",getpname(who),getmodname(mod),xch,ych,mod);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if ((mod == '^') && (capsleft[who] <= 0)) {
|
||
|
if (why) sprintf(why, "can't place %s %s at %c%c - no capstones left\n",getpname(who),getmodname(mod),xch,ych);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (strlen(bpos) > 0) {
|
||
|
if (why) sprintf(why, "can't place %s %s at %c%c - not free\n",getpname(who),getmodname(mod),xch,ych);
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
int canmoveanyto(int who, int tx, int ty, char *why) {
|
||
|
int x,y;
|
||
|
if (!canmoveto(who, tx, ty, why)) return FALSE;
|
||
|
if (why) strcpy(why,"");
|
||
|
|
||
|
// for each tile we control, see if it can move to given spot
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
int ok = TRUE;
|
||
|
int count;
|
||
|
if( (x == tx) && (y == ty)) {
|
||
|
ok = FALSE;
|
||
|
}
|
||
|
if (ok && (x != tx) && (y != ty)) { // same row/col
|
||
|
ok = FALSE;
|
||
|
}
|
||
|
if ( ok && !canmovefrom(who, x, y, why)) { // got somethong to move here
|
||
|
ok = FALSE;
|
||
|
}
|
||
|
count = counttiles(board[y*w+x]);
|
||
|
if (ok && (x == tx) && abs(tx - x) > count) { // distance ok?
|
||
|
ok = FALSE;
|
||
|
}
|
||
|
if (ok && (y == ty) && abs(ty - y) > count) { // distance ok?
|
||
|
ok = FALSE;
|
||
|
}
|
||
|
// can this stack get one of our color to there???
|
||
|
if (canmakedropstr(who, x,y,tx,ty,why)) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int canmakedropstr(int who, int sx, int sy, int dx, int dy, char *str) { // return TRUE if we succeeded
|
||
|
int count,maxtry,trystacksize, d;
|
||
|
char moving[MAXSTACK*2];
|
||
|
char *mp;
|
||
|
char *p;
|
||
|
char *spos;
|
||
|
int smod;
|
||
|
int xmod[]={0, 1, 0, -1}; /// nesw
|
||
|
int ymod[]={1, 0, -1, 0}; /// nesw
|
||
|
char localstr[256];
|
||
|
int db = TRUE;
|
||
|
|
||
|
spos=board[sy*w+sx];
|
||
|
smod = getmod(spos);
|
||
|
count = counttiles(board[sy*w+sx]);
|
||
|
if (count < 1) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (count > size) maxtry = size;
|
||
|
else maxtry = count;
|
||
|
|
||
|
// which dir?
|
||
|
if ((sx == dx) && (sy < dy)) {
|
||
|
d = 0;
|
||
|
} else if ((sx == dx) && (sy > dy)) {
|
||
|
d = 2;
|
||
|
} else if ((sy == dy) && (sx > dx)) {
|
||
|
d = 3;
|
||
|
} else if ((sy == dy) && (sx < dx)) {
|
||
|
d = 1;
|
||
|
} else {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// try each stack size
|
||
|
for (trystacksize = 1; trystacksize <= maxtry; trystacksize++) {
|
||
|
int x,y;
|
||
|
int stillgood = TRUE;
|
||
|
int xch,ych;
|
||
|
// pick up stack of tiles to move
|
||
|
p = spos + strlen(spos)-1;
|
||
|
if (ismod(*p)) p--;
|
||
|
p -= (trystacksize-1) ;
|
||
|
strcpy(moving, p);
|
||
|
mp = moving;
|
||
|
|
||
|
// max distance we can move before hitting wall/cap/edge?
|
||
|
x = sx; y = sy;
|
||
|
xytopos(x, y, &xch, &ych);
|
||
|
stillgood = TRUE;
|
||
|
sprintf(localstr, "%d%c%c%c", trystacksize,xch,ych,getdirchar(d));
|
||
|
while (stillgood) {
|
||
|
// move one cell toward dst
|
||
|
x += xmod[d];
|
||
|
y += ymod[d];
|
||
|
if (!isvalidxy(x,y) || getmodxy(x,y) == '^' ||
|
||
|
(getmodxy(x,y) == '|' && (smod != '^' || counttiles(mp) != 1)) ) {
|
||
|
// hit a blocker - stop
|
||
|
stillgood = FALSE;
|
||
|
} else {
|
||
|
// are we there yet
|
||
|
if (x == dx && y == dy) {
|
||
|
char temp[64];
|
||
|
// dump the rest here and we're done
|
||
|
sprintf(temp, "%1d",counttiles(mp));
|
||
|
strcat(localstr, temp);
|
||
|
if (str) strcpy(str,localstr);
|
||
|
return TRUE;
|
||
|
// TODO: go further to control mlre locations
|
||
|
} else {
|
||
|
char *z;
|
||
|
// not there yet.
|
||
|
|
||
|
// must drop at least one
|
||
|
// TODO: drop more than 1, prefer our color
|
||
|
//z = strchr(mp, getpchar(who));
|
||
|
|
||
|
// stone to drop.
|
||
|
z = mp; // drop 1
|
||
|
if (z) {
|
||
|
// if match, drop so it's on top
|
||
|
int numtodrop = (z - moving) + 1;
|
||
|
char temp[10];
|
||
|
sprintf(temp, "%1d",numtodrop);
|
||
|
strcat(localstr, temp);
|
||
|
|
||
|
if (db) printf("db: ** numtodrop: %d. tiles in stack: %d (%s)\n", numtodrop, counttiles(mp), mp);
|
||
|
// no more tiles in moving stack??
|
||
|
if (numtodrop >= counttiles(mp)) {
|
||
|
// this stack size.
|
||
|
stillgood = FALSE;
|
||
|
} else {
|
||
|
// remove dropped tiles from moving stack
|
||
|
mp += numtodrop;
|
||
|
if (db) printf("db: ** new tiles in stack: %d (%s)\n", counttiles(mp), mp);
|
||
|
}
|
||
|
} else {
|
||
|
// if not, fail this stack size.
|
||
|
stillgood = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
char getdirchar(int dir) {
|
||
|
switch (dir) {
|
||
|
case 1:
|
||
|
return '>';
|
||
|
case 3:
|
||
|
return '<';
|
||
|
case 0:
|
||
|
return '+';
|
||
|
case 2:
|
||
|
return '-';
|
||
|
}
|
||
|
return '=';
|
||
|
}
|
||
|
|
||
|
int canmovefrom(int who, int x, int y, char *why) {
|
||
|
int mod;
|
||
|
int idx,xch,ych;
|
||
|
char *bpos;
|
||
|
xytopos(x,y,&xch, &ych);
|
||
|
idx = y*w+x;
|
||
|
bpos = board[idx];
|
||
|
mod = getmod(bpos);
|
||
|
|
||
|
if (why) strcpy(why,"");
|
||
|
// is there anything at the source location?
|
||
|
if (!strlen(bpos) ) {
|
||
|
sprintf(why, "empty source location\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
// does the other player own the source location?
|
||
|
if (getowner(bpos) == getpchar(1 - who)) {
|
||
|
sprintf(why, "not owner of source location\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
int canmoveto(int who, int x, int y, char *why) {
|
||
|
int mod;
|
||
|
int idx,xch,ych;
|
||
|
char *bpos;
|
||
|
xytopos(x,y,&xch, &ych);
|
||
|
idx = y*w+x;
|
||
|
bpos = board[idx];
|
||
|
mod = getmod(bpos);
|
||
|
|
||
|
if (why) strcpy(why,"");
|
||
|
if (mod == '^') {
|
||
|
// capstone?
|
||
|
sprintf(why, "%s capstone at %c%c\n",getpname(getowner(bpos)), xch,ych);
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
int isvalidxy(int x, int y) {
|
||
|
if (x >= 0 && x < w && y >= 0 && y < w) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int isvalidxypos(char x, char y) {
|
||
|
char firstx = 'a';
|
||
|
char lastx = 'a' + w - 1;
|
||
|
char firsty = '1';
|
||
|
char lasty = '1'+h-1;
|
||
|
if (x >= firstx && x <= lastx && y >= firsty && y <= lasty) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int parsecmd(char *cmd) {
|
||
|
int err = FALSE;
|
||
|
int mod=0;
|
||
|
char *p=cmd;
|
||
|
if (!p) return TRUE;
|
||
|
|
||
|
if (!strcmp(cmd, "\\r")) {
|
||
|
// resign
|
||
|
winner = 1-turn;
|
||
|
logmove(turn, cmd, ' ');
|
||
|
return FALSE;
|
||
|
} else if (strstr(cmd, "\\w ")) { // check if loc would give us a win
|
||
|
int xpos,ypos,x,y,wouldwin;
|
||
|
p = cmd + 3;
|
||
|
xpos = p[0];
|
||
|
ypos = p[1];
|
||
|
postoxy(xpos,ypos,&x,&y);
|
||
|
wouldwin = xywouldgivewinto(turn, x, y);
|
||
|
printf(" *** would %c%c give us a win? %s\n", xpos,ypos,
|
||
|
wouldwin ? "YES" : "no");
|
||
|
return TRUE;
|
||
|
} else if (strstr(cmd, "\\m ")) { // check if we can move to loc
|
||
|
char temp[256];
|
||
|
int xpos,ypos,x,y;
|
||
|
p = cmd + 3;
|
||
|
xpos = p[0];
|
||
|
ypos = p[1];
|
||
|
postoxy(xpos,ypos,&x,&y);
|
||
|
if (canmoveanyto(turn, x, y, temp)) {
|
||
|
printf(" *** %c%c is reachable: %s\n", xpos,ypos, temp);
|
||
|
} else {
|
||
|
printf(" *** can't move to %c%c\n", xpos,ypos);
|
||
|
}
|
||
|
return TRUE;
|
||
|
} else if (!strcmp(cmd, "\\a")) { // simlulate AI move for us, with debug info
|
||
|
char cmd[256];
|
||
|
|
||
|
genaicmd(cmd, TRUE);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
if (ismod(p[0])) {
|
||
|
mod = p[0];
|
||
|
p++;
|
||
|
}
|
||
|
|
||
|
if (strchr(p, 'q')) {
|
||
|
gameover = TRUE;
|
||
|
} else if (isalpha(p[0]) && isdigit(p[1]) && isdir(p[2])) {
|
||
|
// move all tiles in direction
|
||
|
int xpos = p[0];
|
||
|
int ypos = p[1];
|
||
|
int dir = p[2];
|
||
|
if (movetile(ALL,xpos,ypos,dir,p+3)) err = TRUE;
|
||
|
} else if (isdigit(p[0]) && isalpha(p[1]) && isdigit(p[2]) && isdir(p[3]) ) {
|
||
|
// move x tiles in direction
|
||
|
int count = p[0] - '0';
|
||
|
int xpos = p[1];
|
||
|
int ypos = p[2];
|
||
|
int dir = p[3];
|
||
|
if (count > size) {
|
||
|
printf("* can't move more than %d tiles\n",size);
|
||
|
err = TRUE;
|
||
|
} else {
|
||
|
if (movetile(count,xpos,ypos,dir,p+4)) err = TRUE;
|
||
|
}
|
||
|
} else if ((strlen(p) == 2) && isalpha(p[0]) && isdigit(p[1]) ) {
|
||
|
// place tile
|
||
|
int xpos = p[0];
|
||
|
int ypos = p[1];
|
||
|
if (isvalidxypos(xpos,ypos)) {
|
||
|
if (placetile((turnnumber == 1) ? (1-turn) : turn,xpos,ypos,mod)) err = TRUE;
|
||
|
} else {
|
||
|
printf("* invalid cell reference '%c%c'\n",xpos,ypos);
|
||
|
err = TRUE;
|
||
|
}
|
||
|
} else {
|
||
|
printf("* bad command '%s'\n",cmd);
|
||
|
err = TRUE;
|
||
|
}
|
||
|
|
||
|
if (err && ai[turn]) {
|
||
|
char temp[10];
|
||
|
printf("...ai made an error?!");
|
||
|
fgets(temp ,MAX_CMD_LEN, stdin);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (!err) {
|
||
|
logmove(turn, cmd, topofmove);
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
char *makecolorstr(char *orig, char *newstr) {
|
||
|
char *p;
|
||
|
char *curcol;
|
||
|
strcpy(newstr, "");
|
||
|
for (p = orig; *p; p++) {
|
||
|
if (!ismod(*p)) {
|
||
|
curcol = getpcol(getpchar(*p));
|
||
|
strcat(newstr, curcol);
|
||
|
}
|
||
|
strncat(newstr, p, 1);
|
||
|
}
|
||
|
strcat(newstr, reset);
|
||
|
return newstr;
|
||
|
}
|
||
|
|
||
|
void showaivalues(int best) {
|
||
|
int x,y;
|
||
|
char ch;
|
||
|
|
||
|
printf(FORMAT_ROWHEAD, ' ');
|
||
|
ch = 'a';
|
||
|
for (x=0; x < w; x++) {
|
||
|
printf(FORMAT_COLHEAD, ch++);
|
||
|
}
|
||
|
printf(FORMAT_ROWTAIL, ' ');
|
||
|
printf("\n");
|
||
|
line();
|
||
|
ch = '1' + h - 1;
|
||
|
for (y=h-1; y >= 0; y--) {
|
||
|
// show ai priority values
|
||
|
printf(FORMAT_ROWHEAD, ch);
|
||
|
for (x=0; x < w; x++) {
|
||
|
int val = aivalue[y*w+x];
|
||
|
// show owner
|
||
|
printf(FORMAT_AIVAL, val == best ? GREEN : YELLOW, val, reset);
|
||
|
}
|
||
|
printf(FORMAT_ROWTAIL, ch);
|
||
|
printf("\n");
|
||
|
|
||
|
// TODO: move this to a function
|
||
|
// show board stack
|
||
|
printf(FORMAT_ROWHEAD, ch);
|
||
|
for (x=0; x < w; x++) {
|
||
|
char *str = board[y*w+x];
|
||
|
|
||
|
// show stack
|
||
|
if (str) {
|
||
|
char origstr[MAXSTACK*10];
|
||
|
char colorstr[MAXSTACK*10];
|
||
|
sprintf(origstr,FORMAT_STACK, str ? str : " ");
|
||
|
// add ansi color codes
|
||
|
makecolorstr(origstr, colorstr);
|
||
|
//strcpy(colorstr,origstr);
|
||
|
printf("%s", colorstr);
|
||
|
} else {
|
||
|
printf(FORMAT_STACK, " ");
|
||
|
}
|
||
|
}
|
||
|
printf(FORMAT_ROWTAIL, ch);
|
||
|
ch--;
|
||
|
printf("\n");
|
||
|
line();
|
||
|
|
||
|
}
|
||
|
printf(FORMAT_ROWHEAD, ' ');
|
||
|
ch = 'a';
|
||
|
for (x=0; x < w; x++) {
|
||
|
printf(FORMAT_COLHEAD, ch++);
|
||
|
}
|
||
|
printf(FORMAT_ROWTAIL, ' ');
|
||
|
printf("\n");
|
||
|
|
||
|
/*
|
||
|
for (i=0;i<MAXPLAYERS; i++) {
|
||
|
showboardbinary(i);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
void showboard(void) {
|
||
|
int x,y;
|
||
|
char ch;
|
||
|
printf("\n%sRound %d - Turn %d (%s%s%s%s):\n", BOLD, curround, turnnumber, getpcol(turn), BOLD, getpname(turn), reset);
|
||
|
printf(FORMAT_ROWHEAD, ' ');
|
||
|
ch = 'a';
|
||
|
for (x=0; x < w; x++) {
|
||
|
printf(FORMAT_COLHEAD, ch++);
|
||
|
}
|
||
|
printf(FORMAT_ROWTAIL, ' ');
|
||
|
printf("\n");
|
||
|
line();
|
||
|
ch = '1' + h - 1;
|
||
|
for (y=h-1; y >= 0; y--) {
|
||
|
/*
|
||
|
printf(FORMAT_ROWHEAD, ch);
|
||
|
for (x=0; x < w; x++) {
|
||
|
char *str = board[y*w+x];
|
||
|
// show owner
|
||
|
printf(FORMAT_OWNER, getpcol(getowner(str)),getowner(str), getmod(str), reset);
|
||
|
}
|
||
|
printf(FORMAT_ROWTAIL, ch);
|
||
|
printf("\n");
|
||
|
*/
|
||
|
printf(FORMAT_ROWHEAD, ch);
|
||
|
for (x=0; x < w; x++) {
|
||
|
char *str = board[y*w+x];
|
||
|
|
||
|
// show stack
|
||
|
if (str) {
|
||
|
char origstr[MAXSTACK*10];
|
||
|
char colorstr[MAXSTACK*10];
|
||
|
sprintf(origstr,FORMAT_STACK, str ? str : " ");
|
||
|
// add ansi color codes
|
||
|
makecolorstr(origstr, colorstr);
|
||
|
//strcpy(colorstr,origstr);
|
||
|
printf("%s", colorstr);
|
||
|
} else {
|
||
|
printf(FORMAT_STACK, " ");
|
||
|
}
|
||
|
}
|
||
|
printf(FORMAT_ROWTAIL, ch);
|
||
|
ch--;
|
||
|
printf("\n");
|
||
|
line();
|
||
|
|
||
|
}
|
||
|
printf(FORMAT_ROWHEAD, ' ');
|
||
|
ch = 'a';
|
||
|
for (x=0; x < w; x++) {
|
||
|
printf(FORMAT_COLHEAD, ch++);
|
||
|
}
|
||
|
printf(FORMAT_ROWTAIL, ' ');
|
||
|
printf("\n");
|
||
|
|
||
|
/*
|
||
|
for (i=0;i<MAXPLAYERS; i++) {
|
||
|
showboardbinary(i);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
int xtoleftshift(int num) {
|
||
|
return (w-num-1);
|
||
|
}
|
||
|
|
||
|
|
||
|
void showboardbinary(int pnum) {
|
||
|
int x,y;
|
||
|
int line[h];
|
||
|
int allones = pow(2,w)-1;
|
||
|
int hline = 0,vline = allones;
|
||
|
char ch;
|
||
|
printf("Binary board for %s:\n", getpname(pnum));
|
||
|
|
||
|
// show top heading
|
||
|
printf(" - ");
|
||
|
ch = 'a';
|
||
|
for (x=0; x < w; x++) {
|
||
|
printf("%c",ch++);
|
||
|
}
|
||
|
printf("\n");
|
||
|
|
||
|
ch = '1' + h - 1;
|
||
|
for (y=h-1; y >= 0; y--) {
|
||
|
line[y] = 0;
|
||
|
for (x=0; x < w; x++) {
|
||
|
char *str = board[y*w+x];
|
||
|
if ((getmod(str) != '|') && getowner(str) != ' ' && (getpnum(getowner(str)) == pnum)) {
|
||
|
int this;
|
||
|
this = 1 << xtoleftshift(x);
|
||
|
line[y] |= this;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
printbinaryline(ch--, line[y], TRUE);
|
||
|
}
|
||
|
|
||
|
//printbinaryline('X', vline);
|
||
|
for (y=h-1; y >= 0; y--) {
|
||
|
hline |= line[y];
|
||
|
vline &= line[y];
|
||
|
//printbinaryline('x', vline);
|
||
|
}
|
||
|
/*
|
||
|
printf("raw:\n");
|
||
|
ch = '1' + h - 1;
|
||
|
for (y=h-1; y >= 0; y--) {
|
||
|
line[y] = 0;
|
||
|
printf("%c - ", ch--);
|
||
|
// show line
|
||
|
for (x=0; x < w; x++) {
|
||
|
char *str = board[y*w+x];
|
||
|
if ((getmod(str) != '|') && getowner(str) != ' ' && (getpnum(getowner(str)) == pnum)) {
|
||
|
printf("%d",1);
|
||
|
} else {
|
||
|
printf("%d",0);
|
||
|
}
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
printf("horizontal line:\n");
|
||
|
printbinaryline('H', hline, FALSE);
|
||
|
printf(" - %s\n",(hline == allones) ? "YES" : "");
|
||
|
|
||
|
// all 1s = yes
|
||
|
printf("vertical line:\n");
|
||
|
printbinaryline('V', vline, FALSE);
|
||
|
printf(" - %s\n",(vline) ? "YES" : "");
|
||
|
// all 1s = yes
|
||
|
|
||
|
}
|
||
|
|
||
|
int xyvalidforroad(char (*b)[MAXBOARDSTRLEN], int who, int x, int y) {
|
||
|
char *cell;
|
||
|
cell = b[y*w+x];
|
||
|
if (isvalidxy(x,y) && (getpnum(getowner(cell)) == who) && (getmod(cell) != '|')) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
int checkxyforroad(char (*b)[MAXBOARDSTRLEN], int who, int sx, int sy, int sd) {
|
||
|
int xmod[]={0, 1, 0, -1}; /// nesw
|
||
|
int ymod[]={1, 0, -1, 0}; /// nesw
|
||
|
int x,y,newx,newy,d,xch,ych,newxch,newych;
|
||
|
int startxch,startych;
|
||
|
int startdir,done,turncount;
|
||
|
int db = 0;
|
||
|
int visited[MAXW*MAXH];
|
||
|
|
||
|
// mark everywhere as not visited
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
visited[y*w+x] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
x=sx;
|
||
|
y=sy;
|
||
|
d=sd;
|
||
|
startdir=d;
|
||
|
|
||
|
xytopos(x, y, &xch, &ych);
|
||
|
startxch = xch;
|
||
|
startych = ych;
|
||
|
|
||
|
if (db) fprintf(stderr,"** db: looking for %s roads from %c%c (owned by %c / %d)\n", getpname(who), xch,ych, getowner(b[sy*w+sx]), getpnum(getowner(b[sy*w+sx])));
|
||
|
|
||
|
if (getpnum(getowner(b[sy*w+sx])) != who) {
|
||
|
if (db) fprintf(stderr,"** db: %s doesn't own %c%c - stopping\n", getpname(who), xch,ych);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
done = FALSE;
|
||
|
turncount = 0;
|
||
|
while (!done) {
|
||
|
int moved = FALSE;
|
||
|
int leftdir,rightdir;
|
||
|
// figure out where left/right is.
|
||
|
leftdir = (d - 1); if (leftdir < 0) leftdir = 3;
|
||
|
rightdir = (d + 1); if (rightdir >= 4) rightdir = 0;
|
||
|
|
||
|
// is there a valid cell in the current dir?
|
||
|
newx = x + xmod[d];
|
||
|
newy = y + ymod[d];
|
||
|
xytopos(newx, newy, &newxch, &newych);
|
||
|
|
||
|
if (db) fprintf(stderr,"** db: Checking forward (dir %d) from %c%c (%c%c)\n", d, xch,ych,newxch,newych);
|
||
|
if (xyvalidforroad(b, who,newx,newy)) {
|
||
|
x = newx;
|
||
|
y = newy;
|
||
|
moved = TRUE;
|
||
|
} else { // turn left
|
||
|
newx = x + xmod[leftdir];
|
||
|
newy = y + ymod[leftdir];
|
||
|
|
||
|
xytopos(newx, newy, &newxch, &newych);
|
||
|
|
||
|
if (db) fprintf(stderr,"** db: Checking left (dir %d) from %c%c (%c%c)\n", leftdir, xch,ych,newxch,newych);
|
||
|
if (xyvalidforroad(b, who,newx,newy)) {
|
||
|
x = newx;
|
||
|
y = newy;
|
||
|
d = leftdir;
|
||
|
moved = TRUE;
|
||
|
} else { // turn right
|
||
|
newx = x + xmod[rightdir];
|
||
|
newy = y + ymod[rightdir];
|
||
|
xytopos(newx, newy, &newxch, &newych);
|
||
|
if (db) fprintf(stderr,"** db: Checking right (dir %d) from %c%c (%c%c)\n", rightdir, xch,ych,newxch,newych);
|
||
|
if (xyvalidforroad(b, who,newx,newy)) {
|
||
|
x = newx;
|
||
|
y = newy;
|
||
|
d = rightdir;
|
||
|
moved = TRUE;
|
||
|
} else {
|
||
|
// reverse direction
|
||
|
if (--d < 0) d = 3;
|
||
|
if (--d < 0) d = 3;
|
||
|
|
||
|
if (db) fprintf(stderr,"** db: Turning around (dir %d) from %c%c (%c%c)\n", d, xch,ych,newxch,newych);
|
||
|
if (xyvalidforroad(b, who,newx,newy)) {
|
||
|
x = newx;
|
||
|
y = newy;
|
||
|
moved = TRUE;
|
||
|
} else {
|
||
|
if (db) fprintf(stderr,"** db: Couldn't turn around - no roads.\n");
|
||
|
done = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (moved) {
|
||
|
if (db) fprintf(stderr,"** db: %c%c is owned by us, moving there.\n", newxch,newych);
|
||
|
xch = newxch;
|
||
|
ych = newych;
|
||
|
turncount = 0;
|
||
|
// back to start?
|
||
|
if ((x == sx) && (y == sy)) {
|
||
|
if (db) fprintf(stderr,"** db: We're back at the start (%c%c) - no roads.\n", startxch,startych);
|
||
|
done = TRUE;
|
||
|
// other side?
|
||
|
} else if ((sx == 0) && (x == w-1)) {
|
||
|
if (db) fprintf(stderr,"** db: Horizontal w->e path from from %c%c to %c%c!\n", startxch,startych,xch,ych);
|
||
|
return TRUE;
|
||
|
} else if ((sx == w-1) && (x == 0)) {
|
||
|
if (db) fprintf(stderr,"** db: Horizontal e->w path from from %c%c to %c%c!\n", startxch,startych,xch,ych);
|
||
|
return TRUE;
|
||
|
} else if ((sy == 0) && (y == h-1)) {
|
||
|
if (db) fprintf(stderr,"** db: Vertical s->n path from from %c%c to %c%c!\n", startxch,startych,xch,ych);
|
||
|
return TRUE;
|
||
|
} else if ((sy == h-1) && (y == 0)) {
|
||
|
if (db) fprintf(stderr,"** db: Vertical n->s path from from %c%c to %c%c!\n", startxch,startych,xch,ych);
|
||
|
return TRUE;
|
||
|
}
|
||
|
} else {
|
||
|
// didn't move?
|
||
|
done = TRUE;
|
||
|
}
|
||
|
}
|
||
|
if (db) fprintf(stderr,"** db: No roads found. Returning false.\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int playerhasroads(int pnum, char (*b)[]) {
|
||
|
int x,y;
|
||
|
|
||
|
// check for roads from bottom to top
|
||
|
for (x=0; x < w; x++) {
|
||
|
if (checkxyforroad(b, pnum, x, 0, NORTH)) return TRUE;
|
||
|
|
||
|
}
|
||
|
// check for roads from left to right
|
||
|
for (y=h-1; y >= 0; y--) {
|
||
|
if (checkxyforroad(b, pnum, 0, y, EAST)) return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void printbinaryline(char prefix, int num, int wantcr) {
|
||
|
int x;
|
||
|
// show line number
|
||
|
printf("%c - ", prefix);
|
||
|
// show line
|
||
|
for (x=0; x < w; x++) {
|
||
|
if (num & (1 << xtoleftshift(x))) {
|
||
|
printf("%d",1);
|
||
|
} else {
|
||
|
printf("%d",0);
|
||
|
}
|
||
|
}
|
||
|
if (wantcr) printf("\n");
|
||
|
}
|
||
|
|
||
|
void line(void) {
|
||
|
int x;
|
||
|
for (x=0; x < w; x++) {
|
||
|
printf("%s", LINEPART);
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
int gotwinner(void) {
|
||
|
if (winner >= 0) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void determinewinner(void) {
|
||
|
int x,y,i;
|
||
|
int flatstones[MAXPLAYERS];
|
||
|
int stonepoints,sizepoints;
|
||
|
|
||
|
// win by roads?
|
||
|
if (!gotwinner()) {
|
||
|
if (hasroad[0] && hasroad[1]) {
|
||
|
// current turn wins a tie
|
||
|
winner = turn;
|
||
|
} else if (hasroad[0]) {
|
||
|
winner = 0;
|
||
|
} else if (hasroad[1]) {
|
||
|
winner = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!gotwinner()) {
|
||
|
// count flatstones
|
||
|
for (y=0; y < h; y++) {
|
||
|
for (x=0; x < w; x++) {
|
||
|
if (!getmod(board[y*w+x])) {
|
||
|
flatstones[getpnum(getowner(board[y*w+x]))]++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (flatstones[0] > flatstones[1]) {
|
||
|
winner = 0;
|
||
|
} else if (flatstones[0] < flatstones[1]) {
|
||
|
winner = 0;
|
||
|
} else {
|
||
|
// current turn wins a tie
|
||
|
winner = turn;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!gotwinner()) {
|
||
|
// shouldn't be possible...
|
||
|
winner = turn;
|
||
|
}
|
||
|
|
||
|
// only winner gets points
|
||
|
// 1 point for every board square (size*size)
|
||
|
// 1 point for each of their remaining tiles
|
||
|
sizepoints = (size * size);
|
||
|
stonepoints = tilesleft[winner];
|
||
|
roundpoints[winner] = sizepoints + stonepoints;
|
||
|
printf("%s*** %s%s%s%s%s wins round %d and earns %d points (%d from board size, %d from remaining tiles)%s\n", BOLD, getpcol(winner), BOLD, getpname(winner), reset, BOLD, curround,
|
||
|
roundpoints[winner], sizepoints, stonepoints,reset);
|
||
|
|
||
|
// update totals
|
||
|
for (i=0; i<MAXPLAYERS; i++) {
|
||
|
gamepoints[i] += roundpoints[i];
|
||
|
}
|
||
|
|
||
|
// show totals
|
||
|
showscores();
|
||
|
// TODO: bold this
|
||
|
printf("\n%s--- Press ENTER for next round ---%s\n", BOLD, reset);
|
||
|
// wait for key
|
||
|
fgets(cmdbuf ,MAX_CMD_LEN, stdin);
|
||
|
}
|
||
|
|
||
|
void showscores(void) {
|
||
|
int i;
|
||
|
char temp[64];
|
||
|
printf("Current scores:\n");
|
||
|
for (i=0; i<MAXPLAYERS; i++) {
|
||
|
sprintf(temp, "%s%12s%s",getpcol(i), getpname(i), reset);
|
||
|
printf(" %s: %s%d points%s\n", temp, getpcol(i),gamepoints[i], reset);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void pickfirstplayer() {
|
||
|
if (rand() % 2 == 0) firstturn = 0; else firstturn = 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
int normalise(char *a) {
|
||
|
int donesomething = FALSE;
|
||
|
if (!a) return FALSE;
|
||
|
switch (*a) {
|
||
|
case '^':
|
||
|
case 'k':
|
||
|
*a = 'C'; donesomething = TRUE; break;
|
||
|
case 's':
|
||
|
case '|':
|
||
|
*a = 'S'; donesomething = TRUE; break;
|
||
|
}
|
||
|
return donesomething;
|
||
|
}
|
||
|
|
||
|
void logmove(int who, char *txt, char append) {
|
||
|
char temp[256];
|
||
|
char *p;
|
||
|
|
||
|
// use standard notation as per https://ustak.org/portable-tak-notation/
|
||
|
strcpy(temp, txt);
|
||
|
for (p = temp ; *p; p++) {
|
||
|
normalise(p);
|
||
|
}
|
||
|
|
||
|
if ((append == 'S') || (append == 'C')) {
|
||
|
strncat(temp, &append, 1);
|
||
|
}
|
||
|
|
||
|
if (flattened) {
|
||
|
strcat(temp, "*");
|
||
|
}
|
||
|
|
||
|
// first player's turn?
|
||
|
if (who == firstturn) {
|
||
|
sprintf(movelog, "%s", temp);
|
||
|
} else {
|
||
|
strcat(movelog, " ");
|
||
|
strcat(movelog, temp);
|
||
|
printf("%s%s%d.%s%s %s%s\n", YELLOW, BOLD, turnnumber, reset, YELLOW, movelog,reset);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[]) {
|
||
|
init();
|
||
|
atexit(cleanup);
|
||
|
if (setupgame(5, 2)) {
|
||
|
exit(1);
|
||
|
}
|
||
|
srand(time(NULL));
|
||
|
pickfirstplayer(); // randomize 'firstturn'
|
||
|
while (curround <= numrounds) {
|
||
|
setupround();
|
||
|
while (!gameover) {
|
||
|
showboard();
|
||
|
for (readcmd(cmdbuf); parsecmd(cmdbuf); readcmd(cmdbuf));
|
||
|
updateroadstatus();
|
||
|
if (checkendgame()) {
|
||
|
showboard();
|
||
|
determinewinner();
|
||
|
gameover = 1;
|
||
|
}
|
||
|
nextturn();
|
||
|
}
|
||
|
nextround();
|
||
|
}
|
||
|
}
|