Добавить tic-tac-toe.html
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Крестики-нолики</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #0f0f23;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
color: #eee;
|
||||
}
|
||||
h1 { color: #7b68ee; margin-bottom: 10px; }
|
||||
#status {
|
||||
font-size: 1.3em;
|
||||
margin-bottom: 15px;
|
||||
min-height: 35px;
|
||||
color: #ffd700;
|
||||
}
|
||||
.board {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 100px);
|
||||
grid-template-rows: repeat(3, 100px);
|
||||
gap: 5px;
|
||||
}
|
||||
.cell {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: #1a1a3e;
|
||||
border: 2px solid #7b68ee;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 3em;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.cell:hover { background: #2a2a5e; transform: scale(1.05); }
|
||||
.cell.x { color: #ff6b6b; }
|
||||
.cell.o { color: #4ecdc4; }
|
||||
.cell.win { background: #2a4a2a; border-color: #0f0; box-shadow: 0 0 15px #0f0; }
|
||||
#scoreBoard {
|
||||
margin-top: 20px;
|
||||
font-size: 1.1em;
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
}
|
||||
#restart {
|
||||
margin-top: 20px;
|
||||
padding: 10px 30px;
|
||||
font-size: 1em;
|
||||
background: #7b68ee;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
}
|
||||
#restart:hover { background: #6a5acd; transform: scale(1.05); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>❌ Крестики-нолики</h1>
|
||||
<div id="status">Ваш ход (X)</div>
|
||||
<div class="board" id="board"></div>
|
||||
<div id="scoreBoard">
|
||||
<span>Вы (X): <b id="xs">0</b></span>
|
||||
<span>НИЧЬЯ: <b id="ds">0</b></span>
|
||||
<span>Компьютер (O): <b id="os">0</b></span>
|
||||
</div>
|
||||
<button id="restart">Новая игра</button>
|
||||
|
||||
<script>
|
||||
const board = document.getElementById('board');
|
||||
const status = document.getElementById('status');
|
||||
let cells = Array(9).fill('');
|
||||
let currentTurn = 'X';
|
||||
let gameActive = true;
|
||||
let scores = { X: 0, O: 0, D: 0 };
|
||||
const wins = [[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 render() {
|
||||
board.innerHTML = '';
|
||||
cells.forEach((val, i) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'cell' + (val === 'X' ? ' x' : val === 'O' ? ' o' : '');
|
||||
div.textContent = val;
|
||||
div.onclick = () => move(i);
|
||||
board.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
function checkWin(player) {
|
||||
return wins.find(w => w.every(i => cells[i] === player));
|
||||
}
|
||||
|
||||
function isDraw() { return cells.every(c => c !== ''); }
|
||||
|
||||
function move(i) {
|
||||
if (!gameActive || cells[i] || currentTurn !== 'X') return;
|
||||
cells[i] = 'X';
|
||||
render();
|
||||
const w = checkWin('X');
|
||||
if (w) { endGame('X', w); return; }
|
||||
if (isDraw()) { endGame('D'); return; }
|
||||
currentTurn = 'O';
|
||||
status.textContent = 'Компьютер думает...';
|
||||
setTimeout(aiMove, 400);
|
||||
}
|
||||
|
||||
function aiMove() {
|
||||
let best = -Infinity, bestMove = -1;
|
||||
for (let i = 0; i < 9; i++) {
|
||||
if (cells[i]) continue;
|
||||
cells[i] = 'O';
|
||||
let sc = minimax(false, 0);
|
||||
cells[i] = '';
|
||||
if (sc > best) { best = sc; bestMove = i; }
|
||||
}
|
||||
cells[bestMove] = 'O';
|
||||
render();
|
||||
const w = checkWin('O');
|
||||
if (w) { endGame('O', w); return; }
|
||||
if (isDraw()) { endGame('D'); return; }
|
||||
currentTurn = 'X';
|
||||
status.textContent = 'Ваш ход (X)';
|
||||
}
|
||||
|
||||
function minimax(isMax, depth) {
|
||||
if (checkWin('O')) return 10 - depth;
|
||||
if (checkWin('X')) return depth - 10;
|
||||
if (isDraw()) return 0;
|
||||
if (isMax) {
|
||||
let best = -Infinity;
|
||||
for (let i = 0; i < 9; i++) {
|
||||
if (cells[i]) continue;
|
||||
cells[i] = 'O';
|
||||
best = Math.max(best, minimax(false, depth + 1));
|
||||
cells[i] = '';
|
||||
}
|
||||
return best;
|
||||
} else {
|
||||
let best = Infinity;
|
||||
for (let i = 0; i < 9; i++) {
|
||||
if (cells[i]) continue;
|
||||
cells[i] = 'X';
|
||||
best = Math.min(best, minimax(true, depth + 1));
|
||||
cells[i] = '';
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
function endGame(result, wLine) {
|
||||
gameActive = false;
|
||||
if (result === 'D') {
|
||||
status.textContent = '🤝 Ничья!';
|
||||
scores.D++;
|
||||
} else {
|
||||
const name = result === 'X' ? 'Вы победили!' : 'Компьютер победил!';
|
||||
status.textContent = (result === 'X' ? '🎉 ' : '😞 ') + name;
|
||||
scores[result]++;
|
||||
if (wLine) {
|
||||
const allCells = board.children;
|
||||
wLine.forEach(i => allCells[i].classList.add('win'));
|
||||
}
|
||||
}
|
||||
document.getElementById('xs').textContent = scores.X;
|
||||
document.getElementById('os').textContent = scores.O;
|
||||
document.getElementById('ds').textContent = scores.D;
|
||||
}
|
||||
|
||||
document.getElementById('restart').onclick = () => {
|
||||
cells = Array(9).fill('');
|
||||
currentTurn = 'X';
|
||||
gameActive = true;
|
||||
status.textContent = 'Ваш ход (X)';
|
||||
render();
|
||||
};
|
||||
|
||||
render();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user