-
Notifications
You must be signed in to change notification settings - Fork 571
_ #2011
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
_ #2011
Conversation
WalkthroughAdds a full client-side bot system for singleplayer: core bot logic, analysis, strategies, action execution, and a control panel UI. Wires bot integration into ClientGameRunner, provides a global console helper, and documents how to run/use the bot. Increases singleplayer bot slider max and default. Minor renderer and HTML updates. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant ClientGameRunner
participant BotIntegration
participant PlayerBot
participant EventBus
note over ClientGameRunner,BotIntegration: Singleplayer game start
User->>ClientGameRunner: Launch singleplayer
ClientGameRunner->>BotIntegration: initialize(gameView, eventBus, {autoStart:false})
User->>BotIntegration: start() (via UI or console)
BotIntegration->>PlayerBot: enable()
loop every 100ms
BotIntegration->>PlayerBot: tick()
alt Spawn phase
PlayerBot->>EventBus: Spawn event
else Main game
PlayerBot->>EventBus: Build/Attack/Alliance/etc.
end
end
sequenceDiagram
autonumber
actor User
participant BotControlPanel
participant BotIntegration
note over BotControlPanel: UI polling every 1s
BotControlPanel->>BotIntegration: getStatus()
BotIntegration-->>BotControlPanel: BotStatus
User->>BotControlPanel: Click Start/Stop
BotControlPanel->>BotIntegration: start()/stop()
note over User,BotIntegration: Console helper (openFrontBot)<br/>start | stop | status | analysis | forceTick | isRunning
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 29
🧹 Nitpick comments (27)
src/client/SinglePlayerModal.ts (2)
27-27
: 4k cap and 2.5k default bots: verify engine limits and UX4k players is extreme; 2.5k default may stall low/mid machines and blow up map/player limits. Consider an adaptive cap (map-size/tiles-based) and a safer default (e.g., 200–400).
Would you like a quick patch to compute maxBots from expected player capacity per map?
Also applies to: 35-35
358-362
: Clamp instead of ignoring out-of-range inputReturn-early can leave the UI value unchanged without feedback. Clamp to [0, maxBots] to be robust for keyboard edits and programmatic changes.
- if (isNaN(value) || value < 0 || value > this.maxBots) { - return; - } - this.bots = value; + if (isNaN(value)) return; + const clamped = Math.min(this.maxBots, Math.max(0, value)); + this.bots = clamped;HOW_TO_RUN_THE_BOT.md (3)
117-124
: Add language to fenced blockAnnotate the block as plain text to satisfy markdownlint.
-``` +```text 1. Game starts → Bot icon (🤖) appears in top-right 2. Click icon → Control panel opens 3. Click "Start Bot" → Bot begins analysis 4. Spawn phase → Bot automatically selects best spawn location 5. Early game → Bot manages resources and looks for expansion 6. Mid game → Bot makes strategic decisions based on threats/opportunities--- `139-142`: **Add language to fenced block** Same here—mark as text/log. ```diff -``` +```text PlayerBot: Spawning at (45, 32) with confidence 87% Spawn reasons: Large land area (167 tiles), Isolated position, Good water access
--- `146-146`: **Tone tweak** One exclamation point is enough; consider trimming for a more neutral voice. </blockquote></details> <details> <summary>src/client/bot/analysis/GameStateAnalyzer.ts (2)</summary><blockquote> `128-132`: **Guard against empty cluster arrays** Avoid `Math.max()` on an empty list. ```diff const clusters = this.analyzeTerritorialClusters(player); - const largestClusterSize = Math.max(...clusters.map((c) => c.size)); + const largestClusterSize = + clusters.length > 0 ? Math.max(...clusters.map((c) => c.size)) : 0; const islandCount = clusters.length;
196-227
: Add helper methods used above (outside this hunk)New helpers to estimate missing data.
I can push this patch for you.
// Place inside GameStateAnalyzer class, near other helpers: private estimateCurrentPopulation(player: PlayerView): number { const max = this.estimateMaxPopulation(player); // 60% of capacity, at least current troops return Math.max(player.troops(), Math.floor(max * 0.6)); } private estimateCurrentTroopRatio(player: PlayerView, population: number): number { if (population <= 0) return 0.8; return Math.min(0.95, player.troops() / population); }src/client/index.html (1)
391-394
: Mount only for singleplayer or hide via existing hideable pattern.Avoid rendering the panel in multiplayer to reduce DOM noise and logs. Add the hideable class or gate creation on gameType.
Apply one of:
- Add component-hideable wrapper and toggle "parent-hidden" in MP.
- Or render the element only when gameType === Singleplayer.
Please check overlap on small screens (<= 640px) with replay-panel and right sidebar.
src/client/ClientGameRunner.ts (2)
48-53
: Avoid double registration/import side effects for the web component.This file imports the BotControlPanel for side effects, and GameRenderer also imports the class. Keep registration in one place to prevent accidental double evaluation.
Option A: Keep this side-effect import here; remove the runtime import from GameRenderer and use customElements.get() there. Option B: Register in GameRenderer only.
675-680
: Public getter seems unnecessary; prefer composition over globals.Since UI queries the global accessor, this method adds little value. Either remove it, or have BotControlPanel receive the integration via property for better composition and testability.
src/client/graphics/GameRenderer.ts (2)
6-6
: Use a type-only import or remove it to avoid side effects.You don’t use the class value at runtime; importing it can re-execute module code and re-register the element.
-import { BotControlPanel } from "../bot/ui/BotControlPanel"; +// import type to avoid runtime side effects +import type { BotControlPanel } from "../bot/ui/BotControlPanel";
156-165
: Avoid instanceof on a type-only class; rely on the Custom Elements registry.Instanceof will fail if the class isn’t the same constructor reference. Use customElements.get() and HTMLElement guards.
- const botControlPanel = document.querySelector( - "bot-control-panel", - ) as BotControlPanel; - if (!(botControlPanel instanceof BotControlPanel)) { - console.log( - "Bot control panel not found - this is normal for multiplayer games", - ); - } + const botControlPanel = document.querySelector("bot-control-panel") as BotControlPanel | null; + const isDefined = !!customElements.get("bot-control-panel"); + if (!botControlPanel || !isDefined) { + console.log("Bot control panel not found - this is normal for multiplayer games"); + }src/client/bot/ui/BotControlPanel.ts (2)
243-249
: Construct via tag name to use the registered custom element constructor.new BotControlPanel() bypasses the registry. Prefer document.createElement.
-export function addBotControlPanel( - container: HTMLElement = document.body, -): BotControlPanel { - const panel = new BotControlPanel(); - container.appendChild(panel); - return panel; -} +export function addBotControlPanel( + container: HTMLElement = document.body, +): BotControlPanel { + const panel = document.createElement("bot-control-panel") as BotControlPanel; + container.appendChild(panel); + return panel; +}
129-139
: Prefer composition over globals.Expose a @Property({attribute:false}) integration?: BotIntegration and read status from it when provided; fallback to getBotIntegration() if absent. Easier to test and avoids globals.
Happy to draft this refactor.
src/client/bot/strategy/SpawnStrategy.ts (2)
203-209
: BFS explores water too; push only land neighbors to reduce work.- // Add neighbors - const neighbors = this.gameView.neighbors(tile); - for (const neighbor of neighbors) { - if (!visited.has(neighbor)) { - toVisit.push(neighbor); - } - } + // Add only land neighbors + for (const n of this.gameView.neighbors(tile)) { + if (!visited.has(n) && this.gameView.isLand(n)) { + toVisit.push(n); + } + }
327-347
: Avoid recomputing heavy analysis on every call. Cache per tick.Store the last SpawnDecision and the tick it was computed. Reuse until tick changes.
I can add a small cache with an optional invalidate() API and tests.
src/client/bot/PlayerBot.ts (2)
86-99
: Reduce noisy logs on disabled ticksThe info log on every disabled tick can spam the console. Consider downgrading to
debug
or gating with averbose
flag.
39-41
: MaketickRate
configurableExpose
tickRate
via constructor param or setter for easier tuning across maps/speeds.src/client/bot/integration/BotIntegration.ts (1)
159-171
: Gate global console helper in productionExposing
openFrontBot
is handy for dev, but should be disabled in prod builds.Example:
- (window as any).openFrontBot = { ... }; + if (process.env.NODE_ENV !== "production") { + (window as any).openFrontBot = { ... }; + }src/client/bot/strategy/BuildStrategy.ts (5)
36-57
: Avoid N× sequential awaits forplayer.actions
This loop awaits per tile and can be slow on large borders. Batch with limited concurrency to keep the worker busy but safe.
One simple approach without extra libs:
- for (const tile of borderTiles) { - const actions = await player.actions(tile); - const buildableUnits = actions.buildableUnits; - for (const buildable of buildableUnits) { - const decision = this.evaluateBuildingOption(player, tile, buildable, analysis); - if (decision) { - recommendations.push(decision); - } - } - } + const concurrency = 8; + for (let i = 0; i < borderTiles.length; i += concurrency) { + const batch = borderTiles.slice(i, i + concurrency); + const actionsBatch = await Promise.all( + batch.map((tile) => player.actions(tile).then((a) => ({ tile, a }))) + ); + for (const { tile, a } of actionsBatch) { + for (const buildable of a.buildableUnits) { + const decision = this.evaluateBuildingOption(player, tile, buildable, analysis); + if (decision) recommendations.push(decision); + } + } + }
100-133
: Narrow types and rename to avoid interface shadowing
BuildDecision
here conflicts in name withActionExecutor
’sBuildDecision
(different shape). Rename toBotBuildDecision
to make intent clear.Apply this diff:
-export interface BuildDecision { +export interface BotBuildDecision { tile: TileRef; unitType: UnitType; priority: number; reasoning: string; canUpgrade: number | false; existingUnitId?: number; cost: bigint; } -export interface BuildAnalysis { - recommendations: BuildDecision[]; +export interface BuildAnalysis { + recommendations: BotBuildDecision[]; totalCost: bigint; - immediateBuilds: BuildDecision[]; - futureBuilds: BuildDecision[]; + immediateBuilds: BotBuildDecision[]; + futureBuilds: BotBuildDecision[]; }And update references in this file accordingly. No runtime change; just clarity.
169-173
: Prefer integers for prioritiesMultiplying by
1.2
turns priorities into floats. Consider using integer math (e.g.,priority = Math.round(priority * 12 / 10)
) to keep types simple.
371-387
: Replace stubbed SAM “important asset” check or mark TODOThis block always returns
false
. Add a TODO with a small plan, or wire a simple check for own City/Factory neighbors.Example:
- // Check if neighbor has important buildings - // This would require additional game state analysis - return false; // Simplified for now + // TODO: consider own Cities/Factories nearby as “important” + const unit = this.gameView.unit(this.gameView.cell(neighbor).unitId ?? -1); + return unit?.owner().smallID() === player.smallID() && + (unit.type() === UnitType.City || unit.type() === UnitType.Factory);
122-133
: Reason strings: make them specific“Building X for strategic value” is vague. Include one or two top reasons (e.g., “border tile + low gold income”).
src/client/bot/execution/ActionExecutor.ts (3)
229-267
: Prefer a discriminated union for batch items; avoidany
Gives compile-time safety and simpler switches.
Minimal change:
- public executeBatch( - decisions: Array<{ - type: "attack" | "build" | "alliance" | "boat" | "spawn"; - decision: any; - }>, - ): void { + public executeBatch(decisions: DecisionBatchItem[]): void {Add near the interfaces section:
type DecisionBatchItem = | { type: "attack"; decision: AttackDecision } | { type: "build"; decision: BuildDecision } | { type: "alliance"; decision: AllianceDecision } | { type: "boat"; decision: BoatAttackDecision } | { type: "spawn"; decision: TileRef };
27-33
: Reuse the shared BuildDecision type to avoid driftThere’s already a richer
BuildDecision
instrategy/BuildStrategy.ts
. Import and reuse it instead of redefining.-export interface BuildDecision { - unitType: UnitType; - tile: TileRef; - priority: number; - reasoning: string; -} +// At top of file (imports) +// import type { BuildDecision } from "../strategy/BuildStrategy"; +// Then remove this local interface.
172-183
: Optional: validate emoji idPrevent out-of-range or NaN values, if the protocol defines a range.
public executeEmoji( recipient: PlayerView, emoji: number, reasoning: string, ): void { + if (!Number.isInteger(emoji) || emoji < 0) { + console.error(`ActionExecutor: Invalid emoji id: ${emoji}`); + return; + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (12)
HOW_TO_RUN_THE_BOT.md
(1 hunks)src/client/ClientGameRunner.ts
(4 hunks)src/client/SinglePlayerModal.ts
(3 hunks)src/client/bot/PlayerBot.ts
(1 hunks)src/client/bot/analysis/GameStateAnalyzer.ts
(1 hunks)src/client/bot/execution/ActionExecutor.ts
(1 hunks)src/client/bot/integration/BotIntegration.ts
(1 hunks)src/client/bot/strategy/BuildStrategy.ts
(1 hunks)src/client/bot/strategy/SpawnStrategy.ts
(1 hunks)src/client/bot/ui/BotControlPanel.ts
(1 hunks)src/client/graphics/GameRenderer.ts
(2 hunks)src/client/index.html
(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
PR: openfrontio/OpenFrontIO#1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.
Applied to files:
src/client/graphics/GameRenderer.ts
src/client/bot/ui/BotControlPanel.ts
🧬 Code graph analysis (8)
src/client/bot/ui/BotControlPanel.ts (3)
src/client/bot/PlayerBot.ts (1)
BotStatus
(12-19)src/client/ClientGameRunner.ts (1)
getBotIntegration
(678-680)src/client/bot/integration/BotIntegration.ts (1)
getBotIntegration
(179-181)
src/client/bot/execution/ActionExecutor.ts (5)
src/core/game/Game.ts (1)
PlayerID
(14-14)src/client/bot/strategy/BuildStrategy.ts (1)
BuildDecision
(6-14)src/core/game/GameMap.ts (1)
TileRef
(3-3)src/core/EventBus.ts (1)
EventBus
(7-44)src/client/Transport.ts (11)
SendSpawnIntentEvent
(68-70)SendAttackIntentEvent
(72-77)SendBoatAttackIntentEvent
(79-86)BuildUnitIntentEvent
(88-93)SendAllianceRequestIntentEvent
(34-39)SendAllianceReplyIntentEvent
(55-62)SendBreakAllianceIntentEvent
(41-46)SendTargetPlayerIntentEvent
(95-97)SendEmojiIntentEvent
(99-104)SendDonateGoldIntentEvent
(106-111)SendDonateTroopsIntentEvent
(113-118)
src/client/bot/PlayerBot.ts (6)
src/client/bot/analysis/GameStateAnalyzer.ts (2)
GameStateAnalyzer
(82-618)GameStateAnalysis
(71-80)src/client/bot/strategy/SpawnStrategy.ts (1)
SpawnStrategy
(24-348)src/client/bot/strategy/BuildStrategy.ts (1)
BuildStrategy
(23-435)src/client/bot/execution/ActionExecutor.ts (1)
ActionExecutor
(48-268)src/core/game/GameView.ts (3)
GameView
(364-696)myPlayer
(509-512)PlayerView
(180-362)src/core/EventBus.ts (1)
EventBus
(7-44)
src/client/bot/strategy/BuildStrategy.ts (6)
src/client/bot/execution/ActionExecutor.ts (1)
BuildDecision
(27-32)src/core/game/GameMap.ts (1)
TileRef
(3-3)src/client/bot/analysis/GameStateAnalyzer.ts (1)
GameStateAnalysis
(71-80)src/core/game/PlayerImpl.ts (2)
buildableUnits
(875-894)canBuild
(896-943)src/core/game/Game.ts (1)
BuildableUnit
(723-729)src/client/bot/strategy/SpawnStrategy.ts (1)
hasWaterAccess
(250-269)
src/client/ClientGameRunner.ts (1)
src/client/bot/integration/BotIntegration.ts (2)
initializeBotIntegration
(144-174)getBotIntegration
(179-181)
src/client/bot/strategy/SpawnStrategy.ts (3)
src/core/game/GameMap.ts (1)
TileRef
(3-3)src/core/game/GameView.ts (1)
GameView
(364-696)src/client/bot/analysis/GameStateAnalyzer.ts (1)
GameStateAnalyzer
(82-618)
src/client/bot/integration/BotIntegration.ts (4)
src/client/bot/PlayerBot.ts (2)
PlayerBot
(30-426)BotStatus
(12-19)src/core/game/GameView.ts (1)
GameView
(364-696)src/core/EventBus.ts (1)
EventBus
(7-44)src/client/ClientGameRunner.ts (1)
getBotIntegration
(678-680)
src/client/bot/analysis/GameStateAnalyzer.ts (3)
src/core/game/GameMap.ts (1)
TileRef
(3-3)src/core/game/GameView.ts (2)
PlayerView
(180-362)GameView
(364-696)src/core/game/Game.ts (1)
Gold
(16-16)
🪛 LanguageTool
HOW_TO_RUN_THE_BOT.md
[grammar] ~1-~1: There might be a mistake here.
Context: # 🤖 How to Run the OpenFront AI Bot The AI bot has been fully integrated...
(QB_NEW_EN)
[grammar] ~5-~5: There might be a mistake here.
Context: ... to run it: ## 🚀 Quick Start (2 steps) ### 1. Start OpenFrontIO ```bash npm start ...
(QB_NEW_EN)
[grammar] ~19-~19: There might be a mistake here.
Context: ...e game The bot is now available! 🎉 ## 🎮 Control the Bot ### Option A: Visual...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...w available!** 🎉 ## 🎮 Control the Bot ### Option A: Visual UI (Easiest) 1. **Look...
(QB_NEW_EN)
[grammar] ~25-~25: There might be a mistake here.
Context: ...on** in the top-right corner of the game 2. Click the icon to open the bot control...
(QB_NEW_EN)
[grammar] ~28-~28: There might be a mistake here.
Context: ...ance 4. Watch the bot play! It will: - Automatically select optimal spawn locat...
(QB_NEW_EN)
[grammar] ~29-~29: There might be a mistake here.
Context: ...matically select optimal spawn locations - Manage resources and troops - Make st...
(QB_NEW_EN)
[grammar] ~32-~32: There might be a mistake here.
Context: ...c decisions - Show real-time analysis ### Option B: Browser Console 1. **Open bro...
(QB_NEW_EN)
[grammar] ~56-~56: There might be a mistake here.
Context: ...ess: 80 }); ``` ## 🎯 What the Bot Does ### ✅ Fully Working Features - **Smart Spaw...
(QB_NEW_EN)
[grammar] ~65-~65: There might be a mistake here.
Context: ...xplanations ### 🔄 Basic Implementation - Threat Detection: Identifies incoming ...
(QB_NEW_EN)
[grammar] ~71-~71: There might be a mistake here.
Context: ...uture Enhancements (Not Yet Implemented) - Full attack execution - Unit constructio...
(QB_NEW_EN)
[grammar] ~73-~73: There might be a mistake here.
Context: ...et Implemented) - Full attack execution - Unit construction - Naval operations - N...
(QB_NEW_EN)
[grammar] ~74-~74: There might be a mistake here.
Context: ...ull attack execution - Unit construction - Naval operations - Nuclear weapons - Adv...
(QB_NEW_EN)
[grammar] ~75-~75: There might be a mistake here.
Context: ...n - Unit construction - Naval operations - Nuclear weapons - Advanced diplomacy ##...
(QB_NEW_EN)
[grammar] ~76-~76: There might be a mistake here.
Context: ...ion - Naval operations - Nuclear weapons - Advanced diplomacy ## 🔧 Configuration ...
(QB_NEW_EN)
[grammar] ~79-~79: There might be a mistake here.
Context: ... Advanced diplomacy ## 🔧 Configuration Customize bot behavior from the console:...
(QB_NEW_EN)
[grammar] ~92-~92: There might be a mistake here.
Context: ...ggressive }); ``` ## 🐛 Troubleshooting ### Bot Icon Not Showing? - Make sure you'r...
(QB_NEW_EN)
[grammar] ~96-~96: There might be a mistake here.
Context: ... sure you're in a singleplayer game (bot doesn't work in multiplayer) - Check...
(QB_NEW_EN)
[grammar] ~103-~103: There might be a mistake here.
Context: ...force a decision - Check if the game is in spawn phase (bot waits for spawn to com...
(QB_NEW_EN)
[grammar] ~103-~103: There might be a mistake here.
Context: ...n - Check if the game is in spawn phase (bot waits for spawn to complete) ### Wa...
(QB_NEW_EN)
[grammar] ~103-~103: There might be a mistake here.
Context: ...f the game is in spawn phase (bot waits for spawn to complete) ### Want More Debug...
(QB_NEW_EN)
[grammar] ~115-~115: There might be a mistake here.
Context: ...status(); ## 🎭 Example Bot Session
1. Game starts → Bot icon (🤖) appears in top-right 2. Click icon → Control panel opens 3. Click "Start Bot" → Bot begins analysis 4. Spawn phase → Bot automatically selects best spawn location 5. Early game → Bot manages resources and looks for expansion 6. Mid game → Bot makes strategic decisions based on threats/opportunities ``` ## 🏆 Advanced Usage ### Multiple Difficul...
(QB_NEW_EN)
[grammar] ~126-~126: There might be a mistake here.
Context: .../opportunities ``` ## 🏆 Advanced Usage ### Multiple Difficulty Levels - Easy: ...
(QB_NEW_EN)
[grammar] ~130-~130: There might be a mistake here.
Context: ...eaceful, slow, high confidence threshold - Medium: Balanced approach (default) - ...
(QB_NEW_EN)
[grammar] ~131-~131: There might be a mistake here.
Context: ... Medium: Balanced approach (default) - Hard: Aggressive, fast decisions, nucl...
(QB_NEW_EN)
[grammar] ~132-~132: There might be a mistake here.
Context: ... fast decisions, nuclear weapons enabled - Expert: Very aggressive, risky plays, ...
(QB_NEW_EN)
[style] ~146-~146: Using many exclamation marks might seem excessive (in this case: 5 exclamation marks for a text that’s 2491 characters long)
Context: ...ayer game and click the 🤖 icon to begin! 🎮
(EN_EXCESSIVE_EXCLAMATION)
[grammar] ~146-~146: There might be a mistake here.
Context: ... game and click the 🤖 icon to begin! 🎮
(QB_NEW_EN)
🪛 markdownlint-cli2 (0.17.2)
HOW_TO_RUN_THE_BOT.md
117-117: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
139-139: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🪛 GitHub Actions: 🧪 CI
src/client/bot/execution/ActionExecutor.ts
[error] 15-15: TS2305: Module '../../Transport' has no exported member 'SendSetTargetTroopRatioEvent'.
[warning] 107-107: WARNING in ./src/client/bot/execution/ActionExecutor.ts 107:31-59 export 'SendSetTargetTroopRatioEvent' (imported as 'SendSetTargetTroopRatioEvent') was not found in '../../Transport' (possible exports: BuildUnitIntentEvent, CancelAttackIntentEvent, CancelBoatIntentEvent, MoveWarshipIntentEvent, PauseGameEvent, SendAllianceExtensionIntentEvent, SendAllianceReplyIntentEvent, SendAllianceRequestIntentEvent, SendAttackIntentEvent, SendBoatAttackIntentEvent, SendBreakAllianceIntentEvent, SendDeleteUnitIntentEvent, SendDonateGoldIntentEvent, SendDonateTroopsIntentEvent, SendEmbargoIntentEvent, SendEmojiIntentEvent, SendHashEvent, SendKickPlayerIntentEvent, SendQuickChatEvent, SendSpawnIntentEvent, SendTargetPlayerIntentEvent, SendUpgradeStructureIntentEvent, SendWinnerEvent, Transport).
src/client/bot/strategy/BuildStrategy.ts
[error] 242-242: TS2339: Property 'workers' does not exist on type 'PlayerView'.
src/client/bot/analysis/GameStateAnalyzer.ts
[error] 198-198: TS2339: Property 'population' does not exist on type 'PlayerView'.
[error] 199-199: TS2339: Property 'workers' does not exist on type 'PlayerView'.
[error] 201-201: TS2339: Property 'targetTroopRatio' does not exist on type 'PlayerView'.
[error] 361-361: TS2339: Property 'workers' does not exist on type 'PlayerView'.
[error] 388-388: TS2339: Property 'population' does not exist on type 'PlayerView'.
[error] 403-403: TS2339: Property 'population' does not exist on type 'PlayerView'.
[error] 405-405: TS2339: Property 'workers' does not exist on type 'PlayerView'.
[error] 450-450: TS2339: Property 'targetTroopRatio' does not exist on type 'PlayerView'.
[error] 495-495: TS2339: Property 'targetTroopRatio' does not exist on type 'PlayerView'.
🔇 Additional comments (4)
src/client/SinglePlayerModal.ts (1)
224-229
: LGTM: slider bound uses the new maxUsing
max=${this.maxBots}
keeps UI and validation consistent.src/client/bot/analysis/GameStateAnalyzer.ts (2)
175-186
: Double-check Relation enum orderingThe threshold checks assume ordinal ordering (e.g., Friendly > Neutral > Hostile). Confirm enum values or switch to explicit sets to avoid misclassification.
Would you like a quick refactor to use explicit sets (e.g.,
isHostile = relation === Relation.Hostile || relation === Relation.War
)?Also applies to: 337-342
550-553
: LGTM: phase detectionUsing
inSpawnPhase()
is the right source of truth.src/client/bot/execution/ActionExecutor.ts (1)
54-57
: Spawn looks goodEvent emission and logging are straightforward.
### 🚧 Future Enhancements (Not Yet Implemented) | ||
|
||
- Full attack execution | ||
- Unit construction | ||
- Naval operations | ||
- Nuclear weapons | ||
- Advanced diplomacy | ||
|
||
## 🔧 Configuration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doc conflict: “Nuclear weapons” listed as future work but also “enabled” on Hard
These two sections contradict each other. Please align them (either move “nuclear weapons” out of Hard or out of “Future Enhancements”).
- **Hard**: Aggressive, fast decisions, nuclear weapons enabled
+ **Hard**: Aggressive, fast decisions
Also applies to: 130-134
🧰 Tools
🪛 LanguageTool
[grammar] ~71-~71: There might be a mistake here.
Context: ...uture Enhancements (Not Yet Implemented) - Full attack execution - Unit constructio...
(QB_NEW_EN)
[grammar] ~73-~73: There might be a mistake here.
Context: ...et Implemented) - Full attack execution - Unit construction - Naval operations - N...
(QB_NEW_EN)
[grammar] ~74-~74: There might be a mistake here.
Context: ...ull attack execution - Unit construction - Naval operations - Nuclear weapons - Adv...
(QB_NEW_EN)
[grammar] ~75-~75: There might be a mistake here.
Context: ...n - Unit construction - Naval operations - Nuclear weapons - Advanced diplomacy ##...
(QB_NEW_EN)
[grammar] ~76-~76: There might be a mistake here.
Context: ...ion - Naval operations - Nuclear weapons - Advanced diplomacy ## 🔧 Configuration ...
(QB_NEW_EN)
[grammar] ~79-~79: There might be a mistake here.
Context: ... Advanced diplomacy ## 🔧 Configuration Customize bot behavior from the console:...
(QB_NEW_EN)
🤖 Prompt for AI Agents
In HOW_TO_RUN_THE_BOT.md around lines 71-79 (and also lines 130-134), the
document lists "Nuclear weapons" both as a future enhancement and as enabled in
the Hard configuration; remove the contradiction by deleting "Nuclear weapons"
from the "Future Enhancements" list and ensure the Hard configuration section
clearly documents that nuclear weapons are enabled (or, if the intended state is
disabled, instead remove/mark it disabled in the Hard config). Update both
locations (71-79 and 130-134) so only one place asserts the feature's status and
add a short note clarifying its current availability.
public analyzeResources(player: PlayerView): ResourceAnalysis { | ||
const gold = player.gold(); | ||
const population = player.population(); | ||
const workers = player.workers(); | ||
const troops = player.troops(); | ||
const targetTroopRatio = player.targetTroopRatio(); | ||
|
||
// Estimate rates (would need historical data for accuracy) | ||
const goldPerTick = this.estimateGoldPerTick(player); | ||
const maxPopulation = this.estimateMaxPopulation(player); | ||
const populationGrowthRate = this.estimatePopulationGrowthRate(player); | ||
|
||
const isResourceConstrained = this.isResourceConstrained(player); | ||
// Calculate troop ratio without circular dependencies | ||
const recommendedTroopRatio = this.calculateRecommendedTroopRatioSafe( | ||
player, | ||
isResourceConstrained, | ||
); | ||
|
||
return { | ||
gold, | ||
goldPerTick, | ||
population, | ||
workers, | ||
troops, | ||
targetTroopRatio, | ||
maxPopulation, | ||
populationGrowthRate, | ||
isResourceConstrained, | ||
recommendedTroopRatio, | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix TS errors: PlayerView has no population/workers/targetTroopRatio
Replace nonexistent calls with safe estimates so CI passes.
public analyzeResources(player: PlayerView): ResourceAnalysis {
const gold = player.gold();
- const population = player.population();
- const workers = player.workers();
- const troops = player.troops();
- const targetTroopRatio = player.targetTroopRatio();
+ const population = this.estimateCurrentPopulation(player);
+ const troops = player.troops();
+ const workers = Math.max(0, population - troops);
+ const targetTroopRatio = this.estimateCurrentTroopRatio(player, population);
// Estimate rates (would need historical data for accuracy)
const goldPerTick = this.estimateGoldPerTick(player);
const maxPopulation = this.estimateMaxPopulation(player);
- const populationGrowthRate = this.estimatePopulationGrowthRate(player);
+ const populationGrowthRate = this.estimatePopulationGrowthRate(player);
const isResourceConstrained = this.isResourceConstrained(player);
// Calculate troop ratio without circular dependencies
const recommendedTroopRatio = this.calculateRecommendedTroopRatioSafe(
player,
isResourceConstrained,
);
return {
gold,
goldPerTick,
population,
workers,
troops,
targetTroopRatio,
maxPopulation,
populationGrowthRate,
isResourceConstrained,
recommendedTroopRatio,
};
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
public analyzeResources(player: PlayerView): ResourceAnalysis { | |
const gold = player.gold(); | |
const population = player.population(); | |
const workers = player.workers(); | |
const troops = player.troops(); | |
const targetTroopRatio = player.targetTroopRatio(); | |
// Estimate rates (would need historical data for accuracy) | |
const goldPerTick = this.estimateGoldPerTick(player); | |
const maxPopulation = this.estimateMaxPopulation(player); | |
const populationGrowthRate = this.estimatePopulationGrowthRate(player); | |
const isResourceConstrained = this.isResourceConstrained(player); | |
// Calculate troop ratio without circular dependencies | |
const recommendedTroopRatio = this.calculateRecommendedTroopRatioSafe( | |
player, | |
isResourceConstrained, | |
); | |
return { | |
gold, | |
goldPerTick, | |
population, | |
workers, | |
troops, | |
targetTroopRatio, | |
maxPopulation, | |
populationGrowthRate, | |
isResourceConstrained, | |
recommendedTroopRatio, | |
}; | |
} | |
public analyzeResources(player: PlayerView): ResourceAnalysis { | |
const gold = player.gold(); | |
const population = this.estimateCurrentPopulation(player); | |
const troops = player.troops(); | |
const workers = Math.max(0, population - troops); | |
const targetTroopRatio = this.estimateCurrentTroopRatio(player, population); | |
// Estimate rates (would need historical data for accuracy) | |
const goldPerTick = this.estimateGoldPerTick(player); | |
const maxPopulation = this.estimateMaxPopulation(player); | |
const populationGrowthRate = this.estimatePopulationGrowthRate(player); | |
const isResourceConstrained = this.isResourceConstrained(player); | |
// Calculate troop ratio without circular dependencies | |
const recommendedTroopRatio = this.calculateRecommendedTroopRatioSafe( | |
player, | |
isResourceConstrained, | |
); | |
return { | |
gold, | |
goldPerTick, | |
population, | |
workers, | |
troops, | |
targetTroopRatio, | |
maxPopulation, | |
populationGrowthRate, | |
isResourceConstrained, | |
recommendedTroopRatio, | |
}; | |
} |
🧰 Tools
🪛 GitHub Actions: 🧪 CI
[error] 198-198: TS2339: Property 'population' does not exist on type 'PlayerView'.
[error] 199-199: TS2339: Property 'workers' does not exist on type 'PlayerView'.
[error] 201-201: TS2339: Property 'targetTroopRatio' does not exist on type 'PlayerView'.
private getNeighboringPlayers(player: PlayerView): PlayerView[] { | ||
return this.gameView | ||
.players() | ||
.filter( | ||
(p) => | ||
p.id() !== player.id() && p.isAlive() && p.type() !== PlayerType.Bot, | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not exclude bots from neighbor analysis
In singleplayer, all neighbors are bots; this filter empties analysis and weakens decisions.
private getNeighboringPlayers(player: PlayerView): PlayerView[] {
return this.gameView
.players()
.filter(
(p) =>
- p.id() !== player.id() && p.isAlive() && p.type() !== PlayerType.Bot,
+ p.id() !== player.id() && p.isAlive(),
);
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private getNeighboringPlayers(player: PlayerView): PlayerView[] { | |
return this.gameView | |
.players() | |
.filter( | |
(p) => | |
p.id() !== player.id() && p.isAlive() && p.type() !== PlayerType.Bot, | |
); | |
} | |
private getNeighboringPlayers(player: PlayerView): PlayerView[] { | |
return this.gameView | |
.players() | |
.filter( | |
(p) => | |
p.id() !== player.id() && p.isAlive(), | |
); | |
} |
🤖 Prompt for AI Agents
In src/client/bot/analysis/GameStateAnalyzer.ts around lines 295 to 302, the
neighbor-selection currently filters out bots which causes an empty neighbor
list in singleplayer; remove the restriction that excludes PlayerType.Bot so
neighbors include all alive players except the subject. Update the filter to
only exclude the same player and require isAlive() (remove the p.type() !==
PlayerType.Bot check) so bot opponents are included in neighbor analysis.
private estimateGoldPerTick(player: PlayerView): number { | ||
// More accurate gold calculation | ||
const workers = player.workers(); | ||
const territories = player.numTilesOwned(); | ||
const cities = player.units(UnitType.City).length; | ||
const factories = player.units(UnitType.Factory).length; | ||
const tradePorts = player.units(UnitType.Port).length; | ||
|
||
// Base gold per worker (affected by buildings) | ||
const goldPerWorker = 2 + factories * 0.5; // Factories boost worker efficiency | ||
const territoryBonus = territories * 0.3; // Small gold per territory | ||
const cityBonus = cities * 5; // Cities provide significant gold | ||
const tradeBonus = tradePorts * 3; // Ports enable trade | ||
|
||
return workers * goldPerWorker + territoryBonus + cityBonus + tradeBonus; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove nonexistent workers() usage in gold-per-tick calc
Compute income from territory/buildings only.
private estimateGoldPerTick(player: PlayerView): number {
- // More accurate gold calculation
- const workers = player.workers();
const territories = player.numTilesOwned();
const cities = player.units(UnitType.City).length;
const factories = player.units(UnitType.Factory).length;
const tradePorts = player.units(UnitType.Port).length;
- // Base gold per worker (affected by buildings)
- const goldPerWorker = 2 + factories * 0.5; // Factories boost worker efficiency
- const territoryBonus = territories * 0.3; // Small gold per territory
- const cityBonus = cities * 5; // Cities provide significant gold
- const tradeBonus = tradePorts * 3; // Ports enable trade
-
- return workers * goldPerWorker + territoryBonus + cityBonus + tradeBonus;
+ const territoryBonus = territories * 0.5;
+ const cityBonus = cities * 5;
+ const factoryBonus = factories * 2;
+ const tradeBonus = tradePorts * 3;
+ return territoryBonus + cityBonus + factoryBonus + tradeBonus;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private estimateGoldPerTick(player: PlayerView): number { | |
// More accurate gold calculation | |
const workers = player.workers(); | |
const territories = player.numTilesOwned(); | |
const cities = player.units(UnitType.City).length; | |
const factories = player.units(UnitType.Factory).length; | |
const tradePorts = player.units(UnitType.Port).length; | |
// Base gold per worker (affected by buildings) | |
const goldPerWorker = 2 + factories * 0.5; // Factories boost worker efficiency | |
const territoryBonus = territories * 0.3; // Small gold per territory | |
const cityBonus = cities * 5; // Cities provide significant gold | |
const tradeBonus = tradePorts * 3; // Ports enable trade | |
return workers * goldPerWorker + territoryBonus + cityBonus + tradeBonus; | |
} | |
private estimateGoldPerTick(player: PlayerView): number { | |
const territories = player.numTilesOwned(); | |
const cities = player.units(UnitType.City).length; | |
const factories = player.units(UnitType.Factory).length; | |
const tradePorts = player.units(UnitType.Port).length; | |
const territoryBonus = territories * 0.5; | |
const cityBonus = cities * 5; | |
const factoryBonus = factories * 2; | |
const tradeBonus = tradePorts * 3; | |
return territoryBonus + cityBonus + factoryBonus + tradeBonus; | |
} |
🧰 Tools
🪛 GitHub Actions: 🧪 CI
[error] 361-361: TS2339: Property 'workers' does not exist on type 'PlayerView'.
🤖 Prompt for AI Agents
In src/client/bot/analysis/GameStateAnalyzer.ts around lines 359 to 374, remove
the nonexistent workers() usage and change the gold-per-tick calculation to only
use territory and building income: delete the workers variable and goldPerWorker
computation and return only territoryBonus + cityBonus + tradeBonus (keeping
factories factored into any building-related bonuses if needed) so the function
no longer references player.workers().
private estimatePopulationGrowthRate(player: PlayerView): number { | ||
const current = player.population(); | ||
const max = this.estimateMaxPopulation(player); | ||
const cities = player.units(UnitType.City).length; | ||
|
||
if (current >= max) return 0; | ||
|
||
const growthRatio = (max - current) / max; | ||
const cityBonus = cities * 2; // Cities boost growth rate | ||
const baseGrowth = 8; | ||
|
||
return Math.min(20, baseGrowth * growthRatio + cityBonus); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace population() with an estimate
Avoid calling a missing API.
private estimatePopulationGrowthRate(player: PlayerView): number {
- const current = player.population();
+ const current = this.estimateCurrentPopulation(player);
const max = this.estimateMaxPopulation(player);
const cities = player.units(UnitType.City).length;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private estimatePopulationGrowthRate(player: PlayerView): number { | |
const current = player.population(); | |
const max = this.estimateMaxPopulation(player); | |
const cities = player.units(UnitType.City).length; | |
if (current >= max) return 0; | |
const growthRatio = (max - current) / max; | |
const cityBonus = cities * 2; // Cities boost growth rate | |
const baseGrowth = 8; | |
return Math.min(20, baseGrowth * growthRatio + cityBonus); | |
} | |
private estimatePopulationGrowthRate(player: PlayerView): number { | |
const current = this.estimateCurrentPopulation(player); | |
const max = this.estimateMaxPopulation(player); | |
const cities = player.units(UnitType.City).length; | |
if (current >= max) return 0; | |
const growthRatio = (max - current) / max; | |
const cityBonus = cities * 2; // Cities boost growth rate | |
const baseGrowth = 8; | |
return Math.min(20, baseGrowth * growthRatio + cityBonus); | |
} |
🧰 Tools
🪛 GitHub Actions: 🧪 CI
[error] 388-388: TS2339: Property 'population' does not exist on type 'PlayerView'.
🤖 Prompt for AI Agents
In src/client/bot/analysis/GameStateAnalyzer.ts around lines 387 to 399, the
code calls a non-existent player.population() API; replace that call with a
local estimate method (e.g., this.estimatePopulation(player)) or directly
compute the population from known player data (sum unit population values and
city populations / sizes), ensure estimatePopulation(player) is implemented on
this class (or available on PlayerView), and use that estimated value in the
existing growth-rate calculation so the function no longer depends on a missing
API.
if (!this.isVisible) { | ||
return html` | ||
<div | ||
class="show-btn" | ||
@click=${this.toggleVisibility} | ||
title="Show Bot Control Panel" | ||
> | ||
🤖 ${this.botStatus.isEnabled ? "✅" : "⏸️"} | ||
</div> | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Localize the “show” button title and make it accessible.
- <div
- class="show-btn"
- @click=${this.toggleVisibility}
- title="Show Bot Control Panel"
- >
+ <div
+ class="show-btn"
+ @click=${this.toggleVisibility}
+ title=${translateText("bot.show_panel")}
+ aria-label=${translateText("bot.show_panel")}
+ role="button"
+ tabindex="0"
+ @keydown=${(e: KeyboardEvent) => (e.key === "Enter" || e.key === " ") && this.toggleVisibility()}
+ >
🤖 ${this.botStatus.isEnabled ? "✅" : "⏸️"}
</div>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if (!this.isVisible) { | |
return html` | |
<div | |
class="show-btn" | |
@click=${this.toggleVisibility} | |
title="Show Bot Control Panel" | |
> | |
🤖 ${this.botStatus.isEnabled ? "✅" : "⏸️"} | |
</div> | |
`; | |
if (!this.isVisible) { | |
return html` | |
<div | |
class="show-btn" | |
@click=${this.toggleVisibility} | |
title=${translateText("bot.show_panel")} | |
aria-label=${translateText("bot.show_panel")} | |
role="button" | |
tabindex="0" | |
@keydown=${(e: KeyboardEvent) => | |
(e.key === "Enter" || e.key === " ") && this.toggleVisibility()} | |
> | |
🤖 ${this.botStatus.isEnabled ? "✅" : "⏸️"} | |
</div> | |
`; |
🤖 Prompt for AI Agents
In src/client/bot/ui/BotControlPanel.ts around lines 167–176, the "show" button
title is hardcoded and uses a non-semantic div; replace it with a semantic,
accessible control and use the app's i18n/localization function for the
title/label. Specifically, change the div to a <button> (or add role="button"
and tabindex if you must keep an element) and set its title and aria-label to a
localized string (e.g., t('botControl.showPanel')), add an accessible state
attribute like aria-pressed={this.botStatus.isEnabled} (or aria-expanded if
appropriate), keep the @click handler, and ensure the element is
keyboard-focusable so screen readers and keyboard users can operate it.
<h3 style="margin: 0 0 8px 0; font-size: 14px;">🤖 AI Assistant</h3> | ||
<button | ||
class="toggle-btn ${this.botStatus.isEnabled ? "stop" : ""}" | ||
@click=${this.toggleBot} | ||
> | ||
${this.botStatus.isEnabled ? "Stop Bot" : "Start Bot"} | ||
</button> | ||
<div class="status-line"> | ||
<strong>Phase:</strong> ${this.botStatus.currentPhase} | ||
</div> | ||
<div class="status-line"> | ||
<strong>Status:</strong> ${this.botStatus.isActive | ||
? "Active" | ||
: "Idle"} | ||
</div> | ||
<div class="status-line"> | ||
<strong>Decision:</strong> ${this.botStatus.lastDecision} | ||
</div> | ||
<div class="status-line"> | ||
<strong>Confidence:</strong> ${this.botStatus.confidence}% | ||
</div> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Localize static labels.
Replace hard-coded strings.
- <h3 style="margin: 0 0 8px 0; font-size: 14px;">🤖 AI Assistant</h3>
+ <h3 style="margin: 0 0 8px 0; font-size: 14px;">🤖 ${translateText("bot.panel.title")}</h3>
...
- <div class="status-line">
- <strong>Phase:</strong> ${this.botStatus.currentPhase}
+ <div class="status-line">
+ <strong>${translateText("bot.phase")}:</strong> ${this.botStatus.currentPhase}
</div>
...
- <div class="status-line">
- <strong>Status:</strong> ${this.botStatus.isActive ? "Active" : "Idle"}
+ <div class="status-line">
+ <strong>${translateText("bot.status")}:</strong> ${this.botStatus.isActive ? translateText("bot.active") : translateText("bot.idle")}
</div>
...
- <div class="status-line">
- <strong>Decision:</strong> ${this.botStatus.lastDecision}
+ <div class="status-line">
+ <strong>${translateText("bot.decision")}:</strong> ${this.botStatus.lastDecision}
</div>
...
- <div class="status-line">
- <strong>Confidence:</strong> ${this.botStatus.confidence}%
+ <div class="status-line">
+ <strong>${translateText("bot.confidence")}:</strong> ${this.botStatus.confidence}%
</div>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<h3 style="margin: 0 0 8px 0; font-size: 14px;">🤖 AI Assistant</h3> | |
<button | |
class="toggle-btn ${this.botStatus.isEnabled ? "stop" : ""}" | |
@click=${this.toggleBot} | |
> | |
${this.botStatus.isEnabled ? "Stop Bot" : "Start Bot"} | |
</button> | |
<div class="status-line"> | |
<strong>Phase:</strong> ${this.botStatus.currentPhase} | |
</div> | |
<div class="status-line"> | |
<strong>Status:</strong> ${this.botStatus.isActive | |
? "Active" | |
: "Idle"} | |
</div> | |
<div class="status-line"> | |
<strong>Decision:</strong> ${this.botStatus.lastDecision} | |
</div> | |
<div class="status-line"> | |
<strong>Confidence:</strong> ${this.botStatus.confidence}% | |
</div> | |
<h3 style="margin: 0 0 8px 0; font-size: 14px;">🤖 ${translateText("bot.panel.title")}</h3> | |
<button | |
class="toggle-btn ${this.botStatus.isEnabled ? "stop" : ""}" | |
@click=${this.toggleBot} | |
> | |
${this.botStatus.isEnabled ? "Stop Bot" : "Start Bot"} | |
</button> | |
<div class="status-line"> | |
<strong>${translateText("bot.phase")}:</strong> ${this.botStatus.currentPhase} | |
</div> | |
<div class="status-line"> | |
<strong>${translateText("bot.status")}:</strong> | |
${this.botStatus.isActive | |
? translateText("bot.active") | |
: translateText("bot.idle")} | |
</div> | |
<div class="status-line"> | |
<strong>${translateText("bot.decision")}:</strong> ${this.botStatus.lastDecision} | |
</div> | |
<div class="status-line"> | |
<strong>${translateText("bot.confidence")}:</strong> ${this.botStatus.confidence}% | |
</div> |
class="toggle-btn ${this.botStatus.isEnabled ? "stop" : ""}" | ||
@click=${this.toggleBot} | ||
> | ||
${this.botStatus.isEnabled ? "Stop Bot" : "Start Bot"} | ||
</button> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Start/Stop label should reflect running state, not “isEnabled”.
Use isActive (or integration.isRunning()) for consistency with the toggle behavior.
- <button
- class="toggle-btn ${this.botStatus.isEnabled ? "stop" : ""}"
+ <button
+ class="toggle-btn ${this.botStatus.isActive ? "stop" : ""}"
@click=${this.toggleBot}
>
- ${this.botStatus.isEnabled ? "Stop Bot" : "Start Bot"}
+ ${this.botStatus.isActive ? translateText("bot.stop") : translateText("bot.start")}
</button>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
class="toggle-btn ${this.botStatus.isEnabled ? "stop" : ""}" | |
@click=${this.toggleBot} | |
> | |
${this.botStatus.isEnabled ? "Stop Bot" : "Start Bot"} | |
</button> | |
<button | |
class="toggle-btn ${this.botStatus.isActive ? "stop" : ""}" | |
@click=${this.toggleBot} | |
> | |
${this.botStatus.isActive ? translateText("bot.stop") : translateText("bot.start")} | |
</button> |
🤖 Prompt for AI Agents
In src/client/bot/ui/BotControlPanel.ts around lines 193 to 198, the button’s
class and label currently use this.botStatus.isEnabled but should reflect the
running state; replace uses of this.botStatus.isEnabled with
this.botStatus.isActive (or call integration.isRunning() if that API is
available) for both the class conditional and the displayed text so the toggle
reflects whether the bot is actively running; ensure the click handler still
calls toggleBot but reads/sets the active/running flag rather than the enabled
flag.
${this.botStatus.recommendations.length > 0 | ||
? html` | ||
<div class="recommendations"> | ||
<strong>Recommendations:</strong> | ||
<ul style="margin: 4px 0; padding-left: 16px;"> | ||
${this.botStatus.recommendations.map( | ||
(rec) => html`<li>${rec}</li>`, | ||
)} | ||
</ul> | ||
</div> | ||
` | ||
: ""} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Localize “Recommendations” header.
- <div class="recommendations">
- <strong>Recommendations:</strong>
+ <div class="recommendations">
+ <strong>${translateText("bot.recommendations")}:</strong>
🤖 Prompt for AI Agents
In src/client/bot/ui/BotControlPanel.ts around lines 225-236 the
"Recommendations:" header is hardcoded; replace it with the project's
localization call (use the component's i18n/translation helper — e.g.
this.i18n.t('bot.recommendations') or translate('bot.recommendations')) and
render that value in the template (add the ":" either inside the localized
string or append it after the translated text) so the header is localizable and
falls back to an English default if missing.
// Initialize bot integration for singleplayer games | ||
if (this.lobby.gameStartInfo?.config.gameType === GameType.Singleplayer) { | ||
console.log("Initializing bot for singleplayer game"); | ||
initializeBotIntegration(this.gameView, this.eventBus, { | ||
autoStart: false, // Don't auto-start, let user control via console | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Init looks good; add cleanup on stop() to prevent leaks.
When the game ends, the global bot instance persists. Call cleanup to stop timers and detach listeners.
Example change (outside this hunk, in stop()):
public stop(saveFullGame: boolean = false) {
if (!this.isActive) return;
this.isActive = false;
// NEW: clean up bot integration if present
try { getBotIntegration()?.cleanup(); } catch {}
this.worker.cleanup();
this.transport.leaveGame(saveFullGame);
if (this.connectionCheckInterval) {
clearInterval(this.connectionCheckInterval);
this.connectionCheckInterval = null;
}
}
I can wire this and add a regression test that ensures no timers remain after stop().
🤖 Prompt for AI Agents
In src/client/ClientGameRunner.ts around lines 214-220, the bot integration is
initialized for singleplayer games but never cleaned up on stop; update the
stop() method to detect and cleanup the bot integration instance before tearing
down the worker/transport (call the global getBotIntegration()?.cleanup() or
equivalent inside a try/catch), then proceed with existing cleanup
(worker.cleanup(), transport.leaveGame, clearInterval for
connectionCheckInterval) to ensure timers and listeners are removed and prevent
leaks.
Uh oh!
There was an error while loading. Please reload this page.