將改進混亂時鐘的網頁版

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

Re: 將改進混亂時鐘的網頁版

帖子 ejsoon »

改進:
1. 如果初始盤面包含0,說明那裡沒有棋子,並且已經有棋子被拿在手上。完善這個邏輯。

例如ckvalue="0,1,11,3,7,10,12,6,2,9,8,4",則推出甲方手上有5,當前局面在棋子1的正位上是空位。默認現在輪到乙方行動。

同時,如果初始盤面即ckvalue的值的末尾是「;0」,則表示接下來輪到甲方行動,如果沒有,或者有「;1」,則是輪到乙方行動。

例如ckvalue="1,2,0,3,0,6,9,0,5,0,8,7;0",則推出甲方手上有11,乙方手上有4、10、12,當前局面輪到甲方。

如果後面還有一個「;n」或「;a,b,c,d,e」,則最後一個數字表示上一步剛走完的棋子,這一步不能走。

比如ckvalue="1,2,0,3,0,6,9,0,5,0,8,7;0;9"表示上一步乙方剛走完9。再如ckvalue="1,2,0,3,0,6,9,0,5,0,8,7;0;5,9,7"表示上一步乙方剛走完7。

2. 如果ckvalue有值,則不再出現「遊戲開始」和「重新開始」按鈕,而是直接開始,並出現「向前、向後」的導航欄。

3. 如果ckvalue沒有值,仍跟原來一樣。需要改進的是:

3.1 「隨機局面」應該放在「開始遊戲」和「重新開始」的前面。

3.2 當點擊「隨機局面」時,分針的旋轉動畫應參照其它地方,只改動svg的transform中的rotate屬性值,不能改動style。

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

Re: 將改進混亂時鐘的網頁版

帖子 ejsoon »

gemini fast:

代码: 全选

為了完善這個邏輯,我們需要修改 `parseInitialState` 方法,使其能夠解析帶有分號(`;`)的複雜 `ckvalue` 字串,並在初始化時正確分配手牌、設定目前玩家以及限制上一步移動的棋子。

以下是針對 `ChaosClock` 類別的邏輯更新與程式碼實現:

---

## 邏輯更新重點
1.  **分段解析**:使用 `;` 將字串切分為「盤面資料」、「目前玩家」與「移動紀錄」。
2.  **手牌推導**:遍歷 1~12 號棋子,若該棋子不在盤面上(值為 0 的位置),則根據奇偶數分配給甲方(單數)或乙方(雙數)。
3.  **回合設定**:預設為乙方(1),若標記為 `;0` 則切換為甲方。
4.  **最後移動限制**:讀取最後一段的最後一個數字,將其設定為 `lastMovedPiece`,防止該棋子在本回合被立即移動。

---

## 程式碼實現

請將 `parseInitialState` 替換為以下程式碼,並新增 `parseComplexCkvalue` 輔助方法:

```javascript
parseInitialState() {
    const ckvalue = this.objectEl.getAttribute('ckvalue');
    this.isRandomMode = false;

    if (ckvalue && ckvalue.trim() !== "") {
        this.parseComplexCkvalue(ckvalue);
    } else {
        this.isRandomMode = true; 
        this.btnRandom.style.display = 'inline-block';
        const derangement = this.getRandomDerangement();
        this.setBoard(derangement);
    }
}

parseComplexCkvalue(ckvalue) {
    const segments = ckvalue.split(';');
    
    // 1. 解析盤面 (第一段)
    const boardVals = segments[0].split(',').map(Number);
    this.board = boardVals.map(v => (v === 0 ? null : v));
    this.initialBoardStr = segments[0];

    // 2. 推導手牌 (1~12 中不在盤面上的棋子)
    this.hands = { 0: [], 1: [] };
    const pieceOnBoard = new Set(this.board.filter(v => v !== null));
    for (let i = 1; i <= 12; i++) {
        if (!pieceOnBoard.has(i)) {
            const owner = i % 2 === 0 ? 1 : 0;
            this.hands[owner].push(i);
        }
    }

    // 3. 解析目前回合 (第二段)
    // 預設為 1 (乙方),若明確標註為 0 則為甲方
    if (segments.length > 1 && segments[1].trim() === "0") {
        this.turn = 0;
        this.initialPlayer = 0;
    } else {
        this.turn = 1;
        this.initialPlayer = 1;
    }

    // 4. 解析上一步移動的棋子 (第三段)
    if (segments.length > 2 && segments[2].trim() !== "") {
        const moves = segments[2].split(',').map(Number);
        this.lastMovedPiece = moves[moves.length - 1]; // 取得最後一個數字
    } else {
        this.lastMovedPiece = null;
    }

    // 執行渲染與事件綁定
    this.renderInitialState();
}

renderInitialState() {
    // 渲染盤面上的棋子
    for (let i = 0; i < 12; i++) {
        if (this.board[i] !== null) {
            this.renderPiecePosition(this.board[i], i);
        }
    }

    // 渲染手上的棋子
    [0, 1].forEach(owner => {
        this.hands[owner].forEach(pieceValue => {
            const el = this.getPieceEl(pieceValue);
            if (el) {
                const x = owner === 1 ? 208 : -208;
                const y = this.getAvailableHandY(owner);
                this.updateTransform(el, 'translate', `${x},${y}`);
                this.updateOpacity(el, 1);
            }
        });
    });

    this.bindEvents();
    this.updateStateDisplay();
}
```

