# From Species Data to Battle Stats

Lecture ID: lecture_w10_d01
Week 10, Day 1

## Objectives
- Use Pokemon Showdown as a secondary comparison for battle data hooks, Dex/mods, simulator turns, and regression testing without replacing FireRed as the main architecture source.
- Connect week 10 material to the overall course plan.

## Transcript
All right, let’s bring week ten into focus.

Last week, when we were working with creature records, we separated two ideas that are easy to collapse if you are only playing the game and not reading the architecture. We separated the stored creature from the interpreted creature. The stored creature is the thing in the party slot or box slot: species, level, experience, personality, IV-like values, EV-like values, moves, PP, friendship, held item, status, and so on. The interpreted creature is what the engine needs right now: maximum HP, current battle stats, move usability, evolution eligibility, learnable moves, and visible summary-screen facts.

Today we are moving through that boundary. We are not starting over with “what is a Pokemon struct,” because we already did that. We are asking the more architectural question: how does FireRed get from species data plus individual stored state to the battle-facing numbers that a turn engine can consume?

And I want you to hear the phrasing carefully. I did not say “how does FireRed store attack.” I said, “how does FireRed derive battle-facing attack.” That distinction is the center of today’s lecture.

If you look at the source focus for this week, the important path is `pokefirered/src/pokemon.c`, especially the chunks named in your packet: `chunk_4573760bdda53819`, which we are using for Pokemon runtime calculations; `chunk_f56e3a6588d98715`, which we are using for data mutation and lookup; and `chunk_c089f03b70501fda`, which we are using for creature state helpers. Those chunks sit on top of the species table work from `pokefirered/src/data/pokemon/species_info.h`, specifically `chunk_2e759c52d0f4129a`, and later this week we will connect them to `pokefirered/src/data/pokemon/tmhm_learnsets.h`.

So, board cue here: I’m going to write a pipeline, not a formula yet.

Species table. Stored individual record. Data accessor. Stat calculator. Battle stat field. Battle turn consumer.

That is the architecture. The arithmetic is important, but the shape of the data flow is more important.

Now, in a beginner implementation, someone might make a species object with an attack value and then just copy that into a battle object. That works for a toy creature game for about fifteen minutes. But a FireRed-style RPG needs species identity, level, experience, individual variance, training variance, nature-like modifiers in later generations, current HP preservation, status, move PP, evolution transitions, and script-driven mutation. Even in Generation Three, where the model is simpler than modern Pokemon, the runtime stat is not simply stored in the species record.

The species table gives base stats. Base HP, base Attack, base Defense, base Speed, base Special Attack, base Special Defense. Those are species-level constants. They say, “This species tends to be shaped like this.” But they are not the actual numbers shown on the summary screen for an individual party member. The actual party member has a level. It has hidden per-individual variation. It has accumulated training values. It has a personality value that may affect derived properties. It has a current HP value that has to survive recalculation carefully. If the maximum HP changes after leveling or evolution, the current HP cannot be blindly overwritten without creating visible nonsense.

Check for understanding: if Charmander evolves into Charmeleon and its max HP increases, what should happen to current HP? Should it become full? Should it stay numerically identical? Should it increase by the difference between old max HP and new max HP? FireRed has to answer that kind of question consistently, because evolution is not just a graphic and a species ID change. It is a controlled mutation of stored creature state followed by recalculation.

That is why `pokefirered/src/pokemon.c chunk_f56e3a6588d98715`, the packet’s data mutation and lookup area, matters in the same week as `chunk_4573760bdda53819`, the runtime calculation area. Mutation and calculation are coupled, but they should not be the same conceptual operation. Mutation changes what the creature is. Calculation asks, given what the creature is, what numbers should the rest of the engine see?

Let’s make this concrete.

Suppose we are building the lab version with three species. We will use Sprout, Emberling, and Shellbit as fake species. Sprout has base HP 45, Attack 49, Defense 49, Speed 45, Special Attack 65, Special Defense 65. Emberling has base HP 39, Attack 52, Defense 43, Speed 65, Special Attack 60, Special Defense 50. Shellbit has base HP 44, Attack 48, Defense 65, Speed 43, Special Attack 50, Special Defense 64. Those are species table values. In your species table, those numbers live in static data. They are not modified when an individual levels.

Now create one stored creature: species Emberling, level 16, experience appropriate for level 16, IV-like attack value 12, EV-like attack value maybe 1024 if we are modeling Generation Three style, current HP 31, moves Scratch and Ember, PP values attached to those move slots. The species table says Emberling’s base Attack is 52. But the stat calculator combines base Attack, level, individual value, effort value, and any generation-specific modifier rules. The result is what the battle engine should read when it needs the creature’s Attack.

Here’s the important software design idea: the battle engine should not have to know how base stats, IVs, EVs, and level combine. It should be able to ask for the battle-ready number, or receive a battle struct already populated with those numbers. If the turn executor is also doing stat formula reconstruction, you have leaked species architecture into battle resolution. That is the kind of coupling that makes later mechanics painful.

Now pause and connect this to earlier weeks. In week one and week two, we talked about FireRed as a set of cooperating state machines and tables. In the runtime shell, `pokefirered/src/main.c chunk_b35676322b169f85` gave us `AgbMain`, and `pokefirered/src/main.c chunk_6cfd90f6d0136799` gave us the callback handoff pattern through `SetMainCallback2`. Then `pokefirered/src/task.c chunk_cb0bb91a147bfb59` and `chunk_3ffebc0341225936` gave us task creation and per-frame task dispatch through `CreateTask` and `RunTasks`. That was not “just engine trivia.” It trained us to look for boundaries: who owns the loop, who owns the state, who gets called next?

Today is the same skill applied inside creature data. Who owns the permanent data? Who owns derived numbers? Who owns recalculation after mutation? Who consumes the results?

That is the through-line of the course.

Now, let’s bring Pokemon Showdown in, but carefully. Showdown is not our authority for FireRed mechanics. It is a modern simulator architecture with a different runtime environment, different language, different mod system, and a different purpose. FireRed is a shipped GBA game architecture. Showdown is a battle simulator and data platform. But Showdown is very useful as a comparison because it makes certain hooks explicit.

In your research packet, one cited Showdown example is `pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f`. That excerpt shows damage logic accepting a move as a string, number, or active move object, resolving it through the Dex, checking immunities, handling OHKO moves, invoking a damage callback, supporting level-based damage, fixed damage, and then continuing through the damage path. The reason I want you to notice that is not because FireRed does it the same way. It does not. The point is that Showdown exposes a hook-oriented simulator design: data can carry callbacks, generation mods can override behavior, and the battle calculation path deliberately invokes those hooks.

FireRed’s architecture is more static, more C-table-driven, and more compiled. Its species data and runtime calculation functions are closer to fixed contracts. But both systems face the same deeper problem: battle behavior cannot be hardcoded as one flat formula if the game needs exceptions, species differences, move differences, held item effects, generation rules, and regression protection.

Another Showdown comparison from the packet is `pokemon-showdown/data/mods/gen4/items.ts chunk_ef574f63c118f2f6`. The excerpt includes item behavior such as Life Orb-like damage modification and Light Ball modifying Pikachu’s offensive power through an `onBasePower` hook. Again, do not import that into FireRed as if FireRed’s item engine is TypeScript callbacks. The comparison is architectural: Showdown places battle effects near data definitions through named event hooks. FireRed tends to represent many mechanics through C functions, constants, tables, flags, and switch-like behavior. One is hook-heavy and mod-aware; the other is compiled and table-oriented.

So if I ask, “Where does the stat come from?” a Showdown-style answer might involve Dex species data, set data, battle Pokemon construction, and event hooks that modify effective stats or damage. A FireRed-style answer involves the species info table, stored Pokemon data, accessor functions, stat calculation routines in `pokefirered/src/pokemon.c chunk_4573760bdda53819`, and mutations or lookups in `chunk_f56e3a6588d98715`.

Both systems separate base data from battle-facing behavior. They just draw the seams differently.

Let me put a small example on the board. Imagine the battle engine needs to resolve a physical attack.

In a naive design, the move code says: attacker.species.baseAttack times level, divided by something, minus defender.species.baseDefense. That is wrong architecturally. It reaches backward into species data and ignores the stored individual.

In a better FireRed-like design, the battle setup or Pokemon runtime layer has already produced attacker.attack and defender.defense. The move resolution uses those battle stats, possibly after battle-stage modifiers, burn modifiers, move-specific exceptions, item effects, and so forth. Species base Attack is upstream. It is not the direct input to every damage calculation.

Now I’m going to say something that tends to separate advanced students from intermediate students: derived data is not less real than stored data. It is just real at a different moment.

A calculated Attack stat is real during battle. A maximum HP value is real when rendering the summary screen. A level-up learnset result is real at the moment the level-up routine asks, “Should this creature learn a move now?” But these values are not all equally worth storing permanently. If you store every derived value, you risk stale state. If you derive everything repeatedly, you risk scattered formulas and inconsistent behavior. The architecture is about deciding where derivation happens and how consumers get the result.

FireRed’s `GetMonData`-style access pattern, which we covered last week, is part of that answer. I am not going to re-teach it, but I want to reuse it. Accessors centralize reads and writes across the boxed and party representations. When runtime calculation code needs level, species, IV-like fields, or EV-like fields, it should use the creature data contract rather than poking through arbitrary memory from every subsystem. That is the same principle as headers acting as cross-module contracts from week two.

Let’s do a check. If a UI screen, the battle engine, and the daycare system all need to know a Pokemon’s level, what is the architectural danger if each module computes or reads it differently? Yes: inconsistent behavior. One screen says level 17, battle treats it as level 16, daycare uses experience directly and has an off-by-one edge. Centralized data access does not magically remove bugs, but it gives the codebase fewer places for that bug to live.

Now, because this is week ten, I want to raise the sophistication. Earlier homework asked you to design minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence. Some of those submissions had a pattern where the party object became a giant bag of fields, and every system reached into it freely. That was acceptable for a week-nine minimal clone if you were proving you understood the systems. For week ten, that bar goes up. Your lab this week asks you to write a species table plus stat calculator, then add a TM/HM compatibility bitset and a level-up move resolver for a three-species test set. That means you must now distinguish data declaration, stored individual state, derived calculations, and eligibility queries.

Let’s take the TM/HM bitset briefly, because it helps clarify the stat story even though the second lecture this week will focus more on learnsets.

A TM/HM compatibility table answers a yes-or-no question: can this species learn this machine move? It is not the same as the current moves list. The current moves list is stored on the individual. The TM/HM learnset is species-level eligibility. FireRed’s high-signal table for that is named in the packet as `pokefirered/src/data/pokemon/tmhm_learnsets.h`. When a player uses a TM, the game does not ask, “Is Thunderbolt currently in move slot three?” It asks something more like, “Given this creature’s species, is the TM move compatible, and are there other restrictions such as egg, form, or context depending on the game?” Then, if allowed, the mutation changes the individual’s move slot and PP state.

That is parallel to stats. Species base stats are eligibility-like input data for calculation. They are not the current runtime stats themselves.

Now compare that with Showdown’s modern learnset modeling. The packet names `pokemon-showdown/data/learnsets.ts` as a high-signal modern learnset file, and it also names modded learnset files like `pokemon-showdown/data/mods/gen9dlc1/learnsets.ts`, `pokemon-showdown/data/mods/gen9predlc/learnsets.ts`, and `pokemon-showdown/data/mods/gen8dlc1/learnsets.ts`. We use those as a secondary comparison because Showdown has to represent learnability across generations, formats, and mod layers. FireRed does not need a runtime Dex mod stack in the same way. FireRed needs the specific tables for FireRed. But Showdown reminds us that “can learn move X” is not a universal static truth. It is relative to ruleset, generation, and source.

