將改進混亂時鐘的網頁版
Re: 將改進混亂時鐘的網頁版
代码: 全选
改進程式:
1.把add、delete、update、undo、redo、dataarea、minimize等這些過於簡單的id加上前綴ejlcl。
2.當點擊update時,將進入「伸手」狀態。當處於「伸手」狀態時,點擊其中一個dataarea>img.ejtilesvgdata,則「握手」成功,程式將每隔7秒就把數據保存至img所指向的地方。
3.當「握手成功」後,再次點擊任意一個dataarea>img.ejtilesvgdata,包括「握手成功」的那個,它的作用都是將此data上載。
4.當「握手」之後再次點擊update,它將會「放手」,即取消每隔7秒保存數據。當只處於「伸手」狀態,再次點擊update也會取消此狀態。
5.當點擊delete、undo或redo時,如果處於「握手」狀態,則先「放手」,再執行相應操作。
6.當點擊delete時,它應始終保持「刪除狀態」,點擊其中一個dataarea>img.ejtilesvgdata則刪除相應數據,刪除完後不會退出該狀態,只有再次點擊delete才會退出。
7.只有在「握手、放手」時才會把當前數據存進undo stack中,每隔7秒保存不會。
只需給出所要修改的地方。所有的代碼和注釋都要使用英文。- 附件
-
ejtilelocalstorage202604181010.js- (11.6 KiB) 已下载 4 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
代码: 全选
To improve the script while maintaining its functionality and structure, here are the specific modifications. These changes update IDs with the `ejlcl` prefix and implement the new "Handshake" logic for auto-saving.
### 1. Updated HTML String and Element Selection
Change the IDs in the `htmlString` and the corresponding variable assignments to use the `ejlcl` prefix.
```javascript
// Updated HTML with ejlcl prefixes
const htmlString = `
<style>
/* ... existing styles ... */
.functionbuttons .select svg { border: 1px solid #a72; }
</style>
<div class="savetolocalstorage">
<div id="ejlclminimize">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><line x1="4" y1="12" x2="20" y2="12"></line></svg>
</div>
<h4 class="savetolocalstorageh4">Local Storage</h4>
<div class="functionbuttons">
<span id="ejlcladd"><svg ...></span>
<span id="ejlcldelete"><svg ...></span>
<span id="ejlclupdate"><svg ...></span>
<span id="ejlclundo"><svg ...></span>
<span id="ejlclredo"><svg ...></span>
</div>
<div id="ejlcldataarea" class="dataarea" style="position: relative;"></div>
</div>
`;
// Updated Element Selectors
const dataarea = document.getElementById('ejlcldataarea');
const addButton = document.getElementById('ejlcladd');
const deleteButton = document.getElementById('ejlcldelete');
const updateButton = document.getElementById('ejlclupdate');
const undoButton = document.getElementById('ejlclundo');
const redoButton = document.getElementById('ejlclredo');
const minimizeButton = document.getElementById('ejlclminimize');
```
---
### 2. Implementation of Handshake and Persistent Delete Logic
The following modifications replace the existing click listeners and logic for `dataarea`, `updateButton`, and `deleteButton`.
```javascript
// New state variable for "Handshake" status
let isHandshaking = false;
function releaseHand() {
if (autoSaveInterval) {
clearInterval(autoSaveInterval);
autoSaveInterval = null;
}
if (autoSaveTarget) {
autoSaveTarget.style.border = '';
autoSaveTarget = null;
isHandshaking = false;
saveState(); // Push to undo stack only on "release"
}
}
// Logic for dataarea click (Handshake and Upload)
dataarea.addEventListener('click', function(event) {
const target = event.target;
if (target.classList.contains('ejtilesvgdata')) {
if (deleteMode) {
deleteSvg(target);
// No longer exiting deleteMode here; remains active until toggled
} else if (updateMode && !isHandshaking) {
// Reaching out -> Handshake Success
saveState(); // Save state before starting handshake
autoSaveTarget = target;
autoSaveTarget.style.border = '2px solid #a72';
isHandshaking = true;
autoSaveInterval = setInterval(autoSaveTick, 7000);
} else {
// Normal click OR click during active Handshake: Upload data
etmain.outerHTML = atob(target.src.split(',')[1]);
etinit();
recordHistory();
}
setButtonBorder();
}
});
// Update Button: Toggle Handshake
updateButton.addEventListener('click', function() {
if (updateMode) {
if (isHandshaking) releaseHand();
updateMode = false;
} else {
updateMode = true;
deleteMode = false; // Disable delete if update is turned on
}
setButtonBorder();
});
// Delete Button: Toggle Delete Mode and release Hand if active
deleteButton.addEventListener('click', function() {
if (isHandshaking) releaseHand();
deleteMode = !deleteMode;
if (deleteMode) updateMode = false;
setButtonBorder();
});
// Undo/Redo: Release Hand before operation
undoButton.addEventListener('click', () => {
if (isHandshaking) releaseHand();
undols();
});
redoButton.addEventListener('click', () => {
if (isHandshaking) releaseHand();
redols();
});
```
---
### 3. Updated `autoSaveTick`
Ensure the auto-save function updates storage but skips the `saveState()` history stack as requested.
```javascript
function autoSaveTick() {
if (!autoSaveTarget) return;
let etmainclone = etmain.cloneNode(true);
let etmaincwait = etmainclone.querySelector(".etwait");
if (etmaincwait) etmaincwait.remove();
let data = etmainclone.outerHTML;
autoSaveTarget.src = 'data:image/svg+xml;base64,' + btoa(data);
// Update storage without pushing to undo stack
updateLocalStorage();
showSaveIndicator(autoSaveTarget);
}
```
---
### 4. Modified `deleteSvg`
Update the `deleteSvg` function to ensure it doesn't accidentally interfere with the persistent delete mode.
```javascript
function deleteSvg(img) {
// If we happen to delete the auto-save target, stop the interval
if (img === autoSaveTarget) {
clearInterval(autoSaveInterval);
autoSaveInterval = null;
autoSaveTarget = null;
isHandshaking = false;
}
img.remove();
saveState();
updateLocalStorage();
}
```https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
代码: 全选
改進程式:
1.把add、delete、update、undo、redo、dataarea、minimize等這些過於簡單的id加上前綴ejlcl。
2.當點擊update時,將進入「伸手」狀態。當處於「伸手」狀態時,點擊其中一個dataarea>img.ejtilesvgdata,則「握手」成功,程式將每隔7秒就把數據保存至img所指向的地方。
3.當「握手」之後再次點擊update,它將會「放手」,即取消每隔7秒保存數據。當只處於「伸手」狀態,再次點擊update也會取消此狀態。
4.當「握手成功」後,再次點擊任意一個dataarea>img.ejtilesvgdata,包括「握手成功」的那個,它的作用都是將此data上載。同時「放手」。
5.當點擊add、delete、undo或redo時,如果處於「握手」狀態,則先「放手」,再執行相應操作。
6.當點擊delete時,它應始終保持「刪除狀態」,點擊其中一個dataarea>img.ejtilesvgdata則刪除相應數據,刪除完後不會退出該狀態,只有再次點擊delete才會退出,或者點擊add、update、undo、redo也會先退出再執行相應功能。。
7.只有在「握手、放手」時才會把當前數據存進undo stack中,每隔7秒保存不會。
只需給出所要修改的地方。所有的代碼和注釋都要使用英文。代码: 全选
Here are the exact modifications needed to implement your requirements. Replace the corresponding sections in your original script with the code blocks below.
### 1. Update the `htmlString` (Prefixing IDs)
Replace the `<style>` block and HTML template to update `dataarea`, `minimize`, and the function buttons with the `ejlcl` prefix.
```javascript
// Replace the htmlString declaration
const htmlString = `
<style>
.savetolocalstorage {
width: 80%;
margin: 20px auto;
border: 1px solid #ccc;
padding: 10px;
position: relative;
}
.functionbuttons > span {
display: inline-block;
width: 24px;
height: 24px;
box-sizing: content-box;
margin-right: 7px;
}
.functionbuttons svg {
border: 1px solid #ccc;
cursor: pointer;
}
.functionbuttons .select svg {
border: 1px solid #a72;
}
#ejlcldataarea {
display: flex;
flex-wrap: wrap;
}
.savetolocalstorage img {
width: 120px;
height: 120px;
border: 1px solid #777;
margin: 5px;
cursor: pointer;
}
.savetolocalstorageh4 {
margin: 4px 0;
}
#ejlclminimize {
background: transparent;
border: none;
cursor: pointer;
position: absolute;
top: 10px;
right: 10px;
}
#ejlclminimize svg {
width: 24px;
height: 24px;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
border: 1px solid #777;
}
#localStorageButton {
margin-top: 7px;
}
</style>
<div class="savetolocalstorage">
<div id="ejlclminimize">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<line x1="4" y1="12" x2="20" y2="12"></line>
</svg>
</div>
<h4 class="savetolocalstorageh4">Local Storage</h4>
<div class="functionbuttons">
<span id="ejlcladd"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M 5,12 h 14 M 12,5 v 14" stroke="#333" fill="none" stroke-width="2"></path></svg></span>
<span id="ejlcldelete"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M 5,12 h 14" stroke="#333" fill="none" stroke-width="2"></path></svg></span>
<span id="ejlclupdate"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M 12,2 L 12,20 M 7,15 L 12,20 L 17,15" stroke="#333" fill="none" stroke-width="2"></path></svg></span>
<span id="ejlclundo"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M 12,20 A 8,8 0 1 0 4,12" stroke="#333" stroke-width="2" fill="none"></path><path d="M 8,11 L 4,16 L 0,11 Z" stroke="none" fill="#333"></path></svg></span>
<span id="ejlclredo"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M 12,20 A 8,8 0 1 1 20,12" stroke="#333" stroke-width="2" fill="none"></path><path d="M 16,11 L 20,16 L 24,11 Z" stroke="none" fill="#333"></path></svg></span>
</div>
<div id="ejlcldataarea" style="position: relative;"></div>
</div>
`;
```
### 2. Update Element Selectors
Replace the `querySelector` and `getElementById` assignments to match the newly prefixed IDs.
```javascript
// Replace the DOM query assignments
const dataarea = document.getElementById('ejlcldataarea');
const addButton = document.getElementById('ejlcladd');
const deleteButton = document.getElementById('ejlcldelete');
const updateButton = document.getElementById('ejlclupdate');
const undoButton = document.getElementById('ejlclundo');
const redoButton = document.getElementById('ejlclredo');
const minimizeButton = document.getElementById('ejlclminimize');
```
### 3. Add State Management Functions
Insert these helper functions right before your event listeners. They manage the "let go" functionality and clean up states before executing new commands.
```javascript
// Helper function to "let go" (cancel handshake or reaching out state)
function cancelHandshake() {
if (updateMode) {
updateMode = false;
if (autoSaveInterval) {
clearInterval(autoSaveInterval);
autoSaveInterval = null;
}
if (autoSaveTarget) {
autoSaveTarget.style.border = '';
autoSaveTarget = null;
saveState(); // Push to undo stack when letting go
}
}
}
// Helper function to let go AND cancel delete mode
function cancelHandshakeAndModes() {
cancelHandshake();
deleteMode = false;
setButtonBorder();
}
```
### 4. Update Event Listeners
Replace the event listeners for `dataarea`, `add`, `delete`, `update`, `undo`, and `redo` with the following block to fulfill requirements #2, #3, #4, #5, and #6.
```javascript
// Replace existing event listeners for dataarea and buttons
dataarea.addEventListener('click', function(event) {
const target = event.target;
if (target.classList.contains('ejtilesvgdata')) {
if (deleteMode) {
// Persistent delete mode: execute delete, DO NOT turn off deleteMode
deleteSvg(target);
} else if (updateMode) {
if (!autoSaveTarget) {
// Currently "reaching out" -> "Handshake" successful
saveState(); // Push to undo stack when turning on auto-save
autoSaveTarget = target;
autoSaveTarget.style.border = '2px solid #a72';
if (autoSaveInterval) clearInterval(autoSaveInterval);
autoSaveInterval = setInterval(autoSaveTick, 7000);
} else {
// Currently in "Handshake" -> load data and "let go"
etmain.outerHTML = atob(target.src.split(',')[1]);
etinit();
recordHistory();
cancelHandshake(); // Let go (cancels 7s interval & pushes to stack)
setButtonBorder();
}
} else {
// Normal click
etmain.outerHTML = atob(target.src.split(',')[1]);
etinit();
recordHistory();
}
}
});
addButton.addEventListener('click', function() {
cancelHandshakeAndModes(); // "Let go" first
addSvg();
});
deleteButton.addEventListener('click', function() {
if (deleteMode) {
deleteMode = false; // Exit persistent delete mode
} else {
cancelHandshakeAndModes(); // "Let go" first
deleteMode = true; // Enter persistent delete mode
}
setButtonBorder();
});
updateButton.addEventListener('click', function() {
if (updateMode) {
// "Let go" (cancel handshake or reaching out)
cancelHandshake();
} else {
// "Reach out"
cancelHandshakeAndModes(); // Cancel delete mode if active
updateMode = true;
}
setButtonBorder();
});
undoButton.addEventListener('click', function() {
cancelHandshakeAndModes(); // "Let go" first
undols();
});
redoButton.addEventListener('click', function() {
cancelHandshakeAndModes(); // "Let go" first
redols();
});
```
### 5. Update the `showSaveIndicator` Function
Because the `add` button ID was changed, the selector used to clone the SVG inside `showSaveIndicator` must also be updated.
```javascript
// Replace the querySelector inside showSaveIndicator
function showSaveIndicator(img) {
const addSvg = document.querySelector('#ejlcladd svg'); // Updated ID
const plusIndicator = addSvg.cloneNode(true);
// Position the SVG over the target image
plusIndicator.style.position = 'absolute';
plusIndicator.style.top = img.offsetTop + 'px';
plusIndicator.style.left = img.offsetLeft + 'px';
plusIndicator.style.width = '24px';
plusIndicator.style.height = '24px';
plusIndicator.style.borderRadius = '2px';
plusIndicator.style.zIndex = '10';
// Change the SVG path stroke to green
const paths = plusIndicator.querySelectorAll('path');
paths.forEach(p => p.setAttribute('stroke', '#0f0'));
dataarea.appendChild(plusIndicator);
// Remove the indicator after 500 milliseconds
setTimeout(() => {
plusIndicator.remove();
}, 500);
}
```https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
當點擊center時,如果svg中不存在「viewBox="0 0 480 480" preserveAspectRatio="xMidYMid meet"」屬性,則加上。當upload ejtile時,如果存在則要在上載的內容中去掉。
只需給出所要修改的地方,所有的代碼和注釋都要使用英文。
- 附件
-
ejtileanimate202511172231.js- (137.7 KiB) 已下载 4 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
代码: 全选
根據您的需求,以下是針對 `etaniCenterClick` 函數(處理 **center** 點擊)以及 `handleContentHTMLClick` 函數(處理 **upload ejtile**)的修改建議:
### 1. 修改 `etaniCenterClick` 函數
在設置 `etdrop` 的變換屬性之前,檢查 `etani`(SVG 對象)是否具有 `viewBox` 和 `preserveAspectRatio` 屬性,若無則添加。
```javascript
// Handle the 'Center' button click to reset the .etdrop transform
function etaniCenterClick() {
const etdrop = etani.querySelector('.etdrop');
if (!etdrop) return;
// --- Add these lines to handle viewBox and preserveAspectRatio ---
if (!etani.getAttribute('viewBox')) {
etani.setAttribute('viewBox', '0 0 480 480');
}
if (!etani.getAttribute('preserveAspectRatio')) {
etani.setAttribute('preserveAspectRatio', 'xMidYMid meet');
}
// ----------------------------------------------------------------
// toggle center or current transform
if (this.innerHTML === 'center') {
this.innerHTML = 'current';
etdrop.setAttribute('transform', 'translate(240,240) scale(1,1)');
updateEtaniResult();
} else {
this.innerHTML = 'center';
const originalSvg = document.getElementById('etmain');
const originalDrop = originalSvg.querySelector('.etdrop');
const originalTransform = originalDrop.getAttribute('transform');
if (originalTransform) {
etdrop.setAttribute('transform', originalTransform);
updateEtaniResult();
}
}
}
```
### 2. 修改 `handleContentHTMLClick` 函數中的上載邏輯
在執行 `etaniEjtileClick()` 之前,先移除 `etani` 中存在的 `viewBox` 和 `preserveAspectRatio` 屬性。
```javascript
// Handle the HTML popup window
function handleContentHTMLClick() {
if (!etani) return;
// ... (existing code for UI setup)
etaniWindow(htmlDiv, () => {
const parser = new DOMParser();
const eDom = parser.parseFromString(textarea.value, 'image/svg+xml');
etani = eDom.documentElement;
// ... (existing code for mode setting)
// upload ejtile
if (ejtileSpan.classList.contains('selected')) {
// --- Add these lines to remove attributes before uploading ---
etani.removeAttribute('viewBox');
etani.removeAttribute('preserveAspectRatio');
// -----------------------------------------------------------
etaniEjtileClick();
}
// update UI and result
updateUIandResult();
});
}
```代码: 全选
// 修改 handleContentHTMLClick 函數內部的確認邏輯
etaniWindow(htmlDiv, () => {
const parser = new DOMParser();
const eDom = parser.parseFromString(textarea.value, 'image/svg+xml');
// Only remove attributes from the content to be uploaded, not changing current etani attributes directly here
const uploadContent = eDom.documentElement;
// set repeat or freeze mode for the new etani state
etani = uploadContent; // Update current state
let eMode = document.querySelector('.etaniMode');
eMode.querySelector('span.active').classList.remove('active');
if (etani.getAttribute('rfm') === 'repeat') {
eMode.querySelector('.etaniModeRepeat').classList.add('active');
} else if (etani.getAttribute('rfm') === 'freeze') {
eMode.querySelector('.etaniModeFreeze').classList.add('active');
} else if (etani.getAttribute('rfm') === 'mixed') {
eMode.querySelector('.etaniModeMixed').classList.add('active');
}
// upload ejtile
if (ejtileSpan.classList.contains('selected')) {
// --- Remove attributes only from the temporary upload document ---
uploadContent.removeAttribute('viewBox');
uploadContent.removeAttribute('preserveAspectRatio');
// -----------------------------------------------------------------
etaniEjtileClick();
}
// update UI and result
updateUIandResult();
});https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
代码: 全选
重寫該程式:
1.保留取到svg的部分,以及區分兩種模式的部分。注意頁面上可能出現多個svg,它們之間不要發生干涉。
2.當取到svg之後,將其稱作棋盤,其內部的.etdrop>use[href=tilen]稱作棋子,其中n是0~11的整數,代表棋子1~12。
3.當button模式要產生隨機盤面時,將把每個棋子隨機放在非正位的地方(更改它的translate)。
棋子12的正位是(0,-144),從棋子1開始,以(0,0)為圓心,以144為半徑,每隔30度順時針偏移,就是它的正位坐標。
計算出的坐標值保留最多三位小數,要用「*1000,round(),/1000」。
4.當number模式時,把每個棋子按順序從棋子1的正位開始放。
比如[chaosclock]3,1,11,5,7,10,12,6,2,9,8,4[/chaosclock],則是把棋子3放在棋子1的正位,把棋子1放在棋子2的正位。
如果中間出現0,則說明有棋子是不在鐘面上的,這些棋子如果是單數,則把它移到(-208,y)的坐標上,雙數棋子則移到(208,y),y的值隨機從「-200,-120,-40,40,120,200」中取,一個位置只能放一個棋子。
比如[chaosclock]0,2,9,0,0,0,3,0,4,7,11,0[/chaosclock],則棋子1和棋子6等都不在鐘面上,最終隨機把棋子1移到了(-208,40),棋子6移到了(208,120)。
5.當完成以上步驟時,遊戲開始。拿到cclok中的#path1029和#path900,分別稱作分針和時針。
起初時針的rotate為90,表示輪到乙方,每當行動方切換,它就會增加180。
遊戲一開始是輪到乙方,當輪到任何一方時,他都可以點擊鐘面上的棋子,則這個棋子將會繞(0,0)坐標順時針旋轉1秒至它要到達的地方。
用js實現旋轉動畫,只包括對translate的改動。
棋子所要到達的地方,是它的棋子的數值乘以30度。如果所到達的地方有其它棋子,則這個棋子將會被吃掉。
被吃掉的棋子,如果是單數則移到「x=-208,y在六個數隨機取」的坐標上,雙數則是x=208。移動時先把透明度設為0.5,再用js實現移動動畫,用時1s,移完後再把透明度去掉。
比如[chaosclock]3,1,11,5,7,10,12,6,2,9,8,4[/chaosclock],這時輪到乙方,當點擊棋子3時,它將走到棋子5所在的位置,棋子5將變透明度0.5並移到左邊x為-208的某個地方。https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 將改進混亂時鐘的網頁版
對此有何解決辦法?
在博弈搜尋(尤其是蒙特卡洛樹搜尋,MCTS)中,**「循環走子」(Cycles/Lops)**確實是個令人頭疼的問題。它不僅浪費計算資源(讓樹變得虛胖),還可能導致搜尋陷入局部死循環,無法探索更有價值的路徑。
要解決這個問題,我們可以從**規則層面**、**數據結構層面**以及**搜尋策略層面**入手。
---
## 1. 狀態表示與重複檢測(基礎方案)
最直觀的方法是讓搜尋引擎「記住」走過的路。
* **使用 Zobrist Hashing(置換表):**
為棋盤上的每個位置和每種棋子分配一個隨機的 64 位整數。通過異或運算(XOR)快速計算當前局面的 Hash 值。
* **路徑記錄(Path History):**
在 MCTS 的單次模擬(Simulation)或選擇(Selection)過程中,維護一個當前路徑的 Hash 集合。
* **操作:** 如果下一步產生的局面 Hash 已經存在於當前路徑中,說明發生了循環。
---
## 2. 規則層面的約束(強制手段)
仿照經典棋類的規則,在「合法動作生成器」裡直接掐斷循環的可能性:
* **長跟/長打禁手(類似圍棋的「打劫」規則):**
禁止走回上一步或前幾步出現過的完全相同的局面。
* **和棋判定(類似象棋、國際象棋):**
* **三重複現(Threefold Repetition):** 如果同一個局面在同一局中出現三次,直接判定為和棋(在 MCTS 中給予評分 $V = 0$ 或一個中性值)。
* **步數限制(Max Steps):** 設定一個步數上限,超過後強制結束並根據當前子力估值。
---
## 3. MCTS 演算法的優化(進階方案)
如果你的遊戲本質上允許循環(例如某些特定的博弈),可以通過調整 MCTS 的行為來應對:
### 變「樹」為「圖」(Transposition Tables in MCTS)
標準 MCTS 是樹狀結構,同一個局面可能出現在不同的分支。
* **做法:** 使用**置換表(Transposition Table)**將具有相同 Hash 值的節點指向同一個記憶體位址。
* **效果:** 這樣搜尋結構就從「樹」變成了「有向圖」。當出現循環時,搜尋會自然地在圖中繞圈,而不是無窮無盡地向下生長新節點。
### 循環懲罰(Loop Penalty)
在 **Selection(選擇)** 階段,如果發現某個動作會導致回到祖先節點:
* **做法:** 人為降低該動作的 UCB 權重,或者在 **Backpropagation(回傳)** 時,給予該路徑一個極低的獎勵值(Penalty)。
* **邏輯:** 「既然走這步會回到原地,那這步棋顯然沒有進展,不如去探索別的。」
---
## 4. 針對「子循環」的啟發式剪枝
你提到 a 棋子 3 步回到原地,b 棋子 4 步回到原地。如果這些動作對全域局勢**毫無貢獻**,可以採用以下技巧:
* **空步檢測(Null Move Observation):** 如果一系列操作後,棋盤狀態與 $N$ 步前完全一致,且當前玩家沒有獲得任何額外優勢,則在展開節點時直接過濾掉這類「無效循環」動作。
* **進度計數器:** 在局面狀態中加入一個「進度因子」(例如:棋子向前推進的距離、吃子數量)。如果連續 $X$ 步進度因子沒有變化且發生了重複,則判定該分支價值為零。
---
## 總結建議
1. **首選:** 引入 **Zobrist Hash** 並實施 **「三重複現和棋」** 規則。這是代價最低、效果最好的方法。
2. **次選:** 如果你希望搜尋更聰明,實現 **置換表(Transposition Table)**,讓 MCTS 具備處理圖結構的能力。
3. **輔助:** 在 Rollout(隨機模擬)階段,設定硬性的步數上限,防止模擬階段掉進無限循環的黑洞。
您目前的棋類遊戲是否有明確的「和棋」規則?或者您是希望 AI 能夠自動學會避免這種無意義的循環?
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
在线用户
正浏览此版面之用户: 没有注册用户 和 2 访客