---

## 範例解析測試

根據您提供的規則,程式碼將按如下方式運作:

### 案例 A:`ckvalue="0,1,11,3,7,10,12,6,2,9,8,4"`
* **盤面**:位置 1(正位 1)為空,其餘位置填充對應棋子。
* **手牌**:1~12 中只有 **5** 沒出現在字串裡。5 是單數 $\rightarrow$ **甲方手持:[5]**。
* **回合**:無第二段 $\rightarrow$ **輪到乙方**。
* **限制**:無第三段 $\rightarrow$ 無移動限制。

### 案例 B:`ckvalue="1,2,0,3,0,6,9,0,5,0,8,7;0"`
* **盤面**:對應位置顯示棋子,0 的位置為空。
* **手牌**:缺失 4, 10, 11, 12。
    * 單數:11 $\rightarrow$ **甲方手持:[11]**。
    * 雙數:4, 10, 12 $\rightarrow$ **乙方手持:[4, 10, 12]**。
* **回合**:第二段為 `0` $\rightarrow$ **輪到甲方**。

### 案例 C:`ckvalue="...;0;5,9,7"`
* **限制**:第三段最後一個數字是 **7**。
* **結果**:`this.lastMovedPiece = 7`。在 `handlePieceClick` 中,若玩家點擊 7 號棋子,會觸發 `if (pieceValue === this.lastMovedPiece) return;` 邏輯,禁止移動。

---

## 額外建議
在 `startGame` 方法中,目前的程式碼會強制將 `this.turn = 1`。為了支援從特定存檔點開始,建議修改如下:

```javascript
async startGame() {
    this.state = 'playing';
    // 移除 this.turn = 1; 讓其保留 parseComplexCkvalue 解析出的結果
    this.btnStart.style.display = 'none';
    this.btnRandom.style.display = 'none';
    
    const playerName = this.turn === 1 ? '乙方' : '甲方';
    this.setNormalMessage(`遊戲開始,輪到${playerName}`);
    
    // 時針指向對應玩家 (乙方 90, 甲方 270)
    this.hourRotation = this.turn === 1 ? 90 : 270;
    ...
}
```

這能確保當 `ckvalue` 指定甲方開始時,UI 指針與狀態文字能正確對應。

單一相關問題:您是否需要為手牌區增加視覺標籤(例如在左右兩側標示「甲方手牌」與「乙方手牌」),以方便玩家區分?
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5246
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

Re: 將改進混亂時鐘的網頁版

帖子 ejsoon »

gemini fast:

代码: 全选

這是一個很棒的優化方向。為了讓遊戲在有初始盤面時能「直接進入狀況」,我們需要調整 `init` 和 `setupGame` 的邏輯,跳過手動點擊開始的步驟,並自動切換 UI 顯示狀態。

以下是針對你需求修改的建議方案:

### 核心邏輯調整

1.  **自動觸發 `startGame`**:在解析完 `ckvalue` 後,如果判斷為「讀檔模式」(即有值且不是隨機模式),則自動調用開始函數。
2.  **隱藏多餘按鈕**:在 `createUI` 或 `startGame` 中,根據是否有 `ckvalue` 決定 `btnStart` 和 `btnRestart` 的 `display` 屬性。
3.  **導航欄自動顯示**:確保 `historyContainer` 在讀檔後立即設為 `block`。