This is why I keep using the phrase “data hook” carefully. In Showdown, many behaviors are explicitly hooks: `onModifyMove`, `onBasePower`, `damageCallback`, and similar mechanisms. In `pokemon-showdown/data/mods/gen9ssb/moves.ts chunk_f99c7de3b95d7f40`, the excerpt shows a move modifying its own behavior depending on the Pokemon’s species, including changing flags, healing behavior, volatile status, and set changes. That is a very direct example of data-defined battle behavior. FireRed’s data does not usually look like that. But FireRed still has hook points in the broader sense: functions that consult data and branch into behavior. The difference is that the hook is often a C function reading tables rather than a callback embedded in a TypeScript object.

Now, back to FireRed stats.

Let’s write the conceptual stat calculation inputs on the board.

Species base stat. Level. Individual value. Effort value. Nature or personality-derived modifier, depending on generation. Current HP preservation rules. Status and battle-stage modifiers later in battle.

For FireRed specifically, we care about the Generation Three model, but as architects we should categorize inputs by lifetime. Species base stat has application lifetime; it is compiled data. Level has creature lifetime; it changes as the creature gains experience. Individual value has creature lifetime; it is assigned and stored. Effort value has creature lifetime but changes through battle rewards, vitamins, or other mechanics. Current battle-stage modifiers have battle lifetime. Temporary move or item effects may have turn lifetime or volatile lifetime.

That lifetime classification tells you where data belongs.

Let me give you a bug story in abstract form. Suppose you implement evolution by changing only species ID. You do not recalculate stats. What happens? The summary screen may still show old max HP, old Attack, old Defense, until some later event forces recalculation. Or worse, battle uses old derived stats while the UI shows the new species name and sprite. That is a stale derived-data bug.

Now suppose you fix that by recalculating every stat whenever anyone calls getAttack. That removes staleness, but now you may repeatedly recompute values in hot paths, and you may accidentally mix permanent stats with battle-stage modifiers. You might also make current HP handling difficult, because max HP recalculation has a side effect relationship with current HP. Some derived values are pure queries. Others need controlled update moments.

So FireRed-style architecture tends to have routines that recalculate and write party Pokemon stats at specific times: creation, level-up, evolution, healing or summary preparation, battle setup, and so on. The packet’s focus on `pokefirered/src/pokemon.c chunk_4573760bdda53819` is exactly about that runtime calculation layer. You should read that chunk with the question, “Which values are being read from stored data, which constants are being pulled from species data, and which fields are being updated as derived runtime state?”

Let’s slow down and test the model.

Question: if I change a species table’s base Speed from 45 to 90, do all existing stored creatures of that species need their save records rewritten?

No. Not in a clean architecture. The stored creature has species identity and individual state. The base Speed lives in species data. Once the runtime recalculates, derived Speed changes. Now, in a shipped game, patching compiled species data is not trivial, but architecturally the save record should not duplicate every species base stat. That would be redundant and brittle.

Question: if I change an individual creature’s effort value for Speed, do I need to change the species table?

No. That is individual state. It belongs to the creature record. The species table stays the same.

Question: if I apply a battle-stage Speed drop during battle, do I mutate the stored Speed stat permanently?

No. That is battle state. It may read from the calculated Speed as a base, but the stage modifier belongs to the battle runtime. If you store Growl or Tail Whip effects back into the party record as permanent stat changes, your architecture has confused battle lifetime with creature lifetime.

That last one is a classic design error, and it appears in many student prototypes. A move modifies the target’s attack, so the code does `target.attack -= 5` on the party object. That seems to work for one battle. Then the creature leaves battle permanently weakened, or the summary screen shows battle-modified stats, or saving the game persists temporary effects. The fix is not just “add a reset.” The fix is to model the lifetimes correctly.

Now, when we talk about battle stats in FireRed, we also need to account for move slots and PP because stats do not enter battle alone. A party Pokemon enters battle as a package of identity, current HP, status, moves, PP, calculated stats, and volatile battle-only fields. Move slots are stored individual state. PP values are stored per move slot because PP changes through use. A species learnset can say a creature may learn Ember at level seven, but that does not mean every individual currently has Ember. The individual may have forgotten it. A TM compatibility bitset can say the species can learn Dig, but that does not mean Dig is present in slot one.

This gives us a second board pipeline.

Learnset table or TM/HM table. Eligibility query. Player or level-up event. Move-slot mutation. PP initialization. Battle move selection.

Notice the similarity to the stat pipeline. Static data is consulted. Stored state is mutated or derived. Battle consumes a prepared representation.

Now, I want to make a careful connection to scripts without re-teaching script execution. In earlier lectures, we covered script pointer execution, script command handlers, flags, variables, branching, and blockers. Those systems matter here because many creature mutations are triggered by script-level events: receiving a starter, being given a gift Pokemon, using an item, healing at a Pokemon Center, or triggering an evolution-related flow. The script layer should not manually calculate every stat. It should request a domain operation: give this Pokemon, heal party, start battle, teach move, evolve creature. Then the Pokemon subsystem applies the correct data mutations and recalculations.

So, if you design your lab clone and your script command says, “Set species to Shellbit, set max HP to 44, set Attack to 48, set move one to Tackle,” you have made the script too knowledgeable. A better script command says, “Create species Shellbit at level five,” and the creature subsystem fills in default moves, stats, PP, and stored values according to the rules you implement.

That is the same architecture principle as map headers and object events from the overworld unit. Data declares intent. Runtime systems interpret it through controlled contracts.

Now, let’s bring regression testing into this. Showdown is extremely useful for thinking about regression tests because a simulator has to preserve battle behavior across huge data sets and many generations. The packet points us to `pokemon-showdown/test` as a recurring module, especially `pokemon-showdown/test/TESTS.md`, `pokemon-showdown/test/assert.js`, and `pokemon-showdown/test/common.js`. The research packet does not give us exact chunk IDs for those files here, so I am not going to pretend it does, but the module-level note is enough to support the comparison: Showdown treats battle mechanics as testable simulator behavior.

For your FireRed-inspired lab, the lesson is: do not only test that the formula compiles. Test architectural invariants.

For example, create Sprout level five. Assert that its calculated HP matches expected output from species base HP plus individual inputs. Then mutate its experience to level six through a controlled level-up path. Assert that max HP changes, current HP is preserved or increased according to your chosen rule, and move learning is checked. Then teach a TM through the compatibility bitset. Assert that the move slot changes and PP is initialized. Then start a battle. Assert that battle-stage changes do not mutate the stored party stats.

Those are not giant tests. Those are architecture tests.

And if we compare to Showdown again, the simulator turn model is built around deterministic event progression: construct battle, choose actions, resolve turn, assert state. FireRed’s battle engine is not the same architecture, but you can borrow the testing intuition. You can construct data, run the domain operation, and assert the resulting state. That is how you protect the boundary between species data, stored Pokemon data, and battle runtime data.

Let’s stop for a comprehension check.

Imagine I give you this bug report: “After evolving, the Pokemon’s species name and sprite changed, but its stats stayed the same until it leveled up again.” Where would you inspect first?

You should inspect the evolution mutation path and the stat recalculation call. In the source map for this week, that means thinking across `pokefirered/src/pokemon.c chunk_f56e3a6588d98715`, for mutation and lookup, and `pokefirered/src/pokemon.c chunk_4573760bdda53819`, for runtime calculations. You would also ask whether the UI is reading stale cached values or asking through the appropriate data access path.

Second bug report: “Using a battle stat-lowering move permanently lowers the target’s Attack after battle.” Where is the likely architectural confusion?

That is battle-lifetime state being written into creature-lifetime stored data. You would inspect the battle stat stage representation, not the species table. You would make sure the permanent party record is not being mutated by temporary battle effects.

Third bug report: “A TM says it is compatible, but after teaching the move, the move has zero PP.” What boundary is implicated?

The eligibility query succeeded, probably through a TM/HM compatibility table like the FireRed table named in the packet, `pokefirered/src/data/pokemon/tmhm_learnsets.h`, but the move-slot mutation failed to initialize PP from move data. So the bug is not necessarily in compatibility. It may be in the transition from eligibility to stored move state.

This is how I want you to debug this week: name the boundary before you hunt the line.

Now, one subtlety. In FireRed, the species table is not only stats. It includes many species-level properties: types, catch rate, experience yield, growth rate, egg groups, abilities, and more depending on the struct. The chunk named in the packet, `pokefirered/src/data/pokemon/species_info.h chunk_2e759c52d0f4129a`, is part of that species table continuation. For today, we are emphasizing base stats because the lecture is “From Species Data to Battle Stats,” but do not mentally reduce species data to numbers for damage. Species is a hub of default identity. Battle stats are one projection from that hub.

And that is why species transitions, meaning evolution, are so architecturally interesting. Evolution changes the species ID, but it does not create an entirely new unrelated stored creature. The individual remains continuous: nickname, trainer data, experience, friendship, perhaps moves, IV-like values, EV-like values, and current HP relationship continue. But species-derived projections change: base stats, types, learnset eligibility, evolution targets, possibly ability interpretation depending on mechanics. Evolution is a controlled species transition over a persistent individual identity.

That phrase is worth writing down: persistent individual identity, changed species projection.

In your lab, when Sprout evolves into Budleaf, you should not delete Sprout and create a brand-new Budleaf from scratch unless your assignment explicitly says to model evolution as replacement. For a FireRed-like architecture, you mutate the species field, preserve the individual fields, recalculate derived stats, and then handle move learning or display events as required. That is a more faithful model and a better test of your architecture.

Now I’m going to transition from the conceptual pipeline into the actual stat-calculation responsibilities we will keep unpacking next. Keep the pipeline visible in your notes: species table, stored individual, accessor, calculator, battle consumer. The next move is to ask what each stage is allowed to know, because once we define those knowledge boundaries, the code almost writes its own shape.

So, let’s put that question on the board: what is each stage allowed to know? Species data is allowed to know what the species is in the abstract. It knows base HP, base Attack, base Defense, base Speed, base Special Attack, base Special Defense. It knows types, growth rate, catch rate, egg groups, abilities in the generation-three sense, and other species-level facts. The species table does not know that your Bulbasaur is level 17, has 21 Attack IV, has 3 Attack EVs, has a particular nature, currently has 27 hit points remaining, or is poisoned. Those are individual facts. In the source focus for today, that separation is exactly why we keep pointing back to pokefirered/src/data/pokemon/species_info.h, chunk_2e759c52d0f4129a, as the table side, and pokefirered/src/pokemon.c, especially chunk_4573760bdda53819, as the runtime calculation side.

Now, notice the phrase I just used: “in the abstract.” That is a design phrase, not just a literary phrase. A species record is a template, but it is not a complete creature. If you store only species records, then every Bulbasaur is the same Bulbasaur. If you store only individual records with copied-out stats and no species reference, then evolution, scaling, balancing, and table patches become painful. The FireRed architecture avoids both mistakes. It stores an individual that references a species, then derives battle-ready numbers from the combination.

