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

分享和讨论Javascript相关的话题
回复
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5293
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

標準pgn格式:

代码: 全选

[Game "Chinese Chess"]
[Title "尹昇 先胜  赵攀伟"]
[Event "2026年第二届胡林翼杯象棋公开赛"]
[Red "尹昇"]
[RedTeam ""]
[Black "赵攀伟"]
[BlackName ""]
[Opening ""]
[Date ""]
[Site ""]
[Round ""]
[Format "ICCS"]
1. C3-C4 B7-C7
2. H2-E2 C9-E7
3. C0-A2 I9-I8
4. H0-G2 I8-B8
5. B2-D2 B8-B1
6. I0-I1 H7-H1
7. E2-E1 B1-B2
8. I1-H1 B2-D2
9. H1-H9 D2-G2
10. E1-E6 D9-E8
11. H9-G9 G2-G3
12. B0-D1 B9-D8
13. E6-E4 A9-B9
14. G9-H9 B9-B5
15. D1-F2 G3-E3
16. D0-E1 B5-E5
17. A0-B0 D8-E6
18. B0-B9 E8-D9
19. B9-B7 E5-E4
20. F2-E4 E3-E4
21. H9-H2 E7-G9
22. H2-E2 C7-G7
23. E1-D0 G7-G0
24. E0-E1 E4-E2
25. E1-E2 F9-E8
26. B7-B3 G0-D0
27. B3-E3 E6-G5
28. E3-D3 D0-A0
29. D3-D6 G5-F3
30. D6-G6 G9-E7
31. G6-G3 F3-G5
32. G3-E3 E7-G9
33. E2-E1 A6-A5
34. E1-E0 A5-A4
35. A3-A4 A0-A4
36. E3-A3 A4-B4
37. A3-A6 B4-B7
38. A6-C6 B7-E7
39. C6-G6 G9-I7
40. E0-D0 I6-I5
41. C4-C5 E8-F7
42. F0-E1 I5-I4
43. G6-H6 I4-I3
44. H6-H9 E9-E8
45. H9-D9 I3-H3
46. C5-D5 H3-G3
47. D9-D6 E8-E9
48. D6-D7 I7-G9
49. D7-C7 F7-E8
50. D5-D6 E8-D9
51. C7-C4 E7-G7
52. C4-C7 G7-E7
53. C7-C5 G5-F3
54. D6-D7 E7-E4
55. C5-F5 D9-E8
56. D7-D8 E4-D4
57. D0-E0 E8-D7
58. F5-D5 G3-G2
59. E0-D0 D7-E8
60. D0-D1 G2-G1
61. D5-F5 E8-D7
62. F5-F3 D4-D8
63. E1-D2 G9-E7
64. F3-E3 G1-F1
65. E3-E7  *
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5293
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

講解中國象棋的標準pgn格式

代码: 全选

在中國象棋(Xiangqi)的數位化記錄中,雖然我們常用 **PGN (Portable Game Notation)** 這個詞,但嚴格來說,中國象棋使用的是基於國際象棋 PGN 格式修改而來的 **XQFE** 或 **WXF** 標記標準。

以下是中國象棋標準棋譜格式的詳細拆解:

---

## 1. 檔案結構
一個標準的中國象棋 PGN 檔案通常由兩個部分組成:**標籤部份 (Tag Section)** 和 **著法部份 (Movetext Section)**。

### 標籤部份 (Tag Section)
這部分用中括號包裹,提供對局的背景資訊。常見的標籤包括:
* `[Game "Chinese Chess"]`:遊戲類型。
* `[Event "賽事名稱"]`:比賽名稱。
* `[Round "輪次"]`:第幾輪比賽。
* `[Red "棋手名"]`:紅方姓名。
* `[Black "棋手名"]`:黑方姓名。
* `[Result "1-0"]`:結果(1-0 紅勝,0-1 黑勝,1/2-1/2 和棋)。
* `[FEN "起始陣式"]`:如果不是從開局開始,需註明 FEN 字串。

### 著法部份 (Movetext Section)
這部分記錄具體的走子過程,序號後接紅方走子,再接黑方走子。

---

