Skip to content

Pokémon Browser Game: Complete the caught collection

A successful catch will add one species to a visible caught-Pokémon list. The total will come from that list’s state array. Catching the same species again will end the encounter and report the result without adding another entry.

What you will practice

  • Trace one confirmed capture from the active encounter to the caught collection before rendering.
  • Use the Pokédex number, rather than the display name, to recognize a species already recorded.
  • Render an empty result and caught entries from the same state array.
  • Derive the visible total from the array instead of storing a second count.
  • Test first, different, and duplicate catches in one game session with controlled known encounters.
  • Keep capture status, movement controls, and keyboard focus consistent with the finished battle loop.

Build the battle loop proved that capture can pass one species identity out of an encounter. At that checkpoint, the game still shows a caught total of 0. This stage completes the handoff and makes the visible list agree with the final state after each catch.

confirm catch → copy number and name → check number in collection
→ add at most one record → end encounter
→ set status → render list and total → focus North

The catch decision belongs to the battle action. The list renderer reads the result; it does not decide whether a species was caught.

The static Pokédex remains a reference page. It will not read the current game state. A full Reset control, container queries, and new animation belong to later stages.

Starting point

Before you start

  • The Build the battle loop article passes its full self-check, and normal encounter mode has been restored.
  • A successful capture saves the wild Pokémon number and name in a temporary pendingCapture record, ends the encounter, and focuses North.
  • state.caughtPokemon is an empty array. The Current game summary shows its length as 0, but no caught-list section exists yet.
  • pokedex.html already contains the static catalog, including the species used by the encounter list.
Current state
Battle actions work. Captures are confirmed, but no caught Pokémon appears in the game page.
First action
Open game.js at the successful-capture branch. Mark where the number and name are copied, where the encounter ends, and where renderGame() runs.
First checkpoint
You can point to a place for the collection check between copying the identity and the final render. Attack, failed capture, and Run still follow their current routes.
Help trigger
Use the nearest Assistance level or ask for help if a catch adds two entries, the list and total disagree, a different species cannot be tested without losing the first, or focus disappears after capture.

Complete this stage when:

  • game.html has a titled caught-Pokémon section with a semantic list and a saved-HTML empty message;
  • before any catch, the list says No Pokémon caught yet. and the total is 0;
  • a successful catch copies one record with the species’ Pokédex number and name before the active encounter is cleared;
  • a first catch adds one record and shows total 1; a different species adds a second record and shows total 2;
  • a later catch of either recorded number adds no entry, leaves the total unchanged, and reports that the species is already in the collection;
  • failed capture, defeat, and Run leave the collection unchanged;
  • the final successful-capture route has one collection decision and one render; the interim pendingCapture property is removed once the direct route works;
  • rendered names use textContent, and the total comes from state.caughtPokemon.length;
  • the final status appears in the existing polite status region without receiving focus, and North receives focus after the encounter ends;
  • starting a new trainer through the existing form clears the collection, list, and total; and
  • every encounter species has a matching static Pokédex entry, and the game still works near 320 CSS pixels and at 200% browser zoom.

The full in-page Reset control is the next stage. For this stage, use a fresh trainer submission or page reload to return to the empty collection before a new test route.

Copy this table into your project notes. If you are completing the Level 2 assignment, record the planned routes in interactive-test-report.md. Choose the species and attack counts that match your own battle rules.

Collection before the catch Confirmed species Collection after the catch Visible result
Empty First number One record One entry, total 1, added status
Contains the first number Different number Two records Two entries, total 2, added status
Contains the first and second numbers First number again The same two records Two entries, total 2, already-recorded status

A caught record needs a Pokédex number and display name. The number identifies the species. The name supplies text for the list and status. Two records with the same number count as the same species even if their names differ.

Trace the successful-capture path in game.js. It currently copies identity into state.pendingCapture and then ends the encounter. In this stage, move the collection decision into that same action. Copy the identity while state.activeEncounter still exists. Compare it with state.caughtPokemon before the call that ends and renders the battle. When this works, remove pendingCapture from the initial state and the valid-trainer setup route. Do not leave a second, stale copy of a catch in state.

Read the trace aloud from the threshold comparison to the final focus call. The collection check occurs only after a successful threshold result and before the render. A failed catch never reaches it.

Assistance 1 — Locate the handoff

Find the assignment to pendingCapture in the successful-capture branch. That branch already has the correct number and name. The new collection decision belongs after that copy and before the existing terminal-result helper.

Checkpoint: The collection has one update point

What now works
The first, different, and repeat-catch results are written down. One successful-capture path owns the collection decision; rendering only reads its result.
Files changed
game.js, project notes or interactive-test-report.md
What remains
Add a visible list with a useful empty result.
Next action
Open game.html below the Current game summary and add the titled caught-Pokémon section.
If it does not work
If the planned order updates the list after renderGame(), move the collection decision before the terminal-result call. If it occurs in renderGame(), return it to the capture action.

