# Showdown Turn Loop Anatomy

Lecture ID: lecture_w23_d01
Week 23, Day 1

## Objectives
- Identify the FireRed files, structs, functions, constants, data tables, and state variables that implement each major game system.
- Connect week 23 material to the overall course plan.

## Transcript
All right, let’s pick up exactly where week twenty-two left us. By now, when I say “trace behavior,” I do not mean, “find the one function that does the thing.” That habit should be gone. In FireRed, and in any serious creature RPG, visible behavior is nearly always the product of data tables, global state, callback dispatch, task dispatch, script dispatch, and a few very carefully arranged state machines. Last week’s homework and quiz asked you to trace from records through globals, callbacks, tasks, scripts, structs, and concrete functions. This week, we keep that discipline, but we use Pokemon Showdown as a comparison object. Not as the authority over FireRed. Not as a replacement for the cartridge architecture. As a clean, inspectable simulator that makes some boundaries explicit that FireRed often hides inside callbacks and globals.

Today’s lecture is called Showdown Turn Loop Anatomy, and the word “anatomy” matters. We are not just saying, “a turn happens.” We are going to divide a turn into organs: request state, player choice collection, commitment, action queue construction, queue resolution, move execution, damage and side effects, and protocol output. Then we will keep asking the FireRed question: where is the equivalent responsibility in the GBA codebase? Sometimes the answer is a specific file. Sometimes it is a struct plus a global plus a callback. Sometimes it is not one-to-one, because Showdown is a server simulator and FireRed is a local game runtime.

Let me put the first frame on the board. Imagine a turn as a transaction. On the left, players receive a legal choice request. In the middle, each player returns an intention: move, switch, pass, maybe a forced action. On the right, the simulator commits those intentions, sorts them into an executable queue, runs them, mutates battle state, and emits a protocol log. That is the Showdown shape. The source packet points us to pokemon-showdown/sim/battle.ts, chunk_3b063b1b44a6d638, for the Battle class and request-state model; pokemon-showdown/sim/battle.ts, chunk_889e310105810281, for the choose and commitChoices flow; pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e, for the battle action queue; pokemon-showdown/sim/battle-actions.ts, chunk_95a7524b39636e24, for runMove responsibilities; pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59ac, for the BattleStream API; and pokemon-showdown/server/README.md, chunk_1ba53f1706d86463, for the server runtime boundary. Those are our landmarks.

Now, before we enter Showdown, recall the FireRed landmarks without re-teaching them. In FireRed, startup gives us AgbMain in pokefirered/src/main.c, chunk_b35676322b169f85. The main callback slot, SetMainCallback2, is in pokefirered/src/main.c, chunk_6cfd90f6d0136799. Task allocation and per-frame task dispatch come from CreateTask and RunTasks in pokefirered/src/task.c, chunks chunk_cb0bb91a147bfb59 and chunk_3ffebc0341225936. That means FireRed’s runtime asks, frame by frame, “which callback owns the machine right now, and which tasks are alive?” Showdown asks a different question: “has the simulator received enough committed choices to advance a deterministic turn?” Same design problem, different runtime boundary.

Here is the first important distinction. FireRed’s player is physically present at the machine. The local UI can ask for input, hold partial menu state, animate a cursor, and commit when the A button is pressed. The battle engine and the UI engine are tightly choreographed. In Showdown, the player may be across a websocket, a test harness, or a BattleStream object. So Showdown’s Battle class needs explicit request state. It must know, for each side, what the simulator is currently asking for and whether that side has answered. That is why pokemon-showdown/sim/battle.ts, chunk_3b063b1b44a6d638, is such an important comparison point. The Battle object is not merely “the place where damage happens.” It is the owner of battle state and the request lifecycle.

Let’s slow down there. When Showdown sends a request, it is not just asking, “what move do you want?” It is sending a structured description of the legal decision surface. Which active Pokemon are present? Are they trapped? Are they fainted? Must they switch? Are moves disabled? Is there a team preview decision? Is this a move request, a switch request, or something else? In FireRed, many of those questions are encoded through battle controller state, menu state, battler status flags, side conditions, and global battle variables. In Showdown, because the simulator boundary is textual and network-friendly, the request has to be serializable and explicit.

A concrete example. Suppose player one has an active Pokemon with four moves, but the second move has zero PP and the third is disabled by a volatile condition. In FireRed, the fight menu can render those move slots with PP and availability, and the battle controller can reject or prevent an illegal selection based on battle state. The data and state live in places like party Pokemon data, move tables, battle globals, and controller buffers. In Showdown, the request sent to the player contains enough information for the client to show which moves can be chosen. The choice returns as something compact, like “move 1” or “switch 2,” and then Battle.choose validates and records it. That choose and commit flow is the focus of pokemon-showdown/sim/battle.ts, chunk_889e310105810281.

Check for understanding here: if a player clicks a move button in Showdown’s web client, has the move happened yet? No. The click is a proposed choice. Even after the client sends it, the simulator still has to validate it, store it, and wait until the turn can be committed. This is a major conceptual upgrade over beginner battle-loop explanations, where input and execution are often collapsed into one step.

Now let’s put a simple pseudo-turn on the board. I’ll write it in plain English rather than code. Start of turn: Battle produces requests for both sides. Player one chooses move one. Player two chooses switch two. Battle records both choices. Once all required choices are present, Battle commits choices. Commit creates actions: player two’s switch action, player one’s move action, perhaps residual actions, perhaps before-turn actions. BattleQueue orders those actions. The simulator resolves the queue. When it reaches the move action, BattleActions.runMove handles the active move’s responsibilities. Damage and effects mutate the battle. Output is appended to the battle protocol stream. Then, if the battle continues, new requests are generated.

That is the skeleton. The valuable part is not memorizing the skeleton. The valuable part is assigning responsibility accurately. Request state belongs to Battle. Choice parsing and commitment live in Battle’s choose and commitChoices flow. Queue ordering belongs to BattleQueue in pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e. Move execution responsibility is concentrated in BattleActions.runMove in pokemon-showdown/sim/battle-actions.ts, chunk_95a7524b39636e24. Protocol-facing interaction is exposed through BattleStream in pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59ac. And full online runtime concerns belong outside the simulator proper, with the server boundary documented in pokemon-showdown/server/README.md, chunk_1ba53f1706d86463.

Now I want to connect that to FireRed in a way that will matter for your lab. FireRed does not have a BattleStream object. FireRed has a cartridge runtime. It has callbacks, tasks, hardware timing, save data, link modes, battle setup, and battle controller responsibilities. We have already covered RFU mode constants, link manager messages, VBlank timers, and link state, so I am not going to re-teach those. But I do want you to reuse them as prior foundation. In FireRed, once you move from overworld into battle, the boundary is not “send text into a stream.” It is “set battle type flags, prepare parties and opponent data, transition callbacks, allocate tasks, and enter the battle main loop.” The research packet points us to pokefirered/src/battle_setup.c, chunk_494d132a59422c85, which includes battle setup dependencies like task, overworld, script, event data, battle, battle transition, and battle communication headers. That include list is not trivia. It tells you battle entry is a cross-system handoff.

So, when I ask, “what is the FireRed equivalent of Showdown’s request state?” the answer is not one filename. The equivalent is distributed. Part of it is the battle UI asking the local player what to do. Part of it is battle controller communication. Part of it is battler state and selection globals. Part of it is the task/callback shell that keeps the game in battle mode instead of overworld mode. The local runtime does not need to serialize a choice request for a browser client in the ordinary single-player case. But it still must represent the same concept: what choices are currently legal, what choice has been selected, and when the battle engine may advance.

This is why Showdown is pedagogically useful in weeks twenty-three and twenty-four. It makes the transaction boundary visible. FireRed makes the hardware and callback boundary visible. A mature architecture reading can hold both in the same mind.

Let’s inspect the commit idea more carefully. In many student designs, I see a function called doTurn, and inside it the code immediately asks player one for a move, asks player two for a move, and runs damage. That may work for a toy game, but it erases three major needs. First, choices may come from different sources: local input, AI, link opponent, replay file, or network client. Second, choices may be submitted at different times. Third, execution order is not the same as input order. Showdown’s choose and commitChoices flow, again in pokemon-showdown/sim/battle.ts, chunk_889e310105810281, exists because the simulator must separate “I received a command” from “the turn is ready to resolve.”

Concrete example. Player one chooses a damaging move with normal priority. Player two chooses a switch. The player one command might arrive first over the network, but the switch action may resolve before the move depending on the battle rules. Input arrival order is not battle order. In a local FireRed battle, the menu makes it feel sequential, but battle resolution still depends on priority, speed, switching rules, forced switches, pursuit-like exceptions depending on generation, and faint replacement handling. If your architecture lets UI order leak into simulation order, you have built a bug generator.

Now, let’s talk about the action queue. On the board, I’m drawing a vertical queue with labeled entries: before turn event, switch action, mega or special action if the generation supports it, move action, residual event, faint handling, end-turn event. The exact contents depend on generation and format, but the architectural idea is stable. Pokemon Showdown has an explicit queue module, pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e. The queue is the simulator’s answer to the question, “what executable units are waiting, and by what ordering rule should they run?”

In FireRed, the equivalent is more implicit. You will see state variables, command buffers, controller execution, battle scripting, animation waits, and per-frame progression. FireRed often turns one logical action into many frame-level states because the GBA has to animate, wait for text, play sound, update OAM, and synchronize link communication. Showdown does not need to spend frames moving sprites. It can emit protocol messages like “damage happened” or “move used,” and a client can animate however it wants. So Showdown’s queue is closer to pure battle semantics, while FireRed’s battle loop interleaves semantics with presentation timing.

Do not misunderstand that as “Showdown is cleaner and FireRed is messy.” That would be too shallow. FireRed is solving for a very different machine. Its architecture has to own the screen, audio, input, timing, memory, and link transport under GBA constraints. Showdown’s architecture has to own deterministic simulation, validation, text protocol output, testability, and online separation. The fact that they factor responsibilities differently is not a moral ranking. It is architecture matching environment.

Let’s do another check for understanding. If Showdown’s BattleQueue says a move action is next, does that mean the move’s damage formula is inside the queue? No. The queue schedules the action. The move execution system performs the action. That distinction is one of today’s main points. A queue entry should not become a second battle engine. It should carry enough information to invoke the right responsibility at the right time.

That brings us to runMove. The source focus names pokemon-showdown/sim/battle-actions.ts, chunk_95a7524b39636e24, for runMove responsibilities. I want you to think of runMove as the boundary where a chosen move stops being an intention and becomes a sequence of rule applications. It has to answer questions like: can the user still act? What move object is being used after transformations or overrides? What targets are valid? Does the move fail? Does it consume PP? Which events fire before the hit? What happens on immunity? How are secondary effects handled? What output messages are added? What state changes persist afterward?

We already used the Dex comparison in recent lectures, so I will only briefly weave it forward. Showdown can ask the Dex for move, species, item, and ability definitions. The prior lecture on Dex lookup compared that to FireRed’s tables and facades. For today, the important point is that runMove is not a lookup layer. It consumes looked-up move data and battle state to perform rule execution. The move data may include callbacks, flags, target categories, base power, and effects. The simulator then applies those definitions through a structured event system.

