將製作ejcees(中國象棋打譜程式)
Re: 將製作ejcees(中國象棋打譜程式)
增加ctrl加方向鍵跳到分支處,上下切換分支:
https://ejsoon.vip/wp-content/uploads/2 ... 99975.html
- 附件
-
ejcees20260326autoplay99975.7z- (41.08 KiB) 已下载 6 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
走子提示改為機器碼:
https://ejsoon.vip/wp-content/uploads/2 ... 99976.html
這回可以間接跟引擎玩了。
- 附件
-
ejcees20260326machine99976.7z- (41.08 KiB) 已下载 6 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
分析以下
「
标题: 红右正马 黑冲3卒型 第145局 黑左横车式
分类: 象棋谱大全-现代棋书专集
赛事: 布局定式与战理
轮次: 仙人指路 第5章 仙人指路对卒底炮转列炮
布局: E12 仙人指路转右中炮对卒底炮
日期: 0000.00.00
评论: 阎文清,张强
作者: 阳光海盗
棋谱主人: 象棋谱大全
棋谱价值: 0
浏览次数: 14388
来源网站: http://www.dpxq.com/
本局黑左横车式,弈至如图列举黑方两种应法:(甲)变横车过宫属于直感应付,从局势的发展来看,黑方3路线弱点突出,易受攻击而累及全局。(乙)变跳边马,继而退炮联防,是较有弹性的布置,可争得对抗之势。
【主变】
1. 兵七进一 炮2平3
2. 炮八平五
左中炮是早期多见的走法,后因效果欠佳,渐受冷落,尤其是在80年代,由于右中炮的兴起(并取得了良好的战绩),受其负面影响,红左中炮这一变例在大赛中鲜有使用,几乎已被人们所遗忘。但布局的演变大多是此起彼伏,带有阶段性的特点。步入90年代,经棋手们进一步的改进,使此布局又重现了生机,从而展开新一轮的“兵炮”之争。
2. ………… 炮8平5
黑方以列手炮应战,针锋相对。如改走马8进7或象3进5,则马八进七,红方效力充分。这也是左中炮与右中炮的区别所在。
3. 马二进三
改进之着。如改走马八进七,则马8进7,红方右翼易受掣肘。因如接走马二进一,则中路有显薄弱;又如马二进三,则又要顾忌黑方卒3进1的凌逼。这正是兵方长久以来悬而未解的难点所在。现红方跳右马,以弃七兵为契机,迅速展开右翼子力,着法积极有力。它给“左中炮”容易被动挨打的偏面之说画上了句号。
3. ………… 马8进7
4. 车一平二 卒3进1
另有车9进1或马2进1的应着,但马八进七顺利跃马后,红方较从容。
5. 马八进九 卒3进1
6. 车九平八 车9进1
起横车呼应右翼,并对过河卒有所兼顾。
7. 仕六进五
红方补士,着法细腻,它有效地限制了黑方炮3退1再马2进3的理想部署。
如图形势,黑方主要有两种应着:(甲)车9平4;(乙)马2进1。分述如下:
7. ………… 车9平4 ①
8. 炮二进四
伸炮过河,窥虚进招,是此变例的精华着法。如改走炮二平一,则落入俗套。
8. ………… 炮3进2 ②③
如改走马2进1,则车二进五!车4进2,兵三进一,红方主动;又如改走卒7进1,则车二进四,卒3平4,兵三进一,炮3进3,炮二平三,象7进9,炮五平七!伏车八平七的凶着,黑方难应。
9. 车二进四 卒3平4 ④
红车巡河,稳中带凶,黑方平卒避捉,正着,如改走炮3平7,则车二平七!炮7进3,车七进五,炮5进4,车八进三,炮5退2,车八进二,炮5进2,炮二平五,马7进5,车八平五,车4进5,车五进一,士6进5,车五进二,红胜定。
10. 炮五平六 卒4平5
11. 兵五进一 马2进3
12. 相七进五 车1平2
13. 车八进九 马3退2
14. 马九进七 卒1进1
15. 车二平三 炮3平7
16. 炮二退一 红方占优。
变①接主变第7回合
7. ………… 马2进1
8. 炮二进四 炮3退1
退炮加强联防,随机应变。
9. 车二进五 马1进3 ⑤
改走车1平2邀兑,较为工稳,以下:车八进九,马1退2,车二平七,车9平8!双方互缠。
10. 炮二平五 马7进5
11. 炮五进四 炮3平5
12. 炮五进二 车9平5
13. 车二平七 马3退4
14. 车七退一 红方稍优。
变②接主变第8回合
8. ………… 马2进1
9. 车二进五 车4进2
10. 兵三进一 红方主动。
变③接主变第8回合
8. ………… 卒7进1
9. 车二进四 卒3平4
10. 兵三进一 炮3进3
11. 炮二平三 象7进9
12. 炮五平七
伏车八平七的凶着,黑方难应。
变④接主变第9回合
9. ………… 炮3平7
10. 车二平七 炮7进3
11. 车七进五 炮5进4
12. 车八进三 炮5退2
13. 车八进二 炮5进2
14. 炮二平五 马7进5
15. 车八平五 车4进5
16. 车五进一 士6进5
17. 车五进二 红胜定。
变⑤接变①第9回合
9. ………… 车1平2
10. 车八进九 马1退2
11. 车二平七 车9平8
双方互缠。
棋谱由 http://www.dpxq.com/ 生成
」
不以「\d+\.」開頭的行,將成為該節點的評論。比如「炮3退1」的評論是「退炮加强联防,随机应变。」
首個「\d+\.」前面的行,將成為根節點的評論。但是不包括「
棋谱价值: …
浏览次数: …
来源网站: …
【主变】」。
「变⑤接变①第9回合」的「接」字是指分支要接到哪裡。可能接到「主節點」上,也有可能接到別的節點上。
主節點就是分支第一個子節點。
寫出一個函式,判斷是否為東萍格式(行首含有「\d+\.」),然後轉成historyFEN。
代碼中只能出現「接」、「主」這兩個中文,再加上⑤①這樣的圈圈數字來定位,比如「变⑤接变①第9回合」提取成「⑤接①9」,「变③接主变第8回合」提取出「③接主8」,則可以知道這些分支接入的是哪個節點。
只需給出所要修改的地方,所有的代碼和注釋都要使用英文。https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
【主变】
1. 兵三进一 ①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱
变①接主变第1回合
1. 兵七进一 卒9进1 ⑲
变②接主变第1回合
1. 车一进一 马8进7 ⑳
变③接主变第1回合
1. 仕六进五
变④接主变第1回合
1. 帅五进一
变⑤接主变第1回合
1. 炮八进一
变⑥接主变第1回合
1. 炮八进二
变⑦接主变第1回合
1. 炮八进三
变⑧接主变第1回合
1. 炮二平三
变⑨接主变第1回合
1. 炮二平四
变⑩接主变第1回合
1. 炮二平五
变⑪接主变第1回合
1. 炮二平六
变⑫接主变第1回合
1. 炮二进一
变⑬接主变第1回合
1. 炮二进二
变⑭接主变第1回合
1. 炮二进三
变⑮接主变第1回合
1. 炮二进四
变⑯接主变第1回合
1. 相三进一
变⑰接主变第1回合
1. 相七进五
变⑱接主变第1回合
1. 相三进五
变⑲接变①第1回合
1. ………… 炮8进1
变⑳接变②第1回合
1. ………… 象7进5
棋谱由 http://www.dpxq.com/ 生成https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
改進:
當導入文本時,形如「1. 炮二平五 炮8平5」的文本要作以下調整。
首先在代碼中,它將與其它格式獨立出來,一旦判定為這種格式,則專門處理。
它可以附上評論,直接寫在著法下面。如果是半回合插入評論,則後面會用「…………」來接上,例如:
「
1. 炮二平五 炮8平5
2. 马二进三 马8进7
3. 车一平二 车9进1
4. 马八进七
至此形成顺炮直车对横车的基本阵势。
4. ………… 车9平4
5. 兵三进一 卒3进1
对挺3卒,互相制约,是布局中的常用战术。
」
這裡的「马八进七」、「卒3进1」都有評論。
它將能接入分支,接入方法是在後方加圈圈數字「」,然後下方會有獨立的一塊來表示對應的分支,例如:
「
7. 车二平七 车4进1
8. 马三进四 ④
正着。此时红方不可车七进四贸然吃象,否则炮2平3,马七退五,象7进5,车七退一,马2进4,黑方大优。
8. ………… 马2进3
9. 炮八进四 炮5平3 ⑤
平炮打车,次序井然。如径走象3进5,则炮八平三!炮5平3,车七平四,车1平2,炮五平三,黑方左翼受攻。
10. 炮八平七 象3进5
变③接主变第8回合
8. 车九进一 象3进1
变④接变②第8回合
8. 车七进四 炮2平3
黑方大优。
」
④指的就是第7回合「车4进1」之後,第一分支為「马三进四」,第二分支為「车七进四」。
在代碼中只需要包含「①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳」這些符號,不要存在中文。
不需要管「接主变第n回合」還是「接变x第n回合」,只要第二次出現的圈圈數字下方的段落,它就跟第一次出現的地方是同級節點。
在「【」或「1.」前面的文字,將成為根節點的評論。
以上修改只影響到「導入文本」,跟「導出文本」無關。
只需給出要修改的地方,所有的代碼和注釋都要使用英文。https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
To implement this feature, you only need to replace the importExportedText function. The new logic will detect if the text matches the specific numbered format. If it does, it will parse the file sequentially: applying moves, attaching comments directly to the current node, parsing branch markers ①-⑳, linking them to the parent context of the specified move, and seamlessly jumping back when those markers appear on a standalone line.
Here is the fully updated importExportedText function:
代码: 全选
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 URL format directly if pasted
if (text.includes('#') || text.startsWith('i;') || text.startsWith('i+') || text.startsWith('i@') || /^([a-zA-Z0-9]+,)+[wb]/.test(text)) {
let hashPart = text.includes('#') ? text.split('#')[1] : text;
if (hashPart) {
try {
let parsedTree = parseUrlHash(hashPart);
if (parsedTree && parsedTree.fen) {
historyFEN = parsedTree;
initBranch();
currentStepIndex = 0;
let fenArr = historyFEN.fen.split(' ');
if (fenArr.length >= 4) {
isRotateEnabled = fenArr[2] === '1';
isFlipEnabled = fenArr[3] === '1';
reapplyVisualPositions();
}
renderRecordUI();
renderNoteUI();
jumpToStep(0);
saveStateToUndo();
updateToolHighlights();
return;
}
} catch (e) {
console.log("Failed to parse as URL parameter, falling back to text notation.", e);
}
}
}
// 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);
let fenArr = historyFEN.fen.split(' ');
if (fenArr.length === 6) {
isRotateEnabled = fenArr[2] === '1';
isFlipEnabled = fenArr[3] === '1';
reapplyVisualPositions();
}
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.");
}
}
// --- NEW: Handle Formatted Text with Branches and Comments ---
// Checks if the format contains numbered move notation (e.g., 1. 炮二平五 or 1. ... 炮8平5)
const isNumberedFormat = /(?:^|\n)\s*\d+\.\s*(?:[.]{3,}|…+|[\u4e00-\u9fa5]+)/.test(text);
if (isNumberedFormat) {
let importedFen = INITIAL_FEN;
const fenRegex = /\b(?:[rnbakcpRNBAKCP1-9]{1,9}\/){9}[rnbakcpRNBAKCP1-9]{1,9} [wb](?:(?: -| \d+){4})?\b/;
const fenMatch = text.match(fenRegex);
let textBody = text;
if (fenMatch) {
importedFen = fenMatch[0];
textBody = textBody.replace(fenMatch[0], ''); // Remove FEN so it isn't parsed as comment
}
// Isolate the root comment from the moves
let cutIndex = -1;
let matchMeta = textBody.match(/【/);
let matchMove = textBody.match(/(?:^|\n)\s*\d+\./);
if (matchMeta && matchMove) {
cutIndex = Math.min(matchMeta.index, matchMove.index);
} else if (matchMeta) {
cutIndex = matchMeta.index;
} else if (matchMove) {
cutIndex = matchMove.index;
}
let rootComment = "";
if (cutIndex > 0) {
rootComment = textBody.substring(0, cutIndex).trim();
}
if (cutIndex !== -1) {
textBody = textBody.substring(cutIndex);
}
textBody = textBody.replace(/【.*?】/g, ''); // Strip metadata
let attachNode = {
fen: importedFen,
move: null,
lastMove: null,
c: rootComment,
v: []
};
historyFEN = attachNode;
currentBranch = [];
let currNode = attachNode;
let currentFen = importedFen;
let branchMap = {}; // Maps circled numbers to their parent context
let lines = textBody.split('\n').map(l => l.trim()).filter(l => l.length > 0);
let hasError = false;
const circleRegex = /[①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳]/;
for (let line of lines) {
// Check if line is a branch marker header (contains symbol but doesn't start with a move number)
if (circleRegex.test(line) && !/^\s*\d+\./.test(line)) {
let match = line.match(circleRegex);
if (match) {
let marker = match[0];
// Jump back to the point indicated by the branch marker
if (branchMap[marker]) {
currNode = branchMap[marker].node;
currentFen = branchMap[marker].fen;
}
}
continue;
}
// Check if line contains moves
let moveMatch = line.match(/^\s*(\d+)\.\s*(.*)/);
if (moveMatch) {
let remainder = moveMatch[2];
let tokens = remainder.trim().split(/\s+/).filter(t => t.length > 0);
let parentContext = { node: currNode, fen: currentFen };
for (let token of tokens) {
if (/^[.]{3,}$|^…+$/.test(token)) {
continue; // Skip half-move ellipsis markers
}
// Check for inline branch marker
let circleMatch = token.match(circleRegex);
if (circleMatch) {
let marker = circleMatch[0];
// Map the marker to the state *before* this alternative move is made
branchMap[marker] = { node: parentContext.node, fen: parentContext.fen };
token = token.replace(circleRegex, '');
if (token === '') continue; // Skip if it was just the marker
}
let isRed = currentFen.includes(' w');
let moveEn = NotationConverter.toEnglish(token, isRed);
let dc = deriveCoordsFromMove(currentFen, moveEn, isRed);
if (!dc) {
hasError = true;
break;
}
let nextFen = simulateMove(currentFen, dc);
// Append or select the child 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;
}
// Update the parent context before shifting the cursor forward
parentContext = { node: currNode, fen: currentFen };
currNode = currNode.v[childIdx];
currentFen = nextFen;
}
} else {
// Regular text logic: append as a comment to the currently active node
if (currNode) {
if (currNode.c) currNode.c += '\n' + line;
else currNode.c = line;
}
}
}
if (hasError) {
alert("Parsed partially due to an invalid move notation.");
}
initBranch();
currentStepIndex = 0;
let fenArr = historyFEN.fen.split(' ');
if (fenArr.length >= 4) {
isRotateEnabled = fenArr[2] === '1';
isFlipEnabled = fenArr[3] === '1';
reapplyVisualPositions();
}
renderRecordUI();
renderNoteUI();
jumpToStep(0);
saveStateToUndo();
updateToolHighlights();
return;
}
// --- END OF NEW LOGIC ---
// 1. Locate explicitly provided FEN if present
let importedFen = null;
const fenRegex = /\b(?:[rnbakcpRNBAKCP1-9]{1,9}\/){9}[rnbakcpRNBAKCP1-9]{1,9} [wb](?:(?: -| \d+){4})?\b/;
const fenMatch = text.match(fenRegex);
let movesStr = text;
if (fenMatch) {
importedFen = fenMatch[0];
// Strip the FEN string declaration so it isn't parsed as moves later
const cleanRegex = new RegExp(`position fen | moves |${fenRegex.source}`, 'g');
movesStr = movesStr.replace(cleanRegex, '').trim();
}
// 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
let movesArr = movesStr.replace(/\.\.\./g, ' ').split('\n');
for (let i = 0; i < movesArr.length; i++) {
if (/^\d+\./.test(movesArr[i].trim())) {
let j = i + 1;
while (j < movesArr.length) {
if (!/^\d+\./.test(movesArr[j].trim())) {
break;
}
j++;
}
movesStr = movesArr.slice(i, j).map(str => {
str = str.trim().replace(/^\d+\./g, '');
return str.trim().split(/\s+/).slice(0, 2).join(' ');
}).join('\n');
break;
}
}
let tokens = movesStr.split(/\s+/).filter(t => t.length > 0 && t !== 'moves');
let tokensStartIdx = 0;
for (let i = 0; i < tokens.length; i++) {
if (/^1\./.test(tokens[i])) {
tokensStartIdx = i;
break;
}
}
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 = tokensStartIdx; i < tokens.length; i++) {
let token = tokens[i].replace(/^\d+\.\s*/, '').substring(0, 4);
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(中國象棋打譜程式)
代码: 全选
// Check if line contains moves
let moveMatch = line.match(/^\s*(\d+)\.\s*(.*)/);
if (moveMatch) {
let remainder = moveMatch[2];
let tokens = remainder.trim().split(/\s+/).filter(t => t.length > 0);
let lastParentContext = null; // Track the parent of the most recently parsed move
for (let token of tokens) {
if (/^[.]{3,}$|^…+$/.test(token)) {
continue; // Skip half-move ellipsis markers
}
// Check for branch markers
let circleMatch = token.match(circleRegex);
if (circleMatch) {
let marker = circleMatch[0];
token = token.replace(circleRegex, '');
if (token !== '') {
// Marker is attached to the move (e.g., "马三进四④")
// The parent of this branch is the current node BEFORE this move is applied
branchMap[marker] = { node: currNode, fen: currentFen };
} else {
// Marker is standalone (e.g., "马三进四 ④")
// It applies to the alternative of the LAST processed move
if (lastParentContext) {
branchMap[marker] = lastParentContext;
} else {
// Fallback if marker appears before any move is parsed
branchMap[marker] = { node: currNode, fen: currentFen };
}
continue; // Skip further processing since it was only a marker
}
}
// Save context BEFORE applying the move, to serve as the parent for subsequent standalone markers
lastParentContext = { node: currNode, fen: currentFen };
let isRed = currentFen.includes(' w');
let moveEn = NotationConverter.toEnglish(token, isRed);
let dc = deriveCoordsFromMove(currentFen, moveEn, isRed);
if (!dc) {
hasError = true;
break;
}
let nextFen = simulateMove(currentFen, dc);
// Append or select the child 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;
}
// Update cursor forward
currNode = currNode.v[childIdx];
currentFen = nextFen;
}
}https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
一,把文本用\n分成陣列後,trim()之後匹配「^\d+\.」,只要匹配到一個,則符合這種棋譜類型。
二,把每一個「^\d+\.」開頭的行,在替換掉圈圈數字以及省略號(可能為…+或...)之後,用空格分隔。
三,把不以「^\d+\.」開頭的行,當作前一步的評論。
四,如果出現圈圈數字在某一步的右邊,它將表示分支。
第一次出現的地方是指插入位置,一定會在以「^\d+\.」開頭的行內。
第二次會帶出一個段落表示分支插入的著法,該行一定不以「^\d+\.」開頭,。
例如:「
7. 车二平七 车4进1
8. 马三进四 ④
变④接变②第8回合
8. 车七进四
」
則「车4进1」的子節點是「马三进四」和「车七进四」。
特別注意插入的地方不是在它後面,而是在圈圈的前面,也就是說不能把「车七进四」當成「马三进四」的子節點。
五,同一個圈圈數字,只需要管前兩次出現的地方,第三次或更多則不用去管。
六,分支內是可能再嵌套分支的。
七,分支的圈圈數字,有可能出現在一回合的先手方,也可以是後手方。比如可以是「 7. 车二平七 ④ 车4进1 」,也可以是「 7. 车二平七 车4进1 ④ 」。
八,分支的圈圈數字,有可能多個連在一起,。比如可以是「 7. 车二平七 ②④ 车4进1 」,表示這裡要插入兩個分支。
只需給出所要修改的地方,所有的代碼和注釋都要使用英文。
gemini pro用完了,現在是thinking:
代码: 全选
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, '進');
// --- TYPE DETECTION ---
// Split lines and check if any line starts with "1.", "2.", etc.
const lines = text.split('\n').map(l => l.trim());
const isNumberedFormat = lines.some(line => /^\d+\./.test(line));
if (isNumberedFormat) {
let importedFen = INITIAL_FEN;
const fenRegex = /\b(?:[rnbakcpRNBAKCP1-9]{1,9}\/){9}[rnbakcpRNBAKCP1-9]{1,9} [wb](?:(?: -| \d+){4})?\b/;
const fenMatch = text.match(fenRegex);
if (fenMatch) importedFen = fenMatch[0];
// Root Node setup
let rootNode = {
fen: importedFen,
move: null,
lastMove: null,
c: "",
v: []
};
// Extract root comment (text before the first "1." or metadata bracket)
let firstMoveIndex = text.search(/(?:^|\n)\s*\d+\./);
let firstBracketIndex = text.indexOf('【');
let cutPoint = (firstMoveIndex !== -1 && firstBracketIndex !== -1)
? Math.min(firstMoveIndex, firstBracketIndex)
: Math.max(firstMoveIndex, firstBracketIndex);
if (cutPoint > 0) {
rootNode.c = text.substring(0, cutPoint).trim();
}
historyFEN = rootNode;
let currNode = rootNode;
let currFen = importedFen;
// Maps circle markers to their definition: { node, fen, count }
// count: 1 means first appearance (anchor set), 2 means second (jumped)
const branchMap = new Map();
const circleRegex = /[①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳]/g;
for (let line of lines) {
if (!line) continue;
if (/^\d+\./.test(line)) {
// --- MOVE LINE PROCESSING ---
// Strip the leading number "1. " and separate tokens
let movePart = line.replace(/^\d+\./, '').trim();
// Remove ellipses but keep circle markers
let tokens = movePart.replace(/[.]{3,}|…+/g, ' ').split(/\s+/).filter(t => t);
for (let token of tokens) {
let markers = token.match(circleRegex) || [];
// Clean the move string from markers and notation fluff
let moveStr = token.replace(/[①-⑳.…]/g, '');
// FIRST APPEARANCE: If a marker is found on a move,
// its parent is the state BEFORE this move is executed.
for (let m of markers) {
if (!branchMap.has(m)) {
branchMap.set(m, { node: currNode, fen: currFen, count: 1 });
}
}
if (moveStr) {
let isRed = currFen.includes(' w');
let moveEn = NotationConverter.toEnglish(moveStr, isRed);
let dc = deriveCoordsFromMove(currFen, moveEn, isRed);
if (dc) {
let nextFen = simulateMove(currFen, dc);
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);
currNode = newNode;
} else {
currNode = currNode.v[childIdx];
}
currFen = nextFen;
}
}
}
} else {
// --- COMMENT OR BRANCH HEADER PROCESSING ---
let markers = line.match(circleRegex) || [];
let branchSwitched = false;
for (let m of markers) {
let info = branchMap.get(m);
// SECOND APPEARANCE: Jump parser back to the recorded anchor point
if (info && info.count === 1) {
currNode = info.node;
currFen = info.fen;
info.count = 2; // Mark as handled
branchSwitched = true;
break;
}
}
// If it's not a branch header (or it's the 3rd+ time seeing a marker), it's a comment
if (!branchSwitched && !line.includes('【')) {
if (currNode.c) currNode.c += "\n" + line;
else currNode.c = line;
}
}
}
// UI update logic
initBranch();
currentStepIndex = 0;
let fenArr = historyFEN.fen.split(' ');
if (fenArr.length >= 4) {
isRotateEnabled = fenArr[2] === '1';
isFlipEnabled = fenArr[3] === '1';
reapplyVisualPositions();
}
renderRecordUI();
renderNoteUI();
jumpToStep(0);
saveStateToUndo();
updateToolHighlights();
return;
}
// ... (keep the rest of the existing non-numbered/JSON/URL parsing logic below)
}https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
在线用户
正浏览此版面之用户: 没有注册用户 和 9 访客
