共享会话
改進
分享于 2026年9月19日 05:39改進 最新遊戲規則為:
遊戲規則
拚數棋是一款二人弈棋,即二人抽象策略遊戲。
目標:
雙方比拚多個區域內的數字,數字之差將作為得分。雙方互換先後手玩兩局,總分高者獲勝。
棋子:
本棋共有五種棋子,每方每種棋子有五個。
這五種棋子分別為「單一、雙二、角二、網三、叉零」:
「單一」是一個正方形有一條實邊和一個三角形; 「雙二」是一個正方形有兩條分開的實邊和兩個三角形; 「角二」是一個正方形有兩條相連實邊和兩個三角形; 「圍三」是一個正方形有三條實邊和三個三角形; 「零叉」是一個正方形內部兩條對角線交叉。
棋盤:
七乘七方格,中間的格子上有圓點標記。
棋規:
落子規則:
先手第一手棋一定要用「角二」下到棋盤中心位置,此後雙方輪流落子。要求落下的棋子要跟場上已有的棋子相鄰,但實邊不能相鄰。不能形成空洞。先手方必須落完二十五個棋子,後手方要落下二十四個棋子,最後剩下的棋子不能是「叉零」。
提子規則:
落子結束後開始提子,由後手方先提子,任意選取一個己方棋子,按它上面的三角形指向行動,單一則走一步,雙二則選擇兩個方向的其中一個走一步,角二則選擇斜向一步或兩步,圍三則三個方向選一個走一步,一直走到一個己方的「叉零」。不能走到沒有棋子的空位上,也不能走出棋盤邊界。如果選取的是「叉零」則直接完成本次提子。把這些棋子拿在手上,之後輪到先手方提子。雙方輪流提完五個「叉零」,則本局結束。
計分規則:
當落子階段結束,實邊圍成的一個區域內,一方的三角形數量減去另一方的三角形數,結果即為這一方在本區域的得分。當一個區域只有一方棋子,則不能計分,必須是比拚過雙方三角形數量的區域才可計分。如果是通向棋盤外的區域,則視為同一區域。在計完落子階段的分數後,才能開始提子,然後再計提子分數。一方每回合所提的棋子中,己方棋子的三角形數量減去對方的棋子的三角形數量,則是本回合提子得分,如果只包含本方棋子則不能得分。
勝負判定:
雙方玩兩局,最終分數為正整數者獲勝。
在任意一局中,如果一方不能按規則落子或提子,則直接判負。
規則介紹完畢,下面說明這版規則跟上一版的區別:
在落子階段,後手方最後剩下的棋子不能是「叉零」。去掉「在落子階段結束時,後手方會剩下一個棋子,這個棋子也將計到提子階段的得分」這條規則,這個棋子將不再參與計分。
在提子階段,每方可以任意選擇一個己方棋子,包括「叉零」。去掉「當走出邊界則會從另一邊回來,上下、左右是打通的」這條規則,邊界將不再打通。之前的提子只有一輪,現在要進行五輪,雙方要把己方所有的「叉零」都提走。每回合的提子都獨立計分,並且要包含對方的棋子,否則不能計分。
回答要求:明確給出所要修改的地方和所要替換的代碼。
QuanAI我會先檢查 HTML 中與落子結束、剩餘棋子、提子移動、邊界處理、提子輪次及計分相關的程式,再逐項給出可直接替換的代碼。
已確認舊版邏輯仍包含「棋盤邊界環回、剩餘棋子加分、只提兩次」;接著讀取其餘腳本,找出主遊戲、AI、復原與規則文字的所有對應點。
主程式也已定位到相同舊規則,且目前禁止以「叉零」直接提子。最後讀取後段,以涵蓋提子提交、人工點擊、AI 狀態轉換、結果顯示與棋譜輸出。
以下修改可讓 battlenumber.html 完整符合新版規則。CSS 和棋盤 SVG 不需要修改;主要要同步修改兩套邏輯:
<script id="mctsWorkerSource">內的 AI 模擬規則。- 後面的主遊戲
<script>。
若只修改主程式而不修改 Worker,AI 仍會按舊規則計算,因此兩邊都必須改。
一、加入提子總回合常數
1. 修改 MCTS Worker
在 Worker 內找到:
const TYPE_CROSS = 4;替換為:
const TYPE_CROSS = 4;
const CROSS_COUNT_PER_PLAYER = 5;
const TOTAL_EXTRACTIONS = CROSS_COUNT_PER_PLAYER * 2;2. 修改主遊戲程式
在後面的主程式內同樣找到:
const TYPE_CROSS = 4;替換為:
const TYPE_CROSS = 4;
const CROSS_COUNT_PER_PLAYER = 5;
const TOTAL_EXTRACTIONS = CROSS_COUNT_PER_PLAYER * 2;這會把提子階段由原本的兩次,改為雙方各提五次,共十次。
二、落子時禁止後手最後剩下「叉零」
後手第 24 次落子前,手上只剩兩枚棋子。該次落子後,如果仍有「叉零」留在手上,這步便不合法。
1. 修改 Worker 的 canPlace()
在 Worker 的 canPlace() 中找到:
const total = s.placeCount[0] + s.placeCount[1];
if (total === 0) {
return player === s.first && type === TYPE_CORNER && index === 24;
}替換為:
const total = s.placeCount[0] + s.placeCount[1];
if (total === 0) {
return player === s.first && type === TYPE_CORNER && index === 24;
}
// 棋盤已有 47 枚時,輪到後手放第 24 枚。
// 這一步落下後,後手剩餘的最後一枚不能是「叉零」。
if (
total === SIZE - 2 &&
player !== s.first &&
s.inv[player][TYPE_CROSS] -
(type === TYPE_CROSS ? 1 : 0) > 0
) {
return false;
}2. 修改主程式的 validatePlacement()
找到:
if (gameState.inventories[player][type] <= 0) return "noStock";
if (gameState.board[index]) return "occupied";
if (total === 0) return null;替換為:
if (gameState.inventories[player][type] <= 0) return "noStock";
if (gameState.board[index]) return "occupied";
// 棋盤已有 47 枚時,後手正準備放第 24 枚。
// 落下這枚後,最後剩下的棋子不能是「叉零」。
if (
total === SIZE - 2 &&
player !== gameState.first &&
gameState.inventories[player][TYPE_CROSS] -
(type === TYPE_CROSS ? 1 : 0) > 0
) {
return "leftoverCross";
}
if (total === 0) return null;三、移除後手剩餘棋子的提子加分
1. 修改 Worker 的落子結束邏輯
在 Worker 的 applyAction() 中找到:
if (total === SIZE) {
next.phase = "extract";
next.extractIndex = 0;
next.turn = 1 - next.first;
let leftover = 0;
for (let type = 0; type < 5; type++) {
leftover += next.inv[next.turn][type] * TRI[type];
}
next.raw[next.turn] += leftover;
} else {
next.turn = 1 - p;
}替換為:
if (total === SIZE) {
next.phase = "extract";
next.extractIndex = 0;
// 落子完成後由後手先提子。
next.turn = 1 - next.first;
} else {
next.turn = 1 - p;
}2. 修改主程式的 commitPlacement()
在 commitPlacement() 中找到整個:
if (total >= SIZE) {
state.placementFinal = state.placementScore;
state.phase = "extract";
state.extractIndex = 0;
state.turn = 1 - state.first;
state.leftover = [];
let leftoverScore = 0;
for (let type = 0; type < 5; type++) {
const quantity = state.inventories[state.turn][type];
if (quantity > 0) {
state.leftover.push({
player: state.turn,
type,
quantity
});
leftoverScore += quantity * TRIANGLES[type];
}
}
state.extractRaw[state.turn] += leftoverScore;
state.record.push({
kind: "extractStart",
round: state.round,
firstExtractor: state.turn + 1,
leftover: clone(state.leftover),
leftoverScore
});
} else {
state.turn = 1 - player;
}替換為:
if (total >= SIZE) {
state.placementFinal = state.placementScore;
state.phase = "extract";
state.extractIndex = 0;
// 後手先提子。
state.turn = 1 - state.first;
// 僅在棋譜中記錄未落下的棋子,不參與任何計分。
const unplacedPieces = [];
for (let type = 0; type < 5; type++) {
const quantity = state.inventories[state.turn][type];
if (quantity > 0) {
unplacedPieces.push({
player: state.turn,
type,
quantity
});
}
}
state.record.push({
kind: "extractStart",
round: state.round,
firstExtractor: state.turn + 1,
unplacedPieces
});
} else {
state.turn = 1 - player;
}3. 從初始狀態移除舊的 leftover
在 makeFreshState() 中找到:
extractRaw: [0, 0],
extractIndex: 0,
leftover: [],
phase: "place",替換為:
extractRaw: [0, 0],
extractIndex: 0,
phase: "place",四、取消棋盤邊界環回
必須同時修改 Worker 和主程式的 movementDestinations()。
1. 替換 Worker 的 movementDestinations()
將 Worker 內整個函數:
function movementDestinations(board, index) {到其結束的 },完整替換為:
function movementDestinations(board, index) {
const tile = board[index];
if (!tile || tile[1] === TYPE_CROSS) return [];
const type = tile[1];
const rot = tile[2];
const r = Math.floor(index / N);
const c = index % N;
const raw = [];
if (type === TYPE_SINGLE) {
raw.push([-1, 0, 1]);
} else if (type === TYPE_DOUBLE) {
raw.push([0, -1, 1], [0, 1, 1]);
} else if (type === TYPE_CORNER) {
raw.push([-1, -1, 1], [-1, -1, 2]);
} else if (type === TYPE_SURROUND) {
raw.push([-1, 0, 1], [0, -1, 1], [0, 1, 1]);
}
const result = [];
const used = new Set();
for (const [baseDr, baseDc, distance] of raw) {
const [dr, dc] = rotateVector(baseDr, baseDc, rot);
const nr = r + dr * distance;
const nc = c + dc * distance;
// 新規則:不能走出棋盤,不再從另一邊回來。
if (nr < 0 || nr >= N || nc < 0 || nc >= N) {
continue;
}
const ni = nr * N + nc;
if (!used.has(ni)) {
used.add(ni);
result.push(ni);
}
}
return result;
}2. 替換主程式的 movementDestinations()
將主程式內整個同名函數替換為:
function movementDestinations(board, index) {
const tile = board[index];
if (!tile || tile.type === TYPE_CROSS) return [];
const row = Math.floor(index / N);
const col = index % N;
const movement = [];
if (tile.type === TYPE_SINGLE) {
movement.push([-1, 0, 1]);
} else if (tile.type === TYPE_DOUBLE) {
movement.push([0, -1, 1], [0, 1, 1]);
} else if (tile.type === TYPE_CORNER) {
movement.push([-1, -1, 1], [-1, -1, 2]);
} else if (tile.type === TYPE_SURROUND) {
movement.push([-1, 0, 1], [0, -1, 1], [0, 1, 1]);
}
const destinations = [];
const used = new Set();
for (const [baseDr, baseDc, distance] of movement) {
const [dr, dc] = rotateVector(baseDr, baseDc, tile.rot);
const nextRow = row + dr * distance;
const nextCol = col + dc * distance;
// 新規則:超出棋盤的方向直接視為不可走。
if (
nextRow < 0 ||
nextRow >= N ||
nextCol < 0 ||
nextCol >= N
) {
continue;
}
const next = nextRow * N + nextCol;
if (!used.has(next)) {
used.add(next);
destinations.push(next);
}
}
return destinations;
}五、允許直接選擇自己的「叉零」完成提子
路徑只包含一枚自己的叉零也是合法提子,分數為零。
1. 替換 Worker 的 extractionActions()
將 Worker 內整個 extractionActions() 替換為:
function extractionActions(s, cap) {
const p = s.turn;
const actions = [];
const board = s.board;
for (let start = 0; start < SIZE && actions.length < cap; start++) {
const startTile = board[start];
if (!startTile || startTile[0] !== p) {
continue;
}
// 可以直接選取自己的「叉零」,立即完成本次提子。
if (startTile[1] === TYPE_CROSS) {
actions.push({
kind: "extract",
path: [start]
});
continue;
}
const seen = new Uint8Array(SIZE);
const path = [start];
seen[start] = 1;
const dfs = current => {
if (actions.length >= cap) return;
const destinations = movementDestinations(board, current);
shuffle(destinations);
for (const next of destinations) {
if (actions.length >= cap || seen[next]) continue;
const tile = board[next];
if (!tile) continue;
if (tile[1] === TYPE_CROSS) {
if (tile[0] === p) {
actions.push({
kind: "extract",
path: path.concat(next)
});
}
continue;
}
seen[next] = 1;
path.push(next);
dfs(next);
path.pop();
seen[next] = 0;
}
};
dfs(start);
}
return actions;
}2. 替換主程式的 findOneExtractionPath()
function findOneExtractionPath(gameState, player, start) {
const board = gameState.board;
const startTile = board[start];
if (!startTile || startTile.player !== player) {
return null;
}
// 自己的叉零可直接完成本次提子。
if (startTile.type === TYPE_CROSS) {
return [start];
}
const seen = new Uint8Array(SIZE);
const path = [start];
seen[start] = 1;
function dfs(current) {
for (const next of movementDestinations(board, current)) {
if (seen[next]) continue;
const tile = board[next];
if (!tile) continue;
if (tile.type === TYPE_CROSS) {
if (tile.player === player) {
return path.concat(next);
}
continue;
}
seen[next] = 1;
path.push(next);
const result = dfs(next);
if (result) return result;
path.pop();
seen[next] = 0;
}
return null;
}
return dfs(start);
}3. 替換主程式的 getExtractionActions()
function getExtractionActions(gameState, player, limit = 500) {
const actions = [];
const board = gameState.board;
for (let start = 0; start < SIZE && actions.length < limit; start++) {
const startTile = board[start];
if (!startTile || startTile.player !== player) {
continue;
}
// 直接提走自己的叉零。
if (startTile.type === TYPE_CROSS) {
actions.push({
kind: "extract",
path: [start]
});
continue;
}
const seen = new Uint8Array(SIZE);
const path = [start];
seen[start] = 1;
function dfs(current) {
if (actions.length >= limit) return;
for (const next of movementDestinations(board, current)) {
if (actions.length >= limit || seen[next]) continue;
const tile = board[next];
if (!tile) continue;
if (tile.type === TYPE_CROSS) {
if (tile.player === player) {
actions.push({
kind: "extract",
path: path.concat(next)
});
}
continue;
}
seen[next] = 1;
path.push(next);
dfs(next);
path.pop();
seen[next] = 0;
}
}
dfs(start);
}
return actions;
}4. 替換主程式的 validateExtractionPath()
function validateExtractionPath(gameState, player, path) {
if (!Array.isArray(path) || path.length < 1) {
return "invalidPath";
}
const startTile = gameState.board[path[0]];
if (!startTile || startTile.player !== player) {
return "ownStart";
}
// 路徑只有一枚自己的叉零時,直接合法。
if (startTile.type === TYPE_CROSS) {
return path.length === 1 ? null : "invalidPath";
}
// 非叉零起點必須至少再走到一枚己方叉零。
if (path.length < 2) {
return "invalidPath";
}
const seen = new Set([path[0]]);
for (let step = 1; step < path.length; step++) {
const previous = path[step - 1];
const current = path[step];
if (seen.has(current)) return "revisited";
const tile = gameState.board[current];
if (!tile) return "emptyExtraction";
if (!movementDestinations(gameState.board, previous).includes(current)) {
return "illegalDirection";
}
// 叉零只能作為路徑終點,不能再從叉零繼續行動。
if (step < path.length - 1 && tile.type === TYPE_CROSS) {
return "invalidPath";
}
seen.add(current);
}
const finalTile = gameState.board[path[path.length - 1]];
if (
!finalTile ||
finalTile.type !== TYPE_CROSS ||
finalTile.player !== player
) {
return "opponentCross";
}
return null;
}5. 修改人工點擊叉零的處理
在 handleExtractionCell() 中找到:
if (tile.type === TYPE_CROSS) {
showNotice("errors.nonCrossStart");
return;
}
if (!findOneExtractionPath(state, state.turn, index)) {替換為:
// 選取自己的叉零時,直接完成本次提子。
if (tile.type === TYPE_CROSS) {
commitExtraction([index]);
return;
}
if (!findOneExtractionPath(state, state.turn, index)) {這樣玩家點擊自己的叉零後會立即提走,不需要再選下一枚棋子。
六、提子改為十次,並且只有包含對方棋子才得分
1. 替換 Worker applyAction() 的提子部分
在 Worker 的 applyAction() 中找到:
} else {
let value = 0;
for (const index of action.path) {
const tile = next.board[index];
value += tile[0] === p ? TRI[tile[1]] : -TRI[tile[1]];
}
next.raw[p] += value;
for (const index of action.path) {
next.board[index] = null;
}
next.extractIndex++;
if (next.extractIndex >= 2) {
next.done = true;
} else {
next.turn = next.first;
}
}替換為:
} else {
let value = 0;
let hasOpponentPiece = false;
for (const index of action.path) {
const tile = next.board[index];
if (tile[0] === p) {
value += TRI[tile[1]];
} else {
value -= TRI[tile[1]];
hasOpponentPiece = true;
}
}
// 路徑只有本方棋子時,本回合不計分。
if (hasOpponentPiece) {
next.raw[p] += value;
}
for (const index of action.path) {
next.board[index] = null;
}
next.extractIndex++;
if (next.extractIndex >= TOTAL_EXTRACTIONS) {
next.done = true;
} else {
// 雙方輪流提子。
next.turn = 1 - p;
}
}2. 替換主程式的 commitExtraction()
將整個函數替換為:
function commitExtraction(path, delayBeforeAI = 100) {
const player = state.turn;
const error = validateExtractionPath(state, player, path);
if (error) {
showNotice(`errors.${error}`);
return false;
}
let triangleDifference = 0;
let hasOpponentPiece = false;
for (const index of path) {
const tile = state.board[index];
if (tile.player === player) {
triangleDifference += TRIANGLES[tile.type];
} else {
triangleDifference -= TRIANGLES[tile.type];
hasOpponentPiece = true;
}
}
// 本回合若沒有提到對方棋子,得分為零。
const awardedScore = hasOpponentPiece
? triangleDifference
: 0;
const pathRecord = path.map(index => ({
square: coord(index),
index,
owner: state.board[index].player + 1,
type: state.board[index].type
}));
state.extractRaw[player] += awardedScore;
for (const index of path) {
state.board[index] = null;
}
state.record.push({
kind: "extract",
round: state.round,
player: player + 1,
extractionNumber: state.extractIndex + 1,
path: pathRecord,
containsOpponent: hasOpponentPiece,
triangleDifference,
rawScore: awardedScore
});
state.extractIndex++;
if (state.extractIndex >= TOTAL_EXTRACTIONS) {
finalizeRound(state);
} else {
// 後手、先手交替,直到雙方各提完五個叉零。
state.turn = 1 - player;
resolveNoLegalAction(state);
}
pushHistory();
onStateChanged(delayBeforeAI);
return true;
}七、修改提子進度顯示
1. 中文訊息
找到:
extract: "第 {round} 局 · 提子 {step}/2 · 輪到{player}({role})",替換為:
extract: "第 {round} 局 · 提子 {step}/{total} · 輪到{player}({role})",2. 英文訊息
找到:
extract: "Game {round} · Extraction {step}/2 · {player} to act ({role})",替換為:
extract: "Game {round} · Extraction {step}/{total} · {player} to act ({role})",3. 修改 normalMessage()
找到:
main: tr("messages.extract", {
round: state.round,
step: state.extractIndex + 1,
player: playerName(state.turn),
role: roleName(state.turn)
}),替換為:
main: tr("messages.extract", {
round: state.round,
step: state.extractIndex + 1,
total: TOTAL_EXTRACTIONS,
player: playerName(state.turn),
role: roleName(state.turn)
}),八、修改錯誤訊息
1. 中文
在中文 errors 中刪除:
nonCrossStart: "不能以「叉零」作為提子起點。",並在 noStock 附近加入:
leftoverCross: "後手最後剩下的棋子不能是「叉零」。",修改後相關部分應為:
noStock: "這一種棋子已經用完。",
leftoverCross: "後手最後剩下的棋子不能是「叉零」。",
adjacent: "每一手棋都要跟場上已有棋子貼合。",2. 英文
刪除:
nonCrossStart: "A Cross Zero cannot be the starting piece.",加入:
leftoverCross: "The second player's final unplaced piece cannot be a Cross Zero.",九、移除結果視窗中的「剩餘棋子已計分」
1. 修改中文結果文字
找到:
rawExtraction: "提子原始分:玩家一 {p1},玩家二 {p2};表中顯示雙方的零和淨分。",
leftoverIncluded: "後手剩餘的一枚棋子已計入提子原始分。",替換為:
rawExtraction: "提子累計分:玩家一 {p1},玩家二 {p2}。每回合只有提子路徑包含對方棋子時才計分;表中顯示雙方的零和淨分。",2. 修改英文結果文字
找到:
rawExtraction: "Raw extraction scores: Player One {p1}, Player Two {p2}. The table shows the zero-sum net score.",
leftoverIncluded: "The second player's unplaced piece is included in the raw extraction score.",替換為:
rawExtraction: "Accumulated extraction scores: Player One {p1}, Player Two {p2}. A turn scores only if its path contains an opposing piece. The table shows the zero-sum net score.",3. 修改 roundScoreTable()
找到:
<p class="result-note">
${tr("result.rawExtraction", {
p1: formatSigned(summary.extractRaw[0]),
p2: formatSigned(summary.extractRaw[1])
})}
${tr("result.leftoverIncluded")}
</p>替換為:
<p class="result-note">
${tr("result.rawExtraction", {
p1: formatSigned(summary.extractRaw[0]),
p2: formatSigned(summary.extractRaw[1])
})}
</p>十、修改回退功能
舊版 undo() 會在提子後一次回退到整個提子階段開始。新版有十個提子回合,應改成每次只回退一手。
將整個 undo() 替換為:
function undo() {
if (manualPath.length) {
manualPath = [];
renderBoard();
renderMessage();
renderToolbar();
return;
}
if (historyIndex <= 0) return;
restoreHistory(historyIndex - 1);
}十一、更新規則視窗
在 RULE_SECTIONS.zh 中修改以下段落。
1. 中文「目標」
替換為:
{
title: "目標",
html: `
<p>雙方在由實邊分隔的區域中比較三角形數量,數字之差作為得分。</p>
<p>落子後雙方輪流提子,直到各自提完五個「叉零」。雙方互換先後手玩兩局,兩局總分較高的一方獲勝。</p>
`
},2. 中文「落子」
替換為:
{
title: "落子",
html: `
<ol>
<li>先手第一手必須把「角二」放在中心格。</li>
<li>此後雙方輪流落子,新棋子必須以邊貼合場上至少一枚棋子。</li>
<li>相鄰棋子的兩條實邊不能在同一條格線上重合。</li>
<li>落子後不能令尚未佔用的格子形成封閉空洞。</li>
<li>先手放完二十五枚棋子,後手放二十四枚。</li>
<li>後手最後剩下的一枚棋子不能是「叉零」,而且不參與任何計分。</li>
</ol>
`
},3. 中文「提子」
替換為:
{
title: "提子",
html: `
<p>落子完畢後,由後手先提子,之後雙方輪流提子,直到各自提完五個「叉零」。</p>
<ol>
<li>每回合任意選擇一枚己方棋子作為起點,包括「叉零」。</li>
<li>如果選取的是己方「叉零」,直接提走該棋子並完成本回合。</li>
<li>如果選取其他棋子,按照目前棋子的三角形方向移動:單一走一步;雙二選一個方向走一步;角二沿斜向走一步或兩步;圍三選一個方向走一步。</li>
<li>每次必須落到仍在棋盤上的棋子,不能走到空位,不能走出棋盤邊界,也不能重複經過同一枚棋子。</li>
<li>提子路徑必須終止於己方的「叉零」。完成後,移除整條路徑上的所有棋子。</li>
</ol>
`
},4. 中文「計分」
替換為:
{
title: "計分",
html: `
<p><strong>落子分:</strong>實邊把棋盤分成多個區域。通向棋盤外的部分視為同一區域。只有同時含有雙方棋子的區域才計分;玩家一三角形數減去玩家二三角形數,就是玩家一在該區域的淨分,玩家二取得相反分數。</p>
<p><strong>提子分:</strong>每一個提子回合獨立計分。己方棋子的三角形數量減去對方棋子的三角形數量,就是該回合的提子分。如果該回合只提到本方棋子,則該回合得零分。</p>
`
},在 RULE_SECTIONS.en 中對應替換。
5. 英文「Objective」
{
title: "Objective",
html: `
<p>Players compare the numbers of triangles inside areas separated by solid edges. The difference becomes the score.</p>
<p>After placement, the players alternate extraction turns until each has removed all five of their Cross Zero pieces. Two games are played with the first-player roles swapped, and the higher combined score wins.</p>
`
},6. 英文「Placement」
{
title: "Placement",
html: `
<ol>
<li>The first player must place a Corner Two on the centre square.</li>
<li>Every later piece must share a side with at least one piece already on the board.</li>
<li>Two solid edges may not overlap along the same grid line.</li>
<li>A placement may not create an enclosed hole of empty squares.</li>
<li>The first player places twenty-five pieces and the second player places twenty-four.</li>
<li>The second player's final unplaced piece cannot be a Cross Zero and does not take part in scoring.</li>
</ol>
`
},7. 英文「Extraction」
{
title: "Extraction",
html: `
<p>After placement, the second player extracts first. The players then alternate until each has removed all five of their Cross Zero pieces.</p>
<ol>
<li>On each turn, choose any friendly piece as the starting piece, including a Cross Zero.</li>
<li>If a friendly Cross Zero is selected, it is removed immediately and the extraction turn is complete.</li>
<li>Otherwise, move according to the triangles on the current piece: Single One moves one step; Double Two chooses one direction and moves one step; Corner Two moves one or two diagonal steps; Surround Three chooses one direction and moves one step.</li>
<li>Every landing square must contain a piece. A move cannot leave the board, land on an empty square, or visit the same piece twice.</li>
<li>The path must finish on the acting player's own Cross Zero. All pieces in the completed path are removed.</li>
</ol>
`
},8. 英文「Scoring」
{
title: "Scoring",
html: `
<p><strong>Placement:</strong> solid edges divide the board into regions. All openings leading outside the board belong to one outside region. A region scores only when it contains pieces belonging to both players. Player One's triangles minus Player Two's triangles is Player One's net score; Player Two receives the opposite score.</p>
<p><strong>Extraction:</strong> every extraction turn is scored separately. Friendly triangles minus opposing triangles is the score for that turn. If the extracted path contains only friendly pieces, that turn scores zero.</p>
`
},十二、建議更新棋譜版本
在 exportRecord() 中找到:
version: 1,替換為:
version: 2,因為新版棋譜的提子回合、計分方式及 extractStart 資料結構已經改變。
修改後的關鍵行為
完成以上替換後,遊戲會按以下方式運作:
- 後手第 24 次落子後,剩餘棋子不能是叉零。
- 後手未落下的棋子不再加分。
- 棋子不能跨越棋盤邊界。
- 玩家可以直接選擇自己的叉零並提走。
- 提子由後手開始,雙方交替,共十個提子回合。
- 每方恰好提走五個自己的叉零。
- 每回合獨立計算提子分。
- 路徑只有本方棋子時,該回合得零分。
- 人類玩家、備用 AI 和 MCTS AI 均使用相同的新規則。
- 回退按鈕每次只回退一個提子回合,而不是退回整個提子階段。