#include #include #include #include #include #include #include class RNG { public: RNG() : state{0, 0, 0, 0} { } RNG(uint32_t seed) { reseed(seed); } void reseed(uint32_t seed) { state[0] = 0x6C078965 * (seed ^ (seed >> 30)) + 1; state[1] = 0x6C078965 * (state[0] ^ (state[0] >> 30)) + 2; state[2] = 0x6C078965 * (state[1] ^ (state[1] >> 30)) + 3; state[3] = 0x6C078965 * (state[2] ^ (state[2] >> 30)) + 4; } inline uint32_t getRandomUint32() { uint32_t n = state[0] ^ (state[0] << 11); state[0] = state[1]; state[1] = state[2]; state[2] = state[3]; state[3] = n ^ (n >> 8) ^ state[3] ^ (state[3] >> 19); return state[3]; } inline bool getRandomBoolean() { return ((getRandomUint32() & 0x80000000) != 0); } inline int getRandomInt(int min, int max) { return ((static_cast(getRandomUint32()) * static_cast(max - min + 1)) >> 32) + min; } inline float getRandomFloat(float min, float max) { uint32_t val = 0x3F800000 | (getRandomUint32() >> 9); float fval = reinterpret_cast(val); return min + ((fval - 1.0f) * (max - min)); } protected: uint32_t state[4]; }; inline int intCeil(float x) { return static_cast(x + 0.99999f); } class Turnips { public: static const size_t numberOfPatterns = 4; static const size_t numberOfHalfDays = 14; static const size_t numberOfPrices = 700; inline bool computePrices(uint32_t prevPattern, RNG &rng, uint32_t userPattern, const int32_t userPrices[numberOfHalfDays], uint32_t &pattern, int32_t prices[numberOfHalfDays], const bool takeSundayPriceFromInput = false, const int32_t tol = 0) __attribute__((always_inline)) { if (takeSundayPriceFromInput) { prices[0] = userPrices[0]; prices[1] = userPrices[1]; } else { prices[0] = rng.getRandomInt(90, 110); if ((userPrices[0] > 0) && (std::abs(prices[0] - userPrices[0])) > tol) return false; prices[1] = prices[0]; } x = rng.getRandomInt(0, 99); if (prevPattern == 0) { if (x < 20) pattern = 0; else if (x < 50) pattern = 1; else if (x < 65) pattern = 2; else pattern = 3; } else if (prevPattern == 1) { if (x < 50) pattern = 0; else if (x < 55) pattern = 1; else if (x < 75) pattern = 2; else pattern = 3; } else if (prevPattern == 2) { if (x < 25) pattern = 0; else if (x < 70) pattern = 1; else if (x < 75) pattern = 2; else pattern = 3; } else { if (x < 45) pattern = 0; else if (x < 70) pattern = 1; else if (x < 85) pattern = 2; else pattern = 3; } if ((userPattern < numberOfPatterns) && (pattern != userPattern)) return false; if (pattern == 0) { // PATTERN 0 - "Random": high, decreasing, high, decreasing, high decPhaseLen1 = (rng.getRandomBoolean() ? 3 : 2); decPhaseLen2 = 5 - decPhaseLen1; hiPhaseLen1 = rng.getRandomInt(0, 6); hiPhaseLen2And3 = 7 - hiPhaseLen1; hiPhaseLen3 = rng.getRandomInt(0, hiPhaseLen2And3 - 1); t = 2; // high phase 1 for (size_t i = 0; i < hiPhaseLen1; i++) { prices[t++] = intCeil(rng.getRandomFloat(0.9, 1.4) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; } // decreasing phase 1 rate = rng.getRandomFloat(0.8, 0.6); for (size_t i = 0; i < decPhaseLen1; i++) { prices[t++] = intCeil(rate * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; rate -= 0.04; rate -= rng.getRandomFloat(0, 0.06); } // high phase 2 for (size_t i = 0; i < (hiPhaseLen2And3 - hiPhaseLen3); i++) { prices[t++] = intCeil(rng.getRandomFloat(0.9, 1.4) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; } // decreasing phase 2 rate = rng.getRandomFloat(0.8, 0.6); for (size_t i = 0; i < decPhaseLen2; i++) { prices[t++] = intCeil(rate * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; rate -= 0.04; rate -= rng.getRandomFloat(0, 0.06); } // high phase 3 for (size_t i = 0; i < hiPhaseLen3; i++) { prices[t++] = intCeil(rng.getRandomFloat(0.9, 1.4) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; } } else if (pattern == 1) { // PATTERN 1 - "Large Spike": decreasing middle, high spike, random low peakStart = rng.getRandomInt(3, 9); rate = rng.getRandomFloat(0.9, 0.85); for (t = 2; t < peakStart; t++) { prices[t] = intCeil(rate * prices[0]); if ((userPrices[t] > 0) && (std::abs(prices[t] - userPrices[t]) > tol)) return false; rate -= 0.03; rate -= rng.getRandomFloat(0, 0.02); } prices[t++] = intCeil(rng.getRandomFloat(0.9, 1.4) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; prices[t++] = intCeil(rng.getRandomFloat(1.4, 2.0) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; prices[t++] = intCeil(rng.getRandomFloat(2.0, 6.0) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; prices[t++] = intCeil(rng.getRandomFloat(1.4, 2.0) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; prices[t++] = intCeil(rng.getRandomFloat(0.9, 1.4) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; for (; t < numberOfHalfDays; t++) { prices[t] = intCeil(rng.getRandomFloat(0.4, 0.9) * prices[0]); if ((userPrices[t] > 0) && (std::abs(prices[t] - userPrices[t]) > tol)) return false; } } else if (pattern == 2) { // PATTERN 2 - "Decreasing": consistently decreasing rate = 0.9; rate -= rng.getRandomFloat(0, 0.05); for (size_t t = 2; t < numberOfHalfDays; t++) { prices[t] = intCeil(rate * prices[0]); if ((userPrices[t] > 0) && (std::abs(prices[t] - userPrices[t]) > tol)) return false; rate -= 0.03; rate -= rng.getRandomFloat(0, 0.02); } } else { // PATTERN 3 - "Small Spike": decreasing, spike, decreasing peakStart = rng.getRandomInt(2, 9); // decreasing phase before the peak rate = rng.getRandomFloat(0.9, 0.4); for (t = 2; t < peakStart; t++) { prices[t] = intCeil(rate * prices[0]); if ((userPrices[t] > 0) && (std::abs(prices[t] - userPrices[t]) > tol)) return false; rate -= 0.03; rate -= rng.getRandomFloat(0, 0.02); } prices[t++] = intCeil(rng.getRandomFloat(0.9, 1.4) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; prices[t++] = intCeil(rng.getRandomFloat(0.9, 1.4) * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; rate = rng.getRandomFloat(1.4, 2.0); prices[t++] = intCeil(rng.getRandomFloat(1.4, rate) * prices[0]) - 1; if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; prices[t++] = intCeil(rate * prices[0]); if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; prices[t++] = intCeil(rng.getRandomFloat(1.4, rate) * prices[0]) - 1; if ((userPrices[t-1] > 0) && (std::abs(prices[t-1] - userPrices[t-1]) > tol)) return false; // decreasing phase after the peak if (t < numberOfHalfDays) { rate = rng.getRandomFloat(0.9, 0.4); for (; t < numberOfHalfDays; t++) { prices[t] = intCeil(rate * prices[0]); if ((userPrices[t] > 0) && (std::abs(prices[t] - userPrices[t]) > tol)) return false; rate -= 0.03; rate -= rng.getRandomFloat(0, 0.02); } } } return true; } protected: int x; size_t decPhaseLen1; size_t decPhaseLen2; size_t hiPhaseLen1; size_t hiPhaseLen2And3; size_t hiPhaseLen3; float rate; size_t t; size_t peakStart; }; void compute(int argc, char *argv[]) { if (argc != 4) { throw std::runtime_error("wrong number of arguments"); } const uint32_t seed = std::stoi(argv[2]); const uint32_t prevPattern = std::stoi(argv[3]); uint32_t pattern; int32_t prices[Turnips::numberOfHalfDays]; RNG rng(seed); const uint32_t userPattern = 9999; const int32_t userPrices[Turnips::numberOfHalfDays] = {}; Turnips turnips; turnips.computePrices(prevPattern, rng, userPattern, userPrices, pattern, prices); std::cout << pattern; for (size_t t = 0; t < Turnips::numberOfHalfDays; t++) std::cout << " " << prices[t]; std::cout << std::endl; } void bruteForce(int argc, char *argv[]) { if (argc != 20) { throw std::runtime_error("wrong number of arguments"); } const size_t numberOfWorkers = std::stoi(argv[2]); const size_t workerId = std::stoi(argv[3]); const uint32_t minSeed = static_cast(static_cast(workerId) * static_cast(0xFFFFFFFF) / static_cast(numberOfWorkers)); const uint32_t maxSeed = (((numberOfWorkers > 1) && (workerId < numberOfWorkers - 1)) ? static_cast((static_cast(workerId) + 1.0) * static_cast(0xFFFFFFFF) / static_cast(numberOfWorkers)) : 0); const uint64_t numberOfSeeds = static_cast(maxSeed - 1) - static_cast(minSeed) + 1; const uint32_t userPrevPattern = std::stoi(argv[4]); const uint32_t userPattern = std::stoi(argv[5]); int32_t userPrices[Turnips::numberOfHalfDays]; for (size_t t = 0; t < Turnips::numberOfHalfDays; t++) { userPrices[t] = std::stoi(argv[t + 6]); } const uint32_t minPrevPattern = ((userPrevPattern < Turnips::numberOfPatterns) ? userPrevPattern : 0); const uint32_t maxPrevPattern = ((userPrevPattern < Turnips::numberOfPatterns) ? (userPrevPattern + 1) : Turnips::numberOfPatterns); uint64_t numberOfMatches = 0; std::cerr << std::fixed << std::setprecision(3); uint32_t pattern; int32_t prices[Turnips::numberOfHalfDays]; RNG rng; Turnips turnips; for (uint32_t seed = minSeed; seed != maxSeed; seed++) { if ((seed % 2000000 == 0) && (seed != minSeed)) { const uint32_t seedsProcessed = seed - minSeed; const double seedsProcessedPercentage = 100.0 * static_cast(seedsProcessed) / static_cast(numberOfSeeds); const uint64_t numberOfPossibleMatches = static_cast(maxPrevPattern - minPrevPattern) * seedsProcessed; const double matchesPercentage = 100.0 * static_cast(numberOfMatches) / static_cast(numberOfPossibleMatches); std::cerr << "Worker " << workerId << ": " << seedsProcessed << "/" << numberOfSeeds << " processed (" << seedsProcessedPercentage << "%), " << numberOfMatches << "/" << numberOfPossibleMatches << " matches (" << matchesPercentage << "%)" << std::endl; } for (uint32_t prevPattern = minPrevPattern; prevPattern < maxPrevPattern; prevPattern++) { rng.reseed(seed); if (turnips.computePrices(prevPattern, rng, userPattern, userPrices, pattern, prices)) { std::cout << seed << " " << prevPattern << " " << pattern; for (size_t t = 0; t < Turnips::numberOfHalfDays; t++) std::cout << " " << prices[t]; std::cout << std::endl; numberOfMatches++; } } } } template std::ostream& operator<<(std::ostream& stream, const std::vector vector) { for (size_t i = 0; i < vector.size(); i++) { if (i > 0) stream << " "; stream << vector[i]; } return stream; } void sample(int argc, char *argv[]) { if (argc != 22) { throw std::runtime_error("wrong number of arguments"); } const std::string sampleMode(argv[2]); const bool verbose = (sampleMode == "verbose"); const size_t numberOfSamples = std::stoi(argv[3]); const size_t minimumNumberOfMatches = std::stoi(argv[4]); const uint32_t seed = std::stoi(argv[5]); const uint32_t userPrevPattern = std::stoi(argv[6]); const uint32_t userPattern = std::stoi(argv[7]); int32_t userPrices[Turnips::numberOfHalfDays]; size_t now = 2; for (size_t t = 0; t < Turnips::numberOfHalfDays; t++) { userPrices[t] = std::stoi(argv[t + 8]); if (userPrices[t] > 0) now = t + 1; } uint64_t numberOfMatches = 0; std::cerr << std::fixed << std::setprecision(3); size_t i = 0; uint32_t prevPattern = userPrevPattern; const bool takeSundayPriceFromInput = (userPrices[0] > 0); const bool randomPrevPattern = (userPrevPattern >= Turnips::numberOfPatterns); uint32_t pattern; int32_t prices[Turnips::numberOfHalfDays]; const int32_t tol = 1; RNG rng(seed); Turnips turnips; double x; std::vector prevPatternCounts(Turnips::numberOfPatterns, 0); std::vector patternCounts(Turnips::numberOfPatterns, 0); std::vector> pricesHistogram(Turnips::numberOfHalfDays, std::vector(Turnips::numberOfPrices, 0)); std::vector maxPriceHistogram(Turnips::numberOfPrices, 0); while ((i < numberOfSamples) || ((minimumNumberOfMatches == 0) || (numberOfMatches < minimumNumberOfMatches))) { if ((i % 2000000 == 0) && (i > 0)) { const double processedPercentage = 100.0 * static_cast(i) / static_cast(numberOfSamples); const double matchesPercentage = 100.0 * static_cast(numberOfMatches) / static_cast(i); std::cerr << "Seed " << seed << ": " << i << "/" << numberOfSamples << " processed (" << processedPercentage << "%), " << numberOfMatches << "/" << i << " matches (" << matchesPercentage << "%)" << std::endl; } if (randomPrevPattern) { x = rng.getRandomFloat(0.0, 1.0); if (x < 0.34627733) prevPattern = 0; else if (x < 0.59364012) prevPattern = 1; else if (x < 0.74124752) prevPattern = 2; else prevPattern = 3; } if (turnips.computePrices(prevPattern, rng, userPattern, userPrices, pattern, prices, takeSundayPriceFromInput, tol)) { numberOfMatches++; if (verbose) { std::cout << prevPattern << " " << pattern; for (size_t t = 0; t < Turnips::numberOfHalfDays; t++) std::cout << " " << prices[t]; std::cout << std::endl; } else { prevPatternCounts[prevPattern]++; patternCounts[pattern]++; for (size_t t = 0; t < Turnips::numberOfHalfDays; t++) { pricesHistogram[t][prices[t]]++; } if (now < Turnips::numberOfHalfDays) { maxPriceHistogram[*std::max_element(std::begin(prices) + now, std::end(prices))]++; } } } i++; } if (!verbose) { std::cout << numberOfMatches << " " << prevPatternCounts << " " << patternCounts << " " << pricesHistogram << " " << maxPriceHistogram << std::endl; } } int main(int argc, char *argv[]) { if (argc < 2) { throw std::runtime_error("missing mode"); } const std::string mode(argv[1]); if (mode == "compute") { // turnips compute compute(argc, argv); } else if (mode == "bruteForce") { // turnips bruteForce bruteForce(argc, argv); } else if (mode == "sample") { // turnips sample // sample(argc, argv); } else { throw std::runtime_error("invalid mode"); } return 0; }