將改進混亂時鐘的網頁版
Re: 將改進混亂時鐘的網頁版
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/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
代码: 全选
為了完善這個邏輯,我們需要修改 `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/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
代码: 全选
這是一個很棒的優化方向。為了讓遊戲在有初始盤面時能「直接進入狀況」,我們需要調整 `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/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
「隨機按鈕」置於「遊戲開始」前。
- 附件
-
chaosclockgame202608211539.js- (43.19 KiB) 已下载 3 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
一,將前一步剛走的棋子的border屬性設為#fff,不是剛走完的棋子都設回#777。這在歷史進退中也有效。
二,去掉「當前盤面、手持、行動序列」的文字提示。
三,在「輪到甲方/乙方」的文字提示中,加上「第幾回合,」,加到「輪到某方」的前面。
只需給出所要修改的地方。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
代码: 全选
根據您的需求,以下是針對 `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/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
下一步要為分支加垃圾桶。
- 附件
-
chaosclockgame.7z- (16.73 KiB) 已下载 2 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
在线用户
正浏览此版面之用户: 没有注册用户 和 1 访客
