ttrts/source/game/game.cpp

547 lines
14 KiB
C++
Raw Normal View History

#include "game.h"
2014-12-16 13:12:59 +00:00
#include <algorithm>
#include <string.h>
2014-12-16 13:12:59 +00:00
CTTRTSGame::CTTRTSGame( ucoord_t c, ucoord_t r )
: dimensions( c,r )
, turn (0), name ( "Custom_Game" )
{
}
// Move constructor
CTTRTSGame::CTTRTSGame(CTTRTSGame&& game)
: m_OrderUnitPairs(std::move(game.m_OrderUnitPairs))
, dimensions(std::move(game.dimensions))
, turn(std::move(game.turn))
, name(std::move(game.name))
{
}
CTTRTSGame& CTTRTSGame::operator=(CTTRTSGame&& game)
{
m_OrderUnitPairs = std::move(game.m_OrderUnitPairs);
dimensions = std::move(game.dimensions);
turn = std::move(game.turn);
name = std::move(game.name);
return *this;
}
2014-12-16 13:12:59 +00:00
// Interpret a string of orders
int CTTRTSGame::IssueOrders( Team team, const std::string& _orders )
2014-12-16 13:12:59 +00:00
{
COrderVector orderVector;
2014-12-16 13:13:02 +00:00
// Copy the const orders into a buffer we can edit
std::string orders = _orders;
// Find a line end
2014-12-16 13:12:59 +00:00
size_t pos;
2014-12-16 13:13:02 +00:00
while ( (pos = orders.find('\n')) != std::string::npos )
2014-12-16 13:12:59 +00:00
{
2014-12-16 13:13:02 +00:00
// Grab the string up to the line end
2014-12-16 13:12:59 +00:00
const std::string sorder = orders.substr(0, pos);
2014-12-16 13:13:02 +00:00
// Erase all of string up to and including the line end
2014-12-16 13:12:59 +00:00
orders.erase(0,pos+1);
2014-12-16 13:13:02 +00:00
// Create an order from the string and push it back
2014-12-21 10:39:39 +00:00
SOrder order = GetOrderFromString( sorder );
2014-12-16 13:12:59 +00:00
orderVector.push_back(order);
}
2014-12-16 13:13:02 +00:00
// Call our add order by vector method
return IssueOrders(team,orderVector);
2014-12-16 13:12:59 +00:00
}
// Issue orders by vector to the game
int CTTRTSGame::IssueOrders( Team team, const COrderVector& orders )
2014-12-16 13:12:59 +00:00
{
// verify all the orders
for ( auto order : orders )
2014-12-16 13:12:59 +00:00
{
2014-12-16 13:13:02 +00:00
// If any order returns non-zero, back out
if ( IssueOrder(team,order) )
2014-12-16 13:12:59 +00:00
return 1;
}
return 0;
}
// Issue a single order
2014-12-21 10:39:39 +00:00
int CTTRTSGame::IssueOrder( Team team, const SOrder & order )
2014-12-16 13:12:59 +00:00
{
2014-12-16 13:13:02 +00:00
// Verify the order
if ( VerifyOrder(team,order) )
2014-12-16 13:12:59 +00:00
return 1;
2014-12-16 13:13:02 +00:00
// Get the right unit for the order
2014-12-21 10:31:00 +00:00
for ( SOrderUnitPair & pair : m_OrderUnitPairs )
{
2014-12-21 10:44:08 +00:00
if (pair.unit.GetID() == order.unit )
{
pair.order = order;
return 0;
}
}
2014-12-16 13:12:59 +00:00
2014-12-16 13:13:02 +00:00
// Unit was not found, return 2
return 2;
2014-12-16 13:12:59 +00:00
}
// Verify a position
int CTTRTSGame::VerifyPos(uvector2 vec) const
2014-12-16 13:12:59 +00:00
{
2014-12-16 13:13:02 +00:00
// Simply check if within the bounds of our dimensions for now
if ( ( vec.x >= dimensions.x )
|| ( vec.y >= dimensions.y ) )
{
return 1;
}
return 0;
}
2014-12-16 13:12:59 +00:00
// Get a units new position
2014-12-21 10:31:00 +00:00
uvector2 CTTRTSGame::GetNewPosition( const SOrderUnitPair & pair ) const
{
2014-12-16 13:13:02 +00:00
// Grab the order
switch ( pair.order.command)
{
2014-12-16 13:13:02 +00:00
// For forward orders, grab in front
case command_c::F:
2014-12-21 10:44:08 +00:00
return pair.unit.GetInFront();
2014-12-16 13:13:02 +00:00
// For all other orders, just grab the old position
default:
2014-12-21 10:44:08 +00:00
return pair.unit.GetPos();
}
}
// Simulate and progress to the next turn
// Returns non-zero if simulation failed
int CTTRTSGame::SimulateToNextTurn()
{
int error = 0;
// Attempt all movement orders
2014-12-21 10:31:00 +00:00
for ( SOrderUnitPair & pair : m_OrderUnitPairs )
{
switch ( pair.order.command)
{
case command_c::F:
{
// Verify new unit position will be on the board
uvector2 newpos = GetNewPosition(pair);
// Verify the position is even available
bool possible = ( VerifyPos(newpos) == 0 );
if ( possible )
{
// If any unit is in this spot, or moving unit moving to said spot, reject this
2014-12-21 10:31:00 +00:00
for ( const SOrderUnitPair & pair2 : m_OrderUnitPairs )
{
// Skip myself
2014-12-21 10:44:08 +00:00
if(pair.unit.GetID() == pair2.unit.GetID() ) continue;
if( GetNewPosition(pair2) == newpos )
{
possible = false;
break;
}
}
}
// If the movement is still possible
if ( possible )
{
2014-12-21 10:44:08 +00:00
pair.unit.SetPos(newpos);
}
}
break;
default:
break;
}
}
// Turn all units that need turning
2014-12-21 10:31:00 +00:00
for ( SOrderUnitPair & pair : m_OrderUnitPairs )
{
switch ( pair.order.command)
{
case command_c::L:
{
// Simply turn left
2014-12-21 10:44:08 +00:00
pair.unit.TurnLeft();
}
break;
case command_c::R:
{
// Simply turn right
2014-12-21 10:44:08 +00:00
pair.unit.TurnRight();
}
break;
default:
break;
}
}
// Iterate through all charge states
bool charging = true;
while(charging)
{
// Assume no more charging
charging = false;
// Initially move all units
2014-12-21 10:31:00 +00:00
for ( SOrderUnitPair & pair : m_OrderUnitPairs )
{
if ( pair.order.command == command_c::A )
{
2014-12-21 10:44:08 +00:00
uvector2 newpos = pair.unit.GetInFront();
// If move would be within the arena
if ( ( newpos.x <= dimensions.x-1 ) && ( newpos.y <= dimensions.y-1 ) )
{
2014-12-21 10:44:08 +00:00
pair.unit.SetPos(newpos);
// Unit moved, so more charging needs to be done
charging = true;
}
}
}
std::vector< unit_id_t > toKill; // Vector to store which units to kill
// Initially move all units to check for pass through
2014-12-21 10:31:00 +00:00
for ( SOrderUnitPair & pair1 : m_OrderUnitPairs )
if ( pair1.order.command == command_c::A )
2014-12-21 10:31:00 +00:00
for ( SOrderUnitPair & pair2 : m_OrderUnitPairs )
2014-12-21 10:44:08 +00:00
if (pair1.unit.GetID() != pair2.unit.GetID() // Don't check the same units
&& pair2.order.command == command_c::A )
{
if( CheckForPassThrough(pair1.unit,pair2.unit) )
{
2014-12-21 10:44:08 +00:00
toKill.push_back(pair1.unit.GetID());
toKill.push_back(pair2.unit.GetID());
}
}
// Kill all units to kill
KillAll(toKill);
toKill.clear();
// Check for all matching spots
2014-12-21 10:31:00 +00:00
for ( SOrderUnitPair & pair1 : m_OrderUnitPairs )
for ( SOrderUnitPair & pair2 : m_OrderUnitPairs )
{
2014-12-21 10:44:08 +00:00
if(pair1.unit.GetID() == pair2.unit.GetID() ) continue; // Don't check the same units
2014-12-21 10:44:08 +00:00
if(pair1.unit.GetPos() == pair2.unit.GetPos() )
{
if( pair1.order.command == command_c::A )
{
2014-12-21 10:44:08 +00:00
toKill.push_back(pair2.unit.GetID());
}
if( pair2.order.command == command_c::A )
{
2014-12-21 10:44:08 +00:00
toKill.push_back(pair1.unit.GetID());
}
}
}
// Kill all units to kill
KillAll(toKill);
toKill.clear();
}
// Clear all orders
2014-12-21 10:31:00 +00:00
for ( SOrderUnitPair & pair : m_OrderUnitPairs )
2014-12-21 10:39:39 +00:00
pair.order = SOrder();
// Increment the current turn
turn++;
return error;
}
// Kill all units in list
void CTTRTSGame::KillAll( std::vector< unit_id_t >& vec )
{
// Sort and erase all duplicates
std::sort( vec.begin(), vec.end() );
vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() );
for ( auto id : vec )
{
for ( OrderUnitPairVector::iterator it = m_OrderUnitPairs.begin();
it != m_OrderUnitPairs.end();
it++ )
{
2014-12-21 10:44:08 +00:00
if((*it).unit.GetID() == id )
{
// Remove the unit from our alive unit pairs
m_OrderUnitPairs.erase(it);
break;
}
}
}
}
2014-12-16 13:12:59 +00:00
// Check if two units passed through each other
bool CTTRTSGame::CheckForPassThrough( const CUnit& one, const CUnit& two )
{
2014-12-21 10:44:08 +00:00
uvector2 pos1 = one.GetPos();
uvector2 pos2 = two.GetPos();
dir_t dir1 = one.GetDir();
dir_t dir2 = two.GetDir();
if( pos1.x == pos2.x ) { // Same col
if (pos1.y == (pos2.y + 1)) {
if (dir1 == dir_t::N && dir2 == dir_t::S)
return true;
}
else if (pos1.y == (pos2.y - 1)) {
if (dir1 == dir_t::S && dir2 == dir_t::N)
return true;
}
}
else if( pos1.y == pos2.y ) { // Same row
if( pos1.x == (pos2.x+1) ) {
if( dir1 == dir_t::E && dir2 == dir_t::W )
return true;
}
else if( pos1.x == (pos2.x-1) ) {
if( dir1 == dir_t::E && dir2 == dir_t::W )
return true;
}
}
2014-12-16 13:12:59 +00:00
return false;
2014-12-16 13:12:59 +00:00
}
// Add a unit, nonzero return value indicates error
int CTTRTSGame::AddUnit( CUnit&& unit )
{
// Verify the unit
2014-12-21 10:44:08 +00:00
if( !unit.Valid() )
return 1;
// Verify if the unit can be placed on the current board
2014-12-21 10:44:08 +00:00
const uvector2 pos = unit.GetPos();
if( (pos.x >= dimensions.x) || (pos.y >= dimensions.y) )
return 2;
2014-12-16 13:13:02 +00:00
// If any unit's position matches, reject this
2014-12-21 10:31:00 +00:00
for ( const SOrderUnitPair & pair: m_OrderUnitPairs )
2014-12-16 13:13:01 +00:00
{
2014-12-21 10:44:08 +00:00
if(pair.unit.GetPos() == unit.GetPos() )
return 3;
2014-12-16 13:13:01 +00:00
}
2014-12-16 13:13:02 +00:00
// Add the unit with a blank order
2014-12-21 10:39:39 +00:00
m_OrderUnitPairs.push_back( SOrderUnitPair(std::move(unit), SOrder()) );
2014-12-16 13:13:01 +00:00
2014-12-16 13:12:59 +00:00
return 0;
}
// Add a units, nonzero return value indicates error
int CTTRTSGame::AddUnits( CUnitVector&& units )
{
CUnitVector::iterator it;
2014-12-16 13:12:59 +00:00
for ( it = units.begin(); it != units.end(); it++ )
{
// Attempt the unit add
if ( AddUnit( std::move(*it) ) )
return 1;
}
2014-12-16 13:12:59 +00:00
// All units added successfully
2014-12-16 13:12:59 +00:00
return 0;
}
// Verify any order
2014-12-21 10:39:39 +00:00
int CTTRTSGame::VerifyOrder( Team team, const SOrder & order ) const
2014-12-16 13:12:59 +00:00
{
int ret = 1;
2014-12-16 13:12:59 +00:00
// Grab the unit ID
const unit_id_t unitID = order.unit;
// Attempt to find the unit
2014-12-21 10:31:00 +00:00
for ( const SOrderUnitPair & pair : m_OrderUnitPairs )
2014-12-16 13:12:59 +00:00
{
2014-12-16 13:13:02 +00:00
// Accept if we have the unit
2014-12-21 10:44:08 +00:00
if (pair.unit.GetID() == unitID
&& pair.unit.GetTeam() == team )
2014-12-16 13:12:59 +00:00
{
ret = 0;
2014-12-16 13:12:59 +00:00
break;
}
}
// for now, as long as the unit exists we can attempt the order
return ret;
}
// Get unit by unit ID
const CUnit& CTTRTSGame::GetUnitByIDConst( unit_id_t id ) const
{
2014-12-21 10:31:00 +00:00
for ( const SOrderUnitPair & pair : m_OrderUnitPairs )
{
// Attempt the unit add
2014-12-21 10:44:08 +00:00
if (pair.unit.GetID() == id )
return pair.unit;
}
// Return an invalid unit
static CUnit invalid_unit;
return invalid_unit;
}
// Get an order by unit ID
2014-12-21 10:39:39 +00:00
const SOrder & CTTRTSGame::GetOrderByIDConst( unit_id_t id ) const
{
2014-12-21 10:31:00 +00:00
for ( const SOrderUnitPair & pair : m_OrderUnitPairs )
{
// Attempt the unit add
2014-12-21 10:44:08 +00:00
if (pair.unit.GetID() == id )
return pair.order;
}
// Return an invalid unit
2014-12-21 10:39:39 +00:00
static SOrder invalid_order;
return invalid_order;
}
// Get unit by unit ID
CUnit& CTTRTSGame::GetUnitByID( unit_id_t id )
{
2014-12-21 10:31:00 +00:00
for ( SOrderUnitPair & pair : m_OrderUnitPairs )
{
// Attempt the unit add
2014-12-21 10:44:08 +00:00
if (pair.unit.GetID() == id )
return pair.unit;
}
// Return an invalid unit
static CUnit invalid_unit;
return invalid_unit;
}
// Get a vector of the teams in the current game
std::vector<Team> CTTRTSGame::GetTeams() const
{
std::vector<Team> teams;
teams.reserve(GetNumUnits());
// Grab all teams
2014-12-21 10:31:00 +00:00
for ( const SOrderUnitPair & pair : m_OrderUnitPairs )
{
2014-12-21 10:44:08 +00:00
teams.push_back(pair.unit.GetTeam());
}
// Remove dupes
std::sort( teams.begin(), teams.end() );
teams.erase( std::unique( teams.begin(), teams.end() ), teams.end() );
return teams;
}
// Check if we have a win state
Team CTTRTSGame::CheckForWin() const
{
// Array of units for each Team
unsigned int units[(int) Team::NUM_INVALID];
memset(units,0,sizeof(units));
// Count up all the units for each Team
2014-12-21 10:31:00 +00:00
for ( const SOrderUnitPair & pair : m_OrderUnitPairs )
{
2014-12-21 10:44:08 +00:00
const int team = (int) pair.unit.GetTeam();
units[team] += 1;
}
// Default winning Team to invalid (no win)
Team winningTeam = Team::NUM_INVALID;
// For each of the teams
for ( unsigned int i = 0; i < _countof(units); i++ )
{
// if there are still units in this Team, and the winning Team hasn't been set
if( units[i] > 0 && winningTeam == Team::NUM_INVALID )
{
winningTeam = (Team)i;
}
// Otherwise, if there are units in this Team and the winning Team HAS been set
else if ( units[i] > 0 )
{
// Set back to invalid and break out of the loop
winningTeam = Team::NUM_INVALID;
break;
}
}
return winningTeam;
}
// Get the game information as a string
std::string CTTRTSGame::GetStateAsString() const
{
// Print out the header
char header[64];
snprintf(header, 512, GAME_HEADER_FORMATTER , name.c_str(), dimensions.x, dimensions.y, turn );
// Gather unit information
std::string units;
2014-12-21 10:31:00 +00:00
for ( const SOrderUnitPair & pair : m_OrderUnitPairs )
{
units += CUnit::GetStringFromUnit(pair.unit);
units += '\n';
}
// Append the header and units
std::string state(header);
state += '\n';
state += GAME_HEADER_DELIMITER;
state += units;
state += "END";
return state;
}
// Get the game information as a string
CTTRTSGame CTTRTSGame::CreateFromString( const std::string& input )
{
size_t headerEnd = input.find(GAME_HEADER_DELIMITER);
std::string header = input.substr(0, headerEnd);
std::string units = input.substr(headerEnd + strlen(GAME_HEADER_DELIMITER));
// Grab information from the header
char buf[64];
unsigned int turn;
unsigned int sizex;
unsigned int sizey;
sscanf(header.c_str(), GAME_HEADER_FORMATTER, buf, &sizex, &sizey, &turn );
CTTRTSGame game(sizex,sizey);
game.SetName(buf);
game.SetTurn(turn);
// For each line, construct a unit
size_t pos;
while ( ( pos = units.find('\n') ) != std::string::npos )
{
std::string unit_string = units.substr(0,pos);
units.erase(0,pos+1);
game.AddUnit(CUnit::GetUnitFromString(unit_string));
}
return game;
}