Random Commander From File

Loads a player with a randomly selected commander from a specified file.

 * This function reads a file ('User/commander/commander.txt') that contains a list of potential commander IDs or names.
 * It selects one randomly to be used as the player's commander. If the file doesn't exist, or no valid commanders are found, a default commander ID (670972) is used.
* Can read the commander name and a multiverseID
* Lines that start with '#' are ignored
This commit is contained in:
Eduardo MG
2024-08-21 22:33:31 -06:00
parent ec2ef1856b
commit 8793f05cea
3 changed files with 183 additions and 2 deletions

View File

@@ -18,6 +18,7 @@ typedef enum
GAME_TYPE_RANDOM3,
GAME_TYPE_RANDOM5,
GAME_TYPE_RANDOMCOMMANDER,
GAME_TYPE_RANDOMCOMMANDERFROMFILE,
GAME_TYPE_HORDE,
GAME_TYPE_SET_LIMITED,
GAME_TYPE_STORY,

View File

@@ -45,6 +45,7 @@ protected:
Player * loadPlayerRandomThree(GameObserver* observer, int isAI);
Player * loadPlayerRandomFive(GameObserver* observer, int isAI);
Player * loadPlayerRandomCommander(GameObserver* observer, int isAI);
Player * loadPlayerRandomCommanderFromFile(GameObserver* observer, int isAI);
Player * loadPlayerHorde(GameObserver* observer, int isAI);
Player * loadRandomSetLimited(GameObserver* observer, int isAI);
Player * initPlayer(GameObserver *observer, int playerId);

View File

