mirror of
https://github.com/wiwikuan/duel-25.git
synced 2025-10-03 05:26:17 +00:00
570 lines
21 KiB
HTML
570 lines
21 KiB
HTML
![]() |
<!DOCTYPE html>
|
|||
|
<html lang="zh-TW">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<title>「決鬥 25」紙牌對戰遊戲 by Wiwi.Blog</title>
|
|||
|
<style>
|
|||
|
body {
|
|||
|
font-family: Arial, sans-serif;
|
|||
|
margin: 20px;
|
|||
|
background-color: #f5f5f5;
|
|||
|
}
|
|||
|
|
|||
|
.game-container {
|
|||
|
max-width: 400px;
|
|||
|
margin: 0 auto;
|
|||
|
padding: 10px;
|
|||
|
border: 1px solid #ccc;
|
|||
|
box-sizing: border-box;
|
|||
|
border-radius: 8px;
|
|||
|
background-color: white;
|
|||
|
}
|
|||
|
|
|||
|
.game-title {
|
|||
|
text-align: left;
|
|||
|
margin: 0 0 3px 0;
|
|||
|
font-size: 1.4rem;
|
|||
|
font-weight: bold;
|
|||
|
}
|
|||
|
|
|||
|
.game-rules {
|
|||
|
text-align: left;
|
|||
|
font-size: 0.8rem;
|
|||
|
margin-bottom: 5px;
|
|||
|
}
|
|||
|
|
|||
|
.hp-display {
|
|||
|
display: flex;
|
|||
|
justify-content: left;
|
|||
|
align-items: left;
|
|||
|
gap: 20px;
|
|||
|
margin-top: 5px;
|
|||
|
margin-bottom: 3px;
|
|||
|
font-size: 1rem;
|
|||
|
font-weight: bold;
|
|||
|
}
|
|||
|
|
|||
|
.player-hp {
|
|||
|
color: #2e7d32;
|
|||
|
}
|
|||
|
|
|||
|
.computer-hp {
|
|||
|
color: #c62828;
|
|||
|
}
|
|||
|
|
|||
|
.hand-title {
|
|||
|
font-size: 1rem;
|
|||
|
margin-bottom: 8px;
|
|||
|
font-weight: bold;
|
|||
|
}
|
|||
|
|
|||
|
.card-container {
|
|||
|
display: flex;
|
|||
|
gap: 10px;
|
|||
|
justify-content: left;
|
|||
|
flex-wrap: wrap;
|
|||
|
margin-bottom: 15px;
|
|||
|
}
|
|||
|
|
|||
|
.card {
|
|||
|
border: 1px solid #777;
|
|||
|
border-radius: 3px;
|
|||
|
padding: 0px 7px;
|
|||
|
cursor: pointer;
|
|||
|
font-size: 1rem;
|
|||
|
min-width: 35px;
|
|||
|
text-align: center;
|
|||
|
transition: all 0.2s;
|
|||
|
background-color: white;
|
|||
|
}
|
|||
|
|
|||
|
.card:hover {
|
|||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|||
|
transform: translateY(-2px);
|
|||
|
}
|
|||
|
|
|||
|
.battle-log {
|
|||
|
min-height: 7rem;
|
|||
|
border: 1px solid #ddd;
|
|||
|
border-radius: 4px;
|
|||
|
padding: 8px;
|
|||
|
font-size: 0.9rem;
|
|||
|
line-height: 1.4;
|
|||
|
overflow-y: auto;
|
|||
|
max-height: 7rem;
|
|||
|
background-color: #fafafa;
|
|||
|
}
|
|||
|
|
|||
|
.game-over {
|
|||
|
text-align: center;
|
|||
|
font-weight: bold;
|
|||
|
font-size: 16px;
|
|||
|
color: #d32f2f;
|
|||
|
margin: 10px 0;
|
|||
|
}
|
|||
|
|
|||
|
.restart-button {
|
|||
|
display: block;
|
|||
|
margin: 10px auto 0;
|
|||
|
padding: 8px 16px;
|
|||
|
background-color: #1976d2;
|
|||
|
color: white;
|
|||
|
border: none;
|
|||
|
border-radius: 4px;
|
|||
|
cursor: pointer;
|
|||
|
font-size: 14px;
|
|||
|
}
|
|||
|
|
|||
|
.restart-button:hover {
|
|||
|
background-color: #1565c0;
|
|||
|
}
|
|||
|
|
|||
|
hr {
|
|||
|
margin: 0;
|
|||
|
height: 1px;
|
|||
|
}
|
|||
|
</style>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div class="game-container">
|
|||
|
<h2 class="game-title">「決鬥 25」紙牌對戰遊戲 v2.0</h2>
|
|||
|
|
|||
|
<div class="game-rules">by <a href="https://wiwi.blog">Wiwi Kuan</a> | <a href="https://wiwi.blog/blog/simple-card-battle-game">怎麼玩?</a><br />♠️♣️ 攻擊 | ♦️ 反擊 | ♥️ 回血</div>
|
|||
|
|
|||
|
<hr />
|
|||
|
|
|||
|
<div class="hp-display">
|
|||
|
<span class="player-hp">玩家:<span id="playerHp">25</span> HP</span>
|
|||
|
<span class="computer-hp">電腦:<span id="computerHp">25</span> HP</span>
|
|||
|
</div>
|
|||
|
|
|||
|
<div>
|
|||
|
<div class="hand-title">你的手牌(點擊出牌):</div>
|
|||
|
<div class="card-container" id="playerHand"></div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="battle-log" id="battleResult">點擊手牌出牌開始對戰!</div>
|
|||
|
|
|||
|
<div class="game-over" id="gameOverMessage"></div>
|
|||
|
|
|||
|
<button class="restart-button" id="restartButton" style="display: none;" onclick="initGame()">重新開始</button>
|
|||
|
</div>
|
|||
|
|
|||
|
<script>
|
|||
|
// 遊戲狀態
|
|||
|
let gameState = {
|
|||
|
playerHp: 25,
|
|||
|
computerHp: 25,
|
|||
|
playerHand: [],
|
|||
|
computerHand: [],
|
|||
|
deck: [],
|
|||
|
gameEnded: false,
|
|||
|
battleResult: '點擊手牌出牌開始對戰!',
|
|||
|
gameOverMessage: ''
|
|||
|
};
|
|||
|
|
|||
|
// 建立一副完整的撲克牌
|
|||
|
function createDeck() {
|
|||
|
const suits = ['♠️', '♥️', '♦️', '♣️'];
|
|||
|
const values = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
|
|||
|
const newDeck = [];
|
|||
|
|
|||
|
for (let suit of suits) {
|
|||
|
for (let value of values) {
|
|||
|
newDeck.push({ suit, value });
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return newDeck;
|
|||
|
}
|
|||
|
|
|||
|
// 洗牌
|
|||
|
function shuffleDeck(deck) {
|
|||
|
const shuffled = [...deck];
|
|||
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
|||
|
const j = Math.floor(Math.random() * (i + 1));
|
|||
|
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|||
|
}
|
|||
|
return shuffled;
|
|||
|
}
|
|||
|
|
|||
|
// 取得牌的點數值
|
|||
|
function getCardValue(card) {
|
|||
|
if (card.value === 'A') return 1;
|
|||
|
if (card.value === 'J') return 11;
|
|||
|
if (card.value === 'Q') return 12;
|
|||
|
if (card.value === 'K') return 13;
|
|||
|
return parseInt(card.value);
|
|||
|
}
|
|||
|
|
|||
|
// 判斷牌的類型
|
|||
|
function getCardType(card) {
|
|||
|
if (card.suit === '♠️' || card.suit === '♣️') return 'attack';
|
|||
|
if (card.suit === '♦️') return 'counter';
|
|||
|
if (card.suit === '♥️') return 'heal';
|
|||
|
}
|
|||
|
|
|||
|
// 格式化顯示牌
|
|||
|
function formatCard(card) {
|
|||
|
return `${card.value}${card.suit}`;
|
|||
|
}
|
|||
|
|
|||
|
// 處理攻擊傷害
|
|||
|
function processAttack(attackCard, targetHp) {
|
|||
|
const damage = getCardValue(attackCard);
|
|||
|
return Math.max(0, targetHp - damage);
|
|||
|
}
|
|||
|
|
|||
|
// 處理回血
|
|||
|
function processHeal(healCard, currentHp) {
|
|||
|
const healAmount = getCardValue(healCard);
|
|||
|
return Math.min(25, currentHp + healAmount);
|
|||
|
}
|
|||
|
|
|||
|
// 處理一回合的戰鬥
|
|||
|
function processBattle(playerCard, computerCard) {
|
|||
|
let battleLog = [];
|
|||
|
battleLog.push(`玩家出牌:${formatCard(playerCard)}`);
|
|||
|
battleLog.push(`電腦出牌:${formatCard(computerCard)}`);
|
|||
|
|
|||
|
let newPlayerHp = gameState.playerHp;
|
|||
|
let newComputerHp = gameState.computerHp;
|
|||
|
|
|||
|
const playerCardType = getCardType(playerCard);
|
|||
|
const computerCardType = getCardType(computerCard);
|
|||
|
|
|||
|
// 第一階段:處理攻擊和反擊
|
|||
|
if (playerCardType === 'attack' && computerCardType === 'attack') {
|
|||
|
// 雙方互相攻擊
|
|||
|
newComputerHp = processAttack(playerCard, newComputerHp);
|
|||
|
newPlayerHp = processAttack(computerCard, newPlayerHp);
|
|||
|
battleLog.push(`雙方互相攻擊!`);
|
|||
|
battleLog.push(`玩家受傷 ${getCardValue(computerCard)},電腦受傷 ${getCardValue(playerCard)}`);
|
|||
|
} else {
|
|||
|
// 玩家攻擊
|
|||
|
if (playerCardType === 'attack') {
|
|||
|
if (computerCardType === 'counter') {
|
|||
|
newPlayerHp = processAttack(computerCard, newPlayerHp);
|
|||
|
battleLog.push(`玩家攻擊被反擊!受到 ${getCardValue(computerCard)} 反擊傷害`);
|
|||
|
} else {
|
|||
|
newComputerHp = processAttack(playerCard, newComputerHp);
|
|||
|
battleLog.push(`玩家攻擊!電腦受到 ${getCardValue(playerCard)} 傷害`);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 電腦攻擊
|
|||
|
if (computerCardType === 'attack') {
|
|||
|
if (playerCardType === 'counter') {
|
|||
|
newComputerHp = processAttack(playerCard, newComputerHp);
|
|||
|
battleLog.push(`電腦攻擊被反擊!受到 ${getCardValue(playerCard)} 反擊傷害`);
|
|||
|
} else {
|
|||
|
newPlayerHp = processAttack(computerCard, newPlayerHp);
|
|||
|
battleLog.push(`電腦攻擊!玩家受到 ${getCardValue(computerCard)} 傷害`);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 檢查致命傷害
|
|||
|
if (newPlayerHp <= 0 || newComputerHp <= 0) {
|
|||
|
gameState.playerHp = newPlayerHp;
|
|||
|
gameState.computerHp = newComputerHp;
|
|||
|
battleLog.push('致命傷害!遊戲結束!');
|
|||
|
gameState.battleResult = battleLog.join('<br>');
|
|||
|
checkGameEnd(newPlayerHp, newComputerHp);
|
|||
|
updateDisplay();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 處理無效反擊
|
|||
|
if (playerCardType === 'counter' && computerCardType !== 'attack') {
|
|||
|
battleLog.push(`玩家反擊無效(對方沒有攻擊)`);
|
|||
|
}
|
|||
|
if (computerCardType === 'counter' && playerCardType !== 'attack') {
|
|||
|
battleLog.push(`電腦反擊無效(對方沒有攻擊)`);
|
|||
|
}
|
|||
|
|
|||
|
// 更新生命值
|
|||
|
gameState.playerHp = newPlayerHp;
|
|||
|
gameState.computerHp = newComputerHp;
|
|||
|
|
|||
|
// 第二階段:處理回血
|
|||
|
if (playerCardType === 'heal') {
|
|||
|
const originalPlayerHp = newPlayerHp;
|
|||
|
newPlayerHp = processHeal(playerCard, newPlayerHp);
|
|||
|
gameState.playerHp = newPlayerHp;
|
|||
|
battleLog.push(`玩家回血 ${newPlayerHp - originalPlayerHp} 點`);
|
|||
|
}
|
|||
|
|
|||
|
if (computerCardType === 'heal') {
|
|||
|
const originalComputerHp = newComputerHp;
|
|||
|
newComputerHp = processHeal(computerCard, newComputerHp);
|
|||
|
gameState.computerHp = newComputerHp;
|
|||
|
battleLog.push(`電腦回血 ${newComputerHp - originalComputerHp} 點`);
|
|||
|
}
|
|||
|
gameState.battleResult = battleLog.join('<br>');
|
|||
|
}
|
|||
|
|
|||
|
// 檢查遊戲是否結束
|
|||
|
function checkGameEnd(currentPlayerHp = gameState.playerHp, currentComputerHp = gameState.computerHp) {
|
|||
|
let gameOverMsg = '';
|
|||
|
|
|||
|
if (currentPlayerHp <= 0 && currentComputerHp <= 0) {
|
|||
|
gameOverMsg = '平手!';
|
|||
|
} else if (currentPlayerHp <= 0) {
|
|||
|
gameOverMsg = '電腦勝利!';
|
|||
|
} else if (currentComputerHp <= 0) {
|
|||
|
gameOverMsg = '玩家勝利!';
|
|||
|
}
|
|||
|
|
|||
|
if (gameOverMsg) {
|
|||
|
gameState.gameEnded = true;
|
|||
|
gameState.gameOverMessage = gameOverMsg;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
// 牌堆用完的 Game Over
|
|||
|
function noMoreCardsGameOver() {
|
|||
|
gameState.gameEnded = true;
|
|||
|
gameState.gameOverMessage = '牌堆用完,平手!';
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
// 補牌
|
|||
|
function drawCards(currentPlayerHand, currentComputerHand, currentDeck) {
|
|||
|
const newPlayerHand = [...currentPlayerHand];
|
|||
|
const newComputerHand = [...currentComputerHand];
|
|||
|
const newDeck = [...currentDeck];
|
|||
|
|
|||
|
// 玩家補牌
|
|||
|
while (newPlayerHand.length < 5 && newDeck.length > 0) {
|
|||
|
newPlayerHand.push(newDeck.pop());
|
|||
|
}
|
|||
|
|
|||
|
// 電腦補牌
|
|||
|
while (newComputerHand.length < 5 && newDeck.length > 0) {
|
|||
|
newComputerHand.push(newDeck.pop());
|
|||
|
}
|
|||
|
|
|||
|
return { newPlayerHand, newComputerHand, newDeck };
|
|||
|
}
|
|||
|
|
|||
|
// MCTS AI 選牌
|
|||
|
function mctsChooseCard(gameStateForAI) {
|
|||
|
const { computerHand, playerHp, computerHp } = gameStateForAI;
|
|||
|
let bestCard = 0;
|
|||
|
let bestWinRate = -1;
|
|||
|
let debugInfo = [];
|
|||
|
|
|||
|
// 對每張手牌模擬 5000 次對局
|
|||
|
for (let i = 0; i < computerHand.length; i++) {
|
|||
|
let wins = 0;
|
|||
|
for (let sim = 0; sim < 5000; sim++) {
|
|||
|
if (simulateGame(gameStateForAI, i)) wins++;
|
|||
|
}
|
|||
|
const winRate = wins / 5000;
|
|||
|
const card = computerHand[i];
|
|||
|
debugInfo.push(`${formatCard(card)}: ${(winRate * 100).toFixed(1)}% 勝率`);
|
|||
|
|
|||
|
if (winRate > bestWinRate) {
|
|||
|
bestWinRate = winRate;
|
|||
|
bestCard = i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 輸出 debug 資訊
|
|||
|
console.log(`🤖 MCTS 思考中... (血量 ${computerHp}/${playerHp})`);
|
|||
|
console.log(debugInfo.join(' | '));
|
|||
|
console.log(`→ 選擇 ${formatCard(computerHand[bestCard])} (${(bestWinRate * 100).toFixed(1)}% 勝率)`);
|
|||
|
|
|||
|
return bestCard;
|
|||
|
}
|
|||
|
|
|||
|
// 模擬完整對局(電腦視角,回傳電腦是否獲勝)
|
|||
|
function simulateGame(gameStateForSim, computerCardIndex) {
|
|||
|
let simPlayerHp = gameStateForSim.playerHp;
|
|||
|
let simComputerHp = gameStateForSim.computerHp;
|
|||
|
let simDeck = [...gameStateForSim.deck];
|
|||
|
let simPlayerHand = [...gameStateForSim.playerHand];
|
|||
|
let simComputerHand = [...gameStateForSim.computerHand];
|
|||
|
|
|||
|
// 第一回合:電腦出指定牌,玩家隨機出牌
|
|||
|
const computerCard = simComputerHand.splice(computerCardIndex, 1)[0];
|
|||
|
const playerCard = simPlayerHand.splice(Math.floor(Math.random() * simPlayerHand.length), 1)[0];
|
|||
|
|
|||
|
const result = simulateBattle(playerCard, computerCard, simPlayerHp, simComputerHp);
|
|||
|
simPlayerHp = result.playerHp;
|
|||
|
simComputerHp = result.computerHp;
|
|||
|
|
|||
|
if (simPlayerHp <= 0) return true;
|
|||
|
if (simComputerHp <= 0) return false;
|
|||
|
|
|||
|
// 補牌並繼續隨機對局
|
|||
|
while (simPlayerHand.length < 5 && simDeck.length > 0) simPlayerHand.push(simDeck.pop());
|
|||
|
while (simComputerHand.length < 5 && simDeck.length > 0) simComputerHand.push(simDeck.pop());
|
|||
|
|
|||
|
// 後續回合都隨機出牌
|
|||
|
for (let turn = 0; turn < 20 && simPlayerHp > 0 && simComputerHp > 0 && simDeck.length > 0; turn++) {
|
|||
|
if (simPlayerHand.length === 0 || simComputerHand.length === 0) break;
|
|||
|
|
|||
|
const pCard = simPlayerHand.splice(Math.floor(Math.random() * simPlayerHand.length), 1)[0];
|
|||
|
const cCard = simComputerHand.splice(Math.floor(Math.random() * simComputerHand.length), 1)[0];
|
|||
|
|
|||
|
const battleResult = simulateBattle(pCard, cCard, simPlayerHp, simComputerHp);
|
|||
|
simPlayerHp = battleResult.playerHp;
|
|||
|
simComputerHp = battleResult.computerHp;
|
|||
|
|
|||
|
if (simPlayerHp <= 0) return true;
|
|||
|
if (simComputerHp <= 0) return false;
|
|||
|
|
|||
|
while (simPlayerHand.length < 5 && simDeck.length > 0) simPlayerHand.push(simDeck.pop());
|
|||
|
while (simComputerHand.length < 5 && simDeck.length > 0) simComputerHand.push(simDeck.pop());
|
|||
|
}
|
|||
|
|
|||
|
return simComputerHp > simPlayerHp;
|
|||
|
}
|
|||
|
|
|||
|
// 快速戰鬥模擬
|
|||
|
function simulateBattle(playerCard, computerCard, playerHp, computerHp) {
|
|||
|
let newPlayerHp = playerHp;
|
|||
|
let newComputerHp = computerHp;
|
|||
|
|
|||
|
const pType = getCardType(playerCard);
|
|||
|
const cType = getCardType(computerCard);
|
|||
|
|
|||
|
// 攻擊和反擊邏輯
|
|||
|
if (pType === 'attack' && cType === 'attack') {
|
|||
|
newPlayerHp = Math.max(0, newPlayerHp - getCardValue(computerCard));
|
|||
|
newComputerHp = Math.max(0, newComputerHp - getCardValue(playerCard));
|
|||
|
} else {
|
|||
|
if (pType === 'attack') {
|
|||
|
if (cType === 'counter') {
|
|||
|
newPlayerHp = Math.max(0, newPlayerHp - getCardValue(computerCard));
|
|||
|
} else {
|
|||
|
newComputerHp = Math.max(0, newComputerHp - getCardValue(playerCard));
|
|||
|
}
|
|||
|
}
|
|||
|
if (cType === 'attack') {
|
|||
|
if (pType === 'counter') {
|
|||
|
newComputerHp = Math.max(0, newComputerHp - getCardValue(playerCard));
|
|||
|
} else {
|
|||
|
newPlayerHp = Math.max(0, newPlayerHp - getCardValue(computerCard));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 如果有致命傷害就不回血了
|
|||
|
if (newPlayerHp <= 0 || newComputerHp <= 0) {
|
|||
|
return { playerHp: newPlayerHp, computerHp: newComputerHp };
|
|||
|
}
|
|||
|
|
|||
|
// 回血
|
|||
|
if (pType === 'heal') newPlayerHp = Math.min(25, newPlayerHp + getCardValue(playerCard));
|
|||
|
if (cType === 'heal') newComputerHp = Math.min(25, newComputerHp + getCardValue(computerCard));
|
|||
|
|
|||
|
return { playerHp: newPlayerHp, computerHp: newComputerHp };
|
|||
|
}
|
|||
|
|
|||
|
// 玩家出牌
|
|||
|
function playCard(cardIndex) {
|
|||
|
if (gameState.gameEnded) return;
|
|||
|
|
|||
|
// 玩家出牌
|
|||
|
const newPlayerHand = [...gameState.playerHand];
|
|||
|
const playerCard = newPlayerHand.splice(cardIndex, 1)[0];
|
|||
|
|
|||
|
// 電腦用 MCTS 選牌
|
|||
|
const newComputerHand = [...gameState.computerHand];
|
|||
|
const gameStateForAI = {
|
|||
|
playerHp: gameState.playerHp,
|
|||
|
computerHp: gameState.computerHp,
|
|||
|
deck: gameState.deck,
|
|||
|
playerHand: newPlayerHand,
|
|||
|
computerHand: newComputerHand
|
|||
|
};
|
|||
|
const computerCardIndex = mctsChooseCard(gameStateForAI);
|
|||
|
const computerCard = newComputerHand.splice(computerCardIndex, 1)[0];
|
|||
|
|
|||
|
// 處理戰鬥
|
|||
|
processBattle(playerCard, computerCard);
|
|||
|
|
|||
|
// 補牌
|
|||
|
const { newPlayerHand: finalPlayerHand, newComputerHand: finalComputerHand, newDeck } = drawCards(newPlayerHand, newComputerHand, gameState.deck);
|
|||
|
|
|||
|
gameState.playerHand = finalPlayerHand;
|
|||
|
gameState.computerHand = finalComputerHand;
|
|||
|
gameState.deck = newDeck;
|
|||
|
|
|||
|
if (newDeck.length < 2 && !gameState.gameEnded) { // 牌堆用完,而且沒有人死掉嗎?
|
|||
|
noMoreCardsGameOver();
|
|||
|
}
|
|||
|
|
|||
|
updateDisplay();
|
|||
|
}
|
|||
|
|
|||
|
// 更新顯示
|
|||
|
function updateDisplay() {
|
|||
|
document.getElementById('playerHp').textContent = gameState.playerHp;
|
|||
|
document.getElementById('computerHp').textContent = gameState.computerHp;
|
|||
|
document.getElementById('battleResult').innerHTML = gameState.battleResult;
|
|||
|
document.getElementById('gameOverMessage').textContent = gameState.gameOverMessage;
|
|||
|
|
|||
|
// 更新手牌顯示
|
|||
|
const playerHandDiv = document.getElementById('playerHand');
|
|||
|
playerHandDiv.innerHTML = '';
|
|||
|
gameState.playerHand.forEach((card, index) => {
|
|||
|
const cardDiv = document.createElement('div');
|
|||
|
cardDiv.className = 'card';
|
|||
|
cardDiv.textContent = formatCard(card);
|
|||
|
cardDiv.onclick = () => playCard(index);
|
|||
|
playerHandDiv.appendChild(cardDiv);
|
|||
|
});
|
|||
|
|
|||
|
// 顯示/隱藏重新開始按鈕
|
|||
|
const restartButton = document.getElementById('restartButton');
|
|||
|
if (gameState.gameEnded) {
|
|||
|
restartButton.style.display = 'block';
|
|||
|
} else {
|
|||
|
restartButton.style.display = 'none';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 初始化遊戲
|
|||
|
function initGame() {
|
|||
|
// 重置遊戲狀態
|
|||
|
gameState.playerHp = 25;
|
|||
|
gameState.computerHp = 25;
|
|||
|
gameState.gameEnded = false;
|
|||
|
gameState.gameOverMessage = '';
|
|||
|
gameState.battleResult = '點擊手牌出牌開始對戰!';
|
|||
|
|
|||
|
// 建立並洗牌
|
|||
|
const newDeck = shuffleDeck(createDeck());
|
|||
|
|
|||
|
// 發初始手牌
|
|||
|
const initialPlayerHand = [];
|
|||
|
const initialComputerHand = [];
|
|||
|
|
|||
|
for (let i = 0; i < 5; i++) {
|
|||
|
initialPlayerHand.push(newDeck.pop());
|
|||
|
initialComputerHand.push(newDeck.pop());
|
|||
|
}
|
|||
|
|
|||
|
gameState.playerHand = initialPlayerHand;
|
|||
|
gameState.computerHand = initialComputerHand;
|
|||
|
gameState.deck = newDeck;
|
|||
|
|
|||
|
updateDisplay();
|
|||
|
}
|
|||
|
|
|||
|
// 頁面加載時初始化遊戲
|
|||
|
window.onload = function() {
|
|||
|
initGame();
|
|||
|
};
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|