In game.html, add a section after the Current game summary. Give the section a heading and a list with a stable ID. Put one empty-message list item in saved HTML so the initial page has a clear result before JavaScript runs. Keep the existing caught-total output in the summary.

This is a partial structure. Add the section relationship and your chosen class names:

game.html — caught-list starting point
<h2 id="caught-heading">Caught Pokémon</h2>
<ul id="caught-list">
<li>No Pokémon caught yet.</li>
</ul>

Select the list once near the other stable DOM selections in game.js. Include it in the existing missing-elements check. Do not select it again each time renderGame() runs.

Add one small collection-rendering responsibility that:

  1. removes the previous list children;
  2. adds the empty-message item when state.caughtPokemon.length is 0;
  3. otherwise visits the array with for...of and creates one li for each record; and
  4. writes the total from the array length.

Use replaceChildren(), createElement(), append(), and textContent. Build each list item’s text from the record’s number and name. Keep the list in array order. Add the new renderer to renderGame() so the initial call, a catch, and a new trainer submission all show the current array.

In styles.css, reuse the game’s section surface, border, spacing, and text colors. Give list items room to wrap long names. Keep the list readable as one column at narrow width; a container query comes later.

Save and reload game.html before starting a trainer. The caught section shows its empty message and the summary shows 0. Start a trainer and confirm that the same result remains. Inspect the Console for errors.

Sample game before trainer setup: the Current game summary shows caught total 0, and the Caught Pokémon list contains one No Pokémon caught yet message.
The empty message is one visible list item while the state array and caught total are empty.
Sample game before trainer setup: the Current game summary shows caught total 0, and the Caught Pokémon list contains one No Pokémon caught yet message.
Assistance 2 — Separate an empty array from a rendered empty result

An empty array has no records to visit, but the page still needs one visible list item. Handle length 0 before the loop. On later renders, clear the list first so that empty item disappears when the first species is added.

Checkpoint: The empty collection renders

What now works
The saved page and the first JavaScript render show one empty message and a total of 0. The list and summary both read state.caughtPokemon.
Files changed
game.html, game.js, styles.css
What remains
Update the array when a catch succeeds.
Next action
Add a lookup that compares a copied Pokédex number with the records already in state.caughtPokemon.
If it does not work
If the empty message repeats, check whether the renderer clears old children first. If the summary differs, derive it from the array in the same rendering pass.

Use the stable-identity lookup pattern to search state.caughtPokemon by number. A for...of loop can return the matching record or null. This lookup reads the array; it does not change it.

In the successful-capture branch, create one record from the active wild Pokémon before the encounter ends:

game.js — the confirmed identity only
const caughtPokemon = {
number: wildPokemon.number,
name: wildPokemon.name,
};

Compare caughtPokemon.number with the existing array. If the lookup returns null, add the record with push() and choose an added status. Otherwise, leave the array unchanged and choose an already-recorded status. In both cases, end the encounter once, render once, and focus North. Do not call the wild-response code.

Keep this order when you edit:

Point in the action State or interface result
Threshold has passed The active wild record still holds the number and name.
Identity copied One caught record is available for the lookup.
Lookup complete The array receives one record or stays unchanged.
Terminal result The encounter ends and the selected status is stored.
Render and focus List, total, status, controls, and North focus agree.

Remove pendingCapture after the direct route works. The array becomes the only state that records caught species. The renderer must not call push(), and a catch must not update a separate numeric count.

Use one controlled species. Catch it once and inspect state.caughtPokemon in DevTools: it contains one number-and-name record. The list contains one entry, the total is 1, and the status says it was added. North has visible focus. Then trigger and catch the same species again. The encounter ends, but the array, list, and total remain at one.

Sample game after the first Misdreavus catch: the status reports the addition, the caught total is 1, and one Misdreavus record appears in the list.
One successful catch changes the collection array, rendered list, and derived total together.
Sample game after the first Misdreavus catch: the status reports the addition, the caught total is 1, and one Misdreavus record appears in the list.
Assistance 3 — Trace the duplicate branch

Let the lookup return the existing record or null. Check the result before push(). The duplicate path must skip push() but continue to the shared terminal result with its own status. Do not put the duplicate check after the encounter has been cleared.

Assistance 4 — Use a partial lookup outline

Complete the comparison and return value yourself. The number argument and the record number must use the same type.

game.js — partial lookup
function findCaughtPokemon(number) {
for (const caughtPokemon of state.caughtPokemon) {
// Compare the two Pokédex numbers.
// Return the matching record.
}
return null;
}

Checkpoint: One successful catch records at most one species

What now works
The first catch adds one number-and-name record; catching that number again ends battle with an already-recorded status and no second entry.
Files changed
game.js, game.html, styles.css
What remains
Prove that a different species can join the same collection and check the static Pokédex relationship.
Next action
Prepare two controlled species in one session and record the expected collection after each catch.
If it does not work
If the list shows two copies, compare the incoming and existing numeric Pokédex numbers before push(). If state has one record but the DOM has two, check replaceChildren() in the collection renderer.

