stephenengland / SubnauticaRandomizer

A Subnautica Mod that randomizes recipes and changes Blueprint Fragment requirements for replayability
3 stars 0 forks source link

A Better Way of Randomizing #2

Open stephenengland opened 6 years ago

stephenengland commented 6 years ago

The current process of randomizing identifies each ingredient with a "depth difficulty".

Each recipe has a list of "Depth difficulties" that it needs and the randomization process randomly picks a difficulty that matches.

This is a poor way of generating recipes. It isn't going to be dispersed in a good way (i.e. titanium won't get used a lot) and some ingredients are more difficult than others, even though they are obtainable at a certain depth.

Can someone think of a better heuristic for doing this?

TheRealC commented 4 years ago

Hey! I've actually been thinking about this for a while, and I think it's worth taking a page from other randomizers - the default thing to do is to have each (in this case) resource require one or more items to be considered obtainable.

A very simple example: Creepvine has "Survival Knife" OR "Thermoblade" set as "Required". Thus, if you have either of the above, the randomizer should treat Creepvine as (permanently) available for all future recipes. Conversely, if you don't yet have a Survival Knife or a Thermoblade, no recipe should require you to need Creepvine - in particular, Creepvine cannot show up in the recipe for Survival Knife and Thermoblade (though it could show up in the recipe of one of them!).