A small example. Take a basic damaging move. At a beginner level, you might say the engine subtracts HP. At our level, that sentence is almost useless. The engine must identify the active move, verify the user can move, resolve target position, check protection and immunity, calculate accuracy unless bypassed, calculate damage using stats and modifiers, apply damage, trigger on-hit effects, trigger secondary effects, update faint state, and emit protocol messages. If a move has a special damage callback, the ordinary damage path may be modified. The research packet includes pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, which shows damage calculation logic considering immunities, OHKO moves, damage callbacks, fixed damage, level-based damage, and related move properties. That chunk is not today’s main file, but it is useful evidence that Showdown’s move execution is data-driven and callback-aware rather than a single hard-coded “subtract power” routine.

Now contrast FireRed. In FireRed, move definitions, battle scripts, constants, and globals cooperate. The cartridge battle engine is not receiving a text command from a remote client, but it still has to move from selected command to executable battle behavior. FireRed’s equivalent of “runMove responsibilities” lives across battle command execution, move effects, battle scripts, damage calculation helpers, status flags, and animation/text sequencing. Again, one-to-one mapping is the wrong expectation. Responsibility mapping is the right expectation.

Let me say that another way, because this is where advanced students sometimes overfit the comparison. You are not allowed to say, “Showdown has BattleStream, therefore FireRed must have BattleStream but hidden.” No. FireRed has a different boundary. You are allowed to say, “Showdown’s BattleStream is the API through which an external caller pushes commands and receives protocol output; FireRed’s analogous boundary for player-visible battle progression is the callback/task/controller shell plus local input and rendering.” That is a disciplined comparison.

Now, BattleStream. In the lab, you will run or pseudocode a minimal BattleStream exchange. You should imagine something like this: create a stream, write a start command with format and players, write player choices, read protocol output. The exact syntax is not the point of the lecture transcript here; the architecture is. Pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59ac, is the source focus for this API. The stream lets a caller treat the simulator as a command processor. It is especially useful for testing, embedding, and separating the simulator from the server runtime.

That separation matters. Pokemon Showdown also has a server, and the research packet directs us to pokemon-showdown/server/README.md, chunk_1ba53f1706d86463, for server run command and localhost behavior. The server is not the battle simulator itself. It hosts users, rooms, networking, chat, ladder or room behavior depending on configuration, and it can instantiate or communicate with battles. This is the boundary that many new Showdown readers blur. BattleStream is a simulator-facing API. The server is an online runtime around many concerns. In FireRed terms, confusing those would be like confusing the battle engine with the whole GBA main loop plus overworld plus save system plus link entry points.

Let’s pause and recap the first arc. A Showdown turn begins with explicit request state owned by Battle. Player choices are received through choose-like APIs and are not execution yet. commitChoices turns complete choices into actions. BattleQueue orders actions according to battle rules. BattleActions.runMove executes move semantics. BattleStream exposes the simulator as a command-output interface. The server runtime sits outside that simulator boundary. FireRed solves the same gameplay problem through local callbacks, tasks, global battle state, controller flow, battle setup, scripts, and rendering timing.

Now I want to connect week twenty-three to the overall course plan. Weeks one through twenty-two gave us FireRed as a stack of cooperating state machines: runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link. The course thesis, from the research synthesis, is that FireRed should be read as data tables flowing into runtime globals, callbacks, tasks, scripts, structs, and player-visible behavior. Week twenty-three adds a comparison lens: Showdown gives us a modern simulator where turn-state boundaries are easier to name and test. Week twenty-four will use that comparison for verification and cumulative synthesis. The final will not ask you to worship either architecture. It will ask whether you can explain why each one puts state where it does.

So, as we go into the next segment, keep this question active: when an RPG says “waiting for player choice,” where is that fact stored, who is allowed to change it, and what evidence tells the engine it may advance? In Showdown, that question points directly into Battle request state and commit flow. In FireRed, it points into the battle controller and the callback/task-driven runtime. Same gameplay concept, different architectural expression. In the next part, we will walk a single turn more mechanically, from request generation through queue insertion, and we will keep annotating the board with the FireRed analog beside each Showdown responsibility.

Good. Now let us make that mechanical.

Imagine one ordinary singles turn. Player one has Charizard active. Player two has Blastoise active. Charizard chooses Thunder Punch, Blastoise chooses Surf. I am choosing a familiar example because I do not want the species matchup to be the hard part. The architecture is the hard part. The turn begins, conceptually, with the simulator saying, “Both sides need input.” But an engine cannot run on a concept. It needs stored state. It needs a request object, or a mode flag, or a controller command, or a task state. That is the architectural question we are tracking.

In Pokemon Showdown, the relevant source focus is the Battle class and request-state model in pokemon-showdown/sim/battle.ts, chunk_3b063b1b44a6d638. The important thing is not merely that there is a Battle object. Many engines have a battle object. The important thing is that the Battle object is the central owner of turn progress, side request state, and the decision about whether the simulator is still waiting or may advance. So when I say, “the battle is waiting for choices,” I want you to translate that into: the Battle instance has generated requests for the sides, the sides have not all committed valid choices yet, and the simulation must not execute the action queue until the commit condition is satisfied.

Now, on the board, I am going to draw four boxes in a row. First box: request. Second box: choice. Third box: commit. Fourth box: queue execution. Underneath that, I will draw the FireRed comparison line. In FireRed, we do not have a network-facing BattleStream request object in the same sense. We have battle setup, battle controllers, global battle state, callbacks, and per-frame task dispatch. The earlier runtime shell material gave us the frame dispatch side: pokefirered/src/main.c, chunk_b35676322b169f85, for AgbMain startup responsibilities; pokefirered/src/main.c, chunk_6cfd90f6d0136799, for SetMainCallback2; and pokefirered/src/task.c, chunk_3ffebc0341225936, for RunTasks. I am not re-teaching those today. I am just reminding you that FireRed’s “may advance” question is usually expressed through callback state, task state, controller state, and global battle variables rather than through a single server-friendly API call.

So let us walk the first box: request. In Showdown, when the simulator reaches the point where player input is required, the Battle class builds a request for each side. This request is not just a UI prompt. It is a structured statement of legal options from the simulator’s current truth. It knows whether the side is choosing a move, forced to switch, handling team preview, or waiting. This is why I keep saying that “request” is part of the simulator boundary, not just the client boundary. The client may render buttons, but the simulator determines what the buttons mean.

Check your understanding here: if a Pokemon is trapped and cannot switch, should the client be the authority that removes the switch button? No. The client may hide it, but the simulator must be the authority. Otherwise, a malicious or buggy client could submit a switch anyway. In Showdown terms, the request is a filtered view of simulator state. In FireRed terms, the UI menu is also constrained by battle state, but the trust boundary is different because the cartridge is not running as a public server accepting arbitrary text commands from remote clients.

Now move to the second box: choice. The choose and commit flow is in pokemon-showdown/sim/battle.ts, chunk_889e310105810281. This is where the architecture becomes especially useful for us. A player’s choice is not the same thing as a resolved action. If Charizard chooses Thunder Punch, that choice is still partly symbolic. It must be parsed, validated, attached to the correct side, and held until the turn can commit. It is not yet “damage Blastoise.” It is closer to “side one’s active Pokemon intends to use move slot X against target Y, subject to later legality checks and turn-order resolution.”

That distinction is easy to miss if you only think in terms of visible gameplay. A player presses Fight, presses Thunder Punch, and the move happens. But internally there are at least three different truths. There is the input truth: what did the player select? There is the action truth: what action is placed in the queue? And there is the execution truth: what actually happens after speed, priority, paralysis, fainting, redirection, immunities, protection, and move effects are applied? Advanced architecture work means keeping those truths separate.

Let us test that with the Charizard and Blastoise example. Suppose Charizard selects Thunder Punch, but before Charizard moves, Blastoise somehow uses a higher-priority action that knocks Charizard out. Was Charizard’s choice valid when selected? Yes. Does Charizard’s queued action necessarily execute successfully? No. Those are different phases. If your engine collapses them into one phase, you will have trouble representing interruption, fainting, Encore-like locks, forced moves, or replacement decisions.

In FireRed, the analogous separation exists, but it is distributed differently. Battle entry itself is prepared through battle setup code; the packet gives us pokefirered/src/battle_setup.c, chunk_494d132a59422c85, which includes battle transition, overworld, script, event, item, and battle headers. That include list is not decorative. It tells you battle launch is a boundary-crossing event. The overworld and script system hand control toward the battle system, battle transition handles presentation, and battle code takes over the domain logic. Once in battle, FireRed’s player input is mediated by battle menus and controllers rather than by Showdown’s text-command choice parser. Same conceptual phase, different expression.

Now third box: commit. Commit is the moment where the simulator says, “I have enough valid choices to stop waiting and begin executing the turn.” The subtlety is that commit is not necessarily identical to “both humans clicked.” Some sides may be controlled by AI. Some requests may be wait states. Some battles may involve forced switches. Some formats have multiple active Pokemon. Some actions are generated automatically. Therefore the commit function has to ask a structural question: are all required choices present and coherent for the current request state?

This is where Showdown’s architecture is very explicit. The Battle class can receive choices side by side, and only when the commit condition is met does the turn progress into queue resolution. That is the source-level reason we care about pokemon-showdown/sim/battle.ts, chunk_889e310105810281. It is not just “the player picks a move.” It is the transition from request-state world into action-queue world.

On the board, I am writing: “choice is local to a side; commit is global to the battle.” That sentence is worth underlining. A side can be ready while the battle is not ready. This matters in online play. It also matters in local deterministic simulation. If your test harness submits only one side’s move and then expects damage to occur, your mental model is wrong. You have only satisfied one side’s request. The battle still lacks the other required input.

Now, compare this to FireRed. In a cartridge battle, especially a single-player wild or trainer battle, the engine can often generate opponent decisions internally and proceed through controller states without exposing a server-like “commitChoices” boundary. But the boundary still exists conceptually. The player battler has an input-selected action. The opponent battler may have an AI-selected action. The battle engine must gather the needed actions before resolving the turn. FireRed’s implementation is shaped by GBA runtime constraints and by its callback/task/controller architecture. Showdown’s implementation is shaped by server simulation, text protocols, and deterministic replay needs.

Let us move into the fourth box: the action queue. The source focus here is pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e. Once choices are committed, the simulator does not simply run player one then player two. It creates actions. These actions have type, priority, speed ordering, subordering, target information, and references to the acting Pokemon or side. The queue is the formal structure that turns simultaneous choices into sequential execution.

This is the proper place to discuss priority. Priority is not just a number printed on a move. It is one ordering dimension in the action queue. Quick Attack has higher move priority than many normal attacks, but switching, mega evolution in older rulesets, before-turn effects, residual effects, and forced actions may occupy different places in the broader action ordering. So an action queue is more general than a move list. It is the battle’s schedule.

Here is the concrete example. Charizard chooses Thunder Punch. Blastoise chooses Surf. Assume no priority modifiers. The queue receives two move actions. The queue resolver compares action priority and then speed-relevant ordering. If Charizard is faster, Charizard’s move action resolves first. But that does not mean the queue “contains damage.” It contains an instruction to attempt the move. When Charizard’s action is popped or resolved, the simulator still has to run the move pipeline. That pipeline checks whether Charizard can act, whether the move is still legal, what the target is, whether the move hits, and how the effects apply.

