Fix AI bot turn chain for multiple bots - prevent race conditions

This commit is contained in:
sokol
2026-02-21 21:01:57 +03:00
parent 64c81da166
commit e427f1c68d
3 changed files with 435 additions and 67 deletions

View File

@@ -54,10 +54,11 @@ class GameUI {
this.gamePhase = 'movement';
this.hasMoved = false;
this.isAIThinking = false;
this.isProcessingTurn = false; // Prevent re-entrancy during turn processing
this.offsetX = 0;
this.offsetY = 0;
this.init();
}
@@ -115,7 +116,8 @@ class GameUI {
this.gamePhase = 'movement';
this.hasMoved = false;
this.isAIThinking = false;
this.isProcessingTurn = false;
// Initialize AI bots AFTER map is created
this.aiBots = {};
for (let i = 1; i <= this.playerCount; i++) {
@@ -123,16 +125,17 @@ class GameUI {
this.aiBots[i] = new AIBot(i, this.map, this);
}
}
// Initialize starting positions for all players
this.initializePlayers();
this.centerMap();
this.render();
this.createPlayerCards();
this.updateUI();
this.log(`New game started with ${this.playerCount} players!`);
console.log(`[GAME] New game started with ${this.playerCount} players`);
// Start first player's turn (AI if needed)
this.checkAndRunAITurn();
}
@@ -515,38 +518,82 @@ class GameUI {
}
async endTurn() {
if (this.gamePhase !== 'movement') return;
// Remove isAIThinking check - AI needs to call endTurn() after its move
if (this.gamePhase !== 'movement') {
console.log(`[GAME] endTurn() called but gamePhase is '${this.gamePhase}', ignoring`);
return;
}
// Apply supply
const supply = this.map.calculateSupply(this.currentPlayer);
this.distributeSupply(supply);
this.log(`Player ${this.currentPlayer} received ${supply} supply`);
// Next player
this.currentPlayer = (this.currentPlayer % this.playerCount) + 1;
this.hasMoved = false;
// Prevent re-entrancy - only one turn processing at a time
if (this.isProcessingTurn) {
console.log(`[GAME] endTurn() called but isProcessingTurn is true, ignoring`);
return;
}
this.cancelSelection();
this.updateUI();
this.render();
this.isProcessingTurn = true;
console.log(`[GAME] endTurn() - Player ${this.currentPlayer} ending turn`);
this.log(`Player ${this.currentPlayer}'s turn`);
// Check if next player is AI
this.checkAndRunAITurn();
try {
// Apply supply
const supply = this.map.calculateSupply(this.currentPlayer);
this.distributeSupply(supply);
this.log(`Player ${this.currentPlayer} received ${supply} supply`);
console.log(`[GAME] Player ${this.currentPlayer} received ${supply} supply`);
// Next player
const previousPlayer = this.currentPlayer;
this.currentPlayer = (this.currentPlayer % this.playerCount) + 1;
this.hasMoved = false;
console.log(`[GAME] Turn transition: P${previousPlayer} -> P${this.currentPlayer}`);
this.cancelSelection();
this.updateUI();
this.render();
this.log(`Player ${this.currentPlayer}'s turn`);
console.log(`[GAME] Player ${this.currentPlayer}'s turn started (${this.playerTypes[this.currentPlayer]})`);
// Check if next player is AI and await completion
await this.checkAndRunAITurn();
} catch (error) {
console.error(`[GAME] Error in endTurn():`, error);
this.log(`Error during turn transition: ${error.message}`, 'error');
} finally {
this.isProcessingTurn = false;
console.log(`[GAME] endTurn() completed for Player ${this.currentPlayer}`);
}
}
checkAndRunAITurn() {
/**
* Check if current player is AI and run their turn
* @returns {Promise<boolean>} - true if AI turn was run, false if human turn
*/
async checkAndRunAITurn() {
if (this.playerTypes[this.currentPlayer] === 'ai' && this.aiBots[this.currentPlayer]) {
console.log(`[AI] Player ${this.currentPlayer} is AI, starting turn`);
this.isAIThinking = true;
this.updateUI();
// Run AI turn and reset flag when done
this.aiBots[this.currentPlayer].playTurn().then(() => {
try {
// Run AI turn and wait for it to complete
await this.aiBots[this.currentPlayer].playTurn();
console.log(`[AI] Player ${this.currentPlayer} AI turn completed`);
return true;
} catch (error) {
console.error(`[AI] Error during Player ${this.currentPlayer} AI turn:`, error);
this.log(`AI error: ${error.message}`, 'error');
this.isAIThinking = false;
});
this.updateUI();
// Still advance to next player even on error
return true;
} finally {
// Always reset the flag when AI turn completes (success or error)
this.isAIThinking = false;
console.log(`[AI] Player ${this.currentPlayer} isAIThinking reset to false`);
}
}
console.log(`[AI] Player ${this.currentPlayer} is human, no AI turn needed`);
return false;
}
distributeSupply(supply) {