Let me write a simplified equation on the board. Not code yet, just architecture. Battle stat equals species base stat plus individual variance plus training plus level scaling plus nature adjustment, with HP using its own formula. That formula is not merely math trivia. It tells us the dependency graph. If base stats change, derived stats must be recalculated. If level changes, derived stats must be recalculated. If EVs change, derived stats must be recalculated. If species changes during evolution, derived stats must be recalculated. If nickname changes, no stat recalculation is needed. If current HP changes because of damage, max HP is not recalculated. That is the kind of boundary judgment you need for your lab.

Check yourself here: if I call a setter that changes the species field, and nothing in the system recomputes max HP, Attack, Defense, Speed, and the special stats, what bug appears? Yes, you get a creature that displays as the evolved species but fights with the previous species’ stats. That is the classic half-mutated entity bug. In a data-driven RPG, those bugs are common because the visible identity and the mechanical identity are often stored in different places or produced at different times.

In FireRed, the runtime Pokemon structure carries calculated stats that battle and menus can read quickly. We already covered the Pokemon and BoxPokemon representation last week, so I’m not going to reopen the whole storage layout. The important continuation is this: the stored identity and training fields are not enough for battle. The game materializes battle-facing fields from them. That is why pokefirered/src/pokemon.c chunk_4573760bdda53819 matters for today’s lecture. It is part of the source area where Pokemon runtime calculations are concentrated. And pokefirered/src/pokemon.c chunk_f56e3a6588d98715 matters because mutation and lookup are not side topics; they are the doors through which the calculator is reached.

Now let’s do a concrete example. Suppose our lab species table has three species: Sprout, Budleaf, and Thornbloom. Sprout has base Attack 45, Budleaf has base Attack 62, and Thornbloom has base Attack 82. The individual creature has level 16, Attack IV 20, Attack EV 24, and a neutral nature. When it is Sprout, the calculator combines base 45 with level 16, IV 20, and EV 24. When it evolves into Budleaf, we do not erase the IV, we do not erase the EV, and we do not erase the level. We change the species reference from Sprout to Budleaf, then rerun the calculation using base 62 instead of base 45. That one substitution is the architectural point. Species transition should be narrow; derived-stat refresh should be explicit.

Here is where advanced students sometimes over-engineer. They say, “Why store calculated stats at all? Why not calculate on every read?” That is a fair question. In a modern server simulator, recalculating or lazily resolving values may be acceptable, especially if the data model is optimized around clarity, testing, or hot patching. In a Game Boy Advance RPG, you have a different performance profile, a different memory layout, and a very different engine style. FireRed often prefers explicit mutation and stored runtime fields because many systems need fast, predictable reads: party menu display, summary screen, battle setup, damage calculation, healing items, level-up handling, and script-visible operations. The architecture is not “pure” in a functional-programming sense. It is practical C code for a constrained machine.

Now I want to bring in Pokemon Showdown, carefully. Showdown is not our authority for FireRed mechanics. It is a secondary comparison for how a modern battle simulator organizes hooks, mods, and regression tests. In the research packet, pokemon-showdown/data/learnsets.ts is flagged as a high-signal modern learnset modeling file, and pokemon-showdown/data/mods/gen9dlc1/learnsets.ts, pokemon-showdown/data/mods/gen9predlc/learnsets.ts, and pokemon-showdown/data/mods/gen8dlc1/learnsets.ts are called out as examples of modded generational data. That comparison is useful because Showdown makes versioned data differences first-class. FireRed, by contrast, is one concrete game with compiled data tables. So when we use Showdown in this course, the question is not, “How do we replace FireRed with Showdown?” The question is, “What does a modern simulator make explicit that FireRed bakes into tables and C control flow?”

For example, Showdown’s battle data frequently uses hooks. The packet gives pokemon-showdown/data/mods/gen4/items.ts, chunk_ef574f63c118f2f6, where item behavior can modify base power or damage through event-style callbacks. It also gives pokemon-showdown/data/mods/gen9ssb/moves.ts, chunk_f99c7de3b95d7f40, where a move can modify its own behavior based on the acting Pokemon’s species. That is an excellent comparison point for us. In FireRed, if a held item, move, ability, or species-specific rule changes a stat or damage result, you typically trace through concrete C functions and tables. In Showdown, you often trace through a Dex object, data object, and hook chain. Both are data-driven, but the unit of extension differs.

Let me pause and ask a check-for-understanding question. If FireRed’s species table says Charmander has a base Speed value, and Showdown has a move hook that changes behavior if the user is a certain species, are those the same kind of data? No. They are both data-adjacent, but one is declarative species input and the other is executable battle behavior attached to a data record. That distinction matters because your lab this week should not sneak executable battle rules into the species table unless you are intentionally designing a hook system. For the three-species assignment, the species table should stay boring. Boring is a compliment here. Base stats, types, growth information, learnset pointers or references, TM/HM compatibility bits. The calculator and resolver should do the work.

Now, let’s connect this to move slots and PP, because week 10 is not only stats. A battle-ready creature is not just a stat block. It also has up to four moves, each with current PP, possibly modified PP maximums, and eligibility rules that explain how the move got there or whether it can be learned now. In FireRed, move slots and PP live with the individual creature state, while move eligibility comes from external tables and procedures. Again, do not re-teach yourself the whole GetMonData system; remember it as our accessor foundation. The architectural lesson today is that move possession and move possibility are different states.

If Sprout currently knows Tackle, Growl, Vine Tap, and Quick Root, that is possession. If Sprout is compatible with TM07, that is eligibility. If Sprout learns Leaf Guard at level 18, that is level-up learnset resolution. If Budleaf can learn TM22 but Sprout cannot, that is species-specific compatibility. These are not interchangeable. A common student bug is to use the TM/HM learnset bitset as if it were the current move list. Another common bug is to mutate the current move list while merely checking eligibility. Those are separate responsibilities.

The source focus names pokefirered/src/data/pokemon/tmhm_learnsets.h as the high-signal FireRed TM/HM learnset table for this week. The table design is compact because TM/HM compatibility is essentially a yes-or-no question repeated across many species and many machines. A bitset is the natural representation. You can ask, “Does this species have the bit for this TM?” and you get an answer without storing a big list of strings or move objects per creature. In your lab, if you have eight TMs, a single integer can represent compatibility. TM01 is bit zero, TM02 is bit one, and so on. Budleaf’s compatibility might be 0b00101101. That number is not user-friendly, but it is engine-friendly.

Let’s work that example aloud. Say TM01 teaches Cut, TM03 teaches Seed Burst, and TM06 teaches Toxic Dust. Budleaf’s bitset has bits 0, 2, and 5 set. When the player selects TM03, the menu should ask the eligibility layer, “Can Budleaf learn this machine?” The eligibility layer masks the bitset. It does not inspect Budleaf’s current four moves first. If the answer is yes, then the move-learning flow checks whether there is an empty slot or whether the player must replace a move. That replacement flow is a UI and mutation problem. The bitset solved only compatibility.

Board cue: I’m drawing two columns now. Left column: “Can learn.” Right column: “Does know.” Under “Can learn,” I’m writing species TM/HM bitset and level-up learnset table. Under “Does know,” I’m writing four move slots and current PP. Put a line between them. That line is one of the main architecture lines of the week.

FireRed’s learn-move user interface sits downstream of those mechanics. The research packet gives pokefirered/src/learn_move.c, chunk_af3598df6f092723, which supports discussion of the move relearner state machine and menu flow. That file is useful because it reminds us that learning a move is not just a table lookup. It becomes a state machine: build a list, respond to input, confirm replacement, mutate the Pokemon, update PP, return to the previous context. We have already studied task and callback dispatch earlier in the course, so you should see the continuity. Data does not jump directly to visible behavior. It passes through runtime state, callbacks, tasks, menu states, and mutation functions.

Now compare that with Pokemon Showdown’s learnset modeling. Showdown needs to answer questions like, “Is this set legal in this generation, with this event move, from this transfer path?” That is a different product problem. Its learnset files are not merely “what can this cartridge teach right now?” They support competitive validation across formats and mods. The packet points us to pokemon-showdown/data/learnsets.ts and the modded learnset files as high-signal sources for this reason. So the comparison is: FireRed uses compact cartridge-oriented tables to drive in-game teaching and progression; Showdown uses modern data structures to validate and simulate many rule environments. When you design your lab, stay closer to FireRed. When you write regression tests, borrow Showdown’s discipline.

And that brings us to testing. Pokemon Showdown is valuable to this course not because its mechanics are identical to FireRed’s, but because it treats battle behavior as something that must be regression tested. The packet identifies pokemon-showdown/test as a recurring module, with files like pokemon-showdown/test/TESTS.md, pokemon-showdown/test/assert.js, and pokemon-showdown/test/common.js. Even without importing its architecture wholesale, we can borrow the idea that a battle engine should have small, repeatable cases. If a species evolves, stat recalculation should be testable. If a TM bit is set, compatibility should be testable. If a level-up learnset contains two moves at the same level, resolver behavior should be testable. If a stat formula changes, a known creature should produce a known stat.

Let’s make that specific. For the lab, I would expect at least one test that creates Sprout at level 16, records its calculated max HP and Attack, evolves it into Budleaf, recalculates, and asserts that the species changed, the individual values stayed the same, and the derived stats changed according to the new base stats. I would expect a second test that checks TM compatibility using the bitset, not by scanning the current moves. I would expect a third test that resolves level-up moves for a species at a target level and handles the four-move limit in a deterministic way. Advanced students should also test current HP behavior. If max HP increases on evolution, do you preserve the same current HP, preserve the same percentage, or add the delta? FireRed has its own behavior in context, but for your lab you must choose and document the rule. Architecture includes these little policy decisions.

Check for understanding: why is “add the max HP delta to current HP” a different policy from “preserve current HP exactly”? Think about a creature with 20 out of 30 HP before evolution, whose max HP becomes 40. Preserving exactly gives 20 out of 40. Adding the delta gives 30 out of 40. Preserving percentage might give around 26 or 27 out of 40, depending on rounding. All three are plausible in an abstract RPG. Only one should be implemented. If your tests do not pin it down, your future self will accidentally choose a policy while refactoring.

Now, another boundary: battle consumers should not know the species table layout. A battle damage function should not reach into a giant species_info record every time it wants the attacker’s Attack stat. By the time battle is consuming a Pokemon, the relevant stats should already be available through the runtime Pokemon state or battle battler state. This is part of the “consumer” side of the pipeline I asked you to keep visible: species table, stored individual, accessor, calculator, battle consumer. If the battle consumer bypasses the calculator, it can silently disagree with menus, summary screens, item effects, and level-up previews.

This is one reason pokefirered/src/pokemon.c chunk_c089f03b70501fda, described in the packet as creature state helpers, belongs in today’s source focus. Helper functions are not glamorous, but they are where architecture becomes enforceable. A helper can preserve an invariant. A helper can centralize a lookup. A helper can prevent five systems from each interpreting the same field differently. In a C codebase, you do not get a private class boundary in the modern object-oriented sense. You get conventions, headers, helper functions, and discipline. We talked in week 2 about headers as cross-module contracts; this is where that idea pays off. The contract is not just “this function exists.” The contract is “this is the way other systems are supposed to ask the Pokemon question.”

Let’s briefly connect to the course map. Weeks 9 through 11 are our Creature and Party Data Model module. Last week, the emphasis was representation: what is a creature record, what is a species record, how do party slots and stored fields work? This week, the emphasis shifts to transformation: how do stats, moves, evolutions, and learnsets turn stored records into playable mechanics? Next week, we will be closer to integration pressure: how party data interacts with battle, inventory, UI, save, and link-facing constraints. So today’s lecture is deliberately in the middle. We are not at raw data anymore, but we are not yet fully inside the battle engine either. We are at the conversion layer.

