<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ZF032726</title>
    <description>The latest articles on DEV Community by ZF032726 (@zf032726).</description>
    <link>https://dev.to/zf032726</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4177829%2F7b72401b-d6cf-44c9-ac45-89d17c6063e7.png</url>
      <title>DEV Community: ZF032726</title>
      <link>https://dev.to/zf032726</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zf032726"/>
    <language>en</language>
    <item>
      <title>Reading a locked Pet Index: verifying 199 Roblox pets without unlocking them</title>
      <dc:creator>ZF032726</dc:creator>
      <pubDate>Wed, 14 Oct 2026 05:19:30 +0000</pubDate>
      <link>https://dev.to/zf032726/reading-a-locked-pet-index-verifying-199-roblox-pets-without-unlocking-them-4ei4</link>
      <guid>https://dev.to/zf032726/reading-a-locked-pet-index-verifying-199-roblox-pets-without-unlocking-them-4ei4</guid>
      <description>&lt;p&gt;Steal An Egg is a Roblox game where you steal eggs, hatch pets, and the pets earn money every second. How much a pet earns depends on its base income, its weight and its mutations, so players keep asking the same question: what does &lt;em&gt;this&lt;/em&gt; pet make?&lt;/p&gt;

&lt;p&gt;I built a small calculator site to answer that. The code was the easy part. Most of my time went into a different question: which numbers can I actually trust?&lt;/p&gt;

&lt;p&gt;A note on process: AI agents did a lot of the legwork here, reading the in-game index, logging slots, writing the build and the tests, while I set the rules and checked the results. The verification approach is the part I think is worth sharing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with the numbers I found
&lt;/h2&gt;

&lt;p&gt;When I started, the sources disagreed with each other, and some numbers didn't come from anywhere I could trace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The community wiki keeps pet data in two Lua data modules, and they didn't always agree. One listed Light Dove at $225/s, the other at $225K/s. For 18 pets from limited eggs, the "rarity" field held the egg's group name ("Monster", "Brainrot") instead of an actual rarity.&lt;/li&gt;
&lt;li&gt;Guides published in August and September listed the Golden mutation as ×2. The wiki said ×2.5.&lt;/li&gt;
&lt;li&gt;One existing calculator shipped preset pets whose names and incomes don't match any slot in the game's own Pet Index. A pet earning $9/s, for example, would need a slot with a $900 reward, and there isn't one.&lt;/li&gt;
&lt;li&gt;Nobody had published how multiple mutations stack. One tool simply adds them together, and I couldn't find a source for that either way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is surprising for a game that updates every few weeks. But a calculator that multiplies an invented input by a correct formula still gives you a confident, wrong number. I wanted every value on the site to say where it came from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two sources, one rule
&lt;/h2&gt;

&lt;p&gt;I used two sources:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Steal An Egg Wiki on &lt;a href="https://stealanegg.fandom.com/" rel="noopener noreferrer"&gt;Fandom&lt;/a&gt;&lt;/strong&gt; (CC BY-SA). Its data modules give names, rarities, base incomes and base weights for 198 pets, plus 8 names for the newest biome. Every page on the site credits the wiki in the footer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The game itself.&lt;/strong&gt; On 2026-10-09 I went through the in-game Pet Index on a fresh account, logged all 184 slots to a CSV (112 in the World tab, 72 in Limited) and kept screenshots.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When they conflict, the order is: in-game reading &amp;gt; official page &amp;gt; wiki &amp;gt; newer third-party source. The CSVs are frozen in the repo, and the build script only ever reads them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trick: locked slots still show their reward
&lt;/h2&gt;

&lt;p&gt;On a fresh account the Pet Index is almost all "???". I had unlocked exactly one pet, a Chicken. But every locked slot still shows three things: its rarity, the cash reward for discovering it, and a speed reward.&lt;/p&gt;