@@ -168,7 +168,7 @@ void Rules::addExtraRules(GameObserver* g)
difficultyRating = 0;
else if(g->mRules->gamemode == GAME_TYPE_RANDOM1 || g->mRules->gamemode == GAME_TYPE_RANDOM2)
difficultyRating = 0;
else if(g->mRules->gamemode == GAME_TYPE_RANDOM3 || g->mRules->gamemode == GAME_TYPE_RANDOM5 || g->mRules->gamemode == GAME_TYPE_RANDOMCOMMANDER)
else if(g->mRules->gamemode == GAME_TYPE_RANDOM3 || g->mRules->gamemode == GAME_TYPE_RANDOM5 || g->mRules->gamemode == GAME_TYPE_RANDOMCOMMANDER || g->mRules->gamemode == GAME_TYPE_RANDOMCOMMANDERFROMFILE)
difficultyRating = 0;
else if(g->mRules->gamemode == GAME_TYPE_HORDE || g->mRules->gamemode == GAME_TYPE_SET_LIMITED)
difficultyRating = 0;
@@ -200,7 +200,7 @@ void Rules::addExtraRules(GameObserver* g)
a->resolve();
else if(g->mRules->gamemode == GAME_TYPE_RANDOM1 || g->mRules->gamemode == GAME_TYPE_RANDOM2)
a->resolve();
else if(g->mRules->gamemode == GAME_TYPE_RANDOM3 || g->mRules->gamemode == GAME_TYPE_RANDOM5 || g->mRules->gamemode == GAME_TYPE_RANDOMCOMMANDER)
else if(g->mRules->gamemode == GAME_TYPE_RANDOM3 || g->mRules->gamemode == GAME_TYPE_RANDOM5 || g->mRules->gamemode == GAME_TYPE_RANDOMCOMMANDER || g->mRules->gamemode == GAME_TYPE_RANDOMCOMMANDERFROMFILE)
a->resolve();
else if (g->mRules->gamemode == GAME_TYPE_STORY)
a->resolve();
@@ -478,6 +478,182 @@ Player * Rules::loadPlayerRandomCommander(GameObserver* observer, int isAI)
return player;
}
/**
* @brief Loads a player with a randomly selected commander from a specified file.
*
* This function reads a file ('User/commander/commander.txt') that contains a list of potential commander IDs or names.
* It selects one randomly to be used as the player's commander. If the file doesn't exist, or no valid commanders
* are found, a default commander ID (670972) is used.
*
* @param observer A pointer to the GameObserver, used to track game events.
* @param isAI An integer indicating if the player is AI (non-zero) or human (zero).
*
* @return A pointer to the Player object created, either a human or AI player with a deck constructed
* around the selected commander. Returns 'nullptr' if a player could not be created due to errors.
*/
Player* Rules::loadPlayerRandomCommanderFromFile(GameObserver* observer, int isAI)
{
std::ifstream file("User/commander/commander.txt");
// Check if the file exists
if (!file.is_open())
{
std::cerr << "File not found: User/commander/commander.txt. Creating file with default value 670972." << std::endl;
// Create the file with a default commander ID
std::ofstream outFile("User/commander/commander.txt");
if (!outFile.is_open())
{
std::cerr << "Failed to create file: User/commander/commander.txt" << std::endl;
return nullptr;
}
outFile << "670972" << std::endl;
outFile.close();
// Re-open the file to continue processing
file.open("User/commander/commander.txt");
if (!file.is_open())
{
std::cerr << "Failed to open file after creation: User/commander/commander.txt" << std::endl;
return nullptr;
}
}
std::vector<std::string> commanderIds;
std::string line;
while (std::getline(file, line))
{
// Ignore lines that start with '#'
if (!line.empty() && line[0] == '#')
{
continue;
}
// Validate and add the line if it's a valid numeric ID
char* end;
long id = std::strtol(line.c_str(), &end, 10);
if (*end == '\0' && id > 0)
{
commanderIds.push_back(line);
}
else
{
// Attempt to convert name to ID if it's not a valid numeric ID
int cardId = getMTGId(line); // Assumes getMTGId is defined and returns -1 if not found
if (cardId != -1)
{
std::stringstream ss;
ss << cardId;
commanderIds.push_back(ss.str());
}
}
}
file.close();
// If no valid commander IDs found, default to 670972
if (commanderIds.empty())
{
std::cerr << "No valid commander IDs found in file. Using default commander ID 670972." << std::endl;
commanderIds.push_back("670972");
}
// Filter out invalid commander IDs
std::vector<std::string> validCommanderIds;
for (size_t i = 0; i < commanderIds.size(); ++i)
{
int commanderIntId = std::atoi(commanderIds[i].c_str());
MTGCard* card = MTGCollection()->getCardById(commanderIntId);
if (card != nullptr)
{
validCommanderIds.push_back(commanderIds[i]);
}
else
{
std::cerr << "Commander with ID " << commanderIds[i] << " does not exist in primitives." << std::endl;
}
}
// If no valid commander IDs after filtering, default to 670972
if (validCommanderIds.empty())
{
std::cerr << "No valid commanders available after filtering. Using default commander ID 670972." << std::endl;
validCommanderIds.push_back("670972");
}
// Randomly select a commander ID
std::srand(static_cast<unsigned int>(std::time(nullptr)));
int randomIndex = std::rand() % validCommanderIds.size();
std::string commanderId = validCommanderIds[randomIndex];
MTGDeck* cmdTempDeck = NEW MTGDeck(MTGCollection());
int commanderIntId = std::atoi(commanderId.c_str());
if (!cmdTempDeck->add(commanderIntId))
{
std::cerr << "Failed to add commander card with ID: " << commanderId << std::endl;
return nullptr;
}
MTGDeck* tempDeck = NEW MTGDeck(MTGCollection());
tempDeck->meta_commander = true;
std::string lands[] = { "", "forest", "island", "mountain", "swamp", "plains", "basic", "basic" };
DeckDataWrapper* myCommandZone = NEW DeckDataWrapper(cmdTempDeck);
MTGCard* commander = myCommandZone->getCard(0, true);
if (commander == nullptr)
{
std::cerr << "Invalid commander card retrieved from the command zone." << std::endl;
return nullptr;
}
std::stringstream cid;
cid << commanderId;
std::vector<std::string> newCMD;
newCMD.push_back(cid.str());
tempDeck->replaceCMD(newCMD);
std::vector<int> colors;
for (int i = 0; i < Constants::NB_Colors; i++)
{
if (commander->data->getManaCost()->hasColor(i))
colors.push_back(i);
}
if (!colors.empty() && colors[0] != 0)
{
colors.insert(colors.begin(), 0);
}
// Add lands
int numLands = colors.size() > 1 ? 40 / (colors.size() - 1) : 40;
if (colors.size() > 1)
{
for (unsigned int i = 1; i < colors.size(); i++)
{
tempDeck->addRandomCards(numLands, 0, 0, -1, lands[colors[i]].c_str());
}
}
else
{
tempDeck->addRandomCards(numLands, 0, 0, Constants::RARITY_L);
}
tempDeck->addRandomCards(59, 0, 0, -1, "", colors.data(), colors.size());
std::string deckFile = "random";
std::string deckFileSmall = "random";
Player* player = nullptr;
if (!isAI) // Human Player
player = NEW HumanPlayer(observer, deckFile, deckFileSmall, false, tempDeck);
else
player = NEW AIPlayerBaka(observer, deckFile, deckFileSmall, "", tempDeck);
return player;
}
Player * Rules::loadPlayerHorde(GameObserver* observer, int isAI)
{
int nbColors = 1;
@@ -571,6 +747,8 @@ Player * Rules::initPlayer(GameObserver *g, int playerId)
return loadPlayerRandomFive(g, isAI);
case GAME_TYPE_RANDOMCOMMANDER:
return loadPlayerRandomCommander(g, isAI);
case GAME_TYPE_RANDOMCOMMANDERFROMFILE:
return loadPlayerRandomCommanderFromFile(g, isAI);
case GAME_TYPE_HORDE:
return loadPlayerHorde(g, isAI);
case GAME_TYPE_SET_LIMITED:
@@ -895,6 +1073,7 @@ GameType Rules::strToGameMode(string s)
if (s.compare("random3") == 0) return GAME_TYPE_RANDOM3;
if (s.compare("random5") == 0) return GAME_TYPE_RANDOM5;
if (s.compare("random_commander") == 0) return GAME_TYPE_RANDOMCOMMANDER;
if (s.compare("random_commander_from_file") == 0) return GAME_TYPE_RANDOMCOMMANDERFROMFILE;
if (s.compare("horde") == 0) return GAME_TYPE_HORDE;
if (s.compare("set_limited") == 0) return GAME_TYPE_SET_LIMITED;
if (s.compare("story") == 0) return GAME_TYPE_STORY;