Say that back to yourself: the queue schedules attempts, not guaranteed outcomes. That is the central point.

Now we connect that to runMove. Showdown’s runMove responsibilities are in pokemon-showdown/sim/battle-actions.ts, chunk_95a7524b39636e24. runMove is not merely “subtract HP.” It is the orchestration point for move execution. It has to translate the queued move action into battle events: deduct or account for PP where appropriate, handle before-move checks, lock or fail conditions, target resolution, move event hooks, hit processing, secondary effects, faint handling, and protocol messages. The exact details vary by generation and mechanic, but architecturally runMove is where a symbolic action becomes concrete battle consequences.

This is why we spent time in the previous week on the Dex as a battle data facade and generation mods, but we are not re-teaching that today. runMove depends on move data and effect hooks, but today our question is where the turn loop calls into that behavior. Data lookup tells us what Thunder Punch is. The queue tells us when Charizard attempts it. runMove tells us how that attempt becomes events, damage, and output.

Let me give a small board trace. I write: request move. Then I write: side one chooses “move 1”; side two chooses “move 2.” Then: commitChoices. Then: queue has Charizard move action and Blastoise move action. Then: resolve Charizard action. Then: runMove Thunder Punch. Then: damage and messages. Then: resolve Blastoise action, unless Blastoise has fainted or another condition prevents execution. That final caveat is important. Queue entries are not destiny. They are candidates processed against live state.

Now check for understanding. If Thunder Punch knocks out Blastoise before Surf executes, where should the decision to prevent Surf occur? Not at initial choice time. Blastoise was alive when the choice was made. Not at queue insertion time, because the queue may reasonably contain Blastoise’s intended action. The decision belongs at or near action resolution and move execution, when the engine checks current battle state. That is the only phase with the latest truth.

FireRed has the same need, even if the organization differs. The battle engine cannot assume that every selected action remains executable. It must handle fainting, replacement, turn order, and follow-up states. The course-wide pattern is visible again: data tables define possibilities, globals and structs hold live state, callbacks and controllers advance phases, and visible behavior emerges only after a chain of state transitions. That is the same “data table to runtime state to callback or action to visible behavior” tracing skill you used in homework_w22 and quiz_w22. Week twenty-three raises the difficulty by asking you to compare two architectures without flattening them into each other.

Let us now add protocol output, because this is where Showdown differs strongly from FireRed. The BattleStream API is in pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59ac. BattleStream is the interface that lets a caller send input into the simulator and receive protocol output back. This is a server-friendly and test-friendly boundary. You can write commands into the stream, such as starting a battle or choosing actions, and read structured output lines that describe requests, updates, damage, fainting, and other events.

That is not how FireRed presents battle state. FireRed drives graphics, text boxes, animations, sound, and input on the GBA. There is no textual BattleStream protocol as the ordinary runtime boundary. But conceptually, the engine still emits consequences. A move result becomes HP bar updates, battle messages, animation commands, sound cues, and state changes. Showdown emits protocol messages because it is a simulator and server runtime. FireRed emits audiovisual and memory-state consequences because it is a cartridge game engine. Again, same gameplay truth, different boundary.

This distinction will matter in the lab. When you pseudocode a minimal BattleStream exchange, I do not want you to stop at “send move, get damage.” I want you to label each phase. “The stream receives a choice command.” “The Battle validates it against request state.” “When all required choices are present, commitChoices advances the battle.” “The BattleQueue orders actions.” “runMove executes a move action.” “BattleStream emits protocol output.” Then, beside each of those, you write the FireRed equivalent: battle menu/controller input, selected action storage, battle turn progression, speed and priority resolution, move effect routine, HP/text/animation output. Some of those FireRed boxes will be distributed across multiple files and globals; that is the point of the comparison.

Let us briefly include the server runtime boundary. The packet points us to pokemon-showdown/server/README.md, chunk_1ba53f1706d86463, for server run command and localhost behavior. This matters because Showdown is not only a simulator library. It can be run as a server that accepts users, rooms, battles, and networked play. But the server is not supposed to be the place where move damage is invented. The server coordinates users and rooms; the simulator owns battle legality and resolution. That separation is one of the cleanest architectural lessons you can take from Showdown back into your own RPG engine.

In FireRed, we recently studied link foundations: RFU mode constants, link manager callback messages, VBlank timers, and link state. We are not reopening that entire topic. But you should now be able to see why those lectures were placed before this one. A multiplayer boundary always raises the same question: what state is local presentation, what state is communication protocol, and what state is authoritative gameplay? Showdown answers with server infrastructure plus simulator boundaries. FireRed answers with link state machines plus battle engine handoff. The names differ. The architectural pressure is the same.

Now, I want to slow down and ask a more advanced design question. Why does Showdown not just let each client simulate the battle independently? Both clients could know the rules, right? Both clients could receive the teams, choose moves, compute speed, and animate the outcome. Why centralize the authoritative Battle object?

There are several reasons. First, anti-cheat. If the client is authoritative, it can lie. Second, determinism. The simulator needs one source of truth for random numbers, event order, and hidden information. Third, spectators and replays. A central protocol stream can describe what happened without exposing what should remain hidden. Fourth, testing. A deterministic simulator boundary lets tests create a battle, submit choices, and assert outputs. This is one reason pokemon-showdown/test appears as a recurring module in the research packet. The testing story follows naturally from the architecture.

FireRed does not have the same public server trust problem in normal play, but it still needs determinism. It still needs one authoritative battle state. It still needs a coherent sequence from input to result. On the GBA, the constraints are memory, timing, hardware registers, callbacks, and link synchronization. On Showdown, the constraints are server concurrency, protocol output, generation-specific rules, and adversarial clients. Different constraints, same need for disciplined state ownership.

Let us return to our Charizard example and make it slightly more complex. Suppose the player submits “move 1,” Thunder Punch. The simulator accepts the choice. Then, before the turn commits, the player tries to submit a different move. Should that be allowed? The answer depends on the request state and cancellation support. Some simulators or battle modes allow changing an uncommitted choice; some do not once locked in; some support cancel behavior only before all sides commit. The key architecture point is that this policy belongs in the choose and commit flow, not in runMove. runMove should not care whether the player changed their mind thirty seconds earlier. By the time runMove is called, it should receive a resolved action attempt.

That division of responsibility is what I will be looking for in homework_w23. If you write a minimal battle pseudocode and your damage function parses player commands, validates request state, decides whether both sides are ready, sorts speed order, and applies HP damage all in one blob, that is not an advanced answer. It might work for the first demo. It will collapse under switching, forced moves, doubles targeting, online play, or replay tests. The architecture we are studying gives you names for the boundaries: request, choice, commit, queue, runMove, stream output.

Now I am going to add a second board cue. Draw a vertical line between “commit” and “queue execution.” Label the left side “input phase.” Label the right side “simulation phase.” That line is not absolute in all engines, but it is a useful mental model. To the left, we are concerned with what choices are needed, who has supplied them, whether they are legal, and whether the battle may proceed. To the right, we are concerned with ordering, events, damage, fainting, and output. Bugs often happen when a responsibility crosses this line without a reason.

For example, suppose a client UI decides a move is disabled and refuses to send it, but the simulator would have allowed it. That is a UI bug. Suppose the simulator accepts a move even though the request did not allow it. That is a simulator validation bug. Suppose the queue sorts two equal-priority actions incorrectly because it uses stale speed. That is a queue resolution bug. Suppose runMove applies damage before checking immunity. That is a move execution bug. The visible symptom may be “the wrong Pokemon took damage,” but the architectural diagnosis depends on locating the phase.

The research packet gives us a useful reminder from pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f: damage calculation logic can include immunity tests, OHKO handling, callbacks, level-based damage, fixed damage, and then ordinary calculation. I am not using that chunk as today’s main source, but it supports the broader claim that move execution is not a single arithmetic line. The move pipeline is a rule interpreter. Therefore the turn loop must call it at the right time and with the right state.

And from the FireRed side, pokefirered/src/battle_setup.c, chunk_494d132a59422c85, reminds us that entering battle already depends on many modules: overworld, scripts, items, transitions, event data, and battle includes. So when a FireRed battle reaches a player decision, it is already inside a carefully staged runtime context. You cannot understand a battle turn only by looking for a “damage formula.” You must ask how the engine got into battle mode, how input is routed, how state advances frame by frame, and how the result returns to overworld or script state afterward.

Let me pause for a comprehension check. I will phrase this as a final exam-style question, though not at full final difficulty. In Showdown, a player’s selected move is stored before the move executes. What are two reasons that selected move might not produce damage? One: the action may never successfully resolve because the Pokemon faints or cannot move. Two: runMove may resolve the attempt but the move may fail, miss, hit immunity, be blocked, or produce a non-damaging effect. There are many valid variants of that answer, but they must preserve the phase distinction.

Now another check. In FireRed, if you are tracing the equivalent path, should you begin from the move animation? No. The move animation is late evidence. You should begin earlier, from battle setup and battle state, then input routing, then action selection, then turn resolution, then animation and text. This is exactly the discipline we practiced with maps and scripts earlier in the course. Do not start with the thing the player sees. Start with the data and state that make the visible thing inevitable.

As we continue, keep your notes aligned to the four boxes. Request is the simulator’s statement of needed input. Choice is a side’s submitted answer. Commit is the battle-level transition when all necessary answers are ready. Queue execution is the ordered processing of action attempts. runMove is the move execution orchestrator inside that ordered processing. BattleStream is the API boundary that makes this exchange visible to external callers. The server runtime wraps that in users, rooms, and localhost-running infrastructure, as indicated by pokemon-showdown/server/README.md, chunk_1ba53f1706d86463, but it does not replace the simulator’s responsibility.

In the next segment, we will take the same four-box model and stress it with switches and fainting. That is where many simplified turn loops break, because a faint is both an outcome and a generator of new required choices. When a Pokemon faints, the battle may need a replacement request before the next ordinary move choice. So the request system is not only a beginning-of-turn feature. It is also how the simulator pauses at legally necessary decision points. Keep that in mind, because it is the bridge from an ordinary move turn to the more general battle state machine.

Now let’s stress the model with fainting and forced switches.

Imagine a very ordinary turn. Player one chooses Flamethrower, player two chooses Tackle, both choices are committed, the queue orders the actions, Flamethrower resolves first, and the defender faints before it ever gets to Tackle. A beginner implementation often says, “Fine, skip Tackle and start the next turn.” But that is not quite enough. The faint created a new obligation. The fainted side may need to choose a replacement, and that replacement choice is not the same kind of choice as the original move choice. It is a different request state.

So, on the board, I’m going to draw the four boxes again, but I’m adding a loop from outcome back to request. Request, choice, commit, queue, execution, outcome, and then sometimes outcome points not to next turn, but back to request. That little arrow is the key idea for this segment.

