-
-
Save Treeki/85be14d297c80c8b3c0a76375743325b to your computer and use it in GitHub Desktop.
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
// munged from https://github.com/simontime/Resead | |
namespace sead | |
{ | |
class Random | |
{ | |
public: | |
void init(); | |
void init(uint32_t seed); | |
void init(uint32_t seed1, uint32_t seed2, uint32_t seed3, uint32_t seed4); | |
uint32_t getU32(); | |
uint64_t getU64(); | |
void getContext(uint32_t *seed1, uint32_t *seed2, uint32_t *seed3, uint32_t *seed4) const; | |
private: | |
uint32_t mContext[4]; | |
}; | |
void Random::init() | |
{ | |
init(42069); | |
} | |
void Random::init(uint32_t seed) | |
{ | |
mContext[0] = 0x6C078965 * (seed ^ (seed >> 30)) + 1; | |
mContext[1] = 0x6C078965 * (mContext[0] ^ (mContext[0] >> 30)) + 2; | |
mContext[2] = 0x6C078965 * (mContext[1] ^ (mContext[1] >> 30)) + 3; | |
mContext[3] = 0x6C078965 * (mContext[2] ^ (mContext[2] >> 30)) + 4; | |
} | |
void Random::init(uint32_t seed1, uint32_t seed2, uint32_t seed3, uint32_t seed4) | |
{ | |
if ((seed1 | seed2 | seed3 | seed4) == 0) // seeds must not be all zero. | |
{ | |
seed1 = 1; | |
seed2 = 0x6C078967; | |
seed3 = 0x714ACB41; | |
seed4 = 0x48077044; | |
} | |
mContext[0] = seed1; | |
mContext[1] = seed2; | |
mContext[2] = seed3; | |
mContext[3] = seed4; | |
} | |
uint32_t Random::getU32() | |
{ | |
uint32_t n = mContext[0] ^ (mContext[0] << 11); | |
mContext[0] = mContext[1]; | |
mContext[1] = mContext[2]; | |
mContext[2] = mContext[3]; | |
mContext[3] = n ^ (n >> 8) ^ mContext[3] ^ (mContext[3] >> 19); | |
return mContext[3]; | |
} | |
uint64_t Random::getU64() | |
{ | |
uint32_t n1 = mContext[0] ^ (mContext[0] << 11); | |
uint32_t n2 = mContext[1]; | |
uint32_t n3 = n1 ^ (n1 >> 8) ^ mContext[3]; | |
mContext[0] = mContext[2]; | |
mContext[1] = mContext[3]; | |
mContext[2] = n3 ^ (mContext[3] >> 19); | |
mContext[3] = n2 ^ (n2 << 11) ^ ((n2 ^ (n2 << 11)) >> 8) ^ mContext[2] ^ (n3 >> 19); | |
return ((uint64_t)mContext[2] << 32) | mContext[3]; | |
} | |
void Random::getContext(uint32_t *seed1, uint32_t *seed2, uint32_t *seed3, uint32_t *seed4) const | |
{ | |
*seed1 = mContext[0]; | |
*seed2 = mContext[1]; | |
*seed3 = mContext[2]; | |
*seed4 = mContext[3]; | |
} | |
} // namespace sead | |
uint32_t pf(float f) { | |
return *((uint32_t *)&f); | |
} | |
struct TurnipPrices | |
{ | |
int32_t basePrice; | |
int32_t sellPrices[14]; | |
uint32_t whatPattern; | |
int32_t tmp40; | |
void calculate(); | |
// utility stuff for testing | |
sead::Random rng; | |
bool randbool() | |
{ | |
return rng.getU32() & 0x80000000; | |
} | |
int randint(int min, int max) | |
{ | |
return (((uint64_t)rng.getU32() * (uint64_t)(max - min + 1)) >> 32) + min; | |
} | |
float randfloat(float a, float b) | |
{ | |
uint32_t val = 0x3F800000 | (rng.getU32() >> 9); | |
float fval = *(float *)(&val); | |
return a + ((fval - 1.0f) * (b - a)); | |
} | |
int intceil(float val) | |
{ | |
return (int)(val + 0.99999f); | |
} | |
}; | |
void TurnipPrices::calculate() | |
{ | |
basePrice = randint(90, 110); | |
int chance = randint(0, 99); | |
// select the next pattern | |
int nextPattern; | |
if (whatPattern >= 4) | |
{ | |
nextPattern = 2; | |
} | |
else | |
{ | |
switch (whatPattern) | |
{ | |
case 0: | |
if (chance < 20) | |
{ | |
nextPattern = 0; | |
} | |
else if (chance < 50) | |
{ | |
nextPattern = 1; | |
} | |
else if (chance < 65) | |
{ | |
nextPattern = 2; | |
} | |
else | |
{ | |
nextPattern = 3; | |
} | |
break; | |
case 1: | |
if (chance < 50) | |
{ | |
nextPattern = 0; | |
} | |
else if (chance < 55) | |
{ | |
nextPattern = 1; | |
} | |
else if (chance < 75) | |
{ | |
nextPattern = 2; | |
} | |
else | |
{ | |
nextPattern = 3; | |
} | |
break; | |
case 2: | |
if (chance < 25) | |
{ | |
nextPattern = 0; | |
} | |
else if (chance < 70) | |
{ | |
nextPattern = 1; | |
} | |
else if (chance < 75) | |
{ | |
nextPattern = 2; | |
} | |
else | |
{ | |
nextPattern = 3; | |
} | |
break; | |
case 3: | |
if (chance < 45) | |
{ | |
nextPattern = 0; | |
} | |
else if (chance < 70) | |
{ | |
nextPattern = 1; | |
} | |
else if (chance < 85) | |
{ | |
nextPattern = 2; | |
} | |
else | |
{ | |
nextPattern = 3; | |
} | |
break; | |
} | |
} | |
whatPattern = nextPattern; | |
/* | |
if (checkGlobalFlag("FirstKabuBuy")) { | |
if (!checkGlobalFlag("FirstKabuPattern")) { | |
setGlobalFlag("FirstKabuPattern", true); | |
whatPattern = 3; | |
} | |
} | |
*/ | |
for (int i = 2; i < 14; i++) | |
sellPrices[i] = 0; | |
sellPrices[0] = basePrice; | |
sellPrices[1] = basePrice; | |
int work; | |
int decPhaseLen1, decPhaseLen2, peakStart; | |
int hiPhaseLen1, hiPhaseLen2and3, hiPhaseLen3; | |
float rate; | |
switch (whatPattern) | |
{ | |
case 0: | |
// PATTERN 0: high, decreasing, high, decreasing, high | |
work = 2; | |
decPhaseLen1 = randbool() ? 3 : 2; | |
decPhaseLen2 = 5 - decPhaseLen1; | |
hiPhaseLen1 = randint(0, 6); | |
hiPhaseLen2and3 = 7 - hiPhaseLen1; | |
hiPhaseLen3 = randint(0, hiPhaseLen2and3 - 1); | |
// high phase 1 | |
for (int i = 0; i < hiPhaseLen1; i++) | |
{ | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * basePrice); | |
} | |
// decreasing phase 1 | |
rate = randfloat(0.8, 0.6); | |
for (int i = 0; i < decPhaseLen1; i++) | |
{ | |
sellPrices[work++] = intceil(rate * basePrice); | |
rate -= 0.04; | |
rate -= randfloat(0, 0.06); | |
} | |
// high phase 2 | |
for (int i = 0; i < (hiPhaseLen2and3 - hiPhaseLen3); i++) | |
{ | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * basePrice); | |
} | |
// decreasing phase 2 | |
rate = randfloat(0.8, 0.6); | |
for (int i = 0; i < decPhaseLen2; i++) | |
{ | |
sellPrices[work++] = intceil(rate * basePrice); | |
rate -= 0.04; | |
rate -= randfloat(0, 0.06); | |
} | |
// high phase 3 | |
for (int i = 0; i < hiPhaseLen3; i++) | |
{ | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * basePrice); | |
} | |
break; | |
case 1: | |
// PATTERN 1: decreasing middle, high spike, random low | |
peakStart = randint(3, 9); | |
rate = randfloat(0.9, 0.85); | |
for (work = 2; work < peakStart; work++) | |
{ | |
sellPrices[work] = intceil(rate * basePrice); | |
rate -= 0.03; | |
rate -= randfloat(0, 0.02); | |
} | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * basePrice); | |
sellPrices[work++] = intceil(randfloat(1.4, 2.0) * basePrice); | |
sellPrices[work++] = intceil(randfloat(2.0, 6.0) * basePrice); | |
sellPrices[work++] = intceil(randfloat(1.4, 2.0) * basePrice); | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * basePrice); | |
for (; work < 14; work++) | |
{ | |
sellPrices[work] = intceil(randfloat(0.4, 0.9) * basePrice); | |
} | |
break; | |
case 2: | |
// PATTERN 2: consistently decreasing | |
rate = 0.9; | |
rate -= randfloat(0, 0.05); | |
for (work = 2; work < 14; work++) | |
{ | |
sellPrices[work] = intceil(rate * basePrice); | |
rate -= 0.03; | |
rate -= randfloat(0, 0.02); | |
} | |
break; | |
case 3: | |
// PATTERN 3: decreasing, spike, decreasing | |
peakStart = randint(2, 9); | |
// decreasing phase before the peak | |
rate = randfloat(0.9, 0.4); | |
for (work = 2; work < peakStart; work++) | |
{ | |
sellPrices[work] = intceil(rate * basePrice); | |
rate -= 0.03; | |
rate -= randfloat(0, 0.02); | |
} | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * (float)basePrice); | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * basePrice); | |
rate = randfloat(1.4, 2.0); | |
sellPrices[work++] = intceil(randfloat(1.4, rate) * basePrice) - 1; | |
sellPrices[work++] = intceil(rate * basePrice); | |
sellPrices[work++] = intceil(randfloat(1.4, rate) * basePrice) - 1; | |
// decreasing phase after the peak | |
if (work < 14) | |
{ | |
rate = randfloat(0.9, 0.4); | |
for (; work < 14; work++) | |
{ | |
sellPrices[work] = intceil(rate * basePrice); | |
rate -= 0.03; | |
rate -= randfloat(0, 0.02); | |
} | |
} | |
break; | |
} | |
sellPrices[0] = 0; | |
sellPrices[1] = 0; | |
} | |
int main(int argc, char **argv) | |
{ | |
TurnipPrices turnips; | |
if (argc == 3) | |
{ | |
turnips.whatPattern = atoi(argv[1]); | |
turnips.rng.init(atoi(argv[2])); | |
} | |
else | |
{ | |
printf("Usage: %s <pattern> <seed>\n", argv[0]); | |
return 0; | |
} | |
turnips.calculate(); | |
printf("Pattern %d:\n", turnips.whatPattern); | |
printf("Sun Mon Tue Wed Thu Fri Sat\n"); | |
printf("%3d %3d %3d %3d %3d %3d %3d\n", | |
turnips.basePrice, | |
turnips.sellPrices[2], turnips.sellPrices[4], turnips.sellPrices[6], | |
turnips.sellPrices[8], turnips.sellPrices[10], turnips.sellPrices[12]); | |
printf(" %3d %3d %3d %3d %3d %3d\n", | |
turnips.sellPrices[3], turnips.sellPrices[5], turnips.sellPrices[7], | |
turnips.sellPrices[9], turnips.sellPrices[11], turnips.sellPrices[13]); | |
return 0; | |
} |
whatever pattern you give to the program, it still may get changed to a different pattern due to random number (according to the seed) (line 129), and if the final pattern is a 1
, that is the high spike pattern. See line 233.
Example:
$ ./a.out 0 326
Pattern 1:
Sun Mon Tue Wed Thu Fri Sat
110 96 137 660 152 65 82
91 200 180 91 63 80
$ ./a.out 0 291879
Pattern 1:
Sun Mon Tue Wed Thu Fri Sat
110 99 181 186 50 75 68
106 660 120 86 60 55
For folks not familiar with the following C++ code:
int randint(int min, int max)
{
return (((uint64_t)rng.getU32() * (uint64_t)(max - min + 1)) >> 32) + min;
}
float randfloat(float a, float b)
{
uint32_t val = 0x3F800000 | (rng.getU32() >> 9);
float fval = *(float *)(&val);
return a + ((fval - 1.0f) * (b - a));
}
note that randint(a, b)
returns something inclusive of a
and b
, and randfloat(a, b)
returns something inclusive of a
but exclusive of b
.
You will see code which is randfloat(0.9, 0.85)
, and it is almost the same as randfloat(0.85, 0.9)
, except the first case is exclusive 0.85
, while the second case is exclusive 0.9
.
someone knows the mathematical formula necessary in this program? I want to do a mathematical investigation around Turnip Prophet, but I don't really understand about programming
I used this code to build a simulator sampling the market for 1,000,000 weeks:
I used this code to build a simulator sampling the market for 1,000,000 weeks:
Pretty cool! I am glad to report that my own simulation agrees with your Wednesday morning strategy.
I like how you made an analysis of the return as a function of how many friends you have. Nice work!
This is really cool info. I have a different use case: I time travel to various Wednesdays to look for prices greater than 300. When I find that price, I'll use a second switch and ACNH to go to Sunday, buy tons of turnips, and fly to the first switch and sell the turnips. Very nice profit. However the big thing is to find the Wednesday via time travel. It can take hours to find the right day and time. I've used the MeteoNook to find exact times when meteors show up, and was wondering if a similar algorithm could tell me the exact day and time, or at least narrow it down a bit. I'm guessing not, since there is that random function, but thought I'd check around.
The surprising thing is, you can go into a Fortune 500 or Fortune 50 tech company, and it is still a Wendy's.