IVs -> PID html/JavaScript Calculator

mingot

free agent
is a Site Content Manager Alumnusis a Battle Simulator Admin Alumnusis a Top Researcher Alumnusis a Contributor Alumnusis a Smogon Media Contributor Alumnusis an Administrator Alumnus
Inspired by this exchange, I decided that I would take a crack at converting X-Acts very useful Java Applet (IVs to PID) to JavaScript, and hopefully kick off a series of useful calculators that can be put on site. Sarenji did all of the user interface work, including input and validation to turn my rough1routines into something usable. So thank you X-Act and Sarenji!

To use, you may copy the information in the code block below into a file, and name it ivstopid.html, and open in a browser.

Currently the this has been tested in IE6, IE8, Firefox 3, and Chrome.

And here it is:

HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
    <head>
        <title>IVs to PID</title>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
        <script type="text/javascript">
        <!--
            /////////////////////////////////////////////////////////////////
            //  RNG CODE BEGIN
            /////////////////////////////////////////////////////////////////
            //  ctor to define our lcrngr class.  Very simple, holds
            //  the current seed that the _get method will use to
            //  get the previous number.
            function lcrngr(seed)
            {
                //  Set the initial seed of the RNG
                this.seed = seed;
            }
                
            //  Get the previous value        
            function lcrngr_get()
            {                              
                this.seed = (((0xEEB90000 * this.seed | 0) + (0xEB65 * this.seed)) + 0x0A3561A1) & 0xFFFFFFFF;                                                                            
                return this.seed >>> 16;                
            }
            lcrngr.prototype.lcrngr_get=lcrngr_get;
            /////////////////////////////////////////////////////////////////                        
                 
            /////////////////////////////////////////////////////////////////
            //  CALCULATION CODE BEGIN
            /////////////////////////////////////////////////////////////////
            //                
            function calculate(hp, atk, def, spa, spd, spe, nature, id)
            {    
                //  Create our array that we are going to return which
                //  will contain all of the information that we are
                //  interested in.
                var resultArray = [];                
                    
                //  We need to test the last IV with the high bit both
                //  cleared and set, as either can actually produce the
                //  same end IVs.
                var lastIvClear = 0;
                var lastIvSet = 0;                
                lastIvClear = spe + (spa << 5) + (spe << 10);                
                lastIvSet = lastIvClear ^ 0x8000;        
                
                var firstIvClear = 0;
                var firstIvSet = 0;                
                firstIvClear = hp  + (atk << 5) + (def << 10);                
                firstIvSet = firstIvClear ^ 0x8000;                                        
                
                for (var cnt = 0; cnt <= 0x1FFFE; cnt++)
                {    
                    //  We need to test with the high bit cleared 
                    //  and set so we're just going to do them both
                    //  in a single loop.
                    var lastivTest = 0;
                    
                    if(cnt % 2 == 0)
                    {
                        lastivTest = lastIvClear;
                    }
                    else
                    {
                        lastivTest = lastIvSet;
                    }
                
                    //  This is the seed for the string of RNG results
                    //  that we want to test for a match to supplied
                    //  IVs and nature.
                    var seedTest = (lastivTest << 16) + (cnt % 0xFFFF);
                
                    //  We are going to check this one now, reversing
                    //  through and seeing if we get a nature match.
                    var rng = new lcrngr(seedTest);
                                        
                    //  Get the next two numbers, as we need them 
                    //  to create our IV comparison.               
                    var rng1 = rng.lcrngr_get();
                    var rng2 = rng.lcrngr_get();
                     
                    if(rng1 == firstIvSet || rng1 == firstIvClear)                                             
                    {                           
                        var rng3 = rng.lcrngr_get();
                        var rng4 = rng.lcrngr_get();
                        
                        //  Keep track of our starting seed, which is what
                        //  we can pass to a forward running RNG to recreate
                        //  this sequence.
                        var method1Seed = rng.seed;
                        rng.lcrngr_get();
                        var method234Seed = rng.seed;
                    
                        //  This means we have at least a partial match
                        //  for methods 1, 2, and 3.  Lets go through
                        //  each one of these and build a PID/Check the 
                        //  nature.
                        var method1pid = unsign((rng2 << 16) + rng3);                                            
                        
                        if(method1pid % 25 == nature)
                        {
                            var sid = rng2 ^ rng3 ^ id;
                                            
                            resultArray[resultArray.length] 
                                = new seed("Method 1", unsign(method1Seed), method1pid, method1pid % 2, method1pid % 256, sid);                         
                        }
                        
                        var method2pid = unsign((rng3 << 16) + rng4);    
                        if(method2pid % 25 == nature)
                        {
                            var sid = rng3 ^ rng4 ^ id;
                                            
                            resultArray[resultArray.length] 
                                = new seed("Method 2", unsign(method234Seed), method2pid, method2pid % 2, method2pid % 256, sid);                         
                        }
                        
                        var method3pid = unsign((rng2 << 16) + rng4);    
                        if(method3pid % 25 == nature)
                        {
                            var sid = rng2 ^ rng4 ^ id;
                                            
                            resultArray[resultArray.length] 
                                = new seed("Method 3", unsign(method234Seed), method3pid, method3pid % 2, method3pid % 256, sid);                         
                        }                                                                
                    }                                                    
                                        
                    if(rng2 == firstIvSet || rng2 == firstIvClear) 
                    {
                        var rng3 = rng.lcrngr_get();
                        var rng4 = rng.lcrngr_get();
                        
                        //  Get the next RNG call that we need to create our seed
                        rng.lcrngr_get();
                        var method234Seed = rng.seed;
                    
                        var method4pid = unsign((rng3 << 16) + rng4);
                        
                        if(method4pid % 25 == nature)
                        {
                            var sid = rng3 ^ rng4 ^ id;
                        
                            resultArray[resultArray.length] 
                                = new seed("Method 4", unsign(method234Seed), method4pid, method4pid % 2, method4pid % 256, sid);                            
                        }                            
                    }
                }              
                                  
                return resultArray;
            }
                        
            /////////////////////////////////////////////////////////////////
            //  SEED 
            /////////////////////////////////////////////////////////////////
            function seed(method, seed, pid, ability, gender, sid)
            {
                this.method = method;
                this.seed = seed;
                this.pid = pid;
                this.ability = ability;
                this.gender = gender;
                this.sid = sid;
            }
                                   
            function unsign(v)
            { 
                return (v >>> 1) * 2 + (v & 1)             
            }    
            
            /////////////////////////////////////////////////////////////////
            //  DISPLAY / USER INTERFACE
            /////////////////////////////////////////////////////////////////
            //
            function calculateAndDisplay()
            {
                //  Pull all of our values that we are going to pass
                //  to the calculate function from our text boxes and
                //  validate the values.
                var hp  = parseInt(document.getElementById('TextHp').value);
                var atk = parseInt(document.getElementById('TextAtk').value);
                var def = parseInt(document.getElementById('TextDef').value);
                var spa = parseInt(document.getElementById('TextSpA').value);
                var spd = parseInt(document.getElementById('TextSpD').value);
                var spe = parseInt(document.getElementById('TextSpe').value);
                var nat = parseInt(document.getElementById('nature').value);
                var idn = parseInt(document.getElementById('idnum').value);
                
                //alert("Hp: " + hp + "; Atk: " + atk + "; Def: " + def + "; SpA: " + spa + "; SpD " + spd + "; Spe: " + spe + "; Nature: " + nat + "; id: " + idn);
                
                var resultArray = calculate(hp, atk, def, spa, spd, spe, nat, idn);
                
                displayResults(resultArray);
            }
            
            function fixField(field, minValue, maxValue)
            {
                if (field.value.search(/^[0-9]+$/) === -1)
                    field.value = minValue;
                else if (field.value < minValue)
                    field.value = minValue;
                else if (field.value > maxValue)
                    field.value = maxValue;
            }
            
            function displayResults(resultArray)
            {
                var tbl = document.getElementById('resultTable');
                
                while(tbl.rows.length > 1)
                    tbl.deleteRow(tbl.rows.length - 1);
                
                for(i=0;i<resultArray.length;i++) 
                {                                       
                    var lastRow = tbl.rows.length;                      
                    var iteration = lastRow;
                    var row = tbl.insertRow(lastRow);
                                            
                    var cellMethod = row.insertCell(0);
                    var textNode = document.createTextNode(resultArray[i].method);
                    cellMethod.appendChild(textNode);
                      
                    var cellSeed = row.insertCell(1);
                    var textNode = document.createTextNode(resultArray[i].seed.toString(16).toUpperCase());
                    cellSeed.appendChild(textNode);
                      
                    var cellPID = row.insertCell(2);
                    var textNode = document.createTextNode(resultArray[i].pid.toString(16).toUpperCase());
                    cellPID.appendChild(textNode);
                      
                    var cellAbility = row.insertCell(3);
                    var textNode = document.createTextNode(resultArray[i].ability.toString(16).toUpperCase());
                    cellAbility.appendChild(textNode);
                      
                    var cellGender = row.insertCell(4);
                    var textNode = document.createTextNode(resultArray[i].gender.toString());
                    cellGender.appendChild(textNode);
                      
                    var cellSID = row.insertCell(5);
                    var textNode = document.createTextNode(resultArray[i].sid.toString());
                    cellSID.appendChild(textNode);                                                                                                                      
                 }            
            }
            
            function hideError() {
                document.getElementById('jserror').style.display = 'none'; 
            }
        // -->
        </script>
        <style type="text/css">
            body {
                font-family: Verdana, serif;
                color: #444;
            }
            table {
                padding: 10px;
                border: 1px solid black;
                margin: 10px;
            }
            table#resultTable {
                border-collapse:collapse;
                border: none;
            }
            table#resultTable th, table#resultTable td {
                border: 1px solid black;
                padding: 5px 10px;
            }
            label {
                font-weight: bold;
                font-style: italic;
                float: right;
            }
            input#FindPID {
                margin: 5px 0;
                width: 80%;
                padding: 10px;
            }
            h1 {
                font-size: 1.5em;
                font-weight: normal;
                margin-bottom: 0;
                border-bottom: 1px solid #ccc;
            }
            h2 {
                font-family: Helvetica,Arial,sans-serif;
                font-style: italic;
                font-size: 1em;
                font-weight: normal;
                margin: 0;
            }
            #jserror {
                background: #FFDDDD;
                border-top: 1px solid #FFAAAA;
                border-bottom: 1px solid #FFAAAA;
                padding: 10px;
                margin: 10px 0;
            }
        </style>
    </head>
    <body onload="hideError();">
        <h1>IVs to PID</h1>
        <h2>—by X-Act, mingot, and Sarenji</h2>
        
        <div id="jserror">
            <strong>Warning:</strong> JavaScript is disabled! Please enable 
            JavaScript to use this.
        </div>
        
        <table id="form">
            <tr>
                <td><label for="TextHp">HP:</label ></td>
                <td><input id="TextHp" type="text" size="2" maxlength="2" value="31" onblur="fixField(this, 0, 31);"/></td>

                <td><label for="TextAtk">Atk:</label></td>
                <td><input id="TextAtk" type="text" size="2" maxlength="2" value="31" onblur="fixField(this, 0, 31);"/></td>
                <td><label for="TextDef">Def:</label></td>
                <td><input id="TextDef" type="text" size="2" maxlength="2" value="31" onblur="fixField(this, 0, 31);"/></td>

            </tr>
            <tr>
                <td><label for="TextSpA">SpA:</label></td>

                <td><input id="TextSpA" type="text" size="2" maxlength="2" value="31" onblur="fixField(this, 0, 31);"/></td>
                <td><label for="TextSpD">SpD:</label></td>
                <td><input id="TextSpD" type="text" size="2" maxlength="2" value="31" onblur="fixField(this, 0, 31);"/></td>
                <td><label for="TextSpe">Spe:</label></td>

                <td><input id="TextSpe" type="text" size="2" maxlength="2" value="31" onblur="fixField(this, 0, 31);"/></td>
            </tr>
            <tr>
                <td><label for="nature">Nature:</label></td>

                <td><select id="nature">
                    <option value="0">Hardy</option>
                    <option value="1">Lonely</option>

                    <option value="2">Brave</option>
                    <option value="3">Adamant</option>
                    <option value="4">Naughty</option>

                    <option value="5">Bold</option>
                    <option value="6">Docile</option>
                    <option value="7">Relaxed</option>

                    <option value="8">Impish</option>
                    <option value="9">Lax</option>
                    <option value="10">Timid</option>

                    <option value="11">Hasty</option>
                    <option value="12">Serious</option>
                    <option value="13">Jolly</option>

                    <option value="14">Naive</option>
                    <option value="15">Modest</option>
                    <option value="16">Mild</option>
                    <option value="17">Quiet</option>
                    <option value="18">Bashful</option>
                    <option value="19">Rash</option>

                    <option value="20">Calm</option>
                    <option value="21">Gentle</option>
                    <option value="22">Sassy</option>

                    <option value="23">Careful</option>
                    <option value="24">Quirky</option>
                </select></td>

                <td><label for="idnum">ID:</label></td>
                <td><input id="idnum" type="text" size="5" maxlength="5" value="0" onblur="fixField(this, 0, 65535);"/></td>
            </tr>
            <tr>

                <th colspan="16"><input id="FindPID" type="button" value="Find PID" onclick="calculateAndDisplay();"/></th>
            </tr>
        </table>
        
        <table id="resultTable">

            <tr>
                <th>Method</th>
                <th>Seed</th>
                <th>PID</th>

                <th>Ability</th>
                <th>Gender</th>

                <th>SID</th>
            </tr>
        </table>
    
    </body>
