/
dayekb
/
vibecoding_gomoku
Обзор
Документация
Войти
/
dayekb
/
vibecoding_gomoku
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
script.js
460 строк
16 KB
dayekb
upload files
03 сен 2025, 21:49
03 сен 2025, 21:49
a1c536b
Код
Авторство
О чём код?
class TicTacToeGame { constructor() { this.board = []; this.boardSize = 5; this.winCondition = 4; this.difficulty = 'medium'; this.playerIcon = '⭕'; this.aiIcon = '❌'; this.firstMove = 'player'; this.currentPlayer = 'player'; this.gameActive = false; this.stats = { wins: 0, losses: 0, draws: 0 }; this.initializeElements(); this.loadStats(); this.loadTheme(); this.setupEventListeners(); this.createBoard(); } initializeElements() { this.gameBoard = document.getElementById('gameBoard'); this.currentPlayerText = document.getElementById('currentPlayerText'); this.currentPlayerIcon = document.getElementById('currentPlayerIcon'); this.gameMessage = document.getElementById('gameMessage'); this.newGameBtn = document.getElementById('newGameBtn'); this.boardSizeSelect = document.getElementById('boardSize'); this.winConditionSelect = document.getElementById('winCondition'); this.difficultySelect = document.getElementById('difficulty'); this.firstMoveSelect = document.getElementById('firstMove'); this.darkThemeToggle = document.getElementById('darkThemeToggle'); this.winsElement = document.getElementById('wins'); this.lossesElement = document.getElementById('losses'); this.drawsElement = document.getElementById('draws'); } setupEventListeners() { this.newGameBtn.addEventListener('click', () => this.startNewGame()); this.boardSizeSelect.addEventListener('change', (e) => { this.boardSize = parseInt(e.target.value); this.createBoard(); }); this.winConditionSelect.addEventListener('change', (e) => { this.winCondition = parseInt(e.target.value); }); this.difficultySelect.addEventListener('change', (e) => { this.difficulty = e.target.value; }); this.firstMoveSelect.addEventListener('change', (e) => { this.firstMove = e.target.value; }); this.darkThemeToggle.addEventListener('change', (e) => { this.toggleTheme(e.target.checked); }); // Обработчики для radio-кнопок иконок document.querySelectorAll('input[name="playerIcon"]').forEach(radio => { radio.addEventListener('change', (e) => { if (e.target.checked) { this.playerIcon = e.target.value; this.updateCurrentPlayerDisplay(); this.checkIconConflict('player', e.target.value); } }); }); document.querySelectorAll('input[name="aiIcon"]').forEach(radio => { radio.addEventListener('change', (e) => { if (e.target.checked) { this.aiIcon = e.target.value; this.checkIconConflict('ai', e.target.value); } }); }); } createBoard() { this.gameBoard.innerHTML = ''; this.gameBoard.style.gridTemplateColumns = `repeat(${this.boardSize}, 1fr)`; this.board = Array(this.boardSize).fill().map(() => Array(this.boardSize).fill('')); for (let i = 0; i < this.boardSize; i++) { for (let j = 0; j < this.boardSize; j++) { const cell = document.createElement('button'); cell.className = 'cell'; cell.dataset.row = i; cell.dataset.col = j; cell.addEventListener('click', () => this.makeMove(i, j)); this.gameBoard.appendChild(cell); } } } startNewGame() { this.gameActive = true; this.currentPlayer = this.firstMove; this.createBoard(); this.updateCurrentPlayerDisplay(); if (this.currentPlayer === 'player') { this.showMessage('Ваш ход! Выберите клетку.'); } else { this.showMessage('Ход ИИ...'); setTimeout(() => this.makeAIMove(), 500); } } makeMove(row, col) { if (!this.gameActive || this.board[row][col] !== '') { return; } this.board[row][col] = this.currentPlayer; this.updateCell(row, col, this.currentPlayer === 'player' ? this.playerIcon : this.aiIcon); if (this.checkWin(row, col)) { this.endGame(this.currentPlayer); return; } if (this.checkDraw()) { this.endGame('draw'); return; } this.currentPlayer = this.currentPlayer === 'player' ? 'ai' : 'player'; this.updateCurrentPlayerDisplay(); if (this.currentPlayer === 'ai') { this.showMessage('Ход ИИ...'); setTimeout(() => this.makeAIMove(), 500); } else { this.showMessage('Ваш ход!'); } } makeAIMove() { if (!this.gameActive) return; const move = this.getAIMove(); if (move) { this.makeMove(move.row, move.col); } } getAIMove() { const moves = this.getAvailableMoves(); if (moves.length === 0) return null; // Проверяем, может ли ИИ выиграть for (let move of moves) { this.board[move.row][move.col] = 'ai'; if (this.checkWin(move.row, move.col)) { this.board[move.row][move.col] = ''; return move; } this.board[move.row][move.col] = ''; } // Проверяем, может ли игрок выиграть (блокируем) for (let move of moves) { this.board[move.row][move.col] = 'player'; if (this.checkWin(move.row, move.col)) { this.board[move.row][move.col] = ''; return move; } this.board[move.row][move.col] = ''; } // Выбираем ход в зависимости от сложности switch (this.difficulty) { case 'easy': return this.getRandomMove(moves); case 'medium': return Math.random() < 0.7 ? this.getStrategicMove(moves) : this.getRandomMove(moves); case 'hard': return Math.random() < 0.9 ? this.getStrategicMove(moves) : this.getRandomMove(moves); case 'expert': return this.getStrategicMove(moves); default: return this.getRandomMove(moves); } } getStrategicMove(moves) { // Приоритет: центр, углы, края const center = Math.floor(this.boardSize / 2); const corners = [ {row: 0, col: 0}, {row: 0, col: this.boardSize - 1}, {row: this.boardSize - 1, col: 0}, {row: this.boardSize - 1, col: this.boardSize - 1} ]; // Проверяем центр const centerMove = moves.find(move => move.row === center && move.col === center); if (centerMove) return centerMove; // Проверяем углы for (let corner of corners) { const cornerMove = moves.find(move => move.row === corner.row && move.col === corner.col); if (cornerMove) return cornerMove; } // Проверяем клетки рядом с уже занятыми for (let move of moves) { if (this.hasAdjacentPlayer(move.row, move.col)) { return move; } } return this.getRandomMove(moves); } hasAdjacentPlayer(row, col) { const directions = [ [-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1] ]; for (let [dr, dc] of directions) { const newRow = row + dr; const newCol = col + dc; if (this.isValidCell(newRow, newCol) && this.board[newRow][newCol] !== '') { return true; } } return false; } getRandomMove(moves) { return moves[Math.floor(Math.random() * moves.length)]; } getAvailableMoves() { const moves = []; for (let i = 0; i < this.boardSize; i++) { for (let j = 0; j < this.boardSize; j++) { if (this.board[i][j] === '') { moves.push({row: i, col: j}); } } } return moves; } checkWin(row, col) { const player = this.board[row][col]; if (player === '') return false; const directions = [ [0, 1], // горизонталь [1, 0], // вертикаль [1, 1], // диагональ \ [1, -1] // диагональ / ]; for (let [dr, dc] of directions) { let count = 1; // Проверяем в одном направлении let r = row + dr; let c = col + dc; while (this.isValidCell(r, c) && this.board[r][c] === player) { count++; r += dr; c += dc; } // Проверяем в противоположном направлении r = row - dr; c = col - dc; while (this.isValidCell(r, c) && this.board[r][c] === player) { count++; r -= dr; c -= dc; } if (count >= this.winCondition) { this.highlightWinningCells(row, col, dr, dc, count); return true; } } return false; } highlightWinningCells(row, col, dr, dc, count) { const player = this.board[row][col]; const cells = []; // Находим все выигрышные клетки let r = row; let c = col; let found = 0; // Идем в одном направлении while (this.isValidCell(r, c) && this.board[r][c] === player && found < this.winCondition) { cells.push({row: r, col: c}); r += dr; c += dc; found++; } // Идем в противоположном направлении r = row - dr; c = col - dc; found = 0; while (this.isValidCell(r, c) && this.board[r][c] === player && found < this.winCondition) { cells.unshift({row: r, col: c}); r -= dr; c -= dc; found++; } // Подсвечиваем выигрышные клетки cells.slice(0, this.winCondition).forEach(cell => { const cellElement = document.querySelector(`[data-row="${cell.row}"][data-col="${cell.col}"]`); if (cellElement) { cellElement.classList.add('winning'); } }); } isValidCell(row, col) { return row >= 0 && row < this.boardSize && col >= 0 && col < this.boardSize; } checkDraw() { for (let i = 0; i < this.boardSize; i++) { for (let j = 0; j < this.boardSize; j++) { if (this.board[i][j] === '') { return false; } } } return true; } updateCell(row, col, icon) { const cell = document.querySelector(`[data-row="${row}"][data-col="${col}"]`); if (cell) { cell.textContent = icon; cell.disabled = true; cell.classList.add(this.currentPlayer); } } updateCurrentPlayerDisplay() { if (this.currentPlayer === 'player') { this.currentPlayerText.textContent = 'Ваш ход'; this.currentPlayerIcon.textContent = this.playerIcon; } else { this.currentPlayerText.textContent = 'Ход ИИ'; this.currentPlayerIcon.textContent = this.aiIcon; } } endGame(winner) { this.gameActive = false; if (winner === 'player') { this.showMessage('🎉 Поздравляем! Вы выиграли!', 'win'); this.stats.wins++; } else if (winner === 'ai') { this.showMessage('😔 ИИ выиграл! Попробуйте еще раз!', 'lose'); this.stats.losses++; } else { this.showMessage('🤝 Ничья! Хорошая игра!', 'draw'); this.stats.draws++; } this.updateStats(); this.saveStats(); } showMessage(text, type = '') { this.gameMessage.textContent = text; this.gameMessage.className = `message ${type}`; } updateStats() { this.winsElement.textContent = this.stats.wins; this.lossesElement.textContent = this.stats.losses; this.drawsElement.textContent = this.stats.draws; } saveStats() { localStorage.setItem('ticTacToeStats', JSON.stringify(this.stats)); } loadStats() { const saved = localStorage.getItem('ticTacToeStats'); if (saved) { this.stats = JSON.parse(saved); this.updateStats(); } } loadTheme() { const savedTheme = localStorage.getItem('ticTacToeTheme'); if (savedTheme === 'dark') { this.darkThemeToggle.checked = true; document.body.classList.add('dark-theme'); } } toggleTheme(isDark) { if (isDark) { document.body.classList.add('dark-theme'); localStorage.setItem('ticTacToeTheme', 'dark'); } else { document.body.classList.remove('dark-theme'); localStorage.setItem('ticTacToeTheme', 'light'); } } checkIconConflict(changedPlayer, newIcon) { const otherPlayer = changedPlayer === 'player' ? 'ai' : 'player'; const otherIcon = changedPlayer === 'player' ? this.aiIcon : this.playerIcon; if (newIcon === otherIcon) { // Находим случайную доступную иконку для другого игрока const allIcons = ['❌', '⭕', '🔥', '⭐', '💎', '🎯', '🚀', '👑', '🤖', '💀', '⚡', '🌙', '☀️', '🎭']; const availableIcons = allIcons.filter(icon => icon !== newIcon); const randomIcon = availableIcons[Math.floor(Math.random() * availableIcons.length)]; // Обновляем иконку другого игрока if (otherPlayer === 'player') { this.playerIcon = randomIcon; document.querySelector(`input[name="playerIcon"][value="${randomIcon}"]`).checked = true; this.updateCurrentPlayerDisplay(); } else { this.aiIcon = randomIcon; document.querySelector(`input[name="aiIcon"][value="${randomIcon}"]`).checked = true; } // Показываем уведомление this.showMessage(`⚠️ Иконка ${otherPlayer === 'player' ? 'игрока' : 'ИИ'} автоматически изменена на ${randomIcon}`, 'info'); // Убираем уведомление через 3 секунды setTimeout(() => { if (this.gameMessage.textContent.includes('автоматически изменена')) { this.showMessage(''); } }, 3000); } } } // Инициализация игры при загрузке страницы document.addEventListener('DOMContentLoaded', () => { new TicTacToeGame(); });