mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-04-23 08:14:31 +00:00
Fix RNG used by cosmetics editor to use same RNG method as rando (#5979)
Refactor ShipUtils to optionally take a state pointer. Also changed random.h/cpp to be a wrapper around ShipUtils RNG providing a pointer to the rando state variable. Use ShipUtils RNG for UIWidgets GetRandomValue Fix AudioEditor to use ShipUtils RNG and its own state. It seems like shuffling Audio at the right time could have potentially messed with rando seed generation before this, but that bug, if it existed, should also be fixed with this.
This commit is contained in:
parent
9ca5ce0b53
commit
ba0ecc59aa
|
|
@ -109,6 +109,8 @@ void UpdateCurrentBGM(u16 seqKey, SeqType seqType) {
|
|||
}
|
||||
}
|
||||
|
||||
static uint64_t seeded_audio_state = 0;
|
||||
|
||||
void RandomizeGroup(SeqType type, bool manual = true) {
|
||||
std::vector<u16> values;
|
||||
|
||||
|
|
@ -118,7 +120,7 @@ void RandomizeGroup(SeqType type, bool manual = true) {
|
|||
|
||||
uint32_t finalSeed = type + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed()
|
||||
: static_cast<uint32_t>(gSaveContext.ship.stats.fileCreatedAt));
|
||||
Random_Init(finalSeed);
|
||||
ShipUtils::RandInit(finalSeed, &seeded_audio_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +141,7 @@ void RandomizeGroup(SeqType type, bool manual = true) {
|
|||
if (!values.size())
|
||||
return;
|
||||
}
|
||||
Shuffle(values);
|
||||
ShipUtils::Shuffle(values, &seeded_audio_state);
|
||||
for (const auto& [seqId, seqData] : AudioCollection::Instance->GetAllSequences()) {
|
||||
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey);
|
||||
const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey);
|
||||
|
|
|
|||
|
|
@ -2104,6 +2104,8 @@ void ApplySideEffects(CosmeticOption& cosmeticOption) {
|
|||
}
|
||||
}
|
||||
|
||||
static uint64_t seeded_cosmetics_state = 0;
|
||||
|
||||
void RandomizeColor(CosmeticOption& cosmeticOption, bool manual = true) {
|
||||
ImVec4 randomColor;
|
||||
|
||||
|
|
@ -2115,7 +2117,7 @@ void RandomizeColor(CosmeticOption& cosmeticOption, bool manual = true) {
|
|||
(IS_RANDO ? Rando::Context::GetInstance()->GetSeed()
|
||||
: static_cast<uint32_t>(gSaveContext.ship.stats.fileCreatedAt));
|
||||
|
||||
randomColor = GetRandomValue(finalSeed);
|
||||
randomColor = GetRandomValue(finalSeed, &seeded_cosmetics_state);
|
||||
} else {
|
||||
randomColor = GetRandomValue();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,51 +5,25 @@
|
|||
#include <cassert>
|
||||
|
||||
static bool init = false;
|
||||
static uint64_t state = 0;
|
||||
uint64_t rando_state = 0;
|
||||
const uint64_t multiplier = 6364136223846793005ULL;
|
||||
const uint64_t increment = 11634580027462260723ULL;
|
||||
|
||||
// Initialize with seed specified
|
||||
void Random_Init(uint64_t seed) {
|
||||
init = true;
|
||||
state = seed;
|
||||
ShipUtils::RandInit(seed, &rando_state);
|
||||
}
|
||||
|
||||
uint32_t next32() {
|
||||
if (!init) {
|
||||
// No seed given, get a random number from device to seed
|
||||
#if !defined(__SWITCH__) && !defined(__WIIU__)
|
||||
uint64_t seed = static_cast<uint64_t>(std::random_device{}());
|
||||
#else
|
||||
uint64_t seed = static_cast<uint64_t>(std::hash<std::string>{}(std::to_string(rand())));
|
||||
#endif
|
||||
Random_Init(seed);
|
||||
}
|
||||
|
||||
state = state * multiplier + increment;
|
||||
uint32_t xorshifted = static_cast<uint32_t>(((state >> 18) ^ state) >> 27);
|
||||
uint32_t rot = static_cast<int>(state >> 59);
|
||||
return std::rotr(xorshifted, rot);
|
||||
return ShipUtils::next32(&rando_state);
|
||||
}
|
||||
|
||||
// Returns a random integer in range [min, max-1]
|
||||
uint32_t Random(uint32_t min, uint32_t max) {
|
||||
if (min == max) {
|
||||
return min;
|
||||
}
|
||||
assert(max > min);
|
||||
|
||||
uint32_t n = max - min;
|
||||
uint32_t cutoff = UINT32_MAX - UINT32_MAX % static_cast<uint32_t>(n);
|
||||
for (;;) {
|
||||
uint32_t r = next32();
|
||||
if (r <= cutoff) {
|
||||
return min + r % n;
|
||||
}
|
||||
}
|
||||
return ShipUtils::Random(min, max, &rando_state);
|
||||
}
|
||||
|
||||
// Returns a random floating point number in [0.0, 1.0)
|
||||
double RandomDouble() {
|
||||
return ldexp(next32(), -32);
|
||||
return ShipUtils::RandomDouble(&rando_state);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "soh/ShipUtils.h"
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
|
@ -7,37 +8,25 @@
|
|||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
extern uint64_t rando_state;
|
||||
|
||||
void Random_Init(uint64_t seed);
|
||||
uint32_t Random(uint32_t min, uint32_t max);
|
||||
double RandomDouble();
|
||||
|
||||
// Get a random element from a vector or array
|
||||
template <typename T> T RandomElement(std::vector<T>& vector, bool erase) {
|
||||
const auto idx = Random(0, static_cast<uint32_t>(vector.size()));
|
||||
const T selected = vector[idx];
|
||||
if (erase) {
|
||||
vector.erase(vector.begin() + idx);
|
||||
}
|
||||
return selected;
|
||||
return ShipUtils::RandomElement(vector, erase, &rando_state);
|
||||
}
|
||||
template <typename Container> auto& RandomElement(Container& container) {
|
||||
return container[Random(0, static_cast<uint32_t>(std::size(container)))];
|
||||
return ShipUtils::RandomElement(container, &rando_state);
|
||||
}
|
||||
template <typename Container> const auto& RandomElement(const Container& container) {
|
||||
return container[Random(0, static_cast<uint32_t>(std::size(container)))];
|
||||
return ShipUtils::RandomElement(container, &rando_state);
|
||||
}
|
||||
|
||||
template <typename T> const T RandomElementFromSet(const std::set<T>& set) {
|
||||
if (set.size() == 1) {
|
||||
return *set.begin();
|
||||
}
|
||||
uint32_t rand = Random(0, static_cast<uint32_t>(set.size()));
|
||||
auto it = set.begin();
|
||||
for (uint32_t i = 0; i < rand; i++) {
|
||||
it++;
|
||||
}
|
||||
auto test = *it;
|
||||
return *it;
|
||||
return ShipUtils::RandomElementFromSet(set, &rando_state);
|
||||
}
|
||||
|
||||
// Shuffle items within a vector or array
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "ShipUtils.h"
|
||||
#include <libultraship/libultraship.h>
|
||||
#include <random>
|
||||
#include "soh_assets.h"
|
||||
|
||||
extern "C" {
|
||||
|
|
@ -96,3 +97,60 @@ extern "C" void* Ship_GetCharFontTexture(u8 character) {
|
|||
|
||||
return (void*)fontTbl[adjustedChar];
|
||||
}
|
||||
|
||||
static bool rand_init = false;
|
||||
uint64_t default_state = 0;
|
||||
const uint64_t multiplier = 6364136223846793005ULL;
|
||||
const uint64_t increment = 11634580027462260723ULL;
|
||||
|
||||
// Initialize with seed specified
|
||||
void ShipUtils::RandInit(uint64_t seed, uint64_t* state) {
|
||||
rand_init = true;
|
||||
if (state == nullptr) {
|
||||
state = &default_state;
|
||||
}
|
||||
*state = seed;
|
||||
}
|
||||
|
||||
uint32_t ShipUtils::next32(uint64_t* state) {
|
||||
if (state == nullptr) {
|
||||
state = &default_state;
|
||||
}
|
||||
|
||||
if (!rand_init) {
|
||||
// No seed given, get a random number from device to seed
|
||||
#if !defined(__SWITCH__) && !defined(__WIIU__)
|
||||
uint64_t seed = static_cast<uint64_t>(std::random_device{}());
|
||||
#else
|
||||
uint64_t seed = static_cast<uint64_t>(rand());
|
||||
#endif
|
||||
ShipUtils::RandInit(seed, state);
|
||||
}
|
||||
|
||||
*state = *state * multiplier + increment;
|
||||
uint32_t xorshifted = static_cast<uint32_t>(((*state >> 18) ^ *state) >> 27);
|
||||
uint32_t rot = static_cast<int>(*state >> 59);
|
||||
return std::rotr(xorshifted, rot);
|
||||
}
|
||||
|
||||
// Returns a random integer in range [min, max-1]
|
||||
uint32_t ShipUtils::Random(uint32_t min, uint32_t max, uint64_t* state) {
|
||||
if (min == max) {
|
||||
return min;
|
||||
}
|
||||
assert(max > min);
|
||||
|
||||
uint32_t n = max - min;
|
||||
uint32_t cutoff = UINT32_MAX - UINT32_MAX % static_cast<uint32_t>(n);
|
||||
for (;;) {
|
||||
uint32_t r = next32(state);
|
||||
if (r <= cutoff) {
|
||||
return min + r % n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a random floating point number in [0.0, 1.0)
|
||||
double ShipUtils::RandomDouble(uint64_t* state) {
|
||||
return ldexp(next32(state), -32);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,53 @@ void* Ship_GetCharFontTexture(u8 character);
|
|||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
namespace ShipUtils {
|
||||
void RandInit(uint64_t seed, uint64_t* state = nullptr);
|
||||
uint32_t next32(uint64_t* state = nullptr);
|
||||
uint32_t Random(uint32_t min, uint32_t max, uint64_t* state = nullptr);
|
||||
double RandomDouble(uint64_t* state = nullptr);
|
||||
|
||||
// Get a random element from a vector or array
|
||||
template <typename T> T RandomElement(std::vector<T>& vector, bool erase, uint64_t* state = nullptr) {
|
||||
const auto idx = Random(0, static_cast<uint32_t>(vector.size()), state);
|
||||
const T selected = vector[idx];
|
||||
if (erase) {
|
||||
vector.erase(vector.begin() + idx);
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
template <typename Container> auto& RandomElement(Container& container, uint64_t* state = nullptr) {
|
||||
return container[Random(0, static_cast<uint32_t>(std::size(container)), state)];
|
||||
}
|
||||
template <typename Container> const auto& RandomElement(const Container& container, uint64_t* state = nullptr) {
|
||||
return container[Random(0, static_cast<uint32_t>(std::size(container)), state)];
|
||||
}
|
||||
|
||||
template <typename T> const T RandomElementFromSet(const std::set<T>& set, uint64_t* state = nullptr) {
|
||||
if (set.size() == 1) {
|
||||
return *set.begin();
|
||||
}
|
||||
uint32_t rand = Random(0, static_cast<uint32_t>(set.size()), state);
|
||||
auto it = set.begin();
|
||||
for (uint32_t i = 0; i < rand; i++) {
|
||||
it++;
|
||||
}
|
||||
auto test = *it;
|
||||
return *it;
|
||||
}
|
||||
|
||||
template <typename T> void Shuffle(std::vector<T>& vector, uint64_t* state = nullptr) {
|
||||
for (size_t i = 0; i + 1 < vector.size(); i++) {
|
||||
std::swap(vector[i], vector[Random(static_cast<uint32_t>(i), static_cast<uint32_t>(vector.size()), state)]);
|
||||
}
|
||||
}
|
||||
template <typename T, size_t size> void Shuffle(std::array<T, size>& arr, uint64_t* state = nullptr) {
|
||||
for (size_t i = 0; i + 1 < arr.size(); i++) {
|
||||
std::swap(arr[i], arr[Random(static_cast<uint32_t>(i), static_cast<uint32_t>(arr.size()), state)]);
|
||||
}
|
||||
}
|
||||
} // namespace ShipUtils
|
||||
#endif
|
||||
|
||||
#endif // SHIP_UTILS_H
|
||||
|
|
|
|||
|
|
@ -1154,34 +1154,19 @@ void DrawFlagArray8Mask(const std::string& name, uint8_t& flags, Colors color) {
|
|||
} // namespace UIWidgets
|
||||
|
||||
ImVec4 GetRandomValue() {
|
||||
#if !defined(__SWITCH__) && !defined(__WIIU__)
|
||||
std::random_device rd;
|
||||
std::mt19937 rng(rd());
|
||||
#else
|
||||
size_t seed = std::hash<std::string>{}(std::to_string(rand()));
|
||||
std::mt19937_64 rng(seed);
|
||||
#endif
|
||||
std::uniform_int_distribution<int> dist(0, 255 - 1);
|
||||
|
||||
ImVec4 NewColor;
|
||||
NewColor.x = (float)(dist(rng)) / 255.0f;
|
||||
NewColor.y = (float)(dist(rng)) / 255.0f;
|
||||
NewColor.z = (float)(dist(rng)) / 255.0f;
|
||||
NewColor.x = (float)ShipUtils::RandomDouble();
|
||||
NewColor.y = (float)ShipUtils::RandomDouble();
|
||||
NewColor.z = (float)ShipUtils::RandomDouble();
|
||||
return NewColor;
|
||||
}
|
||||
|
||||
ImVec4 GetRandomValue(uint32_t seed) {
|
||||
#if !defined(__SWITCH__) && !defined(__WIIU__)
|
||||
std::mt19937 rng(seed);
|
||||
#else
|
||||
std::mt19937_64 rng(seed);
|
||||
#endif
|
||||
std::uniform_int_distribution<int> dist(0, 255 - 1);
|
||||
|
||||
ImVec4 GetRandomValue(uint32_t seed, uint64_t* state) {
|
||||
ShipUtils::RandInit(seed, state);
|
||||
ImVec4 NewColor;
|
||||
NewColor.x = (float)(dist(rng)) / 255.0f;
|
||||
NewColor.y = (float)(dist(rng)) / 255.0f;
|
||||
NewColor.z = (float)(dist(rng)) / 255.0f;
|
||||
NewColor.x = (float)ShipUtils::RandomDouble(state);
|
||||
NewColor.y = (float)ShipUtils::RandomDouble(state);
|
||||
NewColor.z = (float)ShipUtils::RandomDouble(state);
|
||||
return NewColor;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1052,7 +1052,7 @@ void InsertHelpHoverText(const char* text);
|
|||
} // namespace UIWidgets
|
||||
|
||||
ImVec4 GetRandomValue();
|
||||
ImVec4 GetRandomValue(uint32_t seed);
|
||||
ImVec4 GetRandomValue(uint32_t seed, uint64_t* state = nullptr);
|
||||
|
||||
Color_RGBA8 RGBA8FromVec(ImVec4 vec);
|
||||
ImVec4 VecFromRGBA8(Color_RGBA8 color);
|
||||
|
|
|
|||
Loading…
Reference in a new issue