## 2. 座標標記法 (Algebraic Notation)
與國際象棋類似,中國象棋的標準電子格式通常採用**縱線座標法**。

### 棋盤座標系統
* **水平軸(橫向):** 從左到右用字母 **a 到 i** 表示(共 9 條線)。
* **垂直軸(縱向):** 從下到上用數字 **0 到 9** 表示(共 10 條線)。

> **注意:** 紅方底線為 `0`,黑方底線為 `9`。
> 例如:紅方左側底角車的位置是 `a0`,黑方右側底角炮的位置是 `h7`。

### 著法格式:`[起點座標][終點座標]`
不同於中文傳統棋譜(如「炮二平五」),電子格式直接記錄格點的移動:
* **h2e2**:代表位於 h2 的子移動到了 e2(相當於炮二平五)。
* **h0g2**:代表位於 h0 的子移動到了 g2(相當於馬二進三)。

---

## 3. 中文傳統格式 vs. 電子標準格式
雖然 PGN 內部存儲的是座標,但許多軟體會將其轉換為中文術語以供閱讀。

| 類型 | 電子座標 (ICCS) | 中文傳統格式 |
| :--- | :--- | :--- |
| 進步 | `h0g2` | 馬二進三 |
| 平移 | `h2e2` | 炮二平五 |
| 退步 | `e2e1` | 炮五退一 |

---

## 4. FEN (Forsyth-Edwards Notation) 記法
在 PGN 中,如果對局不是從初始狀態開始,會使用 `[FEN "..."]` 標籤。中國象棋的 FEN 與國際象棋略有不同:

* **棋子縮寫:**
    * **紅方 (大寫):** `R` (車), `N` (馬), `B` (相), `A` (仕), `K` (帥), `C` (炮), `P` (兵)。
    * **黑方 (小寫):** `r` (車), `n` (馬), `b` (象), `a` (士), `k` (將), `c` (砲), `p` (卒)。
* **結構:** 從第 9 行(黑方底線)描述到第 0 行,斜線 `/` 分隔每一行,數字代表連續的空格。

**初始局面的 FEN 範例:**
`rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1`

---

## 5. 範例展示
一個完整的中國象棋 PGN 檔案看起來像這樣:

```text
[Game "Chinese Chess"]
[Event "2024 友誼賽"]
[Red "玩家A"]
[Black "玩家B"]
[Result "1-0"]

1. h2e2 h7e7 2. h0g2 h9g7 3. i0h0 i9h9 4. g2f4 b9a7
```

> **重點提示:**
> 如果你在編寫程式或分析棋譜,請確保使用 **UTF-8** 編碼,以防止標籤中的中文姓名或賽事名稱出現亂碼。

您目前是在處理現有的棋譜檔案,還是正打算為自己的對局寫一個記錄器?
會否可以有[Date ""]
[Site ""]
[Round ""]
這樣的屬性?是否可以認為可以擅自添加屬性?

代码: 全选

是的,你的理解完全正確。在 PGN 格式中,這些屬性(Tags)不僅是**標準組成部分**,而且 PGN 協議本身就設計為**可擴展的**。

以下是針對你疑問的詳細解答:

---

### 1. 標準的「七大標籤」 (STR)
在 PGN 規範中,有七個標籤被稱為 **STR (Seven Tag Roster)**,它們被認為是必不可少的。如果你在使用專業的象棋數據庫軟體(如象棋橋、XQWizard),它們通常會自動生成這七項:

1.  `[Event ""]` (賽事)
2.  `[Site ""]` (地點)
3.  `[Date ""]` (日期,格式通常為 YYYY.MM.DD)
4.  `[Round ""]` (輪次)
5.  `[Red ""]` (紅方棋手)
6.  `[Black ""]` (黑方棋手)
7.  `[Result ""]` (結果)

所以,你提到的 `[Date ""]`、`[Site ""]`、`[Round ""]` 絕對不是擅自添加,而是**標準要求**的內容。

---

### 2. 是否可以「擅自」添加屬性?
**可以。** PGN 格式非常靈活,它允許定義「用戶自定義標籤」。只要符合格式規範(中括號包裹、標籤名在先、值在雙引號內),任何解析器都會將其視為合法的元數據。

