feat: Battle City Tank Arena demo — LLM communication paradox#35
feat: Battle City Tank Arena demo — LLM communication paradox#35JNK234 wants to merge 14 commits into
Conversation
31x31 symmetric arena with brick/steel walls, tank breeds with cardinal movement, bullet mechanics (2 patches/tick, wall damage), explosion visuals, scripted bot AI (patrol/chase/attack/retreat), 3 stage setups (solo nav, team battle, team+comms), win conditions, scoring system, and full interface widgets. LLM action selection is stubbed with random choices for testing.
Replace random action stub with llm:choose that sends tank observations (position, minimap, enemies, objectives) and picks from [move-forward, turn-left, turn-right, fire, stay]. Adds config.txt (openai/gpt-4o-mini/0.3), llm:load-config in setup, llm:clear-history per decision, error handling with carefully block, and LLM call counter.
Stage 3 inter-tank messaging via llm:chat-with-template with message-template.yaml. Ally messages injected into decision context via llm:set-history before llm:choose. Speech bubbles via label/message-timer. show-thinking? toggle uses llm:chat-with-thinking to expose reasoning chains. Adds action-template.yaml for thinking mode fallback parsing.
Documents the BattleAgentBench communication paradox hypothesis, three stages, setup instructions with config placeholder, expected results per model tier, and all LLM primitives used.
All 31 row strings now verified at exactly 31 characters to match the 31x31 world grid. Removed unused variable.
Bug 1: lower-case is not a NetLogo 7 primitive. Replace with multi-case position checks for action parsing. Bug 2: Map Y-axis was inverted — teams spawned at enemy bases. Change py = row-idx - 15 to py = 15 - row-idx so Red base (R) renders at top (y=+15) near red tank spawns (y=+13) and Blue base (B) at bottom (y=-15) near blue tank spawns (y=-13).
- Plot pens use <pen> with <setup>/<update> children instead of inline text content - Chooser uses <choice type="string" value="..."> instead of <option> with quoted text - Add patchSize/frameRate as floats - Add plot-level <setup>/<update> elements - Add pen legend="true" attribute
Remove per-tick llm:clear-history from choose-action so tanks retain memory across ticks. Remove redundant Stage 3 llm:set-history injection (ally messages already included in observations). Add last-move-failed? tracking to execute-action and initialize it in setup-tank-defaults. Enrich observations with wall-blocked feedback and open cardinal directions so the LLM can navigate around obstacles.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c55ce67e68
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ask tanks with [is-llm? and tank-health > 0] [ | ||
| set my-observation build-observation | ||
| ] |
There was a problem hiding this comment.
Rebuild observations after messages are delivered
In Stage 3, build-observation is cached here before generate-message/deliver-messages run, but choose-action later sends that cached string to llm:choose. Since build-observation only serializes ally-message when it is already present, a message generated on tick N cannot influence the ally's decision until tick N+1. That means the new comms stage is not actually testing same-tick tactical coordination, and the comms-override? toggle will appear much weaker than intended.
Useful? React with 👍 / 👎.
| llm:clear-history | ||
| let vars (list | ||
| (list "observation" my-observation) | ||
| ) | ||
| let msg llm:chat-with-template "message-template.yaml" vars |
There was a problem hiding this comment.
Preserve action history when generating messages
llm:clear-history clears the current agent's persistent history, and both llm:chat-with-template and llm:choose append to that same history in src/main/LLMExtension.scala. Because this runs immediately before choose-action, every Stage 3 tick wipes the tank's accumulated action context and replaces it with the message-generation exchange, so the headline comparison is no longer 'Team Battle vs Team Battle + comms' but a different memory regime entirely.
Useful? React with 👍 / 👎.
Summary
llm:choosefor action selection andllm:chat-with-templatefor tactical messagingStages
comms-override?toggleKey Design Decisions
generate-messageuses fresh context (llm:clear-history) for single-shot message generationTest Plan
show-observations?to verify move-failure feedback and open directionsshow-thinking?to verify reasoning chain output