/* --- FILE: js/games/tictactoe/3d-controller.js --- */
/**
* @fileoverview Controller für 3D Tic-Tac-Toe (3x3x3)
*
* Extends BaseGameController mit 3D Board-Support und Multi-View Rendering.
* Verwaltet Slice-Views (XY-Ebenen) und isometrische Projektion.
*
* @class ThreeDGameController
* @extends BaseGameController
*/
class ThreeDGameController extends BaseGameController {
constructor() {
super('3d', 'gameCanvas');
this.isoCanvas = null;
this.axis = 'z';
this.size = 3;
}
init() {
this.isoCanvas = document.getElementById('isoCanvas');
super.init();
}
createGame() {
return new TTT3DBoard(this.size);
}
drawGame() {
TTTRenderer.draw3DSlices(this.canvas, this.game, this.axis);
TTTRenderer.drawIsoView(this.isoCanvas, this.game);
}
setSize(s) {
this.size = s;
document.querySelectorAll('.size-btn').forEach(b => {
b.classList.remove('active');
if (b.innerText.includes(s)) b.classList.add('active');
});
this.reset();
}
reset() {
// ✅ Canvas-Größe für 3D anpassen
this.canvas.width = 400;
this.canvas.height = 250; // Rechteckig für Slices
if (this.isoCanvas) {
this.isoCanvas.width = 250;
this.isoCanvas.height = 250;
}
super.reset();
}
setAxis(a) {
this.axis = a;
document.querySelectorAll('.view-btn').forEach(b => {
b.classList.remove('active');
if (b.innerText.toLowerCase().includes(
a === 'z' ? 'oben' : a === 'y' ? 'vorne' : 'seite'
)) b.classList.add('active');
});
this.drawGame();
}
coordsToMove(mx, my) {
const w = this.canvas.width, h = this.canvas.height, s = this.size;
const pad = 20;
const availW = w - (pad * 2);
const availH = h - (pad * 2);
const boxSize = Math.min(availW / s, availH);
const gap = boxSize * 0.1;
const boardS = boxSize - gap;
const startX = (w - s * boxSize) / 2 + gap / 2;
const startY = (h - boardS) / 2 + 10;
for (let k = 0; k < s; k++) {
const ox = startX + k * boxSize;
if (mx >= ox && mx <= ox + boardS && my >= startY && my <= startY + boardS) {
const c = Math.floor((mx - ox) / (boardS / s));
const r = Math.floor((my - startY) / (boardS / s));
let x, y, z;
if (this.axis === 'z') { z = k; y = r; x = c; }
else if (this.axis === 'y') { y = k; x = c; z = (s - 1) - r; }
else { x = k; y = c; z = (s - 1) - r; }
return z * (s * s) + y * s + x;
}
}
return null;
}
createAIAgent(type) {
if (type === 'random') {
return new RandomAgent();
} else if (type === 'rulebased' || type === 'rulebased_elementary') {
return new RuleBasedAgent(createStrategyTree('3d', 'elementary'));
} else if (type === 'rulebased_advanced') {
return new RuleBasedAgent(createStrategyTree('3d', '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 = `ttt:3d:${profile}`;
const heuristicFn = (typeof HeuristicRegistry !== 'undefined' && HeuristicRegistry.has(regKey))
? HeuristicRegistry.get(regKey).evaluate.bind(HeuristicRegistry.get(regKey))
: (typeof HeuristicRegistry !== 'undefined' && HeuristicRegistry.has('ttt', '3d'))
? HeuristicRegistry.get('ttt', '3d').evaluate.bind(HeuristicRegistry.get('ttt', '3d'))
: ((gameState, player) => {
if (gameState.winner === player) return 10000;
if (gameState.winner !== NONE && gameState.winner !== DRAW) return -10000;
return 0;
});
return new MinimaxAgent({
name: `Minimax 3D (${profile})`,
maxDepth: 3,
useAlphaBeta: true,
heuristicFn
});
}
return null;
}
}