From 8793f05cea0ef26d384465d51555db220b80f7a3 Mon Sep 17 00:00:00 2001 From: Eduardo MG Date: Wed, 21 Aug 2024 22:33:31 -0600 Subject: [PATCH] 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 --- projects/mtg/include/MTGDefinitions.h | 1 + projects/mtg/include/Rules.h | 1 + projects/mtg/src/Rules.cpp | 183 +++++++++++++++++++++++++- 3 files changed, 183 insertions(+), 2 deletions(-) diff --git a/projects/mtg/include/MTGDefinitions.h b/projects/mtg/include/MTGDefinitions.h index 7cd424e17..3ea9b917d 100644 --- a/projects/mtg/include/MTGDefinitions.h +++ b/projects/mtg/include/MTGDefinitions.h @@ -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, diff --git a/projects/mtg/include/Rules.h b/projects/mtg/include/Rules.h index 68d95eeb9..7695907c6 100644 --- a/projects/mtg/include/Rules.h +++ b/projects/mtg/include/Rules.h @@ -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); diff --git a/projects/mtg/src/Rules.cpp b/projects/mtg/src/Rules.cpp index 88d9a475d..d124f8e96 100644 --- a/projects/mtg/src/Rules.cpp +++ b/projects/mtg/src/Rules.cpp @@ -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 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 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(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 newCMD; + newCMD.push_back(cid.str()); + tempDeck->replaceCMD(newCMD); + + std::vector 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;