Kółko i krzyżyk krok 12: komputer gra według reguł
Kółko i krzyżyk krok po kroku - JavaScript na stronach WWW (HTML/CSS/JS)
Kółkiem gra komputer. `computerMove` przechodzi listę reguł, jak robi to człowiek: jeśli może wygrać jednym ruchem, wygrywa; jeśli przeciwnikowi brakuje jednego znaku w linii, blokuje; potem bierze środek, róg, a na końcu bok. Operator `??` przechodzi do następnej reguły, gdy poprzednia nic nie znalazła. Model gry się nie zmienił: komputer po prostu woła `game.play` po ruchu gracza.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>Kółko i krzyżyk</title>
<style>
body { font-family: sans-serif; max-width: 36em; line-height: 1.4; }
input, select, button, textarea { font: inherit; padding: 0.2em 0.4em; }
table { border-collapse: collapse; }
th, td { border: 1px solid #94a3b8; padding: 0.2em 0.6em; }
#board { display: grid; grid-template-columns: repeat(3, 60px); gap: 4px; margin: 0.5em 0; }
#board button { width: 60px; height: 60px; font-size: 1.8em; padding: 0; }
#board .win { background: #bbf7d0; }
</style>
</head>
<body>
<p id="info">Ruch: X</p>
<div id="board"></div>
<p><button id="reset">Nowa gra</button></p>
<p id="score"></p>
<script>
const board = document.getElementById('board');
const info = document.getElementById('info');
const buttons = [];
for (let i = 0; i < 9; i++) {
const button = document.createElement('button');
button.dataset.index = i;
board.append(button);
buttons.push(button);
}
// osiem linii: trzy wiersze, trzy kolumny, dwie przekątne
const LINES = [
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6],
];
function winningLine(cells) {
return LINES.find(([a, b, c]) => cells[a] !== '' && cells[a] === cells[b] && cells[a] === cells[c]) ?? null;
}
// model: cały stan gry i jej zasady, bez dostępu do strony
class Game {
constructor() {
this.score = { X: 0, O: 0, draw: 0 };
this.reset();
}
reset() {
this.cells = Array(9).fill('');
this.player = 'X';
this.result = null; // 'X', 'O', 'draw' albo null, gdy gra trwa
}
get line() {
return winningLine(this.cells);
}
get status() {
if (this.result === 'draw') return 'Remis';
if (this.result) return `Wygrywa ${this.result}`;
return `Ruch: ${this.player}`;
}
play(index) {
if (this.result || this.cells[index] !== '') return false;
this.cells[index] = this.player;
if (this.line) this.result = this.player;
else if (this.cells.every(c => c !== '')) this.result = 'draw';
else this.player = this.player === 'X' ? 'O' : 'X';
if (this.result) this.score[this.result]++;
return true;
}
}
const game = new Game();
// widok: rysuje model, nic nie liczy
function render() {
const line = game.line;
buttons.forEach((button, i) => {
button.textContent = game.cells[i];
button.classList.toggle('win', line !== null && line.includes(i));
});
info.textContent = game.status;
document.getElementById('score').textContent = `X: ${game.score.X}, O: ${game.score.O}, remisy: ${game.score.draw}`;
}
// komputer: wygraj, jeśli możesz; zablokuj przeciwnika; środek; róg; bok
function computerMove(cells, me, other) {
const free = i => cells[i] === '';
const finishing = who => {
for (const line of LINES) {
const marks = line.filter(i => cells[i] === who);
const empty = line.filter(free);
if (marks.length === 2 && empty.length === 1) return empty[0];
}
return null;
};
return finishing(me) ?? finishing(other) ?? (free(4) ? 4 : null) ?? [0, 2, 6, 8].find(free) ?? [1, 3, 5, 7].find(free);
}
board.addEventListener('click', e => {
const button = e.target.closest('button');
if (!button || !game.play(Number(button.dataset.index))) return;
if (!game.result) game.play(computerMove(game.cells, 'O', 'X'));
render();
});
document.getElementById('reset').addEventListener('click', () => {
game.reset();
render();
});
render();
</script>
</body>
</html>Ładowanie interaktywnego edytora... Jeśli się nie pojawi, włącz JavaScript.
Wszystkie lekcje kursu JavaScript na stronach WWW·Zaloguj się, aby zapisać postęp na koncie.