ttrts/source/ttrts/main.cpp

196 lines
5.3 KiB
C++

#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <sys/stat.h>
#include "game.h"
static const char* sk_usage =
"NAME\n"
"\tttrts - Tiny Terminal RTS\n"
"\n"
"SYNOPSYS\n"
"\tttrts [OPTIONS...] MAPFILE\n"
"\n"
"DESCRIPTION\n"
"\tttrts is a tiny terminal based RTS where that uses text\n"
"\tfiles as order lists to control it's units.\n"
"\n"
"\tThis means that any user, program or cat that can read\n"
"\tand write to text files can play the game.\n"
"\n"
"USAGE\n"
"\tWhen invoked, ttrts will set up a full game and output a\n"
"\tsingle file representing the current gamestate into a \n"
"\tlocal directory called ttrts_{GAME_NAME}.\n"
"\n"
"\tThis file can be read in and interpretted by human, robot\n"
"\tor cat. ttrts will wait for orders files to be placed in \n"
"\tit's current working directory.\n"
"\n"
"\tOnce orders have been set for each player taking part \n"
"\tttrts will calculate the new game state and output a new \n"
"\tgamestate file for the next turn.\n"
"\n"
"\tThis process repeats until a winner is chosen!\n"
"\n"
"OPTIONS\n"
"\tMAPFILE:\n"
"\t\tFile to read in the initial game state from\n"
"\n"
"GAMESTATE FILE FORMAT\n"
"\tName\n"
"\t Turn_{TURN_NUMBER}.txt\n"
"\tContents\n"
"\t ===== {GAME_NAME} =====\n"
"\t SIZE:[{X},{Y}]\n"
"\t TURN:{TURN_NUMBER}\n"
"\t ~~~~\n"
"\t UNIT:{ID} tm:{TEAM} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}]\n"
"\t ...\n"
"\n"
"ORDER FILE FORMAT\n"
"\tName\n"
"\t Turn_{TURN_NUMBER}_Team_{TEAM_NUMBER}.txt\n"
"\tContents\n"
"\t ORDER:{ORDER_CHAR} id:{UNIT_ID}\n"
"\t ...\n"
"\n"
"ORDERS\n"
"\tF - Move unit forward one space\n"
"\tL/R - Rotate unit left or right\n"
"\tA - Attack space in front of unit\n";
// time for waiting between file stats
static const std::chrono::milliseconds sk_waitTime = std::chrono::milliseconds(100);
// Check if a file exists
inline bool FileExists( const std::string& name )
{
struct stat buffer;
return (stat (name.c_str(), &buffer) == 0);
}
// Wait for a file to exist
inline void WaitForFile( const std::string& name, const std::chrono::milliseconds& time )
{
while( !FileExists(name) ) std::this_thread::sleep_for(time);
}
// Main program entry point
int main(int argc, char* argv[])
{
// If no args, print usage
if ( argc == 1 )
{
std::cerr<<sk_usage<<std::endl;
return 1;
}
// Attempt to open the game file
std::string gameFile = argv[1];
std::ifstream file(gameFile);
if( file.bad() )
{
std::cerr<<"Error: "<<gameFile<<" file not found"<<std::endl;
return 1;
}
std::string gameDescriptor;
// Reserve the string needed up front
file.seekg(0, std::ios::end);
gameDescriptor.reserve(file.tellg());
file.seekg(0, std::ios::beg);
// Grab the string from the file
gameDescriptor.assign((std::istreambuf_iterator<char>(file)),std::istreambuf_iterator<char>());
if( gameDescriptor.size() == 0 )
{
std::cerr<<"Error: failed to read in any information from "<<gameFile<<std::endl;
return 1;
}
// Create the game
CTTRTSGame game = CTTRTSGame::CreateFromString(gameDescriptor);
// Grab the teams involved
auto teams = game.GetTeams();
// Current game directory
std::string gameDir = "ttrts_" + game.GetName();
// Remove the current game directory
char cmd[128];
snprintf(cmd,128, "test -e %s && rm -rf %s",gameDir.c_str(),gameDir.c_str());
system(cmd);
// Create the game directory
char cmd2[128];
snprintf(cmd2,128, "mkdir %s",gameDir.c_str());
system(cmd2);
// While the game hasn't been won
Team winningTeam = Team::NUM_INVALID;
while ( (winningTeam = game.CheckForWin()) == Team::NUM_INVALID )
{
// Create a turn file
char turnFileName[128];
snprintf(turnFileName,128,"ttrts_%s/Turn_%i.txt",game.GetName().c_str(),game.GetTurn());
std::ofstream turnFile(turnFileName,std::ios::trunc); // truncate to overwrite if a file exists
if ( turnFile.bad() )
{
std::cerr<<"Error: Failed to open new turn file "<< turnFileName <<std::endl;
return 1;
}
// Output the turn description
std::string turnDescriptor = game.GetStateAsString();
turnFile<<turnDescriptor;
turnFile.close();
// Wait for order files
for( Team team : teams )
{
// Construct the team order filename
char teamOrderFileName[128];
snprintf(teamOrderFileName, 128, "ttrts_%s/Turn_%i_Team_%i.txt", game.GetName().c_str(), game.GetTurn(), (int) team);
// Wait for the team order file to be created
std::cout<<"Waiting for "<<teamOrderFileName<<std::endl;
WaitForFile(teamOrderFileName,sk_waitTime); // Wait for the file
std::ifstream turnFile(teamOrderFileName);
// Reserve the full order string
std::string orders;
turnFile.seekg(0, std::ios::end);
orders.reserve(turnFile.tellg());
turnFile.seekg(0, std::ios::beg);
// Grab the string from the file
orders.assign((std::istreambuf_iterator<char>(turnFile)),std::istreambuf_iterator<char>());
// Issue the orders to the game
if( game.IssueOrders(team, orders) )
std::cerr<<"Warning: Orders for team "<<(int)team<<" failed to correctly parse"<<std::endl;
}
// Simulate turn
std::cerr<<"Simulating next turn"<<std::endl;
if ( game.SimulateToNextTurn() )
{
std::cerr << "Error: Failed to simulate next Turn "<<game.GetTurn()<<std::endl;
return 1;
}
}
// Print the winner!
std::cout<<"TTRTS: Game over! Winner:"<<(int)winningTeam<<std::endl;
return 0;
};