Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 6f5f25b995
Fetching contributors…

Cannot retrieve contributors at this time

276 lines (230 sloc) 9.452 kB
/*****************************************************************************
** COMP3130 GROUP PROJECTS IN COMPUTER SCIENCE, SEMESTER 1, 2012
** Copyright (c) 2012, Stephen Gould
** All rights reserved.
**
******************************************************************************
** FILENAME: server.cpp
** AUTHOR(S): Stephen Gould <stephen.gould@anu.edu.au>
**
*****************************************************************************/
#include <cstdlib>
#include <cassert>
#include <cstdio>
#include <ctime>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <map>
#include "board.h"
#include "comms.h"
#include "leader.h"
using namespace std;
// Player --------------------------------------------------------------------
class Player {
public:
int client_socket; //!< socket for communicating with this player
string name; //!< player's name
double time_remaining; //!< time remaining for this player
public:
Player() : client_socket(-1), time_remaining(0.0) { /* do nothing */ }
~Player() {
// close the client socket
if (client_socket != -1) {
close(client_socket);
}
}
//! clear existing data
void clear() { client_socket = -1; name.clear(); time_remaining = 0.0; }
//! returns true if a player has successfully connected
bool connected() const { return (client_socket >= 0); }
//! makes a new connection to a client
void connect(int sock, const string& n, double t) {
client_socket = sock; name = n; time_remaining = t;
}
};
// comms ---------------------------------------------------------------------
//! Waits for two players, one WHITE and one BLACK, to join the game.
//!
void waitForPlayers(Player players[2], int server_socket, double time_limit)
{
cerr << "SERVER: waiting for clients to connect (press Ctrl-C to quit)..." << endl;
players[0].clear();
players[1].clear();
while ((players[0].client_socket == -1) || (players[1].client_socket == -1)) {
struct sockaddr_in client;
int size = sizeof(sockaddr_in);
int socket = accept(server_socket, (struct sockaddr *)&client, (socklen_t*)&size);
if (socket < 0) {
cerr << "SERVER: error listening for client (accept)" << endl;
}
cerr << "SERVER: accepted a connection from " << inet_ntoa(client.sin_addr)
<< " on port " << client.sin_port << endl;
ConnectMessage message;
message.receive(socket);
// check that a player hasn't already connected for this side
if (!players[message.side].connected()) {
players[message.side].connect(socket, message.name, time_limit);
cerr << "SERVER: " << message.name << " connected as "
<< ((message.side == 0) ? "WHITE" : "BLACK") << endl;
// set timeout for receive message
struct timeval timeout;
timeout.tv_sec = (int)time_limit;
timeout.tv_usec = 0;
if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)) < 0) {
cerr << "SERVER: error initializing server socket (setsockopt)" << endl;
exit(-1);
}
} else {
// a player for this side has already connected so simply close the connection
cerr << "SERVER: error " << message.name << " attempting to connect as "
<< ((message.side == 0) ? "WHITE" : "BLACK") << " but " <<
players[message.side].name << " is already connected" << endl;
close(socket);
}
}
}
// main ----------------------------------------------------------------------
int main(int argc, char *argv[])
{
int server_socket = -1;
uint16_t port = DEFAULT_PORT;
double time_limit = 150.0; // 5 minute game limit
const char *board_file = NULL;
// process command line arguments
if (argc > 5) {
cerr << "USAGE: " << argv[0] << " [random seed] [port] [time limit] [board]" << endl;
return 0;
}
if (argc > 1) srand48(atoi(argv[1])); // seed random number generator
if (argc > 2) port = atoi(argv[2]); // set server port
if (argc > 3) time_limit = 0.5 * atof(argv[3]); // set game time
if (argc > 4) board_file = argv[4]; // initial board
// initialize server communications
server_socket = initServerComms(port);
cout << "SERVER: listening on port " << port << endl;
listen(server_socket, 5);
signal(SIGPIPE, SIG_IGN);
// keep playing games until server dies
LeaderBoard leaderBoard;
while (1) {
// wait for clients to connect
Player players[2];
waitForPlayers(players, server_socket, time_limit);
// initialize boad
Board board;
if (board_file != NULL) {
board.read(board_file);
}
board.visualize();
// start game
BoardState player = WHITE;
BoardState opponent = BLACK;
BoardState forfeit = EMPTY;
ServerMessage messageOut;
ClientMessage messageIn;
while (1) {
const int side = (player == WHITE) ? 0 : 1;
const set<pair<int, int> > moves = board.legalMoves(player);
// prepare server message
if (moves.empty()) {
if (board.legalMoves(opponent).empty()) {
break;
}
messageOut.status = NO_MOVE;
} else {
messageOut.status = GIVE_MOVE;
}
messageOut.timeRemaining = players[side].time_remaining;
messageOut.board = board;
// send the message
if (!messageOut.send(players[side].client_socket)) {
cerr << "SERVER: player " << getString(player) << " send error" << endl;
forfeit = player;
break;
}
// wait for response
struct timeval startTime, endTime;
gettimeofday(&startTime, NULL);
if (!messageIn.receive(players[side].client_socket)) {
cerr << "SERVER: player " << getString(player) << " receive error" << endl;
forfeit = player;
break;
}
gettimeofday(&endTime, NULL);
const double duration = endTime.tv_sec - startTime.tv_sec +
1.0e-6 * (endTime.tv_usec - startTime.tv_usec);
players[side].time_remaining -= duration;
// check if player responded within the time limit
if (players[side].time_remaining < 0.0) {
// player forfeits
cerr << "SERVER: player " << getString(player) << " ran out of time" << endl;
forfeit = player;
break;
}
// make the move
if (!moves.empty()) {
// check player made a legal move
if (moves.find(make_pair(messageIn.x, messageIn.y)) == moves.end()) {
// player forfeits
cerr << "SERVER: player " << getString(player) << " made an illegal move" << endl;
forfeit = player;
break;
} else {
board.makeMove(messageIn.x, messageIn.y, player);
}
}
// show the board state
board.visualize();
// switch players
std::swap(player, opponent);
cerr << "SERVER: time remaining " << players[0].time_remaining << " and "
<< players[1].time_remaining << endl;
// update opponents last move
messageOut.x = messageIn.x;
messageOut.y = messageIn.y;
}
// find winner and update leader board
int nWhite = board.cellCount(WHITE);
int nBlack = board.cellCount(BLACK);
if (forfeit == WHITE) {
nWhite = 0;
nBlack = BOARD_SIZE * BOARD_SIZE - 4;
} else if (forfeit == BLACK) {
nWhite = BOARD_SIZE * BOARD_SIZE - 4;
nBlack = 0;
}
if (nWhite == nBlack) {
messageOut.winner = EMPTY;
cout << "TIE";
} else if (nWhite < nBlack) {
messageOut.winner = BLACK;
cout << "BLACK wins";
} else {
messageOut.winner = WHITE;
cout << "WHITE wins";
}
cout << " (" << nWhite << " / " << nBlack << ")\n";
leaderBoard.update(players[0].name, players[1].name, nWhite, nBlack);
leaderBoard.visualize();
// send end of game message to clients
messageOut.status = GAME_END;
messageOut.board = board;
messageOut.timeRemaining = players[0].time_remaining;
if (!messageOut.send(players[0].client_socket)) {
cerr << "SERVER: error sending end of game message to WHITE" << endl;
}
close(players[0].client_socket);
messageOut.timeRemaining = players[1].time_remaining;
if (!messageOut.send(players[1].client_socket)) {
cerr << "SERVER: error sending end of game message to BLACK" << endl;
}
close(players[1].client_socket);
}
// close server socket
close(server_socket);
cout << "SERVER: closed communications" << endl;
return 0;
}
Jump to Line
Something went wrong with that request. Please try again.