In Pokemon Showdown, the Battle class request-state model, in pokemon-showdown/sim/battle.ts, chunk_3b063b1b44a6d638, is important because a battle is not merely “waiting for turn input” or “not waiting.” It can be waiting for specific legal inputs from specific sides. Then the choose and commit flow in pokemon-showdown/sim/battle.ts, chunk_889e310105810281, takes those side choices and advances only when the simulator has enough information to continue. That phrase, “enough information,” is more precise than “both players clicked a button.” In a double battle, in a switch request, after a faint, during team preview, the shape of “enough” changes.

Now compare that to FireRed. FireRed’s battle entry is not exposed as a network API. We enter battle from the overworld through setup code, and the packet points us to pokefirered/src/battle_setup.c, chunk_494d132a59422c85, where battle setup sits among includes for overworld, script, event data, battle transition, and battle headers. That include list is not just noise. It tells us that FireRed battle startup is a handoff from map and script state into a modal battle engine. The overworld does not keep running the creature battle turn loop in parallel as a server room would. The game transitions into battle mode, and the callback-task shell that we studied earlier becomes the runtime surface for that mode.

Check your understanding here. If a Pokemon faints, is the required replacement a move execution result, a player choice, or both? The correct answer is both, but at different levels. It is produced by move execution as an outcome, but it becomes a new player choice requirement before ordinary turn flow can proceed. That dual identity is why fainting is a good test of your architecture.

Let’s take a concrete example. Suppose Charmeleon uses Slash against an opposing Pikachu. Slash lands, damage calculation reduces Pikachu’s HP to zero, and Pikachu faints. In a cartridge-style implementation, the damage routine and animation/message sequence are part of battle execution. But the decision of which party member replaces Pikachu belongs to the trainer-control layer. In Showdown terms, execution has emitted protocol output and altered battle state, and then the battle must produce a request for the affected side. In FireRed terms, battle scripts, controller commands, and party/battler state coordinate to show the faint, update party data, and ask for a switch if the battle continues.

Notice the difference between “the turn loop” and “the battle loop.” The turn loop is the normal cycle of collecting choices and resolving actions. The battle loop is broader. It includes exceptional pauses: forced switches, faint replacement, victory checks, learning moves after experience, item prompts in some contexts, and exit back to the overworld. That distinction is one reason we spent so much time earlier on AgbMain and callback ownership. The main loop mechanism in pokefirered/src/main.c, chunk_b35676322b169f85, and the callback switching described around SetMainCallback2 in pokefirered/src/main.c, chunk_6cfd90f6d0136799, gives FireRed a way to swap the whole active mode of the game. Then the task system, with CreateTask in pokefirered/src/task.c, chunk_cb0bb91a147bfb59, and RunTasks in pokefirered/src/task.c, chunk_3ffebc0341225936, gives a frame-by-frame dispatcher inside that mode. I’m not re-teaching that shell today; I want you to see why it matters here. A battle is not one function call. It is a state machine hosted by the runtime shell.

Now, look back at Showdown’s queue. The action queue in pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e, is where the simulator turns chosen intentions into ordered work. A move choice is not the move itself. It becomes an action object, and the queue can sort it, interleave it with switches, handle priority, and decide what resolves next. That queue is a very explicit architecture. It is easy to test. It is easy to inspect. It is also a useful contrast with FireRed, where the equivalent sequencing is spread across battle state variables, controller callbacks, script commands, and per-frame progression.

Let me say that another way. Showdown externalizes the turn as data moving through an API. FireRed internalizes the turn as state moving through a modal engine. Same conceptual responsibilities, different architectural surfaces.

Now let’s open the move execution box. In Showdown, runMove responsibilities live in pokemon-showdown/sim/battle-actions.ts, chunk_95a7524b39636e24. At our level of abstraction today, runMove is not “subtract HP.” It is the coordinator for a move attempt. It must consider whether the user can act, whether the target is valid, whether the move hits, whether immunity applies, what secondary effects happen, what messages are emitted, and whether the resulting state produces fainting or follow-up events. The data excerpt from pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, is a nice small example of the layered checks inside damage logic: immunity, OHKO behavior, damage callbacks, level-based damage, fixed damage, and then ordinary damage calculation. That is the sort of complexity that should convince you not to design the turn loop as “pick move, call damage formula, done.”

Here’s a board cue. I’m writing “move execution equals policy orchestration.” Under it: legality, target, immunity, power, damage, effects, messages, faint checks. In a data-driven RPG, each of those words can point to a different table, callback, flag, or state variable. If you collapse them into one giant arithmetic function, your clone becomes impossible to extend.

And this is where the course’s larger FireRed objective returns. We are always trying to trace data table to runtime state to visible behavior. In Showdown, move data and mod scripts are loaded through the Dex-facing model we discussed last week. In FireRed, the corresponding source organization is more C-style: headers as contracts, data arrays compiled into the ROM, globals and structs representing active state, and task or callback functions consuming those records. We have already studied generated data and binary embedding, so today the interesting part is not that the data exists. The interesting part is when the engine is allowed to consult that data.

For example, when Showdown describes a move effect such as residual end-of-turn damage in pokemon-showdown/data/text/moves.ts, chunk_92a6298340525a85, the text data is not the executable mechanic by itself. It documents or displays what the mechanic does. The real simulator must still schedule and apply the effect at the right time. In FireRed, the same distinction appears between descriptive UI strings, move tables, battle scripts, and the runtime battle state. Text is not behavior. Tables are not behavior until a state machine reads them.

Let’s do a quick check. If I show you a move description that says, “damages at the end of each turn,” where should you look for the actual architecture? Not only in the text file. You look for the move’s executable effect hook, the residual event timing, the active volatile or status state, and the place in the loop where end-of-turn effects are processed. The description is evidence of intended behavior, but the state machine is the mechanism.

Now, forced switches make this more subtle. Suppose the move does not merely deal damage. Suppose it causes a switch, prevents switching, or modifies the future queue. In Showdown, a move can influence volatile state, side conditions, or future request legality. The example in pokemon-showdown/data/mods/gen9ssb/moves.ts, chunk_f562e1983e9fdeff, shows a custom move-like effect using callbacks such as onTryMove, onPrepareHit, onTry, and a condition object. We do not need that specific modern mechanic for FireRed, but architecturally it is useful because it shows the extensible event-hook style. The simulator can ask the effect object questions at named moments. Can this move be tried? What animation is emitted? Does the field condition block it? What status or volatile state should be attached?

FireRed’s style is less object-callback-heavy and more table-and-script-driven, but the same question remains. At what named moments can data influence execution? Before the move? During target selection? During damage? After damage? At faint? At end of turn? That is exactly how I want you to read battle code for the rest of this week.

Now let’s bring in BattleStream. The BattleStream API in pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59ac, matters because it exposes the simulator boundary as a stream of input and output messages. If you run a minimal exchange, you do not directly poke internal HP fields. You send commands, receive requests and updates, and observe protocol output. That is close to how a client or test harness sees the battle. The server layer, referenced in pokemon-showdown/server/README.md, chunk_1ba53f1706d86463, wraps this with runtime concerns such as running the service locally, rooms, users, and networking. But the battle semantics are still simulator semantics.

This is a good place to contrast with link battles in FireRed without re-teaching link transport. Earlier, we covered RFU mode constants, link manager callback messages, and VBlank-timed link state. Today we only need the bridge. FireRed’s multiplayer battle must synchronize choices across machines, but the cartridge battle engine still owns battle semantics. Transport can deliver or coordinate input, but it should not become the damage formula, the faint logic, or the move effect interpreter. Showdown’s server boundary makes that separation easier to see because the simulator and server are separate modules. FireRed’s boundary is less visually obvious because the code is a single embedded program, but the architectural separation is still there if you trace ownership carefully.

Let’s walk through a minimal BattleStream-style exchange as pseudocode speech. First, the caller creates a battle stream. Then it writes a start command with format and player information. The stream emits an initial request or update. Player one writes a choice, maybe “move 1.” Player two writes a choice, maybe “move 2.” The battle commit flow accepts those choices, the queue orders them, runMove resolves the first action, output is emitted, state is mutated, runMove or the queue resolves the second action if still legal, and then the simulator either emits the next ordinary request or a special request such as a replacement switch. In our lab, you will map each stage to a FireRed concept: setup entry, active battler state, chosen action storage, execution script, HP mutation, faint handling, party replacement, and visible text or animation.

Now, an advanced warning. Do not map Showdown classes one-to-one onto FireRed files. That is the wrong comparison. BattleStream does not mean FireRed has a stream class. BattleQueue does not mean FireRed has a queue file with the same responsibilities. runMove does not mean there is one perfect C function with exactly the same boundary. What transfers is the responsibility map. Request state, choice capture, commit, action ordering, move execution, output, and follow-up request. Those are the boxes. The source layouts differ.

This matters for homework_w23. At this point in the course, I expect you to name both the code artifact and the responsibility. A weak answer says, “The battle code handles moves.” A strong answer says, “The simulator’s choose and commit flow in pokemon-showdown/sim/battle.ts, chunk_889e310105810281, converts side choices into committed battle progress, while the action queue in pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e, orders those committed actions, and battle-actions runMove in chunk_95a7524b39636e24 coordinates execution details. In FireRed, I would look for the equivalent responsibility across battle setup, active battle state, controller flow, battle scripts, and per-frame task or callback progression rather than assuming one direct file match.” That is the standard now.

Let me pause for a comprehension check. Why can’t the server runtime be the authoritative explanation of move order? Because the server runtime can host battles, users, and rooms, but the simulator owns battle semantics. The server can carry commands; it should not decide that Quick Attack beats Tackle or that a fainted Pokemon cannot use its queued move. In Showdown, that distinction is visible in the split between pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59ac, and pokemon-showdown/server/README.md, chunk_1ba53f1706d86463. In FireRed, the same idea is embedded: the link or overworld layer may get you into battle, but battle mode owns battle progression.

Now let’s return to fainting and switches, because this is the stress case. If action A makes Pokemon B faint, what happens to action B? The answer depends on the action type, game rules, and timing. A fainted Pokemon usually cannot execute its ordinary move. But some queued actions may still exist as artifacts that must be skipped, cancelled, or transformed into messages. A robust queue system can represent that cleanly: the action comes up, the engine checks whether it is still valid, and if not, it does not execute as originally planned. In a more implicit state machine, the same check still has to happen somewhere. There is no escaping the rule; only moving where the rule is enforced.

This is also why tests are so valuable. Pokemon Showdown’s architecture lends itself to deterministic battle tests because you can feed choices and inspect outputs. FireRed’s original environment is less test-harness-friendly, but for our architecture course, the same discipline applies. When you design your minimal clone, don’t just test “move deals damage.” Test “faster move KOs slower user before slower action.” Test “faint creates replacement request.” Test “replacement request is not treated as a normal move request.” Test “battle ends instead of requesting replacement when the last party member faints.” These tests target the state machine, not just the arithmetic.

A small recap before we move on. We began with four boxes: request, choice, commit, queue. We expanded that into execution and outcome. Then fainting forced us to add a loop from outcome back to request. Showdown makes these responsibilities visible through Battle state and request modeling in pokemon-showdown/sim/battle.ts, chunks chunk_3b063b1b44a6d638 and chunk_889e310105810281, action ordering in pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e, move execution in pokemon-showdown/sim/battle-actions.ts, chunk_95a7524b39636e24, and stream interaction in pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59ac. FireRed gives us the same architectural problem inside a cartridge runtime, entered through setup code such as pokefirered/src/battle_setup.c, chunk_494d132a59422c85, and hosted by the callback and task machinery we studied earlier.

