From ec413cb712a631228c9075ac46458d68880e2ec7 Mon Sep 17 00:00:00 2001 From: moltbot Date: Wed, 28 Jan 2026 09:38:44 +0000 Subject: [PATCH] Initial commit: Add basic tic-tac-toe game structure and documentation --- PROJECT_PLAN.md | 57 ++++++++++++++++++ README.md | 37 +++++++++++- index.html | 37 ++++++++++++ script.js | 142 ++++++++++++++++++++++++++++++++++++++++++++ styles.css | 155 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 426 insertions(+), 2 deletions(-) create mode 100644 PROJECT_PLAN.md create mode 100644 index.html create mode 100644 script.js create mode 100644 styles.css diff --git a/PROJECT_PLAN.md b/PROJECT_PLAN.md new file mode 100644 index 0000000..cc8d196 --- /dev/null +++ b/PROJECT_PLAN.md @@ -0,0 +1,57 @@ +# 井字过三关游戏项目计划 + +## 项目概述 +开发一个基于Web的井字过三关游戏,提供流畅的游戏体验和直观的用户界面。 + +## 项目目标 +- 实现经典的井字棋游戏逻辑 +- 提供良好的用户体验 +- 支持多平台访问 +- 包含游戏统计功能 + +## 技术架构 +### 前端技术栈 +- HTML5: 结构化页面内容 +- CSS3: 样式设计和动画效果 +- JavaScript ES6+: 游戏逻辑和交互功能 +- LocalStorage: 存储游戏统计数据 + +### 设计原则 +- 响应式设计 +- 用户友好界面 +- 高性能 +- 可维护性 + +## 开发阶段 + +### 阶段一:基础框架搭建 +- [ ] 创建HTML页面结构 +- [ ] 设计CSS样式 +- [ ] 初始化JavaScript文件 + +### 阶段二:核心游戏逻辑 +- [ ] 实现游戏棋盘 +- [ ] 实现玩家轮流下棋 +- [ ] 实现胜利判定算法 +- [ ] 实现平局判定 + +### 阶段三:用户界面优化 +- [ ] 添加游戏状态提示 +- [ ] 实现游戏重置功能 +- [ ] 添加动画效果 + +### 阶段四:功能增强 +- [ ] 实现游戏统计功能 +- [ ] 添加本地存储 +- [ ] 实现响应式设计 + +### 阶段五:测试与优化 +- [ ] 功能测试 +- [ ] 兼容性测试 +- [ ] 性能优化 +- [ ] 用户体验优化 + +## 交付成果 +- 完整的井字过三关游戏Web应用 +- 项目文档 +- 部署指南 \ No newline at end of file diff --git a/README.md b/README.md index 7430839..233c026 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,36 @@ -# tic-tac-toe-game +# 网页版井字过三关游戏 -网页版井字过三关游戏 \ No newline at end of file +## 项目介绍 +这是一个基于Web的井字过三关游戏(Tic-Tac-Toe),玩家可以在浏览器中享受经典的井字棋游戏。该游戏支持双人对战模式,具有直观的用户界面和流畅的游戏体验。 + +## 游戏玩法 +1. 游戏在一个3x3的网格上进行 +2. 两名玩家轮流放置自己的标记(X或O) +3. 首先在横、竖或对角线上连成一线的玩家获胜 +4. 若9个格子填满仍未分出胜负,则为平局 + +## 技术架构 +- 前端:HTML5, CSS3, JavaScript (ES6+) +- 无需后端:纯前端实现 +- 响应式设计:支持桌面和移动设备 +- 本地存储:保存游戏统计数据 + +## 项目特性 +- 直观的用户界面 +- 实时游戏状态显示 +- 游戏统计跟踪 +- 响应式设计 +- 无刷新页面交互 + +## 安装与运行 +1. 克隆项目 +2. 直接在浏览器中打开index.html +3. 开始游戏 + +## 项目计划 +1. 设计游戏界面 +2. 实现核心游戏逻辑 +3. 添加游戏统计功能 +4. 实现响应式设计 +5. 添加音效和动画效果 +6. 测试和优化 \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..56e5e8b --- /dev/null +++ b/index.html @@ -0,0 +1,37 @@ + + + + + + 井字过三关游戏 + + + +
+

井字过三关游戏

