You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

391 lines
7.8 KiB

#pragma once
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
typedef unsigned int idx_t;
/*
* Potential values for the pieces on the board.
*/
enum FieldType
{
EMPTY,
PLAYER_1,
PLAYER_2,
OUT_OF_BOUNDS
};
/*
* direction flags are used by the algorithms below to validate
* and perform moves.
*/
enum direction {
LEFT,
RIGHT,
UP,
DOWN,
UP_LEFT,
UP_RIGHT,
DOWN_LEFT,
DOWN_RIGHT
};
class OthelloBoard
{
protected:
idx_t _rows; // no. of rows
idx_t _cols; // no. of columns
bool _last_player_skipped; // remember if last player skipped
bool _both_skipped; // termination condition
FieldType *_field; // array storing the types of stones set on the board
FieldType _player; // whose turn is it?
/* make object non-assignable */
OthelloBoard& operator=(OthelloBoard&)
{ return *this; }
private:
/*
* Depending on a given direction, we need to setup up to four
* variables in different types of algorithms. This convenience
* function does so.
*/
void setup_directions(unsigned &row_idx, unsigned &col_idx,
int &inc_row, int &inc_col, direction const dir) const;
/*
* Check if a position is reachable from a given direction.
*
* Reachability means, that from the target position given by
* (row, col) there are only non-empty stones of type != _player until
* we find a stone of type _player.
*/
bool position_reachable(unsigned row, unsigned col,
direction const dir) const;
/*
* Really make a move
*/
void make_move(unsigned row, unsigned col,
direction dir);
/*
* Validate correctness of a move.
*
* \param row, col coordinates to move to
*
* \return true if move is valid
*/
bool validate(unsigned row, unsigned col, bool silent=false) const;
public:
/*
* Get the number of rows.
*/
unsigned rows() const { return _rows; }
/*
* Get the number of columns
*/
unsigned columns() const { return _cols; }
/*
* Construct a default OthelloBoard
*/
OthelloBoard(unsigned r = 8,
unsigned c = 8)
: _rows(r),
_cols(c),
_last_player_skipped(false),
_both_skipped(false),
_field(0),
_player(PLAYER_1)
{
_field = new FieldType[rows()*columns()];
for (unsigned idx = 0; idx < rows() * columns(); ++idx)
_field[idx] = EMPTY;
set(rows()/2, columns()/2, PLAYER_1);
set(rows()/2+1, columns()/2+1, PLAYER_1);
set(rows()/2, columns()/2 + 1, PLAYER_2);
set(rows()/2+1, columns()/2, PLAYER_2);
}
/*
* Copy constructor.
*
* XXX: If you plan to use it, go and implement it!
*/
OthelloBoard(const OthelloBoard& copy);
/* Destructor */
virtual ~OthelloBoard() { delete [] _field; }
/* Get current player type */
FieldType player() const { return _player; }
/* Set current player */
void player(int no) {
switch(no) {
case 0: _player = PLAYER_1; break;
case 1: _player = PLAYER_2; break;
default: assert(false); break;
}
}
/*
* Dump the current board state to a file
*/
bool dump_to_fd(int fd, bool fancy = false) const
{
FILE *file = fdopen(dup(fd), "w");
if (!file) return false;
// print head line
if (fancy)
::fprintf(file, " %c ", type_to_str(_player));
else
::fprintf(file, "%c", type_to_str(_player));
if (fancy) {
for (unsigned c = 0; c < columns(); ++c)
::fprintf(file, "%d ", c+1);
::fprintf(file,"\n");
}
for (unsigned r = 0; r < rows(); ++r) {
if (fancy) ::fprintf(file, "%2d ", r+1);
for (unsigned c = 0; c < columns(); ++c) {
if (fancy)
::fprintf(file, "%s ", type_to_str_fancy(get(r+1, c+1)));
else {
::fprintf(file, "%c", type_to_str(get(r+1,c+1)));
}
}
if (fancy) ::fprintf(file,"\n");
}
fclose(file);
return true;
}
/* Special case: dump to stdout */
bool dump_to_stdio() const { return dump_to_fd(STDOUT_FILENO, true); }
/*
* Get element at location.
*
* Location coordinates are
* row with 0 < row <= rows()
* col with 0 < col <= columns()
*
* Otherwise, returns OUT_OF_BOUNDS field type.
*/
FieldType get(unsigned row, unsigned col) const
{
if ((row <= 0) ||
(col <= 0) ||
(row > rows()) ||
(col > columns()))
return OUT_OF_BOUNDS;
return _field[(row-1)*columns() + col - 1];
}
/*
* Set element at certain location.
*
* Location coordinates are
* row with 0 < row <= rows()
* col with 0 < col <= columns()
*/
void set(unsigned row, unsigned col, FieldType type)
{
assert(row > 0);
assert(row <= rows());
assert(col > 0);
assert(col <= columns());
_field[(row-1)*columns() + col - 1] = type;
}
/*
* Perform a move.
*
* First, validates the move. Then performs all necessary
* field type conversions.
*/
bool move(unsigned row, unsigned col);
/*
* Count the number of fields that contain a stone
* of the given type.
*/
unsigned count_of_type(FieldType t) const
{
unsigned res = 0;
for (unsigned i = 0; i < rows() * columns(); ++i)
if (_field[i] == t) res += 1;
return res;
}
void skipped()
{
if (_last_player_skipped)
_both_skipped = true;
else
_last_player_skipped = true;
}
/*
* Determine whether the game is done.
*/
bool finished() const
{
unsigned c1 = count_of_type(PLAYER_1);
unsigned c2 = count_of_type(PLAYER_2);
return ( _both_skipped ||
(c1 == 0) ||
(c2 == 0) ||
(c1 + c2 >= 64)
);
}
/*
* Transform field type into fancy print version (for stdio).
*/
char const *type_to_str_fancy(FieldType t) const
{
switch (t) {
case EMPTY: return "\033[34;1m.\033[0m";
case PLAYER_1: return "\033[31mO\033[0m";
case PLAYER_2: return "\033[33mX\033[0m";
case OUT_OF_BOUNDS: return "/";
default: __builtin_unreachable();
}
}
/*
* Transform field type into print version.
*/
char type_to_str(FieldType t) const
{
switch (t) {
case EMPTY: return '.';
case PLAYER_1: return 'O';
case PLAYER_2: return 'X';
case OUT_OF_BOUNDS: return '/';
default: __builtin_unreachable();
}
}
/*
* Check, whether the given player can make any move at the moment.
*/
bool can_move() const
{
for (unsigned r = 1; r <= rows(); ++r)
for (unsigned c = 1; c <= columns(); ++c)
if (validate(r, c, true))
return true;
return false;
}
/*
* Invalidate all stones belonging to a player.
*
* The engine uses this as a penalty for making more than 3
* illegal moves in a row.
*/
void invalidate_stones(FieldType t)
{
for (unsigned idx = 0; idx < rows() * columns(); ++idx)
if (_field[idx] == t)
_field[idx] = EMPTY;
}
};
struct OthelloMove
{
idx_t row;
idx_t col;
OthelloMove(idx_t r=0, idx_t c=0)
: row(r), col(c)
{ }
};
/***************************************
* MCP adaptor definitions & functions *
***************************************/
typedef OthelloBoard game_state;
typedef OthelloMove game_move;
/**
* Game state is serialized into a human-readable string.
*/
bool serialize_state(int fd, const game_state *state);
/**
* Game state is parsed from a string written by serialize_state.
*/
bool deserialize_state(int fd, game_state *state);
bool serialize_move (int fd, const game_move *move);
bool deserialize_move(int fd, game_move *move);
/**
* Generate the initial board.
*/
void initialize_state(game_state *state, char const *init);
/**
* Returns true, if this is a final game state.
*/
bool is_final_state(game_state const *state);
/**
* Tries to apply a move to the given state. Returns false, if the
* move was invalid.
*/
bool apply_move(game_state *state,
const game_move *move);