Wheel Turns Calculator

peterbones

New member
On the premise that no one likes spending more turns than neccessary at the castle, I've made a handy dandy wheel calculator.  Tell it your desired stats ( something like 104, 67, 67 for a muscle class usually ) and it will tell you how many turns you need to spend with the wheel at the stat position you specify.

WARNING: This functions ok, but not perfectly.  For a few reasons... One being that AFAICT there's no way to get your substat values, so it has to work off your fullstats only.  The other being that when adventuring at the castle, you'll often get a stat adventure before you've turned the wheel to the position you want.  So it's not perfect, but it's good enough for me to use in my scripts.

And you'll need to change a few values yourself.  The base stat gains are the gains from combat adventures.  You'll need to figure out how many stat points you get from an average battle and divide it by 4 (because of the 2:1:1 ratio).  The numbers currently listed are for a 45lb. sombrero & volleyball type familiar (and are fairly approximate), with +51 monster level.  Also you'll need to change the combat frequency (if you have no frequency changing skills, it's ~0.75 I hope).

Code:
int calculate_wheel_turns( stat st, int muscleDesired, int mysticalityDesired, int moxieDesired  )
{
    float baseMuscleGain;
    float baseMysticalityGain;
    float baseMoxieGain;
    float addedMuscleGain;
    float addedMysticalityGain;
    float addedMoxieGain;
    float muscleNeeded;
    float mysticalityNeeded;
    float moxieNeeded;
    float turnsMuscle;
    float turnsMysticality;
    float turnsMoxie;
    float totalTurns;
    float combatFrequency = 0.65;

    print( "Calculating " + stat_to_string( st ) + " turns..." );

    //Base stat gains, calculated as (Average XP Per Fight)*(Combat Frequency)/4
    //This assumes a 45 lb. familiar with +51 ML and is only approximate
    if( my_familiar() == $familiar[ hovering sombrero ] )
    {
        baseMuscleGain = 15 * combatFrequency;
        baseMysticalityGain = 15 * combatFrequency;
        baseMoxieGain = 15 * combatFrequency;
    }
    //This assumes a volleyball type familiar
    else
    {
        baseMuscleGain = 13 * combatFrequency;
        baseMysticalityGain = 13 * combatFrequency;
        baseMoxieGain = 13 * combatFrequency;
    }

    //Calculate how many substats needed
    //Substats to a given fullstat level are given as fullstat^2
    muscleNeeded = ( muscleDesired * muscleDesired ) - ( my_basestat( $stat[ muscle ] ) * my_basestat( $stat[ muscle ] ) );
    mysticalityNeeded = ( mysticalityDesired * mysticalityDesired ) - ( my_basestat( $stat[ mysticality ] ) * my_basestat( $stat[ mysticality ] ) );
    moxieNeeded = ( moxieDesired * moxieDesired ) - ( my_basestat( $stat[ moxie ] ) * my_basestat( $stat[ moxie ] ) );

    //No one likes negative gains
    if( muscleNeeded < 0 )muscleNeeded = 0;
    if( mysticalityNeeded < 0 )mysticalityNeeded = 0;
    if( moxieNeeded < 0 )moxieNeeded = 0;

    //Since stats are shared in a 2:1:1 manner between prime and offstat, double the primestat gain
    if( my_primestat() == $stat[ muscle ] )
        baseMuscleGain = baseMuscleGain * 2;
    if( my_primestat() == $stat[ mysticality ] )
        baseMysticalityGain = baseMysticalityGain * 2;
    if( my_primestat() == $stat[ moxie ] )
        baseMoxieGain = baseMoxieGain * 2;

    //Calculated as Gain*Frequency, where gain is assumed to be ~220, and half of non-combat advs are stat advs
    addedMuscleGain = 220 * ( 1 - combatFrequency ) / 2;
    addedMysticalityGain = 220 * ( 1 - combatFrequency ) / 2;
    addedMoxieGain = 220 * ( 1 - combatFrequency ) / 2;

    //Adjust for stat days
    if( stat_bonus_today() == $stat[ muscle ] )
    {
        addedMuscleGain = addedMuscleGain * 1.5;
        baseMuscleGain = baseMuscleGain * 1.5;
    }

    if( stat_bonus_today() == $stat[ mysticality ] )
    {
        addedMysticalityGain = addedMysticalityGain * 1.5;
        baseMysticalityGain = baseMysticalityGain * 1.5;
    }

    if( stat_bonus_today() == $stat[ moxie ] )
    {
        addedMoxieGain = addedMoxieGain * 1.5;
        baseMoxieGain = baseMoxieGain * 1.5;
    }

    //Do an initial rough calculation, based on turns if all advs spent at the stat's wheel rotation
    turnsMuscle = muscleNeeded / ( baseMuscleGain + addedMuscleGain );
    turnsMysticality = mysticalityNeeded / ( baseMysticalityGain + addedMysticalityGain );
    turnsMoxie = moxieNeeded / ( baseMoxieGain + addedMoxieGain );

    //again, negative advs don't exist
    if( turnsMuscle < 0 )turnsMuscle = 0;
    if( turnsMysticality < 0 )turnsMysticality = 0;
    if( turnsMoxie < 0 )turnsMoxie = 0;

    totalTurns = turnsMuscle + turnsMysticality + turnsMoxie;

    //It will converge, damnit!  Of course, there could be a convergence test, but looping 50 times works
    for x from 0 upto 50
    {
        //Calculate the number of turns needed, this time taking into account the stats gained while at the other wheel rotations
        turnsMuscle = ( muscleNeeded - ( totalTurns * baseMuscleGain ) ) / addedMuscleGain;
        turnsMysticality = ( mysticalityNeeded - ( totalTurns * baseMysticalityGain ) ) / addedMysticalityGain;
        turnsMoxie = ( moxieNeeded - ( totalTurns * baseMoxieGain ) ) / addedMoxieGain;

        if( turnsMuscle < 0 )turnsMuscle = 0;
        if( turnsMysticality < 0 )turnsMysticality = 0;
        if( turnsMoxie < 0 )turnsMoxie = 0;

    //weighted average helps with convergence
    totalTurns = ( turnsMuscle + turnsMysticality + turnsMoxie + ( 3 * totalTurns ) ) / 4;
    }

    print( "Muscle turns expected: " + int_to_string( floor( turnsMuscle ) ) );
    print( "Mysticality turns expected: " + int_to_string( floor( turnsMysticality ) ) );
    print( "Moxie turns expected: " + int_to_string( floor( turnsMoxie ) ) );

    if( st == $stat[ muscle ] )return floor(turnsMuscle);
    if( st == $stat[ mysticality ] )return floor(turnsMysticality);
    if( st == $stat[ moxie ] )return floor(turnsMoxie);
    return 0;
}