常見的自定義或擴展標籤包括:
* `[Annotator ""]`:記錄棋譜註解者。
* `[RedTitle ""]` / `[BlackTitle ""]`:棋手頭銜(如:特級大師)。
* `[RedTeam ""]` / `[BlackTeam ""]`:所屬隊伍。
* `[ECO ""]`:開局分類代碼。
* `[Opening ""]`:開局名稱(例如:當頭炮對屏風馬)。
* `[Time ""]`:具體比賽時間。
* `[Termination ""]`:比賽結束的具體原因(如:超時、認輸)。

---

### 3. 添加屬性時的注意事項
雖然你可以隨意添加屬性,但為了確保檔案在不同軟體間的**兼容性**,建議遵循以下原則:

* **唯一性:** 標籤名稱不要與標準標籤衝突。
* **格式:** 標籤名只能包含字母、數字和下劃線(不建議使用中文做標籤名,但標籤的值可以使用中文)。
* **順序:** 雖然規範建議將 STR(七大標籤)放在最前面,但大多數軟體對自定義標籤的位置並不敏感。

---

### 4. 示例
這是一個包含了標準標籤與自定義標籤的完整頭部:

```text
[Event "2026年五一盃"]
[Site "廣州"]
[Date "2026.05.01"]
[Round "1"]
[Red "王天一"]
[Black "鄭惟桐"]
[Result "1/2-1/2"]
[RedTeam "杭州"]
[BlackTeam "四川"]
[Annotator "人工智慧助手"]
[Opening "中炮過河車對屏風馬左馬盤河"]
```

> **小貼士:**
> 如果你的 `Date` 標籤中某個部分未知,規範要求用 `??` 代替。
> 例如:`[Date "2026.??.??" ]`(只知道年份)。

您是打算用 PGN 格式來整理私人的棋譜庫,還是為了開發某種程序需要校對格式呢?
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5293
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

pgn格式能否添加評論?

代码: 全选

絕對可以。PGN 格式設計的初衷之一就是為了分享與教學,因此**評論(Comments)**是其核心功能。

在中國象棋的 PGN 檔案中,添加評論主要有兩種標準方式:

---

### 1. 大括號評論 `{ ... }`
這是最常見的方式,用於輸入**文字描述**。大括號內的內容會被解析器視為註解,不會影響棋局的連貫性。

* **位置:** 可以放在著法之前或之後。
* **範例:**
    ```text
    1. h2e2 { 紅方起手當頭炮,展現進攻姿態 } h7e7 { 黑方同樣以炮封鎖 }
    2. h0g2 { 馬二進三 } 
    ```

### 2. 分號評論 `;`
如果你想在某一行的末尾添加備註,可以使用分號。分號之後到該行結束的所有內容都會被視為評論。

* **範例:**
    ```text
    1. h2e2 e7e7 ; 這是典型的順手炮開局
    2. h0g2
    ```

---

### 3. 進階評論符號 (NAG)
除了文字,PGN 還支援 **NAG (Numeric Annotation Glyphs)**。這是一些以 `$` 開頭的數字編碼,用來表達對某一步棋的評價,許多棋譜軟體會將它們轉化為特定圖示(如 `!` 或 `?`)。

常見的符號對應如下:
* `$1` 或 `!`:好棋 (Good move)
* `$2` 或 `?`:劣著 (Poor move)
* `$3` 或 `!!`:妙手 (Very good move)
* `$4` 或 `??`:大漏著 (Very poor move)
* `$11`:黑方稍優
* `$13`:紅方明顯優勢

**範例:**
`1. h2e2 $1` (代表這一步是好棋)

---

### 4. 變著 (Recursive Annotation Variations, RAV)
如果你想記錄「如果當時不這樣走,而是那樣走會如何」,可以使用**小括號 `( ... )`** 來記錄**變著**。

* **範例:**
    ```text
    1. h2e2 h7e7 
    2. h0g2 ( 2. i0h0 { 如果改走車一平二... } ) 2... h9g7
    ```

---

