games/connect4/regular-controller.js

/**
 * @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(); 
    }
}