The known-encounter setting from the previous stage uses a const number and a reload to change species. Reloading also clears the in-memory collection. For these collection tests, change only that test-setting declaration to let knownEncounterNumber. Keep the allowed encounter list, normal chance percentage, and shared encounter decision unchanged.

Set encounterTestMode to "known-encounter" in the source, save, and reload. Start a fresh trainer. The sample route uses Misdreavus 200 first and Bounsweet 761 second. Your species and attack counts may differ.

Route Action Expected collection
First In DevTools Console, assign knownEncounterNumber = 200. Enter grass, lower Misdreavus to the capture threshold, and catch it. One Misdreavus record; total 1.
Different Return to the Center. In DevTools Console, assign knownEncounterNumber = 761 without reloading. Enter grass and catch Bounsweet. Misdreavus and Bounsweet, once each; total 2.
Duplicate Return to the Center. Assign knownEncounterNumber = 200 without reloading. Enter grass and catch Misdreavus again. The same two records; total 2; already-recorded status.

For the sample numbers, two Attacks leave Misdreavus at 12 hit points and Bounsweet at 8. From the Center, North enters the adjacent grass. After a catch, South returns to the Center and heals the starter. Change the known number only while no encounter is active; it affects the next encounter decision, not a wild Pokémon already in state.

The Console assignment changes the runtime test setting for the current page. It does not change the saved source. After the routes, restore encounterTestMode to "normal" in the source, restore your documented default known number, save, and reload. Confirm that the normal chance remains the documented value.

Compare each number in encounterDefinitions with pokedex.html. Record one short row per encounter species in project-brief.md. Each one needs a complete static entry. Open pokedex.html with JavaScript disabled: the catalog must still be useful and must not claim to show the current caught collection.

If the second catch starts with an empty list, check whether you reloaded or submitted a new trainer between catches. If the next known species does not change, inspect the let declaration and the Console assignment before entering grass. If a duplicate changes the total, inspect the array before changing the renderer.

Sample game after Misdreavus, Bounsweet, and a second Misdreavus catch: two distinct list entries remain, the total is 2, and the status says Misdreavus is already in the collection.
A repeat catch ends the encounter and reports the duplicate without changing the two recorded species.
Sample game after Misdreavus, Bounsweet, and a second Misdreavus catch: two distinct list entries remain, the total is 2, and the status says Misdreavus is already in the collection.

Checkpoint: First, different, and duplicate catches agree

What now works
One game session records two different Pokédex numbers once each, rejects a repeat number, derives the total, and shows a matching status after every catch. Every encounter species has a static Pokédex entry.
Files changed
game.js, game.html, pokedex.html, project-brief.md if used, interactive-test-report.md if used
What remains
Run the negative, keyboard, and layout checks, then restore normal encounter mode.
Next action
Start a fresh known encounter above the capture threshold and test a failed Poké Ball before the full self-check.
If it does not work
Trace one row at a time: encounter number, array before, lookup result, array after, rendered list, total, status, and focus. Restore the known number only after recording the row.

Use controlled encounters to repeat the new routes. Also check failed capture, defeat, and Run from fresh starting conditions. Those actions must leave the array, list, and total unchanged. A valid new trainer submission must clear them through the existing setup route.

Self-check

Complete these checks against the required result.

  1. Before setup, the saved list and rendered list show one empty message and a total of 0.
  2. A first successful catch creates one record with the expected Pokédex number and name; the empty message disappears, one entry renders, and the total is 1.
  3. A different species caught in the same session creates a second record and entry; the total is 2.
  4. Catching a recorded Pokédex number again ends the encounter with an already-recorded status, and the array, list, and total stay at 2.
  5. The successful-capture branch reads identity before ending the encounter, updates the collection at most once, and renders once. No pendingCapture property or second count remains.
  6. Failed capture, defeat, and Run do not add an entry. Starting a new trainer clears the collection and restores the empty result.
  7. After each terminal catch, movement is enabled, battle controls are unavailable, North has visible focus, and the polite status region reports the result without receiving focus.
  8. Pointer, Enter, and Space each activate one catch action in separate fresh encounters. The Console has no errors.
  9. Every encounter-list number has a matching static Pokédex entry; the static page remains useful without JavaScript.
  10. Check the list and summary near 320 CSS pixels and at 200% browser zoom. Long names wrap and no content requires horizontal page scrolling.
  11. Restore normal encounter mode, reload, and confirm that the normal percentage and allowed species list remain unchanged.

Save a Git checkpoint after the self-check passes and normal encounter mode is restored. The game now shows the complete caught collection for the current page session. The next stage will add one in-page Reset control that restores setup, map, battle, collection, status, controls, and focus together.