Saturday, January 8, 2011

Kit Generator

I really couldn't believe it myself.  The carpal tunnel has of course crept back in, the tendons in both my arms (though still dominant in the left) always feel tense now.  I even pulled an all-nighter last night just because I was so absolutely interested in coding the kit generator.

Once in a while we hold breeding season on PI for Felishorn owners to breed two Felishorns together and get a kit carrying some resemblance of their parents.  There's always the thrill of the unexpected, and an excuse to drool over the cutest pieces of art in the world.  It's quite a bit of work for everyone though, especially the colorists, but I wanted to make a generator that could roll through all the probabilities for gender, breed, mutations, etc so that Jean wouldn't have to spend so much time on that logistic, and more on making sure everyone's up to task.  In the past, we've pulled out random.org and generated random numbers, but I thought it might be cool to be able to generate information for a breeding immediately, especially when dealing with litters.

The entire project taught me more about PHP codes, but for the most part I wrote a couple hundred if statements.  The entire code for the results page isn't that long, but certainly dense with logic statements.  Check out some of the coolest parts of the code after the cut.

The hardest part of the whole thing was to figure out how we could calculate the effects of items.  Natural breeds were easy, but how to create a form that accounts for just about any combination of items that people could choose.  Thank goodness I could break the results down and focus on components: number of offspring, gender, breed, mutations and colors.


Color and breed has a large number amounts of items that affect them, and users can use either 0, 1 or 2 items for each.  Color was really easy: if the mineral is there, state the colors they enhance.  If not, do nothing, and there were no probabilities involved.  It helped create the tree of logic that ultimately becomes the base of the calculations in all other parts:

$mineral1=stripslashes($_POST['mineral1']);
$mineral2=stripslashes($_POST['mineral2']);

//homogeneous minerals
if ( $mineral1 === $mineral2 ) { //two 'none' or two identical minerals
    if ( $mineral1 != "none" ) { $gemsused = $mineral1; } //identical minerals
    if ( $mineral1 == "none" ) { //two 'none', no minerals used
        $gemsused = "nothing";
    }
} //end homogeneous minerals
else { //heterogeneous minerals
    if ( $mineral1 == "none" || $mineral2 == "none" ) { //one mineral, one 'none'
        if ( $mineral1 != "none" && $mineral2 == "none" ) { //if $mineral1 has a value but $mineral2 doesn't
            $gemsused = $mineral1;
        }
        if ( $mineral1 == "none" && $mineral2 != "none" ) { //if $mineral1 doesn't have a value and $mineral2 does
            $gemsused = $mineral2;
        }
    } else { //two different minerals, no 'none'
        $gemsused = "$mineral1 and $mineral2";
    }
} //end heterogeneous minerals

echo "<p>Mineral colors: this kit has a splash of $gemsused.</p>";

I had to play around with a few versions of this before I could decide what I really needed.  Since there's only a maximum of two items that can be used in each slot, those items were either identical to one another that no item was used (not using an item gives that slot a "none" value, which the code considered as a value).

