#Makefile for Stratego
#Use this to build with graphics
-#LIBRARIES = -lSDL -lGL -lpthread
+LIBRARIES = -lSDL -lGL -lpthread
#Use this to build without graphics
-LIBRARIES = -lpthread
+#LIBRARIES = -lpthread
#Use this on Mac OSX (Thanks spartan)
#LIBRARIES = -I/opt/local/include/ -L/opt/local/lib/ -lSDL -framework OpenGL -lpthread
CPP = g++ -Wall -pedantic -g
-OBJ = main.o controller.o network_controller.o ai_controller.o human_controller.o program.o thread_util.o stratego.o graphics.o game.o
+OBJ = main.o controller.o network_controller.o ai_controller.o human_controller.o program.o network.o thread_util.o stratego.o graphics.o game.o
BIN = stratego
virtual MovementResult QuerySetup(const char * opponentName,std::string setup[]);
virtual MovementResult QueryMove(std::string & buffer);
- virtual void Message(const char * message) {Program::SendMessage(message);}
+ virtual void Message(const char * message)
+ {
+ //fprintf(stderr, "Sending message \"%s\" to AI program...\n", message);
+ Program::SendMessage(message);
+ }
virtual void Pause() {Program::Pause();} //Hack wrapper
virtual void Continue() {Program::Continue();} //Hack wrapper
Game* Game::theGame = NULL;
bool Game::gameCreated = false;
-Game::Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard, double newTimeoutTime, bool server, bool client) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard), timeoutTime(newTimeoutTime)
+Game::Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard, double newTimeoutTime) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard), timeoutTime(newTimeoutTime)
{
gameCreated = false;
if (gameCreated)
#endif //BUILD_GRAPHICS
- if (client)
- {
- red = new Client(Piece::RED, "server", redPath); //TODO: Retrieve server address
- }
- else
- {
- assert(redPath != NULL);
- if (strcmp(redPath, "human") == 0)
- red = new Human_Controller(Piece::RED, graphicsEnabled);
- else
- red = new AI_Controller(Piece::RED, redPath, timeoutTime);
- }
-
-
- if (server)
- {
- blue = new Server(Piece::BLUE, "client");
- }
- else
+
+ MakeControllers(redPath, bluePath);
+
+ if (red == NULL || blue == NULL)
{
- assert(bluePath != NULL);
- if (strcmp(bluePath, "human") == 0)
- blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
+ fprintf(stderr, "Game::Game - Error creating controller: ");
+ if (red == NULL)
+ {
+ if (blue == NULL)
+ fprintf(stderr, " BOTH! (red: \"%s\", blue: \"%s\"\n", redPath, bluePath);
+ else
+ fprintf(stderr, " RED! (red: \"%s\")\n", redPath);
+ }
else
- blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
+ fprintf(stderr, "BLUE! (blue: \"%s\")\n", bluePath);
+ exit(EXIT_FAILURE);
}
-
-
+ logMessage("Game initialised.\n");
}
Game::Game(const char * fromFile, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard, double newTimeoutTime) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard), timeoutTime(newTimeoutTime)
-
+ logMessage("Messaging red with \"START\"\n");
red->Message("START");
return buffer.size();
}
+/**
+ * Creates Controller baseds off strings. Takes into account controllers other than AI_Controller.
+ * @param redPath - Either the path to an AI_Controller compatable executable, or one of %human or %network or %network:[IP_ADDRESS]
+ * @param bluePath - Ditto
+ * Sets this->red to a controller using redPath, and this->blue to a controller using bluePath
+ * TODO: Make nicer (this function should be ~half the length)
+ */
+void Game::MakeControllers(const char * redPath, const char * bluePath)
+{
+ Network * redNetwork = NULL;
+ Network * blueNetwork = NULL;
+
+ if (redPath[0] == '@')
+ {
+ if (strcmp(redPath, "@human") == 0)
+ red = new Human_Controller(Piece::RED, graphicsEnabled);
+ else
+ {
+ const char * network = strstr(redPath, "@network");
+ if (network == NULL)
+ {
+ red = NULL;
+ return;
+ }
+ network = strstr(network, ":");
+
+ if (network == NULL)
+ {
+ logMessage("Creating server for red AI... ");
+ redNetwork = new Server();
+ logMessage("Successful!\n");
+
+ }
+ else
+ {
+ network = (const char*)(network+1);
+ logMessage("Creating client for red AI... ");
+ redNetwork = new Client(network);
+ logMessage("Connected to address %s\n", network);
+ }
+
+ logMessage(" (Red's responses will be received over the connection)\n");
+ red = new NetworkReceiver(Piece::RED, redNetwork);
+ }
+ }
+ else
+ red = new AI_Controller(Piece::RED, redPath, timeoutTime);
+
+ if (bluePath[0] == '@')
+ {
+ if (strcmp(bluePath, "@human") == 0)
+ blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
+ else
+ {
+ const char * network = strstr(bluePath, "@network");
+ if (network == NULL)
+ {
+ blue = NULL;
+ return;
+ }
+ network = strstr(network, ":");
+
+ if (network == NULL)
+ {
+ logMessage("Creating server for blue AI... ");
+ blueNetwork = new Server();
+ logMessage("Successful!\n");
+
+ }
+ else
+ {
+ network = (const char*)(network+1);
+ logMessage("Creating client for blue AI... ");
+ blueNetwork = new Client(network);
+ logMessage("Connected to address %s\n", network);
+ }
+ logMessage(" (Blue's responses will be received over the connection)\n");
+ blue = new NetworkReceiver(Piece::BLUE, blueNetwork);
+ }
+ }
+ else
+ blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
+
+ if (redNetwork != NULL)
+ {
+
+ blue = new NetworkSender(Piece::BLUE,blue, redNetwork);
+ logMessage(" (Blue's responses will be copied over the connection)\n");
+ }
+ if (blueNetwork != NULL)
+ {
+
+ red = new NetworkSender(Piece::RED, red, blueNetwork);
+ logMessage(" (Red's responses will be copied over the connection)\n");
+ }
+
+}
class Game
{
public:
- Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false, double newTimeoutTime = 2.0, bool server=false, bool client=false);
+ Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false, double newTimeoutTime = 2.0);
Game(const char * fromFile, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false, double newTimeoutTime = 2.0);
virtual ~Game();
static Game * theGame;
static int Tokenise(std::vector<std::string> & buffer, std::string & str, char split = ' '); //Helper - Split a string into tokens
+
+ private:
+ void MakeControllers(const char * redPath, const char * bluePath); //Create a controller based off a path
public:
int logMessage(const char * format, ...);
FILE * GetLogFile() const {return log;}
-//#define BUILD_GRAPHICS
+#define BUILD_GRAPHICS
#ifdef BUILD_GRAPHICS
#ifndef GRAPHICS_H
{
char * red = NULL; char * blue = NULL; double stallTime = 0.0; bool graphics = false; bool allowIllegal = false; FILE * log = NULL;
Piece::Colour reveal = Piece::BOTH; char * inputFile = NULL; int maxTurns = 5000; bool printBoard = false; double timeoutTime = 2.0;
- bool server = false; bool client = false;
+
for (int ii=1; ii < argc; ++ii)
{
if (argv[ii][0] == '-')
system("less manual.txt");
exit(EXIT_SUCCESS);
}
- else if (strcmp(argv[ii]+2, "server") == 0)
- {
- if (client == true)
- {
- fprintf(stderr, "ARGUMENT_ERROR - Can't be both a server and a client!\n");
- exit(EXIT_FAILURE);
- }
- server = true;
-
- }
- else if (strcmp(argv[ii]+2, "client") == 0)
- {
- if (server == true)
- {
- fprintf(stderr, "ARGUMENT_ERROR - Can't be both a server and a client!\n");
- exit(EXIT_FAILURE);
- }
- client = true;
-
- }
else
{
fprintf(stderr, "ARGUMENT_ERROR - Unrecognised switch \"%s\"...\n", argv[ii]);
if (inputFile == NULL)
{
- if (server)
+ if (red == NULL || blue == NULL) //Not enough players
{
- if (red != NULL && blue != NULL)
- {
- fprintf(stderr, "ARGUMENT_ERROR - When using the --server switch, only supply ONE (1) player.\n");
- exit(EXIT_FAILURE);
- }
- }
- else if (red == NULL || blue == NULL) //Not enough players
- {
- if (client)
- fprintf(stderr, "ARGUMENT_ERROR - When using the --client switch, supply an IP for the Red player.\n");
- else
- fprintf(stderr, "ARGUMENT_ERROR - Did not recieve enough players (did you mean to use the -f switch?)\n");
+ fprintf(stderr, "ARGUMENT_ERROR - Did not recieve enough players (did you mean to use the -f switch?)\n");
exit(EXIT_FAILURE);
}
-
- if (client)
- {
- blue = red; red = NULL;
- }
- Game::theGame = new Game(red,blue, graphics, stallTime, allowIllegal,log, reveal,maxTurns, printBoard, timeoutTime, server, client);
+ Game::theGame = new Game(red,blue, graphics, stallTime, allowIllegal,log, reveal,maxTurns, printBoard, timeoutTime);
}
else
{
- if (server || client)
- {
- fprintf(stderr, "ARGUMENT_ERROR - The -f switch is incompatable with the --server or --client switches!\n");
- exit(EXIT_FAILURE);
- }
Game::theGame = new Game(inputFile, graphics, stallTime, allowIllegal,log, reveal,maxTurns, printBoard, timeoutTime);
}
--- /dev/null
+#include "network.h"
+#include <stdarg.h>
+
+using namespace std;
+
+Network::Network(int newPort) : sfd(-1), port(newPort), file(NULL)
+{
+ sfd = socket(PF_INET, SOCK_STREAM, 0);
+ if (sfd < 0)
+ {
+ perror("Network::Network - Error creating TCP socket");
+ exit(EXIT_FAILURE);
+ }
+}
+
+Network::~Network()
+{
+ if (Valid())
+ {
+ if (shutdown(sfd, SHUT_RDWR) == -1)
+ {
+ perror("Network::~Network - shutting down socket... ");
+ close(sfd);
+ sfd = -1;
+ }
+ }
+ close(sfd);
+}
+
+Server::Server(int newPort) : Network(newPort)
+{
+ struct sockaddr_in name;
+// char buf[1024];
+// int cc;
+
+
+ name.sin_family = AF_INET;
+ name.sin_addr.s_addr = htonl(INADDR_ANY);
+ name.sin_port = htons(port);
+
+ if (bind( sfd, (struct sockaddr *) &name, sizeof(name) ) < 0)
+ {
+ perror("Server::Server - Error binding socket");
+ close(sfd); sfd = -1;
+ exit(EXIT_FAILURE);
+ }
+
+ if (listen(sfd,1) < 0)
+ {
+ perror("Server::Server - Error listening on socket");
+ close(sfd); sfd = -1;
+ exit(EXIT_FAILURE);
+ }
+ int psd = accept(sfd, 0, 0);
+ close(sfd);
+ sfd = psd;
+ if (sfd < 0)
+ {
+ perror("Server::Server - Error accepting connection");
+ close(sfd); sfd = -1;
+ exit(EXIT_FAILURE);
+ }
+
+
+
+ /*
+ for(;;)
+ {
+ cc=recv(sfd,buf,sizeof(buf), 0) ;
+ if (cc == 0) exit (0);
+ buf[cc] = '\0';
+ printf("message received: %s\n", buf);
+ }
+ */
+}
+
+Client::Client(const char * serverAddress, int newPort) : Network(newPort)
+{
+ struct sockaddr_in server;
+ struct hostent *hp;
+
+
+ server.sin_family = AF_INET;
+ hp = gethostbyname(serverAddress);
+ bcopy ( hp->h_addr, &(server.sin_addr.s_addr), hp->h_length);
+ server.sin_port = htons(port);
+
+ if (connect(sfd, (struct sockaddr *) &server, sizeof(server)) < 0)
+ {
+ fprintf(stderr, "Client::Client - Error connecting to server at address %s: ", serverAddress);
+ perror("");
+ close(sfd); sfd = -1;
+ exit(EXIT_FAILURE);
+ }
+
+
+
+
+ /*
+ for (;;) {
+ send(sfd, "HI", 2,0 );
+ sleep(2);
+ }
+ */
+}
+
+/**
+ * Sends a message accross the network
+ * WARNING: Always terminates the message with a newline '\n'
+ * @returns true if the message was successfully sent; false if it was not (ie: the network was not connected!)
+ */
+bool Network::SendMessage(const char * print, ...)
+{
+ if (!Valid()) //Is the process running...
+ return false;
+
+ if (file == NULL)
+ {
+ file = fdopen(sfd, "r+");
+ setbuf(file, NULL);
+ }
+
+ va_list ap;
+ va_start(ap, print);
+
+ if (vfprintf(file, print, ap) < 0 || fprintf(file, "\n") < 0)
+ {
+ va_end(ap);
+ return false;
+ }
+ va_end(ap);
+
+ return true;
+}
+
+/**
+ * Retrieves a message from the network, waiting a maximum amount of time
+ * @param buffer - C++ string to store the resultant message in
+ * @param timeout - Maximum amount of time to wait before failure. If timeout <= 0, then GetMessage will wait indefinately.
+ * @returns true if the response was recieved within the specified time, false if it was not, or an EOF was recieved, or the process was not running.
+ */
+bool Network::GetMessage(string & buffer, double timeout)
+{
+ if (!Valid() || timeout == 0)
+ return false;
+
+ if (file == NULL)
+ {
+ file = fdopen(sfd, "r+");
+ setbuf(file, NULL);
+ }
+
+ assert(&buffer != NULL);
+ GetterThread getterThread(file, buffer);
+ assert(&(getterThread.buffer) != NULL);
+
+ TimerThread timerThread(timeout*1000000);
+
+ getterThread.Start();
+ if (timeout > 0)
+ timerThread.Start();
+
+
+ while (!getterThread.Finished())
+ {
+ if (timeout > 0 && timerThread.Finished())
+ {
+ getterThread.Stop();
+ timerThread.Stop();
+ return false;
+ }
+ }
+
+ getterThread.Stop();
+ if (timeout > 0)
+ timerThread.Stop();
+
+
+
+ if (buffer.size() == 1 && buffer[0] == EOF)
+ return false;
+ return true;
+
+
+}
+
--- /dev/null
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <string>
+#include "thread_util.h"
+
+#ifndef NETWORK_H
+#define NETWORK_H
+
+
+class Network
+{
+ public:
+ Network(int newPort = 666);
+ virtual ~Network();
+ bool Valid() const {return sfd != -1;}
+
+ bool SendMessage(const char * print, ...); //Sends a formated message (NOTE: Prints a newline)
+ bool SendMessage(const std::string & buffer) {return SendMessage(buffer.c_str());} //Sends a C++ string message
+ bool GetMessage(std::string & buffer, double timeout=-1); //Retrieves a message, or waits for a timeout (if positive)
+
+ protected:
+ int sfd;
+ int port;
+ FILE * file;
+
+};
+
+class Server : public Network
+{
+ public:
+ Server(int newPort = 4560);
+ virtual ~Server() {}
+
+};
+
+class Client : public Network
+{
+ public:
+ Client(const char * server = "127.0.0.1", int newPort = 4560);
+ virtual ~Client() {}
+};
+
+#endif //NETWORK_H
#include "network_controller.h"
-NetworkController::NetworkController(const Piece::Colour & newColour, const char * newName) : Controller(newColour, newName), sfd(-1)
-{
- //struct protoent * tcp = getprotobyname("tcp");
-
- sfd = socket(PF_INET, SOCK_STREAM, 0);
- if (sfd < 0)
- {
- perror("NetworkController::NetworkController - Error creating TCP socket");
- return;
- }
-}
-
-NetworkController::~NetworkController()
-{
- if (Valid())
- {
- if (shutdown(sfd, SHUT_RDWR) == -1)
- {
- perror("NetworkController::~NetworkController - shutting down socket... ");
- close(sfd);
- sfd = -1;
- }
- }
- close(sfd);
-}
+using namespace std;
-void NetworkController::Message(const char * string)
+MovementResult NetworkSender::QuerySetup(const char * opponentName, string setup[])
{
+ //fprintf(stderr," NetworkSender::QuerySetup... ");
+ MovementResult result = controller->QuerySetup(opponentName, setup);
+ for (int ii=0; ii < 4; ++ii)
+ assert(network->SendMessage("%s",setup[ii].c_str())); //TODO: Proper error check
+
+ //fprintf(stderr,"Done!\n");
+ return result;
}
-Server::Server(const Piece::Colour & newColour, const char * newName) : NetworkController(newColour, newName)
+MovementResult NetworkReceiver::QuerySetup(const char * opponentName, string setup[])
{
- struct sockaddr_in name;
- char buf[1024];
- int cc;
-
-
- name.sin_family = AF_INET;
- name.sin_addr.s_addr = htonl(INADDR_ANY);
- name.sin_port = htons(NetworkController::port);
-
- if (bind( sfd, (struct sockaddr *) &name, sizeof(name) ) < 0)
+ //fprintf(stderr," NetworkReceiver::QuerySetup... ");
+ for (int ii=0; ii < 4; ++ii)
{
- perror("Server::Server - Error binding socket");
- close(sfd); sfd = -1; return;
- }
-
- if (listen(sfd,1) < 0)
- {
- perror("Server::Server - Error listening on socket");
- close(sfd); sfd = -1; return;
- }
- int psd = accept(sfd, 0, 0);
- close(sfd);
- sfd = psd;
- if (sfd < 0)
- {
- perror("Server::Server - Error accepting connection");
- close(sfd); sfd = -1; return;
- }
-
-
- for(;;)
- {
- cc=recv(sfd,buf,sizeof(buf), 0) ;
- if (cc == 0) exit (0);
- buf[cc] = '\0';
- printf("message received: %s\n", buf);
+ assert(network->GetMessage(setup[ii], 20));
}
+ //fprintf(stderr,"Done!\n");
+ return MovementResult::OK;
}
-
-Client::Client(const Piece::Colour & newColour, const char * newName, const char * address) : NetworkController(newColour, newName)
+MovementResult NetworkSender::QueryMove(string & buffer)
{
- struct sockaddr_in server;
- struct hostent *hp;
-
-
- server.sin_family = AF_INET;
- hp = gethostbyname("127.0.0.1");
- bcopy ( hp->h_addr, &(server.sin_addr.s_addr), hp->h_length);
- server.sin_port = htons(NetworkController::port);
-
- if (connect(sfd, (struct sockaddr *) &server, sizeof(server)) < 0)
- {
- perror("Client::Client - Error connecting to server at address %s");
- close(sfd); sfd = -1;
- return;
- }
-
- for (;;) {
- send(sfd, "HI", 2,0 );
- sleep(2);
- }
-
+ MovementResult result = controller->QueryMove(buffer);
+ network->SendMessage("%s", buffer.c_str());
+ return result;
}
-//EOF
-
+MovementResult NetworkReceiver::QueryMove(string & buffer)
+{
+ assert(network->GetMessage(buffer, 20));
+ return MovementResult::OK;
+}
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <netdb.h>
-#include <fcntl.h>
-#include <errno.h>
-
-
#include "controller.h"
+#include "ai_controller.h"
+#include "human_controller.h"
+#include "network.h"
#ifndef NETWORK_CONTROLLER_H
#define NETWORK_CONTROLLER_H
-
class NetworkController : public Controller
{
public:
- NetworkController(const Piece::Colour & newColour, const char * newName = "no-name");
- virtual ~NetworkController();
+ NetworkController(const Piece::Colour & colour, Network * newNetwork) : Controller(colour), network(newNetwork) {}
+ virtual ~NetworkController() {}
- virtual bool Valid() {return sfd != -1;}
-
- virtual void Message(const char * string);
- virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]) {fprintf(stderr, "NetworkController unimplemented!\n"); assert(false);}
- virtual MovementResult QueryMove(std::string & buffer) {fprintf(stderr, "NetworkController unimplemented!\n"); assert(false);}
+ //virtual void Message(const char * message) = 0;
+ virtual bool Valid() const {return network->Valid();}
protected:
- int sfd; int cfd;
- static const int port = 4950;
+ Network * network;
};
-class Server : public NetworkController
+class NetworkSender : public NetworkController
{
public:
- Server(const Piece::Colour & newColour, const char * newName = "no-name");
- virtual ~Server() {}
+ NetworkSender(const Piece::Colour & colour, Controller * newController, Network * newNetwork) : NetworkController(colour, newNetwork), controller(newController) {}
+ virtual ~NetworkSender() {delete controller;}
+
+ virtual bool Valid() const {return NetworkController::Valid() && controller->Valid();}
+
+ virtual void Message(const char * message)
+ {
+ //fprintf(stderr,"NetworkSender sending message %s to underlying controller\n", message);
+ controller->Message(message);
+ }
+
+ virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]);
+ virtual MovementResult QueryMove(std::string & buffer);
+
+ private:
+ Controller * controller;
+
};
-class Client : public NetworkController
+class NetworkReceiver : public NetworkController
{
public:
- Client(const Piece::Colour & newColour, const char * newName = "no-name", const char * server="127.0.0.1");
- virtual ~Client() {}
+ NetworkReceiver(const Piece::Colour & colour, Network * newNetwork) : NetworkController(colour, newNetwork) {}
+ virtual ~NetworkReceiver() {}
+
+ virtual void Message(const char * message) {} //Do nothing (Counter intuitive much)
+ virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]);
+ virtual MovementResult QueryMove(std::string & buffer);
+
+
};
#endif //NETWORK_CONTROLLER_H