<!DOCTYPE html>
<html lang="zh-HK">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>網頁版波子棋 - 等距跳終極版</title>
<style>
body {
background-color: #2b3a42;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: white;
}
h1 { margin-bottom: 5px; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); }
p { margin-top: 0; margin-bottom: 15px; color: #b0c4de; }
/* 控制面板 */
.controls {
margin-bottom: 20px;
display: flex;
gap: 15px;
height: 40px;
align-items: center;
}
button {
background-color: #8c5a35;
color: white;
border: 2px solid #5a3a22;
padding: 8px 16px;
border-radius: 8px;
cursor: pointer;
font-size: 15px;
font-weight: bold;
box-shadow: 2px 4px 6px rgba(0,0,0,0.4);
transition: all 0.1s;
}
button:hover { background-color: #a07148; transform: translateY(-2px); }
button:active { transform: translateY(1px); box-shadow: 1px 2px 3px rgba(0,0,0,0.4); }
#end-turn-btn { background-color: #2e8b57; border-color: #1a5233; display: none; }
#end-turn-btn:hover { background-color: #3cb371; }
/* 木板底座設計 */
#board-container {
background: radial-gradient(circle at center, #d4a373, #a07148);
padding: 40px;
border-radius: 50%;
box-shadow: 0 15px 30px rgba(0,0,0,0.6), inset 0 0 20px rgba(0,0,0,0.3);
display: flex;
flex-direction: column;
align-items: center;
}
.row { display: flex; justify-content: center; margin-bottom: -5px; }
/* 棋孔設計 */
.hole {
width: 32px; height: 32px;
background-color: #8c5a35;
border-radius: 50%;
margin: 0 3px;
box-shadow: inset 3px 3px 6px rgba(0,0,0,0.7), 1px 1px 1px rgba(255,255,255,0.2);
display: flex; justify-content: center; align-items: center;
cursor: pointer; position: relative;
}
/* 波子通用設計 */
.marble {
width: 28px; height: 28px;
border-radius: 50%;
box-shadow: 2px 3px 5px rgba(0,0,0,0.5);
pointer-events: none; transition: transform 0.1s;
}
.hole:hover .marble { transform: scale(1.1); }
/* 選中與提示特效 */
.selected { box-shadow: 0 0 12px 6px rgba(255, 255, 255, 0.8), inset 3px 3px 6px rgba(0,0,0,0.7) !important; background-color: #6d4223; }
.valid-move { background-color: rgba(255, 255, 255, 0.4) !important; box-shadow: inset 0 0 10px rgba(255,255,255,0.9), 0 0 8px rgba(255,255,255,0.5) !important; }
.valid-move::after { content: ''; width: 10px; height: 10px; background-color: white; border-radius: 50%; position: absolute; }
/* 六種波子顏色 */
.color-1 { background: radial-gradient(circle at 10px 10px, #ffdf80, #d4af37, #8b6508); }
.color-2 { background: radial-gradient(circle at 10px 10px, #4d79ff, #0000cd, #000080); }
.color-3 { background: radial-gradient(circle at 10px 10px, #ff6666, #b22222, #800000); }
.color-4 { background: radial-gradient(circle at 10px 10px, #4da6ff, #00bfff, #00688b); }
.color-5 { background: radial-gradient(circle at 10px 10px, #2e4c3b, #0a1f12, #000000); }
.color-6 { background: radial-gradient(circle at 10px 10px, #ffb3cc, #ff69b4, #c71585); }
</style>
</head>
<body>
<h1>波子棋 (等距跳規則)</h1>
<p>支援長距離等距跳躍、無限連跳與悔棋</p>
<div class="controls">
<button id="undo-btn" onclick="undoMove()">↩ 悔棋 (倒後)</button>
<button id="end-turn-btn" onclick="endTurn()">✔️ 完成連跳</button>
</div>
<div id="board-container">
<div id="board"></div>
</div>
<script>
const rowLayout = [ 1, 2, 3, 4, 13, 12, 11, 10, 9, 10, 11, 12, 13, 4, 3, 2, 1 ];
const boardElement = document.getElementById('board');
// 遊戲狀態變數
let holesMap = {};
let selectedHole = null;
let isMultiJumping = false;
let moveHistory = [];
// 波子棋的 6 個完美相鄰方向向量
const directions = [
{dx: 2, dy: 0}, {dx: -2, dy: 0}, // 左右
{dx: 1, dy: 1}, {dx: -1, dy: 1}, // 左下、右下
{dx: 1, dy: -1}, {dx: -1, dy: -1} // 左上、右上
];
function initBoard() {
for (let r = 0; r < rowLayout.length; r++) {
const rowDiv = document.createElement('div');
rowDiv.className = 'row';
const cols = rowLayout[r];
for (let c = 0; c < cols; c++) {
const hole = document.createElement('div');
hole.className = 'hole';
// 邏輯座標
const x = 2 * c - cols + 1;
const y = r;
hole.dataset.x = x;
hole.dataset.y = y;
holesMap[`${x},${y}`] = hole;
let marbleColor = 0;
if (r < 4) marbleColor = 1;
else if (r > 12) marbleColor = 4;
else if (r >= 4 && r <= 7) {
const limit = 4 - (r - 4);
if (c < limit) marbleColor = 2; else if (c >= cols - limit) marbleColor = 3;
} else if (r >= 9 && r <= 12) {
const limit = r - 8;
if (c < limit) marbleColor = 5; else if (c >= cols - limit) marbleColor = 6;
}
if (marbleColor !== 0) {
const marble = document.createElement('div');
marble.className = `marble color-${marbleColor}`;
hole.appendChild(marble);
}
hole.addEventListener('click', handleHoleClick);
rowDiv.appendChild(hole);
}
boardElement.appendChild(rowDiv);
}
}
function getHole(x, y) {
return holesMap[`${x},${y}`] || null;
}
function handleHoleClick() {
const hasMarble = this.children.length > 0;
const isValidMove = this.classList.contains('valid-move');
if (!hasMarble && isValidMove && selectedHole) {
executeMove(selectedHole, this);
return;
}
if (isMultiJumping) {
if (this === selectedHole) endTurn();
return;
}
if (hasMarble) {
if (selectedHole === this) {
endTurn();
} else {
endTurn();
this.classList.add('selected');
selectedHole = this;
showValidMoves(this, false);
}
} else {
endTurn();
}
}
function executeMove(fromHole, toHole) {
const marble = fromHole.querySelector('.marble');
const sx = parseInt(fromHole.dataset.x);
const sy = parseInt(fromHole.dataset.y);
const tx = parseInt(toHole.dataset.x);
const ty = parseInt(toHole.dataset.y);
// 判定是否為跳躍:移動距離大於 1 格 (邏輯上 dx>2 或 dy>1)
const isJump = Math.abs(tx - sx) > 2 || Math.abs(ty - sy) > 1;
toHole.appendChild(marble);
moveHistory.push({ from: fromHole, to: toHole });
clearHints();
selectedHole = toHole;
toHole.classList.add('selected');
if (isJump) {
const hasMoreJumps = showValidMoves(toHole, true);
if (hasMoreJumps) {
isMultiJumping = true;
document.getElementById('end-turn-btn').style.display = 'block';
} else {
endTurn();
}
} else {
endTurn();
}
}
// 計算並顯示合法路線(加入等距跳躍邏輯)
function showValidMoves(hole, onlyJumps) {
const sx = parseInt(hole.dataset.x);
const sy = parseInt(hole.dataset.y);
let foundMoves = false;
directions.forEach(dir => {
// 單步移動
if (!onlyJumps) {
const stepHole = getHole(sx + dir.dx, sy + dir.dy);
if (stepHole && stepHole.children.length === 0) {
stepHole.classList.add('valid-move');
foundMoves = true;
}
}
// 等距跳躍 (長跳/飛星)
let pivotFound = false;
let pivotDist = 0;
let currentDist = 1;
// 沿著一個方向無限延伸檢查
while (true) {
const checkX = sx + dir.dx * currentDist;
const checkY = sy + dir.dy * currentDist;
const checkHole = getHole(checkX, checkY);
if (!checkHole) break; // 超出邊界
const hasMarble = checkHole.children.length > 0;
if (!pivotFound) {
if (hasMarble) {
pivotFound = true;
pivotDist = currentDist; // 找到跳板,記錄距離
}
} else {
// 已經找到跳板,現在尋找落點
if (hasMarble) {
break; // 跳板後方有障礙物,這條路線作廢
}
if (currentDist === pivotDist * 2) {
// 到達與跳板完全相等的距離,這是一個完美的等距落點!
checkHole.classList.add('valid-move');
foundMoves = true;
break; // 找到落點後就停止這個方向的搜尋
}
}
currentDist++;
}
});
return foundMoves;
}
function endTurn() {
clearHints();
if (selectedHole) selectedHole.classList.remove('selected');
selectedHole = null;
isMultiJumping = false;
document.getElementById('end-turn-btn').style.display = 'none';
}
function undoMove() {
if (moveHistory.length === 0) return;
const lastMove = moveHistory.pop();
const marble = lastMove.to.querySelector('.marble');
lastMove.from.appendChild(marble);
endTurn();
}
function clearHints() {
document.querySelectorAll('.valid-move').forEach(h => h.classList.remove('valid-move'));
}
initBoard();
</script>
</body>
</html>