/**
* @fileoverview Controller für Standard Connect4 (konfigurierbare Größe)
*
* Extends BaseGameController mit Connect4-Support und dynamischer Board-Skalierung.
* Unterstützt variable Reihen/Spalten-Konfiguration.
*
* @class Connect4RegularController
* @extends BaseGameController
*/
class Connect4RegularController extends BaseGameController {
constructor() {
super('connect4-regular', 'gameCanvas');
}
createGame() {
// Read cols/rows from UI if present, or default
const rInput = document.getElementById('rowsInput');
const cInput = document.getElementById('colsInput');
const rows = rInput ? parseInt(rInput.value) : 6;
const cols = cInput ? parseInt(cInput.value) : 7;
return new Connect4Regular(rows, cols);
}
reset() {
// Adjust canvas size for aspect ratio
// We'll try to fit it into a box (e.g. 500x500 max) keeping aspect ratio
const rInput = document.getElementById('rowsInput');
const cInput = document.getElementById('colsInput');
const rows = rInput ? parseInt(rInput.value) : 6;
const cols = cInput ? parseInt(cInput.value) : 7;
// Base size per cell = 60px?
// Or fixed width
const MAX_W = 600;
const MAX_H = 500;
// s * cols <= MAX_W -> s <= MAX_W / cols
// s * rows <= MAX_H -> s <= MAX_H / rows
const s = Math.min(MAX_W / cols, MAX_H / rows);
this.canvas.width = s * cols;
this.canvas.height = s * rows;
super.reset();
}
drawGame() {
Connect4Renderer.drawRegular(this.canvas, this.game);
}
coordsToMove(mx, my) {
const w = this.canvas.width;
const h = this.canvas.height;
const cols = this.game.cols;
const rows = this.game.rows;
const s = Math.min(w / cols, h / rows);
const offsetX = (w - s * cols) / 2;
const offsetY = (h - s * rows) / 2;
const c = Math.floor((mx - offsetX) / s);
const r = Math.floor((my - offsetY) / s);
if (c >= 0 && c < cols && r >= 0 && r < rows) {
// For Connect 4, we just need the column.
// But we should check if they clicked inside the grid.
return c;
}
return null;
}
createAIAgent(type) {
if (type === 'random') {
return new RandomAgent();
} else if (type === 'rule_simple' || type === 'rule_elementary') {
return new RuleBasedAgent(Connect4RulesLibrary.createTree('regular', 'elementary'));
} else if (type === 'rule_complex' || type === 'rule_advanced') {
return new RuleBasedAgent(Connect4RulesLibrary.createTree('regular', 'advanced'));
} else if (type.startsWith('minimax')) {
const profileMap = { minimax: 'v1_baseline', minimax_positional: 'v2_positional', minimax_aggressive: 'v3_aggressive' };
const profile = profileMap[type] || 'v1_baseline';
const regKey = `connect4:regular:${profile}`;
const cols = this.game.cols;
let depth = 5;
if (cols <= 5) depth = 7;
else if (cols <= 6) depth = 6;
else if (cols <= 7) depth = 5;
else depth = 4;
const heuristicFn = (typeof HeuristicRegistry !== 'undefined' && HeuristicRegistry.has(regKey))
? HeuristicRegistry.get(regKey).evaluate.bind(HeuristicRegistry.get(regKey))
: (typeof HeuristicRegistry !== 'undefined' && HeuristicRegistry.has('connect4', 'regular'))
? HeuristicRegistry.get('connect4', 'regular').evaluate.bind(HeuristicRegistry.get('connect4', 'regular'))
: ((gameState, player) => {
if (gameState.winner === player) return 100000;
if (gameState.winner !== NONE && gameState.winner !== DRAW) return -100000;
return 0;
});
return new MinimaxAgent({
name: `Minimax C4 (${profile})`,
maxDepth: depth,
useAlphaBeta: true,
heuristicFn
});
}
return new RandomAgent();
}
}