That conversion layer is also where many clone projects fail. They build a species JSON file. They build a battle screen. Then they connect them directly with ad hoc code. For the first hour, it works. Then they add evolution. Then TMs. Then move relearning. Then status healing. Then saving and loading. Suddenly, every feature has a different idea of what a creature is. FireRed’s architecture is not perfect, but it repeatedly teaches the same lesson: table data needs controlled access, runtime data needs mutation helpers, and visible systems should consume prepared state rather than reinterpreting raw records.

Now, let me use Showdown one more time as a contrast, specifically around simulator turns. The packet includes pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, where damage calculation logic checks move form, immunity, OHKO behavior, damage callbacks, level-based damage, and fixed damage before continuing. That excerpt is useful because it shows a modern simulator making the battle turn pipeline highly inspectable and hook-aware. FireRed’s battle engine has its own structure, but for our purposes today, look at the dependency shape: the simulator turn needs a source, a target, an active move, and resolved properties. It should not be asking, “What is this species’ base Attack in the species table?” as its primary abstraction. It operates on battle participants and resolved move data.

That is the shared architectural principle. Whether you are in FireRed’s C engine or Showdown’s TypeScript simulator, the battle turn should consume a prepared combatant, not a raw encyclopedia entry. The species table is upstream. The turn engine is downstream. If you violate that direction, testing becomes harder because every battle test must reconstruct the whole data universe. If you preserve that direction, you can test the stat calculator separately, the learnset resolver separately, and the battle consumer with controlled fixtures.

For your three-species test set, I want you to think like this. Sprout, Budleaf, Thornbloom are not just cute placeholders. They are probes. Sprout should have at least one lower base stat that changes significantly on evolution, so your recalculation test can catch stale derived stats. Budleaf should differ from Sprout in at least one TM compatibility bit, so your TM test catches species transition effects. Thornbloom should have a level-up move at the evolution level or just after it, so you can decide the order of operations: evolve first, then learn move, or learn move first, then evolve. Real games make these choices concrete. Your architecture should make the sequence visible.

Let’s pause on that sequence. A level-up event can cause a stat recalculation, a move-learning prompt, and an evolution attempt. Those are not just three features; they are an ordered workflow. If the creature levels from 15 to 16, you might first update experience and level, then recalculate stats, then check level-up moves, then check evolution conditions, then if evolution succeeds mutate species and recalculate stats again, then check evolution moves if your rules include them. FireRed’s exact flows are distributed through its Pokemon and party-related code, and our source focus today keeps us around pokefirered/src/pokemon.c chunks 4573760bdda53819, f56e3a6588d98715, and c089f03b70501fda because that is where calculation, mutation, lookup, and helper responsibilities are visible. The lab version does not need the full cartridge workflow, but it does need an explicit workflow.

Here is a useful mental model: do not make “level up” a number assignment. Make it a transaction. Within that transaction, some fields mutate, some derived values refresh, and some events are emitted or queued. In FireRed terms, the visible prompts may be handled by menu state machines and callbacks. In your simplified lab, the events might just be returned as an array like “learned move,” “wants to learn move,” “evolved.” The exact interface is yours, but the design principle is not optional for an advanced implementation. If you bury prompts inside the stat formula, the system will become tangled.

Check for understanding again. Which component should know that TM03 corresponds to Seed Burst? The TM table or move registry should know that. Which component should know whether Budleaf can learn TM03? The TM/HM compatibility layer should know that, using Budleaf’s species bitset. Which component should know whether Budleaf currently has an empty fourth move slot? The individual Pokemon state or move mutation helper should know that. Which component should draw the yes/no prompt? The UI or menu flow should know that. Good. Keep those answers separate.

Now, before we move further into formulas, let me address one subtlety about “data-driven.” Students sometimes hear data-driven and assume all behavior must live in data. That is not what FireRed is showing us. FireRed is data-driven in the sense that large parts of creature identity, species properties, moves, maps, items, and scripts are table-driven. But the engine still contains procedural rules. The stat formula is code. The move-learning process is code. Evolution checks are code consulting data. Showdown, likewise, has data records with executable hooks. The presence of data does not remove control flow. It organizes control flow around stable records.

So when you build the lab, do not write a magical universal interpreter for all creature rules. That is too broad. Write a species table. Write accessors or lookup functions. Write a stat calculator. Write a TM/HM bitset checker. Write a level-up move resolver. Write a species transition function that preserves individual identity and refreshes derived state. That is enough. The sophistication is in the boundaries, not in the volume of abstraction.

All right, in the next stretch we are going to get more precise about the stat formula inputs and the rounding behavior. Keep the board pipeline in view. The calculator is the narrow waist of the system: it sees species base stats through lookup, it sees individual level and training values through accessors, and it writes derived runtime stats for consumers. If you understand that narrow waist, the rest of week 10 becomes much easier to reason about, because moves, PP, learnsets, and evolution all become variations on the same pattern: persistent individual state, external eligibility data, controlled mutation, and refreshed runtime projection.

Now, let’s slow down on that phrase, “derived runtime stats,” because it is easy to say and surprisingly easy to implement incorrectly.

When I say derived, I mean the value is not the identity of the creature. It is not the species, not the personality value, not the level, not the training history, not the nature or equivalent personality-derived modifier. It is a projection from those things into a number the battle engine can consume cheaply. That distinction matters because the moment you store derived state, you create a synchronization problem. You have to know when to refresh it.

So, imagine I put a creature on the board. Let’s use a simple three-species lab example rather than a full Pokédex entry. Species A has base Attack 50. Species B has base Attack 70. Species C has base Attack 95. The individual has level 20, some individual value, some effort value, and current HP. If the creature evolves from A to B, the individual did not become a new save-record identity. The persistent record has changed species, yes, but its level and training history remain. What must be recalculated? Its max HP, Attack, Defense, Speed, Special Attack, and Special Defense. What must be handled carefully? Current HP. You generally do not want evolution to silently heal the creature to full just because max HP changed, unless the game explicitly says that should happen. So you either preserve the current HP value directly, or preserve the damage taken and derive the new current HP from the new max HP. Different games choose different semantics, but the architecture question is the same: the species transition mutates a persistent identity field and then refreshes the runtime projection.

FireRed gives us the shape of that split in the source focus for today. The lecture packet names pokefirered/src/pokemon.c chunk_4573760bdda53819 as the runtime calculation area, pokefirered/src/pokemon.c chunk_f56e3a6588d98715 as data mutation and lookup, and pokefirered/src/pokemon.c chunk_c089f03b70501fda as creature state helpers. That separation is the point. Not necessarily three neat layers in a modern service sense, but three responsibilities: compute values, mutate or retrieve stored fields, and answer creature-state questions in a consistent way.

Let me put the formula pipeline on the board. Board cue: left side, stored inputs; middle, species lookup; right side, derived stats.

Stored inputs: species id, level, experience or level source, individual values, effort values, personality or nature-like modifier, current HP, moves, PP, status, friendship, and other identity-bearing state. Species lookup: base stats, types, growth rate, gender ratio, abilities, evolution metadata, learnset references, and TM/HM compatibility. Derived output: max HP and battle stats, and later today we will treat move eligibility as a cousin of that same process.

The first advanced point is that “lookup” is not a passive detail. A lookup function defines a contract. If every subsystem can reach directly into the species table and reinterpret bytes however it wants, then you do not have a stable architecture; you have many small architectures that happen to share memory. FireRed leans heavily on accessor-style boundaries. We talked last week about Pokemon and BoxPokemon representation and GetMonData-style APIs, so I am not going to reopen that entire discussion. But carry it forward here: stat calculation should be thought of as a client of the creature data API, not as a random piece of arithmetic floating in space.

Now, check for understanding. If I ask, “Where does Attack come from?” the advanced answer is not “the species table.” The advanced answer is, “Attack is derived from species base Attack plus individual and training inputs, at a particular level, passed through the game’s stat formula and stored back into runtime creature state.” The species table contributes one input. It is not the whole answer.

Let’s make that concrete. Suppose our lab creature has base Attack 70 after evolution, level 20, IV 12, EV 80. I am not asking you to memorize exact constants for an exam in this moment. I am asking you to track input ownership. The base stat belongs to the species table. The level belongs to the individual creature. The IV and EV belong to the individual creature. The formula belongs to the runtime calculation code. The resulting Attack belongs to the mutable party Pokémon structure as a cached derived value. If the creature gains a level, the base stat did not change, but the derived value may. If the creature receives vitamins or training, the species did not change, but the derived value may. If the creature evolves, the individual training did not change, but the base stat lookup now points at a different species row, so the derived value may. Three different events, same refresh obligation.

This is why week 10 sits exactly where it does in the course. In weeks 1 through 8, you learned to trace from data records to runtime behavior through callbacks, tasks, scripts, globals, and source functions. In week 9, we established creature records and species records as the persistent and reference-data sides of the model. Now, week 10 is where that data starts behaving like a game system. A species table by itself is just an encyclopedia. A party record by itself is just a save blob. The calculator, move resolver, learnset checker, and evolution transition are what make those records operational.

Let me add a second board note under the stat calculator: “Refresh points.” Level-up. Evolution. Box-to-party materialization, depending on what the engine stores. Item-based stat changes. Trade or script mutation. Debug tools. Loading from save, if derived values are not trusted. Any time an input changes, either you refresh immediately, or you mark dirty and refresh before consumption. FireRed’s GBA context favors direct mutation and recomputation at known moments, because this is not a server with a lazy ORM and event bus. That is also part of the architecture lesson. A constrained hardware game often achieves reliability by having fewer places where a value can change.

Now we can bring in Pokémon Showdown, carefully. Showdown is useful as a secondary comparison because it makes hooks and mods very explicit. It is not replacing FireRed as our architecture source. FireRed is the primary historical and mechanical reference for this course. But Showdown helps us see a modern simulator’s answer to the same question: where do battle-relevant values come from, and how do generation-specific rules enter the calculation?

The packet points us to pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f, where damage logic accepts a move as a string, number, or active move object, normalizes it, checks immunity, handles OHKO behavior, damage callbacks, level-based damage, fixed damage, and then continues through damage calculation. Do not map that one-to-one onto FireRed’s stat calculator. Instead, notice the architectural pattern. Showdown puts rule variation behind data and hooks. A move may carry a damage callback. A modded generation may inherit or override behavior. The simulator asks the active battle object and dex to normalize inputs, then applies a staged calculation.

That is a more dynamic design than FireRed’s C tables and functions, but it is answering a related problem. The problem is not “how do I calculate damage in JavaScript?” The problem is “how does a creature RPG prevent battle behavior from becoming hardcoded chaos?” FireRed answers with fixed data tables, constants, C structs, and functions like the pokemon.c runtime calculation and mutation regions named in chunks chunk_4573760bdda53819 and chunk_f56e3a6588d98715. Showdown answers with Dex data, mods, event hooks, and regression tests. We study both because each makes different tradeoffs visible.

Here is a small example. Suppose we add a held item that doubles Pikachu’s Special Attack in a later-generation simulator. Showdown’s data can express that as a modifier hook. The packet’s pokemon-showdown/data/mods/gen4/items.ts chunk_ef574f63c118f2f6 shows this style with Light Ball inheriting item data and defining stat modification behavior for Pikachu. In FireRed architecture, for our course purposes, we would ask a different question first: is this a permanent stat change stored on the creature, or a battle-time modifier layered over the calculated stat? If it is battle-time only, do not write it into the Pokémon’s stored Special Attack as though the creature trained. Put it in battle calculation or battle modifiers. That boundary prevents item effects from corrupting persistent creature state.

