第七代电脑

This commit is contained in:
icezhb 2024-12-29 12:47:05 +08:00
parent 617344cbeb
commit 43d761d49a
4 changed files with 310 additions and 1451 deletions

1420
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,6 @@
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-plugin-vue": "^9.30.0", "eslint-plugin-vue": "^9.30.0",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"vite": "^6.0.5", "vite": "^6.0.5"
"vite-plugin-vue-devtools": "^7.6.8"
} }
} }

View File

@ -84,6 +84,12 @@
{{ getPlayerName(winner) }}胜利<br /> {{ getPlayerName(winner) }}胜利<br />
{{ moveCount }} 手获胜 {{ moveCount }} 手获胜
</p> </p>
<p
v-if="gameMode === 'pve' && winner !== (isPlayerBlack ? 1 : 2)"
class="taunt-message"
>
{{ getTauntMessage() }}
</p>
<div class="modal-buttons"> <div class="modal-buttons">
<button @click="closeModal" class="modal-button secondary">查看棋盘</button> <button @click="closeModal" class="modal-button secondary">查看棋盘</button>
<button @click="playAgain" class="modal-button primary">再来一局</button> <button @click="playAgain" class="modal-button primary">再来一局</button>
@ -138,6 +144,30 @@ export default {
gameEnded: false, gameEnded: false,
lastMove: null, // lastMove: null, //
lastWinner: null, // lastWinner: null, //
taunts: {
computerWin: [
'这就是你的全部实力吗?太让我失望了!',
'或许你该找个AI初级版本玩玩',
'我都觉得自己下棋太温柔了...',
'你的棋力比我的运算量还小呢!',
'要不要我让你三手?',
],
timeoutWin: [
'思考太久对身体不好哦~',
'看来是我太强了,让你想不出对策?',
'时间管理也是实力的一部分呢!',
],
quickWin: [
'这么快就结束了?我都还没热身呢!',
'你确定你不是在帮我练习?',
'我建议你先去看看五子棋入门教程...',
],
closeWin: [
'差一点就被你赢了呢...骗你的!',
'让你以为有机会赢,这样才有趣嘛!',
'看起来很激烈?其实我一直在掌控局面哦!',
],
},
} }
}, },
@ -455,52 +485,238 @@ export default {
evaluatePosition(row, col, player) { evaluatePosition(row, col, player) {
let score = 0 let score = 0
const directions = [ const directions = [
[1, 0], // [1, 0],
[0, 1], // [0, 1],
[1, 1], // [1, 1],
[1, -1], // [1, -1],
] ]
//
const opponent = player === 1 ? 2 : 1 const opponent = player === 1 ? 2 : 1
let defenseScore = 0 let defenseScore = 0
let attackScore = 0 let attackScore = 0
//
const centerWeight = Math.max(0, 15 - (Math.abs(7 - row) + Math.abs(7 - col))) * 15
//
const complexPatterns = this.checkComplexPatterns(row, col, player)
if (complexPatterns.doubleFour) attackScore += 150000
if (complexPatterns.tripleThree) attackScore += 40000
// //
for (const [dx, dy] of directions) { for (const [dx, dy] of directions) {
attackScore += this.evaluateDirection(row, col, dx, dy, player) attackScore += this.evaluateDirection(row, col, dx, dy, player)
const pattern = this.checkPattern(row, col, dx, dy, player)
if (pattern.liveFour) attackScore += 120000
if (pattern.liveThree) attackScore += 20000
if (pattern.sleepingFour) attackScore += 15000
if (pattern.doubleThree) attackScore += 25000
} }
// //
for (const [dx, dy] of directions) { for (const [dx, dy] of directions) {
defenseScore += this.evaluateDirection(row, col, dx, dy, opponent) defenseScore += this.evaluateDirection(row, col, dx, dy, opponent)
const pattern = this.checkPattern(row, col, dx, dy, opponent)
if (pattern.liveFour) defenseScore += 110000
if (pattern.liveThree) defenseScore += 18000
if (pattern.sleepingFour) defenseScore += 14000
if (pattern.doubleThree) defenseScore += 22000
} }
// //
if (defenseScore >= 1000) { if (defenseScore >= 80000) {
// score = Math.max(attackScore * 1.3, defenseScore * 2)
score = Math.max(attackScore, defenseScore * 1.2) // } else if (defenseScore >= 15000) {
} else if (defenseScore >= 500) { score = attackScore * 1.8 + defenseScore * 1.2
//
score = Math.max(attackScore, defenseScore * 1.1)
} else { } else {
score = Math.max(attackScore * 1.1, defenseScore) // score = attackScore * 2.5 + defenseScore * 0.4
} }
// //
const centerWeight = 15 - (Math.abs(7 - row) + Math.abs(7 - col)) score += centerWeight * 20
score += centerWeight * 3 // const connectivityScore = this.evaluateConnectivity(row, col, player)
score += connectivityScore * 250
// //
const nearbyScore = this.evaluateNearbyPieces(row, col) if (this.moveCount < 10) {
score += nearbyScore score += this.evaluateOpeningPosition(row, col) * 400
}
//
if (this.moveCount >= 10 && this.moveCount < 25) {
score *= 1.2
}
return score return score
}, },
evaluateNearbyPieces(row, col) { //
let score = 0 checkComplexPatterns(row, col, player) {
const range = 2 // 2 const patterns = {
doubleFour: false, //
tripleThree: false, //
}
let fourCount = 0
let threeCount = 0
const directions = [
[1, 0],
[0, 1],
[1, 1],
[1, -1],
]
this.board[row][col] = player
for (const [dx, dy] of directions) {
const pattern = this.checkPattern(row, col, dx, dy, player)
if (pattern.liveFour || pattern.sleepingFour) fourCount++
if (pattern.liveThree) threeCount++
}
this.board[row][col] = 0
patterns.doubleFour = fourCount >= 2
patterns.tripleThree = threeCount >= 3
return patterns
},
//
evaluateOpeningPosition(row, col) {
//
const centerDist = Math.sqrt(Math.pow(row - 7, 2) + Math.pow(col - 7, 2))
if (centerDist <= 4) return 1
if (centerDist <= 6) return 0.7
return 0.3
},
//
checkPattern(row, col, dx, dy, player) {
const pattern = {
liveFour: false, //
liveThree: false, //
sleepingFour: false, //
doubleThree: false, //
}
//
this.board[row][col] = player
let consecutive = 1
let space = false
let blocked = 0
//
for (let i = 1; i <= 4; i++) {
const newRow = row + dx * i
const newCol = col + dy * i
if (!this.isValidPosition(newRow, newCol)) {
blocked++
break
}
if (this.board[newRow][newCol] === player) {
consecutive++
} else if (this.board[newRow][newCol] === 0) {
space = true
break
} else {
blocked++
break
}
}
//
for (let i = 1; i <= 4; i++) {
const newRow = row - dx * i
const newCol = col - dy * i
if (!this.isValidPosition(newRow, newCol)) {
blocked++
break
}
if (this.board[newRow][newCol] === player) {
consecutive++
} else if (this.board[newRow][newCol] === 0) {
space = true
break
} else {
blocked++
break
}
}
//
this.board[row][col] = 0
//
if (consecutive >= 4 && blocked === 0) pattern.liveFour = true
if (consecutive >= 4 && blocked === 1) pattern.sleepingFour = true
if (consecutive === 3 && blocked === 0) pattern.liveThree = true
if (this.checkDoubleThree(row, col, player)) pattern.doubleThree = true
return pattern
},
//
checkDoubleThree(row, col, player) {
let threeCount = 0
const directions = [
[
[1, 0],
[-1, 0],
], //
[
[0, 1],
[0, -1],
], //
[
[1, 1],
[-1, -1],
], // -
[
[1, -1],
[-1, 1],
], // -
]
this.board[row][col] = player
for (const [dir1, dir2] of directions) {
let count = 1
let blocked = 0
//
for (const [dx, dy] of [dir1, dir2]) {
for (let i = 1; i <= 3; i++) {
const newRow = row + dx * i
const newCol = col + dy * i
if (!this.isValidPosition(newRow, newCol)) {
blocked++
break
}
if (this.board[newRow][newCol] === player) {
count++
} else if (this.board[newRow][newCol] !== 0) {
blocked++
break
} else {
break
}
}
}
if (count === 3 && blocked === 0) threeCount++
}
this.board[row][col] = 0
return threeCount >= 2
},
//
evaluateConnectivity(row, col, player) {
let connectivity = 0
const range = 2
for (let i = -range; i <= range; i++) { for (let i = -range; i <= range; i++) {
for (let j = -range; j <= range; j++) { for (let j = -range; j <= range; j++) {
@ -510,17 +726,15 @@ export default {
const newCol = col + j const newCol = col + j
if (this.isValidPosition(newRow, newCol)) { if (this.isValidPosition(newRow, newCol)) {
const piece = this.board[newRow][newCol] if (this.board[newRow][newCol] === player) {
if (piece !== 0) { //
// connectivity += (range + 1 - Math.max(Math.abs(i), Math.abs(j))) * 30
const distance = Math.max(Math.abs(i), Math.abs(j))
score += (range - distance + 1) * 20
} }
} }
} }
} }
return score return connectivity
}, },
evaluateDirection(row, col, dx, dy, player) { evaluateDirection(row, col, dx, dy, player) {
@ -688,6 +902,43 @@ export default {
// //
// //
}, },
getTauntMessage() {
if (this.gameMode !== 'pve' || this.winner === (this.isPlayerBlack ? 1 : 2)) {
return null //
}
let taunts
if (this.winner === 'timeout') {
taunts = this.taunts.timeoutWin
} else if (this.moveCount < 15) {
taunts = this.taunts.quickWin
} else if (this.hasCloseWin()) {
taunts = this.taunts.closeWin
} else {
taunts = this.taunts.computerWin
}
return taunts[Math.floor(Math.random() * taunts.length)]
},
hasCloseWin() {
//
const playerPiece = this.isPlayerBlack ? 1 : 2
for (let i = 0; i < 15; i++) {
for (let j = 0; j < 15; j++) {
if (this.board[i][j] === 0) {
this.board[i][j] = playerPiece
const pattern = this.checkPattern(i, j, [1, 0], playerPiece)
this.board[i][j] = 0
if (pattern.liveFour || pattern.sleepingFour) {
return true
}
}
}
}
return false
},
}, },
beforeUnmount() { beforeUnmount() {
@ -1207,4 +1458,33 @@ export default {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(76, 175, 80, 0.3); 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);
}
@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);
}
}
</style> </style>

View File

@ -2,13 +2,11 @@ import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
vue(), vue(),
vueDevTools(),
], ],
resolve: { resolve: {
alias: { alias: {