config/constants.js

/**
 * @fileoverview ZENTRALE Konstanten-Definitionen
 * Alle Dateien verwenden diese Konstanten von hier.
 * Keine Duplikate mehr in verschiedenen Dateien!
 * 
 * Struktur:
 * - GAME_CONSTANTS: Spieler, Status, Felder
 * - AI_CONSTANTS: Suchtiefen, Algorithmus-Parameter
 * - UI_CONSTANTS: Farben, Dimensionen
 * @author Alexander Wolf
 */

// ===== GLOBALER DEBUG-CONFIG FALLBACK (FÜR ALLE DATEIEN) =====
// Falls debug-config.js noch nicht geladen ist, erstelle einen Dummy
// Dadurch funktionieren DebugConfig.log() Aufrufe überall
if (typeof window !== 'undefined' && !window.DebugConfig) {
    window.DebugConfig = {
        log: function(domain, level, message, payload) {
            // Silent fallback - wenn DebugConfig.js nicht geladen ist
            // werden Logs einfach ignoriert
        },
        shouldLog: function(domain, level) {
            // Silent fallback - kritische Logs sollten immer geloggt werden
            return (level === 'error' || level === 'critical' || level === 'warn');
        },
        config: {},
        domains: {}
    };
    // NOTE: DEBUG_CONFIG und DEBUG_DOMAINS werden NUR von debug-config.js deklariert!
    // Sie nicht hier deklarieren, um doppelte Deklarationen zu vermeiden.
}

// Universaler Fallback für DEBUG_DOMAINS (falls debug-config.js nicht lädt)
if (typeof window !== 'undefined' && !window.DEBUG_DOMAINS) {
    window.DEBUG_DOMAINS = {
        VIZ_TREE_ADAPTER_BFS: 'VIZ_TREE_ADAPTER_BFS',
        VIZ_TREE_ADAPTER_DFS: 'VIZ_TREE_ADAPTER_DFS',
        VIZ_TREE_ADAPTER_MINIMAX: 'VIZ_TREE_ADAPTER_MINIMAX'
    };
}

/**
 * IframeBridge targetOrigin — Security-Default für postMessage.
 * Für lokale Entwicklung (file://): '*' ist notwendig, da file://-Iframes origin 'null' haben.
 * Für Production: window.location.origin einsetzen um Cross-Origin-Angriffe zu verhindern.
 *
 * Hinweis: window.location.origin liefert bei file:// je nach Browser 'null' (Firefox)
 * oder 'file://' (Chrome) — beides funktioniert NICHT als postMessage targetOrigin.
 * Daher prüfen wir direkt auf das Protokoll.
 *
 * @type {string}
 */
const BRIDGE_TARGET_ORIGIN = (typeof window !== 'undefined'
    && window.location.protocol !== 'file:'
    && window.location.origin
    && window.location.origin !== 'null')
    ? window.location.origin
    : '*';

/**
 * Globale Spiel-Konstanten, die alle Spiele nutzen
 * @type {Object}
 */
const GAME_CONSTANTS = {
    // Spieler IDs
    PLAYER1: 1,          // Blau / Kreis
    PLAYER2: 2,          // Rot / Kreuz
    
    // Spiel-Status
    NONE: 0,             // Kein Gewinner / Spiel läuft
    DRAW: 3,             // Unentschieden
    
    // Feld-Status (hauptsächlich Tic-Tac-Toe)
    CELL_EMPTY: 0,
    INVALID_INDEX: -1,
    
    // Board-Größen (Tic-Tac-Toe)
    TTT_BOARD_SIZE: 9,
    TTT_GRID_WIDTH: 3,
    TTT_GRID_HEIGHT: 3,
    TTT_CENTER_INDEX: 4,
    TTT_CORNERS: [0, 2, 6, 8],
    TTT_WIN_CONDITIONS: [
        [0,1,2], [3,4,5], [6,7,8], // Horizontal
        [0,3,6], [1,4,7], [2,5,8], // Vertikal
        [0,4,8], [2,4,6]           // Diagonal
    ],
    
    // Board-Größen (Connect4)
    CONNECT4_ROWS: 6,
    CONNECT4_COLS: 7,
    CONNECT4_WIN_LENGTH: 4,
    
    // Board-Größen (3D)
    TTT_3D_DEFAULT_SIZE: 3,
    TTT_3D_MAX_SIZE: 5,
};

// ===== AI-ALGORITHMUS KONSTANTEN =====
/**
 * AI und Algorithmus-Parameter
 * @type {Object}
 */