</html>
 

X-Act

np: Biffy Clyro - Shock Shock
is a Site Content Manager Alumnusis a Programmer Alumnusis a Smogon Discord Contributor Alumnusis a Top Researcher Alumnusis a Top CAP Contributor Alumnusis a Top Tiering Contributor Alumnusis a Top Contributor Alumnusis a Smogon Media Contributor Alumnusis an Administrator Alumnus
I tested this and it works, but it seems to work very slowly. Looking at the code, I'm not seeing anything that is obviously downgrading performance. I don't know if anyone can spot things that can speed up performance or if Javascript is really that slow.
 
Nice work, I just tested it too, but it's not very slow here. Maybe because I'm running localhost?

Anyway, if you guys don't mind, I'm going to try to make it faster.
 
Hey guys really new to PIDs and I have two quick questions,

When the form asks for 'ID:' is this the Trainer ID appearing in my Poke's summary?

And when the PID does show, where will it show?
(I can't tell if my comp is going too slow or I just can't see it. :/ )
 

mingot

free agent
is a Site Content Manager Alumnusis a Battle Simulator Admin Alumnusis a Top Researcher Alumnusis a Contributor Alumnusis a Smogon Media Contributor Alumnusis an Administrator Alumnus
Yes, it is. I tend to think that if you are not seeing results there is some issue with your browser and JavaScript when loading from a file directly on your system. I have had this problem reported before and Sarenji added some code to warn when JavaScript was not enabled for the page. I will see if I can get him to update with that when I next see him.
 
