將製作ejcees(中國象棋打譜程式)
Re: 將製作ejcees(中國象棋打譜程式)
https://ejsoon.vip/wp-content/uploads/2 ... 77741.html
- 附件
-
ejcees20260401moblie99977741.7z- (44.52 KiB) 已下载 7 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
在上傳的文件中,結合importExportedText和document.getElementById('tool-exp-svg').addEventListener('click', () => {...})這兩個函式,做一個獨立的函式ejceesExpSvg(text)。
首先,在importExportedText函式中,它會接受各種格式的棋譜文本,把它轉換為historyFEN,把這段提取出來,最後把轉換結果console.log。
做一個html+js頁面,來測試ejceesExpSvg(text)函式。裡面需要一個textarae和一個button,以及一個div用來輸出svg。當按下button時執行這個函式。- 附件
-
tmp2.txt- (36.04 KiB) 已下载 8 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>象棋 SVG 轉換測試</title>
<style>
body { font-family: sans-serif; padding: 20px; background: #f0f0f0; }
textarea { width: 100%; height: 150px; margin-bottom: 10px; }
#svg-container { margin-top: 20px; background: white; padding: 10px; border: 1px solid #ccc; min-height: 400px; }
.controls { margin-bottom: 20px; }
</style>
</head>
<body>
<h2>ejceesExpSvg 測試工具</h2>
<textarea id="inputText" placeholder="請貼入 FEN、JSON 或 著法文本 (如: 1. 炮二平五 馬8進7...)"></textarea>
<div class="controls">
<button onclick="handleConvert()">執行 ejceesExpSvg</button>
</div>
<h3>輸出結果:</h3>
<div id="svg-container"></div>
<script>
// --- 模擬原始環境中缺少的輔助變數與函式 ---
let historyFEN = null;
let currentBranch = [];
const moveInterval = 1.5; // [cite: 173]
const moveSpeed = 0.5; //
// 模擬基礎 SVG 模板 (對應原文中的 .ejceespb)
const SVG_TEMPLATE = `
<svg class="ejceespb" viewBox="0 0 432 480" xmlns="http://www.w3.org/2000/svg" style="width:400px">
<rect width="432" height="480" fill="#f9d3a1" />
<g class="etboard"></g>
<g class="etdrop"></g>
</svg>`;
// 模擬字串編碼 [cite: 155]
function b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => String.fromCharCode('0x' + p1)));
}
// 模擬座標轉換
function getVisualCoords(x, y) {
return { x: x, y: y };
}
// --- 核心函式:ejceesExpSvg ---
function ejceesExpSvg(text) {
console.log("開始解析文本...");
let movesStr = text.trim();
// 1. 文本標準化
movesStr = movesStr.replace(/车/g, '車').replace(/马/g, '馬').replace(/帅/g, '帥').replace(/将/g, '將').replace(/后/g, '後').replace(/进/g, '進');
// 2. 解析邏輯 (簡化版 importExportedText)
let parsedHistory = null;
if (movesStr.startsWith('{')) {
// 處理 JSON 格式 [cite: 10]
try {
const data = JSON.parse(movesStr);
function expand(node, parentFen) {
let newNode = {
fen: node.fen || parentFen,
move: node.m || null,
lastMove: node.lastMove || null, // 假設已有座標
c: node.c || "",
v: []
};
if (node.v) newNode.v = node.v.map(child => expand(child, newNode.fen));
return newNode;
}
parsedHistory = expand(data, data.fen); [cite: 20]
} catch (e) { console.error("JSON 解析失敗"); }
} else {
// 處理純文本或 FEN [cite: 49, 111]
// 此處簡化為建立一個基礎結構
parsedHistory = { fen: movesStr, v: [], c: "啟始位置" };
}
console.log("解析結果 (historyFEN):", parsedHistory);
historyFEN = parsedHistory;
// 3. SVG 生成邏輯 [cite: 140]
const parser = new DOMParser();
const svgDoc = parser.parseFromString(SVG_TEMPLATE, 'image/svg+xml');
const cloneSvg = svgDoc.querySelector('svg');
cloneSvg.setAttribute('class', 'ejceespbanimate'); [cite: 158]
const etboardClone = cloneSvg.querySelector('.etboard');
const etdropClone = cloneSvg.querySelector('.etdrop');
// 遍歷路徑節點 [cite: 168-172]
let pathNodes = [];
let curr = historyFEN;
while (curr && curr.v && curr.v.length > 0) {
curr = curr.v[0]; // 預設走第一條分支
pathNodes.push(curr);
}
const movedPieceIds = new Set();
const initialCoords = {};
// 生成動畫指令 [cite: 174-188]
pathNodes.forEach((node, i) => {
let stepNum = i + 1;
let moveData = node.lastMove;
if (!moveData) return;
let pieceId = "p" + i; // 模擬 pieceId
movedPieceIds.add(pieceId);
let fromStr = `${moveData.startX * 48 + 24},${moveData.startY * 48 + 24}`;
let toStr = `${moveData.endX * 48 + 24},${moveData.endY * 48 + 24}`;
// 建立 SMIL 動畫
const animT = document.createElementNS('http://www.w3.org/2000/svg', 'animateTransform');
animT.setAttribute('id', `step${stepNum}`);
animT.setAttribute('attributeName', 'transform');
animT.setAttribute('type', 'translate');
animT.setAttribute('from', fromStr);
animT.setAttribute('to', toStr);
animT.setAttribute('dur', `${moveSpeed}s`);
animT.setAttribute('fill', 'freeze');
animT.setAttribute('data-comment', b64EncodeUnicode(node.c || ""));
// 將動畫加入模擬的棋子中 (此處省略具體棋子創建,僅展示邏輯)
console.log(`生成第 ${stepNum} 步動畫: ${fromStr} -> ${toStr}`);
});
// 序列化輸出 [cite: 201]
const svgString = new XMLSerializer().serializeToString(cloneSvg);
return svgString;
}
function handleConvert() {
const text = document.getElementById('inputText').value;
const container = document.getElementById('svg-container');
// 執行函式
const resultSvg = ejceesExpSvg(text);
// 渲染到頁面
container.innerHTML = resultSvg;
}
</script>
</body>
</html>https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
一,在ejceesExpSvg中,缺少的變量都寫到其內部的最前面。
二,在「 return movesStr;」下方是插入節點位置的邏輯,現在去掉這個邏輯,改為如果前面提取到fen局面,則以這個局面為根節點,如果沒有提取到,則以INITIAL_FEN為根節點。
僅需給出所要修改的地方。
- 附件
-
ejceeseExpSvg202604011309.txt- (21.66 KiB) 已下载 10 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
function ejceesExpSvg(text) {
// --- 內部使用的變量定義 ---
let historyFEN = null;
let currentBranch = [];
let currentStepIndex = 0;
let isRotateEnabled = false;
let isFlipEnabled = false;
let isRangeMode = false;
let rangeStart = null;
let rangeEnd = null;
let isExportTextMode = false;
let isEditingComment = false;
let isEditMode = false;
let isAutoPlaying = false;
const INITIAL_FEN = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1";
const moveInterval = 1.5;
const moveSpeed = 0.5;
// ------------------------
// ... 隨後是 importExportedText 函式定義在 importExportedText 函式內部,找到 return movesStr;(約第 808 行),將其及其下方的「2. Identify the Attach Point」邏輯刪除,替換為以下以 importedFen 或 INITIAL_FEN 為根節點的邏輯 :
代码: 全选
// 刪除原有的 return movesStr; 及其下方的節點查找邏輯
// 2. Identify the Attach Point (Parent Node)
// 改為:以提取到的局面或初始局面作為唯一的根節點
let attachNode = {
fen: importedFen || INITIAL_FEN,
move: null,
lastMove: null,
c: "",
v: []
};
historyFEN = attachNode;
let attachIndex = 0;
currentBranch = [];
currentStepIndex = 0; // 重置當前步數索引
// 3. Process move sequence text (後續邏輯保持不變,將從此根節點開始解析著法)
// ...https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
在「return cloneSvg;」之前,對
jumpToStep(exportStart);
const cloneSvg = document.querySelector('.ejceespb').cloneNode(true);
進行更改。其意義:為先用jumpToStep改變當前局面的svg,然後再賦值給cloneSvg。
在jumpToStep()函式中,改變當前局面的svg要用到loadFEN()和applyLastMoveVisuals(),也要對其更改。
之前的svg是直接寫在頁面上,現在由於要整合進一個js的function中,所以svg不會出現在html頁面上。
僅需給出所要修改的地方。
- 附件
-
ejceeseExpSvg202604011420.txt- (54.46 KiB) 已下载 8 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
// 1. 你的 SVG 字串變量
const svgString = '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><circle cx="50" cy="50" r="40" fill="red" /></svg>';
// 2. 初始化 DOMParser
const parser = new DOMParser();
// 3. 將字串解析為 XML (SVG 屬於 XML 格式)
const doc = parser.parseFromString(svgString, 'image/svg+xml');
// 4. 取得解析後的 SVG 元素
// parseFromString 會回傳一個完整的 document,我們需要取出它的 root 元素
const clonedSvg = doc.documentElement;
// 現在 clonedSvg 就是一個真正的 DOM 元素,你可以像以前一樣操作它
clonedSvg.style.border = "1px solid black";
document.body.appendChild(clonedSvg);https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
3a1k3/4a2R1/9/p1p4c1/8p/2P6/P5P1P/4C4/7r1/2BAKAB2 w - - 0 1
- 附件
-
- Ejcees(3)(1).jpg (42.18 KiB) 查看 207 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
解決一個嚴重問題:難道之前判定是否會被將死都是錯誤的?
https://ejsoon.vip/wp-content/uploads/2 ... 77742.html
- 附件
-
ejcees20260402editbug99977742.7z- (44.5 KiB) 已下载 8 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
前面未完全理解代碼,現在在完全理解代碼的基礎上解決了問題,語義是對的了:
https://ejsoon.vip/wp-content/uploads/2 ... 77744.html
- 附件
-
ejcees20260402checkbugagain99977744.7z- (44.5 KiB) 已下载 9 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
新建的時候,保持rotate。
edit完之後,如果check會有提示。
https://ejsoon.vip/wp-content/uploads/2 ... 77745.html
- 附件
-
ejcees20260403newrotate99977745.7z- (44.53 KiB) 已下载 6 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
https://ejsoon.vip/wp-content/uploads/2 ... Enter.html
- 附件
-
CtrlEnter.7z- (100.53 KiB) 已下载 4 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
<div class="exp-fmt-btn active" data-fmt="cn" data-odd="0">Chinese Notation</div>
<div class="exp-fmt-btn" data-fmt="en" data-odd="0">English Notation</div>
<div class="exp-fmt-btn" data-fmt="engine" data-odd="0">Engine Format</div>
<div class="exp-fmt-btn" data-fmt="url" data-odd="0">URL Parameter</div>
現在要增加功能,當點擊其中一個時,程式將記住這個選擇,下次點擊tool-exp-txt時,上回記住的按鈕將會是active。
整個renderExportTextUI()的代碼如下,只需給出所要修改的地方,所有的代碼和注釋都要使用英文。
代码: 全选
// --- Refactored UI for Export Text ---
function renderExportTextUI() {
const recordContainer = document.querySelector('.ejceesrecord');
const commentDiv = document.querySelector('.ejceescomment');
const btnDiv = document.querySelector('.ejceestextbtn');
const cnText = generateExportText(false);
const cnTextWidthComment = generateCnTextWidthComment();
const enText = generateExportText(true);
const urlText = generateUrlHash();
// Generate UCCI Engine Format String
let path = getGamePath();
let engineText = "position fen " + path.fen;
let currNode = path;
if (currNode && currNode.v && currNode.v.length > 0) {
engineText += " moves";
}
while (currNode && currNode.v && currNode.v.length > 0) {
currNode = currNode.v[0];
let dc = currNode.lastMove;
if (dc) {
engineText += " " + String.fromCharCode(97 + dc.startX) + (9 - dc.startY) + String.fromCharCode(97 + dc.endX) + (9 - dc.endY);
}
}
// Create readonly textarea
recordContainer.innerHTML = `<textarea class="ejceescomment-edit" id="export-textarea" style="width:100%; height:100%; resize:none; border:none; outline:none; background:#2a2a2a; color:#fff; padding:8px; font-family:monospace; font-size:14px; white-space:pre-wrap; overflow:auto;"></textarea>`;
const textarea = document.getElementById('export-textarea');
// Set up the 4 toggle buttons, distributed evenly vertically
commentDiv.innerHTML = `<div class="exp-fmt-outer">
<div class="exp-fmt-btn active" data-fmt="cn">Chinese Notation</div>
<div class="exp-fmt-btn" data-fmt="en">English Notation</div>
<div class="exp-fmt-btn" data-fmt="engine">Engine Format</div>
<div class="exp-fmt-btn" data-fmt="url">URL Parameter</div>
</div>`;
const formats = {
'cn': cnText,
'en': enText,
'engine': engineText,
'url': urlText
};
const formatBtns = commentDiv.querySelectorAll('.exp-fmt-btn');
formatBtns.forEach(btn => {
btn.setAttribute('data-odd', '0');
btn.addEventListener('click', () => {
const wasActive = btn.classList.contains('active');
// Update active styling and reset others
formatBtns.forEach(b => {
b.classList.remove('active');
if (!wasActive && b !== btn) {
b.setAttribute('data-odd', '0'); // Reset state of inactive buttons
}
});
btn.classList.add('active');
const fmt = btn.getAttribute('data-fmt');
// If the button was just activated, display default text
if (!wasActive) {
btn.setAttribute('data-odd', '0');
textarea.value = formats[fmt];
} else {
// Toggle logic for already active buttons
if (fmt === 'cn') {
if (btn.getAttribute('data-odd') === '0') {
btn.setAttribute('data-odd', '1');
textarea.value = cnTextWidthComment;
} else {
btn.setAttribute('data-odd', '0');
textarea.value = formats[fmt];
}
} else if (fmt === 'engine') {
// Append searchmoves for engine format
if (btn.getAttribute('data-odd') === '0') {
btn.setAttribute('data-odd', '1');
const isRed = currentTurn === 'w';
const moves = getAllLegalMoves(isRed, tileMap);
// Format array of moves into standard coordinates
const searchMovesStr = moves.map(m => {
return String.fromCharCode(97 + m.startX) + (9 - m.startY) +
String.fromCharCode(97 + m.endX) + (9 - m.endY);
}).join(' ');
textarea.value = formats[fmt] + (searchMovesStr ? "\ngo searchmoves " + searchMovesStr : "");
} else {
btn.setAttribute('data-odd', '0');
textarea.value = formats[fmt];
}
} else {
// Any other formats just stay the same on repeat clicks
textarea.value = formats[fmt];
}
}
textarea.blur();
setTimeout(() => {
textarea.focus();
textarea.select();
}, 12);
});
});
// Initialize default state (Chinese)
textarea.value = cnText;
setTimeout(() => textarea.select(), 10);
btnDiv.innerHTML = `
<div class="ejceestextbtninner">
<div class="note-btn btn-confirm" id="exp-confirm" title="Confirm">
<svg viewBox="0 0 24 24"><path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
</div>
<div class="note-btn btn-cancel" id="exp-cancel" title="Cancel">
<svg viewBox="0 0 24 24"><path fill="#F44336" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</div>
</div>
`;
document.getElementById('exp-cancel').addEventListener('click', () => {
isExportTextMode = false;
renderRecordUI();
renderNoteUI();
updateToolHighlights();
});
document.getElementById('exp-confirm').addEventListener('click', () => {
const text = textarea.value;
isExportTextMode = false;
importExportedText(text);
});
}
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
// Global variable to store the last selected export format (default to 'cn')
let lastExportFormat = 'cn';
// --- Refactored UI for Export Text ---
function renderExportTextUI() {
const recordContainer = document.querySelector('.ejceesrecord');
const commentDiv = document.querySelector('.ejceescomment');
const btnDiv = document.querySelector('.ejceestextbtn');
const cnText = generateExportText(false);
const cnTextWidthComment = generateCnTextWidthComment();
const enText = generateExportText(true);
const urlText = generateUrlHash();
// Generate UCCI Engine Format String
let path = getGamePath();
let engineText = "position fen " + path.fen;
let currNode = path;
if (currNode && currNode.v && currNode.v.length > 0) {
engineText += " moves";
}
while (currNode && currNode.v && currNode.v.length > 0) {
currNode = currNode.v[0];
let dc = currNode.lastMove;
if (dc) {
engineText += " " + String.fromCharCode(97 + dc.startX) + (9 - dc.startY) + String.fromCharCode(97 + dc.endX) + (9 - dc.endY);
}
}
// Create readonly textarea
recordContainer.innerHTML = `<textarea class="ejceescomment-edit" id="export-textarea" style="width:100%; height:100%; resize:none; border:none; outline:none; background:#2a2a2a; color:#fff; padding:8px; font-family:monospace; font-size:14px; white-space:pre-wrap; overflow:auto;"></textarea>`;
const textarea = document.getElementById('export-textarea');
// Set up the 4 toggle buttons, use lastExportFormat to determine the 'active' class
commentDiv.innerHTML = `<div class="exp-fmt-outer">
<div class="exp-fmt-btn ${lastExportFormat === 'cn' ? 'active' : ''}" data-fmt="cn">Chinese Notation</div>
<div class="exp-fmt-btn ${lastExportFormat === 'en' ? 'active' : ''}" data-fmt="en">English Notation</div>
<div class="exp-fmt-btn ${lastExportFormat === 'engine' ? 'active' : ''}" data-fmt="engine">Engine Format</div>
<div class="exp-fmt-btn ${lastExportFormat === 'url' ? 'active' : ''}" data-fmt="url">URL Parameter</div>
</div>`;
const formats = {
'cn': cnText,
'en': enText,
'engine': engineText,
'url': urlText
};
const formatBtns = commentDiv.querySelectorAll('.exp-fmt-btn');
formatBtns.forEach(btn => {
btn.setAttribute('data-odd', '0');
btn.addEventListener('click', () => {
const wasActive = btn.classList.contains('active');
const fmt = btn.getAttribute('data-fmt');
// Remember the choice for next time
lastExportFormat = fmt;
// Update active styling and reset others
formatBtns.forEach(b => {
b.classList.remove('active');
if (!wasActive && b !== btn) {
b.setAttribute('data-odd', '0'); // Reset state of inactive buttons
}
});
btn.classList.add('active');
// If the button was just activated, display default text
if (!wasActive) {
btn.setAttribute('data-odd', '0');
textarea.value = formats[fmt];
} else {
// Toggle logic for already active buttons
if (fmt === 'cn') {
if (btn.getAttribute('data-odd') === '0') {
btn.setAttribute('data-odd', '1');
textarea.value = cnTextWidthComment;
} else {
btn.setAttribute('data-odd', '0');
textarea.value = formats[fmt];
}
} else if (fmt === 'engine') {
// Append searchmoves for engine format
if (btn.getAttribute('data-odd') === '0') {
btn.setAttribute('data-odd', '1');
const isRed = currentTurn === 'w';
const moves = getAllLegalMoves(isRed, tileMap);
// Format array of moves into standard coordinates
const searchMovesStr = moves.map(m => {
return String.fromCharCode(97 + m.startX) + (9 - m.startY) +
String.fromCharCode(97 + m.endX) + (9 - m.endY);
}).join(' ');
textarea.value = formats[fmt] + (searchMovesStr ? "\ngo searchmoves " + searchMovesStr : "");
} else {
btn.setAttribute('data-odd', '0');
textarea.value = formats[fmt];
}
} else {
textarea.value = formats[fmt];
}
}
textarea.blur();
setTimeout(() => {
textarea.focus();
textarea.select();
}, 12);
});
});
// Initialize state using the remembered format
textarea.value = formats[lastExportFormat];
setTimeout(() => textarea.select(), 10);
btnDiv.innerHTML = `
<div class="ejceestextbtninner">
<div class="note-btn btn-confirm" id="exp-confirm" title="Confirm">
<svg viewBox="0 0 24 24"><path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
</div>
<div class="note-btn btn-cancel" id="exp-cancel" title="Cancel">
<svg viewBox="0 0 24 24"><path fill="#F44336" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</div>
</div>
`;
document.getElementById('exp-cancel').addEventListener('click', () => {
isExportTextMode = false;
renderRecordUI();
renderNoteUI();
updateToolHighlights();
});
document.getElementById('exp-confirm').addEventListener('click', () => {
const text = textarea.value;
isExportTextMode = false;
importExportedText(text);
});
}https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
直接用gemini fast,增加記住導出文本格式的功能:
https://ejsoon.vip/wp-content/uploads/2 ... 77746.html
- 附件
-
ejcees20260403ctrlenter99977746.7z- (44.75 KiB) 已下载 4 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
在线用户
正浏览此版面之用户: 没有注册用户 和 3 访客