---

### 程式碼實現

請更新 `ChaosClock` 類別中的以下方法:

#### 1. 修改 `setupGame` 與 `parseInitialState`
增加對「自動開始」的判斷。

```javascript
setupGame() {
    this.doc = this.objectEl.contentDocument;
    if (!this.doc) return;

    this.createUI();
    const ckvalue = this.objectEl.getAttribute('ckvalue');
    
    if (ckvalue && ckvalue.trim() !== "") {
        // 情況 A:有初始值,直接解析並自動開始
        this.isRandomMode = false;
        this.parseComplexCkvalue(ckvalue);
        this.autoStartGame(); 
    } else {
        // 情況 B:無初始值,顯示隨機按鈕與開始按鈕
        this.isRandomMode = true;
        this.btnRandom.style.display = 'inline-block';
        this.btnStart.style.display = 'inline-block';
        const derangement = this.getRandomDerangement();
        this.setBoard(derangement);
    }
}
```

#### 2. 新增 `autoStartGame` 與優化 `startGame`
將開始邏輯抽離,避免重複顯示 UI 元素。

```javascript
async autoStartGame() {
    // 隱藏開始與重新開始按鈕
    this.btnStart.style.display = 'none';
    this.btnRestart.style.display = 'none';
    this.btnRandom.style.display = 'none';

    this.state = 'playing';
    
    // 根據解析出的 this.turn 設定初始訊息
    const playerName = this.turn === 1 ? '乙方' : '甲方';
    this.setNormalMessage(`遊戲開始,輪到${playerName}`);

    // 設定時針初始角度 (乙方 90, 甲方 270)
    this.hourRotation = this.turn === 1 ? 90 : 270;
    
    this.isAnimating = true;
    await this.updatePointersRealtime();
    this.isAnimating = false;

    // 顯示資料與導航欄
    this.dataDisplay.style.display = 'block';
    this.historyContainer.style.display = 'block';
    
    // 初始化歷史紀錄
    this.history = [];
    this.saveState();
}

// 修改原有的 startGame 以便兼容手動點擊
async startGame() {
    this.btnRestart.style.display = 'none'; // 開始時確保重新開始隱藏
    await this.autoStartGame();
}
```

#### 3. 調整 `endGame` 邏輯
確保在遊戲結束時,只有在 `isRandomMode`(隨機模式)下才顯示重新開始,或者根據你的需求完全禁用。

```javascript
endGame(resultCode) {
    this.state = 'ended';
    // ... 原有的時針旋轉與紀錄邏輯 ...

    // 如果是讀檔進來的(非隨機模式),不顯示重新開始,僅保留導航欄供複盤
    if (this.isRandomMode) {
        this.btnRestart.style.display = 'inline-block';
        this.btnRandom.style.display = 'inline-block';
    } else {
        // 固定盤面模式下,結束後不顯示重新開始,玩家只能透過導航欄回溯
        this.btnRestart.style.display = 'none';
    }
}
```

---

### 邏輯細節說明

* **導航欄(History Navigation)**:
    由於 `autoStartGame` 會觸發 `saveState()`,導航欄會立即記錄下當前的初始狀態。玩家一進入頁面,就可以看到 `1/1` 的歷史標記,隨著走子,導航按鈕(向前、向後)會自動變為可用狀態。
* **按鈕佔位**:
    透過 `display: none`,原本頁面上方的「開始遊戲」按鈕會消失,視覺上會直接呈現鐘面與下方的歷史導航控制列(`historyContainer`)。
* **指針同步**:
    在 `autoStartGame` 中,我加入了 `updatePointersRealtime`。這會讓鐘面指針在頁面載入後,優雅地從預設位置轉動到目前行動方的方向(例如轉到 9 點鐘方向代表甲方行動),提供更好的儀式感。

這樣修改後,你的 `ChaosClock` 就像是一個「嵌入式謎題」,使用者打開網頁就能直接開始思考棋局,不需要多餘的點擊操作。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5246
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

Re: 將改進混亂時鐘的網頁版

帖子 ejsoon »

chaosclockgame202608211539

「隨機按鈕」置於「遊戲開始」前。
附件
chaosclockgame202608211539.js
(43.19 KiB) 已下载 3 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5246
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

