On this page
- Find the first failing stage
- A short verification routine
- Common symptoms
- What belongs in the console?
- Worked diagnosis: food disappears, but the turtle does not gain energy
- Worked diagnosis: Reset works once, then behaves differently
- Keep a small evidence record
- When asking for help
- Separate compatibility from scientific rules
Checking and troubleshooting a model
Manual contents | Previous: Grazers and food | Next: Quick reference and glossary | Compatibility and known limitations
Start with one small, predictable case. A realistic-looking animation is not
evidence that the rules, timing, or conservation arithmetic are correct.
All advice here assumes lifecycle: "aligned".
Find the first failing stage
Work down this list. Stop at the first mismatch rather than changing several rules at once.
- Does the app load? If not, save all four definitions and fix the first syntax/import/runtime exception. A later error may only be a consequence.
- Is time-zero state correct? Check Project, constructors, defaults, init, and setup. Do not investigate movement until the starting world is right.
- Does one Step advance time? If not, inspect the first exception and clock configuration. Do not add a timer or manually call step/shift hooks.
- Does the intended rule execute? Check visit mode and patch activation. One temporary log at the rule entrance distinguishes "not scheduled" from "scheduled but calculating the wrong result."
- Is the proposed result correct? Inspect input values, current state, pending state, units, and assignment order.
- Is the committed result correct? Custom Stores need synchronization; immediate and pending writes have different meanings.
- Is only the picture/chart wrong? Compare numeric state with appearance and published series before changing the scientific rule.
- Does it fail only after Reset? Check reset ownership, cached references, repeated registrations, and whether setup reconstructs derived state.
Use the quick-reference tables while checking each stage. Pause before editing; save and relaunch after code or Project changes. A live slider change is not the same operation as loading new code.
A short verification routine
- Startup: inspect time zero, population, colors, input defaults, chart values, and one representative turtle/patch.
- One tick: write down what it should read, propose, and commit. Step once. Compare the result, not only its appearance.
- Boundary: move/query near each edge, and check a corner or tiny wrapped world where duplicate neighbors would matter.
- Inputs: change each live input; also test zero/min/max where supported. Distinguish changes taking effect now, next tick, reset, or relaunch.
- Reset twice: run, change inputs, reset, step, and reset again. Check custom state, current slider values, initial outputs, and listener counts.
- Population changes: if used, test zero agents, one birth/death, setup of newborns, and eventual removal from local queries.
- Randomness: check a deterministic case first. Reset restores saved starts, not the random stream; matching every future random path is not a reset test.
The fire, Walkers, and Grazers tutorials include small expected-result tables. Keep such a table with your model's own tests.
Common symptoms
| Symptom | Likely checks |
|---|---|
| App never connects or has a JavaScript error | Save All; inspect the browser console; check import/export names and generated shell wiring; solve the first exception before changing rules |
| Empty/incorrect initial state but Reset helps | Confirm aligned flag, remove constructor init/setup calls, move population-dependent work to setup; do not make a reset-before-run ritual |
| Only background appears | Check Project agent, exported Turtle, Main initAgents, count/start array, and agent palette/size |
| Patch rules do nothing | Check visitMode includes C; register active patches using visitAlways in setup; do not confuse drawing with activation |
| Turtle rules do nothing | Check visitMode includes A and Main initialized the population |
| Turtle steps but never moves | Check movement mode, live speed, and super.turtleStep; Next_Location requires a destination proposal |
| Turtle turns but movement seems delayed | Base heading updater runs after scheduling movement; proposal order determines which move uses it |
| Movement is too large or points the wrong way | Coordinates are not pixels; theta is radians, zero east, y grows downward |
| Updater never runs | Assign a function, keep aligned base-step call; evaluate custom Store updaters explicitly |
| Updater runs twice | Remove old manual built-in autoNext calls; check duplicate patch activation paths |
| Current value does not change immediately | next assignments commit at shift; inspect current after a full Step |
| Update order changes the answer | Look for ordinary fields, immediate setters, or intentional pending-state claims; distinguish them from synchronous rules |
| Resource becomes negative or is created | Specify growth/claim order; cap consumption by remaining pending availability; check accounting, not just color |
| Query unexpectedly includes center | block includes center; use blockX/ring when appropriate; turtle nhood queries exclude the calling turtle separately |
| Query misses turtles | Use committed occupancy with a working agent index; dying/newborn-hidden turtles are excluded; inspect the query's selected patches |
| Radius result differs from a grid ring | Euclidean radius and square/hex graph distance are different; see the neighborhood reference |
| Neighborhood call throws RangeError | Grid distances must be nonnegative safe integers; radii finite/nonnegative; nhood4 requires square topology |
| Chart is empty or wrong shape | Match exact code/site, legend length, scalar/series array; use showList for a prebuilt series array; publish in setup and update |
| Slider moves but model ignores it | Match code/case; read it in model code; distinguish live/initial/reset-only input policies |
| Population count slider does not resize | Initial count configuration is not a live resizing command; use designed birth/death rules or relaunch |
| Speed default 0 becomes 1 | Legacy numeric default fallback remains; the tutorials explicitly apply current speed in setup and pending speed each step |
| Custom state survives Reset unexpectedly | Give each owner an explicit local reset policy; synchronize only commits, it does not define reset |
| Listeners accumulate after Reset | Wire once in init/constructor; setup repeats; clean registrations owned by discarded turtles |
| Saved turtle references stop working | World reset recreates turtles; rebuild references in setup rather than using old objects |
| Colored/invisible turtle still exists | Color/visibility is not death; use an explicit rule and die when removal is intended |
| Birth does not immediately change the count | Birth insertion/setup is deferred until shift; local visibility can begin later |
What belongs in the console?
Use temporary console.log statements for inspection, not a second model loop.
For example, inside update:
console.log("tick", this.currentTime, "population", this.turtles.size);
This fragment assumes an agent world. For a patch-only world, inspect
this.patches.toList().length instead.
Log both current and pending custom Store values when diagnosing phase order.
The browser console is distinct from a configured model logger display.
Do not conceal an exception with a broad catch, return a success-shaped empty
value, or silently change the intended rule just to keep the picture moving.
Inspect the right phase
| Inspection point | What it can tell you |
|---|---|
| Constructor | Custom allocations exist; runtime defaults may not yet be assigned |
| Init | Context/defaults and one-time registrations; Main populations are not ready |
| Setup | Initial/reset world after populations are ready |
| Coordinate | Committed state before this tick's individual rules |
| Inside an individual rule | Current state and proposals made so far, not a completed tick |
| Update | Committed results after the clock advances |
Log primitive values such as patch.food.get(), not just a whole mutable
object: browser consoles may expand an object using values it acquired later.
Record the time and object id or patch coordinates alongside each number.
Remove noisy diagnostic logs once the small case agrees with your prediction.
Worked diagnosis: food disappears, but the turtle does not gain energy
Use the stationary Grazers check, not a randomly moving population. Its first tick should change occupied-patch food from 4 to 2 and turtle energy from 4 to 5.
Trace these questions in order:
- Which patch was eaten? The rule uses
myPatch()before movement. Inspecting only the destination patch can make a correct result look wrong. - How much was granted? Record the
eatenresult fromtakeFood. BITE is a maximum request, not a promise of that much food. - What energy was proposed? With energy 4 and eaten 2,
4 - 1 + 2is 5, not 6: the tick has a cost. - Was energy registered? The custom Store needs
this.main.synchronize(this.energy)in turtle init. A correctenergy.nextwith unchangedenergy.get()after a completed tick suggests a commitment problem, not an eating problem. - Was the chart published after commitment? Main update reads the new energy; coordinate reads the old energy.
- Does only color disagree? Grazers' color updater reads
energy.nextinside the step and needs the aligned base turtle step to evaluate it.
Do not "fix" the symptom by adding a second energy assignment in Main, calling Store shift yourself, or removing the cost. Repair the first broken link in the intended calculation.
Worked diagnosis: Reset works once, then behaves differently
Compare these checkpoints across startup and two resets:
| Check | Expected ownership |
|---|---|
| Main and patch objects | Same objects survive |
| Custom patch Store | Same Store, restored by patch reset |
| Turtle objects and their Stores | New objects after each world reset |
| Persistent patch activation | Same selected patches; no duplicate Set entries |
| Listeners on surviving objects | No extra copies installed by repeated setup |
| Cached turtle references | Rebuilt in setup, pointing to replacements |
| Sliders | Current values retained, not restored to Project defaults |
| Initial chart values | Published again by setup |
If a model's starting state depends on an input, specify whether reset should use the current input or a saved baseline. Both policies can be meaningful, but they require different code. Reset is not a request to rewind randomness. See Main reset ownership and turtle lifecycle.
Keep a small evidence record
For each repair, record a few rows like these in your own test notes:
| Stage | Expected | Actual |
|---|---|---|
| Startup | Population and state from the configured start | Measured values |
| One Step | Hand-calculated state and time | Measured values |
| Input change + Step | Only the intended rule changes | Measured values |
| Reset + Step | Restored owner state with current input policy | Measured values |
| Second Reset | Same ownership/registration behavior | Measured values |
When randomness is not the subject of the check, use explicit starts and temporarily remove random turns, as in the moving Grazers experiment. Keep a known-good small case while you restore larger populations and randomness.
When asking for help
Include Project lifecycle, visit mode, topology/boundaries, the smallest reproducible start, relevant rule/reset hooks, expected versus actual values, the first exception with its stack, and whether the problem occurs initially, after a tick, or after reset. State whether you edited a standalone app or Studio model. Avoid including private data or credentials.
Separate compatibility from scientific rules
The flag does not migrate a legacy app by itself. Follow the freshening checklist for an explicitly selected migration. Remaining app migrations are deferred. Known movement helper caveats are listed in Programming turtles; they are not limitations of the repaired neighborhood operators.