&lt;p&gt;The Chicken earns $1/s and its slot rewards $100. So the hypothesis was simple: &lt;strong&gt;discovery reward = 100 × base income&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If that holds, I don't need to unlock anything. For each slot there should be exactly one wiki pet in the same tab, with the same rarity, whose income × 100 equals the reward. If the rule were wrong, the slots wouldn't line up.&lt;/p&gt;

&lt;p&gt;There was one wrinkle. The game abbreviates big numbers to one decimal and &lt;strong&gt;truncates&lt;/strong&gt; instead of rounding, so a $18.5K/s pet shows a reward of "$1.8M", not "$1.9M". The site uses the same formatter as the game (simplified here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;UNITS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;K&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;M&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;T&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Qa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Qi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Oc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Dc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// In-game abbreviation: one decimal, truncated not rounded ($1.75B -&amp;gt; $1.7B)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatCompact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;UNITS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;0$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;UNITS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result: outside the newest biome, every slot was claimed by exactly one pet. In the World tab, 102 of 104 slots matched exactly, and the other 2 only differed because of truncation. In the Limited tab, 71 of 72 matched exactly, plus one more truncation case.&lt;/p&gt;

&lt;p&gt;The same pass also fixed things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;19 rarities.&lt;/strong&gt; The 18 limited-egg pets where the wiki used the group name have real rarities in the game (Tung Tung Sahur is Rare, Crawler is Cosmic), and Octophant is Divine, not Eternal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Light Dove and Flame Sprite.&lt;/strong&gt; The slot reward is $22.5M, so the income is $225K/s, not $225/s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;22 wiki pets have no slot at all&lt;/strong&gt;: event pets, fusion results and a few others. That fits with them coming from outside the normal eggs, so the site labels them as not in the index instead of guessing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The limits, honestly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This matches values, not names. The Angels &amp;amp; Demons tab has 8 pairs of pets with identical income, so I can confirm both pets of a pair exist, but not which slot is which.&lt;/li&gt;
&lt;li&gt;The newest biome has names on the wiki but no incomes, so 7 of its 8 pets can't be matched yet. They're left out of the calculator.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One Golden Chicken settled ×2 vs ×2.5
&lt;/h2&gt;

&lt;p&gt;The index gives base incomes, but mutations needed a live reading. The pet panel doesn't show weight, but the label floating above a pet in your base does. My only pet was a Golden Chicken, and its label said &lt;strong&gt;61 kg, $9/s&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The wiki's size formula (for weight ratios up to 125):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ratio  = weight / baseWeight      // 61 / 8 = 7.625
size   = ratio ^ (37/60)          // ≈ 3.50
income = base × size × mutation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;With Golden ×2.5: 1 × 3.50 × 2.5 = &lt;strong&gt;8.75&lt;/strong&gt;. The game pays whole dollars, so it shows &lt;strong&gt;$9&lt;/strong&gt;. Match.&lt;/li&gt;
&lt;li&gt;With Golden ×2: 1 × 3.50 × 2 = &lt;strong&gt;7.00&lt;/strong&gt;, which would show &lt;strong&gt;$7&lt;/strong&gt;. No match.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Weight is displayed as a whole number, so I re-ran it for 60.5 to 61.5 kg. Same conclusion. The cash counter agreed too: over about 1,095 seconds my money went up by $9,856, and over another 745 seconds by $6,711. Both work out to $9.00 per second, not $8.75, which is why I think payouts are rounded to whole dollars.&lt;/p&gt;

&lt;p&gt;It's one pet at one weight, so I'm careful about what it proves. It rules out ×2 (that would need a base weight around 5 kg instead of 8), and it's consistent with the wiki's formula and the 8 kg base weight. It says nothing about how two mutations stack. I still don't know that, so when you pick more than one, the calculator shows both the added and the multiplied result instead of choosing for you.&lt;/p&gt;

&lt;p&gt;That reading is now a pair of unit tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Golden Chicken: $1/s base, 8 kg base, 61 kg, Golden x2.5 -&amp;gt; 8.75, shown as $9&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SAE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;baseWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;near&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perSec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;8.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.005&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;per second&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SAE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gameDisplayPerSec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perSec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$9&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Golden x2 would NOT match the in-game $9/s reading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SAE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;baseWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SAE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gameDisplayPerSec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perSec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$7&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another test runs the size formula against three weight and income readings for a different pet that a games site published. All three land within $0.1M.&lt;/p&gt;

&lt;h2&gt;
  
  
  The build: static HTML, no framework
&lt;/h2&gt;

&lt;p&gt;The site is plain HTML, CSS and vanilla JS. Everything, from the build scripts and browser code to the checks and tests, is about 2,800 lines of JavaScript. No framework, no CDN, and no client-side rendering of the data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data/&lt;/code&gt; holds the frozen CSVs. &lt;code&gt;build/&lt;/code&gt; reads them and writes &lt;code&gt;dist/&lt;/code&gt;: the pages, a &lt;code&gt;pets.json&lt;/code&gt; for the calculators, the sitemap and robots.txt.&lt;/li&gt;
&lt;li&gt;Every value table is rendered into the HTML at build time. A small post-processing step adds &lt;code&gt;data-label="&amp;lt;column name&amp;gt;"&lt;/code&gt; to every &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt;, so on narrow screens CSS can turn each row into a labelled card instead of a table that scrolls sideways.&lt;/li&gt;
&lt;li&gt;Each pet gets a label from its check status: &lt;strong&gt;Verified in-game&lt;/strong&gt;, &lt;strong&gt;Estimated&lt;/strong&gt; (income derived from the index reward) or &lt;strong&gt;Unverified&lt;/strong&gt;. The label logic lives in the build, and a test makes sure nothing still marked as pending can show up as verified.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tests and the overflow check
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npm test&lt;/code&gt; runs 18 &lt;code&gt;node:test&lt;/code&gt; cases: the Golden Chicken reading, the size formula on both sides of the 125× breakpoint, the truncating formatter, mutation stacking, input validation (it must never produce &lt;code&gt;NaN&lt;/code&gt;), and checks on the built output such as "no external scripts" and "every table cell has a data-label".&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run check&lt;/code&gt; adds two more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An SEO check&lt;/strong&gt;: title, description, H1, canonical URL, and no dead internal links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A layout check.&lt;/strong&gt; It starts a clean headless Chrome (fresh profile, no extensions), talks to it over the DevTools Protocol using the WebSocket built into Node 22, so no Puppeteer, and opens all 10 pages at &lt;strong&gt;360, 400 and 768 px&lt;/strong&gt;. It fails if the page is wider than the viewport and lists the elements sticking out. It also fails on any console error or warning, uncaught exception or failed request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The measurement itself is tiny:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollWidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// sw &amp;gt; w means horizontal overflow: report the elements whose right edge is past w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tables with five or six columns are the usual way a page like this breaks on a phone, so I made overflow a hard failure instead of something to eyeball.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;

&lt;p&gt;Cloudflare Pages runs &lt;code&gt;npm run build&lt;/code&gt; and serves &lt;code&gt;dist/&lt;/code&gt;. The first build took about two minutes, and the site was live on its own domain about ten minutes after I pushed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Of the 199 pets with a known base income, &lt;strong&gt;176 match a slot in the in-game Pet Index&lt;/strong&gt; by rarity and reward. 22 have no slot, and 1 is matched by rarity only and still marked as pending. It's all on &lt;a href="https://steal-an-egg-calculator.com" rel="noopener noreferrer"&gt;Steal An Egg Calculator&lt;/a&gt;, where each pet shows whether its numbers were checked in-game or come from the wiki.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read the game first.&lt;/strong&gt; I compared third-party sources against each other before I opened the Pet Index. About 40 minutes in the game settled most of those disagreements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn every reading into a test.&lt;/strong&gt; A screenshot is easy to forget. A failing test isn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write down what you can't verify.&lt;/strong&gt; "Stacking unverified, here are both readings" is more useful than a confident guess.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've done something similar for another game, working out the numbers from what the UI gives away, I'd like to hear how you approached it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>gamedev</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
