/
Isip
/
isip
Обзор
Документация
Войти
/
Isip
/
isip
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
index4.html
713 строк
25 KB
Isip
create: index4.html, задание 2, разработка комп игр .docx
29 май 2026, 06:45
Верифицирован
29 май 2026, 06:45
a5523e5
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>Крестики-Нолики · OX Game</title> <style> * { box-sizing: border-box; user-select: none; } body { background: linear-gradient(145deg, #1a2a2f 0%, #0e1a1f 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; font-family: 'Segoe UI', 'Courier New', 'Monaco', monospace; margin: 0; padding: 20px; } /* главный контейнер с текстурой под старую игру */ .game-container { background: #2c2e2b; background-image: radial-gradient(circle at 25% 40%, rgba(80,70,50,0.2) 2%, transparent 2.5%), radial-gradient(circle at 70% 85%, rgba(60,55,35,0.25) 1.8%, transparent 2%); background-size: 32px 32px, 28px 28px; border-radius: 64px 64px 56px 56px; box-shadow: 0 20px 35px rgba(0,0,0,0.5), inset 0 1px 2px rgba(255,255,200,0.1); padding: 24px 28px 32px; transition: all 0.2s; } /* панель статуса / информации */ .info-panel { background: #1f2622; border-radius: 48px; padding: 12px 24px; margin-bottom: 28px; text-align: center; box-shadow: inset 0 2px 4px rgba(0,0,0,0.6), 0 3px 6px rgba(0,0,0,0.3); border-bottom: 2px solid #6a5e4b; } .turn-indicator { font-size: 1.8rem; font-weight: bold; letter-spacing: 2px; color: #f7eac5; text-shadow: 0 2px 0 #4a3e2a; background: #2e2a24; display: inline-block; padding: 8px 28px; border-radius: 60px; font-family: monospace; } .turn-indicator span { font-size: 2rem; margin-right: 8px; } /* сетка 3x3 в стиле пиксель-арт */ .board { display: grid; grid-template-columns: repeat(3, 1fr); background-color: #2a2b26; gap: 8px; background: #3c3530; padding: 12px; border-radius: 42px; box-shadow: inset 0 0 0 2px #5b4f3e, inset 0 0 0 5px #2e2822, 0 12px 20px rgba(0,0,0,0.4); } .cell { background: #d9c8a9; background-image: linear-gradient(145deg, #efe0c1 0%, #cbbf9a 100%); width: 85px; height: 85px; border-radius: 24px; display: flex; align-items: center; justify-content: center; font-size: 4.2rem; font-weight: bold; font-family: monospace; cursor: pointer; transition: 0.07s linear; box-shadow: inset 0 -3px 0 #836e4e, 0 4px 8px rgba(0,0,0,0.3); color: #2a241c; text-shadow: 2px 2px 0 rgba(0,0,0,0.1); } .cell:active { transform: scale(0.96); box-shadow: inset 0 0 0 2px #9c855f; } /* надписи X O в стиле референсов */ .cell.X-move { color: #4d3c28; text-shadow: 2px 2px 0 #b6a071; } .cell.O-move { color: #2e5a4a; text-shadow: 2px 2px 0 #98bba8; } /* меню выбора и кнопки */ .choice-row { display: flex; justify-content: space-between; gap: 16px; margin: 22px 0 18px; } .choice-card { background: #23221d; border-radius: 48px; flex: 1; padding: 12px 0; text-align: center; cursor: pointer; transition: all 0.1s; border: 2px solid #68604c; box-shadow: 0 5px 0 #2f2a1f; color: #f0e2c5; font-weight: bold; font-size: 1.3rem; } .choice-card.active { background: #b5976e; border-color: #ffeaaf; color: #1f1b10; box-shadow: 0 2px 0 #5c482c; transform: translateY(2px); } .choice-card span { font-size: 2rem; margin-right: 6px; } .mode-group { display: flex; gap: 12px; margin: 18px 0 14px; } .mode-btn { background: #312d24; flex: 1; text-align: center; padding: 10px; border-radius: 60px; font-weight: bold; font-size: 1.1rem; letter-spacing: 1px; color: #eedbae; cursor: pointer; transition: 0.07s linear; border-bottom: 3px solid #685e46; } .mode-btn.selected-mode { background: #4f7f6e; color: #faf0cf; border-bottom-color: #cbb26a; transform: scale(0.98); } .action-buttons { display: flex; gap: 20px; margin-top: 24px; } .game-btn { flex: 1; background: #46412f; border: none; font-family: monospace; font-weight: bold; font-size: 1.4rem; padding: 12px 0; border-radius: 48px; color: #fcf3da; cursor: pointer; transition: 0.08s linear; text-transform: uppercase; letter-spacing: 2px; box-shadow: 0 6px 0 #282318; } .game-btn:active { transform: translateY(3px); box-shadow: 0 2px 0 #282318; } .reset-btn { background: #6f553b; } .status-small { text-align: center; margin-top: 18px; font-size: 1rem; font-weight: bold; background: #191e1b; display: inline-block; width: 100%; padding: 6px; border-radius: 40px; color: #cbbc87; } @media (max-width: 500px) { .cell { width: 70px; height: 70px; font-size: 3rem; } .turn-indicator { font-size: 1.3rem; padding: 4px 18px; } .choice-card span { font-size: 1.5rem; } } </style> </head> <body> <div class="game-container"> <div class="info-panel"> <div class="turn-indicator" id="turnText"> <span>⚡</span> OX GAME </div> </div> <!-- игровое поле 3x3 --> <div class="board" id="board"> <!-- 9 ячеек будут динамически созданы, но для структуры заполним через JS --> </div> <!-- выбор: ты играешь за X или O (дизайн как на референсе "X your opponent / O you") --> <div class="choice-row" id="symbolChoiceContainer"> <div class="choice-card" data-symbol="X"> <span>✖</span> X — ваш знак </div> <div class="choice-card" data-symbol="O"> <span>◯</span> O — ваш знак </div> </div> <!-- выбор режима: игра с ботом или с другом --> <div class="mode-group" id="modeSelect"> <div class="mode-btn" data-mode="bot">🤖 ИГРАТЬ С БОТОМ</div> <div class="mode-btn" data-mode="duo">👥 2 ИГРОКА</div> </div> <div class="action-buttons"> <button class="game-btn" id="playBtn">▶ PLAY</button> <button class="game-btn reset-btn" id="resetBtn">🔄 НОВАЯ</button> <button class="game-btn" id="exitBtn">⚙ EXIT</button> </div> <div class="status-small" id="gameStatus"> выберите символ и нажмите PLAY </div> </div> <script> (function() { // ----- DOM элементы ----- const boardContainer = document.getElementById('board'); const turnTextEl = document.getElementById('turnText'); const gameStatusDiv = document.getElementById('gameStatus'); const playBtn = document.getElementById('playBtn'); const resetBtn = document.getElementById('resetBtn'); const exitBtn = document.getElementById('exitBtn'); const symbolCards = document.querySelectorAll('.choice-card'); const modeBtns = document.querySelectorAll('.mode-btn'); // ----- состояние игры ----- let board = ['', '', '', '', '', '', '', '', '']; // 0..8 let gameActive = false; // игра активна (ходы разрешены) let currentPlayer = 'X'; // всегда X начинает партию (по правилам) let playerSymbol = 'X'; // что выбрал человек (X или O) let aiSymbol = 'O'; // противоположный знак (будет обновляться) let gameMode = 'bot'; // 'bot' или 'duo' let waitingForAI = false; // флаг чтобы не запускать ИИ повторно // ссылка на ячейки DOM let cellElements = []; // Инициализация сетки 3x3 function createBoardUI() { boardContainer.innerHTML = ''; cellElements = []; for (let i = 0; i < 9; i++) { const cell = document.createElement('div'); cell.classList.add('cell'); cell.dataset.index = i; cell.textContent = ''; cell.addEventListener('click', () => handleCellClick(i)); boardContainer.appendChild(cell); cellElements.push(cell); } } // Обновить отображение доски из board[] function updateBoardUI() { for (let i = 0; i < 9; i++) { const val = board[i]; cellElements[i].textContent = val; if (val === 'X') { cellElements[i].classList.add('X-move'); cellElements[i].classList.remove('O-move'); } else if (val === 'O') { cellElements[i].classList.add('O-move'); cellElements[i].classList.remove('X-move'); } else { cellElements[i].classList.remove('X-move', 'O-move'); } } } // Проверка победителя / ничьи function checkWinner(boardState) { const winPatterns = [ [0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6] ]; for (let pattern of winPatterns) { const [a,b,c] = pattern; if (boardState[a] && boardState[a] === boardState[b] && boardState[a] === boardState[c]) { return boardState[a]; } } if (boardState.every(cell => cell !== '')) return 'tie'; return null; } // Обновление текста статуса и индикатора function updateGameStatusMessage() { if (!gameActive) { const winner = checkWinner(board); if (winner === 'X') turnTextEl.innerHTML = `<span>✖</span> ПОБЕДА X!`; else if (winner === 'O') turnTextEl.innerHTML = `<span>◯</span> ПОБЕДА O!`; else if (winner === 'tie') turnTextEl.innerHTML = `<span>⚖️</span> НИЧЬЯ`; else turnTextEl.innerHTML = `<span>⏸️</span> ИГРА ОСТАНОВЛЕНА`; if (winner === 'X') gameStatusDiv.innerText = `✖ Крестики победили! Нажмите PLAY или НОВАЯ.`; else if (winner === 'O') gameStatusDiv.innerText = `◯ Нолики победили! Нажмите PLAY или НОВАЯ.`; else if (winner === 'tie') gameStatusDiv.innerText = `Ничья! Поле заполнено.`; else gameStatusDiv.innerText = `Игра не активна. Выберите символ и нажмите PLAY.`; return; } // игра активна: показываем чей ход if (currentPlayer === 'X') { turnTextEl.innerHTML = `<span>✖</span> ХОД X`; } else { turnTextEl.innerHTML = `<span>◯</span> ХОД O`; } // дополнительный текстовый статус if (gameMode === 'bot') { if (currentPlayer === playerSymbol) { gameStatusDiv.innerText = `Ваш ход (${playerSymbol})`; } else { gameStatusDiv.innerText = `Ход соперника (${aiSymbol}) ...`; } } else { // режим 2 игрока if ((currentPlayer === 'X' && playerSymbol === 'X') || (currentPlayer === 'O' && playerSymbol === 'O')) { gameStatusDiv.innerText = `Ваш ход (${currentPlayer})`; } else if ((currentPlayer === 'X' && playerSymbol === 'O') || (currentPlayer === 'O' && playerSymbol === 'X')) { gameStatusDiv.innerText = `Ход игрока за ${currentPlayer}`; } else { gameStatusDiv.innerText = `Ход: ${currentPlayer}`; } } } // завершение игры: блокировка ходов, установка gameActive false, обновление сообщения function endGame(winner) { gameActive = false; waitingForAI = false; updateGameStatusMessage(); } // сделать ход в ячейку (index) function makeMove(index, player) { if (!gameActive) return false; if (board[index] !== '') return false; if (player !== currentPlayer) return false; // ставим знак board[index] = player; updateBoardUI(); // проверка результата const winner = checkWinner(board); if (winner) { gameActive = false; waitingForAI = false; updateGameStatusMessage(); return true; } // смена хода currentPlayer = (currentPlayer === 'X') ? 'O' : 'X'; updateGameStatusMessage(); // после смены хода, если игра активна, и режим бот и ход принадлежит AI — вызываем AI ход if (gameActive && gameMode === 'bot' && currentPlayer === aiSymbol) { scheduleAIMove(); } return true; } // задержка перед ходом бота (естественный отклик) let aiTimeout = null; function scheduleAIMove() { if (aiTimeout) clearTimeout(aiTimeout); if (!gameActive) return; if (gameMode !== 'bot') return; if (currentPlayer !== aiSymbol) return; if (waitingForAI) return; waitingForAI = true; aiTimeout = setTimeout(() => { if (!gameActive || gameMode !== 'bot' || currentPlayer !== aiSymbol) { waitingForAI = false; return; } performAIMove(); waitingForAI = false; aiTimeout = null; }, 120); } // ИИ соображение: ставит первым, если игрок выбрал O (нолик) // по условию: "чтоб он ставил первым, когда человек выбирает круг" // То есть когда playerSymbol === 'O', то ИИ играет за X и должен ходить первым. // Реализуем: ИИ находит лучший ход (минимакс или простой блок+победа) // Используем продвинутый алгоритм (не рандом, а умный ИИ) function getBestAIMove(currentBoard, aiMark, humanMark) { // минимакс для сложности: ИИ непобедимый (но можно). // Для простоты и надёжности реализуем unbeatable AI через минимакс. function minimax(board, depth, isMaximizing, ai, human) { const result = checkWinner(board); if (result === ai) return 10 - depth; if (result === human) return depth - 10; if (result === 'tie') return 0; if (isMaximizing) { let best = -Infinity; for (let i = 0; i < 9; i++) { if (board[i] === '') { board[i] = ai; let score = minimax(board, depth+1, false, ai, human); board[i] = ''; best = Math.max(score, best); } } return best; } else { let best = Infinity; for (let i = 0; i < 9; i++) { if (board[i] === '') { board[i] = human; let score = minimax(board, depth+1, true, ai, human); board[i] = ''; best = Math.min(score, best); } } return best; } } let bestScore = -Infinity; let bestMove = -1; for (let i = 0; i < 9; i++) { if (currentBoard[i] === '') { currentBoard[i] = aiMark; let score = minimax(currentBoard, 0, false, aiMark, humanMark); currentBoard[i] = ''; if (score > bestScore) { bestScore = score; bestMove = i; } } } return bestMove; } function performAIMove() { if (!gameActive) return; if (gameMode !== 'bot') return; if (currentPlayer !== aiSymbol) return; let moveIndex = -1; // используем минимакс const boardStateCopy = [...board]; moveIndex = getBestAIMove(boardStateCopy, aiSymbol, playerSymbol); if (moveIndex !== -1 && board[moveIndex] === '') { makeMove(moveIndex, aiSymbol); } else { // fallback: первая свободная (на случай ошибки) for (let i = 0; i < 9; i++) { if (board[i] === '') { makeMove(i, aiSymbol); break; } } } } // сброс игрового состояния (очистка поля, но не сброс настроек символа и режима) function resetGameState(keepSettings = true) { if (aiTimeout) clearTimeout(aiTimeout); waitingForAI = false; board.fill(''); updateBoardUI(); // Игра не активна, пока не нажмут PLAY gameActive = false; // currentPlayer будет установлен в зависимости от правил выбора символов при старте через startGame() } // Запуск игры: активируем, определяем кто ходит первый с учётом выбора символа function startGame() { // отменяем ожидание AI if (aiTimeout) clearTimeout(aiTimeout); waitingForAI = false; // если игра уже активна, сделаем полный сброс поля, но с текущими настройками board.fill(''); updateBoardUI(); // установка зависимостей символов aiSymbol = (playerSymbol === 'X') ? 'O' : 'X'; // по условию: "чтоб он (ИИ) ставил первым, когда человек выбирает круг" // если игрок выбрал O (круг), то ИИ играет X и должен ходить первым // иначе если игрок выбрал X, то первый ход за игроком (X) let firstPlayer = 'X'; // по классике if (gameMode === 'bot') { if (playerSymbol === 'O') { // ИИ играет за X, ходит первым firstPlayer = 'X'; // и ИИ владеет X // но надо убедиться, что aiSymbol === 'X'? проверка: if (aiSymbol !== 'X') { // перестраховка: если игрок O, то ИИ должен быть X aiSymbol = 'X'; playerSymbol = 'O'; } } else if (playerSymbol === 'X') { // ИИ играет за O, первым ходит игрок (X) firstPlayer = 'X'; aiSymbol = 'O'; } } else { // режим 2 игрока: всегда первый X (игрок, выбравший X либо первый игрок) firstPlayer = 'X'; } currentPlayer = firstPlayer; gameActive = true; // Обновляем UI доски и статуса updateBoardUI(); updateGameStatusMessage(); // если режим бот и текущий ход принадлежит ИИ (согласно firstPlayer и aiSymbol) if (gameMode === 'bot' && gameActive && currentPlayer === aiSymbol) { scheduleAIMove(); } } // Обработчик клика по ячейке function handleCellClick(index) { if (!gameActive) return; // проверяем, может ли человек ходить if (gameMode === 'bot') { // ходит только если текущий игрок равен символу человека if (currentPlayer !== playerSymbol) return; } else { // режим дуо: оба игрока люди, ходит тот, чья очередь currentPlayer // но нужно чтоб оба могли кликать - разрешаем всегда, если ячейка пуста и игра активна if (currentPlayer !== 'X' && currentPlayer !== 'O') return; // дополнительно: если человек выбрал роль X, но играет за O? в дуо это не важно, любой может ходить за любой знак? // по логике игры 2 игрока: ходит X, потом O и тд, независимо от выбранного playerSymbol. // playerSymbol в дуо влияет только на отображение, но не на ограничение ходов. // сделаем так: в дуо любой может ставить любой знак, но очередность соблюдается (игроки сами следят) // Тем не менее, чтобы было честно, разрешаем ход, если совпадает с currentPlayer. if (currentPlayer !== 'X' && currentPlayer !== 'O') return; } // дополнительная проверка: если в режиме дуо но currentPlayer не совпадает? уже проверили if (board[index] !== '') return; // Делаем ход текущим игроком makeMove(index, currentPlayer); } // Обновить активный стиль выбора символа function updateSymbolUISelection() { symbolCards.forEach(card => { const sym = card.dataset.symbol; if (sym === playerSymbol) { card.classList.add('active'); } else { card.classList.remove('active'); } }); } function updateModeUISelection() { modeBtns.forEach(btn => { const modeVal = btn.dataset.mode; if (modeVal === gameMode) { btn.classList.add('selected-mode'); } else { btn.classList.remove('selected-mode'); } }); } // Обработчики выбора символа symbolCards.forEach(card => { card.addEventListener('click', (e) => { if (gameActive) { gameStatusDiv.innerText = 'Сначала завершите игру или нажмите НОВАЯ/PLAY'; return; } const sym = card.dataset.symbol; playerSymbol = sym; updateSymbolUISelection(); gameStatusDiv.innerText = `Выбран знак ${playerSymbol}. Нажмите PLAY для старта.`; // сброс ожидания }); }); // Выбор режима modeBtns.forEach(btn => { btn.addEventListener('click', (e) => { if (gameActive) { gameStatusDiv.innerText = 'Остановите игру (НОВАЯ или EXIT) перед сменой режима'; return; } const mode = btn.dataset.mode; gameMode = mode; updateModeUISelection(); gameStatusDiv.innerText = `Режим: ${gameMode === 'bot' ? 'против ИИ' : '2 игрока'}. Выберите знак и PLAY.`; resetGameState(true); updateBoardUI(); updateGameStatusMessage(); }); }); // PLAY - начинает новую игру с текущими настройками playBtn.addEventListener('click', () => { // полностью сбросить доску и активировать board.fill(''); updateBoardUI(); startGame(); }); // RESET (новая игра) - сбрасывает поле и запускает заново с текущими символами/режимом resetBtn.addEventListener('click', () => { if (aiTimeout) clearTimeout(aiTimeout); waitingForAI = false; board.fill(''); updateBoardUI(); startGame(); }); // EXIT — полный выход из активной игры, отключает возможность ходов и показывает стартовое меню exitBtn.addEventListener('click', () => { if (aiTimeout) clearTimeout(aiTimeout); waitingForAI = false; gameActive = false; board.fill(''); updateBoardUI(); currentPlayer = 'X'; gameStatusDiv.innerText = 'Игра остановлена. Выберите символ и нажмите PLAY.'; turnTextEl.innerHTML = `<span>⚙</span> EXIT`; }); // начальная инициализация createBoardUI(); resetGameState(); updateBoardUI(); playerSymbol = 'X'; aiSymbol = 'O'; gameMode = 'bot'; updateSymbolUISelection(); updateModeUISelection(); gameActive = false; gameStatusDiv.innerText = 'Выберите X или O, режим, затем PLAY'; turnTextEl.innerHTML = `<span>⚡</span> OX GAME`; // дополнительно: стартовое поле updateGameStatusMessage(); })(); </script> </body> </html>