Re: 將改進混亂時鐘的網頁版

帖子 ejsoon »

改進:
一,將前一步剛走的棋子的border屬性設為#fff,不是剛走完的棋子都設回#777。這在歷史進退中也有效。

二,去掉「當前盤面、手持、行動序列」的文字提示。

三,在「輪到甲方/乙方」的文字提示中,加上「第幾回合,」,加到「輪到某方」的前面。

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

Re: 將改進混亂時鐘的網頁版

帖子 ejsoon »

gemini fast:

代码: 全选

根據您的需求,以下是針對 `ChaosClock` 類別的修改建議。

### 一、 棋子邊框顏色反饋 (含歷史紀錄支援)

在 `renderInitialState`、`loadState` 與棋子移動相關方法中,我們需要一個統一的函數來更新棋子邊框。

**1. 新增 `updatePieceBorders` 方法:**
```javascript
updatePieceBorders() {
    for (let i = 1; i <= 12; i++) {
        const el = this.getPieceEl(i);
        if (el) {
            // 尋找 use 標籤內部的 rect 或 path (視 SVG 結構而定,通常 tile 樣式由 stroke 控制)
            // 若 SVG 內部定義了 .tile 類別,可直接修改 style
            if (i === this.lastMovedPiece) {
                el.style.outline = "2px solid #fff"; // 強調前一步
                el.style.outlineOffset = "-2px";
            } else {
                el.style.outline = "1px solid #777"; // 恢復預設
                el.style.outlineOffset = "-1px";
            }
        }
    }
}
```

**2. 在以下位置調用此方法:**
* `renderInitialState()` 的結尾。
* `loadState(index)` 的結尾。
* `executeMove` 與 `executePlace` 動畫完成後的結尾。

---

### 二、 移除文字提示資料

**修改 `updateStateDisplay` 方法:**
直接清空或註釋掉內容,使其不再更新 `dataDisplay` 的文字。

```javascript
updateStateDisplay() {
    if (!this.dataDisplay) return;
    // 已移除盤面、手持、行動序列的文字顯示
    this.dataDisplay.textContent = ""; 
}
```

---

### 三、 狀態提示增加回合數

**1. 修改 `switchTurn` 方法:**
在切換回合時遞增回合計數,並修改訊息格式。

```javascript
switchTurn(updatePointer = true) {
    // ... 原有邏輯 ...
    this.turn = this.turn === 1 ? 0 : 1;
    
    // 如果回到甲方(0)算新的一回合起點,或者每次切換都算一步,此處採計「總行動序次」
    const totalTurns = this.moveSequence.length + 1;
    const playerName = this.turn === 1 ? '乙方' : '甲方';
    
    this.setNormalMessage(`第 ${totalTurns} 回合,輪到${playerName}`);
    // ... 原有邏輯 ...
}
```

**2. 修改 `autoStartGame` 與 `restartGame`:**
初始化時的文字也需同步。

```javascript
// autoStartGame 中
const playerName = this.turn === 1 ? '乙方' : '甲方';
this.setNormalMessage(`第 1 回合,輪到${playerName}`);
```

---

### 修改摘要表

| 修改點 | 檔案位置 (概略行號) | 具體動作 |
| :--- | :--- | :--- |
| **邊框顏色** | `renderInitialState`, `loadState`, `execute...` | 呼叫 `updatePieceBorders()`,依 `lastMovedPiece` 切換 `#fff` 或 `#777`。 |
| **隱藏資料** | `updateStateDisplay` | 刪除 `textContent` 賦值邏輯。 |
| **回合提示** | `setNormalMessage` 相關調用處 | 字串改為 `` `第 ${n} 回合,輪到...` ``。 |

**注意:** 關於邊框屬性,由於 `use` 標籤本身可能不支援 CSS `border`,建議使用 `outline` 或透過 `this.doc.querySelectorAll` 深入修改該 `symbol` 內部元件的 `stroke` 屬性。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5246
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 176 次
被圈友点赞: 204 次
联系:

Re: 將改進混亂時鐘的網頁版

帖子 ejsoon »

chaosclockgame202608211710

下一步要為分支加垃圾桶。
附件
chaosclockgame.7z
(16.73 KiB) 已下载 2 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复

在线用户

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