Mein Schachbot mit Rating
by BlazeKoala99322 lines11.3 KB
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mein Schachbot mit Rating</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/chessboard-1.0.0.min.css">
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f0f0f0;
margin: 0;
overflow: hidden;
}
h1 {
color: #333;
margin-bottom: 10px;
user-select: none;
}
.controls {
margin-bottom: 20px;
font-size: 1rem;
user-select: none;
}
select {
padding: 5px 10px;
font-size: 1rem;
border-radius: 5px;
border: 1px solid #ccc;
cursor: pointer;
}
#board {
width: 400px;
max-width: 90vw;
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
touch-action: none;
}
.chessboard-63984 img, [class^="square-"] img {
touch-action: none;
-webkit-user-drag: none;
user-select: none;
}
#status {
margin-top: 20px;
font-size: 1.2rem;
font-weight: bold;
color: #555;
user-select: none;
}
</style>
</head>
<body>
<h1>Spiele gegen den Bot!</h1>
<div class="controls">
<label for="rating-select">Bot-Stärke wählen: </label>
<select id="rating-select">
<option value="600">Einsteiger (ca. 600 Elo)</option>
<option value="800">Hobby-Spieler (ca. 800 Elo)</option>
<option value="1000">Erfahren (ca. 1000 Elo)</option>
<option value="1200" selected>Fortgeschritten (ca. 1200 Elo)</option>
<option value="1600">Herausfordernd (ca. 1600 Elo)</option>
<option value="2000">Experte (ca. 2000 Elo)</option>
<option value="2200">Meister (ca. 2200 Elo)</option>
</select>
</div>
<div id="board"></div>
<div id="status">Du bist dran (Weiß)</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/chessboard-1.0.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.3/chess.min.js"></script>
<script>
const game = new Chess();
let board = null;
const $status = $('#status');
const pieceValues = {
p: 10,
n: 30,
b: 30,
r: 50,
q: 90,
k: 900
};
function onDragStart (source, piece, position, orientation) {
if (game.game_over()) return false;
if (piece.search(/^b/) !== -1) return false;
return true;
}
// Bewertet die aktuelle Stellung auf dem Brett aus Sicht von Schwarz
function evaluateBoard() {
let value = 0;
const boardState = game.board();
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const piece = boardState[r][c];
if (piece) {
const pValue = pieceValues[piece.type];
if (piece.color === 'b') {
value += pValue;
} else {
value -= pValue;
}
}
}
}
return value;
}
function evaluateMove(move) {
game.move(move);
let value = evaluateBoard();
game.undo();
return value;
}
// Minimax-Algorithmus mit Tiefe 2
function minimaxDepth2() {
const possibleMoves = game.moves({ verbose: true });
let bestMoves = [];
let bestValue = -Infinity;
for (let i = 0; i < possibleMoves.length; i++) {
const move = possibleMoves[i];
game.move(move);
if (game.in_checkmate()) {
game.undo();
return move;
}
const whiteMoves = game.moves({ verbose: true });
let worstValueForBlack = Infinity;
if (whiteMoves.length === 0) {
worstValueForBlack = 0;
} else {
for (let j = 0; j < whiteMoves.length; j++) {
game.move(whiteMoves[j]);
const boardValue = evaluateBoard();
if (boardValue < worstValueForBlack) {
worstValueForBlack = boardValue;
}
game.undo();
}
}
game.undo();
if (worstValueForBlack > bestValue) {
bestValue = worstValueForBlack;
bestMoves = [move];
} else if (worstValueForBlack === bestValue) {
bestMoves.push(move);
}
}
const randomIdx = Math.floor(Math.random() * bestMoves.length);
return bestMoves[randomIdx];
}
function makeBotMove () {
if (game.game_over()) return;
const possibleMoves = game.moves({ verbose: true });
if (possibleMoves.length === 0) return;
const selectedRating = $('#rating-select').val();
let chosenMove = null;
// --- 600 ELO (Komplett zufällige Züge) ---
if (selectedRating === "600") {
const randomIdx = Math.floor(Math.random() * possibleMoves.length);
chosenMove = possibleMoves[randomIdx];
}
// --- 800 ELO (Spielt meistens zufällig, macht aber manchmal grobe Fehler) ---
else if (selectedRating === "800") {
let worstMoves = [];
let worstValue = Infinity;
for (let i = 0; i < possibleMoves.length; i++) {
const moveValue = evaluateMove(possibleMoves[i]);
if (moveValue < worstValue) {
worstValue = moveValue;
worstMoves = [possibleMoves[i]];
} else if (moveValue === worstValue) {
worstMoves.push(possibleMoves[i]);
}
}
// 25% Chance auf einen schlechten Zug, ansonsten zufällig
if (Math.random() < 0.25) {
const randomIdx = Math.floor(Math.random() * worstMoves.length);
chosenMove = worstMoves[randomIdx];
} else {
const randomIdx = Math.floor(Math.random() * possibleMoves.length);
chosenMove = possibleMoves[randomIdx];
}
}
// --- 1000 ELO (Mischt zufällige Züge mit einfachen Schlagzügen) ---
else if (selectedRating === "1000") {
const captures = possibleMoves.filter(m => m.captured);
if (captures.length > 0 && Math.random() < 0.5) {
const randomIdx = Math.floor(Math.random() * captures.length);
chosenMove = captures[randomIdx];
} else {
const randomIdx = Math.floor(Math.random() * possibleMoves.length);
chosenMove = possibleMoves[randomIdx];
}
}
// --- 1200 ELO (Schlägt bevorzugt Figuren, wenn möglich) ---
else if (selectedRating === "1200") {
const captures = possibleMoves.filter(m => m.captured);
if (captures.length > 0) {
const randomIdx = Math.floor(Math.random() * captures.length);
chosenMove = captures[randomIdx];
} else {
const randomIdx = Math.floor(Math.random() * possibleMoves.length);
chosenMove = possibleMoves[randomIdx];
}
}
// --- 1600 ELO (Sucht den sofortigen besten Materialgewinn - Tiefe 1) ---
else if (selectedRating === "1600") {
let bestMoves = [];
let bestValue = -Infinity;
for (let i = 0; i < possibleMoves.length; i++) {
const moveValue = evaluateMove(possibleMoves[i]);
if (moveValue > bestValue) {
bestValue = moveValue;
bestMoves = [possibleMoves[i]];
} else if (moveValue === bestValue) {
bestMoves.push(possibleMoves[i]);
}
}
const randomIdx = Math.floor(Math.random() * bestMoves.length);
chosenMove = bestMoves[randomIdx];
}
// --- 2000 ELO (Nutzt Minimax Tiefe 2, baut selten kleine Ungenauigkeiten ein) ---
else if (selectedRating === "2000") {
if (Math.random() < 0.15) {
const randomIdx = Math.floor(Math.random() * possibleMoves.length);
chosenMove = possibleMoves[randomIdx];
} else {
chosenMove = minimaxDepth2();
}
}
// --- 2200 ELO (Spielt fehlerfrei auf Minimax Tiefe 2) ---
else if (selectedRating === "2200") {
chosenMove = minimaxDepth2();
}
game.move(chosenMove);
board.position(game.fen());
updateStatus();
}
function onDrop (source, target) {
const move = game.move({
from: source,
to: target,
promotion: 'q'
});
if (move === null) return 'snapback';
updateStatus();
if (!game.game_over()) {
window.setTimeout(makeBotMove, 400);
}
}
function onSnapEnd () {
board.position(game.fen());
}
function updateStatus () {
let status = '';
let moveColor = 'Weiß';
if (game.turn() === 'b') {
moveColor = 'Schwarz (Bot)';
}
if (game.in_checkmate()) {
status = 'Spiel vorbei! ' + (game.turn() === 'b' ? 'Du hast gewonnen!' : 'Der Bot hat gewonnen!');
} else if (game.in_draw()) {
status = 'Spiel vorbei! Unentschieden (Patt oder Materialmangel).';
} else {
status = moveColor + ' ist am Zug.';
if (game.in_check()) {
status += ' (Schach!)';
}
}
$status.html(status);
}
const config = {
draggable: true,
position: 'start',
onDragStart: onDragStart,
onDrop: onDrop,
onSnapEnd: onSnapEnd,
pieceTheme: 'https://chessboardjs.com/img/chesspieces/wikipedia/{piece}.png'
};
board = Chessboard('board', config);
updateStatus();
</script>
</body>
</html>Game Source: Mein Schachbot mit Rating
Creator: BlazeKoala99
Libraries: none
Complexity: complex (322 lines, 11.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: mein-schachbot-mit-rating-blazekoala99" to link back to the original. Then publish at arcadelab.ai/publish.