const AI_CONSTANTS = {
    // Suchtiefe
    DEFAULT_MAX_DEPTH: 3,
    DEFAULT_SEARCH_DEPTH: 1000,
    MIN_DEPTH: 0,
    
    // Agent Typen
    AGENT_RANDOM: 'random',
    AGENT_RULEBASED: 'rulebased',
    AGENT_MINIMAX: 'minimax',
    
    // Alpha-Beta Pruning
    ALPHA_INIT: -Infinity,
    BETA_INIT: Infinity,
    
    // Transposition Table
    TRANSPOSITION_TABLE_MAX_SIZE: 100000,
};

// ===== UI & RENDERING KONSTANTEN =====
/**
 * UI-Constants für Rendering und Darstellung
 * @type {Object}
 */
const UI_CONSTANTS = {
    // Canvas Dimensionen
    CANVAS_WIDTH: 500,
    CANVAS_HEIGHT: 500,
    
    // Board Rendering
    BORDER_WIDTH: 6,
    LINE_CAP: 'round',
    BORDER_COLOR: '#2c3e50',
    
    // Farben für Spieler
    PLAYER1_COLOR: '#3498db',  // Blau (Kreis)
    PLAYER2_COLOR: '#e74c3c',  // Rot (Kreuz)
    BACKGROUND_COLOR: '#ecf0f1',
    HIGHLIGHT_COLOR: '#eafaed',
    
    // Text Rendering
    FONT_SIZE: 14,
    FONT_FAMILY: 'Arial, sans-serif',
};

// ===== DEBUG KONSTANTEN =====
/**
 * Debug-Flags für Fehlerdiagnose
 * @type {Object}
 */
const DEBUG_CONSTANTS = {
    LOG_INIT: true,           // Initialisierung loggen
    LOG_MOVES: false,          // Alle Züge loggen
    LOG_SCORING: false,        // Scoring Details loggen
    LOG_AI_DECISIONS: false,   // AI Entscheidungen loggen
};

// ===== MINIMAX-VISUALIZER KONSTANTEN =====
/**
 * Konfiguration für Minimax-/Alpha-Beta-Visualisierung.
 *
 * Enthält bewusst zentrale Schalter für boolesche Features,
 * damit Adapter und UI konsistent konfiguriert werden.
 * @type {Object}
 */
const MINIMAX_VIZ_CONSTANTS = {
    FLAGS: {
        ENABLE_TREE_EXPANSION_MINIMAX: true,
        ENABLE_TREE_EXPANSION_ALPHABETA: true,
        ENABLE_PRUNING_HIGHLIGHT: true,
        USE_ALPHABETA_MOVE_ORDERING: false,
        ENFORCE_ALPHABETA_EVAL_ORDER: false,
        DEBUG_MINIMAX_ADAPTER: false,
        DEBUG_ALPHABETA_ADAPTER: false,
    },

    VALUES: {
        WIN: 1,
        LOSS: -1,
        DRAW: 0,
    },

    TREE: {
        ROOT_DEPTH: 0,
        HIGHLIGHT_EDGE_WIDTH: 2,
    },

    UI: {
        RETRY_DELAY_MS: 120,
        MAX_RETRY_ATTEMPTS: 30,
        RETRY_LOG_INTERVAL: 5,
        PERCENT_BASE: 100,
        STATS_NUMBER_FALLBACK: 0,
    },

    RENDER: {
        VALUE_DECIMALS: 2,
        ALPHA_BETA_DECIMALS: 1,
        ALPHA_BETA_FONT_SCALE: 0.95,
    },

    COLORS: {
        ROOT_PLAYER_1: '#3498db',
        ROOT_PLAYER_2: '#e74c3c',
        EDGE_POSITIVE: '#2730ae',
        EDGE_NEGATIVE: '#c0392b',
        EDGE_NEUTRAL: '#a4ae1c',
        EDGE_PRUNED: '#7f8c8d',
    },
};

// ===== NUMERISCHE VISUALISIERUNGS-KONSTANTEN =====
/**
 * Konfiguration für numerische Knoten-Darstellung (Diagramm-Modus).
 * Verwendet für Lehrbuch-artige Minimax-/Alpha-Beta-Bäume ohne Spielfeld.
 *
 * @type {Object}
 */
