From c2a44ba7c75c3ab84fd4f64410c15f50d6f920c5 Mon Sep 17 00:00:00 2001
From: icezhb <860435387@qq.com>
Date: Sun, 29 Dec 2024 17:44:22 +0800
Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E8=90=BD=E5=AD=90=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=EF=BC=9B=E6=96=B0=E5=A2=9E=E6=88=98=E7=BB=A9=EF=BC=9B?=
=?UTF-8?q?=E4=BC=98=E5=8C=96UI?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/App.vue | 379 ++++++++++++++++++++----------
src/components/GameStatistics.vue | 235 ++++++++----------
2 files changed, 360 insertions(+), 254 deletions(-)
diff --git a/src/App.vue b/src/App.vue
index 95161d6..29c3ced 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -76,25 +76,57 @@
-
🎉 游戏结束 🎉
-
- {{ getPlayerName(currentPlayer === 1 ? 2 : 1) }}胜利!
对手超时三次被判负
-
-
- {{ getPlayerName(winner) }}胜利!
- 第 {{ moveCount }} 手获胜
-
-
-
- {{ getTauntMessage() }}
-
-
@@ -197,6 +229,7 @@ export default {
winningStreaks: {
current: 0,
max: 0,
+ maxLose: 0,
player: 0,
computer: 0,
},
@@ -242,7 +275,15 @@ export default {
},
handleClick(row, col) {
- if (this.board[row][col].player !== 0) return
+ if (
+ !this.gameStarted ||
+ this.board[row][col].player !== 0 ||
+ this.winner ||
+ this.gameEnded || // 增加游戏结束的检查
+ (this.gameMode === 'pve' && this.isComputerTurn)
+ ) {
+ return
+ }
this.stopTimer()
@@ -313,7 +354,6 @@ export default {
handleTimeout(player) {
this.stopTimer()
- console.log('超时玩家:', player === 1 ? '黑方' : '白方')
// 记录当前状态
const isPlayerTurn =
@@ -457,7 +497,6 @@ export default {
if (!this.isValidPosition(newRow, newCol)) break
const nextCell = this.board[newRow][newCol]
if (nextCell.player !== playerType) {
- console.log('遇到非己方棋子:', newRow, newCol)
break
}
pieces.push([newRow, newCol])
@@ -471,20 +510,16 @@ export default {
if (!this.isValidPosition(newRow, newCol)) break
const nextCell = this.board[newRow][newCol]
if (nextCell.player !== playerType) {
- console.log('遇到非己方棋子:', newRow, newCol)
break
}
pieces.unshift([newRow, newCol])
count++
}
- console.log('找到的棋子:', pieces)
-
// 严格检查是否形成五子连珠
const isWin = count === 5 && pieces.length === 5
if (isWin) {
- console.log('确认五子连珠!玩家:', playerType, '位置:', pieces)
this.playWinningAnimation()
return {
isWin: true,
@@ -520,14 +555,12 @@ export default {
// 检查相邻棋子的距离是否严格为1,且方向一致
if (curr[0] - prev[0] !== dx || curr[1] - prev[1] !== dy) {
- console.log('棋子不连续或方向不一致:', curr, prev)
return false
}
// 检查是否属于同一玩家
const currPiece = this.board[curr[0]][curr[1]]
if (this.isPlayerPiece(currPiece) !== isHuman) {
- console.log('棋子属于不同玩家:', curr)
return false
}
}
@@ -536,13 +569,11 @@ export default {
// 验证获胜棋子
validateWinningPieces(pieces, player) {
- console.log('验证获胜棋子:', pieces, '期望玩家:', player)
const validation = pieces.map(([row, col]) => ({
position: [row, col],
valid: this.isValidPosition(row, col),
piece: this.board[row][col],
}))
- console.log('验证详情:', validation)
return pieces.every(([row, col]) => {
return this.isValidPosition(row, col) && this.board[row][col].player === player
})
@@ -1200,6 +1231,10 @@ export default {
if (this.currentPlayer === (this.isPlayerBlack ? 1 : 2)) {
stats.computerWins++
stats.winningStreaks.current = Math.min(0, stats.winningStreaks.current) - 1
+ stats.winningStreaks.maxLose = Math.max(
+ stats.winningStreaks.maxLose,
+ Math.abs(stats.winningStreaks.current),
+ )
} else {
stats.playerWins++
stats.winningStreaks.current = Math.max(0, stats.winningStreaks.current) + 1
@@ -1214,8 +1249,8 @@ export default {
} else {
stats.computerWins++
stats.winningStreaks.current = Math.min(0, stats.winningStreaks.current) - 1
- stats.winningStreaks.computer = Math.max(
- stats.winningStreaks.computer,
+ stats.winningStreaks.maxLose = Math.max(
+ stats.winningStreaks.maxLose,
Math.abs(stats.winningStreaks.current),
)
}
@@ -1294,10 +1329,27 @@ export default {
.modal-content {
background: white;
- padding: 40px;
+ padding: 30px;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
animation: modalFadeIn 0.3s ease-out;
+ max-height: 90vh;
+ overflow-y: auto;
+ width: 90%;
+ max-width: 600px;
+ position: relative;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.modal-content::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 4px;
+ background: linear-gradient(90deg, #4caf50, #45a049);
+ border-radius: 16px 16px 0 0;
}
.modal-content h2 {
@@ -1655,105 +1707,146 @@ export default {
z-index: 1000;
}
-.victory-modal .modal-content {
+.modal-content {
background: white;
- padding: 40px;
+ padding: 30px;
border-radius: 16px;
- text-align: center;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
animation: modalFadeIn 0.3s ease-out;
+ max-height: 90vh;
+ overflow-y: auto;
+ width: 90%;
+ max-width: 600px;
}
-.victory-modal h2 {
- font-size: 28px;
- color: #2c3e50;
+.victory-header {
+ text-align: center;
margin-bottom: 20px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ padding-top: 10px;
+}
+
+.victory-emoji {
+ font-size: 48px;
+ animation: bounce 1s infinite;
+}
+
+.victory-header h2 {
+ font-size: 24px;
+ color: #2c3e50;
+ margin: 0;
+ font-weight: 600;
+}
+
+.victory-info {
+ display: flex;
+ flex-direction: column;
+}
+
+.victory-result {
+ background: linear-gradient(145deg, #f8f9fa, #e9ecef);
+ padding: 15px;
+ border-radius: 12px;
+ text-align: center;
+ font-size: 16px;
+ color: #2c3e50;
+ font-weight: 500;
+ margin-bottom: 20px;
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
+ border: 1px solid rgba(0, 0, 0, 0.05);
+}
+
+.divider {
+ height: 1px;
+ background: linear-gradient(90deg, transparent, #e0e0e0, transparent);
+ margin: 15px 0;
+}
+
+.victory-stats {
+ margin: 15px 0;
+}
+
+.taunt-message {
+ margin: 15px 0;
+ padding: 12px;
+ background: linear-gradient(145deg, #e53935, #d32f2f);
+ color: white;
+ border-radius: 8px;
+ font-style: italic;
+ font-size: 16px;
+ font-weight: bold;
+ text-align: center;
+ box-shadow: 0 4px 12px rgba(229, 57, 53, 0.2);
+ animation: slideIn 0.3s ease-out;
}
.modal-buttons {
display: flex;
- gap: 20px;
+ gap: 15px;
justify-content: center;
- margin-top: 30px;
+ margin-top: 20px;
+ padding: 10px 0;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.modal-button {
- padding: 12px 24px;
- font-size: 16px;
+ padding: 10px 20px;
+ font-size: 14px;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
- background-color: #6c757d;
color: white;
-}
-
-.modal-button.primary {
- background-color: #4caf50;
-}
-
-.start-screen {
- text-align: center;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- z-index: 10;
- background: rgba(255, 255, 255, 0.95);
- padding: 25px 40px;
- border-radius: 12px;
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
- backdrop-filter: blur(4px);
- min-width: 240px;
-}
-
-.start-button {
- padding: 12px 36px;
- font-size: 18px;
- background: linear-gradient(145deg, #4caf50, #45a049);
- color: white;
- border: none;
- border-radius: 8px;
- cursor: pointer;
- transition: all 0.3s ease;
- margin-bottom: 12px;
- box-shadow: 0 4px 12px rgba(76, 175, 80, 0.2);
- width: 100%;
-}
-
-.start-button:hover {
- transform: translateY(-2px);
- box-shadow: 0 6px 15px rgba(76, 175, 80, 0.3);
-}
-
-.turn-info {
- color: #2c3e50;
- font-size: 15px;
- margin-top: 5px;
- opacity: 0.8;
+ min-width: 100px;
+ font-weight: 500;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
}
.modal-button.secondary {
background-color: #6c757d;
-}
-
-.modal-button.secondary:hover {
- background-color: #5a6268;
- transform: translateY(-2px);
+ box-shadow: 0 2px 8px rgba(108, 117, 125, 0.2);
+ background: linear-gradient(145deg, #6c757d, #5a6268);
}
.modal-button.primary {
background-color: #4caf50;
+ box-shadow: 0 2px 8px rgba(76, 175, 80, 0.2);
+ background: linear-gradient(145deg, #4caf50, #45a049);
}
-.modal-button.primary:hover {
- background-color: #45a049;
- transform: translateY(-2px);
+.modal-button.exit {
+ background: linear-gradient(145deg, #dc3545, #c82333);
+ box-shadow: 0 2px 8px rgba(220, 53, 69, 0.2);
}
.modal-button:hover {
transform: translateY(-2px);
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
+ filter: brightness(1.1);
+}
+
+@keyframes bounce {
+ 0%,
+ 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-5px);
+ }
+}
+
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
}
.view-result {
@@ -1780,32 +1873,72 @@ export default {
box-shadow: 0 6px 15px rgba(76, 175, 80, 0.3);
}
-.taunt-message {
- margin-top: 15px;
- padding: 10px;
- background: linear-gradient(145deg, #e53935, #d32f2f);
- color: white;
- border-radius: 8px;
- font-style: italic;
- animation: tauntPulse 2s infinite;
- font-size: 18px;
- font-weight: bold;
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
- box-shadow: 0 4px 15px rgba(229, 57, 53, 0.4);
+.exit {
+ background-color: #dc3545;
}
-@keyframes tauntPulse {
- from {
- transform: scale(1);
- box-shadow: 0 4px 15px rgba(229, 57, 53, 0.4);
- }
- 50% {
- transform: scale(1.05);
- box-shadow: 0 6px 20px rgba(229, 57, 53, 0.6);
- }
- 100% {
- transform: scale(1);
- box-shadow: 0 4px 15px rgba(229, 57, 53, 0.4);
- }
+.start-screen {
+ text-align: center;
+ z-index: 10;
+ background: rgba(255, 255, 255, 0.95);
+ /* padding: 25px 40px; */
+ /* border-radius: 12px; */
+ /* box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); */
+ backdrop-filter: blur(4px);
+ min-width: 240px;
+ margin: 20px auto;
+ width: 80%;
+ max-width: 300px;
+ position: relative;
+ /* border: 1px solid rgba(0, 0, 0, 0.1); */
+}
+
+.start-button {
+ padding: 12px 36px;
+ font-size: 18px;
+ background: linear-gradient(145deg, #43a047, #2e7d32);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ margin-bottom: 12px;
+ box-shadow: 0 4px 15px rgba(67, 160, 71, 0.3);
+ width: 100%;
+ font-weight: 500;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
+ position: relative;
+ overflow: hidden;
+}
+
+.start-button:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(67, 160, 71, 0.4);
+ filter: brightness(1.1);
+}
+
+.start-button::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ transition: 0.5s;
+}
+
+.start-button:hover::after {
+ left: 100%;
+}
+
+.turn-info {
+ color: #2c3e50;
+ font-size: 15px;
+ margin-top: 5px;
+ font-weight: 500;
+ padding: 8px;
+ background: rgba(0, 0, 0, 0.03);
+ border-radius: 4px;
}
diff --git a/src/components/GameStatistics.vue b/src/components/GameStatistics.vue
index 996135b..1c172e6 100644
--- a/src/components/GameStatistics.vue
+++ b/src/components/GameStatistics.vue
@@ -1,53 +1,49 @@
-
战绩统计
-
-
总对局
-
{{ totalGames }}
-
局
-
-
-
胜率
-
- {{ formatPercent(winRate) }}
+
+
+
总对局
+
{{ totalGames }}
+
局
-
%
-
-
-
玩家胜场
-
{{ playerWins }}
-
胜
-
-
-
电脑胜场
-
{{ computerWins }}
-
胜
-
-
-
最大连胜
-
{{ maxStreak }}
-
连胜
-
-
-
平均步数
-
{{ avgMoves }}
-
步
-
-
-
-
胜率分布
-
-
-
玩家
-
{{ formatPercent(playerWinRate) }}%
+
+
胜率
+
+ {{ formatPercent(winRate) }}
+
+
%
-
-
电脑
-
{{ formatPercent(computerWinRate) }}%
+
+
玩家胜场
+
{{ playerWins }}
+
胜
+
+
+
电脑胜场
+
{{ computerWins }}
+
胜
+
+
+
+
+
+
最大连胜
+
{{ maxStreak }}
+
连胜
+
+
+
最大连败
+
{{ maxLoseStreak }}
+
连败
+
+
+
平均步数
+
{{ avgMoves }}
+
步
@@ -76,6 +72,9 @@ export default {
maxStreak() {
return this.stats?.winningStreaks?.max || 0
},
+ maxLoseStreak() {
+ return this.stats?.winningStreaks?.maxLose || 0
+ },
avgMoves() {
return this.stats?.avgMoves || 0
},
@@ -83,20 +82,6 @@ export default {
if (!this.totalGames) return 0
return (this.playerWins / this.totalGames) * 100
},
- playerWinRate() {
- if (!this.totalGames) return 0
- return (this.playerWins / this.totalGames) * 100
- },
- computerWinRate() {
- if (!this.totalGames) return 0
- return (this.computerWins / this.totalGames) * 100
- },
- playerBarWidth() {
- return `${Math.max(this.playerWinRate, 20)}%`
- },
- computerBarWidth() {
- return `${Math.max(this.computerWinRate, 20)}%`
- },
},
methods: {
formatPercent(value) {
@@ -108,49 +93,69 @@ export default {