How to manipulate the RNG and achieve Perfection in 61 days (2024)

When I looked through the content that was added in the 1.6 update, the one thing that stood out to me was the Queen Of Sauce Cookbook, because of what it would mean for how quickly you can achieve Perfection.

In 1.5, you were always limited by learning all the cooking recipes, which takes until end of year 2. It is very doable to complete all other Perfection requirements well before that point, so the question of how quickly you could achieve Perfection was not that interesting. In fact, players like @TheHaboo have completed it in half the time, by starting in year 2.

But the Queen Of Sauce Cookbook changes everything. Without being limited by learning the recipes through the TV each week, you can achieve Perfection much earlier, namely after Spring 2, Year 2. You are now limited by the Special Orders, of which you need to complete 9 to learn all crafting recipes, so you learn the last crafting recipe on Spring 2, Year 2 in the mail. This by itself would be a much harder goal than it was in 1.5, given the new content means more things to do, like unlocking all Masteries.

However, we can go further. Taking @TheHaboo's concept of choosing a different starting date, we can optimize for the absolute minimum number of days you need to theoretically achieve Perfection.
As it turns out, the limiting factor ends up being the festivals throughout the year. There are exactly three festivals which are required: The Flower Dance on Spring 24, The Stardew Valley Fair on Fall 16, and the Spirit's Eve on Fall 27. All of them offer unique items or recipes that are critical to achieving Perfection, namely the Rarecrows, a Stardrop and crafting recipes for the Tub o'Flowers and Jack-O-Lantern. The shortest span of time which contains all three dates is 60 days, starting on Spring 24, and ending on Fall 27. However, you'll always need one extra day because the Deluxe Scarecrow recipe is deliviered to you only the day after, on Fall 28, in the mail. This brings us to a theoretical minimum of 61 days to achieve Perfection start to finish.

Actually being able to complete Perfection in only 61 days would require a lot of planning, and even more imortantly: luck.

Understanding randomness in Stardew Valley

Stardew Valley contains a lot of random events: Help Wanted quests, mine floors, monster loot, fishing treasures, artifact spots, the Traveling Cart, nighttime events, geodes, Golden Walnuts, and a lot more is governed by RNG. There exist multiple tools to predict and exploit the randomness, like @MouseyPounds' Stardew Predictor, and @BlaDe's Map Predictor.

Randomness in Stardew Valley generally comes in two flavors, seeded and unseeded.

Seeded randomness

A lot of the random events are seeded, which means their outcomes are generated by a PRNG with a known starting value that is derived from your game state. A central component to this game state is each game's unique ID, which is assigned when a new game is started.

For example, when the game determines which items are available in the Traveling Cart, it takes the game's ID and the current day, computes a hash from those values, and uses that hash as the seed for a new PRNG. That PRNG is then used to roll the cart items. This means by knowing these two initial values, you can recreate the process that the game goes through and predict what the items in the Traveling Cart will be for any game at any day. This is exactly what @MouseyPounds' and @BlaDe's Predictors do for you.

One important "feature" of the game's unique ID is that you can change it for an existing save. This is usually referred to as "dynamic seeding", and involves going to the New Game menu and entering a custom seed, before backing out and loading your save. This allows some control over the seeded RNG outcomes, and is very effective e.g. when looking for specific items in the Traveling Cart, or certain weather on a specific day. You can only do this once per in-game day though, so for each day you need to find an ID that satisfies all your requirements for the day at once.

Unseeded randomness

On the other hand, there are unseeded random events, which are unpredicatble and give you a different outcome every time (*1). For example, no predictor can tell you which fish you are going to catch if you use your fishing rod. Internally, all these unpredicatble events draw from a global shared PRNG called Game1.random and influence each other. That means while the game is running, everything calls this PRNG constantly: The critters in the background, the directions and speeds of any particles or debris, the blinking animation of the characters. So when a random event like the drops when cutting some Weeds is rolled with that same RNG, it could be in any state, and return any value, and repeating the same event is likely to give you a different outcome.

Conquering unseeded randomness