Pause there. This is one of the most common errors in student clone projects. A student writes a clean species table and a clean stat function, then adds a held item or status effect by directly changing the stored stat field. It seems to work for one battle. Then the creature leaves battle, and the boosted value is still there. Or it levels up, and the boost is baked into the next calculation. Or it gets boxed, and now the save contains a temporary battle condition. The fix is conceptual before it is technical: separate baseline derived stats from transient battle modifiers.

Let’s do a quick check. If a Pokémon burns and its physical damage is reduced, should we rewrite its Attack stat in the party record? No. We should apply the burn effect at the battle damage or effective-stat layer. If a Pokémon levels up, should we rewrite its baseline stats? Yes, because the input level changed and the projected runtime stats should be refreshed. If a Pokémon evolves, should we replace the whole individual record? No. We mutate species and refresh derived projections while preserving identity-bearing fields.

Good. Now let’s connect this to move slots and PP, because the pattern repeats.

A move slot in the creature record is persistent individual state. “This creature currently knows Move 1, Move 2, Move 3, Move 4.” PP is also persistent, at least current PP and PP Up-derived maximum changes are. But learnability is external eligibility data. A level-up learnset says which moves a species may learn at which levels. A TM/HM bitset says whether the species is compatible with a machine. A move relearner asks a related historical or species-eligible question. Those are not the same thing as the moves currently stored on the creature.

The packet names pokefirered/src/learn_move.c chunk_af3598df6f092723 as a move relearner state machine area, and that excerpt is useful because it reminds us that learning a move is not just a data lookup. There is menu state, list navigation, field transition, text windows, summary screen integration, and task or callback coordination. That connects backward to our earlier course material: data table to callback to visible behavior. The player sees a menu and text prompts. Underneath, the system is checking eligibility, offering choices, and mutating four move slots only after the player confirms.

Now, board cue: I am drawing two boxes. First box says “eligible moves.” Second box says “known moves.” There is an arrow from eligible moves to known moves labeled “controlled mutation.” That arrow is the learning process. The eligible set can be large. The known set is four slots. Do not confuse them.

For the lab this week, your three-species test set should make that distinction painfully visible. Species A learns Tackle at level 1 and Quick Attack at level 7. Species B learns Tackle at level 1, Quick Attack at level 7, and Flame Wheel at level 16. Species C learns a different branch move at level 16. Then give them a TM/HM compatibility bitset. Maybe TM01 applies to A and B but not C. Maybe HM01 applies to all three. Your resolver should answer two different questions. First, “At this level, what level-up moves should be learned or offered?” Second, “Can this species learn this TM/HM?” Those are not interchangeable, and they should not share a vague function named canLearnMove unless you are explicit about source type.

This is where Showdown’s learnset modeling is a helpful comparison. The packet identifies pokemon-showdown/data/learnsets.ts as a high-signal modern learnset file, and it also points to generation-specific mod learnsets like pokemon-showdown/data/mods/gen9dlc1/learnsets.ts, pokemon-showdown/data/mods/gen9predlc/learnsets.ts, and pokemon-showdown/data/mods/gen8dlc1/learnsets.ts. Again, not FireRed mechanics. But architecturally, Showdown has to preserve a much wider historical compatibility surface: generations, events, transfers, DLC changes, formats, and legality rules. FireRed’s TM/HM table named in the packet, pokefirered/src/data/pokemon/tmhm_learnsets.h, is a much more compact cartridge-era representation. That is the contrast I want you to see. FireRed optimizes for fixed data on constrained hardware. Showdown optimizes for broad, evolving rule modeling.

Let’s not drift into “modern is better” or “old is simpler.” FireRed’s approach is powerful because it makes many common checks cheap. A bitset can answer compatibility with a mask operation. If TM index 12 corresponds to a bit position, then species compatibility is one or more integer words and a bit test. That is exactly the sort of representation you should use in the lab. Do not store an array of strings for TM compatibility in your runtime creature object. Store compatibility by species in reference data. Then ask, “Does the species row’s bitset contain the TM bit?” That gives you a direct line from data table to game behavior.

Let me give you a concrete pseudo-example in words. The player selects a TM from the bag. The item-use flow identifies the move associated with that TM. The party menu asks the compatibility checker for each party creature. The checker reads the creature’s species id through the creature data boundary, looks up that species in the TM/HM learnset table, tests the bit for that TM, and returns true or false. If true, the UI can allow selection. If the player confirms, the move learning routine mutates one of the four move slots and initializes PP. Notice what did not happen: the TM item did not scan every possible move table by name; the creature did not store its own copy of all compatible TMs; the battle engine did not decide learnability.

Check for understanding. Why should TM compatibility belong to species reference data rather than the individual Pokémon record? Because compatibility is a property of the species form under the ruleset, not a history-bearing property of this one individual. The individual may currently know the move or may have used a TM in the past, but the question “is this species compatible with this machine?” is reference data. Good.

Now evolution complicates this slightly, and this is why the week bundles stats, moves, evolutions, and learnsets together. When species changes, the compatibility table changes. A creature that could not learn a TM before evolution may become eligible after evolution. A creature may evolve and have a different level-up learnset from that point forward. But it does not forget its current moves just because the species row changed, unless a specific rule says it must. So evolution touches persistent species id, derived stats, possible ability or type lookup, and future eligibility queries. It usually does not rewrite the four move slots automatically except where the evolution process explicitly grants or prompts for a move.

That last sentence is important for clone design. Do not make evolution call “rebuild moves from species learnset.” That is an attractive mistake because it feels data-driven. It is also wrong for the model we are studying. The known moves are individual state. The learnset is eligibility data. Evolution should not erase individual history.

Now let’s relate this to testing, because at the advanced level you should not trust this architecture unless you can pin down regressions. FireRed itself is not organized like a modern JavaScript test suite. Showdown, however, has a strong culture of simulator regression testing, and the packet points us to pokemon-showdown/test as a recurring module around assertions, Pokémon, moves, battle, species, and Showdown. Even without treating its mechanics as FireRed’s mechanics, we can borrow the testing mindset. You create tiny battle or data scenarios that prove the boundary is working. Does a species transition preserve current moves? Does stat recalculation change Attack after evolution? Does current HP policy behave exactly as specified? Does TM compatibility use the evolved species row after evolution? Does a battle-only modifier disappear after battle?

For your lab, I want you to think in that style. You do not need a giant test harness. But you do need regression cases that catch boundary violations. One test should construct Species A at level 15, calculate stats, evolve to Species B, and assert that species id changed, level stayed 15, IVs and EVs stayed the same, max HP and Attack were recalculated, and move slots remained unchanged. A second test should check TM compatibility before and after evolution. A third should resolve level-up moves at a level where Species A and Species B differ. That gives you meaningful coverage of the architecture, not just arithmetic.

Let me recap the narrow waist before we move into rounding behavior. The calculator consumes stored individual inputs and species reference data. It writes derived runtime stats. The move resolver consumes species learnset data and individual level or event context. It may offer a controlled mutation to move slots. The TM/HM checker consumes species compatibility bitsets and a machine index. It should not mutate by itself. The evolution function mutates species identity, preserves individual history, and triggers refresh of derived projections. These are four small functions, but they encode the whole week’s design philosophy.

And now, with that in place, the rounding details will make more sense. Rounding is not just a math footnote. In a deterministic RPG, rounding is part of the ruleset. If two engines disagree by one point of Speed, turn order changes. If they disagree by one point of HP, survival changes. If Showdown has generation mods and simulator hooks, it must test those edges. If FireRed calculates runtime stats in pokefirered/src/pokemon.c chunk_4573760bdda53819, then our FireRed-style clone must treat that calculation as rules, not as a suggestion. So keep your attention on integer division, order of operations, and when modifiers are applied. Those details are where architecture meets player-visible behavior.

So let’s put a small calculation on the board, because this is one of those places where everyone nods until the first off-by-one test fails. Suppose we have a level 50 creature, a base Speed of 80, an individual value of 31, and an effort value of 252. The FireRed-style stat path is not “combine everything as floating point and round at the end.” It is a sequence of integer operations. Effort contributes as effort divided by 4, and that division floors. Then the level scaling divides by 100, and that floors. Then the flat stat constant is added. Then, for non-HP stats, the nature adjustment applies as another integer-scaled multiplier. If the nature boosts Speed, you multiply by 110 and divide by 100. If it lowers Speed, you multiply by 90 and divide by 100. Again, integer division. Again, a floor.

Now, listen to the architectural implication. The species table gives us the base Speed. The stored Pokémon record gives us level, IV, EV, personality-derived nature, and species identity. The runtime stat calculator combines those values. The battle engine later observes the result. So if you put rounding in the wrong layer, you do not merely have an ugly calculator. You have broken the contract between persisted identity and battle behavior.

And this is why I keep using the word projection. The stored creature is not identical to its battle stats. It is projected into battle stats by a ruleset. FireRed’s source organization reinforces that. The week’s primary runtime source focus, pokefirered/src/pokemon.c chunk_4573760bdda53819, is important precisely because it is where species data and per-creature stored data become usable runtime numbers. The separate mutation and lookup material in pokefirered/src/pokemon.c chunk_f56e3a6588d98715 matters because data mutation and data access are not the same act as battle-stat derivation. And pokefirered/src/pokemon.c chunk_c089f03b70501fda matters because creature state helpers become the small hinge points that keep the rest of the game from reaching directly into representation details.

Pause there. Check your understanding. If I change a creature’s species after evolution but do not recalculate its derived stats, what kind of bug do I have? Not a species table bug. Not primarily a save-format bug. I have a stale projection bug. The stored identity changed, but the cached or recalculated view used by gameplay did not catch up. That is a different class of defect than “wrong base stat in the table,” and advanced engineers need to distinguish those two immediately.

Let me give you a concrete evolution example, because this ties together almost every idea from today. Imagine a three-species test set for the lab. Species A evolves into Species B at level 16, and Species B evolves into Species C at level 36. Each species has a different base Attack and different level-up moves. Species A has base Attack 45. Species B has base Attack 65. Species C has base Attack 85. Our stored creature starts as Species A with a fixed personality, fixed IVs, some accumulated EVs, current level 16, four move slots, and PP values. When evolution succeeds, the game should not discard the creature and allocate a new one from scratch. It should mutate the species identity while preserving the individual’s history: personality, trainer ID, experience, IVs, EVs, friendship, nickname rules, current move slots unless evolution learning modifies them, and so on. Then it must refresh derived stats from the new species record.

That is the difference between a database row and a creature. A weak implementation treats species as the whole object. A stronger implementation treats species as a reference that participates in a richer identity. In FireRed, we have already spent the previous lectures on Pokémon and BoxPokemon representation, and I am not going to reteach that now. The important carry-forward is this: the stored record is compact and persistent, while the game’s visible behavior is assembled through accessors, helpers, and calculators. That is exactly the architecture you need for a Game Boy Advance RPG where memory layout, save compatibility, and deterministic behavior all matter.