So as we continue, keep this sentence in your notes: a turn loop is not complete until it can pause for every legally required decision created by its own outcomes. That sentence is the bridge to the next part, where we will look more closely at replacement decisions, end-of-turn effects, and why “commit choices” must be separated from “execute actions” if you want a battle engine that survives real Pokemon rules.

Now, replacement decisions are where a lot of simplified battle engines reveal that they are not really battle engines yet. A toy turn loop says, “both players pick a move, sort by priority and speed, execute, apply damage, next turn.” That is fine for a classroom sketch in week one. It is not fine once a faint happens in the middle of a turn, once a switch is forced, once a move creates a trapping effect, or once an end-of-turn status creates another faint after the visible attacks are over.

So let me put a small timeline on the board. Imagine player one’s active creature uses a damaging move. The target faints. In a single-player cartridge battle, the game cannot simply continue as though nothing happened. It has to ask, “does the opponent have a replacement?” If yes, the battle has to enter a replacement-selection state. In FireRed, that pause is not an abstract promise. It must be represented in menus, party-screen state, battle controller communication, and ultimately globals and callback/task-driven control flow. In Showdown, the equivalent idea is much more explicit as request state. That is why we keep returning to pokemon-showdown/sim/battle.ts, chunk chunk_3b063b1b44a6d638, for the Battle class and request-state model, and pokemon-showdown/sim/battle.ts, chunk chunk_889e310105810281, for the choose and commitChoices flow. The simulator is almost saying out loud what the cartridge encodes through state machines.

And notice the phrase I used: “enter a replacement-selection state.” Not “call a function that picks the next creature.” That distinction matters. A replacement selection may be controlled by the AI, by a local human player, by a remote player over a link, or by a server-side protocol connection. The battle engine should not care too early which of those is true. It should produce the requirement for a decision and then wait until the appropriate controller supplies that decision. We have seen this same shape before in FireRed’s broader runtime. The callback shell and task system let the game say, “this is what we are doing now,” then return control frame after frame until input, animation, script, or communication has advanced enough. We are not re-teaching RFU or the link manager today, but you should feel the reuse of that prior foundation: link state, callback messages, and VBlank-safe communication all make sense only if the game can pause without losing its place.

Let us make the Showdown version concrete. Suppose Showdown has reached a point where one side must switch because its active Pokemon has fainted. The simulator does not want to resolve the next full turn until that replacement exists. It produces a request. The client sees the request and sends back a choice. That choice goes through the choose path. Then, only when the relevant choices are available and legal, commitChoices can move the system from “collecting decisions” to “resolving actions.” That separation is not bureaucracy. It is the protection that keeps the battle state coherent. If you let execution begin while one side is still deciding, you have leaked future information, broken simultaneity, or made the server dependent on client timing.

Here is the check for understanding. If both players choose moves for the turn, and then the faster move knocks out the slower Pokemon before it acts, what happens to the slower Pokemon’s previously committed move? Think it through before I answer. It was a legal choice when selected. It was committed into the action queue. But by the time its action slot arrives, the actor may no longer be able to act. So the action queue does not mean “all committed choices must produce their original visible effect.” It means “these are the pending action intentions, and each one must be validated against the current battle state when its turn to resolve arrives.” That is exactly why pokemon-showdown/sim/battle-queue.ts, chunk chunk_f572d84e556bce5e, is central today. A queue in a Pokemon battle is not merely a sorted list; it is a list of actions whose execution conditions may be invalidated by earlier actions.

Now, let us connect that to FireRed. FireRed is not exposing a BattleStream API to a client process. It is running on a GBA, inside a game loop. The battle entry boundary includes setup code such as pokefirered/src/battle_setup.c, chunk chunk_494d132a59422c85. That source chunk is useful because it shows battle setup living at an intersection of task, overworld, script, transition, and battle includes. In other words, a FireRed battle does not begin as a pure function call from “the RPG” into “the battle module.” It begins as a handoff from the overworld and event systems into a new modal runtime, with transitions, flags, music, trainer data, encounter context, and callbacks all involved. Once you accept that, you stop looking for one master “turn loop” that reads like a textbook. You start looking for distributed state: active battlers, controller commands, menu selections, script buffers, battle outcomes, animation waits, and callback steps.

This is a recurring theme of the course. In earlier weeks, I asked you to trace visible behavior from data table to runtime globals to callback or task to player-visible output. That same discipline applies here. In Showdown, the path is often easier to name: Dex data feeds an active battle object, the battle object builds requests, choices become queued actions, queued actions invoke move execution, and protocol output is emitted. In FireRed, the path is more embedded: species, move, item, and trainer data feed battle setup and battler state; battle controller logic asks for choices; battle scripts and commands resolve effects; and the display system renders the result. Different surface, same architectural burden.

Let me pause and write a phrase under the timeline: “commit is not execution.” I want that in your notes because it is the mistake that produces many broken clones. A player’s move choice is a promise about intent. It is not a guarantee that damage will happen. It is not even a guarantee that the selected move will be the move that ultimately occurs, because rules can redirect, disable, fail, call another move, lock a user into a move, or replace the choice with something else under special effects. The execution phase has to consult the current state, not the stale state from the choice menu.

Showdown makes this especially visible in the responsibilities around runMove, cited in pokemon-showdown/sim/battle-actions.ts, chunk chunk_95a7524b39636e24. Do not think of runMove as “subtract HP.” That is much too small. A real move execution path has to handle target validation, immunity, accuracy, pressure from statuses, volatile effects, move flags, event hooks, damage calculation, secondary effects, protocol messages, and post-move consequences. Some of those responsibilities are attached to the move’s data. Some are attached to the attacker. Some are attached to the defender. Some are attached to field state. Some are attached to the generation’s rules. We already used Dex and generation mods as prior foundation, so I will not re-teach that lookup layer now. But this is where that layer pays rent: runMove is not hardcoding every move; it is orchestrating data-driven and hook-driven behavior under a deterministic turn context.

A FireRed comparison helps here. In the cartridge codebase, move effects are also not merely “base power times attack over defense.” The engine has battle scripts, constants, tables, and state variables that together implement the behavior. FireRed’s architecture often uses compact data and command-oriented scripts because it is running under cartridge constraints and a frame-based display loop. Showdown, by contrast, can represent many effects as TypeScript objects, callbacks, and simulator events. If you are designing your own creature RPG, neither representation is automatically superior. What matters is that your move execution layer has a place for generic rules, move-specific rules, state mutation, failure messages, and deferred consequences.

Let us use a concrete example. I am going to keep the move generic so we do not spend ten minutes on one named edge case. Suppose a move deals damage and also applies a residual condition that damages the target at the end of each turn. The immediate action phase applies the move. But the full consequence of the move is not finished when damage appears. A condition has been installed into battle state. Later, the end-of-turn phase must discover that condition, apply residual damage, possibly produce a faint, possibly request a replacement, and only then allow the next request cycle. That means the end-of-turn phase itself can create decisions. This is why the sentence from the previous part is so important: a turn loop is not complete until it can pause for every legally required decision created by its own outcomes.

You can see the data side of that idea in Showdown’s move text and effect descriptions as well. For example, pokemon-showdown/data/text/moves.ts, chunk chunk_92a6298340525a85, includes descriptions for effects that damage foes at the end of each turn. We are not using that text file as the simulator logic, but it is a useful reminder that the player-facing rule is explicitly turn-structured: something happens after the main move, across multiple turns, and may happen on the last turn as well. Any engine that treats a turn as only “pick move, deal damage” has nowhere to put that rule cleanly.

Now I want to ask another check question. Where should residual damage live: inside the original move action, or inside a later residual phase? The answer is, the original move action may install or modify the condition, but the later residual phase should apply the recurring end-of-turn damage. If you keep applying all future damage inside the original move action, you are not simulating state. You are predicting a future that may be interrupted by switching, fainting, immunity changes, ability changes, item changes, or field changes. Advanced battle systems are full of future-sensitive effects. So we store state and revisit it at the correct phase.

This phase structure also explains why server boundaries matter. Showdown’s BattleStream, in pokemon-showdown/sim/battle-stream.ts, chunk chunk_ced1577d9e2d59ac, gives us a boundary where input and output can be exchanged through a stream-like API. The battle can emit protocol output. The caller can send player choices. That is fundamentally different from FireRed’s in-process UI loop, but the architectural categories are comparable. FireRed’s “client” is not a browser connected to a server. It is the local battle UI, the battle controller, and sometimes link communication. Showdown externalizes the boundary into protocol messages. FireRed internalizes it into callbacks, tasks, controller buffers, and hardware-facing display/audio/input systems.

And this is where pokemon-showdown/server/README.md, chunk chunk_1ba53f1706d86463, enters our week. The server runtime can be started and reached through localhost behavior, according to that README chunk. For us, the important architecture point is not “how do I host a public server?” The point is that Showdown’s simulator can sit behind an online runtime. The server boundary has to preserve the same invariants as the local simulator: collect legal choices, commit them at the right time, resolve the queue deterministically, and emit messages that let clients display the battle. FireRed has no Node server in the middle, but it has a different boundary: the battle engine must cooperate with the GBA’s main loop, with local input, and with link timing when multiplayer is involved.

Let me draw two columns now. Left column: Showdown. Right column: FireRed. In the Showdown column, I write “Battle request state,” “choose,” “commitChoices,” “battle queue,” “runMove,” “BattleStream output.” In the FireRed column, I write “battle setup,” “battle controller choice state,” “battle scripts and commands,” “per-frame callback/task hosting,” “battle display and result state.” The exact function names do not line up one to one, and I do not want you forcing them. The mapping is architectural, not lexical. If you are comparing codebases, the correct question is not “what is FireRed’s commitChoices function?” The correct question is “where does FireRed separate choice collection from effect resolution, and how does that separation survive animation waits, input waits, and link waits?”

That distinction will matter in the lab. When you pseudocode a minimal BattleStream exchange, you should not just write “p1 move tackle, p2 move tackle, output damage.” You should label every boundary. First, initialization creates battle state. Second, the battle emits a request. Third, each player supplies a choice. Fourth, the simulator validates and commits those choices. Fifth, queued actions resolve. Sixth, move execution mutates state and emits protocol output. Seventh, residual or end-of-turn effects run. Eighth, if a decision is required, the battle emits another request instead of blindly advancing. That eighth step is where many designs fail.

Now let us connect to the course plan, because week 23 is not a random excursion into another project. In weeks 1 through 22, we built FireRed as the primary architecture: main loop, callbacks, tasks, map loading, event scripts, party data, battle entry, item systems, save persistence, and link boundaries. The recent homework and quiz asked you to trace gameplay from data records through globals, callbacks, tasks, scripts, structs, and concrete source functions. That was not busywork. It prepared you for this comparison. Showdown is useful now because it makes certain battle-engine concepts explicit that FireRed necessarily distributes across a cartridge-oriented runtime. But FireRed remains the anchor. Showdown is the comparison lens.