So the question is, can you manipulate the unseeded events? In theory: yes, you can just wait until a desirable outcome comes around, and trigger the random event then. In practice, this is infeasible for a human to do, because you would need perfect knowledge about what the RNG outcome would be at any moment, and frame-perfect precision to hit the window of opportunity where the RNG aligns in your favor. For a tool-assisted run, this is very possible though.

Importantly, manipulating the unseeded events like this wouldn't even take up any in-game time, because the global RNG still advances while the time of day is paused, like for the character's blinking animation inside the menu. This means a TAS can bend any unseeded random event to its will

at no in-game time cost

. Catching exactly the fish you want with minimal bite delay? No problem. Hooking a treasure chest with exactly the artifact you need while doing it? All possible.

With this, achieving Perfection in those minimum 61 days seems way more plausible.

Putting together a tool-assisted setup

TAS tools for Stardew Valley do exist, like the one from Underscore76. However, I quickly realized that beside it not being updated for 1.6 yet (at the time of writing), there is a bigger problem with going for the traditional TAS approach with a predefined set of inputs: the waiting. Just because it is possible to arbitrarily manipulate randomness without costing in-game time, does not mean it won't cost a lot of real time. Especially for unlikely events, you might need to pause for a very long time (*2).

So instead, I chose a different path: instead of having to wait for the stock RNG to advance to the value I need, I manipulate the RNG values directly to give the desired outcomes. Basically, whenever the global RNG would return a value, I get to choose which value it returns (cue xkcd). This eliminates the wait times, making both creating and watching such a run a less insane endeavor.

To achieve this, I created a mod that hooks into the game's RNG, and allows manipulating the returned values (*3). From there it was a matter of adding the hooks in all the needed places, and a lot of planning to make them return the values you need at every opportunity. It turns out that just because you can influence the RNG outcomes, does not mean it's easy to decide which outcome is actually the most preferable in any scenario.

Planning out the run

With the RNG manipulation setup working, what I needed was a route. I created big spreadsheets, with all the things needed for Perfection, tabulating all the resources needed and ways to most conveniently collect them with the constraints I have. It turns out that some things that are usually very hard become trivial, like raising your skills quickly or gaining a lot of money. Meanwhile other things which are usually simple become a challenge, like growing Carrots, which cannot be naturally found any time during the whole time window of the run.

Overall though, I realized that the superpower of manipulating the RNG will overpower the challenge easily, and I should embrace the absurdity of what it allows. I planned out all the items I need and a rough order of events, but didn't create a detailed route for every single day ahead of time, and instead focused on the most immediate needs each day, trusting that the pieces will all fit together in the end and I didn't realize I forgot something at the very end. Besides one small oversight of not accounting for the coal needed for Smoked Fish, it actually worked out well.

The full demonstration

Note: the video contains annotations explaining a lot of the route details and choices made.

The final run completes perfection in the planned 61 in-game days, and less than 5 hours of real time. The RNG manipulation is pre-programmed for each day, and the inputs in the run are performed manually by myself in the moment, though with additional tool assistance like slowdown and automatic animation canceling. It is NOT a speedrun, and also not a traditional TAS. It is best seen as a demonstration that achieving Perfection in 61 days is possible, and a showcase of the absurdity that can ensue when pushing the RNG to its limits.

Conclusion

This ended up being a much deeper rabbit hole than I initially expected, and I hope it was an informative dive into the intricacies of the game. I learned a lot about the inner workings of the game, and even uncovered some bugs, like the Monster Compendium not doubling drops or Tiger Slimes not dropping Snake Vertebrae.

So, can this be done in a real run? Maybe. I'm fairly confident that a more traditional TAS can also do it, even in a reasonable amount of time, by being more modest with the RNG manipulation outcomes. For example, it's actually not necessary to raise the friendship using copious amounts of Stardrop Tea, there is enough time to max out all friendship the normal way with presents.

To do it as a seeded challenge run by a human would be an enormous undertaking and take meticulous planning of each moment of each day, and countless retries for each day to get the best possible outcomes and avoid any mistakes. It's not strictly impossible, but I also expect few to be crazy enough to actually attempt it. The same techniques can still be used for any more modest challenge goal you set yourself, or just to optimize your normal gameplay.

Appendix: Technical details

(*1) Caveat: Unseeded events are actually seeded