Now, let’s widen the lens and compare this to Pokémon Showdown, carefully. Showdown is not our authority for FireRed mechanics. It is not a replacement source. It is a useful secondary comparison because it shows how a modern simulator expresses hooks, generation-specific mods, and regression tests around battle behavior. In FireRed, many rules are compiled into C functions and static tables. In Showdown, many rules are represented as data objects with event hooks. You can see that style in pokemon-showdown/data/mods/gen4/items.ts chunk_ef574f63c118f2f6, where item behavior uses hooks like damage modification and base power modification. You can also see move-specific hook behavior in pokemon-showdown/data/mods/gen9ssb/moves.ts chunk_f99c7de3b95d7f40, where a move can modify itself based on the acting Pokémon’s species and can run effects on hit. And in pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f, the damage path checks immunities, fixed damage, level-based damage, callbacks, and other conditions in a controlled sequence.

What should you learn from that comparison? Not “FireRed should have been TypeScript.” That is too shallow. The real lesson is that both systems need explicit rule boundaries. FireRed expresses those boundaries through source functions, structs, constants, generated tables, and runtime helpers. Showdown expresses many of them through Dex data, mods, and simulator event hooks. Different implementation medium, same architectural pressure: rules must be findable, deterministic, and testable.

Let’s take Speed again. In FireRed, the runtime calculator produces a Speed stat. In a battle, turn order uses that stat, then applies battle-specific effects. A clean architecture separates the permanent stat projection from temporary battle modifiers. Showdown’s hook-heavy design makes that separation especially visible because it can say, in effect, “here is the base data, here is a modded generation view of the data, here is a simulator turn, and here are callbacks that alter behavior at specific moments.” FireRed does not phrase it that way, but it still has analogous layers. Species data is not the same as party data. Party data is not the same as battle-side state. Battle-side state is not the same as final damage or turn order after field conditions.

So, if you are building the lab clone this week, do not write one giant function called calculateEverything. I want you to feel allergic to that. You need a species table. You need a stored creature record. You need a stat calculator that takes species identity and stored individual data as inputs. You need a move eligibility resolver that can answer different questions: can this species learn this TM or HM, what moves should be known at this level, and what happens when a level-up move is encountered but all four slots are full? Those are separate questions. In FireRed, the source packet points us to pokefirered/src/data/pokemon/tmhm_learnsets.h as the high-signal TM/HM learnset table, and that should tell you something about shape. TM/HM compatibility is naturally a bitset or bitset-like static capability table. It is not naturally the same thing as a chronological level-up move list.

Let me say that another way. “Can learn Thunderbolt by TM” and “learns Quick Attack at level 13” are both move eligibility facts, but they are not the same kind of fact. One is a compatibility flag indexed by item or move-machine identity. The other is a progression rule indexed by species and level. If you collapse them into one undifferentiated list of move names, you will make your early prototype look simpler and your later evolution, tutor, reminder, and validation logic worse.

This is where pokemon-showdown/data/learnsets.ts is useful as a comparison, with the warning printed in red in your mind: it is modern Showdown modeling, not FireRed mechanics. Showdown needs to represent learnability across formats, generations, event distributions, transfers, and competitive validation. FireRed needs to run a specific cartridge ruleset. So when you inspect Showdown learnsets in this course, use it to see what a mature validator has to remember. Do not import its assumptions back into FireRed unless you have proved the specific rule belongs to FireRed. The research packet flags pokemon-showdown/data/learnsets.ts as high-signal for modern learnset modeling, while our FireRed source focus for this week keeps pokefirered/src/data/pokemon/tmhm_learnsets.h and pokefirered/src/pokemon.c at the center.

Now, let’s do a board sketch in words. I’m drawing four boxes from left to right. First box: species static data. Second box: individual stored data. Third box: derived runtime stats and move state. Fourth box: battle turn behavior. Above the first box I write species_info.h, and specifically for today’s packet, pokefirered/src/data/pokemon/species_info.h chunk_2e759c52d0f4129a. Above the second and third boxes I write pokemon.c, with chunk_4573760bdda53819 for runtime calculations and chunk_f56e3a6588d98715 for mutation and lookup. Above the move eligibility side lane I write tmhm_learnsets.h. Then, off to the side, not replacing the diagram, I write Showdown Dex, mods, simulator hooks, tests.

The side note matters because this is where regression testing enters the lecture. Once your stat calculator is deterministic, you can write tiny tests that lock down the edge cases. Level 5 with low EVs. Level 50 with 252 EVs. Neutral nature. Boosting nature. Lowering nature. HP versus non-HP. Evolution at a level where the species changes and stats must refresh. A move learned exactly at the current level. A TM compatibility bit that is set for Species B but not Species A. Those are not glamorous tests, but they are the tests that stop ruleset drift.

Showdown’s project culture is helpful here. The packet identifies pokemon-showdown/test as a recurring course module around assert, Pokémon, moves, battle, species, and Showdown, with anchor files such as pokemon-showdown/test/TESTS.md, pokemon-showdown/test/assert.js, and pokemon-showdown/test/common.js. We are not using those files to claim anything about FireRed’s implementation. We are using them as evidence of a modern simulator’s need for repeatable battle and data tests. That is exactly the habit I want you to bring into the lab: if your architecture has a rule boundary, it deserves a regression test at that boundary.

A quick check for understanding. Why is a TM/HM bitset a good structure for FireRed-style compatibility? Think about access pattern. The game often asks a yes-or-no question: can this species learn this machine? The answer is compact, static, and indexed. A bitset gives you a dense table, predictable lookup, and no allocation. Now, why is a bitset less appropriate for level-up learnsets? Because level-up learnsets are ordered progression data. You care about level thresholds, sometimes multiple moves at the same level, and the chronological scan when a creature levels up. A bitset can tell you membership, but it cannot naturally tell you when and in what order the move should be offered. Different question, different representation.

This is one of the larger themes of week 10: data shape should follow rule shape. We saw earlier in the course that maps have headers because map loading has to answer map-shaped questions. We saw that scripts have pointer execution because story progression needs instruction-like control flow. We saw that flags and variables store world memory because the game needs durable branching state. We saw that Pokémon and BoxPokemon split runtime convenience from storage constraints. Now, in week 10, stats, moves, evolutions, and learnsets continue the same pattern. You are not learning a pile of special cases. You are learning how a production creature RPG repeatedly chooses compact static data, durable individual state, and deterministic runtime projections.

Let me bring in move slots and PP, because they are easy to underestimate. A creature’s known moves are not simply the result of asking, “what can this species learn?” The known moves are part of the individual’s current state. The species may be eligible for a hundred moves across level-up, TM, HM, tutor, breeding, or events, but the creature has four move slots. Those slots have current PP values. If a move is forgotten, current state changes. If a move is newly learned, you may initialize PP. If a move’s max PP changes through PP Ups or similar data, the runtime representation must preserve the relationship between move identity, current PP, and maximum PP modifiers. The eligibility system answers “is this legal or available?” The move-slot system answers “what does this individual currently know and how much resource remains?”

So, in your lab, when you implement three species, do not just return an array of all possible moves and call that the moveset. That misses the central architecture. Have a known move array of four slots. Have PP beside it or in a parallel structure, depending on the style you choose. Have a resolver that can produce level-up candidates for a species and level. Have a TM/HM compatibility check. Then write tests that prove Species A cannot use a TM, Species B can use that TM after evolution, and the individual’s existing move slots persist across the species transition unless a learning operation explicitly changes them.

And yes, that means evolution can affect future eligibility without rewriting the past. A Species A individual that evolves into Species B may now be compatible with a different TM set. But that does not mean all of Species B’s level-one moves magically replace the current move slots. The current moves are individual state. Learnability is potential. Keep those apart.

Now, let’s connect this to battle data hooks. In FireRed, a move selected in battle points into move data, and the battle engine applies rules. In Showdown, move data may include event callbacks. That is why chunks like pokemon-showdown/data/mods/gen9ssb/moves.ts chunk_f99c7de3b95d7f40 are useful for comparison: a move can alter its behavior through hooks such as modifying the move or running an on-hit effect. Similarly, pokemon-showdown/data/mods/gen4/items.ts chunk_ef574f63c118f2f6 shows item hooks that modify damage or base power. The architecture lesson is not the syntax. The architecture lesson is timing. A hook is meaningful only because the simulator has a defined point at which it runs. FireRed’s C engine also has timing points, even if they are expressed as function calls and switch cases rather than TypeScript object callbacks.

When you design your own clone, you need the same discipline. Do not let held-item logic randomly rewrite base stats during species lookup. Do not let field effects mutate permanent stored data. Do not let a move’s temporary battle effect permanently change the species table. These sound obvious, but many student engines violate them because the first version is small and everything is reachable from everywhere. You are advanced enough now that I expect you to protect boundaries before the prototype grows teeth.

Let’s walk one scenario end to end. A level 15 Species A wins a battle and gains enough experience to reach level 16. The experience system updates stored experience and level. The stat calculator refreshes derived stats using Species A’s base stats at the new level. The level-up move resolver checks Species A’s level-up table for moves learned at 16. Then the evolution resolver checks whether Species A evolves at 16, depending on the game’s ordering rules. If evolution succeeds, species identity mutates to Species B. Derived stats refresh again, now using Species B’s base stats. The move resolver may then need to consider evolution-specific move rules if your ruleset has them. Finally, the UI displays the result, and the stored creature now has a new species identity but the same individual history.

I’m deliberately saying “depending on the game’s ordering rules” because you should not trust vibes for this. You should check the source or write your clone’s rule explicitly. In architecture terms, the danger is not merely picking the wrong order. The danger is having no order, so different call paths do different things. If rare candy, battle experience, daycare growth, and scripted level changes all trigger different stat refresh and evolution sequences, your game becomes a collection of hidden inconsistencies. FireRed’s value as a primary source is that it shows us how a real production game centralizes many of these operations in helper functions and data access patterns rather than letting every feature invent creature mutation from scratch.

Here is another check. Suppose your test says that Species B has a base Speed of 80, but after evolution the creature still has the Speed it had as Species A. Where do you inspect first? You inspect the evolution mutation path and the stat refresh path, not the battle turn selector. If the battle turn selector is merely reading Speed, it may be behaving perfectly. The wrong value was handed to it. That is the kind of debugging discipline this course is trying to train: trace from visible behavior back through runtime state to data and mutation source.

Now, as we connect week 10 to the overall course plan, notice how the same trace has been with us since week 1. Runtime shell gives us the frame loop and callback slots. Tasks give us per-frame dispatch. Maps and scripts give us world behavior. Flags and variables give us durable progression memory. Creature records give us party and storage identity. Species tables give us static biological definitions. This week sits at the boundary where static creature design becomes battle-relevant state. Next, when we move deeper into battle, inventory, and UI, this foundation will let us ask more precise questions. Is this item changing stored creature data? Is it changing a battle-only modifier? Is it checking species compatibility? Is it consuming PP? Is it teaching a move? Is it invoking a script command that eventually calls a party mutation helper? Those questions are the architecture.

I want to emphasize one more sophistication point before we move on. Regression tests should not only test happy paths. They should test representational boundaries. For the lab, I would expect at least one test where a level-up move is available but all four move slots are full. I would expect one test where a TM compatibility bit is absent and the teaching operation fails without altering the current move slots. I would expect one test where evolution changes species and recalculates stats while preserving IVs and EVs. And I would expect one test where two stat calculations differ only in nature, so that you prove the nature multiplier happens at the correct stage with integer rounding.