This is obviously very different from having items require depth, as Creepvine should logically have a depth level of somewhere between 0 and 20m or thereabouts, while with the proposed system, you could feasibly not even be able to harvest Creepvine until the very end of the game, and still be able to complete (though you'd have to do a knifeless run!).

On the other hand, straight-up depth requirements can be approximated by item requirements; for example, you could (numbers taken out of thin air without any balancing in mind) consider that items no deeper than 50m are available without items, items up to 100m are available with either Seaglide OR Standard O2 Tank OR High Capacity O2 Tank OR Lightweight High Capacity O2 Tank OR Ultra High Capacity O2 Tank OR Seamoth OR Prawn OR Cyclops, items up to 150m are available with either (Seaglide AND Standard O2 Tank) OR High Capacity O2 Tank OR Lightweight High Capacity O2 Tank OR Ultra High Capacity O2 Tank OR Seamoth OR Prawn OR Cyclops, etc.

Thus - importing terminology from other randomizers I am familiar with - one could write a so-called logic , which dictates what items should logically be required to be able to obtain each item.

It is also possible to group items and ensure that any given recipe involves some number of items from any given group, e.g. the Cyclops recipe should probably involve at least some number of hard-to-get items. Therefore, the recipe for the Cyclops should not be randomized until the player is able to obtain some number of such items.

Indeed, this suggests a reasonable algorithm:

  1. Set all "free" resources as available to the player (Titanium, Copper, Silver, Gold, Lead, several fish sorts, Gas Pods etc.).
  2. Randomly choose any manufactured item that is not set to demand items from a group the player cannot access. Randomize this recipe.
  3. Add all newly unlocked resources (according to logic) to the pool of resources available to the player.
  4. Repeat from 1 until the recipes for all parts of the escape rocket have been randomized + the player has logical access to the Sea Emperor + the recipe for the Hatching Enzymes has been randomized.
  5. Randomize remaining recipes more or less blindly (these will not be required to complete the game anyways). This step can be tweaked if the intention is that every run should always be 100% completeable, as opposed to just being able to escape Planet 4546B.

Note in particular that this system generates recipes procedurally, in such a fashion that a recipe is not randomized until it is certain that the player will eventually be able to make it. Thus, there is no need to go through the recipes after generation to ensure the run is beatable; it was already built to guarantee this.

If there is still interest in this project, I would be willing to draft up a rough logic - as seeing a Subnautica randomizer would certainly be something I'm very interested in!

While I am at it, I should add that this system would allow every item you can put in your inventory - fish, seeds, eggs, food, drink, manufactured equipment, anything - to potentially appear in recipes, and this is something I would very much like to see.

Finally, to combat the fact that some resources are simply much rarer than other, every resource could be assigned some sort of "value", a rough function of its abundancy and relative difficulty in finding/extracting. Each recipe should then demand that the total weighted value of items involved in generating it should be within a certain range (precise values not recommended, due to restricting choice too much). Another completely not-thought-through-and-unbalanced example:

Any of the following would be valid recipes for the O2 Tank:

None of the following would be valid recipes for the O2 Tank:

A reasonable algorithm for recipe generation would be to randomly pick any available material (weighted towards lower-value items) with total value less than what is currently missing to reach the maximum total demanded value of the recipe, then add it to the recipe and repeat, although there are several possible tweaks possible, of course.

This would also give all manufactured items a total value ( = the sum of the value of their parts, possibly plus a small number to make up for the effort of crafting it), which would then be used for when the manufactured item itself should randomly be included as part of the recipe of another manufactured item (e.g. a 6x Titanium O2 Tank would be worth 60 + (small number, e.g. 5), and could now randomly feature in any other future recipe, contributing 65 towards the total value of the recipe.

Actually-finally, one could theoretically use this system to write several different variations of logic, each expecting different things from the player (e.g. a casual logic expecting only intended strategies, a glitched logic expecting various out-of-bounds tricks etc.) - though it is perhaps nice to keep things simple for a first version. At any rate, a logic system as drafted above could also allow one to formalize randomization of databoxes and blueprints, leading to some interesting scenarios where there are runs where e.g. the player has to go to the Mountain wrecks to find required databoxes, and runs where they are blessedly spared from needing to visit any leviathan-heavy areas at all (aside from the path to the Sea Emperor, obviously).

Again, I hope there is still interest in the project - it is exciting, and I would certainly have liked to try my hand at this myself, but I have no coding experience to speak of and no time to learn it. However, in terms of logic and plain-text algorithms, I would like to think I could contribute at least a bit.

stephenengland commented 4 years ago

I have been lazy and putting off the non-fun part of this project, releasing it! I don't know why I put it off so long.. but it's not downloadable on the mod site right now and I need to just "get it done". I also had planned on splitting out the functionality into two mods. Someone suggested this since the features are so different. So I'll find time to do that soon! I never gauged how interested people are in these mods because I never released them

TheRealC commented 4 years ago

In that case, I would attempt to put together a logic draft over the course of the next few days, as spare time allows. Of course, there is no real rush, but I think something interesting can be done, in due time.

Do you have any specific inputs on my suggestions, or features you would like to have added/removed? Otherwise, I'd just go with something like what I suggested.

stephenengland commented 4 years ago

A few things to point out... The randomizer is VERY random right now, haha. The difficult can vary wildly. If we can find a way to include a difficulty setting, bonus points. Also, depth should still play a part somewhat. If all the vehicles require materials at high depths, it might make the game impossible or ridiculously hard. But I guess some people would want that, too.

stephenengland commented 4 years ago

But otherwise I'm not trying to be too prescriptive on the logic.. it might be difficult to code depending on how it's defined. And there will probably need to be some sort of "definition" file to define the dependencies. I was using an Excel spreadsheet

TheRealC commented 4 years ago

The logic file itself, which would contain all needed definitions, can easily just be a text file, as long as there's a system in place for how to read it. For now, I would just start by writing out a plain-text explanation of how things work, and maybe give a minimal working example of what the actual logic file could look like.

As for controlling difficulty, this is absolutely possible, but at the same time, I would not want to control it too much. The nature of randomizers in general is that sometimes, you get very hard runs, and sometimes you get very easy runs. As long as all runs are actually doable within the game rules (ideally without the use of glitches), however, everything is as it should be. This increases variety and thus replayability, at least in my experience.

stephenengland commented 4 years ago

I released what I have right now: https://www.nexusmods.com/subnautica/mods/309?tab=description

TheRealC commented 4 years ago

As it has been a few days, I wanted to report in and confirm that I am still working on a logic file for the randomizer, although it is taking some time.

The current progress can be found at https://pastebin.com/T6et9NMP . Please do note that it is quite far from being completed and has several obvious flaws . My immediate question is: Is something like this (once completed) going to be useful for the randomizer?

To reiterate, the intent of the logic is that every recipe in the game should be randomized, while ensuring that the player can never get put in a situation where it is impossible to complete the game. At the same time, some measures are taken to ensure that (1) recipes are not unreasonably difficult, and (2) recipes are not excessively easy. The former would make the game too frustrating, while the latter could lead to games ending too quickly (if e.g. Prawn Suit and its depth models are abnormally cheap).

The "Required/Conditional" system is therefore in place solely to prevent impossible games. It is not intended to create balance or difficulty, and merely states minimum conditions required to get each item in the game. For example, all seeds you need to cut the respective plant to obtain have "Required: Any Knife" (or, equivalently, "Conditional: Survival Knife; Thermoblade", implying you need at least one of the listed items), because it is impossible for the player to obtain these seeds without a knife. This ensures the recipe for both knives will not contain any such seeds (though it is possible for one of the knife recipes to contain seeds, and not the other).

The recipe info system is my current suggestion for difficulty balancing. The second bit of the recipe info essentially tells you how "early/late" in the game it should be possible to obtain the item, assuming you have the correct Required/Conditional items, as a number between 0 (beginning of the game) to 3 (end of the game).

E.g.:

In all cases, logical restrictions (Required/Conditional) must always be respected, in addition to the above restrictions.

Is this system clear, and does it seem useable? If so, I will attempt to finish the logic as soon as feasible. If not, we should discuss further.

stephenengland commented 4 years ago

Thanks for your work on this!

I think maybe it'd be good to describe the current logic - so that what you are trying to do can be integrated into it. There are a couple of things I like from the current logic, some from yours.

Here is a spreadsheet of the current data: https://github.com/thealah/SubnauticaRandomizer/blob/master/SubnauticaRandomizer/recipeinformation.csv

"RequiredIngredients" column defines ingredients that must be in the recipe. I thought this was important for flavor - the Thermoblade should require a knife, for example.

"DepthDifficulty" defines how difficult it is to make the item. This will be used in "RandomizeDifficulty".

"RestrictedTools" defines if you need something in order to make this. I believe when I coded this, this only works for raw materials? This helps control the "Required/Conditional" problem you were talking about.

"RandomizeDifficulty" is a bit tricky... it is a comma delimited numeric list. If you put "100, 0, 0" it will require one material that appears in depths 100ish (Lithium, for example), two items that appear in the shallows (copper, quartz for example).

ALSO keep in mind, if I remember correctly, the materials that aren't raw (Titanium Ingot) count as "X number" of ingredients. For example, if you needed "0,0,100" and Titanium Ingot was "0,100", and Titanium ingot was randomly chosen, it would look for one "0" to fill in the remainder.

"Quantity" is just how many will be made when you create it.

I think based on looking at your data, what you are looking for could just be translated into the current spreadsheet - and it all might just magically work. A lot of the leg-work would be defining difficulties for each fish and stuff like that.

I would love if we had fish in here. I think it'd be quite interesting if you had to gather fish to make certain things. It might need to be optional due to how difficult it can make the game, but it would definitely create a whole new spin for players who love the game - it would force you to learn a lot more about where fish are, what fish exist.

Am I missing something between the two systems?

A couple of notes:

I don't mind removing the "RequiredIngredients", or maybe just making it an optional randomizer setting - if people are wanting a higher variance for funsies.

I was trying to control the difficulty - both from "shit this takes a ton of resources to build, thats lame" and "I can't dive that deep!"

I wrote the logic for this a year ago.. so I'm pulling most of this from memory. I didn't do a deep dive into my code to validate everything 100%.

TheRealC commented 4 years ago

I see. Thank you for your input!

For "RequiredIngredients", I dislike it, as it goes against the spirit of randomizers - randomizers generally have lots of situations that are not innately "flavorful" or make any sense, but turn out to be great fun in their own (sometimes comedic) ways. Deleting it would be fine. In the same vein, I'm slightly inclined to suggest that (a) the Metal Scrap recipe for Titanium need not exist as-is, and (b) the Titanium/Plasteel Ingot recipes need not exist as is, although this is maybe more subjective (they do serve a purpose of enabling & compacting Titanium collection and storage, although changing the former might reduce the need for the latter two).

"RestrictedTools" mostly does what "Required/Conditional" is meant to do; however, it has one weakness, which already becomes apparent with the many Knife entries you've listed. All the items on the list can be harvested with a Survival Knife or a Thermoblade, and if you remove the "RequiredIngredients" entry, it is entirely possible to have runs where you obtain Thermoblade before Survival Knife. Therefore, all the items you've listed should be flagged as available for recipes as soon as you have Survival Knife or Thermoblade available.

This is an even more invovled issue when it comes to depth restrictions (which we'll currently separate from your "DepthDifficulty", to discuss later). For example, consider items that are generally only found at a depth of 200m. In the interest of ease-of-play (i.e. not forcing the player to hoard a full inventory of Bladderfish & drinks), I agree that such items should not be considered as available right off the bat. However, deciding exactly when they should be considered is trickier. Due to the randomizers, well, random, nature, it is not given that the player will obtain the various diving equipment in the "intended" order. Thus, one should consider all the possible combinations of items that should be sufficient to reach 200m depth, have at least some time to retrieve the item in question, and get back to the surface safely. Without testing this too rigidly in-game (something I'd have to do before finalizing), all of the following combinations sound like they would be sufficient:

...and probably many more combinations. While we could (and probably should) discuss some of these choices in more details later, depending on how difficult/easy the randomized playthrough is intended to be, there needs to be a system in place that accepts that as long as you have any (at least) one of the combinations above, then you should have access to items found at depth of 200m, and such items can therefore be included in recipes. If you do not have any of the combinations above, then you cannot obtain items found at a depth of 200m, and those should not appear in recipes.

This matches my "Conditional" system - each of the combinations above are equivalent to one "Conditional" combination. While it would definitely be possible to rewrite your system to account for it, it does not seem to natively fit.

An advantage of a logic-based system is the possibility to create virtual "items". E.g. you can define the virtual item

Now any other item that requires having a knife (i.e. all seeds) can have "Required: Any Knife, Conditional: -". Using this idea, the above 200m depth requirements can be boiled down to

which is still a long list, but at least more manageable. This is intended to solve the "Shit I can't dive that deep" problem - it explicitly states when the program should consider the player as being able to dive that deep.

Now to actually discuss "DepthDifficulty" (and also "RandomizeDifficulty"), I am somewhat more inclined towards my own, simpler, system, that is, splitting all materials & recipes into one of four categories (Early game, midgame, lategame, endgame - roughly), and ensuring each recipe requires materials only from categories equal or lower than its own category, and always at least one ingredient from its own category. This means less fine-control on recipes (which I think is good, as it means more variation), while still ensuring there is some kind of natural progression despite the randomization of items available. Of course, it might also be interesting to do some behind-the-scenes magic to ensure no material becomes required too often, but this can be done in other ways than trying to control recipes direclty, and isn't an immediate priority for the first few versions (in my opinion).

With that said, we both agree that some kind of system should be involved to ensure recipes do not have to extreme variations in terms of total price, e.g. you should not need thirty different items just to craft an O2 Tank. Here, I suspect we will simply have to try out a few things and see what works - my suggestion was putting a value on each material and giving a range of accepted total values for each recipe, but your approach or a combination of these might prove more suitable.

Also 100% yes fish. Including aquarium fish! And eggs. If only there was a way to put Reaper Leviathans in your inventory...

On a personal note, I doubt I will have much time to contribute this week due to work reasons, but this is a temporary situation, and by Sunday I should be able to give this due attention.