The global PRNG used for unseeded random events is actually itself seeded: It is reset at the start of each day with known inputs. In practice, tracking exactly how it develops from the start of the game becomes infeasible very quickly, as random outcomes influence each other. That means there is technically a third category of randomness, for all the events that happen during a new day and use the global PRNG. Those events are unpredicatble, as the global PRNG is influenced by too many things to track its internal state through the whole process, but also not manipulatable because it's an entirely deterministic process with known inputs and no player interaction. Examples of events that fall into this category are the spawn of forageables and artifact spots or the growing and spreading of weeds and trees. For my demonstration, I left those events alone for the most part, and where I needed them them I mostly relied on good old luck, and dealt with whatever outcomes I got.

(*2) Caveat: Limits to the game's randomness

The game's PRNG is by necessity not perfectly random, which means it cannot actually produce any arbitrary string of random bits. That means for very unlikely events, it might not only take a very long time, it might literally never happen. In my considerations, I mostly ignore this fact, because the limitation does not actually exist in the game, but in the implementation of the .NET Framework the game runs on. The game uses the .NET Framework's standard Random class, and inherits any limitations it has. However, the .NET standard does not prescribe how the randomness must be implemented, which means it's possible to build a compliant .NET Framework with a better RNG and run the unmodified game on it to produce any arbitrarily unlikely event you want, even if the stock RNG never could.

(*3) Inner workings of the RNG manipulation mod

The mod I built is a SMAPI mod (there's great documentation on how modding works on the wiki) which uses Harmony Transpilers extensively to insert shims into places where the game calls the RNG. Each call site gets replaced by my shim implementation, which can employ different behaviors and inspect the game's internal state to decide which value it wants to return.

As an example, in the game there's a function FishingRod.calculateTimeUntilFishingBite which determines the time until a fish bites after casting your rod. In that function, the game eventually calls code like this (simplified):

Code:

float time = Game1.random.Next(minFishingBiteTime, maxFishingBiteTime);

The Harmony Transpiler would look for this piece of code, and replace it with:

Code:

float time = Util.NextMinMaxShim(Game1.random, minFishingBiteTime, maxFishingBiteTime, Config.fishingBiteDelayBehavior);

Notably, it added the shim and another parameter fishingBiteDelayBehavior, which can now be used to control the behavior when this specific instance of Game1.random.Next is called. The structure of the shim was chosen so adding it requires minimal changes to the IL representation of the existing code. It just requires to push one additional parameter onto the stack, and replace the callvirt instruction for the original method with a call to the static shim method.

The fishingBiteDelayBehavior itself is just an enum which informs how the shim is meant to behave. This is more flexible than using a dedicated shim for each behavior, as it allows you to update the desired behavior on the fly without needing to patch the IL again.

The shim and behavior can be very simple, like always returning the minimum bite delay, or arbitrarily complex, like checking the current game state whether a fish is still needed for the Master Angler or a cooking recipe, when deciding which fish to hook.

Initially, I actually tried a different approach, instead of changing the callsites, to change the RNG function directly, and inspect the call stack to determine which context the RNG is currently called in. This turned out to be a terrible way of doing it, because the call stack inspection is both slow, adding significant runtime cost to each invocation, and brittle, since due to optimizations the compiler can make you can't rely on always finding the stack frames you expect. Using Transpilers on the callsites moves all the costs to initialization time, and is immune to any JIT optimizations that may happen when the code is executed.

How to manipulate the RNG and achieve Perfection in 61 days (2024)
Top Articles
Latest Posts
Article information

Author: Laurine Ryan

Last Updated:

Views: 5293

Rating: 4.7 / 5 (57 voted)

Reviews: 80% of readers found this page helpful

Author information

Name: Laurine Ryan

Birthday: 1994-12-23

Address: Suite 751 871 Lissette Throughway, West Kittie, NH 41603

Phone: +2366831109631

Job: Sales Producer

Hobby: Creative writing, Motor sports, Do it yourself, Skateboarding, Coffee roasting, Calligraphy, Stand-up comedy

Introduction: My name is Laurine Ryan, I am a adorable, fair, graceful, spotless, gorgeous, homely, cooperative person who loves writing and wants to share my knowledge and understanding with you.