const NUMERIC_VIZ_CONSTANTS = {
    /** Kreis-Rendering-Dimensionen */
    CIRCLE: {
        /** Mindestradius für Knoten-Kreise in Pixeln */
        MIN_RADIUS: 18,
        /** Padding innerhalb des Kreises für den Wert-Text */
        VALUE_PADDING: 0.6,
        /** Linienbreite des Kreisrands (Basis, wird durch scale geteilt) */
        BORDER_WIDTH: 2.5,
    },

    /** Typografie-Einstellungen */
    FONT: {
        /** Schriftgröße für Knotenwerte (Basis, wird durch scale geteilt) */
        VALUE_SIZE: 14,
        /** Schriftgröße für Rollenlabel MAX/MIN (Basis) */
        ROLE_LABEL_SIZE: 10,
        /** Schriftgröße für Alpha/Beta-Anzeige (Basis) */
        ALPHA_BETA_SIZE: 10,
        /** Schriftfamilie für Werte und Labels */
        FAMILY: 'Arial, sans-serif',
        /** Schriftfamilie für Alpha/Beta (monospace für Ausrichtung) */
        MONO_FAMILY: 'monospace',
    },

    /** Layout-Abstände */
    SPACING: {
        /** Abstand Rolle-Label über dem Kreis (Pixel) */
        ROLE_LABEL_OFFSET: 6,
        /** Abstand Alpha/Beta unter dem Kreis (Pixel) */
        ALPHA_BETA_OFFSET: 5,
        /** Zeilenabstand zwischen Alpha und Beta Zeile */
        ALPHA_BETA_LINE_GAP: 2,
    },

    /** Farben für numerische Knoten */
    COLORS: {
        /** MAX-Knoten Akzentfarbe (Rand + Rollenlabel) */
        MAX_ACCENT: '#e74c3c',
        /** MAX-Knoten Füllfarbe */
        MAX_FILL: '#fde8e8',
        /** MIN-Knoten Akzentfarbe (Rand + Rollenlabel) */
        MIN_ACCENT: '#3498db',
        /** MIN-Knoten Füllfarbe */
        MIN_FILL: '#e8f0fd',
        /** Neutrale Füllfarbe (nicht evaluiert) */
        NEUTRAL_FILL: '#ffffff',
        /** Neutrale Randfarbe */
        NEUTRAL_BORDER: '#bdc3c7',
        /** Wert-Text Farbe */
        VALUE_TEXT: '#2c3e50',
        /** Alpha/Beta-Text Farbe */
        ALPHA_BETA_TEXT: '#7f8c8d',
        /** Pruned Knoten: Füllfarbe */
        PRUNED_FILL: '#f2f2f2',
        /** Pruned Knoten: Randfarbe */
        PRUNED_BORDER: '#999999',
        /** Placeholder-Text für noch nicht evaluierte Knoten */
        PLACEHOLDER_TEXT: '#aaaaaa',
        /** Unentschieden/Draw Füllfarbe (value = 0) */
        DRAW_FILL: '#ecf0f1',
    },

    /** Kanten-Farben für algorithmische Hervorhebung */
    EDGE_COLORS: {
        DEFAULT: 'rgba(136, 136, 136, 0.55)',
        BEST: '#27ae60',
        PRUNED: '#bdc3c7',
        PROPAGATION: '#f39c12',
        ALPHA_CUTOFF: '#e74c3c',
        BETA_CUTOFF: '#3498db',
    },

    /** Kanten-Breiten */
    EDGE_WIDTHS: {
        DEFAULT: 1.25,
        BEST: 3,
        PRUNED: 1,
        CUTOFF: 2.5,
        PROPAGATION: 2,
    },
};

// ===== GLOBALE VARIABLEN (Zentrale Definition) =====
// Diese werden von tictactoe/logic.js und connect4/logic.js referenziert
// Definition hier verhindert "Identifier has already been declared" Fehler
// wenn mehrere Module geladen werden
if (typeof window !== 'undefined') {
    // Browser-Kontext: Definiere als window-Properties
    if (typeof window.NONE === 'undefined') window.NONE = GAME_CONSTANTS.NONE;
    if (typeof window.PLAYER1 === 'undefined') window.PLAYER1 = GAME_CONSTANTS.PLAYER1;
    if (typeof window.PLAYER2 === 'undefined') window.PLAYER2 = GAME_CONSTANTS.PLAYER2;
    if (typeof window.DRAW === 'undefined') window.DRAW = GAME_CONSTANTS.DRAW;
    if (typeof window.CELL_EMPTY === 'undefined') window.CELL_EMPTY = GAME_CONSTANTS.CELL_EMPTY;
    if (typeof window.INVALID_INDEX === 'undefined') window.INVALID_INDEX = GAME_CONSTANTS.INVALID_INDEX;
} else {
    // Node.js-Kontext: Definiere mit var (global-Scope)
    if (typeof NONE === 'undefined') var NONE = GAME_CONSTANTS.NONE;
    if (typeof PLAYER1 === 'undefined') var PLAYER1 = GAME_CONSTANTS.PLAYER1;
    if (typeof PLAYER2 === 'undefined') var PLAYER2 = GAME_CONSTANTS.PLAYER2;
    if (typeof DRAW === 'undefined') var DRAW = GAME_CONSTANTS.DRAW;
    if (typeof CELL_EMPTY === 'undefined') var CELL_EMPTY = GAME_CONSTANTS.CELL_EMPTY;
    if (typeof INVALID_INDEX === 'undefined') var INVALID_INDEX = GAME_CONSTANTS.INVALID_INDEX;
}