### 💡 實踐小建議
* **編碼格式:** 由於評論通常包含中文,請務必將檔案儲存為 **UTF-8** 編碼,否則在不同的棋譜軟體(如《象棋橋》或手機 App)中開啟時,評論極易變成亂碼。
* **軟體相容性:** 大多數現代象棋軟體都能完美支援 `{}` 和 `()`。

您是打算在整理棋譜時加入自己的心得分析,還是正在編寫一個自動生成戰報的工具呢?
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5293
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

1-0結果標志是否可加可不加?前面標籤不是定義了嗎?還有哪些除了著法之外還能加的東西?

代码: 全选

這是一個非常深入的問題。這牽涉到 PGN 的**冗餘設計(Redundancy)**原則,旨在確保數據在傳輸或局部損壞時仍能保持完整。

---

### 1. 著法末尾的 `1-0` 是否可加可不加?

**答案是:強烈建議加上,且在標準規範中是「必須」的。**

雖然你在標籤區(Tag Section)已經定義了 `[Result "1-0"]`,但在著法區(Movetext Section)末尾再次出現結果標誌有以下幾個原因:

* **檔案完整性校驗:** 解析器會比對標籤區的結果與著法區末尾的標誌。如果不一致,軟體會提示檔案可能損毀或被竄改。
* **結束訊號:** 著法區的結果標誌(`1-0`, `0-1`, `1/2-1/2` 或 `*`)是解析器的「終止符」。它告訴程式:「這局棋到此結束,後面的內容(如有)可能是下一局棋或無關文字。」
* **如果不加會怎樣?**
    * 大多數現代軟體(如象棋橋、XQWizard)仍能讀取,它們會以標籤區的結果為準。
    * 但在處理「連續包含多局棋」的長 PGN 檔案時,缺少結束標誌可能導致解析器無法正確切換到下一局。

> **提示:** 如果棋局尚未結束(如打譜到一半),請使用星號 `*` 作為結束標誌。

---

### 2. 除了著法和評論,還能加什麼?

除了你已經知道的標籤、著法和 `{}` 評論,PGN 著法區還支援以下內容:

#### A. 變著 (Recursive Annotation Variations, RAV)
這在教學棋譜中最常見。你可以用**小括號 `()`** 嵌套另一個平行宇宙的走法。
* **範例:** `1. h2e2 h7e7 2. h0g2 (2. i0h0 {若改走邊車...} i9h9 3. h0h4) h9g7`
* 變著裡面甚至可以再嵌套變著(括號套括號)。

#### B. 數值註解符號 (NAG, $n$)
如果你不想寫長篇大論,只想給某步棋打分,可以使用 `$n`。
* `$1` = 好棋 (`!`)
* `$2` = 壞棋 (`?`)
* `$18` = 局面混亂
* 這些符號在軟體中通常會直接顯示為圖示。

#### C. 回合序號的修正 (Ellipsis)
當你在黑方著法後加入長篇評論,想接著寫下一回合時,通常會用 `...`。
* **範例:** `1. h2e2 h7e7 {黑方應以順手炮} 2. h0g2 ...`(這主要是為了增強人類可讀性,機器解析時通常會忽略多餘的點)。

#### D. 行動符號 (Suffix Glyphs)
雖然在電子格式中較少見(因為有 NAG),但你仍可以直接在著法後緊跟 `!`、`?`、`!!`、`??`、`!?` 或 `?!`。
* **範例:** `2. h0g2! h9g7?`

---

### 3. PGN 的「隱藏」特性:多局併存
PGN 檔案本質上是一個**文本串聯**。這意味著你可以在一個 `.pgn` 檔案中,直接把多段完整的棋譜內容(包含各自的標籤區和著法區)接在一起。

```text
[Event "第一局"]
...
1. h2e2 h7e7 1-0

[Event "第二局"]
...
1. i0h0 i9h9 1/2-1/2
```

這種結構讓它成為了極佳的「數據庫」載體。

---

### 總結一下你的疑惑:
標籤區的 `Result` 是給數據庫**索引和過濾**用的,而著法區末尾的 `1-0` 是給解析引擎**判定終止點**用的。

你現在是在嘗試手寫一個 PGN 檔案來測試某個軟體的讀取能力嗎?還是有遇到某些軟體讀不出你的評論呢?
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5293
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5293
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

把導出文本中的Chinese Notation改成DP Format。

它一開始時是沒有評論的,當點擊一次時,它將顯示評論,這個維持不變。

現在要改進的是,當點擊第二次時,將顯示帶有分支的版本,按鈕顏色顯示為淡紫色。

帶分支的導入邏輯已經寫有了,參照它來做。需要說明的是,當超出20個,則使用括號加數字:(21)。

例如:

各種評論
1. 炮二平五 馬2進3
2. 馬二進三 卒7進1
3. 車一平二 ① 炮8平7
4. 兵七進一 車1進1
5. 馬八進九 ② 象3進5
6. 炮八平七 炮2進4 ③
7. 兵五進一 馬8進9
這裡是評論
8. 車九平八 車1平2
9. 兵五進一 卒5進1
10. 馬三進五 車9平8
11. 車二進九 馬9退8
12. 炮五進三 士4進5


3. 相三進一


5. 仕四進五


6. ………… 馬8進9

則「車一平二」的分支是「相三進一」,「馬八進九」的分支是「仕四進五」,「炮2進4」的分支是「馬8進9」。

只需要給出所要修改的地方。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5293
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

gemini pro只吃難吃的:

代码: 全选

這裡為您提供需要修改的 4 個地方,以實作「DP Format」的三段切換(無評論、有評論、帶分支的淡紫色狀態)與文本生成。

### 1. 新增按鈕淡紫色狀態的 CSS
在 `<style>` 區塊中,找到 `.exp-fmt-btn.active[data-odd="1"] { background: orange; }`,在它下方新增 `[data-odd="2"]` 的樣式:

```css
    .exp-fmt-btn.active[data-odd="1"] {
        background: orange;
    }
    .exp-fmt-btn.active[data-odd="2"] {
        background: #ce93d8; /* 淡紫色 */
    }
```

### 2. 新增 `generateDPFormat` 函數
在 `generateExportText` 函數定義的後面,插入處理分支邏輯的新函數 `generateDPFormat`:

```javascript
        function generateExportText(isEnglish) {
            // ... (原本的代碼保持不變) ...
            return text.trim();
        }

        // 新增:帶有分支與標記的 DP Format 生成函數
        function generateDPFormat(isEnglish = false) {
            let text = historyFEN.fen + '\n';
            if (historyFEN.c && historyFEN.c.trim() !== '') {
                text += historyFEN.c.trim() + '\n';
            }

            let branchQueue = [];
            let branchCounter = 1;

            // 超過20個分支使用括號數字
            function getMarker(num) {
                if (num <= 20) return String.fromCharCode(0x245F + num);
                return `(${num})`;
            }

            function processSequence(startNode, absDepth, moveNum, isRed, pathArr) {
                let seqText = "";
                let curr = startNode;
                let lineBuffer = "";
                
                while (curr.v && curr.v.length > 0) {
                    let choiceIdx = 0;
                    if (pathArr && pathArr[absDepth] !== undefined) {
                        choiceIdx = pathArr[absDepth];
                    }
                    if (choiceIdx >= curr.v.length) choiceIdx = 0;
                    
                    let nextNode = curr.v[choiceIdx];
                    let moveStr = isEnglish ? nextNode.move : NotationConverter.toChinese(nextNode.move);
                    
                    let markers = "";
                    for (let i = 0; i < curr.v.length; i++) {
                        if (i !== choiceIdx) {
                            let marker = getMarker(branchCounter++);
                            markers += " " + marker;
                            branchQueue.push({
                                marker: marker,
                                parentNode: curr,
                                choiceIdx: i,
                                moveNum: moveNum,
                                isRedTurn: isRed,
                                absDepth: absDepth
                            });
                        }
                    }

                    if (isRed) {
                        if (lineBuffer === "") {
                            lineBuffer = `${moveNum}. `;
                        }
                        lineBuffer += `${moveStr}${markers}`;
                        
                        if (nextNode.c && nextNode.c.trim() !== '') {
                            seqText += lineBuffer + '\n' + nextNode.c.trim() + '\n';
                            lineBuffer = "";
                            isRed = false;
                        } else {
                            isRed = false;
                        }
                    } else {
                        if (lineBuffer === "") {
                            lineBuffer = `${moveNum}. ………… `;
                        } else {
                            lineBuffer += " ";
                        }
                        lineBuffer += `${moveStr}${markers}`;
                        
                        seqText += lineBuffer + '\n';
                        if (nextNode.c && nextNode.c.trim() !== '') {
                            seqText += nextNode.c.trim() + '\n';
                        }
                        lineBuffer = "";
                        isRed = true;
                        moveNum++;
                    }
                    
                    curr = nextNode;
                    absDepth++;
                }
                
                if (lineBuffer !== "") {
                    seqText += lineBuffer + '\n';
                }
                
                return seqText.trim();
            }

            let mainIsRed = historyFEN.fen.split(' ')[1] === 'w';
            let mainText = processSequence(historyFEN, 0, 1, mainIsRed, currentBranch);
            text += mainText;

            while (branchQueue.length > 0) {
                let b = branchQueue.shift();
                text += `\n\n${b.marker}\n`;
                
                // 為分支生成虛擬的選擇路徑(只在當前層級選擇特定分支,後續默認走主線0)
                let branchPath = [];
                for (let i = 0; i < b.absDepth; i++) {
                    branchPath.push(0); 
                }
                branchPath.push(b.choiceIdx);

                let branchText = processSequence(b.parentNode, b.absDepth, b.moveNum, b.isRedTurn, branchPath);
                text += branchText;
            }

            return text;
        }
```

