將製作ejcees(中國象棋打譜程式)

分享和讨论Javascript相关的话题
回复
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

css的flex能否實現每個子元素平均横向分成幾塊,然後子元素在這個塊的中間?


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/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

去掉按鈕的hover效果。

帥五進一而非進五。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

上次由 ejsoon 在 2026年 3月 11日 21:14,总共编辑 1 次。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

改進:

當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/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

grok:

代码: 全选

<!-- 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/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

chatgpt:

代码: 全选

下面只給出**需要新增或修改的地方**。所有代碼與註釋均為英文。
(假設現有結構不變,只是在現有 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/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

附件
ejcees20260311text995.7z
(27.22 KiB) 已下载 3 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4667
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 201 次
联系:

Re: 將製作ejcees(中國象棋打譜程式)

帖子 ejsoon »

本次功能實現,只有gemini做到了(雖然也不完美),grok差很多,chatgpt更是不行。claude直接吃不掉。

還好這次gemini願意吃,之前常常因為代碼太長而拒吃。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复

在线用户

正浏览此版面之用户: 没有注册用户 和 2 访客