I am creating a KotH (King of the Hill) challenge for Programming Puzzles and Code Golf. Other site users will write programs that play Texplode (a version of this game on a square grid). The program works, but I don't think it's anywhere near perfect; I don't often publish C programs so I rarely get any critique on them.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
bool move(int boardsize, int board[boardsize * boardsize], int boardown[boardsize * boardsize], int playernum, int movepos);
bool hascounters(int boardsize, int board[boardsize * boardsize], int boardown[boardsize * boardsize], int playernum);
void writegrid(int boardsize, int board[boardsize * boardsize], int boardown[boardsize * boardsize], int playernum, FILE *file);
bool readinput(FILE *file, int *movepos);
void printoutput(int boardsize, int board[boardsize * boardsize], int boardown[boardsize * boardsize]);
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Texplode, by wizzwizz4.\n");
printf("Based on Hexplode.\n");
printf("The first argument is the board size.\n");
printf("The second argument is the number of players.\n");
printf("The 2n+1th argument is the nth player's input file.\n");
printf("The 2n+2th argument is the nth player's output file.\n");
printf("Human-readable ANSI art will be printed to STDOUT.\n");
return 0;
}
int boardsize = atoi(argv[1]);
int playercount = atoi(argv[2]);
if (argc < (2 * playercount + 3)) {
printf("You promised more players than you gave me.\n");
}
FILE *playerinv[playercount];
FILE *playeroutv[playercount];
for (int i = 0; i < playercount; i++) {
playerinv[i] = fopen(argv[2 * i + 3], "r");
playeroutv[i] = fopen(argv[2 * i + 4], "w");
}
int board[boardsize * boardsize];
memset(board, 0, sizeof(board));
int boardown[boardsize * boardsize];
bool stillplaying[playercount];
memset(stillplaying, true, sizeof(stillplaying));
bool playoverride = true;
int numplaying = playercount;
while (1) {
if (numplaying <= 1) {
break;
}
for (int i = 0; i < playercount; i++) {
if (stillplaying[i] || playoverride) {
writegrid(boardsize, board, boardown, i, playeroutv[i]);
int movepos = 0;
if (!readinput(playerinv[i], &movepos)) {
printf("Error! :-S\n");
}
move(boardsize, board, boardown, i, movepos);
numplaying = 0;
for (int j = 0; j < playercount; j++) {
if (hascounters(boardsize, board, boardown, j)) {
numplaying += 1;
stillplaying[j] = true;
} else {
stillplaying[j] = false;
}
}
}
printoutput(boardsize, board, boardown);
}
playoverride = false;
}
printf("Game over.\n");
}
bool move(int boardsize, int board[boardsize * boardsize], int boardown[boardsize * boardsize], int playernum, int movepos) {
if (movepos >= boardsize * boardsize) {
return false;
}
if (board[movepos] == 0) {
board[movepos] = 1;
boardown[movepos] = playernum;
return true;
}
if (boardown[movepos] == playernum) {
board[movepos] += 1;
/* Cascade */
bool notdone = true;
while (notdone) {
notdone = false;
for (int i = 0; i < boardsize * boardsize; i++) {
int neighbourcount = 4;
unsigned int neighbours = 0xf;
if (i / boardsize == 0) {
neighbourcount -= 1;
neighbours &= ~0x1;
}
if (i / boardsize == boardsize - 1) {
neighbourcount -= 1;
neighbours &= ~0x2;
}
if (i % boardsize == 0) {
neighbourcount -= 1;
neighbours &= ~0x4;
}
if (i % boardsize == boardsize - 1) {
neighbourcount -= 1;
neighbours &= ~0x8;
}
if (board[i] >= neighbourcount) {
notdone = true;
board[i] -= neighbourcount;
boardown[i] = playernum;
if (neighbours & 0x1) {
board[i - boardsize] += 1;
boardown[i - boardsize] = playernum;
}
if (neighbours & 0x2) {
board[i + boardsize] += 1;
boardown[i + boardsize] = playernum;
}
if (neighbours & 0x4) {
board[i - 1] += 1;
boardown[i - 1] = playernum;
}
if (neighbours & 0x8) {
board[i + 1] += 1;
boardown[i + 1] = playernum;
}
}
}
}
return true;
}
return false;
}
bool hascounters(int boardsize, int board[boardsize * boardsize], int boardown[boardsize * boardsize], int playernum) {
for (int i = 0; i < boardsize * boardsize; i++) {
if (board[i] && boardown[i] == playernum) {
return true;
}
}
return false;
}
void writegrid(int boardsize, int board[boardsize * boardsize], int boardown[boardsize * boardsize], int playernum, FILE *file) {
for (int i = 0; i < boardsize * boardsize; i++) {
if (board[i] == 0) {
putc('0', file);
continue;
}
if (boardown[i] == playernum) {
putc('3' + board[i], file);
} else {
putc('0' + board[i], file);
}
}
putc(';', file);
fflush(file);
}
bool readinput(FILE *file, int *movepos) {
return (fscanf(file, "%d;", movepos) == 1); /* 1 "%d", one match. */
}
void printoutput(int boardsize, int board[boardsize * boardsize], int boardown[boardsize * boardsize]) {
printf("\x1B[1;1H");
for (int x = 0, i = 0; x < boardsize; x += 1) {
for (; i < (x + 1) * boardsize; i++) {
if (board[i]) {
printf("\x1B[%dm%d", boardown[i] + 30, board[i]);
} else {
printf("\x1B[m ");
}
}
printf("\n");
}
}
I also have a basic UI program that I used for testing on Windows, where I don't have the luxury of using /dev/ttyn
files. I know that this is not well-written, but I am providing it so that people have a way to test any changes that they make.
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
char * getline(void);
int main(int argc, char* argv[]) {
/* "\\.\pipe\" pipename */
HANDLE pipein = CreateNamedPipe(
argv[1],
PIPE_ACCESS_INBOUND,
PIPE_TYPE_BYTE,
PIPE_UNLIMITED_INSTANCES,
0, /* Out buffer */
256, /* In buffer */
0, /* Default wait timeout */
NULL
);
HANDLE pipeout = CreateNamedPipe(
argv[2],
PIPE_ACCESS_OUTBOUND,
PIPE_TYPE_BYTE,
PIPE_UNLIMITED_INSTANCES,
256, /* Out buffer */
0, /* In buffer */
0, /* Default wait timeout */
NULL
);
while (1) {
if (_kbhit()) { /* Keyboard hit */
char* input = getline();
WriteFile(
pipeout,
input,
strlen(input),
NULL,
NULL
);
free(input);
}
DWORD availbytes = 0;
PeekNamedPipe(
pipein,
NULL,
0,
NULL,
&availbytes,
NULL
);
if (availbytes) {
char byte;
ReadFile(
pipein,
&byte,
1,
NULL,
NULL
);
printf("%c", byte);
}
}
}
/* https://stackoverflow.com/a/314422/5223757 */
char * getline(void) {
char * line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c;
if(line == NULL)
return NULL;
for(;;) {
c = fgetc(stdin);
if(c == EOF)
break;
if(--len == 0) {
len = lenmax;
char * linen = realloc(linep, lenmax *= 2);
if(linen == NULL) {
free(linep);
return NULL;
}
line = linen + (line - linep);
linep = linen;
}
if((*line++ = c) == '\n')
break;
}
*line = '\0';
return linep;
}