### 3. 修改複製行為 (`performCopy`)
找到 `performCopy` 函數,將判定 `cn` 的複製邏輯增加對 `odd === 2` 的支持:

```javascript
        function performCopy() {
            let textToCopy = "";
            const fmt = lastExportFormat || 'cn';
            const odd = formatOddStates[fmt] || 0;

            if (fmt === 'cn') {
                if (odd === 2) {
                    textToCopy = generateDPFormat(false);
                } else if (odd === 1) {
                    textToCopy = generateTextWithComment(false);
                } else {
                    textToCopy = generateExportText(false);
                }
            } else if (fmt === 'en') {
                textToCopy = odd === 1 ? generateTextWithComment(true) : generateExportText(true);
            // ... 後續代碼不變 ...
```

### 4. 修改 UI 界面生成與點擊切換邏輯 (`renderExportTextUI`)
找到 `renderExportTextUI` 函數,並做以下幾處局部修改:

首先,在初始化文本常量的地方加上 `cnTextWithBranches`:
```javascript
            const cnText = generateExportText(false);
            const cnTextWidthComment = generateTextWithComment(false);
            const cnTextWithBranches = generateDPFormat(false); // 新增這行
            const enText = generateExportText(true);
```

接著,把按鈕的文字改成 DP Format:
```javascript
            commentDiv.innerHTML = `<div class="exp-fmt-outer">
                <div class="exp-fmt-btn ${lastExportFormat === 'cn' ? 'active' : ''}" data-fmt="cn" data-odd="${formatOddStates['cn']}">DP Format</div>
                <div class="exp-fmt-btn ${lastExportFormat === 'en' ? 'active' : ''}" data-fmt="en" data-odd="${formatOddStates['en']}">English Notation</div>
                <div class="exp-fmt-btn ${lastExportFormat === 'engine' ? 'active' : ''}" data-fmt="engine" data-odd="${formatOddStates['engine']}">Engine Format</div>
                <div class="exp-fmt-btn ${lastExportFormat === 'url' ? 'active' : ''}" data-fmt="url" data-odd="${formatOddStates['url']}">URL Parameter</div>
               </div>`;
```

然後,在按鈕 `click` 事件中的 `wasActive` 狀態切換判定裡,讓 `cn` 支持 3 段切換 (0、1、2):
```javascript
                    if (wasActive) {
                        if (fmt === 'cn') {
                            formatOddStates[fmt] = (formatOddStates[fmt] + 1) % 3; // 新增這行以支援第三階段
                        } else {
                            formatOddStates[fmt] = formatOddStates[fmt] === 1 ? 0 : 1;
                        }
                    }
                    btn.setAttribute('data-odd', formatOddStates[fmt]);
```

