-
-
Save jacob1/e13d220d174f390777bf8b747c3ac64d to your computer and use it in GitHub Desktop.
AC:NH turnip price calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
bool calculate(int myPattern, int myBasePrice, int *myPrices); | |
// 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); | |
} | |
}; | |
bool TurnipPrices::calculate(int myPattern, int myBasePrice, int *myPrices) | |
{ | |
basePrice = randint(90, 110); | |
if (myBasePrice && basePrice != myBasePrice) | |
return false; | |
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; | |
} | |
} | |
if (myPattern && nextPattern != myPattern) | |
return false; | |
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); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
} | |
// decreasing phase 1 | |
rate = randfloat(0.8, 0.6); | |
for (int i = 0; i < decPhaseLen1; i++) | |
{ | |
sellPrices[work++] = intceil(rate * basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
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); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
} | |
// decreasing phase 2 | |
rate = randfloat(0.8, 0.6); | |
for (int i = 0; i < decPhaseLen2; i++) | |
{ | |
sellPrices[work++] = intceil(rate * basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
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); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
} | |
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); | |
//printf("mine %d, theirs %d\n", myPrices[work], sellPrices[work]); | |
if (myPrices[work] && sellPrices[work] != myPrices[work]) | |
return false; | |
rate -= 0.03; | |
rate -= randfloat(0, 0.02); | |
} | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
sellPrices[work++] = intceil(randfloat(1.4, 2.0) * basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
sellPrices[work++] = intceil(randfloat(2.0, 6.0) * basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
printf("mine %d, theirs %d\n", myPrices[work - 1], sellPrices[work - 1]); | |
sellPrices[work++] = intceil(randfloat(1.4, 2.0) * basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
for (; work < 14; work++) | |
{ | |
sellPrices[work] = intceil(randfloat(0.4, 0.9) * basePrice); | |
if (myPrices[work] && sellPrices[work] != myPrices[work]) | |
return false; | |
} | |
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); | |
if (myPrices[work] && sellPrices[work] != myPrices[work]) | |
return false; | |
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); | |
if (myPrices[work] && sellPrices[work] != myPrices[work]) | |
return false; | |
rate -= 0.03; | |
rate -= randfloat(0, 0.02); | |
} | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * (float)basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
sellPrices[work++] = intceil(randfloat(0.9, 1.4) * basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
rate = randfloat(1.4, 2.0); | |
sellPrices[work++] = intceil(randfloat(1.4, rate) * basePrice) - 1; | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
sellPrices[work++] = intceil(rate * basePrice); | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
sellPrices[work++] = intceil(randfloat(1.4, rate) * basePrice) - 1; | |
if (myPrices[work - 1] && sellPrices[work - 1] != myPrices[work - 1]) | |
return false; | |
// decreasing phase after the peak | |
if (work < 14) | |
{ | |
rate = randfloat(0.9, 0.4); | |
for (; work < 14; work++) | |
{ | |
sellPrices[work] = intceil(rate * basePrice); | |
if (myPrices[work] && sellPrices[work] != myPrices[work]) | |
return false; | |
rate -= 0.03; | |
rate -= randfloat(0, 0.02); | |
} | |
} | |
break; | |
} | |
sellPrices[0] = 0; | |
sellPrices[1] = 0; | |
return true; | |
} | |
int main(int argc, char **argv) | |
{ | |
TurnipPrices turnips; | |
int myPattern = 0; | |
int myBasePrice = 0; | |
int *myPrices = new int[14]; | |
if (argc >= 2) | |
{ | |
turnips.whatPattern = atoi(argv[1]); | |
//turnips.rng.init(atoi(argv[2])); | |
} | |
else | |
{ | |
printf("Usage: %s <last week's pattern> <current pattern> <sunday sell price> <space delimited prices by day>\n" | |
"0 if unknown for any of these. Prices by day default to 0\n", argv[0]); | |
return 0; | |
} | |
// Known issue where if your pattern is '0', it will check all patterns because it thinks you put "unknown" | |
// But the bruteforcer doesn't work anyway so whatever | |
if (argc >= 3) | |
myPattern = atoi(argv[2]); | |
if (argc >= 4) | |
myBasePrice = atoi(argv[3]); | |
for (int i = 4; i < argc && i < 17; i++) | |
myPrices[i - 2] = atoi(argv[i]); | |
printf("Pattern %d:\n", myPattern); | |
printf("Sun Mon Tue Wed Thu Fri Sat\n"); | |
printf("%3d %3d %3d %3d %3d %3d %3d\n" | |
" %3d %3d %3d %3d %3d %3d\n", | |
myBasePrice, myPrices[2], myPrices[4], myPrices[6], myPrices[8], myPrices[10], myPrices[12], | |
myPrices[3], myPrices[5], myPrices[7], myPrices[9], myPrices[11], myPrices[13]); | |
for (uint32_t i = 0; i < UINT32_MAX; i++) | |
{ | |
turnips.rng.init(i); | |
if (i % 100000000 == 0) | |
printf("Calculating %u ...\n", i); | |
if (turnips.calculate(myPattern, myBasePrice, myPrices)) | |
{ | |
printf("Pattern %d, Seed %u:\n", turnips.whatPattern, i); | |
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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment