Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-implement chance of good and bad mutations based on instability #58832

Merged
merged 4 commits into from
Jul 9, 2022

Conversation

actually-a-cat
Copy link
Contributor

@actually-a-cat actually-a-cat commented Jun 30, 2022

Summary

Bugfixes "Fix effect of instability on chances of getting good and bad mutations"

Purpose of change

Instability and its effects on chances of getting good and bad mutations were added in #55743. However, actual behavior is different from description in that PR in several major ways:

  • Chances of bad mutations begin a lot earlier, and ramp up a lot faster, than shown on the graph
  • Instability can force good mutation or bad mutations, but when it does neither, mutations are completely random (and the distribution of good/bad/neutral mutations is determined by their relative abundance, not by instability). At many levels of instability, this happens most of the time.
  • The graph shows a constant 10% chance of neutral mutations, but I don't see any trace of that in the code.
  • There is a major penalty for having more than one type of mutagen vitamin, which is not mentioned in the PR, and is also excessive (even 1 point of mutagen counts and mutagen decays very slowly, so you're basically guaranteed to have this penalty forever if you ever take more than one type of mutagen)

I believe those to be cause of #58667 and various other complaints about bad mutations.

Fixes #58667.

Describe the solution

Chance of a mutation being bad is explicitly calculated using the following equation:

where i is current instability, I_0 is instability level at which bad mutations begin appearing, and I_50 is the level at which good and bad mutations are equally likely. Currently those are set to 900 and 2800 respectively, but this is easily tunable.

The formula is a bit gnarly but it's just a piece of a sigmoid curve, altered to take those parameters. The graph can be viewed here. Screenshot in case the link stops working:

image

This is then rolled to determine whether a mutation should be good or bad. (If doesn't roll bad, then it rolled good.) I made a change here: neutral mutations are always allowed, regardless of which way the roll goes. I feel this is more robust.

The result is used to gate which mutations are allowed as candidates.

The penalty for having more than one type of mutagen is gone. Maybe it's not a bad idea but it would need to be reworked.

The logic is split up to allow easily and independently adjusting the curve, replacing it entirely, and changing the rules for which mutations are allowed based on the roll.

Describe alternatives you've considered

There is an infinite variety of formulas that could be used. I chose the one above because it fairly closely matches the graph in the original PR (it's not my intention to make any significant redesign), and is easily tuned with parameters.

Testing

I mutated at various levels of instability while watching with a debugger to confirm correct values are coming out.

Unit test that mutates many times on a simple debug mutation category and compares the number of resulting bad mutations to manually calculated values. The test has limited value, not operating on real mutations and not exercising all features of mutation, but those same features would change the result in ways that are difficult to control for. I'm open to suggestions for better/additional testing.

Additional context

@github-actions github-actions bot added [C++] Changes (can be) made in C++. Previously named `Code` <Bugfix> This is a fix for a bug (or closes open issue) json-styled JSON lint passed, label assigned by github actions astyled astyled PR, label is assigned by github actions labels Jun 30, 2022
@Maleclypse
Copy link
Member

Am I reading this correctly that your first nine mutations (minimum, more if you wait in between mutations or have robust genetics) are guaranteed to be good?

@actually-a-cat
Copy link
Contributor Author

actually-a-cat commented Jun 30, 2022

Yes. Guaranteed to be good or neutral. This is as intended in #55743 (although the chance for neutral rather than good mutation will be different). It can be adjusted by changing the I0 parameter.

(Bad mutations are still possible as the end result if they are a prerequisite for a good or neutral mutation that gets picked. This is unchanged.)

@Maleclypse
Copy link
Member

Yes. Guaranteed to be good or neutral. This is as intended in #55743 (although the chance for neutral rather than good mutation will be different). It can be adjusted by changing the I0 parameter.

(Bad mutations are still possible as the end result if they are a prerequisite for a good or neutral mutation that gets picked. This is unchanged.)

I wasn't sure about the number in that PR either but it's only really an issue now with Alpha. I think that number is fine if instability also allows at a much lower number to start having a chance to mutate outside of the category that your mutating towards. So maybe at 300-400 instability you get a 10% chance to mutate outside of your category and that chance caps at 25% at 1200 instability.

@github-actions github-actions bot added the BasicBuildPassed This PR builds correctly, label assigned by github actions label Jun 30, 2022
@actually-a-cat
Copy link
Contributor Author

actually-a-cat commented Jun 30, 2022

I agree that it might need to be adjusted or mechanics changed but I feel it should go in a separate PR. I'm just trying to fix this up so it works as designed, then we can tinker with the design.

As an aside keep in mind that unless you have Robust Genetics instability is basically permanent. So, you get your 10 freebies, but unless you optimized your character for mutations on creation (or got very lucky), those are all the freebies you'll ever get. You can only exploit that with Alpha if you haven't mutated at all before. I don't think we should balance around RG, not too hard at least.

(As an aside to an aside: right now every mutation causes 100 instability, but this is actually adjustable, Alpha mutations could be made harder to get and more destabilizing with a JSON change)

@Maleclypse
Copy link
Member

Maleclypse commented Jun 30, 2022

And Robust Genetics consumes checks notes 3600 instability per minute?

  {
    "type": "mutation",
    "id": "ROBUST",
    "name": { "str": "Robust Genetics" },
    "points": 3,
    "description": "Your genetics have rapidly adapted to the chaos of the Cataclysm, and the genetic damage caused by mutations will fade much quicker.",
    "starting_trait": true,
    "cancels": [ "CHAOTIC_BAD" ],
    "category": [ "FISH", "SLIME", "ALPHA", "MEDICAL", "PLANT" ],
    "vitamin_rates": [ [ "instability", 3600 ] ]
  }

I'm thinking currently this system needs more than a small nudge to work as intended.

edit: Here's what vitamin_rate says in the documentation, not sure if that's accurate.

"vitamin_rates": [ [ "vitC", -1200 ] ], // How much extra vitamins do you consume per minute. Negative values mean production

@actually-a-cat
Copy link
Contributor Author

actually-a-cat commented Jun 30, 2022

I believe this means it consumes 1 instability per 3600 seconds, i.e. 24 per day.

edit: it does, see character_body.cpp:284-296.

@Maleclypse
Copy link
Member

I believe this means it consumes 1 instability per 3600 seconds, i.e. 24 per day.

Can you clarify that in the documentation then? Also can you show me where it says that each mutation causes 100 instability? I'm not having any luck finding that in the json or c++.

@actually-a-cat
Copy link
Contributor Author

Instability increase happens in mutation.cpp:1312, by the same amount as the mutation's vitamin cost:

vitamin_mod( vitamin_instability, mdata.vitamin_cost );

Vitamin cost is defaulted to 100 in mutation_data.cpp:265:

optional( jo, was_loaded, "vitamin_cost", vitamin_cost, 100 );

There are currently no mutations with non-default vitamin_cost.

@Maleclypse
Copy link
Member

Instability increase happens in mutation.cpp:1312, by the same amount as the mutation's vitamin cost:

vitamin_mod( vitamin_instability, mdata.vitamin_cost );

Vitamin cost is defaulted to 100 in mutation_data.cpp:265:

optional( jo, was_loaded, "vitamin_cost", vitamin_cost, 100 );

There are currently no mutations with non-default vitamin_cost.

Sorry to be a bother but what does the 9500 mean in the below

void Character::mutate( const int &true_random_chance, const bool use_vitamins )
{
    // Determine the highest mutation category
    mutation_category_id cat;
    weighted_int_list<mutation_category_id> cat_list = get_vitamin_weighted_categories();
    const float instability = vitamin_get( vitamin_instability );
    const bool terminal = instability >= 9500;
    const int flaw = rng( 0, ( cat_list.size() == 1 ? 0 : 10 ) + sqrt( instability ) ) +
                     ( instability / 900 );
    bool force_good = flaw < 10;
    bool force_bad = flaw >= 30;

    if( true_random_chance > 0 && one_in( true_random_chance ) ) {
        cat = mutation_category_ANY;
    } else if( cat_list.get_weight() > 0 ) {
        cat = *cat_list.pick();
        cat_list.add_or_replace( cat, 0 );
    } else {
        add_msg_if_player( m_bad,
                           _( "Your body tries to shift, tries to change, but only contorts for a moment.  You crave a more exotic mutagen." ) );
        return;
    }

@actually-a-cat
Copy link
Contributor Author

actually-a-cat commented Jun 30, 2022

I don't think it does anything. It's part of something called Terminus mutations, an unfinished feature by Mylie. I haven't seen an explanation of what those are meant to be.

src/mutation.cpp Outdated Show resolved Hide resolved
@Sinistrem
Copy link
Contributor

Haven't read this thoroughly yet, but i assume it's also going to fix my issue #57716

@Sinistrem
Copy link
Contributor

I think that number is fine if instability also allows at a much lower number to start having a chance to mutate outside of the category that your mutating towards. So maybe at 300-400 instability you get a 10% chance to mutate outside of your category and that chance caps at 25% at 1200 instability.

Assuming I understand that correctly, I'm not sure adding a chance to mutate outside of your category is a good idea.

If my memory serves me right, the latest mutation rework changed the lore so that you can't mutate towards any category at all unless you have specific mutagen to "guide" the generic blob mutagen, hence the separation between generic an targeted vitamins. So getting an animal tail from consuming alpha primer would be very weird.

More importantly, that adds a ton of ways to mess up or break mutation trees entirely (since I assume this affects the whole system in general, and not just alpha). I.e. getting a herbivore mutation while mutating a carnivore category, gaining a generic night vision perk while trying to mutate cat tree, or gaining a side grade mutation that completely blocks off more potent mutations from your intended tree ( oh god just imagine how badly a chimera can be fked up just by getting some unrelated claws/tail upgrade, preventing it from getting it's own superior versions).

I'm not saying that alpha shouldn't be looked into, just that this is probably the most "chaotic evil" solution possible, and implementing a huge amount of checks and safety measures to make "out of category" mutations work is probably not worth it.

Maybe give alpha mutagen a chance to "botch" mutation due to difficulty, increasing instability and causing noticeable damage without producing any mutations (although I thought that alpha and medical mutations were already balanced by the fact that they are much harder to learn and expensive to produce).

@Maleclypse
Copy link
Member

I think that number is fine if instability also allows at a much lower number to start having a chance to mutate outside of the category that your mutating towards. So maybe at 300-400 instability you get a 10% chance to mutate outside of your category and that chance caps at 25% at 1200 instability.

Assuming I understand that correctly, I'm not sure adding a chance to mutate outside of your category is a good idea.

If my memory serves me right, the latest mutation rework changed the lore so that you can't mutate towards any category at all unless you have specific mutagen to "guide" the generic blob mutagen, hence the separation between generic an targeted vitamins. So getting an animal tail from consuming alpha primer would be very weird.

More importantly, that adds a ton of ways to mess up or break mutation trees entirely (since I assume this affects the whole system in general, and not just alpha). I.e. getting a herbivore mutation while mutating a carnivore category, gaining a generic night vision perk while trying to mutate cat tree, or gaining a side grade mutation that completely blocks off more potent mutations from your intended tree ( oh god just imagine how badly a chimera can be fked up just by getting some unrelated claws/tail upgrade, preventing it from getting it's own superior versions).

I'm not saying that alpha shouldn't be looked into, just that this is probably the most "chaotic evil" solution possible, and implementing a huge amount of checks and safety measures to make "out of category" mutations work is probably not worth it.

Maybe give alpha mutagen a chance to "botch" mutation due to difficulty, increasing instability and causing noticeable damage without producing any mutations (although I thought that alpha and medical mutations were already balanced by the fact that they are much harder to learn and expensive to produce).

None of that would happen. Nothing in the system would prevent you from mutating back into category. It wouldn’t make out of category unpurifiable.

@bombasticSlacks
Copy link
Contributor

Terminus mutations

They are supposed to be point of no return mutations that are supposed to be very bad if you have gone too deep on mutation. (just answering the question, I'm of the opinion it should be ignored / removed until the feature actually exists).

@anoobindisguise
Copy link
Contributor

Terminus mutations

They are supposed to be point of no return mutations that are supposed to be very bad if you have gone too deep on mutation. (just answering the question, I'm of the opinion it should be ignored / removed until the feature actually exists).

I believe one example of this was making Alpha completely dependent on mutagen, they would slowly consume alpha mutagen vitamin to stay alive and would suffer a much worse form of Disintegration if they were lacking.

So Alpha might be one of the strongest categories at the moment due to manageable downsides (disintegration being outright beneficial currently), combined with Extremely Stat mutations being made post threshold effectively nerfing every other tree, as well as mutations now removing mutations from other categories (so you cannot hybridize and pull the best mutations from each tree). Then there's how Multi-Pool characters have much lower stats than Single-Pool characters (alpha benefitting low stats a lot).

All this gives Alpha the likely best net sum of mutations meaning it's top tier, but there are plans to nerf it within the existing system.

@github-actions github-actions bot added [JSON] Changes (can be) made in JSON Code: Tests Measurement, self-control, statistics, balancing. labels Jul 5, 2022
@actually-a-cat actually-a-cat force-pushed the bad-mutation-fix branch 2 times, most recently from cbd742a to 81ac1ae Compare July 5, 2022 21:33
@github-actions github-actions bot removed the BasicBuildPassed This PR builds correctly, label assigned by github actions label Jul 5, 2022
@actually-a-cat actually-a-cat force-pushed the bad-mutation-fix branch 3 times, most recently from a6e1c22 to 2093678 Compare July 6, 2022 01:04
@github-actions github-actions bot added the BasicBuildPassed This PR builds correctly, label assigned by github actions label Jul 6, 2022
@actually-a-cat actually-a-cat force-pushed the bad-mutation-fix branch 2 times, most recently from ac53682 to ea70340 Compare July 6, 2022 11:50
@actually-a-cat actually-a-cat marked this pull request as ready for review July 6, 2022 12:01
@github-actions github-actions bot removed the BasicBuildPassed This PR builds correctly, label assigned by github actions label Jul 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
astyled astyled PR, label is assigned by github actions BasicBuildPassed This PR builds correctly, label assigned by github actions <Bugfix> This is a fix for a bug (or closes open issue) [C++] Changes (can be) made in C++. Previously named `Code` Code: Tests Measurement, self-control, statistics, balancing. [JSON] Changes (can be) made in JSON json-styled JSON lint passed, label assigned by github actions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Instability is a lie?
7 participants