Initial commit: Add basic tic-tac-toe game structure and documentation

This commit is contained in:
2026-01-28 09:38:44 +00:00
parent 2761b915a8
commit ec413cb712
5 changed files with 426 additions and 2 deletions

57
PROJECT_PLAN.md Normal file
View File

@@ -0,0 +1,57 @@
# 井字过三关游戏项目计划
## 项目概述
开发一个基于Web的井字过三关游戏提供流畅的游戏体验和直观的用户界面。
## 项目目标
- 实现经典的井字棋游戏逻辑
- 提供良好的用户体验
- 支持多平台访问
- 包含游戏统计功能
## 技术架构
### 前端技术栈
- HTML5: 结构化页面内容
- CSS3: 样式设计和动画效果
- JavaScript ES6+: 游戏逻辑和交互功能
- LocalStorage: 存储游戏统计数据
### 设计原则
- 响应式设计
- 用户友好界面
- 高性能
- 可维护性
## 开发阶段
### 阶段一:基础框架搭建
- [ ] 创建HTML页面结构
- [ ] 设计CSS样式
- [ ] 初始化JavaScript文件
### 阶段二:核心游戏逻辑
- [ ] 实现游戏棋盘
- [ ] 实现玩家轮流下棋
- [ ] 实现胜利判定算法
- [ ] 实现平局判定
### 阶段三:用户界面优化
- [ ] 添加游戏状态提示
- [ ] 实现游戏重置功能
- [ ] 添加动画效果
### 阶段四:功能增强
- [ ] 实现游戏统计功能
- [ ] 添加本地存储
- [ ] 实现响应式设计
### 阶段五:测试与优化
- [ ] 功能测试
- [ ] 兼容性测试
- [ ] 性能优化
- [ ] 用户体验优化
## 交付成果
- 完整的井字过三关游戏Web应用
- 项目文档
- 部署指南

View File

@@ -1,3 +1,36 @@
# tic-tac-toe-game
# 网页版井字过三关游戏
网页版井字过三关游戏
## 项目介绍
这是一个基于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. 测试和优化

37
index.html Normal file
View File

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>井字过三关游戏</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>井字过三关游戏</h1>
<div class="game-info">
<div class="current-player">当前玩家: <span id="current-player">X</span></div>
<div class="game-status" id="game-status">游戏中</div>
</div>
<div class="score-board">
<div class="player-score">
玩家 X: <span id="score-x">0</span>
</div>
<div class="player-score">
玩家 O: <span id="score-o">0</span>
</div>
<div class="draws">
平局: <span id="score-draw">0</span>
</div>
</div>
<div class="game-board" id="game-board">
<!-- Game board will be generated by JavaScript -->
</div>
<div class="game-controls">
<button id="reset-btn" class="btn">新游戏</button>
<button id="reset-scores-btn" class="btn">重置分数</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

142
script.js Normal file
View File

@@ -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();
});

155
styles.css Normal file
View File

@@ -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;
}
}