If you want a Showdown-inspired testing mindset, think in terms of tiny deterministic battles or deterministic data validations. The packet’s Showdown test module references, including pokemon-showdown/test/TESTS.md, pokemon-showdown/test/assert.js, and pokemon-showdown/test/common.js, are useful because they show a mature simulator treating rules as testable artifacts. But our FireRed clone lab should keep the FireRed architecture shape: static species records, compact compatibility tables, explicit stored creature state, and source-like helper functions.

Let me return to the board one more time. I’m underlining three phrases: identity, eligibility, projection. Identity is the individual creature record: this particular party member, with its history and stored fields. Eligibility is what the species and rules allow: TM compatibility, level-up moves, evolution conditions. Projection is what the runtime derives: current stats, battle-facing values, possible UI displays. Most bugs this week come from mixing those three. If you store projection as identity, you get stale data. If you treat eligibility as current state, you overwrite move slots incorrectly. If you treat identity as species-only, evolution destroys individuality. If you treat battle hooks as species data, temporary effects leak into permanent records.

That is the conceptual center of this lecture segment. FireRed gives us the concrete cartridge-oriented version through pokemon.c calculations and mutation helpers, species_info.h records, and tmhm_learnsets.h compatibility data. Showdown gives us a useful secondary mirror through Dex and mod data, simulator hooks, and regression-test culture, with examples like pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f showing how damage logic has ordered checks and callbacks. The comparison sharpens our eye, but it does not move the authority away from FireRed.

Now, in the next part, I want to take these abstractions and apply them to the lab design directly. We will sketch the three-species table, decide what fields are static versus stored, write the stat calculation contract in plain language before code, and then walk through the level-up move resolver and TM/HM bitset lookup. Keep the identity, eligibility, projection triangle visible, because every design choice in the lab is going to land on one of those points.

ail, TM08 Bulk Up. We can fit that in one byte for the lab, though the real game needs more. Sproutle’s byte might be 00101000 if it can learn Calm Mind and Toxic, depending on bit ordering. Turtide might be 00100100 if it can learn Water Pulse and Toxic. Cindermite might be 00100010 if it can learn Dragon Claw and Toxic. I am not claiming these are FireRed-accurate species compatibilities. I am showing the shape of the data.

Now, what function do we want? canLearnTM(speciesId, machineId). It looks up the species’s compatibility bitset. It maps machineId to a bit index. It returns true or false. It should not inspect the creature’s current move slots. It should not consume the TM. It should not ask whether the bag contains the TM. It should not display “Able” or “Not able.” Those are caller responsibilities. The compatibility function is pure eligibility.

This is a good place to bring in Pokemon Showdown as a comparison, carefully. Showdown is not cartridge FireRed. It is a battle simulator and data platform with its own modeling goals. But it gives us a modern mirror for how data hooks, Dex layers, mods, and test culture can discipline a data-heavy RPG system. The research packet names pokemon-showdown/data/learnsets.ts as a high-signal modern learnset modeling file, and it also points to modded learnset files such as pokemon-showdown/data/mods/gen9dlc1/learnsets.ts, pokemon-showdown/data/mods/gen9predlc/learnsets.ts, and pokemon-showdown/data/mods/gen8dlc1/learnsets.ts. The useful comparison is that Showdown treats learnsets as data that can vary by generation or mod layer, while FireRed gives us the cartridge-era version through C tables and constants.

So, when you design the lab, do not copy Showdown’s TypeScript structures into a FireRed-style clone just because they are modern. Instead, ask what architectural problem both systems are solving. Both need a way to answer, “Is this move legal for this species under this ruleset?” FireRed answers that through fixed tables like species info, level-up data, and TM/HM bitsets. Showdown answers it through Dex-accessed data structures and mod overlays. The lesson is not “use TypeScript.” The lesson is “make move legality data explicit, queryable, and testable.”

Showdown also helps us think about battle hooks. The packet gives us pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f, where damage logic has ordered checks: immunity, OHKO handling, damage callbacks, fixed damage, level-based damage, and then later stages of calculation. That is a simulator-oriented example, but it sharpens our reading of FireRed. FireRed’s stat calculator gives the battle system numbers. Battle then has its own ordered logic for moves, type interactions, modifiers, and effects. If you let the species table contain executable battle behavior for every little exception too early, your clone becomes hard to test. If you isolate hooks and ordered phases, you can test them.

Let me give a concrete regression test set for the lab. Test one: a level 10 Sproutle with known IVs and EVs produces expected HP, Attack, Defense, Speed, Special Attack, and Special Defense. Test two: changing only the species id from Sproutle to Cindermite changes stats according to Cindermite’s base stats. Test three: changing only the level from 10 to 11 changes calculated stats, but does not change learned moves unless the level-up flow is invoked. Test four: the level-up resolver from 6 to 7 returns Vine Whip for Sproutle. Test five: the TM compatibility checker returns true for a compatible machine and false for an incompatible one. Test six: teaching a TM mutates the creature’s move slots and PP, but does not mutate the species table.

That last test is more important than it looks. It catches the common bug where a student stores “known moves” on the species record because the first demo only has one creature of each species. Then the second creature of that species magically inherits the first creature’s taught TM. This is the kind of bug that tells us the architecture boundary is wrong.

Let’s do a quick oral check. I’ll say a field, and you decide whether it belongs to species data, individual stored data, or runtime projection. Base Speed. Species data. Current PP for slot 3. Individual stored data. Max HP. Runtime projection, though it may be cached on the individual after calculation. Species type. Species data. Poison status. Individual or battle state depending on your engine, but not species data. Stat stage plus two Attack in battle. Battle runtime state. TM compatibility. Species eligibility data. Whether the player owns TM06. Inventory state. Good.

Now let’s connect to evolution and species transitions, because this is where the design becomes subtle. Evolution changes species identity while preserving individual continuity. A Charmander becoming Charmeleon is not a new party slot with random IVs. It is the same individual whose species id changes, whose stats are recalculated from a new species table row, whose current HP may need an adjustment, whose moves usually remain unless the evolution flow adds or permits moves, and whose metadata such as nickname, experience, original trainer, and IVs remains. That is a species transition, not a creature replacement.

In your lab, Sproutle can evolve into Sproutorn at level 16 if you want the fourth species as an extension, but the required set is three species, so you may instead model a transition from Sproutle to a placeholder evolved species only if your table includes it. The architectural point is this: evolution should call a mutation function that changes species id, then triggers stat recalculation, then adjusts current HP in a defined way, then checks evolution move learning if your rules include it. It should not create a brand-new creature from the wild generation path. Wild generation initializes moves from level-up defaults. Evolution preserves history.

This is where prior weeks matter. We already studied script pointer execution and script command handlers, and we are not re-teaching them today. But you can now see how a field script that gives the player a starter, a battle script that awards experience, and a party menu that teaches a TM all converge on the same creature data model. The script system may initiate the event. The battle system may initiate the level-up. The bag UI may initiate the TM use. But the creature module must provide stable contracts so those callers do not all reinvent stat and move mutation differently.

This is also why the course plan put creature and party data in weeks 9 through 11, after runtime shell, maps, and scripts. If we had started with stat formulas in week 1, you might have memorized equations without understanding where the numbers live. Now you can place them. AgbMain and callbacks give us the runtime loop. Tasks give us per-frame dispatch. Map headers and event scripts give us world behavior. Flags and variables give us story memory. Party records and species tables give us creature identity and stored state. Battle consumes those projections under a turn system. Save persistence eventually freezes and restores the owned state. That is the architecture.

And yes, regression testing belongs here. The packet points to pokemon-showdown/test as a recurring module, including pokemon-showdown/test/TESTS.md, pokemon-showdown/test/assert.js, and pokemon-showdown/test/common.js. Even without treating Showdown as our mechanics authority, we should learn from its habit of making battle and data behavior testable. In your lab, the test harness can be tiny. It can be a main function that constructs three creatures and asserts numbers. It can be a unit test file. It can be a table of expected outputs. The important habit is that data-driven systems need data regression tests, because a one-line table edit can change behavior as surely as a code edit.

Now, before we move into the next implementation pass, I want you to hold one more distinction clearly. There are lookup functions, calculation functions, and mutation functions. Lookup functions answer questions without changing state: get species info, get base stat, can learn TM. Calculation functions derive values from inputs: calculate max HP, calculate Attack, resolve default moves. Mutation functions change the creature: set species, teach move, reduce PP, gain experience, mark status, evolve. In a small project, these may live in the same source file. That is acceptable. But they should not be mentally collapsed into one kind of operation.

So when you sit down for the lab, start by writing the contracts in comments or in a short design note. Not a huge document. Just enough to prevent confusion. Species table is immutable during play. Creature record is mutable and saved. Battle stats are recalculated from species plus individual data. Level-up resolver returns eligible moves for a level interval. TM/HM checker returns compatibility from a bitset. Teaching a move is a separate mutation. Evolution changes species identity while preserving the individual.

That gives us the skeleton we need. Next, we are going to turn this into a small data layout and walk line by line through the functions: first the species struct, then the creature struct, then the stat calculator, then the level-up resolver, and finally the TM/HM bit test. Keep your eye on which function owns which responsibility, because most bugs in this part of the project are not arithmetic bugs. They are ownership bugs wearing arithmetic costumes.

So, on the board, I’m going to draw two boxes. Left box: species. Right box: individual creature. Under species I’ll put Bulbasaur, base HP, base Attack, base Defense, growth rate, types, TM/HM compatibility, level-up move table reference, evolution rule reference. Under individual creature I’ll put level, experience, IVs, EVs, nature if your clone has one, current HP, moves, PP, status, nickname, held item. Now draw one arrow from species to stat calculator, and one arrow from individual creature to stat calculator. That is the key shape. Battle stats are not “in” the species table, and they are not purely “in” the individual record either. They are produced at a boundary.

Let’s make this concrete. Suppose our three-species test set is Sprout, Budling, and Bloomant. Sprout has base HP 45, base Attack 49, base Defense 49. The creature record says this particular Sprout is level 12, has HP IV 20, Attack IV 7, Defense IV 31, and some EV values. The stat calculator should not care that Sprout evolves into Budling at level 16. It should not care that Sprout learns Vine Jab at level 13. It should not care whether Sprout is allowed to learn a TM. Its job is narrow: combine species base stat inputs with individual growth inputs and produce runtime stats.

Now, check your understanding here. If a bug report says, “After evolution, my creature kept its nickname and moves but its Defense changed,” is that automatically a bug? No. That may be exactly correct. Evolution changes the species identity, so the base Defense input changes. The individual identity remains, so the nickname, IVs, EVs, experience, and move list can persist. The stat calculator runs again with a new species row. That is the distinction we have been building toward since last week’s creature record lecture.

In FireRed, this is why the source focus for today lives heavily in pokefirered/src/pokemon.c, especially the runtime calculation and mutation chunks named in our packet as chunk_4573760bdda53819, chunk_f56e3a6588d98715, and chunk_c089f03b70501fda. Those chunks matter because FireRed is not treating “Pokemon” as a passive bag of values. It gives us a set of accessors, mutators, and helper routines around a compact saved representation. We already covered the BoxPokemon and Pokemon distinction, so I will not re-teach that. But carry it forward: the architecture wants data to be read and mutated through disciplined helper paths, not by letting every system scribble wherever it wants.

