Re: 將製作ejcees(中國象棋打譜程式)
你對currentBranch的理解有誤,currentBranch是一系列的數字,對應的是在當前線路的每一個分岔口(node.v.length>1)所選的分支。
比如currentBranch=[1,2],說明在第一個分岔口上,選擇了第二個分支,在遇到第二個分岔時選擇了第三個分支。
請修改你給的importExportedText()以及其它包含currentBranch的代碼。
比如currentBranch=[1,2],說明在第一個分岔口上,選擇了第二個分支,在遇到第二個分岔時選擇了第三個分支。
請修改你給的importExportedText()以及其它包含currentBranch的代碼。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
gemini:
You are absolutely right. I apologize for the misunderstanding. currentBranch only records choices at forks (where node.v.length > 1), not at every single step along the path.
Because of this, we need to track how many forks we have encountered up to the attach point (attachIndex), truncate currentBranch to that length, and then only append to currentBranch when a node actually has more than one child.
Here is the corrected importExportedText() function. You can replace the entire function with this updated version:
You are absolutely right. I apologize for the misunderstanding. currentBranch only records choices at forks (where node.v.length > 1), not at every single step along the path.
Because of this, we need to track how many forks we have encountered up to the attach point (attachIndex), truncate currentBranch to that length, and then only append to currentBranch when a node actually has more than one child.
Here is the corrected importExportedText() function. You can replace the entire function with this updated version:
代码: 全选
function importExportedText(text) {
text = text.trim();
if (!text) {
renderRecordUI();
renderNoteUI();
updateToolHighlights();
return;
}
// Normalize Chinese notations
text = text.replace(/车/g, '車').replace(/马/g, '馬').replace(/帅/g, '帥').replace(/将/g, '將').replace(/后/g, '後').replace(/进/g, '進');
text = text.replace(/ r(\n|\s|$)/g, ' w$1');
// Handle Direct JSON structure
if (text.startsWith('{')) {
try {
const data = JSON.parse(text);
function expand(node, parentFen) {
let dc, simfen, newNode;
if (node.m) {
dc = deriveCoordsFromMove(parentFen, node.m);
simfen = simulateMove(parentFen, dc);
newNode = { fen: simfen, move: node.m, lastMove: dc, c: node.c || "", v: [] };
} else {
newNode = { fen: parentFen, c: node.c || "", v: [] };
}
if (node.v) { newNode.v = node.v.map(child => expand(child, newNode.fen)); }
return newNode;
}
historyFEN = expand(data, data.fen);
initBranch(); // initBranch correctly builds currentBranch based on forks
currentStepIndex = 0;
renderRecordUI();
renderNoteUI();
jumpToStep(0);
saveStateToUndo();
updateToolHighlights();
return;
} catch (e) {
console.log("JSON parse failed, processing as text notation.");
}
}
// 1. Locate explicitly provided FEN if present
let importedFen = null;
const fenMatch = text.match(/(?:position fen )?([a-zA-Z0-9/]+ [wb])/);
let movesStr = text;
if (fenMatch) {
importedFen = fenMatch[1];
// Strip the FEN string declaration so it isn't parsed as moves later
movesStr = movesStr.replace(/position fen [a-zA-Z0-9/]+ [wb](\s+moves)?/, '');
if (movesStr === text) {
movesStr = movesStr.replace(fenMatch[1], '');
}
}
// 2. Identify the Attach Point (Parent Node)
let attachIndex = currentStepIndex;
let attachNode = null;
if (importedFen) {
// If FEN exists, trace backwards to find a matching state
for (let i = currentStepIndex; i >= 0; i--) {
const node = getNodeAtStep(i);
if (node && node.fen === importedFen) {
attachIndex = i;
attachNode = node;
break;
}
}
// No match down to root -> Overwrite everything
if (!attachNode) {
attachIndex = 0;
attachNode = { fen: importedFen, move: null, lastMove: null, c: "", v: [] };
historyFEN = attachNode;
currentBranch = [];
}
} else {
// No FEN -> Default attach point is the current active step
attachNode = getNodeAtStep(attachIndex);
importedFen = attachNode.fen;
}
// 3. Process move sequence text
movesStr = movesStr.replace(/^\d+\.\s*/gm, ' ').replace(/\s\d+\.\s/g, ' ').replace(/\.\.\./g, ' ');
let tokens = movesStr.split(/\s+/).filter(t => t.length > 0 && t !== 'moves');
let currentFen = importedFen;
let vMap = buildVirtualMap(currentFen);
let currNode = attachNode;
// Calculate how many forks exist from root up to attachIndex
let forkCount = 0;
let tempNode = historyFEN;
for (let i = 0; i < attachIndex; i++) {
if (tempNode.v && tempNode.v.length > 1) {
forkCount++;
}
const choice = (tempNode.v && tempNode.v.length > 1) ? (currentBranch[forkCount - 1] || 0) : 0;
tempNode = tempNode.v[choice];
}
// Truncate currentBranch to discard choices beyond the attach point
currentBranch = currentBranch.slice(0, forkCount);
let hasError = false;
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
let dc = null;
let moveEn = "";
let isRed = currentFen.includes(' w');
// Check for UCCI Engine Coordinate Format (e.g., "a0b0" or "h7e7")
if (/^[a-i][0-9][a-i][0-9]$/.test(token)) {
let startX = token.charCodeAt(0) - 97;
let startY = 9 - parseInt(token.charAt(1), 10);
let endX = token.charCodeAt(2) - 97;
let endY = 9 - parseInt(token.charAt(3), 10);
dc = { startX, startY, endX, endY };
let pId = vMap.get(`${startX},${startY}`);
if (!pId) { hasError = true; break; }
moveEn = getMoveNotation(pId, startX, startY, endX, endY, null, vMap);
} else {
let isEnglish = /^[a-zA-Z]/.test(token) || /^[+\-=1-9][a-zA-Z0-9]/.test(token);
moveEn = isEnglish ? token : NotationConverter.toEnglish(token, isRed);
dc = deriveCoordsFromMove(currentFen, moveEn, isRed);
if (!dc) { hasError = true; break; }
}
let nextFen = simulateMove(currentFen, dc);
// Check if this move already exists as a branch from the current node
let childIdx = currNode.v.findIndex(c => c.move === moveEn && c.fen === nextFen);
if (childIdx === -1) {
let newNode = { fen: nextFen, move: moveEn, lastMove: dc, c: "", v: [] };
currNode.v.push(newNode);
childIdx = currNode.v.length - 1;
}
// If this node is a fork (either it was already, or we just added a new branch), record the choice
if (currNode.v.length > 1) {
currentBranch[forkCount] = childIdx;
forkCount++;
}
currNode = currNode.v[childIdx];
currentFen = nextFen;
let pId = vMap.get(`${dc.startX},${dc.startY}`);
vMap.delete(`${dc.startX},${dc.startY}`);
if (pId) vMap.set(`${dc.endX},${dc.endY}`, pId);
}
if (hasError && tokens.length > 0) {
alert("Parsed partially due to an invalid move notation.");
}
// If the final node we landed on (or existing path we merged into) has further branches, default to 0
while (currNode && currNode.v && currNode.v.length > 0) {
if (currNode.v.length > 1) {
currentBranch.push(0);
}
currNode = currNode.v[0];
}
currentStepIndex = attachIndex;
renderRecordUI();
renderNoteUI();
jumpToStep(currentStepIndex);
saveStateToUndo();
updateToolHighlights();
}https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
- 附件
-
ejcees20260322aabran99946.7z- (36.43 KiB) 已下载 42 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
- 附件
-
ejcees20260323dpxq9995.7z- (36.56 KiB) 已下载 44 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
導入失敗:
代码: 全选
标题: 北京队 蒋川 负 江苏队 王斌
分类: 智运会
赛事: 2009年第01届全国智力运动会
轮次: 第03轮
布局: A64 过宫炮直车对左中炮
红方: 北京队 蒋川
黑方: 江苏队 王斌
结果: 黑方胜
日期: 2009.11.20 09:00-12:00
地点: 成都体育学院
组别: 男子团体
台次: 第01台
备注: 北京队 蒋川 0 - 2 王斌 江苏队
结束方式: 认负
记时规则: 30分+30秒
棋局类型: 全局
棋局性质: 慢棋
红方团体: 北京队
红方姓名: 蒋川
黑方团体: 江苏队
黑方姓名: 王斌
棋谱主人: 棋坛歌神
棋谱价值: 2
浏览次数: 8886
来源网站: 棋坛歌神
【主变: 黑胜】
1. 炮二平六 炮8平5
2. 马二进三 马8进7
3. 车一平二 马2进3
4. 兵七进一 炮2平1
5. 马八进九 车9进1
6. 车九平八 卒7进1
7. 炮八平七 卒5进1
8. 仕四进五 马3进5
9. 相三进五 车1进1
10. 炮七进四 车1平2
11. 车八进八 车9平2
12. 车二进六 车2平4
13. 马九进七 炮1进4
14. 车二平三 炮5退1
15. 马七进九 炮1平7
16. 车三平四 卒1进1
17. 马九进七 马5进3
18. 兵七进一 车4进4
19. 炮七平八 卒5进1
20. 炮八进三 车4平2
21. 炮八平九 炮5进5
22. 兵七进一 卒1进1
23. 车四平六 士6进5
24. 炮六平七 象7进5
25. 车六进二 炮5平3
26. 车六退五 炮3平1
27. 炮九退六 炮7平1
28. 兵七进一 马7进5
29. 兵七进一 马5进4
30. 炮七平九 马4进2
31. 车六进三 马2进3
32. 帅五平四 车2进1
33. 马三进二 车2平8
34. 车六平二 炮1平6
35. 车二平四 炮6退1
认负
棋谱由 http://www.dpxq.com/ 生成https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
導入成功:
代码: 全选
标题: 广东 吕钦 胜 吉林 陶汉明
分类: 五羊杯全国象棋冠军邀请赛
赛事: 2000年第20届五羊杯全国象棋冠军邀请赛
轮次: 第03轮
布局: C78 五七炮互进三兵对屏风马边卒右马外盘河 红左横车对黑兑边卒
红方: 广东 吕钦
黑方: 吉林 陶汉明
结果: 红方胜
日期: 1999.12.21
地点: 广州
评论: 东萍公司
作者: 东萍公司
结束方式: 认负
棋局类型: 全局
棋局性质: 慢棋
红方团体: 广东
红方姓名: 吕钦
黑方团体: 吉林
黑方姓名: 陶汉明
棋谱主人: m
棋谱价值: 1
浏览次数: 5776
来源网站: www.dpxq.com
1.炮二平五 马8进7
2.马二进三 马2进3
3.车一平二 车9平8
4.兵三进一 卒3进1
5.马八进九 卒1进1
6.炮八平七 马3进2
7.车九进一 卒1进1
8.兵九进一 车1进5
9.车二进四 象7进5
10.车九平四 车1平4
11.车四进三 车4进1
12.仕四进五 马2进3
13.马九进八 车4退3
14.炮五平六 炮8平9
15.车二进五 马7退8
16.炮七进三 卒5进1
17.炮七平九 士6进5
18.车四平七 马3进2
19.车七退三 马2退1
20.炮九退一 车4平2
21.马八退六 炮2平3
22.相三进五 马8进6
23.马六进五 车2进1
24.马五进六 炮9平4
25.车七进六 马6进5
26.车七退一 马5进4
27.车七平三 炮4进5
28.仕五进六 车2平8
29.车三平四 车8进2
30.仕六进五 车8平7
31.车四退四 卒9进1
32.帅五平四 马1进2
33.炮九进一 车7平5
34.车四进七 士5退6
35.马三进五 马2退3
36.相五进七 马4退3
37.炮九平二 前马退5
38.帅四平五 士6进5
39.炮二退三 马5退6
40.兵三进一 马3进5
41.马五进四 马5进6
42.炮二平四 后马进4
43.兵三平二 马6进8
44.马四进二 马4退6
45.炮四进三 马8退7
46.兵二平三 马7进5
47.炮四退二 士5进4
48.相七进五 士4进5
49.兵三进一 马6进5
50.炮四进二 后马进7
51.炮四平五 马7进8
52.炮五退一 马8退9
53.马二进三 将5平6
54.炮五平四 马9退8
55.炮四退三 马5退4
56.兵三平二 马8进7
57.炮四退一 马7退8
58.相五进三 卒9进1
59.相七退五 卒9进1
60.炮四进一 卒9平8
61.马三退四 马8进6
62.马四进六 马6进4
63.仕五进四 后马进6
64.马六退八 卒8平7
65.马八进七 士5进6
66.帅五平六 士6退5
67.仕四退五 卒7平6
68.兵二平三 士5进6
69.兵三进一 士6退5
70.炮四退一 象5进7
71.相五退三 象3进5
72.相三进一 象5退3
73.炮四进一 象7退5
74.仕五退四 士5进4
75.马七退八 士4退5
76.马八进七 士5进4
77.炮四平九 象5进7
78.炮九进七 象3进5
79.兵三平四 士4退5
80.炮九平五 马6退7
81.马七退六 马4退5
82.炮五平九 卒6平5
83.炮九退七 卒5平4
84.炮九平四 将6平5
85.仕六退五 将5平4
86.仕五进四 卒4平5
87.帅六平五 卒5平6
88.仕四退五 将4平5
89.仕五进六 马5退3
90.兵四进一*
棋谱由 http://www.dpxq.com/ 生成https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
修複導入東萍象棋文本失敗的問題。
- 附件
-
ejcees20260323dpxqbug99951.7z- (36.6 KiB) 已下载 43 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
小小修改一個小問題: https://ejsoon.vip/ejcees
- 附件
-
ejcees20260323dpcfbug99952.7z- (36.59 KiB) 已下载 39 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
- 附件
-
ejcees20260323trimtxt99953.7z- (36.6 KiB) 已下载 45 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
在局面中增加「 - - 6 48」這幾個標識。
如之前是「C1bak4/4a4/4b1n2/2PR4p/6p2/pr2p4/4c1c1P/2C1B1N2/4A4/2BAK4 w」,現在增加後就是「C1bak4/4a4/4b1n2/2PR4p/6p2/pr2p4/4c1c1P/2C1B1N2/4A4/2BAK4 w - - 6 25」。
第一個「-」表示有沒有rotate,第二個「-」表示有沒有flip,第三個數字為吃子之後的半回合數(初始為0),第四個為當前回合數(初始為1)。
文字描述:
當拖動時,應顯示為坐標+英文走法。
如果走子不對,分兩種情況,一是不符行走規則,二是被將不能執行。將在intervalTime*2s後恢復。恢復當前著法時,應為坐標+英文走法。
做成ejceesmini.js。
做成ejceessvg.js。https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
改進:
一,在fen局面中增加「 - - 6 25」這幾個標識。
如之前是「C1bak4/4a4/4b1n2/2PR4p/6p2/pr2p4/4c1c1P/2C1B1N2/4A4/2BAK4 w」,現在增加後就是「C1bak4/4a4/4b1n2/2PR4p/6p2/pr2p4/4c1c1P/2C1B1N2/4A4/2BAK4 w - - 6 25」。
第一個「-」表示有沒有rotate(如有則為1),第二個「-」表示有沒有flip(如有則為1),第三個數字為吃子之後的半回合數(初始為0),第四個為當前回合數(初始為1)。
二,ejceesoutput文字描述:
當點擊或拖動ejceesstep內的元素時,應顯示為「移動坐標+英文走法」(跟走棋時一樣)。
如果走子不對,可能是不符行走規則,也可能是因為送將不能執行,都要作相應文字提示,在moveInterval*2s後恢復「移動坐標+英文走法」。
選取棋子時,將顯示「棋子坐標和其id」,再次點擊相同棋子取消選取時恢復為「移動坐標+英文走法」。如果選取的是非行動方的棋子,則在moveInterval*2s後恢復「移動坐標+英文走法」。
只需給出所要修改的地方,所有的代碼和注釋都應使用英文。
一,在fen局面中增加「 - - 6 25」這幾個標識。
如之前是「C1bak4/4a4/4b1n2/2PR4p/6p2/pr2p4/4c1c1P/2C1B1N2/4A4/2BAK4 w」,現在增加後就是「C1bak4/4a4/4b1n2/2PR4p/6p2/pr2p4/4c1c1P/2C1B1N2/4A4/2BAK4 w - - 6 25」。
第一個「-」表示有沒有rotate(如有則為1),第二個「-」表示有沒有flip(如有則為1),第三個數字為吃子之後的半回合數(初始為0),第四個為當前回合數(初始為1)。
二,ejceesoutput文字描述:
當點擊或拖動ejceesstep內的元素時,應顯示為「移動坐標+英文走法」(跟走棋時一樣)。
如果走子不對,可能是不符行走規則,也可能是因為送將不能執行,都要作相應文字提示,在moveInterval*2s後恢復「移動坐標+英文走法」。
選取棋子時,將顯示「棋子坐標和其id」,再次點擊相同棋子取消選取時恢復為「移動坐標+英文走法」。如果選取的是非行動方的棋子,則在moveInterval*2s後恢復「移動坐標+英文走法」。
只需給出所要修改的地方,所有的代碼和注釋都應使用英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
當點擊tool-rotate和tool-flip時,要把所有historyFEN所有節點的「- - 」都改成1或-。
gemini:
這是一個很合理的改進!如果我們切換了翻轉或旋轉的視角,的確應該讓整盤棋的記錄(包含所有分支)都同步更新,這樣在導出存檔時,標識才會完全正確。
為此,我們需要寫一個遞迴函式(Recursive function)來遍歷 historyFEN 的整個樹狀結構,並將新的 rotate 和 flip 標識更新到每一個節點的 FEN 字串中。
請將以下代碼加入到你的腳本中(建議放在 tool-rotate 和 tool-flip 的事件監聽器上方):
1. 新增遞迴更新函式
2. 更新按鈕的事件監聽器
接著,找到你原本的 tool-rotate 和 tool-flip 點擊事件(大約在第 1210 行附近),用以下代碼替換它們:
這樣一來,無論你的歷史記錄有多少個分岔(branches),只要點擊了旋轉或翻轉按鈕,整個樹狀結構的 FEN 標識都會被瞬間洗一遍,確保導出或跳轉步驟時資料完全一致。
接下來你還需要我幫忙調整導入存檔(Import JSON / SVG)時解析這些新標識的邏輯嗎?
gemini:
這是一個很合理的改進!如果我們切換了翻轉或旋轉的視角,的確應該讓整盤棋的記錄(包含所有分支)都同步更新,這樣在導出存檔時,標識才會完全正確。
為此,我們需要寫一個遞迴函式(Recursive function)來遍歷 historyFEN 的整個樹狀結構,並將新的 rotate 和 flip 標識更新到每一個節點的 FEN 字串中。
請將以下代碼加入到你的腳本中(建議放在 tool-rotate 和 tool-flip 的事件監聽器上方):
1. 新增遞迴更新函式
代码: 全选
/**
* Recursively updates the rotate and flip flags for all nodes in the history tree.
* @param {Object} node - The current history node.
* @param {string} rotateFlag - '1' or '-'.
* @param {string} flipFlag - '1' or '-'.
*/
function updateHistoryFENFlags(node, rotateFlag, flipFlag) {
if (!node) return;
// Update the FEN string for the current node
if (node.fen) {
const parts = node.fen.split(' ');
// Ensure the FEN has the extended parameters before trying to update them
if (parts.length >= 4) {
parts[2] = rotateFlag;
parts[3] = flipFlag;
node.fen = parts.join(' ');
}
}
// Recursively update all children (branches)
if (node.v && node.v.length > 0) {
for (let i = 0; i < node.v.length; i++) {
updateHistoryFENFlags(node.v[i], rotateFlag, flipFlag);
}
}
}接著,找到你原本的 tool-rotate 和 tool-flip 點擊事件(大約在第 1210 行附近),用以下代碼替換它們:
代码: 全选
// Rotate tool - toggles 180° view and updates all history FENs.
document.getElementById('tool-rotate').addEventListener('click', () => {
if (isExportTextMode || isAutoPlaying) return;
isRotateEnabled = !isRotateEnabled;
// Determine current flags
const rFlag = isRotateEnabled ? '1' : '-';
const fFlag = isFlipEnabled ? '1' : '-';
// Apply flags to the entire history tree
updateHistoryFENFlags(historyFEN, rFlag, fFlag);
reapplyVisualPositions();
saveStateToUndo();
updateToolHighlights();
});
// Flip tool - toggles horizontal mirror and updates all history FENs.
document.getElementById('tool-flip').addEventListener('click', () => {
if (isExportTextMode || isAutoPlaying) return;
isFlipEnabled = !isFlipEnabled;
// Determine current flags
const rFlag = isRotateEnabled ? '1' : '-';
const fFlag = isFlipEnabled ? '1' : '-';
// Apply flags to the entire history tree
updateHistoryFENFlags(historyFEN, rFlag, fFlag);
reapplyVisualPositions();
saveStateToUndo();
updateToolHighlights();
});接下來你還需要我幫忙調整導入存檔(Import JSON / SVG)時解析這些新標識的邏輯嗎?
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
當tool-open以及tool-exp-txt導入json時,要根據已有的- -標識來設定rotate和flip。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
- 附件
-
ejcees20260323fenrotate99955.7z- (37.35 KiB) 已下载 49 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
在线用户
正浏览此版面之用户: 没有注册用户 和 4 访客