I hope you like my math, though I'm free to suggestions about how to make it more accurate.  Especially the stat adventures; I've assumed them to be 220 but I know they vary.  Are they uniformly random, or does it depend on your stats?

Also, if there's a way to get frequency info from mafia that'd be great, otherwise writing a function to calculate it should be easy.  The base stat gains could also be automated, though it'd be a little more work.  Unless someone has a XP calculating function laying around already.  Anyway, I'm feeling too lazy to bother right now.

Edited for convergence.
 

Tirian

Member
It's impossible to predict what the stat bonus is. It is X +/- Y, where X has something to do with the current level of your stat (or your current level, I'm not really certain), and Y is a fairly large randomizing value. And it caps at 250, or 375 with a stat day bonus. And, between you and me, I think that they're slowly nerfing X so that when NS13 comes out we won't be still doing all our levelling in the Castle.

I don't know about frequency data, but there are three effects and one item that increase non-combat frequency by 5%, so it shouldn't be too hard to count them up (and, I guess, the four things that decrease non-combat frequency by 5%).

Welcome, by the way.
 

peterbones

New member
Thanks for the welcome.  I don't know if anyone tried out the script before, but it turns out that due to the jankity way I'm calculating things (I'm not sure if there's any way to just make an equation because of the constraint that turnsMuscle > 0, turnsMysticality > 0, turnsMoxie > 0 along with the 3 variables and 3 equations) if you had a combat frequency higher than 0.65 it might give you a horribly horribly erroneous answer.  Anyway, I changed the loop and now it seems to work for combat frequencies as high as they go (0.95).  I edited the previous post since it was one line changed, and I didn't want anyone to download the wrong code if I posted it again.

I've got a working script of automating the rest.  The combat frequency isn't too bad, like you said.  The XP gains are a little harder.  I've got most of it working using picklish's maps for +stats and +ML.  I'm not too sure on the XP equation though; for instance, do +stats gears/effects get modified by stat days?  Does antiphon?  Right now I have it as:

Code:
float calculate_xp( int baseML, stat st )
{
    int monsterLevel = baseML + get_plusML();

    float xp = 0;

    xp = xp + ( monsterLevel / 5.0 );
    xp = xp + ( get_volleyball_level() / 4.0 );
    xp = xp + ( 3.0 * get_sombrero_level() * sqrt( monsterLevel ) / 100.0 );

    xp = xp / 2;
    if( st != my_primestat() )xp = xp / 2;

    if( have_effect( $effect[ aloysius' antiphon of aptitude ] ) > 0 )xp = xp + 1;

    if( st == stat_bonus_today() )xp = xp * 1.5;

    return xp;
}

It gives answers answers similar to what I get in the location details portion of mafia.

Anyway, here are the updated functions.  Even if you don't like the castle turn calculator, the calculate_xp might come in handy.  You'll need in addition to the maps I've provided picklish's maps ( see http://kolmafia.us/index.php/topic,343.0.html).
 

Attachments

  • more_maps.zip
    1.6 KB · Views: 78
  • wheelTurns.ash
    9.2 KB · Views: 79
Top