#include "game.h" #include #include 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; } // Interpret a string of orders int CTTRTSGame::IssueOrders( player_t player, const std::string& _orders ) { COrderVector orderVector; // Copy the const orders into a buffer we can edit std::string orders = _orders; // Find a line end size_t pos; while ( (pos = orders.find('\n')) != std::string::npos ) { // Grab the string up to the line end const std::string sorder = orders.substr(0, pos); // Erase all of string up to and including the line end orders.erase(0,pos+1); // Create an order from the string and push it back SOrder order = GetOrderFromString( sorder ); orderVector.push_back(order); } // Call our add order by vector method return IssueOrders(player,orderVector); } // Issue orders by vector to the game int CTTRTSGame::IssueOrders( player_t player, const COrderVector& orders ) { // verify all the orders for ( auto order : orders ) { // If any order returns non-zero, back out if ( IssueOrder(player,order) ) return 1; } return 0; } // Issue a single order int CTTRTSGame::IssueOrder( player_t player, const SOrder & order ) { // Verify the order if ( VerifyOrder(player,order) ) return 1; // Get the right unit for the order for ( SOrderUnitPair & pair : m_OrderUnitPairs ) { if (pair.unit.GetID() == order.unit ) { pair.order = order; return 0; } } // Unit was not found, return 2 return 2; } // Verify a position int CTTRTSGame::VerifyPosIsValidMovement(uvector2 vec) const { // Simply check if within the bounds of our dimensions for now if ( ( vec.x >= dimensions.x ) || ( vec.y >= dimensions.y ) ) { return 1; } return 0; } // Get a units new position uvector2 CTTRTSGame::GetNewPosition( const SOrderUnitPair & pair ) const { // Grab the order switch ( pair.order.command) { // For forward orders, grab in front case command_c::F: return pair.unit.GetInFront(); // For all other orders, just grab the old position default: 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 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 = (VerifyPosIsValidMovement(newpos) == 0 ); if ( possible ) { // If any unit is in this spot, or moving unit moving to said spot, reject this for ( const SOrderUnitPair & pair2 : m_OrderUnitPairs ) { // Skip myself if(pair.unit.GetID() == pair2.unit.GetID() ) continue; if( GetNewPosition(pair2) == newpos ) { possible = false; break; } } } // If the movement is still possible if ( possible ) { pair.unit.SetPos(newpos); } } break; default: break; } } // Turn all units that need turning for ( SOrderUnitPair & pair : m_OrderUnitPairs ) { switch ( pair.order.command) { case command_c::L: { // Simply turn left pair.unit.TurnLeft(); } break; case command_c::R: { // Simply turn right 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 for ( SOrderUnitPair & pair : m_OrderUnitPairs ) { if ( pair.order.command == command_c::A ) { uvector2 newpos = pair.unit.GetInFront(); // If move would be within the arena if (VerifyPosIsValidMovement(newpos) == 0 ) { 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 for ( SOrderUnitPair & pair1 : m_OrderUnitPairs ) if ( pair1.order.command == command_c::A ) for ( SOrderUnitPair & pair2 : m_OrderUnitPairs ) 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) ) { 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 for ( SOrderUnitPair & pair1 : m_OrderUnitPairs ) for ( SOrderUnitPair & pair2 : m_OrderUnitPairs ) { if(pair1.unit.GetID() == pair2.unit.GetID() ) continue; // Don't check the same units if(pair1.unit.GetPos() == pair2.unit.GetPos() ) { if( pair1.order.command == command_c::A ) { toKill.push_back(pair2.unit.GetID()); } if( pair2.order.command == command_c::A ) { toKill.push_back(pair1.unit.GetID()); } } } // Kill all units to kill KillAll(toKill); toKill.clear(); } // Clear all orders for ( SOrderUnitPair & pair : m_OrderUnitPairs ) 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++ ) { if((*it).unit.GetID() == id ) { // Remove the unit from our alive unit pairs m_OrderUnitPairs.erase(it); break; } } } } // Check if two units passed through each other bool CTTRTSGame::CheckForPassThrough( const CUnit& one, const CUnit& two ) { uvector2 pos1 = one.GetPos(); uvector2 pos2 = two.GetPos(); dir_c dir1 = one.GetDir(); dir_c dir2 = two.GetDir(); if( pos1.x == pos2.x ) { // Same col if (pos1.y == (pos2.y + 1)) { if (dir1 == dir_c::N && dir2 == dir_c::S) return true; } else if (pos1.y == (pos2.y - 1)) { if (dir1 == dir_c::S && dir2 == dir_c::N) return true; } } else if( pos1.y == pos2.y ) { // Same row if( pos1.x == (pos2.x+1) ) { if( dir1 == dir_c::E && dir2 == dir_c::W ) return true; } else if( pos1.x == (pos2.x-1) ) { if( dir1 == dir_c::E && dir2 == dir_c::W ) return true; } } return false; } // Add a unit, nonzero return value indicates error int CTTRTSGame::AddUnit( CUnit&& unit ) { // Verify the unit if( !unit.Valid() ) return 1; // Verify if the unit can be placed on the current board const uvector2 pos = unit.GetPos(); if( (pos.x >= dimensions.x) || (pos.y >= dimensions.y) ) return 2; // If any unit's position matches, reject this for ( const SOrderUnitPair & pair: m_OrderUnitPairs ) { if(pair.unit.GetPos() == unit.GetPos() ) return 3; } // Add the unit with a blank order m_OrderUnitPairs.push_back( SOrderUnitPair(std::move(unit), SOrder()) ); return 0; } // Add a units, nonzero return value indicates error int CTTRTSGame::AddUnits( CUnitVector&& units ) { CUnitVector::iterator it; for ( it = units.begin(); it != units.end(); it++ ) { // Attempt the unit add if ( AddUnit( std::move(*it) ) ) return 1; } // All units added successfully return 0; } // Verify any order int CTTRTSGame::VerifyOrder( player_t player, const SOrder & order ) const { int ret = 1; // Grab the unit ID const unit_id_t unitID = order.unit; // Attempt to find the unit for ( const SOrderUnitPair & pair : m_OrderUnitPairs ) { // Accept if we have the unit if (pair.unit.GetID() == unitID && pair.unit.GetPlayer() == player) { ret = 0; 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 { for ( const SOrderUnitPair & pair : m_OrderUnitPairs ) { // Attempt the unit add 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 const SOrder & CTTRTSGame::GetOrderByIDConst( unit_id_t id ) const { for ( const SOrderUnitPair & pair : m_OrderUnitPairs ) { // Attempt the unit add if (pair.unit.GetID() == id ) return pair.order; } // Return an invalid unit static SOrder invalid_order; return invalid_order; } // Get unit by unit ID CUnit& CTTRTSGame::GetUnitByID( unit_id_t id ) { for ( SOrderUnitPair & pair : m_OrderUnitPairs ) { // Attempt the unit add if (pair.unit.GetID() == id ) return pair.unit; } // Return an invalid unit static CUnit invalid_unit; return invalid_unit; } // Get a vector of the players in the current game std::vector CTTRTSGame::GetPlayers() const { std::vector players; players.reserve(GetNumUnits()); // Grab all players for ( const SOrderUnitPair & pair : m_OrderUnitPairs ) { players.push_back(pair.unit.GetPlayer()); } // Remove dupes std::sort( players.begin(), players.end() ); players.erase( std::unique( players.begin(), players.end() ), players.end() ); return players; } // Check if we have a win state player_t CTTRTSGame::GetWinningPlayer() const { // Array of units for each Player unsigned int units[(int) player_t::NUM_INVALID]; memset(units,0,sizeof(units)); // Count up all the units for each Player for ( const SOrderUnitPair & pair : m_OrderUnitPairs ) { const int player = (int) pair.unit.GetPlayer(); units[player] += 1; } // Default winning Player to invalid (no win) player_t winningPlayer = player_t::NUM_INVALID; // For each of the players for ( unsigned int i = 0; i < _countof(units); i++ ) { // if there are still units in this Player, and the winning Player hasn't been set if( units[i] > 0 && winningPlayer == player_t::NUM_INVALID ) { winningPlayer = (player_t)i; } // Otherwise, if there are units in this Player and the winning Player HAS been set else if ( units[i] > 0 ) { // Set back to invalid and break out of the loop winningPlayer = player_t::NUM_INVALID; break; } } return winningPlayer; } // 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; 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; }