Download index.html from Lashtw/DiviChess: direct link, hf CLI and curl.
- Browser
- Download file 56.8 kB
-
https://huggingface.co/spaces/Lashtw/DiviChess/resolve/main/index.html
- Command line
-
hf download hf://spaces/Lashtw/DiviChess/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Lashtw/DiviChess/resolve/main/index.html
56.8 kB
| <html lang="zh-TW"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>數戰棋 - 互動式數學遊戲</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'PingFang TC', 'Heiti TC', 'Microsoft JhengHei', sans-serif; | |
| background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%); | |
| color: #f1f5f9; | |
| min-height: 100vh; | |
| padding: 20px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .container { | |
| width: 100%; | |
| max-width: 1200px; | |
| background: rgba(30, 41, 59, 0.7); | |
| backdrop-filter: blur(12px); | |
| -webkit-backdrop-filter: blur(12px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 20px; | |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); | |
| overflow: hidden; | |
| } | |
| .header { | |
| background: linear-gradient(90deg, #10b981, #059669); | |
| color: white; | |
| padding: 24px; | |
| text-align: center; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .header h1 { | |
| font-size: 2.6em; | |
| margin-bottom: 8px; | |
| text-shadow: 0 4px 6px rgba(0,0,0,0.2); | |
| font-weight: 800; | |
| letter-spacing: 2px; | |
| } | |
| .header p { | |
| font-size: 1.1em; | |
| opacity: 0.9; | |
| } | |
| .game-area { | |
| display: flex; | |
| gap: 24px; | |
| padding: 24px; | |
| flex-wrap: wrap; | |
| } | |
| .board-container { | |
| flex: 1; | |
| min-width: 550px; | |
| } | |
| .board { | |
| display: grid; | |
| grid-template-columns: repeat(10, 1fr); | |
| gap: 4px; | |
| background: #0f172a; | |
| padding: 12px; | |
| border-radius: 16px; | |
| margin: 20px 0; | |
| border: 2px solid rgba(255, 255, 255, 0.05); | |
| box-shadow: inset 0 2px 8px rgba(0,0,0,0.5); | |
| } | |
| .cell { | |
| aspect-ratio: 1; | |
| background: #1e293b; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: bold; | |
| font-size: 1.1em; | |
| cursor: pointer; | |
| position: relative; | |
| transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); | |
| border-radius: 8px; | |
| color: #64748b; | |
| user-select: none; | |
| } | |
| .cell:hover { | |
| background: #334155; | |
| color: #f1f5f9; | |
| transform: scale(1.05); | |
| z-index: 10; | |
| } | |
| .cell.white-territory { | |
| background: rgba(33, 150, 243, 0.1); | |
| border: 1px solid rgba(33, 150, 243, 0.3); | |
| color: #60a5fa; | |
| } | |
| .cell.black-territory { | |
| background: rgba(233, 30, 99, 0.1); | |
| border: 1px solid rgba(233, 30, 99, 0.3); | |
| color: #f472b6; | |
| } | |
| .cell.possible-move { | |
| background: rgba(16, 185, 129, 0.25) ; | |
| border: 2px solid #10b981; | |
| color: #34d399; | |
| animation: pulse 1.2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0 { transform: scale(1); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); } | |
| 70% { transform: scale(1.03); box-shadow: 0 0 0 8px rgba(16, 185, 129, 0); } | |
| 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); } | |
| } | |
| .piece { | |
| width: 85%; | |
| height: 85%; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: 800; | |
| color: white; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.5); | |
| cursor: pointer; | |
| transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); | |
| position: relative; | |
| z-index: 5; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.3); | |
| } | |
| .piece.white { | |
| background: linear-gradient(135deg, #3b82f6, #1d4ed8); | |
| border: 2px solid #60a5fa; | |
| } | |
| .piece.black { | |
| background: linear-gradient(135deg, #ec4899, #be185d); | |
| border: 2px solid #f472b6; | |
| } | |
| .piece.selected { | |
| transform: scale(1.15); | |
| box-shadow: 0 0 15px #f59e0b; | |
| border-color: #f59e0b; | |
| animation: selectionGlow 1s infinite alternate; | |
| } | |
| @keyframes selectionGlow { | |
| from { box-shadow: 0 0 10px rgba(245, 158, 11, 0.5); } | |
| to { box-shadow: 0 0 20px rgba(245, 158, 11, 0.9); } | |
| } | |
| .piece.white.upgraded { | |
| background: linear-gradient(135deg, #fbbf24, #1d4ed8); | |
| border: 2px solid #fef08a; | |
| box-shadow: 0 0 15px rgba(59, 130, 246, 0.8); | |
| } | |
| .piece.black.upgraded { | |
| background: linear-gradient(135deg, #fbbf24, #be185d); | |
| border: 2px solid #fef08a; | |
| box-shadow: 0 0 15px rgba(236, 72, 153, 0.8); | |
| } | |
| .piece.upgraded::after { | |
| content: '★'; | |
| position: absolute; | |
| top: -6px; | |
| right: -6px; | |
| font-size: 14px; | |
| color: #fef08a; | |
| text-shadow: 0 1px 2px rgba(0,0,0,0.8); | |
| } | |
| .controls { | |
| flex: 0 0 320px; | |
| padding: 24px; | |
| background: rgba(15, 23, 42, 0.4); | |
| border-radius: 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 16px; | |
| border: 1px solid rgba(255, 255, 255, 0.05); | |
| } | |
| .game-info { | |
| background: rgba(30, 41, 59, 0.6); | |
| padding: 16px; | |
| border-radius: 12px; | |
| border: 1px solid rgba(255, 255, 255, 0.05); | |
| } | |
| .game-info h3 { | |
| color: #34d399; | |
| margin-bottom: 6px; | |
| font-size: 1.1em; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .current-player { | |
| font-size: 1.1em; | |
| font-weight: bold; | |
| text-align: center; | |
| padding: 12px; | |
| border-radius: 10px; | |
| transition: all 0.3s ease; | |
| letter-spacing: 1px; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.15); | |
| } | |
| .current-player.white { | |
| background: rgba(59, 130, 246, 0.15); | |
| color: #60a5fa; | |
| border: 1px solid rgba(59, 130, 246, 0.3); | |
| } | |
| .current-player.black { | |
| background: rgba(236, 72, 153, 0.15); | |
| color: #f472b6; | |
| border: 1px solid rgba(236, 72, 153, 0.3); | |
| } | |
| .button { | |
| background: linear-gradient(135deg, #10b981 0%, #059669 100%); | |
| color: white; | |
| border: none; | |
| padding: 12px 20px; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| font-size: 1em; | |
| font-weight: bold; | |
| transition: all 0.2s ease; | |
| width: 100%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.15); | |
| } | |
| .button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 12px rgba(16, 185, 129, 0.3); | |
| filter: brightness(1.1); | |
| } | |
| .button:active { | |
| transform: translateY(0); | |
| } | |
| .button:disabled { | |
| background: #475569; | |
| color: #94a3b8; | |
| cursor: not-allowed; | |
| transform: none; | |
| box-shadow: none; | |
| filter: none; | |
| } | |
| .button.secondary { | |
| background: linear-gradient(135deg, #475569 0%, #334155 100%); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .button.secondary:hover { | |
| box-shadow: 0 6px 12px rgba(0,0,0,0.2); | |
| } | |
| .instructions { | |
| background: rgba(30, 41, 59, 0.6); | |
| padding: 16px; | |
| border-radius: 12px; | |
| font-size: 0.9em; | |
| border: 1px solid rgba(255, 255, 255, 0.05); | |
| } | |
| .instructions h3 { | |
| color: #38bdf8; | |
| margin-bottom: 10px; | |
| font-size: 1.1em; | |
| } | |
| .instructions ul { | |
| list-style-type: none; | |
| padding: 0; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| .instructions li { | |
| padding-bottom: 6px; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.05); | |
| line-height: 1.4; | |
| color: #cbd5e1; | |
| } | |
| .instructions li:last-child { | |
| border-bottom: none; | |
| padding-bottom: 0; | |
| } | |
| /* 彈出視窗(Mode Selection & Rules) */ | |
| .modal { | |
| position: fixed; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%) scale(0.9); | |
| background: #1e293b; | |
| border: 1px solid rgba(255, 255, 255, 0.15); | |
| padding: 32px; | |
| border-radius: 20px; | |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7); | |
| text-align: center; | |
| z-index: 1000; | |
| display: none; | |
| width: 95%; | |
| max-width: 600px; | |
| color: #f1f5f9; | |
| transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); | |
| } | |
| .modal.show { | |
| display: block; | |
| transform: translate(-50%, -50%) scale(1); | |
| } | |
| .modal h2 { | |
| font-size: 2em; | |
| color: #10b981; | |
| margin-bottom: 16px; | |
| font-weight: 800; | |
| } | |
| .modal-body { | |
| text-align: left; | |
| background: #0f172a; | |
| padding: 20px; | |
| border-radius: 12px; | |
| margin-bottom: 24px; | |
| max-height: 220px; | |
| overflow-y: auto; | |
| border: 1px solid rgba(255, 255, 255, 0.05); | |
| } | |
| .modal-body h4 { | |
| color: #38bdf8; | |
| margin-bottom: 8px; | |
| } | |
| .modal-body p { | |
| margin-bottom: 12px; | |
| font-size: 0.95em; | |
| line-height: 1.5; | |
| color: #cbd5e1; | |
| } | |
| .mode-selection { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: 16px; | |
| } | |
| .mode-btn { | |
| padding: 16px 12px; | |
| border-radius: 12px; | |
| border: 2px solid rgba(255, 255, 255, 0.1); | |
| background: #1e293b; | |
| color: white; | |
| cursor: pointer; | |
| font-weight: bold; | |
| font-size: 1.05em; | |
| transition: all 0.2s ease; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .mode-btn:hover { | |
| border-color: #10b981; | |
| background: rgba(16, 185, 129, 0.1); | |
| transform: translateY(-4px); | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.3); | |
| } | |
| .mode-btn .icon { | |
| font-size: 2em; | |
| } | |
| /* AI 卡片式選單樣式 */ | |
| .ai-selection-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| margin-bottom: 20px; | |
| text-align: left; | |
| } | |
| .ai-card { | |
| background: rgba(15, 23, 42, 0.6); | |
| border: 1px solid rgba(255, 255, 255, 0.08); | |
| border-radius: 12px; | |
| padding: 14px 18px; | |
| cursor: pointer; | |
| transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| } | |
| .ai-card:hover { | |
| transform: translateY(-2px); | |
| background: rgba(30, 41, 59, 0.8); | |
| box-shadow: 0 10px 20px -10px rgba(0, 0, 0, 0.5); | |
| } | |
| .ai-card.easy { | |
| border-left: 5px solid #10b981; | |
| } | |
| .ai-card.easy:hover { | |
| border-color: #10b981; | |
| box-shadow: 0 8px 16px -8px rgba(16, 185, 129, 0.3); | |
| } | |
| .ai-card.medium { | |
| border-left: 5px solid #f59e0b; | |
| } | |
| .ai-card.medium:hover { | |
| border-color: #f59e0b; | |
| box-shadow: 0 8px 16px -8px rgba(245, 158, 11, 0.3); | |
| } | |
| .ai-card.hard { | |
| border-left: 5px solid #ef4444; | |
| } | |
| .ai-card.hard:hover { | |
| border-color: #ef4444; | |
| box-shadow: 0 8px 16px -8px rgba(239, 68, 68, 0.3); | |
| } | |
| .ai-card-title { | |
| font-size: 1.1em; | |
| font-weight: 800; | |
| color: #f8fafc; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .ai-card-desc { | |
| font-size: 0.9em; | |
| color: #94a3b8; | |
| line-height: 1.4; | |
| } | |
| .ai-card-desc b, .ai-card-strategy b { | |
| color: #cbd5e1; | |
| } | |
| .ai-card-strategy { | |
| font-size: 0.85em; | |
| color: #64748b; | |
| border-top: 1px solid rgba(255, 255, 255, 0.05); | |
| padding-top: 6px; | |
| margin-top: 2px; | |
| } | |
| .overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(15, 23, 42, 0.8); | |
| backdrop-filter: blur(8px); | |
| -webkit-backdrop-filter: blur(8px); | |
| z-index: 999; | |
| display: none; | |
| } | |
| .overlay.show { | |
| display: block; | |
| } | |
| .score-board { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 15px; | |
| gap: 16px; | |
| } | |
| .score { | |
| flex: 1; | |
| text-align: center; | |
| padding: 12px; | |
| border-radius: 10px; | |
| font-weight: bold; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| } | |
| .score.white { | |
| background: rgba(59, 130, 246, 0.15); | |
| color: #60a5fa; | |
| border: 1px solid rgba(59, 130, 246, 0.3); | |
| } | |
| .score.black { | |
| background: rgba(236, 72, 153, 0.15); | |
| color: #f472b6; | |
| border: 1px solid rgba(236, 72, 153, 0.3); | |
| } | |
| .score-value { | |
| font-size: 1.8em; | |
| font-weight: 800; | |
| margin-top: 4px; | |
| } | |
| /* Toast Notification */ | |
| .toast-container { | |
| position: fixed; | |
| top: 24px; | |
| right: 24px; | |
| z-index: 2000; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| max-width: 350px; | |
| } | |
| .toast { | |
| background: #1e293b; | |
| color: #f1f5f9; | |
| padding: 16px 20px; | |
| border-radius: 12px; | |
| border-left: 4px solid #10b981; | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3); | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| transform: translateX(120%); | |
| transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); | |
| font-weight: 500; | |
| } | |
| .toast.show { | |
| transform: translateX(0); | |
| } | |
| .toast.info { border-left-color: #3b82f6; } | |
| .toast.warning { border-left-color: #f59e0b; } | |
| .toast.error { border-left-color: #ef4444; } | |
| @media (max-width: 900px) { | |
| .game-area { | |
| flex-direction: column; | |
| } | |
| .board-container { | |
| min-width: auto; | |
| } | |
| .cell { | |
| font-size: 0.9em; | |
| } | |
| .piece { | |
| font-size: 0.85em; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| body { | |
| padding: 8px; | |
| } | |
| .header h1 { | |
| font-size: 1.8em; | |
| } | |
| .game-area { | |
| padding: 12px; | |
| } | |
| .controls { | |
| padding: 16px; | |
| } | |
| .mode-selection { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>🎲 數戰棋 🎲</h1> | |
| <p>透過整除規則學習因數與倍數 - <span id="gameModeText">模式載入中</span></p> | |
| </div> | |
| <div class="game-area"> | |
| <div class="board-container"> | |
| <div class="score-board"> | |
| <div class="score white"> | |
| <div>藍方棋子 (玩家)</div> | |
| <div class="score-value" id="whiteScore">10</div> | |
| </div> | |
| <div class="score black"> | |
| <div id="blackTitle">紅方棋子 (對手)</div> | |
| <div class="score-value" id="blackScore">10</div> | |
| </div> | |
| </div> | |
| <div class="board" id="gameBoard"> | |
| <!-- 棋盤格子會由JavaScript生成 --> | |
| </div> | |
| <div class="current-player" id="currentPlayer"> | |
| 藍方回合 - 選擇要移動的棋子 | |
| </div> | |
| </div> | |
| <div class="controls"> | |
| <div class="game-info"> | |
| <h3>🎯 遊戲目標</h3> | |
| <p>將所有對手的棋子吃掉,或是讓對手無路可走即獲勝!</p> | |
| </div> | |
| <div class="instructions"> | |
| <h3>📋 遊戲規則簡介</h3> | |
| <ul> | |
| <li>🔵 藍方向下前進,🔴 紅方向上前進。</li> | |
| <li>📐 普通棋子只能移動到能被「棋子自身數字」整除的相鄰列格子。</li> | |
| <li>🍽️ 可以吃掉目標格子上的敵方棋子,<b>但不能重疊在自己同色棋子之上</b>。</li> | |
| <li>⭐ 抵達對手底線領地升級為 <b>★ 棋子</b>,自此可任意往上或往下移動,且不受整除限制!</li> | |
| </ul> | |
| </div> | |
| <button class="button" onclick="resetGame()">🔄 重新開始</button> | |
| <button class="button secondary" onclick="switchSides()">🔀 交換執棋</button> | |
| <button class="button secondary" onclick="showHint()">💡 顯示移動提示</button> | |
| <button class="button secondary" onclick="showWelcomeModal()">⚙️ 切換遊戲模式</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 歡迎/規則模式選擇彈窗 --> | |
| <div class="overlay" id="overlay"></div> | |
| <div class="modal" id="welcomeModal"> | |
| <h2>🎲 歡迎來到「數戰棋」</h2> | |
| <div class="modal-body"> | |
| <h4>💡 遊戲玩法大解析:</h4> | |
| <p>1. <b>移動限制</b>:藍棋只能向下、紅棋只能向上(皆每次移動 1 列)。</p> | |
| <p>2. <b>整除原理</b>:想前往的格子編號,必須要能變為你的棋子數字的「倍數」。例如:3 號棋子只能走 12、15、18 等格子。</p> | |
| <p>3. <b>吃子限制</b>:若目標格有敵棋即可吃掉。<b>但請注意,您不能踩到自己人的棋子上喔!</b></p> | |
| <p>4. <b>升級神將 (★)</b>:當棋子殺到對手底線時會升級,升級後突破禁忌,可自由前進或後退 1 列,並不再受整除限制!</p> | |
| </div> | |
| <!-- 第一階段:選擇主模式 --> | |
| <div id="mainModeSelect"> | |
| <p style="margin-bottom: 16px; font-weight: bold; color: #cbd5e1;">請選擇您的遊玩模式:</p> | |
| <div class="mode-selection" style="grid-template-columns: repeat(2, 1fr);"> | |
| <button class="mode-btn" onclick="selectMode('pvp')"> | |
| <span class="icon">👥</span> | |
| <span>雙人本地對戰</span> | |
| </button> | |
| <button class="mode-btn" onclick="showAiDifficultySelect()"> | |
| <span class="icon">🤖</span> | |
| <span>與 AI 對戰</span> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- 第二階段:選擇 AI 對手與個性說明 --> | |
| <div id="aiDifficultySelect" style="display: none;"> | |
| <p style="margin-bottom: 16px; font-weight: bold; color: #cbd5e1;">請點選要對抗的 AI 角色:</p> | |
| <div class="ai-selection-list"> | |
| <!-- 簡單 AI 卡片 --> | |
| <div class="ai-card easy" onclick="selectMode('ai_easy')"> | |
| <div class="ai-card-title">🟢 萌新嘟嘟 (簡單)</div> | |
| <div class="ai-card-desc"><b>個性:</b>活潑好奇、粗心大意。剛學會九九乘法表,常常看錯整除方向或把棋子白白送給你吃,最喜歡盲目亂衝!</div> | |
| <div class="ai-card-strategy"><b>策略:</b>60% 機率完全憑感覺隨機亂走;只有 40% 機率會稍微考慮往前或吃子,防守意識幾乎為零。</div> | |
| </div> | |
| <!-- 中等 AI 卡片 --> | |
| <div class="ai-card medium" onclick="selectMode('ai_medium')"> | |
| <div class="ai-card-title">🟡 管家阿麥 (中等)</div> | |
| <div class="ai-card-desc"><b>個性:</b>謹慎規矩、有條不紊。計算能力良好,知道要保護自己,但偶爾也會看漏眼犯點小迷糊。</div> | |
| <div class="ai-card-strategy"><b>策略:</b>攻守平衡。會主動避開明顯的致命陷阱。思考時會加入些許隨機性,下棋風格非常接近一般人類玩家。</div> | |
| </div> | |
| <!-- 困難 AI 卡片 --> | |
| <div class="ai-card hard" onclick="selectMode('ai_hard')"> | |
| <div class="ai-card-title">🔴 戰神諸葛墨 (困難)</div> | |
| <div class="ai-card-desc"><b>個性:</b>冷靜沉著、算無遺策。擁有極致的數學思維與大局觀,會精準預判你的攻勢,壓迫感十足!</div> | |
| <div class="ai-card-strategy"><b>策略:</b>精確獵殺與防守。絕不走入危險格子,一旦發現破綻便毫不留情吃子;升級後的「★」會主動展開極速獵殺與包夾。</div> | |
| </div> | |
| </div> | |
| <button class="button secondary" onclick="backToMainMode()" style="width: auto; padding: 8px 16px; display: inline-flex; margin: 0 auto; gap: 4px;">⬅️ 回上一步</button> | |
| </div> | |
| </div> | |
| <!-- 勝利宣告彈窗 --> | |
| <div class="modal" id="winnerMessage"> | |
| <h2 id="winnerText"></h2> | |
| <p style="font-size: 1.2em; margin-bottom: 24px; color: #cbd5e1;">恭喜獲勝!太厲害了 🎉</p> | |
| <button class="button" onclick="hideWinnerMessage()">確認並繼續</button> | |
| </div> | |
| <!-- 自訂 Toast 提示區域 --> | |
| <div id="toastContainer" class="toast-container"></div> | |
| <script> | |
| // 自訂提示元件 (代替 Alert) | |
| function toast(message, type = 'info') { | |
| const container = document.getElementById('toastContainer'); | |
| const toastEl = document.createElement('div'); | |
| toastEl.className = `toast ${type}`; | |
| let emoji = 'ℹ️'; | |
| if (type === 'warning') emoji = '⚠️'; | |
| if (type === 'error') emoji = '❌'; | |
| if (type === 'success') emoji = '🎉'; | |
| toastEl.innerHTML = `<span>${emoji}</span><span>${message}</span>`; | |
| container.appendChild(toastEl); | |
| // 觸發進場動畫 | |
| setTimeout(() => { | |
| toastEl.classList.add('show'); | |
| }, 50); | |
| // 3秒後自動消失並刪除 | |
| setTimeout(() => { | |
| toastEl.classList.remove('show'); | |
| setTimeout(() => { | |
| toastEl.remove(); | |
| }, 300); | |
| }, 3000); | |
| } | |
| class MathChessGame { | |
| constructor() { | |
| this.board = Array(100).fill(null); | |
| this.currentPlayer = 'white'; // 'white' (藍) or 'black' (紅) | |
| this.selectedPiece = null; | |
| this.gameEnded = false; | |
| this.whiteScore = 10; | |
| this.blackScore = 10; | |
| this.gameMode = 'pvp'; // 'pvp' or 'ai' | |
| this.aiDifficulty = 'medium'; // 'easy' | 'medium' | 'hard' | |
| this.isAiThinking = false; | |
| this.initializeGame(); | |
| } | |
| initializeGame() { | |
| this.createBoard(); | |
| this.placePieces(); | |
| this.updateDisplay(); | |
| } | |
| createBoard() { | |
| const boardElement = document.getElementById('gameBoard'); | |
| boardElement.innerHTML = ''; | |
| for (let i = 0; i < 100; i++) { | |
| const cell = document.createElement('div'); | |
| cell.className = 'cell'; | |
| cell.textContent = i + 1; | |
| cell.addEventListener('click', () => this.handleCellClick(i)); | |
| // 標記領地 | |
| if (i < 10) { | |
| cell.classList.add('white-territory'); | |
| } else if (i >= 90) { | |
| cell.classList.add('black-territory'); | |
| } | |
| boardElement.appendChild(cell); | |
| } | |
| } | |
| placePieces() { | |
| this.board = Array(100).fill(null); | |
| // 放置藍方棋子(1-10位置) | |
| for (let i = 0; i < 10; i++) { | |
| this.board[i] = { | |
| player: 'white', | |
| number: i + 1, | |
| upgraded: false | |
| }; | |
| } | |
| // 放置紅方棋子(91-100位置) | |
| for (let i = 90; i < 100; i++) { | |
| this.board[i] = { | |
| player: 'black', | |
| number: i - 89, | |
| upgraded: false | |
| }; | |
| } | |
| this.renderPieces(); | |
| } | |
| renderPieces() { | |
| const cells = document.querySelectorAll('.cell'); | |
| cells.forEach((cell, index) => { | |
| const existingPiece = cell.querySelector('.piece'); | |
| if (existingPiece) { | |
| existingPiece.remove(); | |
| } | |
| const piece = this.board[index]; | |
| if (piece) { | |
| const pieceElement = document.createElement('div'); | |
| pieceElement.className = `piece ${piece.player}`; | |
| if (piece.upgraded) { | |
| pieceElement.classList.add('upgraded'); | |
| } | |
| pieceElement.textContent = piece.upgraded ? '★' : piece.number; | |
| pieceElement.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| this.handlePieceClick(index); | |
| }); | |
| cell.appendChild(pieceElement); | |
| } | |
| }); | |
| } | |
| handlePieceClick(position) { | |
| if (this.gameEnded || this.isAiThinking) return; | |
| // AI 模式下玩家不能操控 AI 的紅棋 | |
| if (this.gameMode === 'ai' && this.currentPlayer === 'black') { | |
| return; | |
| } | |
| const piece = this.board[position]; | |
| // 如果已經選取了己方棋子,且點擊的是敵方(同色以外)棋子,則將其視為欲移動至該格進行「吃子」! | |
| if (this.selectedPiece !== null && piece && piece.player !== this.currentPlayer) { | |
| this.handleCellClick(position); | |
| return; | |
| } | |
| if (!piece || piece.player !== this.currentPlayer) return; | |
| this.clearSelection(); | |
| this.selectedPiece = position; | |
| const pieceElement = document.querySelectorAll('.cell')[position].querySelector('.piece'); | |
| if (pieceElement) { | |
| pieceElement.classList.add('selected'); | |
| } | |
| this.showPossibleMoves(position); | |
| this.updateCurrentPlayerDisplay('選擇移動目標'); | |
| } | |
| handleCellClick(position) { | |
| if (this.gameEnded || this.selectedPiece === null || this.isAiThinking) return; | |
| if (this.isValidMove(this.selectedPiece, position)) { | |
| this.makeMove(this.selectedPiece, position); | |
| this.clearSelection(); | |
| if (this.checkGameEnd()) return; | |
| this.switchPlayer(); | |
| } else { | |
| toast('該位置不符合移動或整除規則,或已有己方棋子!', 'warning'); | |
| } | |
| } | |
| isValidMove(from, to) { | |
| const piece = this.board[from]; | |
| if (!piece) return false; | |
| // 檢查目標格是否已有己方(同色)棋子。如果有,則禁止移動! | |
| const targetPiece = this.board[to]; | |
| if (targetPiece && targetPiece.player === piece.player) { | |
| return false; | |
| } | |
| const fromRow = Math.floor(from / 10); | |
| const toRow = Math.floor(to / 10); | |
| // 1. 如果棋子已升級,可任意往上或往下移動一格 (列距為 1) 且不受整除限制 | |
| if (piece.upgraded) { | |
| return Math.abs(toRow - fromRow) === 1; | |
| } | |
| // 2. 未升級棋子的前行方向檢查 | |
| if (piece.player === 'white') { | |
| // 藍方向下 | |
| if (toRow !== fromRow + 1) return false; | |
| } else { | |
| // 紅方向上 | |
| if (toRow !== fromRow - 1) return false; | |
| } | |
| // 3. 未升級棋子的整除規則 (1-based index) | |
| const targetNumber = to + 1; | |
| return targetNumber % piece.number === 0; | |
| } | |
| showPossibleMoves(position) { | |
| const piece = this.board[position]; | |
| if (!piece) return; | |
| const currentRow = Math.floor(position / 10); | |
| // 如果是已升級棋子,需要同時檢查上一列與下一列 | |
| if (piece.upgraded) { | |
| const adjacentRows = [currentRow - 1, currentRow + 1]; | |
| adjacentRows.forEach(targetRow => { | |
| if (targetRow >= 0 && targetRow < 10) { | |
| for (let col = 0; col < 10; col++) { | |
| const targetPosition = targetRow * 10 + col; | |
| if (this.isValidMove(position, targetPosition)) { | |
| const cell = document.querySelectorAll('.cell')[targetPosition]; | |
| cell.classList.add('possible-move'); | |
| } | |
| } | |
| } | |
| }); | |
| return; | |
| } | |
| // 未升級棋子原本的邏輯 | |
| let targetRow = (piece.player === 'white') ? currentRow + 1 : currentRow - 1; | |
| if (targetRow < 0 || targetRow >= 10) return; | |
| for (let col = 0; col < 10; col++) { | |
| const targetPosition = targetRow * 10 + col; | |
| if (this.isValidMove(position, targetPosition)) { | |
| const cell = document.querySelectorAll('.cell')[targetPosition]; | |
| cell.classList.add('possible-move'); | |
| } | |
| } | |
| } | |
| makeMove(from, to) { | |
| const piece = this.board[from]; | |
| const targetPiece = this.board[to]; | |
| // 如果目標位置有對方棋子,吃掉它 | |
| if (targetPiece && targetPiece.player !== piece.player) { | |
| if (targetPiece.player === 'white') { | |
| this.whiteScore--; | |
| toast(`紅方吃掉了藍方的 ${targetPiece.upgraded ? '★' : targetPiece.number} 號棋子!`, 'error'); | |
| } else { | |
| this.blackScore--; | |
| toast(`藍方吃掉了紅方的 ${targetPiece.upgraded ? '★' : targetPiece.number} 號棋子!`, 'success'); | |
| } | |
| } | |
| // 移動棋子 | |
| this.board[to] = piece; | |
| this.board[from] = null; | |
| // 檢查是否升級成 ★ | |
| const targetRow = Math.floor(to / 10); | |
| if (piece.player === 'white' && targetRow >= 9 && !piece.upgraded) { | |
| piece.upgraded = true; | |
| toast('藍方棋子成功抵達底線,升級為超級「★」將軍!', 'success'); | |
| } else if (piece.player === 'black' && targetRow <= 0 && !piece.upgraded) { | |
| piece.upgraded = true; | |
| toast('紅方棋子成功抵達頂線,升級為超級「★」將軍!', 'error'); | |
| } | |
| this.renderPieces(); | |
| this.updateScoreDisplay(); | |
| } | |
| clearSelection() { | |
| this.selectedPiece = null; | |
| document.querySelectorAll('.piece.selected').forEach(piece => { | |
| piece.classList.remove('selected'); | |
| }); | |
| document.querySelectorAll('.cell.possible-move').forEach(cell => { | |
| cell.classList.remove('possible-move'); | |
| }); | |
| } | |
| switchPlayer() { | |
| this.currentPlayer = this.currentPlayer === 'white' ? 'black' : 'white'; | |
| this.updateCurrentPlayerDisplay(); | |
| // 如果切換到紅方,且此時是 AI 模式,觸發 AI 計算與行動 | |
| if (this.gameMode === 'ai' && this.currentPlayer === 'black' && !this.gameEnded) { | |
| this.triggerAiMove(); | |
| } | |
| } | |
| triggerAiMove() { | |
| this.isAiThinking = true; | |
| this.updateCurrentPlayerDisplay('AI 正在思考棋步...'); | |
| // 模擬 AI 思考時間 (800ms) | |
| setTimeout(() => { | |
| if (this.gameEnded) return; | |
| const bestMove = this.calculateBestAiMove(); | |
| if (bestMove) { | |
| this.makeMove(bestMove.from, bestMove.to); | |
| this.clearSelection(); | |
| if (this.checkGameEnd()) { | |
| this.isAiThinking = false; | |
| return; | |
| } | |
| } else { | |
| // AI 無路可走,判定玩家勝出 | |
| this.endGame('藍方 (玩家)'); | |
| this.isAiThinking = false; | |
| return; | |
| } | |
| this.isAiThinking = false; | |
| this.switchPlayer(); | |
| }, 800); | |
| } | |
| // AI 智慧難度決策演算法 | |
| calculateBestAiMove() { | |
| const possibleMoves = []; | |
| // 1. 蒐集所有可能合法的紅方棋步 | |
| for (let from = 0; from < 100; from++) { | |
| const piece = this.board[from]; | |
| if (piece && piece.player === 'black') { | |
| for (let to = 0; to < 100; to++) { | |
| if (this.isValidMove(from, to)) { | |
| possibleMoves.push({ from, to, piece }); | |
| } | |
| } | |
| } | |
| } | |
| if (possibleMoves.length === 0) return null; | |
| // 【重要修正:隨機洗牌機制】 | |
| // 先隨機打亂所有合法的棋步,確保當多個棋步具有「相同最大估值」時, | |
| // 能隨機選出一個,完美解決 AI 單一棋子(如1號棋)重覆走到底的問題。 | |
| for (let i = possibleMoves.length - 1; i > 0; i--) { | |
| const j = Math.floor(Math.random() * (i + 1)); | |
| const temp = possibleMoves[i]; | |
| possibleMoves[i] = possibleMoves[j]; | |
| possibleMoves[j] = temp; | |
| } | |
| // ==================== 簡單模式 (萌新嘟嘟) ==================== | |
| if (this.aiDifficulty === 'easy') { | |
| // 60% 機率完全亂走隨機棋步(此處可以直接選洗牌後的第1個步子) | |
| if (Math.random() < 0.6) { | |
| return possibleMoves[0]; | |
| } | |
| // 40% 機率執行只看「吃子」和「前進」的超基礎評估 | |
| const scoredMoves = possibleMoves.map(move => { | |
| let score = 0; | |
| const targetPiece = this.board[move.to]; | |
| if (targetPiece && targetPiece.player === 'white') { | |
| score += 100; // 單純有子吃就吃 | |
| } | |
| const fromRow = Math.floor(move.from / 10); | |
| const toRow = Math.floor(move.to / 10); | |
| score += (fromRow - toRow) * 10; // 往前走加分 | |
| return { ...move, score }; | |
| }); | |
| scoredMoves.sort((a, b) => b.score - a.score); | |
| return scoredMoves[0]; | |
| } | |
| // ==================== 中等模式 (管家阿麥) ==================== | |
| if (this.aiDifficulty === 'medium') { | |
| const scoredMoves = possibleMoves.map(move => { | |
| let score = 0; | |
| const targetPiece = this.board[move.to]; | |
| // A. 吃子加分 | |
| if (targetPiece && targetPiece.player === 'white') { | |
| score += 200; | |
| score += targetPiece.upgraded ? 100 : targetPiece.number * 5; | |
| } | |
| // B. 達底線升級獎勵 | |
| const toRow = Math.floor(move.to / 10); | |
| if (!move.piece.upgraded && toRow === 0) { | |
| score += 150; | |
| } | |
| // C. 避險評估 (防守:走完是否會立刻被對方吃?) | |
| const tempOriginal = this.board[move.to]; | |
| this.board[move.to] = move.piece; | |
| this.board[move.from] = null; | |
| let isThreatened = false; | |
| for (let pFrom = 0; pFrom < 100; pFrom++) { | |
| const pPiece = this.board[pFrom]; | |
| if (pPiece && pPiece.player === 'white') { | |
| if (this.isValidMove(pFrom, move.to)) { | |
| isThreatened = true; | |
| break; | |
| } | |
| } | |
| } | |
| // 復原 | |
| this.board[move.from] = move.piece; | |
| this.board[move.to] = tempOriginal; | |
| if (isThreatened) { | |
| score -= 80; // 扣分以避免危險 | |
| } | |
| // D. 前進與追逐 | |
| if (!move.piece.upgraded) { | |
| const fromRow = Math.floor(move.from / 10); | |
| score += (fromRow - toRow) * 10; | |
| } else { | |
| // 升級後的棋子,縮短與最近敵棋的距離 | |
| let minDistance = 100; | |
| for (let i = 0; i < 100; i++) { | |
| const p = this.board[i]; | |
| if (p && p.player === 'white') { | |
| const dist = Math.abs(Math.floor(move.to / 10) - Math.floor(i / 10)) + Math.abs((move.to % 10) - (i % 10)); | |
| if (dist < minDistance) minDistance = dist; | |
| } | |
| } | |
| score += (10 - minDistance) * 4; | |
| } | |
| // E. 雜訊 (使 AI 有時會做非最優的選擇,顯得更自然) | |
| score += Math.random() * 20; | |
| return { ...move, score }; | |
| }); | |
| scoredMoves.sort((a, b) => b.score - a.score); | |
| return scoredMoves[0]; | |
| } | |
| // ==================== 困難模式 (戰神諸葛墨) ==================== | |
| if (this.aiDifficulty === 'hard') { | |
| const scoredMoves = possibleMoves.map(move => { | |
| let score = 0; | |
| const targetPiece = this.board[move.to]; | |
| const fromRow = Math.floor(move.from / 10); | |
| const toRow = Math.floor(move.to / 10); | |
| // A. 極高權重的獵殺獎勵 | |
| if (targetPiece && targetPiece.player === 'white') { | |
| score += 350; // 吃子基礎分 | |
| score += targetPiece.upgraded ? 250 : targetPiece.number * 12; // 優先吃高價值棋子 (如 ★ 或 10 號) | |
| } | |
| // B. 追求升級的高權重 | |
| if (!move.piece.upgraded && toRow === 0) { | |
| score += 250; | |
| } | |
| // C. 嚴格且深度的避險評估 | |
| const tempOriginal = this.board[move.to]; | |
| this.board[move.to] = move.piece; | |
| this.board[move.from] = null; | |
| // 1. 走完之後新位置是否會被吃? | |
| let isThreatenedAfterMove = false; | |
| for (let pFrom = 0; pFrom < 100; pFrom++) { | |
| const pPiece = this.board[pFrom]; | |
| if (pPiece && pPiece.player === 'white') { | |
| if (this.isValidMove(pFrom, move.to)) { | |
| isThreatenedAfterMove = true; | |
| break; | |
| } | |
| } | |
| } | |
| // 2. 原來的位置是否本來就處於危險中? | |
| let isThreatenedBeforeMove = false; | |
| for (let pFrom = 0; pFrom < 100; pFrom++) { | |
| const pPiece = this.board[pFrom]; | |
| if (pPiece && pPiece.player === 'white') { | |
| if (this.isValidMove(pFrom, move.from)) { | |
| isThreatenedBeforeMove = true; | |
| break; | |
| } | |
| } | |
| } | |
| // 復原 | |
| this.board[move.from] = move.piece; | |
| this.board[move.to] = tempOriginal; | |
| // 懲罰:嚴厲懲罰將棋子暴露給敵方的走法(除非是以小吃大、高價值交換) | |
| if (isThreatenedAfterMove) { | |
| const myValue = move.piece.upgraded ? 300 : move.piece.number * 10; | |
| const targetValue = targetPiece ? (targetPiece.upgraded ? 400 : targetPiece.number * 15) : 0; | |
| score -= (myValue - targetValue + 350); | |
| } | |
| // 逃跑獎勵:本來危險,走這步能逃到安全點 | |
| if (isThreatenedBeforeMove && !isThreatenedAfterMove) { | |
| score += 150; | |
| } | |
| // D. 最優路徑追逐 | |
| if (!move.piece.upgraded) { | |
| score += (fromRow - toRow) * 15; | |
| } else { | |
| // 升級將軍 (★) 追殺玩家高價值棋子 | |
| let bestTargetDist = 100; | |
| let bestTargetValue = 0; | |
| for (let i = 0; i < 100; i++) { | |
| const p = this.board[i]; | |
| if (p && p.player === 'white') { | |
| const dist = Math.abs(toRow - Math.floor(i / 10)) + Math.abs((move.to % 10) - (i % 10)); | |
| const val = p.upgraded ? 300 : p.number * 10; | |
| if (dist < bestTargetDist) { | |
| bestTargetDist = dist; | |
| bestTargetValue = val; | |
| } | |
| } | |
| } | |
| score += (10 - bestTargetDist) * 8 + (bestTargetValue * 0.1); | |
| } | |
| // 零隨機雜訊,完全走數學最優解 | |
| return { ...move, score }; | |
| }); | |
| scoredMoves.sort((a, b) => b.score - a.score); | |
| return scoredMoves[0]; | |
| } | |
| } | |
| updateCurrentPlayerDisplay(customAction = null) { | |
| const playerDisplay = document.getElementById('currentPlayer'); | |
| let playerName = this.currentPlayer === 'white' ? '藍方' : '紅方'; | |
| if (this.gameMode === 'ai' && this.currentPlayer === 'black') { | |
| let diffZh = '管家阿麥'; | |
| if (this.aiDifficulty === 'easy') diffZh = '萌新嘟嘟'; | |
| if (this.aiDifficulty === 'hard') diffZh = '戰神諸葛墨'; | |
| playerName = `智慧對手【${diffZh}】`; | |
| } | |
| if (customAction) { | |
| playerDisplay.textContent = customAction; | |
| } else { | |
| playerDisplay.textContent = `${playerName}回合 - 選擇要移動的棋子`; | |
| } | |
| playerDisplay.className = `current-player ${this.currentPlayer}`; | |
| } | |
| updateScoreDisplay() { | |
| document.getElementById('whiteScore').textContent = this.whiteScore; | |
| document.getElementById('blackScore').textContent = this.blackScore; | |
| } | |
| updateDisplay() { | |
| this.updateCurrentPlayerDisplay(); | |
| this.updateScoreDisplay(); | |
| // 更改標題及介面文字 | |
| const modeText = document.getElementById('gameModeText'); | |
| const blackTitle = document.getElementById('blackTitle'); | |
| if (this.gameMode === 'ai') { | |
| let diffZh = '管家阿麥'; | |
| if (this.aiDifficulty === 'easy') diffZh = '萌新嘟嘟'; | |
| if (this.aiDifficulty === 'hard') diffZh = '戰神諸葛墨'; | |
| modeText.textContent = `🤖 對戰【${diffZh}】`; | |
| blackTitle.textContent = `紅方棋子 (${diffZh})`; | |
| } else { | |
| modeText.textContent = '👥 雙人本地對戰'; | |
| blackTitle.textContent = '紅方棋子 (玩家)'; | |
| } | |
| } | |
| checkGameEnd() { | |
| if (this.whiteScore === 0) { | |
| let winnerName = '紅方'; | |
| if (this.gameMode === 'ai') { | |
| let diffZh = '管家阿麥'; | |
| if (this.aiDifficulty === 'easy') diffZh = '萌新嘟嘟'; | |
| if (this.aiDifficulty === 'hard') diffZh = '戰神諸葛墨'; | |
| winnerName = `智慧對手【${diffZh}】`; | |
| } | |
| this.endGame(winnerName); | |
| return true; | |
| } else if (this.blackScore === 0) { | |
| this.endGame('藍方 (玩家)'); | |
| return true; | |
| } | |
| return false; | |
| } | |
| endGame(winner) { | |
| this.gameEnded = true; | |
| document.getElementById('winnerText').textContent = `${winner} 榮獲勝利!`; | |
| document.getElementById('overlay').classList.add('show'); | |
| document.getElementById('winnerMessage').classList.add('show'); | |
| } | |
| reset() { | |
| this.gameEnded = false; | |
| this.currentPlayer = 'white'; | |
| this.selectedPiece = null; | |
| this.whiteScore = 10; | |
| this.blackScore = 10; | |
| this.isAiThinking = false; | |
| this.clearSelection(); | |
| this.placePieces(); | |
| this.updateDisplay(); | |
| toast('遊戲已重置,藍方先手!', 'info'); | |
| } | |
| switchSides() { | |
| if (this.isAiThinking) return; | |
| // 交換所有棋子的位置與對應陣營 | |
| const newBoard = Array(100).fill(null); | |
| for (let i = 0; i < 100; i++) { | |
| const piece = this.board[i]; | |
| if (piece) { | |
| const newPosition = 99 - i; | |
| newBoard[newPosition] = { | |
| player: piece.player === 'white' ? 'black' : 'white', | |
| number: piece.number, | |
| upgraded: piece.upgraded | |
| }; | |
| } | |
| } | |
| this.board = newBoard; | |
| // 交換分數 | |
| const tempScore = this.whiteScore; | |
| this.whiteScore = this.blackScore; | |
| this.blackScore = tempScore; | |
| this.currentPlayer = this.currentPlayer === 'white' ? 'black' : 'white'; | |
| this.clearSelection(); | |
| this.renderPieces(); | |
| this.updateDisplay(); | |
| toast('雙方交換陣營!已重新整理戰局', 'success'); | |
| // 如果交換完輪到 AI | |
| if (this.gameMode === 'ai' && this.currentPlayer === 'black' && !this.gameEnded) { | |
| this.triggerAiMove(); | |
| } | |
| } | |
| } | |
| // 全局遊戲實例 | |
| let game = new MathChessGame(); | |
| // 模式選擇與引導流動 | |
| window.addEventListener('DOMContentLoaded', () => { | |
| showWelcomeModal(); | |
| }); | |
| function showWelcomeModal() { | |
| document.getElementById('overlay').classList.add('show'); | |
| document.getElementById('welcomeModal').classList.add('show'); | |
| // 每次重新呼叫模式選擇時,都確保預設顯示第一階段 (主模式) | |
| document.getElementById('mainModeSelect').style.display = 'block'; | |
| document.getElementById('aiDifficultySelect').style.display = 'none'; | |
| } | |
| function showAiDifficultySelect() { | |
| document.getElementById('mainModeSelect').style.display = 'none'; | |
| document.getElementById('aiDifficultySelect').style.display = 'block'; | |
| } | |
| function backToMainMode() { | |
| document.getElementById('mainModeSelect').style.display = 'block'; | |
| document.getElementById('aiDifficultySelect').style.display = 'none'; | |
| } | |
| function selectMode(mode) { | |
| if (mode === 'pvp') { | |
| game.gameMode = 'pvp'; | |
| } else { | |
| game.gameMode = 'ai'; | |
| if (mode === 'ai_easy') game.aiDifficulty = 'easy'; | |
| if (mode === 'ai_medium') game.aiDifficulty = 'medium'; | |
| if (mode === 'ai_hard') game.aiDifficulty = 'hard'; | |
| } | |
| hideWelcomeModal(); | |
| game.reset(); | |
| } | |
| function hideWelcomeModal() { | |
| document.getElementById('overlay').classList.remove('show'); | |
| document.getElementById('welcomeModal').classList.remove('show'); | |
| } | |
| function resetGame() { | |
| game.reset(); | |
| } | |
| function switchSides() { | |
| game.switchSides(); | |
| } | |
| function showHint() { | |
| if (game.isAiThinking) return; | |
| const piece = game.board[game.selectedPiece]; | |
| if (!piece) { | |
| toast('請先在棋盤上選擇您的一個棋子!', 'warning'); | |
| return; | |
| } | |
| let hint = `您選擇的 ${piece.number} 號棋,可以移動到下方(目標格編號能被 ${piece.number} 整除)的格子。\n`; | |
| hint += `例如:${piece.number}、${piece.number * 2}、${piece.number * 3} ... 的位置。`; | |
| if (piece.upgraded) { | |
| hint = '此棋已升級!可以無視整除限制,自由往「上」或「下」一列的任意位置移動!'; | |
| } | |
| toast(hint, 'info'); | |
| } | |
| function hideWinnerMessage() { | |
| document.getElementById('overlay').classList.remove('show'); | |
| document.getElementById('winnerMessage').classList.remove('show'); | |
| game.reset(); | |
| } | |
| </script> | |
| </body> | |
| </html> |