Let me make that sharper. If you copy Showdown’s structure directly into a GBA-style engine, you may produce something inappropriate for the platform. If you ignore Showdown, you may miss clean abstractions around requests, choices, queues, and stream output. The advanced move is to extract invariants, not to copy surface form. The invariant is that a battle engine must maintain a legal state between every visible step. The invariant is that player decisions must be requested only when the rules require them. The invariant is that committed choices become queued intentions, not guaranteed outcomes. The invariant is that move execution must be responsible for more than damage. The invariant is that outputs should describe state changes in an order clients can display.

Here is another example. Suppose both players choose to switch. In a naive engine, a switch is just a change of active creature before moves. But in real Pokemon-like rules, switch actions have ordering, effects may trigger on leaving or entering, hazards or entry effects may apply, abilities may announce themselves, and those effects may cause damage or even fainting. If an entering creature faints due to an entry effect, the engine must request another replacement before the normal move phase continues, if there is any normal move phase left to continue. This sounds unusual, but architecture is judged by edge cases. An engine that handles only the sunny path is not a battle engine; it is a demo.

Now, I am going to point back to runMove one more time. In Showdown, pokemon-showdown/sim/battle-actions.ts, chunk chunk_95a7524b39636e24, represents the kind of code location where move execution responsibilities gather. But runMove is still not the whole battle. It is called within a broader queue and phase system. This is a good design lesson: do not put the entire turn engine inside your move function. The move function needs rich authority over move effects, but it should not own choice collection, server input, replacement prompts, full turn scheduling, and all display protocol by itself. When a function becomes “the place where everything happens,” you lose the ability to pause at legal boundaries.

FireRed’s older architecture teaches a complementary lesson. Because FireRed lives in a per-frame runtime, it is naturally forced to break work into states. Animations take frames. Menus take frames. Palette fades take frames. Link communication takes frames. So the engine cannot just sit in a blocking while loop until everything is done. That is why our earlier work on AgbMain, main callbacks, SetMainCallback2, CreateTask, and RunTasks matters even here, without rehashing those lectures. The physical runtime encourages state-machine design. Showdown’s server and stream model encourages request-response design. Both are different solutions to the same need: the battle must be interruptible at well-defined boundaries while still being deterministic.

Let me give you a small mental model that I use when reading these systems. Every battle turn has three layers. The first layer is decision state: what information does each side need, and what legal choices can they make? The second layer is scheduling state: once decisions are known, what actions exist, and in what order should they be attempted? The third layer is execution state: when an action is attempted, what actually happens under the current rules? Request state belongs mostly to the first layer. The battle queue belongs mostly to the second. runMove belongs mostly to the third. Residual effects and replacement decisions cross layers, because execution creates new decision requirements. That crossing is where the design must be strongest.

Check your understanding with this scenario. A player selects a move. Before that move’s action resolves, the user is affected by a condition that prevents action. Which layer catches the issue? The choice layer allowed the selection because it was legal at the time. The queue layer still contains an action intention. The execution layer, when the action is reached, must check whether the actor can proceed. So if your design only validates at choice time, it is incomplete. You need validation at choice time for user feedback and legality, and validation at execution time for dynamic battle truth.

Now, for FireRed source identification, because that is one of our course objectives, you should be able to say the following without hesitation by the end of this week. Battle entry and setup are associated with files such as pokefirered/src/battle_setup.c, chunk chunk_494d132a59422c85, where the include list itself tells you the battle does not stand alone; it touches task, overworld, item, script, event data, transition, and battle modules. The callback and task machinery from pokefirered/src/main.c and pokefirered/src/task.c, which we studied earlier through the cited chunks in the course packet, hosts modal gameplay work across frames. Creature and move data flow into the battle engine through data tables and structs, not through ad hoc per-battle inventions. And when we compare to Showdown, we identify pokemon-showdown/sim/battle.ts chunks chunk_3b063b1b44a6d638 and chunk_889e310105810281 for request and choice flow, pokemon-showdown/sim/battle-queue.ts chunk chunk_f572d84e556bce5e for scheduling, pokemon-showdown/sim/battle-actions.ts chunk chunk_95a7524b39636e24 for move execution responsibilities, and pokemon-showdown/sim/battle-stream.ts chunk chunk_ced1577d9e2d59ac for API boundary behavior.

Let me synthesize this portion before we move into the next one. The hard part of a Pokemon-like turn loop is not sorting two moves by speed. The hard part is preserving legality and determinism while the battle repeatedly creates new obligations: choose a move, choose a switch, replace a fainted active, apply residual effects, show messages in order, wait for animation, and then produce the next request. Showdown names many of those obligations through request state, choose, commitChoices, queues, runMove, and streams. FireRed implements the same kind of obligations through cartridge-shaped machinery: setup handoffs, callbacks, tasks, controller states, battle scripts, tables, and globals. So when you build or analyze a battle engine, do not ask only, “where is the loop?” Ask, “where can the loop stop, what state proves it is safe to stop there, and what exact decision or action is allowed to resume it?” That is the question we will carry into the next segment, where we will look more closely at protocol output and why the order of messages is itself part of the battle engine’s contract.

Now, protocol output. Let me slow down here, because this is one of those places where a simulator looks like it is merely printing text, but architecturally it is doing something much stronger. It is publishing an ordered history of battle facts. That ordered history is what the client animates, what a replay stores, what a test can compare, and what a server can send across a network boundary. So, if we say, “Charmander used Scratch, Bulbasaur lost HP, Bulbasaur fainted,” the order is not decoration. The order is the contract.

Put that on the board: battle result equals state mutation plus ordered observation. Not just mutation. If the HP number changes internally but the output stream announces it late, early, or with the wrong cause, then the player-facing battle is wrong even if the final HP total is numerically correct.

Showdown makes this unusually explicit because the battle engine has a stream boundary. The week’s source focus names that directly in pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59ac. BattleStream is not the move-effect engine itself; it is the API-shaped membrane around the engine. You write input into it, you read protocol output from it, and the rest of the runtime can treat the battle as a process that communicates through messages. That is very different in presentation from FireRed, where the battle is embedded inside the GBA runtime and coordinated through callbacks, tasks, controller state, and battle scripts. But conceptually, both systems must answer the same question: after the player commits an action, what observable events are emitted, and in what order?

Let’s use a concrete example. Suppose player one chooses Ember and player two chooses Tackle. Ember goes first. Ember deals damage. It also has a chance to burn. Then Tackle may or may not happen depending on whether the target fainted, whether paralysis applied, and so on. In Showdown, the engine cannot simply jump to the end and say, “After the turn, Pokémon A has this much HP and Pokémon B has this status.” It must emit a protocol sequence: move declaration, animation or move line, damage line, status line if burn occurs, faint line if faint occurs, then perhaps the opposing action, then residual effects, then turn transition and next request. The exact vocabulary is Showdown-specific, but the responsibility is general.

In FireRed, the same facts are distributed differently. Some of the visible ordering is driven by battle scripts and controller commands. The battle engine updates globals and buffers, dispatches commands to the active battler controllers, waits for animations or text boxes to complete, and only then proceeds. We have already studied the callback and task shell, so I am not going to re-teach RunTasks or SetMainCallback2 today, but remember the prior foundation: pokefirered/src/main.c, chunk_6cfd90f6d0136799, and pokefirered/src/task.c, chunk_3ffebc0341225936, show the kind of frame-by-frame dispatch machinery that lets FireRed stop in the middle of a larger logical operation. That stopping ability is essential for message order, because a handheld battle is not just calculation. It is calculation paced through graphics, sound, input locks, and text.

Check for understanding: if two implementations produce the same winner, same remaining HP, and same final statuses, are they necessarily equivalent battle engines? No. They may be equivalent at the level of final state, but not equivalent at the level of battle trace. For a game, for replay, for debugging, and for competitive verification, the trace matters. The ordered event log is part of correctness.

Now, this is where Showdown’s architecture gives us a very useful comparison tool for FireRed. In Showdown, the stream is a first-class abstraction. The server can run the simulator and communicate with it in a line-oriented way. The week context points us to pokemon-showdown/server/README.md, chunk_1ba53f1706d86463, for the server runtime angle: the server can be started locally and then used as an online runtime around the simulator. The server is not the same thing as the turn engine. That separation is important. The simulator owns battle legality, request generation, choice commitment, queue resolution, move running, damage, and protocol output. The server owns users, rooms, networking, command routing, and persistence around that experience.

Compare that with FireRed. FireRed does not have a WebSocket server sitting outside the battle engine. Its “runtime boundary” is the cartridge runtime itself: the main callback, the link subsystem when multiplayer is involved, the task list, save data, and the transition from overworld to battle. The packet gives us pokefirered/src/battle_setup.c, chunk_494d132a59422c85, as a good anchor for that setup boundary. Notice the includes there: battle setup reaches into overworld, script, transition, Pokémon data, event data, sound, and battle-related headers. That include list is a map of responsibility. Battle entry is not just “call battle.” It is a negotiated handoff from field state into battle state.

Let me put a simple diagram on the board in words. On the left: Showdown server. Arrow to BattleStream. Arrow to Battle object. Arrow to queue. Arrow to runMove and effect handlers. Arrow back outward as protocol messages. On the right: FireRed overworld and script runtime. Arrow to battle setup. Arrow to battle main callback and tasks. Arrow to battler controllers and battle scripts. Arrow to graphics, text, sound, and state mutation. The arrows are not identical, but the architecture rhyme is strong. Both systems isolate battle progression behind a boundary. Both systems require an input shape. Both systems produce ordered output. Both systems have a point where the next request is created only after the current obligations are exhausted.

This is also why the action queue deserves more respect than it usually gets. Beginners often think the queue is merely “sort moves by speed.” At our level, that is far too shallow. The queue is the structure that turns simultaneous intent into sequential obligations. The week source focus names pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e, for this reason. Choices arrive from players as commitments, but they cannot all execute at the same instant. The queue gives the engine a place to represent “this will happen later, unless something before it invalidates or changes it.” That “unless” is doing a huge amount of work.

Imagine a double battle. Four active Pokémon may have actions. One may switch. One may use Fake Out. One may faint before its queued move arrives. One may be forced out. One move may create weather, and a later move in the same turn now runs under that weather. If your engine stores only “player choices,” you do not yet have a turn. You have intentions. The queue is where intentions become scheduled actions. Then runMove, anchored for us this week in pokemon-showdown/sim/battle-actions.ts, chunk_95a7524b39636e24, is where one of those scheduled actions becomes an evaluated move with all the unpleasant real-world detail: target validation, failure checks, PP or move state, immunity, damage calculation hooks, secondary effects, faint checks, event callbacks, and protocol output.

We saw a small piece of that kind of damage responsibility in the research packet’s excerpt from pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f. That chunk describes a damage path that checks immunity, handles OHKO moves, damage callbacks, level-based damage, fixed damage, and then continues into calculation. The point is not that today’s lecture is about Generation 2 Stadium. The point is that Showdown’s simulator architecture repeatedly treats move execution as a layered evaluation pipeline. A move is not one arithmetic formula. It is a sequence of gates and hooks, and each gate may emit observations or stop later work.