So the first function in your lab should be boring, and I mean that as praise. Something like calculateStat(species, creature, statKind). For HP, use the HP formula. For other stats, use the non-HP formula. If your project is copying FireRed-era mechanics, keep the formula consistent with that generation. If your project is a simplified architecture exercise, you can use a simpler formula, but document it. What matters architecturally is not whether the multiplier is exactly the production game’s multiplier on day one. What matters is that all callers go through one stat calculation function. Battle entry uses it. Summary screen uses it. Level-up recalculation uses it. Evolution uses it. Rare candy uses it. Debug inspection uses it. That gives you one place to test.

Now I want to connect that to Pokemon Showdown, carefully. Showdown is useful here as a secondary comparison, not as the source of truth for FireRed. Showdown’s architecture is designed around a battle simulator with a Dex abstraction, mods, event hooks, and deterministic tests. That is a different runtime problem from a GBA RPG that has overworld, save blocks, party records, script callbacks, and battle transitions. But Showdown helps us see how battle-facing data can be organized when the goal is automated simulation and regression testing.

For example, in pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, the excerpt shows damage logic passing through active move handling, immunity checks, OHKO handling, damage callbacks, level-based damage, and fixed damage cases. The important lesson is not “copy this into FireRed.” The lesson is that modern simulator code exposes battle behavior through well-defined hooks. A move can have callback-shaped behavior. A generation mod can override scripts. A test can exercise the turn engine and verify the outcome. FireRed’s architecture is more static and table-driven in C, but the engineering principle is shared: battle-visible behavior should flow through known choke points, not random scattered conditionals.

Let’s do a spoken comparison. In our FireRed-style clone, a species table row might say, “Sprout can learn TM06 and TM19.” That is a compact compatibility fact. It does not itself teach the move. It does not spend the item. It does not open a menu. It is a data answer. In Showdown’s world, the packet points us toward pokemon-showdown/data/learnsets.ts as a high-signal modern learnset modeling file, and toward mods such as pokemon-showdown/data/mods/gen9dlc1/learnsets.ts and pokemon-showdown/data/mods/gen8dlc1/learnsets.ts. That family of files is useful because it shows learnability as data that can vary by generation or ruleset. Again, do not import Showdown mechanics into FireRed. Use it to sharpen your vocabulary: base data, modded data, resolved data, and battle-legal state are not the same thing.

Now let’s walk the level-up resolver. I’ll write this on the board as a sentence first, not code: given species, old level, new level, return all moves whose learn level is greater than old level and less than or equal to new level, in table order. That phrase “in table order” matters. If your creature jumps from level 10 to level 14 and there are moves at 11 and 13, you need deterministic ordering. If two moves exist at the same level, you need deterministic ordering. If the creature already knows one of them, you need a policy. Does the resolver filter known moves, or does the teaching workflow filter known moves? Pick one owner.

For the lab, I recommend this split: the resolver reports eligible move IDs. The teaching workflow decides what to do with them. That mirrors our recurring architecture pattern. Data lookup is not mutation. Eligibility is not application. A menu choice is not the same thing as changing the creature record. You saw this same separation in scripts and flags. A script command checked or set world state, but the map data itself was not rewritten. You saw it in party data. GetMonData-style APIs expose values, while mutation routes are explicit. Today we are applying the same idea to stats and moves.

Here is the concrete example. Sprout’s learn table is level 1 Tackle, level 1 Growl, level 7 Leaf Tap, level 13 Vine Jab. A Sprout gains enough experience to move from level 12 to level 14. The resolver receives species Sprout, oldLevel 12, newLevel 14. It returns Vine Jab. It does not automatically place Vine Jab into slot three. It does not delete Growl. It does not create PP. It says, “This move became eligible.” Then the learning procedure asks, “Is there an empty move slot?” If yes, assign the move and initialize PP from the move table. If no, open the replacement UI or, in a headless lab test, return a decision-required result.

Pause there. What is the bug if the resolver directly writes to the move slot? The bug is not just “bad style.” The bug is that you have made it hard to reuse the resolver for the move relearner, summary preview, tests, or future tooling. FireRed’s learn move UI is a state machine in its own module; our packet cites pokefirered/src/learn_move.c chunk_af3598df6f092723, where the source excerpt explicitly frames a “Move relearner state machine” with callback and menu states. That supports the design separation. The UI flow has its own state. The creature data has its own mutation helpers. The eligibility data should not secretly perform UI work.

Now TM and HM compatibility. This is usually where students overbuild. They create an array of move IDs on each species, then search it, then add special cases, then wonder why compatibility checks are slow and awkward. FireRed points us toward a compact answer. The packet names pokefirered/src/data/pokemon/tmhm_learnsets.h as the high-signal FireRed TM/HM learnset table. Think bitset. One bit per TM or HM. Species row has a compatibility mask. Checking whether a species can learn a TM becomes: compute the index, load the mask word, test the bit. That is fast, small, and easy to serialize into C data.

On the board I’ll write: canLearnTM(species, tmIndex) returns boolean. That function should not check whether the player owns the TM. That function should not check whether the creature already knows the move. That function should not decrement inventory. That function should not set PP. Those are separate responsibilities. The compatibility bitset answers only one question: does this species permit this machine move?

Now let’s combine the three functions in the lab. You will have getSpeciesInfo(speciesId), calculateStats(creature), resolveLevelUpMoves(speciesId, oldLevel, newLevel), and canLearnMachineMove(speciesId, machineId). Then you will have mutations: setSpeciesOnEvolution(creature, newSpeciesId), teachMove(creature, moveId, slot), and recalculateAfterLevelOrEvolution(creature). If you find yourself writing teachTMAndMaybeEvolveAndMaybeRecalculateStats in one large function, stop. That function is trying to own too many outcomes.

I want to add Showdown again here, because it gives us a testing lens. Showdown’s test module is called out in the packet as recurring course material, especially pokemon-showdown/test/TESTS.md, pokemon-showdown/test/assert.js, and pokemon-showdown/test/common.js. The point for us is the culture of regression testing around battle and data behavior. In a simulator, you can create a battle, choose moves, advance turns, and assert outcomes. In our FireRed-style architecture, we can do smaller but equally valuable tests. Given Sprout at level 12, leveling to 14 returns Vine Jab. Given Sprout’s TM bitset, TM06 returns true and TM22 returns false. Given evolution to Budling, nickname remains, species ID changes, calculated stats change, current HP policy is applied consistently.

Notice that last phrase: current HP policy. This is an advanced class, so I expect you to be precise. When max HP changes after level-up or evolution, what happens to current HP? There are several possible policies. Production Pokemon games commonly preserve damage by increasing current HP by the max HP delta on level-up, so a healthy Pokemon remains healthy. Your clone needs an explicit policy. If max HP goes from 35 to 39 and current HP was 35, should current HP become 39? Probably yes. If current HP was 10, should it become 14, preserving damage, or stay 10, preserving absolute HP? Pick intentionally. Test it. Do not let it emerge accidentally from assignment order.

Let’s check understanding with a small scenario. A level 15 Sprout has 20 out of 40 HP. It evolves into Budling, whose recalculated max HP is 48. If your policy preserves damage, how much HP does it have after evolution? It had 20 damage before, because 40 minus 20 is 20. Preserve the damage, and the new current HP is 28. If your policy preserves percentage, it would be 24. If your policy preserves absolute current HP, it would stay 20. All three are implementable. Only one should be chosen. The architecture mistake is not choosing.

Now, why does this belong in week 10, and how does it connect to the course plan? Weeks 1 and 2 gave us the runtime shell and build/data pipeline vocabulary. We learned that tables become compiled assets, callbacks drive visible behavior, and tasks schedule frame-by-frame work. The middle weeks gave us maps, scripts, flags, and progression, so we could trace player-visible events through command handlers and state mutation. Week 9 moved us into creature records: party slots, saved state, accessors, and species info. This week is the hinge between static creature identity and battle-ready behavior. Stats, moves, learnsets, and evolution are where the data model starts producing mechanics.

That is why the ownership boundaries matter so much. In a creature RPG, almost every major system touches the creature record. Battle reads stats and moves. Party menu reads HP, status, and moves. Items mutate HP, PP, status, level, or evolution. Scripts can give creatures, check party conditions, or trigger special events. Save persists the result. Link systems exchange or compare records. If stat calculation and move eligibility are sloppy, the sloppiness spreads everywhere.

Showdown gives us one more useful contrast here: mods. The packet repeatedly points to pokemon-showdown/data/mods as high-value comparison material. In Showdown, a mod can inherit, override, or reshape data and scripts for a generation or ruleset. The pokemon-showdown/data/mods/gen4/items.ts chunk_ef574f63c118f2f6 excerpt shows item behavior using inherited data and event hooks such as onModifyDamagePhase2 and onBasePower. That is a very different implementation style from FireRed’s C tables and battle engine, but it highlights a powerful question: where does ruleset variation live? In FireRed, generation rules are mostly baked into the game. In Showdown, generation rules are first-class data and scripts layered through Dex/mod resolution. For our course, FireRed remains the architecture source, but Showdown helps us discuss how the same concepts scale when you want many rulesets and automated battle verification.

So, if you were designing your own engine for a long-lived creature RPG, you might choose a FireRed-like compact runtime for the shipped client and a Showdown-like simulator harness for validation. That is a serious production pattern. The client needs memory efficiency, deterministic local behavior, and integration with UI, save, and world state. The simulator harness needs fast scenario creation, readable data overrides, and regression tests. They do not have to be the same program to share data concepts.

Let’s bring this back to your week 10 homework. I am going to expect your three-species test set to prove boundaries, not just happy-path arithmetic. Include one species that evolves. Include one move learned across a multi-level jump. Include one TM-compatible species and one incompatible species. Include a case where the creature has four moves and must make a replacement decision. Include a stat recalculation test where max HP changes. If your test only says, “level 5 stat equals expected number,” you have tested arithmetic but not architecture.

A good test name might be spoken as a behavior sentence: “leveling from twelve to fourteen reports the level thirteen move without mutating move slots.” Another: “machine compatibility checks the species bitset without consuming the TM.” Another: “evolution changes species and recalculates stats while preserving individual values.” Those tests catch ownership bugs. And remember our earlier phrase: most bugs in this part of the project are ownership bugs wearing arithmetic costumes.

Now I’ll do one final board pass. Species table: immutable during play. Creature record: mutable and saved. Stat calculator: pure or mostly pure computation from species plus individual inputs. Move resolver: eligibility over a level interval. TM/HM checker: bitset membership. Teaching move: explicit mutation. Evolution: species transition that preserves the individual and triggers recalculation. Battle: consumes the resolved runtime state.

And Showdown, in the margin, not in the center: Dex and mods for data resolution, event hooks for battle behavior, simulator turns for deterministic execution, tests for regression confidence. Useful comparison, not replacement source. FireRed remains our main architectural object because this course is about the full creature RPG stack, not only the battle simulator.

So the synthesis for today is this: data-driven design is not just “put numbers in tables.” Data-driven design means each table has a clear authority, each runtime record has a clear lifetime, and each transition from data to behavior has a named function that can be inspected and tested. Species data becomes battle stats only through calculation. Learnset data becomes a move only through eligibility and teaching. Evolution becomes a new species identity without destroying the individual. When you can explain those transitions cleanly, you are no longer just copying Pokemon-like mechanics. You are designing the architecture that lets a creature RPG stay coherent as the number of species, moves, items, scripts, and battle cases grows.

## Source References
- pokemon-showdown/data/mods/gen2stadium2/scripts.ts
- pokefirered/src/daycare.c
- pokefirered/src/learn_move.c
- pokemon-showdown/data/mods/gen4/items.ts
- pokemon-showdown/data/mods/gen9ssb/moves.ts