Performance: It can be slow. It takes my machine about three seconds to run through the calculations and it takes longer for some. Additionally, under IE it currently triggers a "script taking to long to execute" dialog (after about 1 second, go figure) and gives the option to cancel. So I am definately looking for any tips to speed up the calculation.
i'll go through this and tell you whatever i can find, but at cursory glance i found stuff i couldn't quite find the reason for. for example, under mult32, you compute the or of variables against zero. shouldn't the code work the same without those steps?
 
For all the OS X users out there, I tested this and it indeed runs on Safari 3.1.1, albeit somewhat slowly as other users have reported. If you guys can optimize it so it runs faster, that would be great, but otherwise, good work!
 

mingot

free agent
is a Site Content Manager Alumnusis a Battle Simulator Admin Alumnusis a Top Researcher Alumnusis a Contributor Alumnusis a Smogon Media Contributor Alumnusis an Administrator Alumnus
i'll go through this and tell you whatever i can find, but at cursory glance i found stuff i couldn't quite find the reason for. for example, under mult32, you compute the or of variables against zero. shouldn't the code work the same without those steps?
The "| 0" basically truncates the number to 32 bits, but as we know all of the inputs to the function will already be <= 0xFFFFFFFF I can actually remove these, so that saves us having to do do that a quarter of a million times. Found one other instruction to remove from that function, too.

Thanks and good eye.

Updated OP with newest version of the code.

And thank you, Arseus, for testing on Safari!

EDIT: Well at the expense of any beauty the code may have had at some point I've managed to really speed it up. Lots of inlining, better 32 bit multiplication, and many other improvements. The IE "slow script" dialog is a thing of the past and this runs in sub 1 second for me on Firefox.
 

Users Who Are Viewing This Thread (Users: 1, Guests: 0)

Top