FireRed has the same design pressure, but its mechanisms are cartridge-native. It uses data tables for moves, species, items, and effects; global battle state for battlers, HP, statuses, selected moves, side conditions, and controller state; battle scripts for many effect sequences; and task or callback pacing for presentation. So when you map Showdown’s runMove to FireRed, do not look for a single perfect twin function and get frustrated when you cannot find it. The equivalent responsibility is spread across battle command execution, battle script interpretation, damage calculation, animation commands, message buffers, and battler controller synchronization.

Let’s do a quick verbal trace. Player chooses a damaging move. In Showdown, choice text or structured choice enters through the choose path, tied this week to pokemon-showdown/sim/battle.ts, chunk_889e310105810281, which covers choose and commitChoices flow. Once both sides are ready, the battle commits choices, queues actions, resolves them, calls move execution, mutates state, emits protocol, then generates the next request. In FireRed, player input is captured through the battle UI and controller flow. The chosen move index and target become battle state. The engine advances through its own command and script machinery. Damage is computed, HP is updated, animations and messages are requested, and the engine waits until those controller obligations complete before continuing. Different vocabulary, same architectural burden.

Here is the check I want you to be able to perform in your own code reading. When you see a function that mutates HP, ask: who is responsible for telling the player? When you see a function that prints or emits damage, ask: who has already committed the HP mutation? When you see a queue entry, ask: what can invalidate this before it executes? And when you see a request object, ask: what exact state is the battle waiting for?

Those four questions are stronger than memorizing function names.

Now, protocol output also helps explain why Showdown is so useful for verification in weeks 23 and 24, but not a replacement for FireRed. Showdown can be tested by feeding input and comparing output traces. The simulator can be run as a deterministic process. That is excellent for regression tests. FireRed can also be tested, but its battle presentation is entangled with frame timing, VRAM updates, audio, input state, and controller synchronization. So our comparison is not “Showdown is cleaner, FireRed is messier.” That would be lazy. The correct comparison is that each engine is optimized for a different runtime boundary. Showdown’s boundary is stream and server. FireRed’s boundary is handheld frame loop and cartridge state.

This distinction also clarifies the role of data text. The research packet includes pokemon-showdown/data/text/moves.ts, chunk_92a6298340525a85, describing move text for effects like residual damage over turns. That file is not the turn loop, but the turn loop depends on effect text being available so a protocol message can become a readable client event. FireRed similarly separates a great deal of user-facing text and effect behavior from the low-level loop. The battle engine does not become simpler because text is in a table. It becomes more inspectable: a move effect can be connected to text, animation, status, damage, and turn timing through explicit records and state.

Let’s pause and test the model with a small scenario. Suppose a trapping effect applies damage at the end of each turn. Where does that belong? Not in the player’s choice, because the player does not re-choose it each turn. Not in initial move damage only, because it happens later. Not in the server room, because it is battle logic. It belongs in residual turn processing, driven by volatile or side-condition state, and it must emit ordered output after the main actions but before the next request. That is true in Showdown. It is also true in FireRed, although the names and storage locations differ. The important part is the phase boundary: main action resolution first, residual obligations next, then request generation.

If you are following, you should feel why “next request” is a major concept. A request is not merely asking the user for input. It is a proof that the engine has reached a stable stopping point. In Showdown, the Battle class and request-state model, named in the week source focus as pokemon-showdown/sim/battle.ts, chunk_3b063b1b44a6d638, gives us language for that stable stop. A side gets a request when the engine knows what kind of decision is currently legal: move, switch, team preview, forced replacement, or no choice. In FireRed, there may not be a JSON-like request object, but the same proof exists in state variables and UI mode. The fight menu appears only when the battle controller and engine agree that player input is legal. A forced switch prompt appears only when the current state requires replacement. The visible menu is the player-facing expression of an internal request state.

Now, a warning. Advanced students sometimes over-map these systems. They try to say, “This Showdown object equals this FireRed struct,” and sometimes that works, but often it produces a false equivalence. Better is to map responsibilities. What stores public battle state? What stores private player choice? What schedules delayed actions? What validates a selected action? What runs a move? What emits output? What blocks until presentation completes? What creates the next input request? FireRed and Showdown answer those questions with different implementation materials, but the questions themselves are stable.

That is also how this week connects back to the larger course plan. In earlier weeks, we learned to trace FireRed from data table to runtime global to callback or task to visible behavior. Week 22’s homework and quiz asked you to do that across source functions and structs, not just conceptually. This week raises the bar: you now have to trace a turn as a conversation between input, scheduling, execution, and output. That is harder than tracing a static species record or a map header, because a battle turn is not a single lookup. It is an interruptible protocol.

And yes, our prior multiplayer material still matters here, but only as foundation. We already covered RFU modes, link manager callback messages, and VBlank-linked state in detail. Today we just carry that forward: once a battle is linked or online, the engine’s stopping points become synchronization points. If both players must commit before the queue resolves, then request state and commit state are not UI trivia. They are network correctness. Showdown’s server boundary makes this obvious. FireRed’s link boundary makes it hardware-specific. Same obligation: do not let one side observe or act on a future state before the shared turn state is valid.

One more concrete example before we wrap this segment. Say both players choose moves, but one Pokémon faints from the first move. In a poorly designed loop, the second queued move might still execute because it was already chosen. In a slightly better loop, the engine checks fainted status before executing. In a mature loop, the engine also emits the faint message at the correct time, suppresses invalid action execution, creates the correct replacement request, blocks ordinary move selection until replacement is complete, and only resumes the turn or next turn when that forced decision is satisfied. That is the real anatomy we care about. Correctness is not just “do not let fainted Pokémon attack.” Correctness is “move from one legal stopping point to the next, with all required observations in between.”

So, as we move into the next part, keep the phrase “ordered observation” in mind. Showdown’s BattleStream gives us a clean way to see that order because the battle speaks through protocol lines. FireRed hides the same obligation inside its battle scripts, buffers, controller commands, callbacks, and frame loop. The architecture lesson is that every serious creature RPG battle engine has to separate choice capture, choice commitment, action scheduling, action execution, presentation output, and next-request generation. The names vary. The obligation does not.

Now put that phrase, ordered observation, beside a second phrase: server boundary. I’ll say it slowly because this is where many battle-engine designs become muddled. A battle simulator is not automatically a server. A battle server is not automatically a simulator. They can live in the same repository, and they can call each other every few milliseconds, but architecturally they answer different questions.

The simulator asks, “Given this battle state and these legal choices, what happens next?” The server asks, “Who is connected, who is allowed to send input, where should output go, and how do I isolate this battle from the rest of the runtime?” In Showdown, BattleStream is one of the clean dividing surfaces for that conversation. The source packet points us to pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59, for the BattleStream API, and to pokemon-showdown/server/README.md, chunk_1ba53f1706d86463, for the server-side run and localhost behavior. That pairing is not accidental. One file is about talking to a battle instance as a stream. The other is about running a broader online application environment.

In FireRed, the same separation is less visibly named, but it is still there. The cartridge does not run a web server, obviously, and it does not have a text protocol called BattleStream. But it absolutely separates the battle’s internal state evolution from the outer runtime shell. We have already studied that outer shell through AgbMain in pokefirered/src/main.c, chunk_b35676322b169f85, SetMainCallback2 in pokefirered/src/main.c, chunk_6cfd90f6d0136799, CreateTask in pokefirered/src/task.c, chunk_cb0bb91a147bfb59, and RunTasks in pokefirered/src/task.c, chunk_3ffebc0341225936. Those are not battle rules. They are scheduling and dispatch surfaces. They are how the rest of the game lets a subsystem take over time.

So, if I draw this on the board, I am drawing two nested boxes. Outer box: runtime host. Inner box: battle simulator. For Showdown, the outer box may be the server process, room logic, users, sockets, and command handling. The inner box is the Battle object receiving choices, committing them, resolving the action queue, calling runMove, and emitting protocol output. For FireRed, the outer box is the GBA main loop, callbacks, tasks, VBlank-sensitive update order, and whatever field or link state handed us into battle. The inner box is battle state, battlers, chosen actions, scripts, buffers, controller commands, animation and text progression, damage, fainting, replacement, and return-to-field conditions.

Now, here is the important advanced point. You cannot compare Showdown’s BattleStream to FireRed’s link system one-to-one. That would be too crude. BattleStream is not “the link cable.” It is a disciplined interface into the simulator. FireRed’s link machinery, which we covered in the RFU lectures, is a communication system around synchronization and remote intent. We are not re-teaching RFU mode constants today, but we are using that prior foundation. The closer comparison is this: BattleStream exposes a serializable boundary around battle input and output, while FireRed’s battle engine exposes a frame-scheduled boundary around callbacks, global battle variables, controller buffers, and battle scripts. Both boundaries constrain who may talk to the battle and when the battle may answer.

Let’s make that concrete. Suppose in Showdown we have a minimal single battle, and player one sends something like a move choice while player two sends a move choice. I am not asking you to memorize exact protocol syntax today; I am asking you to track the architectural sequence. The Battle class maintains request state, which our packet associates with pokemon-showdown/sim/battle.ts, chunk_3b063b1b44a6d638. The choose and commit flow is associated with pokemon-showdown/sim/battle.ts, chunk_889e310105810281. The battle does not resolve just because the first player typed “move 1.” It records a pending choice. It checks whether the choice is legal for the current request. It waits for all required choices. Then commitChoices closes that capture phase and allows scheduling.

That scheduling phase is where pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e, enters the story. The queue is not a decorative list. It is the engine’s promise that effects resolve in a determinate order. Switches, moves, residuals, forced decisions, and special events must not be casually interleaved just because the code path happened to get there. Then, when a move action is actually processed, pokemon-showdown/sim/battle-actions.ts, chunk_95a7524b39636e24, is the source focus for runMove responsibilities. And runMove is not merely “subtract HP.” It is the move execution envelope: can the user act, what move is active, what target is resolved, what events fire before hit, what happens on immunity, how damage or secondary effects are applied, and what output is emitted.

Now check yourself on this before I continue. If a student says, “Showdown’s turn loop is choose, damage, next turn,” what is missing? Say it in your head. Good. Missing are request generation, legal choice validation, commitment, queue construction, ordered action resolution, event hooks, presentation output, faint and replacement decisions, and then the next request. That is not pedantry. Those missing pieces are where battle engines usually break.

Let’s now map the same idea back to FireRed, using the language of the course. FireRed has data tables that describe species, moves, items, trainers, and battle resources. We have treated the Dex comparison as prior foundation, so I will not re-teach generation mods or move lookup. What matters today is that once battle begins, static data is pulled into a stateful runtime. The transition into battle is not magic. The source packet points us to pokefirered/src/battle_setup.c, chunk_494d132a59422c85, which includes battle-related headers such as battle.h, battle_transition.h, battle_controllers.h, and battle_message.h among a broader list of field, script, sound, item, and overworld includes. That include cluster tells a story. Battle setup is a boundary-crossing module. It knows enough about overworld state, scripts, map events, items, transitions, and battle headers to move the player from field play into a battle context.

Please notice how different that is from Showdown’s online boundary. In Showdown, the server can create or route a battle room, then talk through stream-like input and output. In FireRed, the field runtime hands control to battle by changing callbacks, configuring globals, preparing transitions, and letting battle-specific code consume frames. Same architecture role, different hardware assumptions.

