inherit "/std/room/furniture/basic";
class co_ord {
int x;
int y;
}
private mixed* _board;
private string _player_white;
private string _player_black;
private nosave class co_ord* _offsets;
private class co_ord _last_move;
private int _move_no;
#define BOARD_SIZE 8
#define BOARD_BLACK "X"
#define BOARD_WHITE "O"
#define BOARD_TAG "reversi table"
void reset_board();
void setup() {
set_name("table");
set_short("reversi table");
add_adjective("reversi");
set_long("A lovely green coloured board table divided into 8 by 8 grid "
"with small black lines. There are two curved indentations on "
"each side of the table filled with bi coloured discs.\n");
set_weight(100);
//
// Setup the offsets for checking.
//
_offsets = allocate(8, (: new(class co_ord) :));
_offsets[0]->x = 1;
_offsets[0]->y = 1;
_offsets[1]->x = 0;
_offsets[1]->y = 1;
_offsets[2]->x = -1;
_offsets[2]->y = 1;
_offsets[3]->x = -1;
_offsets[3]->y = 0;
_offsets[4]->x = -1;
_offsets[4]->y = -1;
_offsets[5]->x = 0;
_offsets[5]->y = -1;
_offsets[6]->x = 1;
_offsets[6]->y = -1;
_offsets[7]->x = 1;
_offsets[7]->y = 0;
reset_board();
} /* setup() */
/**
* This method sets up the board.
*/
void reset_board() {
int i;
_board = allocate(BOARD_SIZE);
for (i = 0; i < sizeof(_board); i++) {
_board[i] = allocate(BOARD_SIZE);
}
_board[3][3] = BOARD_BLACK;
_board[4][4] = BOARD_BLACK;
_board[3][4] = BOARD_WHITE;
_board[4][3] = BOARD_WHITE;
_move_no = 0;
} /* reset_board() */
private string query_line() {
int i;
string str;
str = " %^B_GREEN%^+";
for (i = 0; i < BOARD_SIZE; i++) {
str += "---+";
}
return str + "%^RESET%^\n";;
} /* query_line() */
/**
* This method returns the person who plays the white player.
* @return the person who plays the white player
*/
string query_white_player() {
if (_player_white) {
return _player_white;
}
return "unknown";
} /* query_white_player() */
/**
* This method returns the person who plays the black player.
* @return the person who plays the black player
*/
string query_black_player() {
if (_player_black) {
return _player_black;
}
return "unknown";
} /* query_black_player() */
/**
* This method returns whose move it is.
*/
string query_whose_move() {
if (_move_no % 2) {
return BOARD_BLACK;
} else {
return BOARD_WHITE;
}
} /* query_whose_move() */
/**
* This method returns the name of the player whose move it is.
*/
string query_player_name_move() {
if (query_whose_move() == BOARD_BLACK) {
return query_black_player();
}
return query_white_player();
} /* query_player_name_move() */
/**
* This method returns the board stuff.
*/
mixed* query_board() {
return _board;
} /* query_board() */
/**
* This method shows the current state of the board.
*/
string query_board_string() {
int x;
int y;
int no_white;
int no_black;
string board_str;
board_str = "";
if (query_whose_move() == BOARD_BLACK) {
board_str += "%^BOLD%^Blacks move.%^RESET%^\n";
} else {
board_str += "%^BOLD%^Whites move.%^RESET%^\n";
}
board_str += "White player (O): " + query_white_player() + "\n";
board_str += "Black player (X): " + query_black_player() + "\n";
board_str += " ";
for (x = 1; x <= sizeof(_board); x++) {
board_str += " " + x + " ";
}
board_str += "\n";
board_str += query_line();
for (x = 0; x < sizeof(_board); x++) {
board_str += sprintf("%c %%^B_GREEN%%^|", x + 'A');
for (y = 0; y < sizeof(_board[x]); y++) {
if (!_board[x][y]) {
board_str += " |";
} else {
if (_board[x][y] == BOARD_BLACK) {
board_str += " %^B_BLACK%^X%^B_GREEN%^ ";
no_black++;
} else {
board_str += " %^B_WHITE%^O%^B_GREEN%^ ";
no_white++;
}
board_str += "|";
}
}
board_str += "%^RESET%^\n";
board_str += query_line();
}
board_str += "\nWhite has " + no_white +" pieces and black has " +
no_black + " pieces.\n";
return board_str;
} /* query_board_string() */
/** @ignore yes */
string long(string str, int dark) {
if (dark) {
return ::long() +
"It is too dark to make out the pieces on the board.\n";
}
return ::long() + query_board_string();
} /* long() */
/**
* This method deciphers some co-ordinates for us.
*/
class co_ord query_co_ords(string str) {
class co_ord bing;
str = lower_case(str);
if (strlen(str) != 2) {
return 0;
}
bing = new(class co_ord);
if (str[0] >= '1' && str[0] <= '8') {
bing->y = str[0] - '1';
if (str[1] >= 'a' && str[1] <= 'h') {
bing->x = str[1] - 'a';
} else {
return 0;
}
} else if (str[0] >= 'a' && str[0] <= 'h') {
bing->x = str[0] - 'a';
if (str[1] >= '1' && str[1] <= '8') {
bing->y = str[1] - '1';
} else {
return 0;
}
}
return bing;
} /* query_co_ords() */
/**
* This method checks to see if the specified move is valid for the specified
* colour.
* @param co_ord the co-ordinate
* @param colour the colour they are
* @return 1 if valid, 0 if not
*/
int is_valid_move(class co_ord bing, string colour) {
int x;
int y;
string other_colour;
class co_ord off;
if (_board[bing->x][bing->y]) {
return 0;
}
if (colour == BOARD_BLACK) {
other_colour = BOARD_WHITE;
} else {
other_colour = BOARD_BLACK;
}
//
// Check stuff.
//
foreach (off in _offsets) {
if (bing->x + off->x < BOARD_SIZE && bing->y + off->y < BOARD_SIZE &&
bing->x + off->x >= 0 && bing->y + off->y >= 0 &&
_board[bing->x + off->x][bing->y + off->y] == other_colour) {
printf("Checking %O -- %O\n", off, bing);
for (x = bing->x + off->x * 2, y = bing->y + off->y * 2;
x < BOARD_SIZE && y < BOARD_SIZE && x >= 0 && y >= 0;
x += off->x, y += off->y) {
if (_board[x][y] == colour) {
return 1;
} else if (!_board[x][y]) {
break;
}
}
}
}
return 0;
} /* is_valid_move() */
/**
* This method makes the move.
* @param co_ord the co-ordinate
* @param colour the colour they are
* @return 1 if valid, 0 if not
*/
int make_move(class co_ord bing, string colour) {
int x;
int y;
string other_colour;
class co_ord off;
if (!is_valid_move(bing, colour)) {
return 0;
}
if (colour == BOARD_BLACK) {
other_colour = BOARD_WHITE;
} else {
other_colour = BOARD_BLACK;
}
//
// Check stuff.
//
_board[bing->x][bing->y] = colour;
foreach (off in _offsets) {
if (bing->x + off->x < BOARD_SIZE && bing->y + off->y < BOARD_SIZE &&
bing->x + off->x >= 0 && bing->y + off->y >= 0 &&
_board[bing->x + off->x][bing->y + off->y] == other_colour) {
for (x = bing->x + off->x * 2, y = bing->y + off->y * 2;
x < BOARD_SIZE && y < BOARD_SIZE && x >= 0 && y >= 0;
x += off->x, y += off->y) {
if (_board[x][y] == colour) {
for (x = bing->x + off->x, y = bing->y + off->y;
;
x += off->x, y += off->y) {
if (_board[x][y] == colour) {
break;
}
_board[x][y] = colour;
}
break;
}
}
}
}
_last_move = bing;
_move_no++;
//
// Send the board data to the other person.
//
if (query_whose_move() == BOARD_BLACK) {
if (find_player(query_black_player())) {
tell_object(find_player(query_black_player()),
query_board_string());
}
} else {
if (find_player(query_white_player())) {
tell_object(find_player(query_white_player()),
query_board_string());
}
}
return 1;
} /* make_move() */
/**
* This method does the actual move.
* @param str the co-ordinate
* @return 1 on success, 0 failure
*/
int do_move(string str) {
class co_ord bing;
if (query_player_name_move() != this_player()->query_name()) {
add_failed_mess("It is not your move!\n");
return 0;
}
bing = query_co_ords(str);
if (!bing) {
add_failed_mess("Sorry, " + str + " is not a valid move.\n");
return 0;
}
if (!is_valid_move(bing, query_whose_move())) {
add_failed_mess("Sorry, " + str + " is not a valid move.\n");
return 0;
}
make_move(bing, query_whose_move());
add_succeeded_mess("$N make$s an exciting move on $I.\n",
({ this_object() }));
return 1;
} /* do_move() */
/**
* This method allows you to join the game.
* @param colour the colour to join as
* @return 1 on success, 0 failure
*/
int do_join(string colour) {
string play;
string name;
if (colour == BOARD_BLACK) {
play = query_black_player();
} else {
play = query_white_player();
}
if (find_player(play) && find_player(play) == environment()) {
add_failed_mess("Someone is already playing that colour.\n");
return 0;
}
if (colour == BOARD_BLACK) {
_player_black = this_player()->query_name();
name = "black";
} else {
_player_white = this_player()->query_name();
name = "white";
}
add_succeeded_mess("$N join$s the game on $I as " + name + ".\n",
({ this_object() }));
return 1;
} /* do_join() */
/**
* This method starts a game.
* @return 1 on success, 0 failure
*/
int do_start() {
if (query_black_player() == this_player()->query_name() ||
query_white_player() == this_player()->query_name()) {
reset_board();
add_succeeded_mess("$N start$s a new game on $I.\n",
({ this_object() }));
return 0;
}
add_failed_mess("Only the players of the game can start a new one.\n");
return 0;
} /* do_start() */
/**
* This method allows you to resign from the game.
* @return 1 on success, 0 on failure
*/
int do_resign() {
if (query_black_player() == this_player()->query_name()) {
_player_black = 0;
add_succeeded_mess("$N resign$s as the black player on $I.\n",
({ this_object() }));
return 1;
}
if (query_white_player() == this_player()->query_name()) {
_player_white = 0;
add_succeeded_mess("$N resign$s as the white player on $I.\n",
({ this_object() }));
return 1;
}
return 0;
} /* do_resign() */
/**
* This method views the current state of the board.
*/
int do_view() {
int dark;
dark = this_player()->check_dark(environment()->query_light());
if (dark) {
add_failed_mess("It is too dark to see the board.\n");
return 0;
}
write(query_board_string());
return 1;
} /* do_view() */
/** @ignore yes */
void init() {
::init();
add_command("join", "game as {black|white}",
(: do_join($4[0] == "black"?BOARD_BLACK:BOARD_WHITE) :));
add_command("resign", "from game",
(: do_resign() :));
add_command("move", "<string'pos'>", (: do_move($4[0]) :) );
add_command("start", "new game", (: do_start() :) );
add_command("view", "", (: do_view() :));
} /* init() */
/** @ignore yes */
mapping query_dynamic_auto_load() {
mapping map;
map = ::query_dynamic_auto_load();
add_auto_load_value(map, BOARD_TAG, "board", _board);
add_auto_load_value(map, BOARD_TAG, "move no", _move_no);
add_auto_load_value(map, BOARD_TAG, "white", _player_white);
add_auto_load_value(map, BOARD_TAG, "black", _player_black);
add_auto_load_value(map, BOARD_TAG, "last move", _last_move);
return map;
} /* query_dynamic_auto_load() */
/** @ignore yes */
void init_dynamic_arg(mapping map, object player) {
::init_dynamic_arg(map, player);
_board = query_auto_load_value(map, BOARD_TAG, "board");
_move_no = query_auto_load_value(map, BOARD_TAG, "move no");
_player_white = query_auto_load_value(map, BOARD_TAG, "white");
_player_black = query_auto_load_value(map, BOARD_TAG, "black");
_last_move = query_auto_load_value(map, BOARD_TAG, "last move");
if (!_board) {
reset_board();
}
} /* init_dynamic_arg() */