最後,在 `updateTextarea` 中對應綁定數值 2 的文本內容:
```javascript
            const updateTextarea = (fmt) => {
                if (fmt === 'cn') {
                    if (formatOddStates[fmt] === 2) {
                        textarea.value = cnTextWithBranches;
                    } else if (formatOddStates[fmt] === 1) {
                        textarea.value = cnTextWidthComment;
                    } else {
                        textarea.value = cnText;
                    }
                } else if (fmt === 'en') {
```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5293
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

將所有注釋都翻譯成英文:

代码: 全选

        // 新增:帶有分支與標記的 DP Format 生成函數
        function generateDPFormat(isEnglish = false) {
            let text = historyFEN.fen + '\n';
            if (historyFEN.c && historyFEN.c.trim() !== '') {
                text += historyFEN.c.trim() + '\n';
            }

            let branchQueue = [];
            let branchCounter = 1;

            // 超過20個分支使用括號數字
            function getMarker(num) {
                if (num <= 20) return String.fromCharCode(0x245F + num);
                return `(${num})`;
            }

            function processSequence(startNode, absDepth, moveNum, isRed, pathArr) {
                let seqText = "";
                let curr = startNode;
                let lineBuffer = "";
                
                while (curr.v && curr.v.length > 0) {
                    let choiceIdx = 0;
                    if (pathArr && pathArr[absDepth] !== undefined) {
                        choiceIdx = pathArr[absDepth];
                    }
                    if (choiceIdx >= curr.v.length) choiceIdx = 0;
                    
                    let nextNode = curr.v[choiceIdx];
                    let moveStr = isEnglish ? nextNode.move : NotationConverter.toChinese(nextNode.move);
                    
                    let markers = "";
                    for (let i = 0; i < curr.v.length; i++) {
                        if (i !== choiceIdx) {
                            let marker = getMarker(branchCounter++);
                            markers += " " + marker;
                            branchQueue.push({
                                marker: marker,
                                parentNode: curr,
                                choiceIdx: i,
                                moveNum: moveNum,
                                isRedTurn: isRed,
                                absDepth: absDepth
                            });
                        }
                    }

                    if (isRed) {
                        if (lineBuffer === "") {
                            lineBuffer = `${moveNum}. `;
                        }
                        lineBuffer += `${moveStr}${markers}`;
                        
                        if (nextNode.c && nextNode.c.trim() !== '') {
                            seqText += lineBuffer + '\n' + nextNode.c.trim() + '\n';
                            lineBuffer = "";
                            isRed = false;
                        } else {
                            isRed = false;
                        }
                    } else {
                        if (lineBuffer === "") {
                            lineBuffer = `${moveNum}. ………… `;
                        } else {
                            lineBuffer += " ";
                        }
                        lineBuffer += `${moveStr}${markers}`;
                        
                        seqText += lineBuffer + '\n';
                        if (nextNode.c && nextNode.c.trim() !== '') {
                            seqText += nextNode.c.trim() + '\n';
                        }
                        lineBuffer = "";
                        isRed = true;
                        moveNum++;
                    }
                    
                    curr = nextNode;
                    absDepth++;
                }
                
                if (lineBuffer !== "") {
                    seqText += lineBuffer + '\n';
                }
                
                return seqText.trim();
            }

            let mainIsRed = historyFEN.fen.split(' ')[1] === 'w';
            let mainText = processSequence(historyFEN, 0, 1, mainIsRed, currentBranch);
            text += mainText;

            while (branchQueue.length > 0) {
                let b = branchQueue.shift();
                text += `\n\n${b.marker}\n`;
                
                // 為分支生成虛擬的選擇路徑(只在當前層級選擇特定分支,後續默認走主線0)
                let branchPath = [];
                for (let i = 0; i < b.absDepth; i++) {
                    branchPath.push(0); 
                }
                branchPath.push(b.choiceIdx);

                let branchText = processSequence(b.parentNode, b.absDepth, b.moveNum, b.isRedTurn, branchPath);
                text += branchText;
            }

            return text;
        }
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复

在线用户

正浏览此版面之用户: ejsoon 和 0 访客