If I write “request” on the board under Showdown, what do I write under FireRed? I might write “input menu state,” “controller command,” or “battle script wait point,” depending on which layer we are inspecting. FireRed does not always have a single object named Request that resembles Showdown’s request model. But it has request-like moments. The battle engine reaches a state where it cannot continue resolving battle logic until the player chooses Fight, Bag, Pokémon, Run, or a move target, or until an animation or message has finished, or until a remote link player’s input has arrived. Those are stopping points. The lesson from Showdown helps us name them more cleanly.

And that is the benefit of using Showdown as our comparison in week 23. We are not replacing FireRed as the architecture source. Your homework and quiz sequence has already enforced that. Week 22 asked you to trace FireRed gameplay from data records through globals, callbacks, tasks, scripts, structs, and concrete source functions. Week 23 now asks you to look at a modern simulator and say, “Ah, this is the same separation, made explicit.” Showdown gives us cleaner nouns. FireRed gives us the embedded-system reality.

Let’s walk a hypothetical turn carefully, but I want you to hear both engines at once.

Start with the battle waiting for choices. In Showdown, the Battle class’s request-state model, again pokemon-showdown/sim/battle.ts, chunk_3b063b1b44a6d638, says, in effect, each side has an outstanding obligation. That obligation is serialized into output so a client knows what it can choose. In FireRed, the equivalent player-facing state is built through battle UI and controller logic. The game draws a menu, receives button input through the frame loop, and stores the selected action in battle state. The player does not see a JSON request. The player sees a Fight menu. But both are constrained views over the same deeper idea: the engine is exposing only legal decisions for the current stopping point.

Now the player chooses. In Showdown, choose and commitChoices, pokemon-showdown/sim/battle.ts, chunk_889e310105810281, make the distinction between “I received a choice” and “the full battle state may advance.” That distinction is essential in multiplayer because one side may choose before the other. It is also essential in AI or test contexts because you may be feeding choices programmatically. In FireRed, an ordinary single-player wild battle may feel immediate because the local player chooses and the enemy choice can be produced by AI logic without waiting for a network peer. But link battles bring back the same issue: the battle cannot honestly resolve until the necessary remote input has been synchronized. We already covered link callback messages and VBlank timers, so I will not reopen that machinery; I just want you to see why prior link architecture becomes relevant here.

Then comes commitment. A committed choice is a point of no casual return. In a well-designed battle engine, once choices are committed, the engine schedules actions based on priority, speed, special conditions, forced switches, pursuit-like exceptions in generations that have them, and all the rule-specific ordering details. Showdown’s battle queue, pokemon-showdown/sim/battle-queue.ts, chunk_f572d84e556bce5e, gives this role a dedicated abstraction. FireRed tends to distribute similar responsibility across battle state variables, action selection arrays, turn-order routines, and script-driven execution. The shape differs, but the obligation is the same: once commitment happens, ordering must be reproducible.

Here is a concrete example. Imagine two active creatures. One uses a priority move. The other uses a normal damaging move. A naïve engine might say, “Just compare speed.” That engine is already wrong. A less naïve engine might say, “Sort by priority, then speed.” Better, but still incomplete. What about paralysis speed modification? What about items or abilities in later generations? What about move failure due to status? What about target fainting before the second action? What about the fact that some effects produce messages before damage, and some produce messages after? Showdown’s queue plus runMove envelope handles this by separating action order from move internals. FireRed’s battle scripts and state machines handle the same problem through a different style: a sequence of command execution, message display, animation, healthbox updates, faint checks, and next script pointer or state.

Now, when I say “runMove envelope,” I mean something broader than a function call. The source focus pokemon-showdown/sim/battle-actions.ts, chunk_95a7524b39636e24, is useful because it encourages us to ask, “What responsibilities belong inside move execution?” Not all of them belong in the damage formula. That is one of the big architecture mistakes in student projects. They build a function called calculateDamage, and then they keep stuffing the whole battle system into it. Accuracy? Damage. Critical hits? Damage. Contact effects? Damage. PP deduction? Damage. Announcements? Damage. Fainting? Damage. Replacement? Damage. Suddenly calculateDamage is no longer a formula; it is the whole battle engine wearing a misleading name.

Showdown avoids that by having move execution coordinate many substeps, while data and event callbacks describe move-specific behavior. FireRed has its own version of that separation. Some information lives in move data tables. Some lives in battle scripts. Some lives in C functions. Some lives in controller and animation code. Again, we are not re-teaching every move table today, but we are reinforcing the architecture rule: do not confuse numeric calculation with turn resolution.

Let me pause for a check. In your notes, write one sentence that distinguishes choice capture from action execution. I’ll give you my version after a moment. Choice capture records what a participant wants to do at a legal decision point. Action execution applies committed battle rules in ordered time and may produce new decision points. That sentence is compact, but it contains most of today’s lecture.

Now let’s bring in output, because output is where architecture becomes observable. Showdown’s BattleStream, from pokemon-showdown/sim/battle-stream.ts, chunk_ced1577d9e2d59, makes battle output visible as protocol lines. This is gold for testing. If a move misses, if damage is applied, if a Pokémon faints, if a replacement request is generated, the stream gives you a record. You can drive a battle with inputs and inspect outputs. That makes the simulator feel almost like a pure machine, even though internally it has rich mutable state.

FireRed output is more entangled with presentation hardware. The engine must update tilemaps, sprites, palettes, windows, sound, text boxes, healthboxes, and animation frames. But from an architectural point of view, FireRed also emits observations. A battle message appears. A cry plays. A health bar drains. A sprite shakes. A faint animation runs. A menu opens. A callback waits. A script resumes. You can think of those as protocol lines that are not text lines. They are visual and temporal protocol events delivered to a human through the GBA presentation layer.

That way of thinking matters for your week 23 lab. The lab asks you to run or pseudocode a minimal BattleStream exchange, then map request, choice, commit, queue, move execution, damage, and protocol output to equivalent FireRed battle concepts. A weak lab submission will just say “Showdown has BattleStream and FireRed has battle.” A strong submission will name the stopping points. It will say, “Here is where the request is generated. Here is where choice is held without resolution. Here is where choices become committed. Here is where action ordering is decided. Here is where the move execution envelope begins. Here is where damage is only one substep. Here is where output is observed. Here is where the next request is generated.” That is the level expected now because recent homework and quizzes already checked basic tracing.

I want to make the server boundary contrast a little sharper. In Showdown, the server README, pokemon-showdown/server/README.md, chunk_1ba53f1706d86463, belongs to the operational environment. Running a localhost server is not the same as invoking the simulator in a unit test or through BattleStream. A room may have spectators, chat, authentication, ladder concerns, command parsing, or process-level behavior. The battle simulator should not need to know most of that. It should receive legalized inputs and produce battle outputs. When that boundary is clean, you can test the simulator without booting the whole social application.

In FireRed, a comparable mistake would be to make the battle damage formula depend directly on overworld map loading or NPC script state. FireRed does have boundary-crossing setup code, and pokefirered/src/battle_setup.c, chunk_494d132a59422c85, shows battle setup touching many systems. But after handoff, the battle engine must become its own temporary world. The overworld does not vanish, but it is no longer the active player interaction model. Main callbacks and tasks have allowed a different state machine to occupy the frame loop. That is precisely why our early weeks on AgbMain, SetMainCallback2, CreateTask, and RunTasks were not “introductory trivia.” They are still paying rent in week 23.

Now, one more subtle point: determinism. Showdown’s architecture is built in a world where battle logs, tests, replays, and networked clients are central. That means a deterministic action queue and explicit protocol output are not luxuries. They are how the simulator remains debuggable. FireRed’s determinism has different pressures. It runs on fixed hardware, with tightly controlled memory and frame behavior. It has pseudo-randomness, player input, link synchronization, and save state implications. It does not need to produce a web replay protocol, but it absolutely needs reproducible internal ordering, or the game becomes unfair, desynchronizes in link play, or displays nonsense.

Think back to our data-table-to-visible-behavior tracing method. For FireRed, a move begins as data. It becomes a selectable menu entry because party data says the creature knows it, battle state says the creature can act, PP and disable-like restrictions allow or deny it, and UI code presents it. Then, after selection, the move becomes a committed action. Then, during execution, the move’s data and battle scripts determine what the player sees and what state changes. Then the engine returns to a stopping point. That entire path is what you should now be able to trace.

For Showdown, the comparable path begins in Dex-style lookup and battle initialization, proceeds to request generation, choice parsing, commitment, queue insertion, runMove, event callbacks, damage and effects, stream output, and next request. The source packet’s broader synthesis reminds us that pokemon-showdown/sim is a recurring course module around battle, Dex, moves, and simulation, while pokemon-showdown/server is a separate recurring module around server, chat, users, and rooms. That distinction should now feel architectural rather than merely organizational.

Let’s do one last board exercise. I am going to write six verbs: expose, capture, commit, schedule, execute, observe. Now I’ll add a seventh: request. Actually, request belongs at both ends. The engine exposes a request. It captures choices. It commits when complete. It schedules actions. It executes actions. It observes output. Then it exposes the next request. If a forced switch occurs, the next request may arrive before the next ordinary turn. If the battle ends, the next request is not a player battle choice; it is a return-to-host transition. In Showdown, that may be protocol output and room cleanup. In FireRed, that may be money, experience, evolution checks, trainer flags, scripts resuming, and a callback returning toward overworld. Different details, same lifecycle.

Here is the closing synthesis for today’s lecture. Week 23 is not asking you to become a Showdown developer instead of a FireRed architecture reader. It is asking you to use Showdown’s explicit simulator surfaces to sharpen your vocabulary for FireRed. Battle request state teaches us to look for legal stopping points. choose and commitChoices teach us not to confuse input arrival with state advancement. The battle action queue teaches us that turn order is a first-class correctness problem. runMove teaches us that move execution is an envelope around many rule hooks, not a synonym for damage. BattleStream teaches us that output order is part of the contract. The server README reminds us that hosting a battle and simulating a battle are different responsibilities.

And when we map that back to FireRed, we see the same course thesis again. FireRed is a stack of cooperating state machines plus data tables. Battle setup crosses from overworld and scripts into battle, as suggested by pokefirered/src/battle_setup.c, chunk_494d132a59422c85. The runtime shell gives subsystems time through AgbMain, SetMainCallback2, CreateTask, and RunTasks in the chunks we studied earlier. The battle system then consumes data, globals, callbacks, scripts, controller commands, and presentation buffers to move from one legal observation point to the next. That is the anatomy.

For the next class, carry forward the clean separation we built today. When we look more directly at the Simulator API and online runtime boundary, I want you to resist the temptation to point at a socket and call that “the battle.” The battle is the rule machine. The server is the host and traffic director. FireRed’s host is not a Node server, but it still has a host. Once you see that, you can compare systems without flattening them, and that is the level of architectural reading this final module is meant to develop.

## Source References
- pokemon-showdown/data/text/moves.ts
- pokemon-showdown/data/mods/gen2stadium2/scripts.ts
- pokemon-showdown/data/mods/gen1/README.md
- pokemon-showdown/data/mods/gen9ssb/moves.ts
- pokefirered/src/battle_setup.c
- pokefirered/src/learn_move.c