It was a nightmare getting to breed and gender.  Homogeneous items were broken down to no items (natural probabilities) and identical items (ignore natural probabilities and calculate item's probabilities only).  Heterogeneous items either had one "none" and one defined (we had to combine natural probabilities and item probability) or both were different (ignore natural probabilities and combine the probabilities of the two different objects).  I'm going to showcase the gender code because it looks more impressive, and color code the pairs of if-else statements to count how many were needed.

$femalecirclet=$_POST['femalecirclet'];
$malecirclet=$_POST['malecirclet'];

//homogeneous circlets
if ( $femalecirclet === $malecirclet ) { //two 'none' or two identical circlets
    if ( $femalecirclet == "none" && $malecirclet == "none" ) { //two 'none', no circlets used
        $kitgender = "Cannot breed without a circlet.  <a href=kitgenerator.php>Go back</a>.</p>";
    } else {
        if ( $femalecirclet == "gold" ) { $kitgender = $femalecirclet; } //two gold circlets
        if ( $femalecirclet == "silver" ) { $kitgender = $femalecirclet; } //two silver circlets
        if ( $femalecirclet == "bronze" ) { //natural gender
            if ( rand(1,10) > 6 ) { $kitgender = "Female"; } else { $kitgender = "Male"; }
        }
    }
//end homogeneous circlets
} else { //heterogeneous circlets
    if ( $femalecirclet == "none" || $malecirclet == "none" ) { //one circlet, one 'none'
        if ( $femalecirclet != "none" && $malecirclet == "none" ) { //if $femalecirclet has a value but $malecirclet doesn't
            if ( $femalecirclet = "bronze" ) { //female bronze circlet used only
                if ( rand(1,10) > 6 ) { $kitgender = "Female"; } else { $kitgender = "Male"; }
            } else { $kitgender = $femalecirclet; }
        }
        if ( $femalecirclet == "none" && $malecirclet != "none" ) { //if $femalecirclet doesn't have a value and $malecirclet does
            if ( $malecirclet = "bronze" ) { //male bronze circlet used only
                if ( rand(1,10) > 6 ) { $kitgender = "Female"; } else { $kitgender = "Male"; }
            } else { $kitgender = $malecirclet; }
        }
    } else { //two different circlets, no 'none'
        if ( $femalecirclet == "bronze" || $malecirclet == "bronze" ) { //if one of them has a bronze
            if ( $femalecirclet != "bronze" ) { $kitgender = $femalecirclet; } //if femalecirclet isn't bronze, then that's the gender determinator
            if ( $malecirclet != "bronze" ) { $kitgender = $malecirclet; } //if malecirclet isn't bronze, then that's the gender determinator
        } else { //if it's heterogeneous without bronze, effects are cancled out
            if ( rand(1,2) == 1 ) { $kitgender = "Female"; } else { $kitgender = "Male"; }
        }
    }
} //end heterogeneous circlets

echo "<p>Gender: $kitgender</p>";

Yeah, that's pretty.  Rand() function was used to generate a random number, which was used to calculate the probabilities.

Calculating mutations took the longest because there's no way to compile a static list of mutations given that custom mutations are an option.  The idea was to allow a textbox whose values are broken down into an array, but to figure out how to define arrays with variables took the longest - I was also the most tired when I got to this point.  But there's this neat little function call explode() that solved all my problems, and the resulting code was more elegant than I thought.

$mut1=stripslashes($_POST['mothermut']);
$mut2=stripslashes($_POST['fathermut']);

$totmut = ($mut1 . " " . $mut2);
$totmutpieces = explode(" ", $totmut);

$mutpiecescount = array_count_values($totmutpieces);

echo "<p>Mutations: <pre><p2>";
print_r(array_map("passmutation",$mutpiecescount));
echo "</p2></pre></p>";

You have the person using the generator write out all the mother's mutations and father's mutations in a format that lumps mutations into unique units, and that can identify mutations that both parents have: featheredwings sabres batwings, and so on.  You combine the two into one variable, then explode it into an array.  Count the values in the array and create a new array tabulating the counts for each mutation present in this breeding.  If there are 2, that means both parents have it and the kit has an 80% chance of inheriting that mutation.  Otherwise, there is only 1 of that mutation and the probability is 40%.  array_map() is a function that applies a user-made function on an array, and so I had my first taste of writing php functions:

function passmutation($x) {
    if ( $x == 2 ) { //both parents have this mutation
        if ( rand(1,5) != 5 ) { //80% probability of passing
            return "mutation passes";
        } else { return "mutation did not pass"; }
    }
    if ( $x == 1 ) { //only one parent has this mutation
        if ( rand(1,5) < 3 ) { //40% probability of passing
            return "mutation passes";
        } else { return "mutation did not pass"; }
    }
}

The only tricky part was that the definition of this function had to be placed at the very top because the majority of the code is actually in a loop.  I had a couple of choices for simulating single, twins and litter breedings.  I could have directed the user to different pages depending on their choice of breeding, but then I thought, what if we could just loop all these calculations, once for each kit?  A simple drop down menu to define the number of kits later (with rand() to determine how many kits in the litter), I had a functioning litter:

$num_kits=$_POST['num_kits'];
if ( $num_kits == "1twins" ) { echo "<center><p2>An amethyst was used, and identical twins were born.</p2>"; }
if ( $num_kits == "2" ) { echo "<p2>An amethyst was used, and fraternal twins were born.</p2>"; }
if ( $num_kits > 2 ) { echo "<p2>A garnet was used, and a litter of $num_kits was born.</p2><br>"; }

if ( $num_kits == "1twins" ) { $num_kits = 1; }

$n = 1;
while ( $n <= $num_kits ) {
     //$num_kits
     echo " <p2>----------------------</p2></center>
         <p><b>Kit #$n</b>";



ALL THOSE CODES HERE


$n++;
}

Thank goodness for 1's and 2's.

Somehow, despite all the cool coding and logic behind the machine, the outputs aren't all that impressive.  The return form is rather blah, and while I tried to spice up the layout design, in the end it seems a very small amount of information for all the work that I went through.  Each kit had a breed, gender, mutation and color, four small results of lots of logic statements.  I guess the most important lesson of this exercise is not how to combine all the possibilities (like the directory), but how to ignore unchosen options and make the machine work with only the given set, nothing more.

Kit #1
Gender: Male
Breed: Gryphon
Mutations:
Array
(
    [featheredwings] => mutation passes
    [sabres] => mutation did not pass
)
Mineral colors: this kit has a splash of black&white and brown.

And in the end, since the mutations were calculated using an array,  I couldn't break out of it and the presentation is somewhat strange.  I could figure it out with a little more time, but now that the core of the work is done I'm not really interested.  We'll probably fiddle around some more with the probabilities as Jean and I decide which are too generous.  But I've spent way too much time out of my academic schedule to work on this, and am falling behind on my reading.  Plus, my fingertips are chaffed from all the typing, and let's not think about my tendon damage.

No comments:

Post a Comment