Files
dusklight/src/dusk/randomizer/generator/randomizer.cpp
T
2026-06-13 07:56:51 -04:00

173 lines
5.9 KiB
C++

#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 <iostream>
#include "dusk/logging.h"
#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<std::string> 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() {
auto contextHash = randomizer_GetContext().mHash;
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<logic::world::World> world = std::make_unique<logic::world::World>(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<logic::world::World> world = std::make_unique<logic::world::World>(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