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