/*! \file Domineering.c
** \author Matthew Mead
** \par email: mmead\@digipen.edu
** \date 2/3/2013
** \brief Implementation of Domineering rule set
*
* See:
* http://en.wikipedia.org/wiki/Domineering
* http://www.jonhurtado.com/?p=25
*******************************************************************************/
#include <string.h> /* strlen, strcpy, strchr */
#include <stdlib.h> /* malloc, calloc, free */
#include <stdio.h> /* printf */
#include "PluginCommon.h"
#include "PluginInterface.h"
#include "Domineering.h"
/*****************************************************************************/
/* Private */
/*****************************************************************************/
/*****************************************************************************/
typedef int bool;
#define true 1
#define false 0
#define DEBUG_PLUGINx
static char Symbols[] = {'0', '-', '|', 'X'};
#ifdef DEBUG_PLUGIN
static void dump_board(struct Game *game)
{
unsigned row, col;
for (row = 0; row < game->rows; row++)
{
for (col = 0; col < game->cols; col++)
{
int index1 = row * game->cols + col;
int index2 = game->board[index1];
char c = Symbols[index2];
if (c)
printf("%c", c);
else
printf(".");
}
printf("\n");
}
printf("\n");
}
#endif
/*
*
* Checks to see if the move (row,col) for the player is a valid move.
*
*/
static bool is_legal_move(struct Game* game, int player, unsigned row, unsigned column)
{
/* The move is off of the board */
if (row >= game->rows || column >= game->cols)
return false;
/* The cell position is already occupied */
if (game->board[row * game->cols + column])
return false;
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: A: 1 is %i, 2 is %i\n", game->player1_dir, game->player2_dir);
printf("***PLUGIN: gameboard at %i,%i is %c (%i)\n", row, column, game->board[row * game->cols + column], game->board[row * game->cols + column]);
printf("***PLUGIN: gameboard at %i,%i is %c (%i)\n", row + 1, column, game->board[row * game->cols + column + 1], game->board[row * game->cols + column + 1]);
#endif
/* Now look to see if the move extends into an occupied cell */
if (player == 1)
{
/* player 1 is horizontal */
if (game->player1_dir == ddHORIZONTAL)
{
if (column + 1 >= game->cols)
return false;
if (game->board[row * game->cols + column + 1])
return false;
}
else /* player 1 is vertical */
{
if (row + 1 >= game->rows)
return false;
if (game->board[(row + 1) * game->cols + column])
return false;
}
}
else /* player 2 */
{
/* player 2 is vetical */
if (game->player2_dir == doVERTICAL)
{
if (row + 1 >= game->rows)
return false;
if (game->board[(row + 1) * game->cols + column])
return false;
}
else /* player 2 is horizontal */
{
if (column + 1 >= game->cols)
return false;
if (game->board[row * game->cols + column + 1])
return false;
}
}
/* If we get this far, it's a valid move */
return true;
}
/*
* Check if this player can move. If this player can't move, then the
* other player has won the game.
*
*/
static bool player_can_move(struct Game *game, int player)
{
/* Try every spot to see if at least one is valid */
unsigned i, j;
for (i = 0; i < game->rows; i++)
for (j = 0; j < game->cols; j++)
if (is_legal_move(game, player, i, j))
{
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: Player %i can move to %i,%i.\n", player, i, j);
fflush(stdout);
#endif
return true;
}
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: Player %i can't move.\n", player);
#endif
return false;
}
/*
* Given a NULL-terminated string of cell positions, mark them as occupied.
* The string will look something like this:
*
* "r.c,r.c,r.c,r.c..."
*
* where r.c is row.column. For example:
*
* "0.0,0.1,1.2.1.3"
*
* TODO: Add more error checking for invalid strings.
*/
static bool mark_off_cells(struct Game *game, const char *string)
{
char *buffer = (char *)malloc(strlen(string) + 1);
char *delim = ",";
char *token;
strcpy(buffer, string);
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: mark off |%s|\n", buffer);
#endif
/* First token */
token = strtok(buffer, delim);
while (token)
{
int row, col;
char *pt;
strcpy(buffer, token);
/* Look for the . separator */
pt = strchr(buffer, '.');
/* Sanity check */
if (!pt)
continue;
/* Separate the row and column and extract each one */
*pt = 0;
row = atoi(buffer);
col = atoi(pt + 1);
*pt = *delim;
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: |%s| (%i,%i)\n", token, row, col);
#endif
/* Update the board with the "dummy" piece */
game->board[row * game->cols + col] = Symbols[3];
/* Next token */
token = strtok(NULL, delim);
}
free(buffer);
return false;
}
/*****************************************************************************/
/* Interface */
/*****************************************************************************/
/*****************************************************************************/
int Init(void)
{
/* Nothing to do, init is done in CreateGame */
return 0;
}
void Shutdown(void)
{
}
const char* Name(void)
{
return "Domineering";
}
const char* Author(void)
{
return "Matthew Mead (mmead@digipen.edu)";
}
const char* Type(void)
{
return "Domineering";
}
const char* Version(void)
{
return "0.3";
}
const char* APIVersion(void)
{
return "0.1";
}
int NumberOfPlayers(void)
{
return 2;
}
void* CreateGame(void)
{
struct Game* newGame = calloc(1, sizeof(struct Game));
if(newGame)
newGame->status = PLUGIN_GAME_STATUS_CREATED;
return newGame;
}
/*
* To specify which player is horizontal
* ORH:1 (player 1) or ORH:2 (player 2)
*
* To specify dimension of the board
* DIM:RxC (R is rows, C is columns, e.g. DIM:4x8)
*
* To remove or mark off cells as in use
* MRK:r.c,r.c,... (r is row, c is column)
* example: MRK:0.0,0.1,0.2,1.1,1.2,2.3
*
*/
int SetGameOption(void* game, const char* option)
{
struct Game* igame = (struct Game*)game;
if (!option)
return 0;
if (!igame)
return 0;
/* Sanity check */
if ((strlen(option) < 4) || (option[3] != ':'))
return 0;
/* Horizontal orientation, e.g. ORH:1 */
if (!strncmp(option, "ORH", 3))
{
if (option[4] == '1')
{
igame->player1_dir = ddHORIZONTAL;
igame->player2_dir = doVERTICAL;
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: Player 1 is moving horizontal\n");
#endif
}
else
{
igame->player1_dir = doVERTICAL;
igame->player2_dir = ddHORIZONTAL;
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: Player 1 is moving vertical\n");
#endif
}
}
else if (!strncmp(option, "DIM", 3))
{
char *buffer, *x;
if (!strchr(option, 'x'))
return 0;
buffer = (char *)malloc(strlen(option) + 1);
strcpy(buffer, option);
x = strchr(option, 'x');
*x = 0;
igame->rows = atoi(option + 4);
igame->cols = atoi(x + 1);
igame->board = (char *)calloc(igame->rows * igame->cols, sizeof(char));
free(buffer);
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: SetGameOption: %i x %i\n", igame->rows, igame->cols);
dump_board(igame);
#endif
}
else if (!strncmp(option, "MRK", 3))
{
if (!igame->board)
{
printf("***PLUGIN: Can't MRK because game->board is NULL\n");
return 0;
}
mark_off_cells(igame, option + 4);
}
return 1;
}
int AddPlayer(void* igame, void* player)
{
struct Game* game = igame;
if(game->players[0] == NULL)
game->players[0] = player;
else if(game->players[1] == NULL)
game->players[1] = player;
else
return 1;
return 0;
}
int RemovePlayer(void* igame, void* player)
{
struct Game* game = igame;
if(game->players[0] == player)
game->players[0] = NULL;
else if(game->players[1] == player)
game->players[1] = NULL;
else
return 1;
if(game->status == PLUGIN_GAME_STATUS_PLAYING)
game->status = PLUGIN_GAME_STATUS_PAUSED;
return 0;
}
int PlayerCount(void* igame)
{
struct Game* game = igame;
return (game->players[0] != 0) + (game->players[1] != 0);
}
void* GetPlayer(void* igame, int playerNumber)
{
struct Game* game = igame;
if(playerNumber == 1 || playerNumber == 2)
return game->players[playerNumber-1];
return NULL;
}
int GetPlayerNumber(void* igame, void* player)
{
struct Game* game = igame;
if(game->players[0] == player)
return 1;
if(game->players[1] == player)
return 2;
return 0;
}
int StartGame(void* igame)
{
struct Game* game = igame;
if(PlayerCount(game) != 2)
return 1;
game->nextPlayer = 1;
game->status = PLUGIN_GAME_STATUS_PLAYING;
return 0;
}
void* GetNextPlayer(void* igame)
{
struct Game* game = igame;
if(game->nextPlayer > 0)
return game->players[game->nextPlayer-1];
return NULL;
}
/*
* The real workhorse of the interface.
*
* Given a player and a move, validate it. If the move is valid, the function
* also checks to see if the next player has a move. If the next player has
* no move, the game is over.
*
*/
int ValidateMove(void* igame, void* iplayer, const char* move)
{
struct Game* game = igame;
int player = game->players[0] == iplayer ? 1 : 2;
int index = atoi(move);
int row = index / game->cols;
int col = index % game->cols;
bool legal;
/* quick sanity check */
if(move == NULL || strlen(move) < 1 || game->status != PLUGIN_GAME_STATUS_PLAYING)
return -1;
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: ValidateMove board:\n");
printf("***PLUGIN: Move %i is %i,%i\n", index, row, col);
fflush(stdout);
dump_board(game);
#endif
/* verify move */
legal = is_legal_move(game, player, row, col);
if (!legal)
{
#ifdef DEBUG_PLUGIN
printf("***PLUGIN: Not legal\n");
#endif
return -1;
}
/* play the move */
/* Player 1 */
if(iplayer == game->players[0])
{
/* Player 1 is horizontal */
if (game->player1_dir == ddHORIZONTAL)
{
game->board[row * game->cols + col] = '1';
game->board[row * game->cols + col + 1] = '1';
game->nextPlayer = 2;
}
else /* Player 1 vertical */
{
game->board[row * game->cols + col] = '1';
game->board[(row + 1) * game->cols + col] = '1';
game->nextPlayer = 2;
}
}
else /* Player 2 */
{
/* Player 2 is vertical */
if (game->player2_dir == doVERTICAL)
{
game->board[row * game->cols + col] = '2';
game->board[(row + 1) * game->cols + col] = '2';
game->nextPlayer = 1;
}
else
{
game->board[row * game->cols + col] = '2';
game->board[row * game->cols + col + 1] = '2';
game->nextPlayer = 1;
}
}
#ifdef DEBUG_PLUGIN
dump_board(game);
printf("***PLUGIN: Calling player_can_move\n");
#endif
/* Check if this move just won the game */
if (!player_can_move(game, game->nextPlayer))
{
game->nextPlayer = GetPlayerNumber(igame, iplayer);
game->status = PLUGIN_GAME_STATUS_OVER_WINNER;
}
return 0;
}
int GetGameStatus(void* igame)
{
struct Game* game = igame;
return game->status;
}
void DestroyGame(void* igame)
{
struct Game* game = igame;
free(game->board);
free(game);
}