#include "randomizer.hpp" #include "logic/entrance_shuffle.hpp" #include "logic/fill.hpp" #include "logic/flatten/flatten.hpp" #include "logic/hints.hpp" #include "logic/plandomizer.hpp" #include "logic/search.hpp" #include "logic/spoiler_log.hpp" #include "logic/world.hpp" #include "seedgen/config.hpp" #include "seedgen/settings.hpp" #include "utility/time.hpp" #include #include "dusk/logging.h" #include "dusk/archipelago/archipelago_context.hpp" #include "dusk/ui/rando_config.hpp" #include "dusk/randomizer/game/randomizer_context.hpp" namespace randomizer { logic::world::World* Randomizer::GetWorld(int worldId /*= 1*/) { auto worldIndex = worldId - 1; if (worldIndex < this->_worlds.size()) { return this->_worlds.at(worldIndex).get(); } return nullptr; } std::optional Randomizer::Generate() { try { GenerateWorlds(); } catch(const std::exception& e) { std::cout << "============================================================" << std::endl; std::cout << "The following exception occured: " << e.what() << std::endl; return e.what(); } return std::nullopt; } void Randomizer::GenerateTrackerWorld(bool useAntiSpoilerLog) { auto contextHash = randomizer_GetContext().mHash; if (!useAntiSpoilerLog) { this->_config.LoadFromFile(GetConfigPath(), GetPrefPath()); this->_config.SetHash(contextHash); }else { if (contextHash.empty()) { return; } std::filesystem::path seedSettings = dusk::ui::GetRandomizerSeedsPath() / contextHash / (contextHash + " Anti-Spoiler Log.txt"); this->_config.LoadFromFile(seedSettings, GetPrefPath()); this->_config.SetHash(contextHash); } std::unique_ptr world = std::make_unique(1, this); world->SetSettings(this->_config.GetSettingsList().front()); // Always use logic when building a tracker world world->Setting("Logic Rules").SetCurrentOption("All Locations Reachable"); world->Build(); this->_worlds.emplace_back(std::move(world)); auto trackerWorld = this->_worlds.at(0).get(); trackerWorld->SetNonProgressLocations(); trackerWorld->SetTrackerNonProgressLocations(); trackerWorld->AssignAreaProperties(); trackerWorld->AssignGoalLocations(); // Cache exit form times. This *must* run before conducting the flattening search, otherwise // the flattening search will pollute the exit timeform cache with a bunch of zeros logic::fill::CacheExitTimeForms(this->_worlds); // Set raw requirements for each location FlattenSearch search = FlattenSearch(trackerWorld); search.doSearch(); } void Randomizer::GenerateWorlds() { utility::time::ScopedTimer<"Seed generation took ", std::chrono::milliseconds> timer; this->_config.LoadFromFile(GetConfigPath(), GetPrefPath()); utility::platform::Log(std::string("Seed: ") + this->_config.GetSeed()); seedgen::config::SeedRNG(this->_config, true, false); // Set the hash now before anything else random is decided. This allows us to show the hash for a seed // before generating it later auto hash = this->_config.GetHash(); utility::platform::Log(std::string("Hash: ") + hash); // Build all worlds int worldId = 1; for (const auto& settings : this->_config.GetSettingsList()) { std::unique_ptr world = std::make_unique(worldId++, this); world->SetSettings(settings); world->ResolveRandomSettings(); world->ResolveConflictingSettings(); world->Build(); this->_worlds.emplace_back(std::move(world)); } // Process Plando Data for all worlds if (this->_config.IsUsingPlandomizer()) { try { logic::plandomizer::LoadPlandomizerData(this->_worlds, this->_config.GetPlandomizerPath()); } catch (const std::runtime_error& e) { throw std::runtime_error("Plandomizer Error: " + std::string(e.what())); } } // Pre Entrance Shuffle Tasks for (auto& world : this->_worlds) { world->PerformPreEntranceShuffleTasks(); } utility::platform::Log("Shuffling Entrances..."); for (auto& world : this->_worlds) { logic::entrance_shuffle::ShuffleWorldEntrances(world.get()); } // Post Entrance Shuffle Tasks for (auto& world : this->_worlds) { world->PerformPostEntranceShuffleTasks(); } logic::fill::CacheExitTimeForms(this->_worlds); // Flattening isn't used for anything yet, but flattens down the requirements for // each location and entrance into a single statement. This will be useful for hints and could potentially // be used to speed up the fill algorithm (but the fill algorithm is already pretty fast, so we'd only gain maybe like // 0.2 seconds back or something) utility::platform::Log("Flattening..."); FlattenSearch search = FlattenSearch(this->_worlds.at(0).get()); search.doSearch(); utility::platform::Log("Filling Worlds..."); logic::fill::FillWorlds(this->_worlds); // Post Fill Tasks for (auto& world : this->_worlds) { world->PerformPostFillTasks(); } // Generate Playthrough logic::search::GeneratePlaythrough(this); // Generate Hints logic::hints::GenerateAllHints(this->_worlds); // Write Logs if (this->_config.IsGeneratingSpoilerLog()) { logic::spoiler_log::GenerateSpoilerLog(this); } logic::spoiler_log::GenerateAntiSpoilerLog(this); } std::filesystem::path Randomizer::GetSeedOutputPath() { return this->_baseOutputPath / "seeds" / this->_config.GetHash(); } } // namespace randomizer