+
+
当前玩家: X
+
游戏中
+
+
+
+ 玩家 X: 0 +
+
+ 玩家 O: 0 +
+
+ 平局: 0 +
+
+
+ +
+
+ + +
+
+ + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..6c23f7b --- /dev/null +++ b/script.js @@ -0,0 +1,142 @@ +class TicTacToeGame { + constructor() { + this.board = ['', '', '', '', '', '', '', '', '']; + this.currentPlayer = 'X'; + this.gameActive = true; + this.scores = { + X: parseInt(localStorage.getItem('scoreX') || '0'), + O: parseInt(localStorage.getItem('scoreO') || '0'), + draw: parseInt(localStorage.getItem('scoreDraw') || '0') + }; + + this.initializeGame(); + } + + initializeGame() { + this.renderBoard(); + this.updateScores(); + this.updateCurrentPlayer(); + + // Add event listeners + document.getElementById('reset-btn').addEventListener('click', () => this.resetGame()); + document.getElementById('reset-scores-btn').addEventListener('click', () => this.resetScores()); + } + + renderBoard() { + const gameBoard = document.getElementById('game-board'); + gameBoard.innerHTML = ''; + + this.board.forEach((cell, index) => { + const cellElement = document.createElement('div'); + cellElement.classList.add('cell'); + cellElement.textContent = cell; + + if (cell === 'X') cellElement.classList.add('x'); + if (cell === 'O') cellElement.classList.add('o'); + + cellElement.addEventListener('click', () => this.makeMove(index)); + gameBoard.appendChild(cellElement); + }); + } + + makeMove(index) { + if (this.board[index] !== '' || !this.gameActive) return; + + this.board[index] = this.currentPlayer; + this.renderBoard(); + + const winResult = this.checkWinner(); + if (winResult.isWin) { + this.handleWin(winResult.winner, winResult.winningCells); + } else if (this.isBoardFull()) { + this.handleDraw(); + } else { + this.switchPlayer(); + } + } + + checkWinner() { + const winPatterns = [ + [0, 1, 2], [3, 4, 5], [6, 7, 8], // Rows + [0, 3, 6], [1, 4, 7], [2, 5, 8], // Columns + [0, 4, 8], [2, 4, 6] // Diagonals + ]; + + for (const pattern of winPatterns) { + const [a, b, c] = pattern; + if (this.board[a] && this.board[a] === this.board[b] && this.board[a] === this.board[c]) { + return { isWin: true, winner: this.board[a], winningCells: pattern }; + } + } + + return { isWin: false, winner: null, winningCells: [] }; + } + + isBoardFull() { + return this.board.every(cell => cell !== ''); + } + + handleWin(winner, winningCells) { + this.gameActive = false; + this.scores[winner]++; + localStorage.setItem(`score${winner}`, this.scores[winner].toString()); + + // Highlight winning cells + const cells = document.querySelectorAll('.cell'); + winningCells.forEach(index => { + cells[index].classList.add('winning-cell'); + }); + + document.getElementById('game-status').textContent = `玩家 ${winner} 获胜!`; + this.updateScores(); + } + + handleDraw() { + this.gameActive = false; + this.scores.draw++; + localStorage.setItem('scoreDraw', this.scores.draw.toString()); + + document.getElementById('game-status').textContent = '平局!'; + this.updateScores(); + } + + switchPlayer() { + this.currentPlayer = this.currentPlayer === 'X' ? 'O' : 'X'; + this.updateCurrentPlayer(); + } + + updateCurrentPlayer() { + document.getElementById('current-player').textContent = this.currentPlayer; + } + + resetGame() { + this.board = ['', '', '', '', '', '', '', '', '']; + this.currentPlayer = 'X'; + this.gameActive = true; + + document.getElementById('game-status').textContent = '游戏中'; + this.renderBoard(); + this.updateCurrentPlayer(); + } + + resetScores() { + this.scores = { X: 0, O: 0, draw: 0 }; + localStorage.setItem('scoreX', '0'); + localStorage.setItem('scoreO', '0'); + localStorage.setItem('scoreDraw', '0'); + + this.updateScores(); + this.resetGame(); + } + + updateScores() { + document.getElementById('score-x').textContent = this.scores.X; + document.getElementById('score-o').textContent = this.scores.O; + document.getElementById('score-draw').textContent = this.scores.draw; + } +} + +// Initialize the game when the page loads +document.addEventListener('DOMContentLoaded', () => { + new TicTacToeGame(); +}); \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..c6f96c1 --- /dev/null +++ b/styles.css @@ -0,0 +1,155 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Arial', sans-serif; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; +} + +.container { + background: white; + border-radius: 15px; + padding: 30px; + box-shadow: 0 20px 40px rgba(0,0,0,0.1); + text-align: center; + max-width: 500px; + width: 90%; +} + +h1 { + color: #333; + margin-bottom: 20px; + font-size: 2.5em; +} + +.game-info { + display: flex; + justify-content: space-around; + margin-bottom: 20px; + font-size: 1.2em; + font-weight: bold; +} + +.current-player { + color: #667eea; +} + +.game-status { + color: #764ba2; +} + +.score-board { + display: flex; + justify-content: space-around; + margin-bottom: 20px; + padding: 10px; + background: #f8f9fa; + border-radius: 8px; +} + +.player-score, .draws { + font-weight: bold; +} + +.game-board { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-gap: 10px; + max-width: 300px; + margin: 0 auto 20px; +} + +.cell { + width: 90px; + height: 90px; + border: 2px solid #ddd; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + font-size: 2em; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; + background: #fff; +} + +.cell:hover { + background: #f0f0f0; + transform: scale(1.05); +} + +.cell.x { + color: #e74c3c; +} + +.cell.o { + color: #3498db; +} + +.winning-cell { + background-color: #d4edda; + animation: pulse 0.6s ease-in-out; +} + +@keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } +} + +.game-controls { + display: flex; + gap: 10px; + justify-content: center; +} + +.btn { + padding: 12px 24px; + font-size: 1em; + border: none; + border-radius: 6px; + cursor: pointer; + transition: all 0.3s ease; + background: #667eea; + color: white; + font-weight: bold; +} + +.btn:hover { + background: #5a6fd8; + transform: translateY(-2px); +} + +.btn:active { + transform: translateY(0); +} + +/* Responsive design */ +@media (max-width: 500px) { + .container { + padding: 20px; + } + + h1 { + font-size: 2em; + } + + .cell { + width: 80px; + height: 80px; + font-size: 1.5em; + } + + .game-info { + flex-direction: column; + gap: 10px; + } +} \ No newline at end of file