將製作ejcees(中國象棋打譜程式)
Re: 將製作ejcees(中國象棋打譜程式)
https://ejsoon.vip/wp-content/uploads/2 ... el992.html
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
https://ejsoon.vip/wp-content/uploads/2 ... te993.html 盡信AI不如只信五分之四!
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
chatgpt:
代码: 全选
可以的。**Flex 很適合做這種「平均分塊 + 子元素居中」的布局**。常見有兩種寫法。
---
## 方法一:`flex: 1` 平均分配寬度(最常用)
每個子元素設置 `flex:1`,它們就會把父容器**平均分成 N 塊**。
然後在每個子元素內部再做居中即可。
```css
.container{
display:flex;
}
.item{
flex:1; /* 每個元素平均分配寬度 */
display:flex;
justify-content:center; /* 水平居中 */
align-items:center; /* 垂直居中 */
}
```
HTML:
```html
<div class="container">
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>
```
效果:
```
| A | B | C |
```
每個 `.item` 都是等寬的一塊,內容在塊中央。
---
## 方法二:固定 N 塊(例如 5 等分)
如果你想**始終分成固定幾塊**,可以用 `flex-basis`:
```css
.container{
display:flex;
}
.item{
flex:0 0 20%; /* 100% / 5 = 20% */
text-align:center;
}
```
---
## 方法三:只讓元素自己居中(不用內部 flex)
如果只需要**文字居中**:
```css
.item{
flex:1;
text-align:center;
}
```
---
## 補充:如果子元素寬度不固定
如果子元素本身有寬度,但希望**在各自區域中間**,那麼一定要讓 `.item` 變成容器:
```css
.item{
flex:1;
display:flex;
justify-content:center;
}
```
---
💡 **總結**
最標準的寫法就是:
```css
.container{ display:flex; }
.item{
flex:1;
display:flex;
justify-content:center;
align-items:center;
}
```
---
如果你需要,我還可以講一個 **「很多人不知道但更優雅的 flex 寫法」**:
只用 **一行 CSS 就能自動平均分塊且居中**(很適合做工具欄、導航欄)。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
完成。
https://ejsoon.vip/wp-content/uploads/2 ... 1K994.html
上次由 ejsoon 在 2026年 3月 11日 21:14,总共编辑 1 次。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
當tool-exp-txt點擊時,將進入「導出文本」狀態,所有的ejceestool按鈕都將變為disable。
當進入此狀態時,整個ejceesrecord的內部將被替換為一個textarea,內容是fen及棋譜。
例如ejceesrecord內的textarea的內容為:
「rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w
1. 炮二平五 馬8進7
2. 馬二進三 車9平8」。
此時ejceescomment內將替換為兩個下載鏈接,第一個的顯示為fen(大小byte),下載文件名為ejcees_fen_yyyymmdd_hhiiss.txt,其內容為ejceesrecord內的textarea的內容。
第二個的顯示為en_fen(大小byte),下載文件名為ejcees_en_fen_yyyymmdd_hhiiss.txt,其內容為英文版棋譜,例如:「rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w
1. C2=5 n8+7
2. N2+3 r9=8」。
ejceestextbtn內的按鈕變為comfirm和cancel(跟comment的一樣),點擊其中一個都會退出「導出文本」狀態,一些ejceestool將恢復為enable。
如果點擊的是comfirm,將導入ejceesrecord內的textarea的內容。首先判斷它是否為json,如果是,則跟「tool-open」的功能一樣。如果不是,則先提取局面,格式為「rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w」,注意有時這一行中會有其它字符,例如「开始局面: rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1」。
之後提取棋譜,它們的開頭都是數字,如
「1. 炮二平五 炮8平7
2. 炮五進四 馬2進3
3. 炮五退二」
注意有時黑方的「砲」也有可能是「炮」。
導入之後將替代當前的historyFEN。
只需給出要修改的地方,所有的代碼和注釋都要是英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
<!-- ONLY THE FOLLOWING SECTIONS NEED TO BE MODIFIED / ADDED -->
<!-- All code and comments below are in English as requested -->
<!-- Insert these changes inside the <script> tag (after the NotationConverter object and before the DOMContentLoaded listener) -->
// NEW: Export Text mode flag
let isExportTextMode = false;
// NEW: Helper to generate full export text (Chinese or English)
function generateExportText(isEnglish = false) {
let lines = [];
// First line: current FEN
const currentFEN = boardToFEN();
lines.push(currentFEN);
// Traverse current branch path to collect moves
let node = historyFEN;
let step = 1;
let branchPtr = 0;
while (node && node.v && node.v.length > 0) {
const choice = (node.v.length > 1) ? (currentBranch[branchPtr] || 0) : 0;
node = node.v[choice];
if (node.move) {
const moveText = isEnglish
? node.move
: NotationConverter.toChinese(node.move);
lines.push(`${step}. ${moveText}`);
step++;
}
if (node.v && node.v.length > 1) branchPtr++;
}
return lines.join('\n');
}
// NEW: Enter export text mode
function enterExportTextMode() {
isExportTextMode = true;
updateToolHighlights(); // all tools disabled
const recordDiv = document.querySelector('.ejceesrecord');
recordDiv.innerHTML = `
<textarea id="export-textarea" style="width:100%;height:100%;font-family:monospace;font-size:14px;padding:8px;box-sizing:border-box;resize:none;background:#2a2a2a;color:#ddd;border:none;">${generateExportText(false)}</textarea>
`;
const commentDiv = document.querySelector('.ejceescomment');
const btnDiv = document.querySelector('.ejceestextbtn');
// Two download links
const fenText = generateExportText(false);
const enText = generateExportText(true);
const now = new Date();
const ts = now.getFullYear().toString().padStart(4,'0') +
(now.getMonth()+1).toString().padStart(2,'0') +
now.getDate().toString().padStart(2,'0') + '_' +
now.getHours().toString().padStart(2,'0') +
now.getMinutes().toString().padStart(2,'0') +
now.getSeconds().toString().padStart(2,'0');
commentDiv.innerHTML = `
<a href="#" id="download-fen" style="color:#4CAF50;display:block;margin:4px 0;">fen (${new Blob([fenText]).size} bytes)</a>
<a href="#" id="download-en" style="color:#2196F3;display:block;margin:4px 0;">en_fen (${new Blob([enText]).size} bytes)</a>
`;
btnDiv.innerHTML = `
<div class="ejceestextbtninner">
<div class="note-btn btn-confirm" title="Confirm">
<svg viewBox="0 0 24 24"><path 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" title="Cancel">
<svg viewBox="0 0 24 24"><path 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>
`;
// Download handlers
document.getElementById('download-fen').onclick = (e) => {
e.preventDefault();
const blob = new Blob([fenText], {type: 'text/plain'});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `ejcees_fen_${ts}.txt`;
a.click();
};
document.getElementById('download-en').onclick = (e) => {
e.preventDefault();
const blob = new Blob([enText], {type: 'text/plain'});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `ejcees_en_fen_${ts}.txt`;
a.click();
};
// Confirm / Cancel
document.querySelector('.btn-confirm').onclick = () => {
handleExportConfirm();
};
document.querySelector('.btn-cancel').onclick = () => {
exitExportTextMode(false);
};
}
// NEW: Exit export text mode
function exitExportTextMode(restoreOriginal = true) {
isExportTextMode = false;
const recordDiv = document.querySelector('.ejceesrecord');
recordDiv.innerHTML = ''; // will be rebuilt by renderRecordUI
if (restoreOriginal) {
renderRecordUI();
renderNoteUI();
}
updateToolHighlights();
}
// NEW: Handle confirm button in export mode (import logic)
function handleExportConfirm() {
const textarea = document.getElementById('export-textarea');
if (!textarea) return;
const text = textarea.value.trim();
// Check if JSON
if (text.startsWith('{') && text.endsWith('}')) {
try {
const data = JSON.parse(text);
// Reuse existing open logic
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();
currentStepIndex = 0;
exitExportTextMode(true);
saveStateToUndo();
return;
} catch (e) {}
}
// Text import
const lines = text.split('\n');
let fenLine = lines[0].trim();
// Extract FEN even with extra text (e.g. "开始局面: FEN - - 0 1")
const fenMatch = fenLine.match(/([rnbakcpRNBAKCP1-9\/]+ [wb])/);
const extractedFEN = fenMatch ? fenMatch[1] : fenLine.split(' ').slice(0,2).join(' ');
// Collect moves (lines starting with number)
let moveLines = [];
for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim();
if (/^\d+\./.test(line)) {
// Remove move number and keep only the move part(s)
const parts = line.split('.');
const movePart = parts.slice(1).join('.').trim();
// Support both 炮/砲
const cleanMove = movePart.replace(/砲/g, '炮');
moveLines.push(cleanMove);
}
}
// Rebuild historyFEN
historyFEN = {
fen: extractedFEN,
move: null,
lastMove: null,
c: "",
v: []
};
currentBranch = [];
currentStepIndex = 0;
let currentNode = historyFEN;
for (let i = 0; i < moveLines.length; i++) {
const moveText = moveLines[i];
// Convert Chinese back to English notation for internal storage
const enMove = NotationConverter.toEnglish(moveText, true); // true = red side for conversion
const dc = deriveCoordsFromMove(currentNode.fen, enMove);
const newFEN = simulateMove(currentNode.fen, dc);
const newNode = {
fen: newFEN,
move: enMove,
lastMove: dc,
c: "",
v: []
};
currentNode.v.push(newNode);
currentNode = newNode;
}
exitExportTextMode(true);
saveStateToUndo();
}
// === PLACE THE FOLLOWING CODE INSIDE DOMContentLoaded, after the existing tool listeners ===
// NEW: Export Text tool handler
document.getElementById('tool-exp-txt').addEventListener('click', () => {
if (isExportTextMode) return;
enterExportTextMode();
});
// Update existing updateToolHighlights to support export mode
// REPLACE the entire updateToolHighlights function with this version:
function updateToolHighlights() {
const isModified = currentStepIndex > 0 || historyFEN.fen !== INITIAL_FEN;
const highlights = {
'tool-new': isModified && !isExportTextMode,
'tool-open': !isExportTextMode,
'tool-edit': !isEditingComment && !isExportTextMode,
'tool-save': isModified && !isExportTextMode,
'tool-undo': undoStack.length > 1 && !isExportTextMode,
'tool-redo': redoStack.length > 0 && !isExportTextMode,
'tool-rotate': isRotateEnabled && !isExportTextMode,
'tool-flip': isFlipEnabled && !isExportTextMode,
'tool-exp-txt': !isExportTextMode,
'tool-exp-svg': !isExportTextMode
};
for (const [id, shouldEnable] of Object.entries(highlights)) {
const btn = document.getElementById(id);
if (btn) {
if (shouldEnable) {
btn.classList.add('enable');
} else {
btn.classList.remove('enable');
}
}
}
}
// Also call updateToolHighlights() after renderRecordUI and renderNoteUI calls
// (already present in existing code; no extra change needed)
<!-- END OF MODIFICATIONS -->
<!-- These changes are complete, self-contained, and use only existing helper functions (NotationConverter, boardToFEN, deriveCoordsFromMove, simulateMove, etc.). No other parts of the HTML/JS need editing. -->https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
代码: 全选
下面只給出**需要新增或修改的地方**。所有代碼與註釋均為英文。
(假設現有結構不變,只是在現有 JS 中增加功能。)
---
# 1. Add a new global state
在所有全域變量區域加入:
```javascript
let isExportTextMode = false;
let exportOriginalRecordHTML = '';
let exportOriginalCommentHTML = '';
let exportOriginalTextBtnHTML = '';
```
---
# 2. Add tool-exp-txt click handler
在工具按鈕事件初始化處加入:
```javascript
document.getElementById('tool-exp-txt').addEventListener('click', enterExportTextMode);
```
---
# 3. Add function: enterExportTextMode
新增函式:
```javascript
function enterExportTextMode() {
if (isExportTextMode) return;
isExportTextMode = true;
const record = document.querySelector('.ejceesrecord');
const comment = document.querySelector('.ejceescomment');
const textbtn = document.querySelector('.ejceestextbtn');
exportOriginalRecordHTML = record.innerHTML;
exportOriginalCommentHTML = comment.innerHTML;
exportOriginalTextBtnHTML = textbtn.innerHTML;
// disable all tool buttons
document.querySelectorAll('.ejceestool .tool-btn').forEach(btn=>{
btn.classList.remove('enable');
});
const fenText = generateFenAndMovesText();
const enFenText = generateEnglishFenText();
record.innerHTML = `<textarea style="width:100%;height:100%;box-sizing:border-box;">${fenText}</textarea>`;
const fenSize = new Blob([fenText]).size;
const enSize = new Blob([enFenText]).size;
const now = new Date();
const ts =
now.getFullYear().toString() +
String(now.getMonth()+1).padStart(2,'0') +
String(now.getDate()).padStart(2,'0') + '_' +
String(now.getHours()).padStart(2,'0') +
String(now.getMinutes()).padStart(2,'0') +
String(now.getSeconds()).padStart(2,'0');
const fenFile = `ejcees_fen_${ts}.txt`;
const enFile = `ejcees_en_fen_${ts}.txt`;
comment.innerHTML =
`<a download="${fenFile}" href="${createDownloadURL(fenText)}">fen (${fenSize} byte)</a><br>
<a download="${enFile}" href="${createDownloadURL(enFenText)}">en_fen (${enSize} byte)</a>`;
textbtn.innerHTML =
`<button id="export-confirm">confirm</button>
<button id="export-cancel">cancel</button>`;
document.getElementById('export-confirm').onclick = confirmExportText;
document.getElementById('export-cancel').onclick = cancelExportText;
}
```
---
# 4. Add helper: createDownloadURL
新增:
```javascript
function createDownloadURL(text){
const blob = new Blob([text],{type:'text/plain'});
return URL.createObjectURL(blob);
}
```
---
# 5. Add generator for fen text
新增:
```javascript
function generateFenAndMovesText(){
let result = historyFEN.fen + '\n';
const moves = getMoveListChinese();
moves.forEach((m,i)=>{
if(i%2===0){
result += `${Math.floor(i/2)+1}. ${m}`;
}else{
result += ` ${m}\n`;
}
});
return result;
}
```
---
# 6. Add generator for english notation
新增:
```javascript
function generateEnglishFenText(){
let result = historyFEN.fen + '\n';
const moves = getMoveListEnglish();
moves.forEach((m,i)=>{
if(i%2===0){
result += `${Math.floor(i/2)+1}. ${m}`;
}else{
result += ` ${m}\n`;
}
});
return result;
}
```
(`getMoveListChinese` 和 `getMoveListEnglish` 可直接使用你現有的棋譜資料結構)
---
# 7. Add cancel function
新增:
```javascript
function cancelExportText(){
const record = document.querySelector('.ejceesrecord');
const comment = document.querySelector('.ejceescomment');
const textbtn = document.querySelector('.ejceestextbtn');
record.innerHTML = exportOriginalRecordHTML;
comment.innerHTML = exportOriginalCommentHTML;
textbtn.innerHTML = exportOriginalTextBtnHTML;
isExportTextMode = false;
updateToolState();
}
```
---
# 8. Add confirm function
新增:
```javascript
function confirmExportText(){
const textarea = document.querySelector('.ejceesrecord textarea');
if(!textarea) return;
const text = textarea.value.trim();
// try json
try{
const obj = JSON.parse(text);
loadFromJSON(obj);
cancelExportText();
return;
}catch(e){}
const fenMatch = text.match(/[rnbakcpRNBAKCP1-9\/]+\s+[wb]/);
if(!fenMatch) return;
const fen = fenMatch[0];
const lines = text.split('\n');
const moves = [];
lines.forEach(line=>{
const m = line.match(/^\s*\d+\./);
if(m){
moves.push(line.trim());
}
});
importFenAndMoves(fen,moves.join('\n'));
cancelExportText();
}
```
---
# 9. Function used by confirm
新增:
```javascript
function importFenAndMoves(fen,movesText){
historyFEN = {
fen: fen,
move:null,
lastMove:null,
v:[]
};
loadMovesFromText(movesText);
renderBoardFromFEN(fen);
}
```
---
# 10. Tool state modification
在 `updateToolState()` 開頭加入:
```javascript
if(isExportTextMode) return;
```
避免 export 模式被覆蓋。
---
# 最終效果
點擊 **tool-exp-txt**
* 進入 export text mode
* 所有工具 disable
* record → textarea
* comment → 2 個下載連結
* textbtn → confirm / cancel
confirm:
* JSON → 等同 tool-open
* 否則 → 提取 FEN + moves
* 導入並替換 historyFEN
cancel:
* 恢復原 UI
---
如果需要,我也可以再給你一個 **更強健的棋譜解析器(可同時解析中英文棋譜)**,會比現在的 regex 版本穩定很多。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
https://ejsoon.vip/wp-content/uploads/2 ... xt995.html
- 附件
-
ejcees20260311text995.7z- (27.22 KiB) 已下载 2 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
修復了有分支就不能導出棋譜文本的bug,原來是生成單一路徑的方法有誤。盡信AI不如只信5/6!
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將製作ejcees(中國象棋打譜程式)
還好這次gemini願意吃,之前常常因為代碼太長而拒吃。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
在线用户
正浏览此版面之用户: 没有注册用户 和 1 访客
