Files
2026-04-26 09:44:19 +02:00

588 lines
22 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Schach - KinderWelt</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
body {
background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%);
min-height: 100vh; display: flex; flex-direction: column; align-items: center;
font-family: 'Comic Sans MS', cursive, sans-serif; padding: 10px;
}
h1 { color: white; margin-bottom: 10px; font-size: 24px; }
.board-container {
background: #5D4037; padding: 10px; border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.chess-board {
display: grid; grid-template-columns: repeat(8, 45px); gap: 0;
border: 3px solid #3E2723;
}
.square {
width: 45px; height: 45px; display: flex;
align-items: center; justify-content: center; position: relative;
cursor: pointer; transition: all 0.15s;
}
.square.light { background: #F0D9B5; }
.square.dark { background: #B58863; }
.square.selected { background: #7B61FF !important; }
.square.valid-move::after {
content: ''; position: absolute; width: 12px; height: 12px;
background: rgba(123, 97, 255, 0.8); border-radius: 50%;
}
.square.valid-capture { background: #FF6B6B !important; }
.square.last-move { box-shadow: inset 0 0 0 3px #FFD93D; }
.square.check {
background: #FF4444 !important;
animation: pulse-check 0.5s infinite;
}
@keyframes pulse-check {
0%, 100% { box-shadow: inset 0 0 0 rgba(255,0,0,0); }
50% { box-shadow: inset 0 0 15px rgba(255,0,0,0.8); }
}
.piece {
font-size: 32px; cursor: grab; transition: transform 0.15s;
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
}
.piece:hover { transform: scale(1.15); }
.piece.white {
filter: drop-shadow(0 0 3px #fff) drop-shadow(0 0 5px #FFD700);
}
.piece.black {
filter: drop-shadow(0 0 3px #000);
}
.coordinates {
display: flex; justify-content: space-around; width: 360px;
color: white; font-size: 11px; margin-top: 3px;
}
.status {
background: rgba(255,255,255,0.15); padding: 12px 25px; border-radius: 25px;
color: white; margin: 12px 0; text-align: center; font-size: 15px; min-height: 45px;
}
.turn-white { color: #FFD700; font-weight: bold; }
.turn-black { color: #AAA; font-weight: bold; }
.mode-select { margin: 10px 0; }
.mode-btn {
background: rgba(255,255,255,0.2); border: 2px solid white; color: white;
padding: 8px 20px; margin: 0 5px; border-radius: 20px; cursor: pointer;
}
.mode-btn.active { background: white; color: #2d3748; }
.hint-btn {
background: #F59E0B; color: white; padding: 10px 20px;
border: none; border-radius: 10px; cursor: pointer;
font-family: inherit; font-weight: bold; font-size: 14px;
}
.promotion-modal {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 25px; border-radius: 15px; display: none; z-index: 100;
}
.promotion-piece { font-size: 50px; margin: 0 10px; cursor: pointer; }
.promotion-piece:hover { transform: scale(1.2); }
.controls { display: flex; gap: 10px; margin-top: 15px; flex-wrap: wrap; justify-content: center; }
.btn {
padding: 10px 20px; border: none; border-radius: 10px; cursor: pointer;
font-family: inherit; font-weight: bold; font-size: 14px;
}
.btn-new { background: #4ade80; color: white; }
.btn-back { background: #ff6b6b; color: white; text-decoration: none; }
.exercise-btn {
background: rgba(255,255,255,0.2); border: 2px solid white; color: white;
padding: 8px 15px; margin: 5px; border-radius: 10px; cursor: pointer;
}
.exercise-btn:hover { background: rgba(255,255,255,0.4); }
.game-over {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px; border-radius: 20px; display: none; z-index: 100;
text-align: center;
}
</style>
</head>
<body>
<h1>♟️ Schach</h1>
<div class="mode-select">
<button class="mode-btn active" onclick="setMode('free')">Freies Spiel</button>
<button class="mode-btn" onclick="setMode('mate1')">Matt in 1</button>
<button class="mode-btn" onclick="setMode('fork')">Gabel</button>
</div>
<div class="status" id="status"><span class="turn-white">Weiß</span> ist am Zug</div>
<div class="board-container">
<div class="chess-board" id="board"></div>
</div>
<div class="coordinates">
<span>a</span><span>b</span><span>c</span><span>d</span>
<span>e</span><span>f</span><span>g</span><span>h</span>
</div>
<div class="controls">
<button class="hint-btn" onclick="showHint()">💡 Tipp</button>
<button class="btn btn-new" onclick="newGame()">🔄 Neues Spiel</button>
<a href="../index.html" class="btn btn-back">⬅️ Zurück</a>
</div>
<div class="promotion-modal" id="promotionModal">
<h3>Wähle Figur:</h3>
<div id="promotionOptions"></div>
</div>
<div class="game-over" id="gameOver">
<h2 id="winnerText">Schachmatt!</h2>
<button class="btn btn-new" onclick="newGame()">🔄 Nochmal</button>
</div>
<script>
const PIECES = {
'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙',
'k': '♚', 'q': '♛', 'r': '♜', 'b': '♝', 'n': '♞', 'p': '♟'
};
let board = [];
let selected = null;
let validMoves = [];
let currentPlayer = 'white';
let lastMove = null;
let waitingPromotion = null;
let currentExercise = null;
const START_POS = [
'rnbqkbnr',
'pppppppp',
' ',
' ',
' ',
' ',
'PPPPPPPP',
'RNBQKBNR'
];
const EXERCISES = {
mate1: [
{
pos: [
' ',
' ',
' ',
' k ',
' ',
' Q ',
' ',
' K'
],
solution: { from: {r: 5, c: 5}, to: {r: 3, c: 5} }, // Qf6-f7 Matt
hint: 'Setze die Dame neben den König auf f7!'
},
{
pos: [
' ',
' ',
' ',
' k ',
' ',
' ',
'R ',
' K'
],
solution: { from: {r: 6, c: 0}, to: {r: 0, c: 0} },
hint: 'Der Turm schließt den König auf der a-Linie ein!'
},
{
pos: [
'k ',
' ',
' ',
' ',
' ',
' ',
'R R',
' K'
],
solution: { from: {r: 6, c: 0}, to: {r: 0, c: 0} },
hint: 'Ein Turm geht auf die 8. Reihe für Matt!'
}
],
fork: [
{
pos: [
' ',
' q ',
' ',
' ',
' k ',
' ',
' N',
' K'
],
solution: { from: {r: 6, c: 7}, to: {r: 4, c: 5} },
hint: 'Der Springer greift König und Dame gleichzeitig an!'
}
]
};
function initBoard() {
const boardEl = document.getElementById('board');
boardEl.innerHTML = '';
board = [];
for (let row = 0; row < 8; row++) {
board[row] = [];
for (let col = 0; col < 8; col++) {
const cell = document.createElement('div');
cell.className = 'square ' + ((row + col) % 2 === 0 ? 'light' : 'dark');
cell.dataset.r = row;
cell.dataset.c = col;
cell.onclick = () => clickSquare(row, col);
const piece = START_POS[row][col];
if (piece !== ' ') {
setPiece(cell, piece);
board[row][col] = piece;
}
boardEl.appendChild(cell);
}
}
updateStatus();
}
function setPiece(cell, piece) {
const span = document.createElement('span');
span.className = 'piece ' + (piece === piece.toUpperCase() ? 'white' : 'black');
span.textContent = PIECES[piece];
cell.innerHTML = '';
cell.appendChild(span);
}
function clickSquare(row, col) {
if (waitingPromotion) return;
const piece = board[row][col];
const isOwn = piece &&
((currentPlayer === 'white' && piece === piece.toUpperCase()) ||
(currentPlayer === 'black' && piece === piece.toLowerCase()));
if (!selected && isOwn) {
selected = { r: row, c: col };
validMoves = getMoves(row, col, piece);
highlightSquare(row, col, 'selected');
highlightMoves();
} else if (selected) {
const move = validMoves.find(m => m.r === row && m.c === col);
if (move) {
makeMove(selected.r, selected.c, row, col);
}
clearHighlights();
selected = null;
validMoves = [];
}
}
function getMoves(r, c, piece) {
const moves = [];
const isWhite = piece === piece.toUpperCase();
const p = piece.toLowerCase();
if (p === 'p') {
const dir = isWhite ? -1 : 1;
const startRow = isWhite ? 6 : 1;
if (isEmpty(r + dir, c)) {
moves.push({ r: r + dir, c });
if (r === startRow && isEmpty(r + 2*dir, c)) {
moves.push({ r: r + 2*dir, c });
}
}
[-1, 1].forEach(dc => {
if (canCapture(r + dir, c + dc, isWhite)) {
moves.push({ r: r + dir, c: c + dc });
}
});
}
else if (p === 'n') {
[[-2,-1],[-2,1],[-1,-2],[-1,2],[1,-2],[1,2],[2,-1],[2,1]].forEach(([dr, dc]) => {
if (canMove(r + dr, c + dc, isWhite)) moves.push({ r: r + dr, c: c + dc });
});
}
else if (p === 'k') {
[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]].forEach(([dr, dc]) => {
if (canMove(r + dr, c + dc, isWhite)) moves.push({ r: r + dr, c: c + dc });
});
}
else if (p === 'r') {
addLineMoves(moves, r, c, [[0,1],[0,-1],[1,0],[-1,0]], isWhite);
}
else if (p === 'b') {
addLineMoves(moves, r, c, [[1,1],[1,-1],[-1,1],[-1,-1]], isWhite);
}
else if (p === 'q') {
addLineMoves(moves, r, c, [[0,1],[0,-1],[1,0],[-1,0],[1,1],[1,-1],[-1,1],[-1,-1]], isWhite);
}
return moves;
}
function addLineMoves(moves, r, c, dirs, isWhite) {
dirs.forEach(([dr, dc]) => {
for (let i = 1; i < 8; i++) {
const nr = r + dr * i, nc = c + dc * i;
if (!inBounds(nr, nc)) break;
if (isEmpty(nr, nc)) {
moves.push({ r: nr, c: nc });
} else if (canCapture(nr, nc, isWhite)) {
moves.push({ r: nr, c: nc });
break;
} else break;
}
});
}
function inBounds(r, c) { return r >= 0 && r < 8 && c >= 0 && c < 8; }
function isEmpty(r, c) { return inBounds(r, c) && !board[r][c]; }
function canCapture(r, c, isWhite) {
if (!inBounds(r, c)) return false;
const target = board[r][c];
return target && (isWhite ? target === target.toLowerCase() : target === target.toUpperCase());
}
function canMove(r, c, isWhite) {
return inBounds(r, c) && (isEmpty(r, c) || canCapture(r, c, isWhite));
}
function makeMove(fr, fc, tr, tc) {
const piece = board[fr][fc];
const captured = board[tr][tc];
board[tr][tc] = piece;
board[fr][fc] = null;
// König geschlagen = Spielende
if (captured === 'K' || captured === 'k') {
endGame(captured === 'K' ? 'Schwarz' : 'Weiß');
return;
}
// Bauern-Umwandlung
if (piece.toLowerCase() === 'p' && (tr === 0 || tr === 7)) {
showPromotion(tr, tc, piece === piece.toUpperCase());
return;
}
finishMove(fr, fc, tr, tc, piece);
}
function showPromotion(r, c, isWhite) {
waitingPromotion = { r, c, isWhite };
const modal = document.getElementById('promotionModal');
const opts = document.getElementById('promotionOptions');
opts.innerHTML = '';
const pieces = isWhite ? ['Q','R','B','N'] : ['q','r','b','n'];
pieces.forEach(p => {
const btn = document.createElement('span');
btn.className = 'promotion-piece';
btn.textContent = PIECES[p];
btn.onclick = () => {
board[r][c] = p;
modal.style.display = 'none';
waitingPromotion = null;
finishMove(null, null, null, null, p);
};
opts.appendChild(btn);
});
modal.style.display = 'block';
}
function finishMove(fr, fc, tr, tc, piece) {
lastMove = { fr, fc, tr, tc };
updateBoard();
// Prüfe Übungslösung
if (currentExercise) {
const sol = currentExercise.solution;
if (lastMove.fr === sol.from.r && lastMove.fc === sol.from.c &&
lastMove.tr === sol.to.r && lastMove.tc === sol.to.c) {
setTimeout(() => {
alert('🎉 Richtig! ' + currentExercise.hint);
currentExercise = null;
}, 300);
}
}
switchPlayer();
}
function switchPlayer() {
currentPlayer = currentPlayer === 'white' ? 'black' : 'white';
updateStatus();
// Prüfe Schach
if (isInCheck(currentPlayer)) {
highlightKing(currentPlayer);
if (isCheckmate(currentPlayer)) {
endGame(currentPlayer === 'white' ? 'Schwarz' : 'Weiß');
}
}
}
function isInCheck(color) {
const king = color === 'white' ? 'K' : 'k';
let kr, kc;
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
if (board[r][c] === king) { kr = r; kc = c; }
}
}
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const p = board[r][c];
if (p && (color === 'white' ? p === p.toLowerCase() : p === p.toUpperCase())) {
const moves = getMoves(r, c, p);
if (moves.some(m => m.r === kr && m.c === kc)) return true;
}
}
}
return false;
}
function isCheckmate(color) {
// Einfach: Keine legalen Züge + Schach
if (!isInCheck(color)) return false;
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const p = board[r][c];
if (p && (color === 'white' ? p === p.toUpperCase() : p === p.toLowerCase())) {
if (getMoves(r, c, p).length > 0) return false;
}
}
}
return true;
}
function highlightKing(color) {
const king = color === 'white' ? 'K' : 'k';
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
if (board[r][c] === king) {
document.querySelector(`[data-r="${r}"][data-c="${c}"]`).classList.add('check');
}
}
}
}
function updateBoard() {
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const cell = document.querySelector(`[data-r="${r}"][data-c="${c}"]`);
const p = board[r][c];
cell.innerHTML = '';
cell.classList.remove('last-move', 'check');
if (p) setPiece(cell, p);
if (lastMove && ((r === lastMove.fr && c === lastMove.fc) || (r === lastMove.tr && c === lastMove.tc))) {
cell.classList.add('last-move');
}
}
}
}
function updateStatus() {
const status = document.getElementById('status');
const cls = currentPlayer === 'white' ? 'turn-white' : 'turn-black';
status.innerHTML = `<span class="${cls}">${currentPlayer === 'white' ? 'Weiß' : 'Schwarz'}</span> ist am Zug`;
}
function highlightSquare(r, c, cls) {
document.querySelector(`[data-r="${r}"][data-c="${c}"]`).classList.add(cls);
}
function highlightMoves() {
validMoves.forEach(m => {
const cell = document.querySelector(`[data-r="${m.r}"][data-c="${m.c}"]`);
if (board[m.r][m.c]) cell.classList.add('valid-capture');
else cell.classList.add('valid-move');
});
}
function clearHighlights() {
document.querySelectorAll('.square').forEach(s => {
s.classList.remove('selected', 'valid-move', 'valid-capture');
});
}
function setMode(mode) {
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
if (mode === 'free') {
currentExercise = null;
newGame();
} else if (EXERCISES[mode]) {
const ex = EXERCISES[mode][Math.floor(Math.random() * EXERCISES[mode].length)];
currentExercise = ex;
loadPosition(ex.pos);
}
}
function loadPosition(pos) {
board = [];
for (let r = 0; r < 8; r++) {
board[r] = [];
for (let c = 0; c < 8; c++) {
board[r][c] = pos[r][c] === ' ' ? null : pos[r][c];
}
}
currentPlayer = 'white';
updateBoard();
updateStatus();
}
function showHint() {
if (!currentExercise) {
alert('💡 Tipps sind nur im Übungsmodus verfügbar!');
return;
}
alert('💡 ' + currentExercise.hint);
}
function endGame(winner) {
document.getElementById('winnerText').textContent = `🎉 ${winner} gewinnt!`;
document.getElementById('gameOver').style.display = 'block';
}
function newGame() {
document.getElementById('gameOver').style.display = 'none';
document.getElementById('promotionModal').style.display = 'none';
waitingPromotion = null;
selected = null;
validMoves = [];
lastMove = null;
currentPlayer = 'white';
currentExercise = null;
initBoard();
}
// Start
initBoard();
</script>
</body>
</html>