「益智遊戲」和「抽象弈棋」
回复
头像
ejsoon
一枝独秀一枝独秀
帖子: 6605
注册时间: 2022年 11月 18日 17:36
联系:

Re: 一個新的遊戲創意

帖子 ejsoon »

修復和改進
一,提示圓點改色

把「對方最後下的一個或兩個棋子」上標的白色小圓點,改成「選中對方棋子的藍色」,而在本方回合中對方的棋子的圓點將改成紫色。

在下載svg中,如果是「動畫」則兩個小圓點的顏色也改成這種藍色,如果是「數字」則去掉小圓點。

二,更改操作說明

在「操作說明」中,把「該棋子中心會出現藍點」改為「紫色的圓點」。英文也要作相應更改。同時演示中的藍點也應該變為紫色的圓點。

在「確認取消」中,在最後加上一句「如果打勾確認為未激活狀態,點擊可以切換默認AI」。英文也要作相應更改。

在「界面介紹」的svg動畫演示中,如果是手機端,則調整viewBox使其左右空白區域去掉。

為何操作說明窗口的文字不可選中?也不能用鍵盤中的上下鍵或翻頁鍵來滾動。修復這個問題。

二,為AI增加選項

當前AI只有思考時間的差異,現在增加一個「搜尋次數」的選項。

在AI設置窗口中,在最下方增加一個開關按鈕,其右方文字為「搜尋次數」,默認關閉。當其開啟時,其上方的限時設置將被一個「規定搜尋次數」的輸入框替代。

默認的搜尋次數為,簡單120,困難240,專家480,自訂960。

同時把AI默認思考時間改掉:簡單7s,困難12s,專家24s,自訂36s。

將開關按鈕做成現代美觀的樣式,其標題為「思考模式」。

在MCTS搜尋中,當思考模式改為「規定搜尋次數」時,將不限時間,直至搜尋到規定次數。而minimax搜尋將不受時間或搜尋次數限制。

回答要求:不要影響當前正確的遊戲流程,明確給出所要修改的地方和所要替換的代碼。

代码: 全选

修復和改進
一,提示圓點改色

把「對方最後下的一個或兩個棋子」上標的白色小圓點,改成「選中對方棋子的藍色」,而在本方回合中對方的棋子的圓點將改成紫色。

在下載svg中,如果是「動畫」則兩個小圓點的顏色也改成這種藍色,如果是「數字」則去掉小圓點。

二,更改操作說明

在「操作說明」中,把「該棋子中心會出現藍點」改為「紫色的圓點」。英文也要作相應更改。同時演示中的藍點也應該變為紫色的圓點。

在「確認取消」中,在最後加上一句「如果打勾確認為未激活狀態,點擊可以切換默認AI」。英文也要作相應更改。

在「界面介紹」的svg動畫演示中,如果是手機端,則調整viewBox使其左右空白區域去掉。

為何操作說明窗口的文字不可選中?也不能用鍵盤中的上下鍵或翻頁鍵來滾動。修復這個問題。

二,為AI增加選項

當前AI只有思考時間的差異,現在增加一個「搜尋次數」的選項。

在AI設置窗口中,在最下方增加一個開關按鈕,其右方文字為「搜尋次數」,默認關閉。當其開啟時,其上方的限時設置將被一個「規定搜尋次數」的輸入框替代。

默認的搜尋次數為,簡單120,困難240,專家480,自訂960。

同時把AI默認思考時間改掉:簡單7s,困難12s,專家24s,自訂36s。

將開關按鈕做成現代美觀的樣式,其標題為「思考模式」。

在MCTS搜尋中,當思考模式改為「規定搜尋次數」時,將不限時間,直至搜尋到規定次數。而minimax搜尋將不受時間或搜尋次數限制。

回答要求:不要影響當前正確的遊戲流程,明確給出所要修改的地方和所要替換的代碼。
附件
trigolden_game2665.html.7z
(66.7 KiB) 已下载 4 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀一枝独秀
帖子: 6605
注册时间: 2022年 11月 18日 17:36
联系:

Re: 一個新的遊戲創意

帖子 ejsoon »

代码: 全选

以下修改只涉及**提示圓點、操作說明、SVG 匯出、AI 設定及搜尋停止條件**,不改動 `validateTurnOnState()`、`actionCheck()`、計分、仲裁、回合切換和棋譜邏輯,因此不會破壞目前正確的遊戲流程。

---

# 一、修復操作說明不能選字、不能用鍵盤滾動

原因有兩個:

1. `body` 設定了 `user-select: none`,操作說明繼承後無法選中文字。
2. `#operation-modal-scroll` 不是可聚焦元素,所以方向鍵、PageUp、PageDown 不會作用在它上面。

## 1. 在 CSS 中加入

建議加在原有這段後面:

```css
#rules-modal-scroll,
#operation-modal-scroll {
  overflow-y: auto;
  padding: 25px;
  box-sizing: border-box;
  flex: 1;
  min-height: 0;
  overscroll-behavior: contain;
}
```

加入:

```css
/* 操作說明允許選取文字,並可接收鍵盤滾動 */
#operation-modal-scroll,
#operation-modal-scroll * {
  -webkit-user-select: text;
  user-select: text;
}

#operation-modal-scroll {
  touch-action: pan-y;
  outline: none;
  scrollbar-gutter: stable;
}

#operation-modal-scroll:focus-visible {
  box-shadow: inset 0 0 0 2px rgba(114, 137, 218, 0.72);
}
```

## 2. 修改操作說明滾動容器

搜尋:

```html
<div id="operation-modal-scroll">
```

替換為:

```html
<div id="operation-modal-scroll" tabindex="0">
```

## 3. 替換 `openOperationGuide()`

搜尋原函數:

```js
function openOperationGuide() {
  const modal = document.getElementById('operation-modal');
  const scroll = document.getElementById('operation-modal-scroll');

  if (!modal) return;

  // 避免兩個開始頁面說明視窗同時顯示。
  const rulesModal = document.getElementById('rules-modal');
  if (rulesModal) {
    rulesModal.style.display = 'none';
  }

  modal.style.display = 'flex';

  if (scroll) {
    scroll.scrollTop = 0;
  }

  randomizeOperationGuideDemos();
}
```

替換為:

```js
function openOperationGuide() {
  const modal = document.getElementById('operation-modal');
  const scroll = document.getElementById('operation-modal-scroll');

  if (!modal) return;

  // 避免兩個開始頁面說明視窗同時顯示。
  const rulesModal = document.getElementById('rules-modal');

  if (rulesModal) {
    rulesModal.style.display = 'none';
  }

  modal.style.display = 'flex';

  updateOperationInterfaceDemoViewBox();
  randomizeOperationGuideDemos();

  if (scroll) {
    scroll.scrollTop = 0;

    // 讓方向鍵、PageUp、PageDown、Home、End 作用於說明內容。
    requestAnimationFrame(() => {
      try {
        scroll.focus({ preventScroll: true });
      } catch (error) {
        scroll.focus();
      }
    });
  }
}
```

---

# 二、操作說明文字和紫色圓點

## 1. 修改「選擇目標和落子」文字

搜尋:

```html
<span class="zh-text">
  當有棋子被選中時,點擊棋盤上的對方棋子,該棋子中心會出現藍點。己方所選棋子的所有可放置位置會以半透明棋子顯示;點擊其中一個半透明棋子,即可把棋子暫時落到棋盤上。
</span>
<span class="en-text">
  After selecting a piece, click an opponent’s piece on the board. A blue dot appears at its center, and
  all legal placements for your selected piece appear as translucent pieces. Click one of them to place
  the piece temporarily on the board.
</span>
```

替換為:

```html
<span class="zh-text">
  當有棋子被選中時,點擊棋盤上的對方棋子,該棋子中心會出現紫色的圓點。己方所選棋子的所有可放置位置會以半透明棋子顯示;點擊其中一個半透明棋子,即可把棋子暫時落到棋盤上。
</span>
<span class="en-text">
  After selecting a piece, click an opponent’s piece on the board. A purple dot appears at its center, and
  all legal placements for your selected piece appear as translucent pieces. Click one of them to place
  the piece temporarily on the board.
</span>
```

## 2. 修改「確認取消」文字

搜尋:

```html
<span class="zh-text">
  當本回合行動完成時,點擊「打勾確認」結束回合。如果想取消本回合的所有選擇和暫時落子,點擊「打叉取消」。
</span>
<span class="en-text">
  When your move is complete, click the check button to confirm and end the turn. Click the cross button
  to cancel all selections and temporary placements made during the current turn.
</span>
```

替換為:

```html
<span class="zh-text">
  當本回合行動完成時,點擊「打勾確認」結束回合。如果想取消本回合的所有選擇和暫時落子,點擊「打叉取消」。如果打勾確認為未激活狀態,點擊可以切換默認 AI。
</span>
<span class="en-text">
  When your move is complete, click the check button to confirm and end the turn. Click the cross button
  to cancel all selections and temporary placements made during the current turn. If the confirm
  checkmark is inactive, click it to cycle the default AI.
</span>
```

---

# 三、手機端裁掉「界面介紹」SVG 左右空白

## 1. 給界面介紹 SVG 加上 ID

搜尋第一個「界面介紹」SVG:

```html
<svg
  class="operation-demo operation-svg"
  viewBox="0 0 760 310"
  role="img"
  aria-label="Animated desktop and mobile interface layout"
>
```

替換為:

```html
<svg
  id="op-interface-demo"
  class="operation-demo operation-svg"
  viewBox="0 0 760 310"
  role="img"
  aria-label="Animated desktop and mobile interface layout"
>
```

## 2. 在 `openOperationGuide()` 前加入

```js
function updateOperationInterfaceDemoViewBox() {
  const svg = document.getElementById('op-interface-demo');

  if (!svg) return;

  /*
   * 手機版裁掉左右空白,但仍保留桌面端和手機端兩層動畫的完整內容。
   * 桌面內容約位於 x=207.5~552.5,所以使用 x=190~570。
   */
  const isPhoneLayout = window.matchMedia('(max-width: 768px)').matches;

  svg.setAttribute('viewBox', isPhoneLayout ? '190 0 380 310' : '0 0 760 310');
}

window.addEventListener('resize', updateOperationInterfaceDemoViewBox);
```

---

# 四、修改棋盤上的藍色、紫色提示圓點

## 1. 增加統一顏色常數

搜尋:

```js
const MCTS_C = 1.414;
```

在其後加入:

```js
// 上一回合棋子提示:沿用原本選中對方棋子的亮藍色。
const LAST_MOVE_DOT_COLOR = '#00bfff';

// 本回合選中的對方棋子提示:紫色。
const TARGET_DOT_COLOR = '#a855f7';
```

## 2. 將選中的對方棋子改為紫色

在 `renderBoard()` 中搜尋:

```js
if (targetOpponentPieceId) {
  let tp = pieces.find(x => x.id === targetOpponentPieceId);
  if (tp) {
    const pt = getPieceTransform(tp.svgId, tp.vertices);
    const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
    circle.setAttribute('cx', trimNum(pt.tx));
    circle.setAttribute('cy', trimNum(pt.ty));
    circle.setAttribute('r', '3');
    circle.setAttribute('fill', '#00bfff');
    circle.setAttribute('stroke', 'none');
    etanidrop.appendChild(circle);
  }
}
```

替換為:

```js
if (targetOpponentPieceId) {
  const tp = pieces.find(piece => piece.id === targetOpponentPieceId);

  if (tp) {
    const pt = getPieceTransform(tp.svgId, tp.vertices);
    const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');

    circle.setAttribute('cx', trimNum(pt.tx));
    circle.setAttribute('cy', trimNum(pt.ty));
    circle.setAttribute('r', '3');
    circle.setAttribute('fill', TARGET_DOT_COLOR);
    circle.setAttribute('stroke', 'none');

    // 供數字 SVG 模式移除所有操作提示圓點。
    circle.classList.add('board-marker-dot', 'target-selection-dot');

    etanidrop.appendChild(circle);
  }
}
```

## 3. 給暫時落子的圓點增加標記 class

在 `tempPieces.forEach()` 內搜尋:

```js
circle.setAttribute('stroke-width', '1');
```

在其後加入:

```js
circle.classList.add('board-marker-dot', 'temporary-piece-dot');
```

## 4. 將對方上一回合棋子圓點由白色改為藍色

搜尋:

```js
} else if (!selectedTile && !targetOpponentPieceId) {
  lastTurnPieces.forEach(tp => {
    const pt = getPieceTransform(tp.svgId, tp.vertices);
    const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
    circle.setAttribute('cx', trimNum(pt.tx));
    circle.setAttribute('cy', trimNum(pt.ty));
    circle.setAttribute('r', '3');
    circle.setAttribute('fill', '#ffffff');
    circle.setAttribute('stroke', 'none');
    etanidrop.appendChild(circle);
  });
}
```

替換為:

```js
} else if (!targetOpponentPieceId) {
  /*
   * 沒有選中目標時,繼續在對方上一回合的一個或兩個棋子上顯示藍點。
   * 選中目標後,這些藍點會隱藏,只保留被選中棋子上的紫點。
   */
  lastTurnPieces.forEach(tp => {
    const pt = getPieceTransform(tp.svgId, tp.vertices);
    const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');

    circle.setAttribute('cx', trimNum(pt.tx));
    circle.setAttribute('cy', trimNum(pt.ty));
    circle.setAttribute('r', '3');
    circle.setAttribute('fill', LAST_MOVE_DOT_COLOR);
    circle.setAttribute('stroke', 'none');
    circle.classList.add('board-marker-dot', 'last-move-dot');

    etanidrop.appendChild(circle);
  });
}
```

這裡特意去掉了 `!selectedTile` 條件,因此選中本方棋子、但尚未選中對方目標時,上一回合的藍色提示仍會保留。

---

# 五、操作說明動畫中的目標點改為紫色

在 `renderOperationPlacementDemo()` 中搜尋:

```js
if (state.stage === 'ghosts' || state.stage === 'placed') {
  boardPiecesGroup.appendChild(
    createOperationSvgElement('circle', {
      cx: targetCenter.x,
      cy: targetCenter.y,
      r: 4,
      fill: '#00bfff',
      stroke: 'none'
    })
  );
}
```

替換為:

```js
if (state.stage === 'ghosts' || state.stage === 'placed') {
  boardPiecesGroup.appendChild(
    createOperationSvgElement('circle', {
      cx: targetCenter.x,
      cy: targetCenter.y,
      r: 4,
      fill: TARGET_DOT_COLOR,
      stroke: 'none'
    })
  );
}
```

---

# 六、SVG 下載中的圓點

## 1. 數字模式移除所有提示圓點

搜尋:

```js
function addDownloadPieceNumbers(svg) {
  prependDownloadFontStyle(svg);

  const drop = svg.querySelector('.etdrop');
```

替換開頭為:

```js
function addDownloadPieceNumbers(svg) {
  prependDownloadFontStyle(svg);

  /*
   * 數字模式使用數字標示落子順序,因此移除:
   * 1. 上一回合藍點;
   * 2. 被選中目標的紫點;
   * 3. 暫時落子的白底紅框圓點。
   */
  svg.querySelectorAll('.board-marker-dot').forEach(dot => {
    dot.remove();
  });

  const drop = svg.querySelector('.etdrop');
```

其餘函數內容保持不變。

## 2. 動畫模式的兩個提示點改為藍色

將函數名稱:

```js
function appendDownloadWhiteDotCircles(drop, turns, cycleDurationSeconds) {
```

改為:

```js
function appendDownloadLastMoveDots(drop, turns, cycleDurationSeconds) {
```

在同一函數中搜尋:

```js
const circle = createDownloadSvgElement('circle', {
  id: `btwhite${circleIndex + 1}`,
  cx: '0',
  cy: '0',
  r: '3',
  fill: '#ffffff',
  stroke: 'none',
```

替換為:

```js
const circle = createDownloadSvgElement('circle', {
  id: `btlastdot${circleIndex + 1}`,
  cx: '0',
  cy: '0',
  r: '3',
  fill: LAST_MOVE_DOT_COLOR,
  stroke: 'none',
```

## 3. 修復空棋盤動畫可能重複生成四個圓點

在 `addDownloadAnimation()` 中搜尋:

```js
const downloadAnimationCycleDuration = turns.length * 2 + 12;

appendDownloadWhiteDotCircles(drop, turns, downloadAnimationCycleDuration);

/*
 * 沒有棋子時仍顯示一個可循環的空局面預覽。
 */
if (turns.length === 0) {
```

替換為:

```js
const downloadAnimationCycleDuration = turns.length === 0 ? 8 : turns.length * 2 + 12;

appendDownloadLastMoveDots(drop, turns, downloadAnimationCycleDuration);

/*
 * 沒有棋子時仍顯示一個可循環的空局面預覽。
 */
if (turns.length === 0) {
```

然後在這個 `if (turns.length === 0)` 的末尾搜尋並刪除:

```js
appendDownloadWhiteDotCircles(drop, turns, 8);
```

最後確認整個文件中已經沒有:

```js
appendDownloadWhiteDotCircles
```

全部應改為:

```js
appendDownloadLastMoveDots
```

---

# 七、AI 設定窗口增加「思考模式」開關

## 1. 增加現代開關 CSS

在 AI 設定相關 CSS 的末尾,例如 `.ai-param-row input:disabled` 後面加入:

```css
/* AI 思考模式開關 */
.ai-thinking-mode-row {
  grid-column: 1 / -1;
  min-height: 58px;
  padding: 12px 2px 0;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border-top: 1px solid #444;
}

.ai-thinking-mode-title {
  color: #fff;
  font-size: 0.95em;
  font-weight: bold;
  letter-spacing: 0.02em;
}

.ai-settings-grid .ai-mode-switch {
  position: relative;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  color: #ddd;
  font-size: 0.9em;
  cursor: pointer;
  user-select: none;
}

.ai-settings-grid .ai-mode-switch input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

.ai-mode-slider {
  position: relative;
  width: 50px;
  height: 28px;
  flex: none;
  box-sizing: border-box;
  border: 1px solid #5d636b;
  border-radius: 14px;
  background: #363b42;
  box-shadow:
    inset 0 2px 5px rgba(0, 0, 0, 0.35),
    0 2px 6px rgba(0, 0, 0, 0.25);
  transition:
    background 0.22s,
    border-color 0.22s,
    box-shadow 0.22s;
}

.ai-mode-slider::before {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #e8eaed;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.42);
  transition:
    transform 0.22s,
    background 0.22s;
}

.ai-mode-switch input:checked + .ai-mode-slider {
  border-color: #7289da;
  background: linear-gradient(135deg, #7289da, #8d6fe8);
  box-shadow:
    inset 0 1px 4px rgba(255, 255, 255, 0.18),
    0 0 12px rgba(114, 137, 218, 0.62);
}

.ai-mode-switch input:checked + .ai-mode-slider::before {
  transform: translateX(22px);
  background: #fff;
}

.ai-mode-switch input:focus-visible + .ai-mode-slider {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

.ai-mode-switch-text {
  min-width: 72px;
  color: #ddd;
  line-height: 1.3;
}
```

## 2. 替換 AI 參數 HTML

在 AI 設定窗口中搜尋:

```html
<div class="ai-param-row">
  <div class="ai-param-title" id="ai-param-title-time">限時 (秒)</div>
  <div class="ai-param-values">
    <input type="number" id="ai-time-2" value="3" step="1" oninput="handleAIParamInput(2)" />
    <input type="number" id="ai-time-1" value="18" step="1" oninput="handleAIParamInput(1)" />
  </div>
</div>
```

替換為:

```html
<div class="ai-param-row">
  <div class="ai-param-title" id="ai-param-title-time">限時 (秒)</div>

  <div class="ai-param-values">
    <input
      type="number"
      id="ai-limit-2"
      value="7"
      min="0.2"
      step="0.1"
      inputmode="decimal"
      oninput="handleAIParamInput(2)"
    />

    <input
      type="number"
      id="ai-limit-1"
      value="36"
      min="0.2"
      step="0.1"
      inputmode="decimal"
      oninput="handleAIParamInput(1)"
    />
  </div>
</div>

<div class="ai-thinking-mode-row">
  <div class="ai-thinking-mode-title" id="ai-thinking-mode-title">思考模式</div>

  <label class="ai-mode-switch">
    <input
      type="checkbox"
      id="ai-search-count-mode"
      onchange="handleAISearchModeToggle()"
    />

    <span class="ai-mode-slider" aria-hidden="true"></span>
    <span class="ai-mode-switch-text" id="ai-search-count-label">搜尋次數</span>
  </label>
</div>
```

開關沒有 `checked`,所以默認為關閉,即默認使用限時模式。

---

# 八、增加 AI 雙語文字

在 `i18n` 中搜尋:

```js
'ai-param-title-time': { zh: '限時 (秒)', en: 'Time Limit (s)' },
```

替換為:

```js
'ai-param-title-time': {
  zh: '限時 (秒)',
  en: 'Time Limit (s)'
},
'ai-param-title-searches': {
  zh: '規定搜尋次數',
  en: 'Fixed Search Count'
},
'ai-thinking-mode-title': {
  zh: '思考模式',
  en: 'Thinking Mode'
},
'ai-search-count-label': {
  zh: '搜尋次數',
  en: 'Search Count'
},
```

## 替換 `updateAIDialogLang()`

```js
function updateAIDialogLang() {
  [1, 2].forEach(p => {
    document.querySelectorAll(`#ai-list-${p} .ai-type-option`).forEach(opt => {
      const val = opt.getAttribute('data-val');
      const labels = AI_TYPE_LABELS[val];

      if (labels) {
        opt.querySelector('.opt-label').textContent = labels[currentLang];
      }
    });

    updateAISelectedDisplay(p);
  });

  // 語言切換後重新套用目前的限時/搜尋次數標題。
  updateAISearchModeUI(false);
}
```

---

# 九、替換 AI 默認參數

搜尋:

```js
const CUSTOM_AI_DEFAULT = { time: 5 };

let aiConfig = {
  1: false,
  2: true,
  type: { 1: 'human', 2: 'easy' },
  lastAIType: { 1: 'easy', 2: 'easy' },
  params: {
    easy: { time: 3 },
    hard: { time: 7 },
    expert: { time: 12 },
    custom: { ...CUSTOM_AI_DEFAULT }
  },

  settings: {
    1: { time: 18 },
    2: { time: 3 }
  }
};
```

替換為:

```js
const CUSTOM_AI_DEFAULT = Object.freeze({
  time: 36,
  searches: 960
});

let aiConfig = {
  1: false,
  2: true,

  type: {
    1: 'human',
    2: 'easy'
  },

  lastAIType: {
    1: 'easy',
    2: 'easy'
  },

  // time:限時秒數;searches:MCTS 規定搜尋次數。
  params: {
    easy: {
      time: 7,
      searches: 120
    },
    hard: {
      time: 12,
      searches: 240
    },
    expert: {
      time: 24,
      searches: 480
    },
    custom: {
      ...CUSTOM_AI_DEFAULT
    }
  },

  // 全局思考模式;默認仍是限時模式。
  searchMode: 'time',

  // 各玩家實際使用的參數。
  settings: {
    1: {
      ...CUSTOM_AI_DEFAULT
    },
    2: {
      time: 7,
      searches: 120
    }
  },

  // 保存雙方各自的自訂數值,切換到預設等級後不會遺失。
  customSettings: {
    1: {
      ...CUSTOM_AI_DEFAULT
    },
    2: {
      ...CUSTOM_AI_DEFAULT
    }
  }
};
```

---

# 十、替換 AI 設定窗口程式

搜尋從:

```js
function updateAIConfig() {
```

開始,一直到:

```js
function cancelAISettingsDialog() {
  syncAIDialogFromConfirmed();
  document.getElementById('ai-settings-dialog').style.display = 'none';
}
```

將這整段替換為以下程式:

```js
let aiDialogDraft = null;

function cloneAISetting(setting = {}) {
  const rawTime = Number(setting.time);
  const rawSearches = Number(setting.searches);

  return {
    time: Number.isFinite(rawTime)
      ? Math.max(0.2, rawTime)
      : CUSTOM_AI_DEFAULT.time,

    searches: Number.isFinite(rawSearches)
      ? Math.max(1, Math.floor(rawSearches))
      : CUSTOM_AI_DEFAULT.searches
  };
}

function getAIParamField(mode) {
  return mode === 'count' ? 'searches' : 'time';
}

function createAIDialogDraft() {
  return {
    searchMode: aiConfig.searchMode === 'count' ? 'count' : 'time',

    type: {
      1: aiConfig.type[1] || 'human',
      2: aiConfig.type[2] || 'human'
    },

    settings: {
      1: cloneAISetting(aiConfig.settings[1]),
      2: cloneAISetting(aiConfig.settings[2])
    },

    customSettings: {
      1: cloneAISetting(aiConfig.customSettings[1]),
      2: cloneAISetting(aiConfig.customSettings[2])
    }
  };
}

function ensureAIDialogDraft() {
  if (!aiDialogDraft) {
    aiDialogDraft = createAIDialogDraft();
  }

  return aiDialogDraft;
}

function setAITypeControl(p, type) {
  const hiddenInput = document.getElementById('ai-p' + p + '-type');

  if (hiddenInput) {
    hiddenInput.value = type;
  }

  document.querySelectorAll(`#ai-list-${p} .ai-type-option`).forEach(option => {
    option.classList.toggle('selected', option.getAttribute('data-val') === type);
  });

  updateAISelectedDisplay(p);
}

function findAIPresetType(field, value) {
  if (!Number.isFinite(value)) {
    return 'custom';
  }

  for (const type of ['easy', 'hard', 'expert']) {
    const presetValue = aiConfig.params[type][field];

    if (Math.abs(value - presetValue) < 1e-9) {
      return type;
    }
  }

  return 'custom';
}

function renderAIDialogParam(p) {
  if (!aiDialogDraft) return;

  const input = document.getElementById('ai-limit-' + p);

  if (!input) return;

  const type = aiDialogDraft.type[p];
  const mode = aiDialogDraft.searchMode;
  const field = getAIParamField(mode);
  const isHuman = type === 'human';

  input.disabled = isHuman;

  if (mode === 'count') {
    input.min = '1';
    input.step = '1';
    input.setAttribute('inputmode', 'numeric');
  } else {
    input.min = '0.2';
    input.step = '0.1';
    input.setAttribute('inputmode', 'decimal');
  }

  if (isHuman) {
    input.value = '';
    return;
  }

  const setting = cloneAISetting(aiDialogDraft.settings[p]);
  const value = setting[field];

  input.value = field === 'searches'
    ? String(Math.floor(value))
    : String(value);
}

function updateAISearchModeUI(updateDraft = true) {
  const checkbox = document.getElementById('ai-search-count-mode');
  const title = document.getElementById('ai-param-title-time');

  if (!checkbox || !title) return;

  if (aiDialogDraft) {
    if (updateDraft) {
      aiDialogDraft.searchMode = checkbox.checked ? 'count' : 'time';
    } else {
      checkbox.checked = aiDialogDraft.searchMode === 'count';
    }
  } else {
    checkbox.checked = aiConfig.searchMode === 'count';
  }

  const mode = aiDialogDraft
    ? aiDialogDraft.searchMode
    : aiConfig.searchMode;

  title.textContent =
    mode === 'count'
      ? i18n['ai-param-title-searches'][currentLang]
      : i18n['ai-param-title-time'][currentLang];

  if (aiDialogDraft) {
    renderAIDialogParam(1);
    renderAIDialogParam(2);
  }
}

function handleAISearchModeToggle() {
  ensureAIDialogDraft();
  updateAISearchModeUI(true);
}

function detectAIPresetType(p) {
  const draft = ensureAIDialogDraft();
  const input = document.getElementById('ai-limit-' + p);

  if (!input) {
    return 'custom';
  }

  const field = getAIParamField(draft.searchMode);
  const value = Number(input.value);

  return findAIPresetType(field, value);
}

function handleAIParamInput(p) {
  const draft = ensureAIDialogDraft();
  const input = document.getElementById('ai-limit-' + p);

  if (!input || input.disabled) return;

  const mode = draft.searchMode;
  const field = getAIParamField(mode);
  const rawValue = Number(input.value);

  // 輸入框被暫時清空時不立刻覆寫設定。
  if (!Number.isFinite(rawValue)) return;

  const value =
    field === 'searches'
      ? Math.max(1, Math.floor(rawValue))
      : Math.max(0.2, rawValue);

  if (field === 'searches') {
    input.value = String(value);
  }

  const previousType = draft.type[p];
  const detectedType = findAIPresetType(field, value);

  if (
    detectedType === 'easy' ||
    detectedType === 'hard' ||
    detectedType === 'expert'
  ) {
    // 輸入值恰好符合預設時,同步整組時間和搜尋次數。
    draft.settings[p] = cloneAISetting(aiConfig.params[detectedType]);
  } else {
    /*
     * 第一次由預設等級改成自訂時,另一種模式使用自訂默認值:
     * 36 秒/960 次。
     */
    if (previousType !== 'custom') {
      draft.customSettings[p] = cloneAISetting(CUSTOM_AI_DEFAULT);
    }

    draft.customSettings[p][field] = value;
    draft.settings[p] = cloneAISetting(draft.customSettings[p]);
  }

  draft.type[p] = detectedType;
  setAITypeControl(p, detectedType);
}

function handleAITypeSelect(p) {
  const draft = ensureAIDialogDraft();
  const type = document.getElementById('ai-p' + p + '-type').value;

  draft.type[p] = type;

  if (
    type === 'easy' ||
    type === 'hard' ||
    type === 'expert'
  ) {
    draft.settings[p] = cloneAISetting(aiConfig.params[type]);
  } else if (type === 'custom') {
    draft.settings[p] = cloneAISetting(draft.customSettings[p]);
  }

  setAITypeControl(p, type);
  renderAIDialogParam(p);
}

// 人類控制的一方不需要顯示 AI 參數。
function updateAIParamInputsState(p) {
  renderAIDialogParam(p);
}

function updateAIConfig(sourceDraft = null) {
  if (sourceDraft) {
    aiConfig.searchMode =
      sourceDraft.searchMode === 'count' ? 'count' : 'time';
  }

  [1, 2].forEach(p => {
    const hiddenInput = document.getElementById('ai-p' + p + '-type');

    const type = sourceDraft
      ? sourceDraft.type[p]
      : hiddenInput
        ? hiddenInput.value
        : aiConfig.type[p];

    if (sourceDraft) {
      // 即使目前選擇人類或預設 AI,也保留尚未啟用的自訂數值。
      aiConfig.customSettings[p] = cloneAISetting(
        sourceDraft.customSettings[p]
      );
    }

    aiConfig.type[p] = type;
    aiConfig[p] = type !== 'human';

    if (type !== 'human') {
      aiConfig.lastAIType[p] = type;
    }

    if (
      type === 'easy' ||
      type === 'hard' ||
      type === 'expert'
    ) {
      aiConfig.settings[p] = cloneAISetting(aiConfig.params[type]);
    } else if (type === 'custom') {
      const customSetting = sourceDraft
        ? sourceDraft.customSettings[p]
        : aiConfig.customSettings[p];

      aiConfig.customSettings[p] = cloneAISetting(customSetting);
      aiConfig.settings[p] = cloneAISetting(customSetting);
    } else if (sourceDraft && sourceDraft.settings[p]) {
      // 人類狀態保留原參數,日後重新啟用 AI 時仍可使用。
      aiConfig.settings[p] = cloneAISetting(sourceDraft.settings[p]);
    }

    setAITypeControl(p, type);
  });

  updateUI();
  checkAndTriggerAI();
}

function syncAIDialogFromConfirmed() {
  aiDialogDraft = createAIDialogDraft();

  const checkbox = document.getElementById('ai-search-count-mode');

  if (checkbox) {
    checkbox.checked = aiDialogDraft.searchMode === 'count';
  }

  [1, 2].forEach(p => {
    setAITypeControl(p, aiDialogDraft.type[p]);

    const dropdown = document.getElementById('ai-dropdown-' + p);

    if (dropdown) {
      dropdown.classList.remove('open');
    }
  });

  updateAISearchModeUI(false);
}

function openAISettingsDialog() {
  syncAIDialogFromConfirmed();
  document.getElementById('ai-settings-dialog').style.display = 'flex';
}

// 按下確認才套用草稿。
function confirmAISettingsDialog() {
  const draft = ensureAIDialogDraft();

  /*
   * 確認前再讀一次輸入框,處理使用鍵盤或瀏覽器數字按鈕
   * 修改後尚未失去焦點的情況。
   */
  [1, 2].forEach(p => {
    const input = document.getElementById('ai-limit-' + p);

    if (input && !input.disabled && input.value !== '') {
      handleAIParamInput(p);
    }
  });

  document.getElementById('ai-settings-dialog').style.display = 'none';

  aiDialogDraft = null;
  updateAIConfig(draft);
}

// 叉叉關閉時捨棄草稿,還原已確認的設定。
function cancelAISettingsDialog() {
  syncAIDialogFromConfirmed();

  document.getElementById('ai-settings-dialog').style.display = 'none';

  aiDialogDraft = null;
}
```

確認整份文件中不再有:

```js
ai-time-1
ai-time-2
```

現在統一使用:

```js
ai-limit-1
ai-limit-2
```

---

# 十一、讓 minimax 不受時間或搜尋次數限制

目前 `runStateMinimax()` 使用 `deadline` 中止。需要移除時間判斷,只保留使用者主動取消 AI 的判斷。

## 1. 修改函數參數

搜尋:

```js
async function runStateMinimax(rootState, rootMoves, maxDepth, deadline) {
```

替換為:

```js
async function runStateMinimax(rootState, rootMoves, maxDepth) {
```

## 2. 在 `runStateMinimax()` 函數內

將所有:

```js
if (cancelAi || performance.now() >= deadline) {
  throw new SearchTimeout();
}
```

替換為:

```js
if (cancelAi) {
  throw new SearchTimeout();
}
```

附件中該函數內共有多處相同判斷,全部只保留 `cancelAi`。

其中這一段:

```js
if (cancelAi || performance.now() >= deadline) {
  throw new SearchTimeout();
}
```

也同樣替換。

這樣 minimax 仍然可以由使用者按打叉取消,但不會因限時或 MCTS 搜尋次數設定而中止。

---

# 十二、讓 MCTS 支援規定搜尋次數

## 1. 修改 MCTS 函數參數

搜尋:

```js
async function runStateMCTS(rootState, rootMoves, deadline, cValue) {
```

替換為:

```js
async function runStateMCTS(
  rootState,
  rootMoves,
  deadline,
  cValue,
  maxSearchCount = Infinity
) {
```

## 2. 修改 MCTS 主循環

搜尋:

```js
while (performance.now() < deadline && !cancelAi) {
```

替換為:

```js
while (
  !cancelAi &&
  searchCount < maxSearchCount &&
  performance.now() < deadline
) {
```

限時模式會傳入有限的 `deadline` 和無限搜尋次數;搜尋次數模式則傳入 `Infinity` 作為 deadline,直到完成指定次數。

---

# 十三、修改 `startAI()` 的停止條件

## 1. 替換時間設定

在 `startAI()` 中搜尋:

```js
const timeLimit = Math.max(0.2, aiConfig.settings[aiPlayer].time || 3) * 1000;
```

替換為:

```js
const activeAISettings =
  aiConfig.settings[aiPlayer] || CUSTOM_AI_DEFAULT;

const searchMode =
  aiConfig.searchMode === 'count' ? 'count' : 'time';

const timeLimit =
  Math.max(
    0.2,
    Number(activeAISettings.time) || CUSTOM_AI_DEFAULT.time
  ) * 1000;

const searchLimit =
  Math.max(
    1,
    Math.floor(
      Number(activeAISettings.searches) ||
        CUSTOM_AI_DEFAULT.searches
    )
  );
```

## 2. 替換搜尋啟動程式

搜尋:

```js
const searchStartTime = performance.now();
const searchDeadline = searchStartTime + timeLimit;

if (useMinimax) {
  result = await runStateMinimax(rootState, rootMoves, turnsLeft, searchDeadline);
} else {
  result = await runStateMCTS(rootState, rootMoves, searchDeadline, MCTS_C);
}
```

替換為:

```js
const searchStartTime = performance.now();

if (useMinimax) {
  /*
   * 終盤 minimax 不受限時或規定搜尋次數限制。
   * 只會在完成指定深度,或使用者主動取消 AI 時結束。
   */
  result = await runStateMinimax(
    rootState,
    rootMoves,
    turnsLeft
  );
} else {
  /*
   * 限時模式:
   *   deadline 為指定時間,maxSearchCount 為 Infinity。
   *
   * 搜尋次數模式:
   *   deadline 為 Infinity,完成指定模擬次數才結束。
   */
  const searchDeadline =
    searchMode === 'time'
      ? searchStartTime + timeLimit
      : Infinity;

  const maxSearchCount =
    searchMode === 'count'
      ? searchLimit
      : Infinity;

  result = await runStateMCTS(
    rootState,
    rootMoves,
    searchDeadline,
    MCTS_C,
    maxSearchCount
  );
}
```

---

# 十四、最終行為

修改後的預期結果如下:

| AI 等級 | 限時模式 | 搜尋次數模式 |
|---|---:|---:|
| 簡單 | 7 秒 | 120 次 |
| 困難 | 12 秒 | 240 次 |
| 專家 | 24 秒 | 480 次 |
| 自訂 | 36 秒 | 960 次 |

搜尋規則:

- 一般局面使用 MCTS。
- 限時模式下,MCTS 到達指定時間停止。
- 搜尋次數模式下,MCTS 不限時間,完成指定次數後停止。
- 終盤 minimax 不受時間或搜尋次數限制。
- minimax 和 MCTS 都仍可由玩家按打叉主動取消。
- 開局隨機落子、最後一子精確求解、仲裁判定均保留原流程。
- 正式落子仍經過原本的 `validateTurnOnState()`,不會繞過合法性檢查。
- 動畫 SVG 顯示兩個藍點。
- 數字 SVG 不顯示藍點、紫點或暫時落子圓點。
- 正式棋盤中,上一回合棋子顯示藍點;選中對方棋子後只顯示紫點。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀一枝独秀
帖子: 6605
注册时间: 2022年 11月 18日 17:36
联系:

Re: 一個新的遊戲創意

帖子 ejsoon »

trigolden_game2666.html

頂鑫棋先暫時做到這裡吧,除非想到還有什麼地方可以修改。

代码: 全选

<?xml version="1.0" standalone="no"?>
<svg id="etanitrigolden" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" width="480" height="480" viewBox="0 0 480 480" preserveAspectRatio="xMidYMid meet"><style type="text/css" data-bt-font="Techfont">@font-face {
    font-family: 'Techfont';
    src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}</style>
        <defs>
          <g id="tile0" fill="#ffc2eb">
            <path d="M -61.687,17.119 L -14.563,-17.119 L 21.437,-17.119 L 32.562,17.119 Z"/>
          </g>
          <g id="tile1" fill="#ffc2eb">
            <path d="M -76.249,24.775 L -0.001,-30.623 L 29.124,-9.463 L 40.249,24.775 Z"/>
          </g>
          <g id="tile2" fill="#ffc2eb"><path d="M -55.468,19.14 L 2.781,-23.18 L 31.906,-2.02 L 2.781,19.14 Z"/></g>
          <g id="tile3" fill="#96ffbe">
            <path d="M -61.687,17.119 L -14.563,-17.119 L 21.437,-17.119 L 32.562,17.119 Z"/>
          </g>
          <g id="tile4" fill="#96ffbe">
            <path d="M -76.249,24.775 L -0.001,-30.623 L 29.124,-9.463 L 40.249,24.775 Z"/>
          </g>
          <g id="tile5" fill="#96ffbe"><path d="M -55.468,19.14 L 2.781,-23.18 L 31.906,-2.02 L 2.781,19.14 Z"/></g>
        <clipPath id="btclip"><path d="M0,0h480v60h-480z"/></clipPath></defs>
        <g id="btcamera" transform="translate(126.318,207.145)"><g class="etdrop" stroke="#777" stroke-width="2" stroke-linejoin="round" transform="scale(0.886385)" id="etanidrop"><use href="#tile0" transform="translate(0,0) scale(-1,1) rotate(248)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt0.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt1"/></use><use href="#tile5" transform="translate(29.661,17.984) scale(-1,1) rotate(284)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt1.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt2"/></use><use href="#tile4" transform="translate(43.587,-36.922) scale(1,1) rotate(148.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt1.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile1" transform="translate(17.33,-78.943) scale(-1,1) rotate(31.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt2.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt3"/></use><use href="#tile0" transform="translate(98.796,-61.734) scale(-1,1) rotate(248)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt2.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile4" transform="translate(137.606,-30.346) scale(1,1) rotate(220.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt3.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt4"/></use><use href="#tile3" transform="translate(119.024,-111.801) scale(-1,1) rotate(68)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt3.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile2" transform="translate(145.504,-177.341) scale(1,1) rotate(328.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt4.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt5"/></use><use href="#tile1" transform="translate(80.214,-143.188) scale(1,1) rotate(40.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt4.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile4" transform="translate(137.715,-229.022) scale(-1,1) rotate(104)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt5.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt6"/></use><use href="#tile4" transform="translate(83.671,-192.618) scale(-1,1) rotate(211.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt5.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile2" transform="translate(46.327,68.129) scale(-1,1) rotate(104)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt6.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt7"/></use><use href="#tile0" transform="translate(3.181,83.524) scale(1,1) rotate(112.002)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt6.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile4" transform="translate(-27.291,-116.386) scale(1,1) rotate(40.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt7.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt8"/></use><use href="#tile5" transform="translate(-26.209,-42.709) scale(-1,1) rotate(247.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt7.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile0" transform="translate(-38.895,-159.194) scale(1,1) rotate(4.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt8.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt9"/></use><use href="#tile1" transform="translate(-86.719,-101.57) scale(1,1) rotate(220.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt8.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile3" transform="translate(-137.691,-97.46) scale(1,1) rotate(4.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt9.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt10"/></use><use href="#tile5" transform="translate(-120.87,-144.489) scale(-1,1) rotate(104)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt9.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile1" transform="translate(-126.086,-54.652) scale(1,1) rotate(40.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt10.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt11"/></use><use href="#tile0" transform="translate(-86.155,-5.252) scale(1,1) rotate(148.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt10.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile4" transform="translate(-79.036,46.596) scale(1,1) rotate(256.002)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt11.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt12"/></use><use href="#tile4" transform="translate(-8.662,133.272) scale(-1,1) rotate(320)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt11.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile1" transform="translate(53.508,177.806) scale(-1,1) rotate(140)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt12.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt13"/></use><use href="#tile1" transform="translate(-12.934,194.371) scale(-1,1) rotate(140)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt12.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile3" transform="translate(73.414,92.487) scale(1,1) rotate(292.002)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt13.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt14"/></use><use href="#tile4" transform="translate(31.687,231.813) scale(-1,1) rotate(355.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt13.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile2" transform="translate(91.559,47.577) scale(1,1) rotate(148.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt14.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt15"/></use><use href="#tile1" transform="translate(192.257,23.148) scale(1,1) rotate(40.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt14.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile3" transform="translate(96.987,206.934) scale(-1,1) rotate(31.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt15.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt16"/></use><use href="#tile4" transform="translate(236.877,60.59) scale(1,1) rotate(256.002)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt15.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile0" transform="translate(243.415,120.765) scale(-1,1) rotate(140)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt16.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt17"/></use><use href="#tile1" transform="translate(284.955,48.603) scale(-1,1) rotate(284)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt16.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile3" transform="translate(318.343,77.8) scale(-1,1) rotate(248)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt17.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt18"/></use><use href="#tile5" transform="translate(304.99,10.726) scale(1,1) rotate(76.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt17.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile1" transform="translate(370.639,75.809) scale(-1,1) rotate(355.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt18.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt19"/></use><use href="#tile0" transform="translate(348.345,26.39) scale(-1,1) rotate(355.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt18.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile3" transform="translate(224.93,161.122) scale(-1,1) rotate(248)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt19.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt20"/></use><use href="#tile3" transform="translate(276.084,150.817) scale(-1,1) rotate(31.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt19.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile2" transform="translate(156.933,169.476) scale(1,1) rotate(4.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt20.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt21"/></use><use href="#tile0" transform="translate(139.829,220.536) scale(-1,1) rotate(211.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt20.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile5" transform="translate(418.86,31.322) scale(1,1) rotate(40.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt21.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt22"/></use><use href="#tile5" transform="translate(354.142,-10.803) scale(1,1) rotate(148.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt21.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile2" transform="translate(434.427,66.733) scale(1,1) rotate(220.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt22.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt23"/></use><use href="#tile0" transform="translate(328.308,-43.655) scale(1,1) rotate(184.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt22.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile5" transform="translate(138.79,140.441) scale(-1,1) rotate(247.999)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt23.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt24"/></use><use href="#tile5" transform="translate(183.143,138.239) scale(-1,1) rotate(104)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt23.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile2" transform="translate(103.297,126.1) scale(1,1) rotate(292.002)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt24.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt25"/></use><use href="#tile2" transform="translate(-121.25,39.247) scale(1,1) rotate(328.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt24.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile5" transform="translate(-152.994,26.421) scale(-1,1) rotate(284)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt25.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt26"/></use><use href="#tile3" transform="translate(-147.73,104.787) scale(-1,1) rotate(68)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt25.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile2" transform="translate(-177.997,-3.095) scale(-1,1) rotate(104)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt26.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt27"/></use><use href="#tile2" transform="translate(-161.954,148.72) scale(1,1) rotate(256.001)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt26.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s"/></use><use href="#tile3" transform="translate(181.908,-176.046) scale(-1,1) rotate(68)"><animate begin="bt0.begin" attributeName="opacity" values="1;0" fill="freeze" dur="1s"/><animate begin="bt27.end" attributeName="opacity" values="0;1;1" fill="freeze" dur="2s" id="bt28"/></use><path d="M 83.671 -192.618 L 80.214 -143.188 L 119.024 -111.801 L 145.504 -177.341 L 137.715 -229.022 Z" fill="none" stroke="#006400" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt6.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><path d="M -86.155 -5.252 L -26.209 -42.709 L 17.33 -78.943 L -27.291 -116.386 L -86.719 -101.57 L -137.691 -97.46 L -126.086 -54.652 Z" fill="none" stroke="#8b0000" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt11.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><path d="M -8.662 133.272 L 3.181 83.524 L 29.661 17.984 L 0 0 L 43.587 -36.922 L 17.33 -78.943 L -26.209 -42.709 L -86.155 -5.252 L -79.036 46.596 Z" fill="none" stroke="#006400" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt12.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><path d="M 53.508 177.806 L -8.662 133.272 L 3.181 83.524 L 29.661 17.984 L 46.327 68.129 L 73.414 92.487 Z" fill="none" stroke="#006400" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt14.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><path d="M 192.257 23.148 L 137.606 -30.346 L 98.796 -61.734 L 43.587 -36.922 L 0 0 L 29.661 17.984 L 46.327 68.129 L 73.414 92.487 L 91.559 47.577 Z" fill="none" stroke="#8b0000" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt15.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><path d="M 31.687 231.813 L -12.934 194.371 L -8.662 133.272 L 53.508 177.806 L 96.987 206.934 Z" fill="none" stroke="#006400" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt16.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><path d="M 348.345 26.39 L 304.99 10.726 L 284.955 48.603 L 318.343 77.8 L 370.639 75.809 Z" fill="none" stroke="#8b0000" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt19.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><path d="M 370.639 75.809 L 318.343 77.8 L 284.955 48.603 L 304.99 10.726 L 348.345 26.39 L 418.86 31.322 L 434.427 66.733 Z" fill="none" stroke="#8b0000" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt23.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><path d="M 73.414 92.487 L 46.327 68.129 L 29.661 17.984 L 3.181 83.524 L -8.662 133.272 L 53.508 177.806 L 96.987 206.934 L 156.933 169.476 L 138.79 140.441 L 103.297 126.1 Z" fill="none" stroke="#8b0000" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt25.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><path d="M -126.086 -54.652 L -137.691 -97.46 L -86.719 -101.57 L -27.291 -116.386 L 17.33 -78.943 L -26.209 -42.709 L -86.155 -5.252 L -79.036 46.596 L -121.25 39.247 L -152.994 26.421 L -177.997 -3.095 Z" fill="none" stroke="#8b0000" stroke-width="3" stroke-linejoin="round" stroke-linecap="round" opacity="0" pointer-events="none"><set begin="bt27.begin+1s" attributeName="opacity" to="1" dur="0.777s"/></path><circle id="btlastdot1" cx="0" cy="0" r="3" fill="#00bfff" stroke="none" transform="translate(777,777)" pointer-events="none"><animateTransform attributeName="transform" attributeType="XML" type="translate" values="777,777;0,0;29.661,17.984;17.33,-78.943;137.606,-30.346;145.504,-177.341;137.715,-229.022;46.327,68.129;-27.291,-116.386;-38.895,-159.194;-137.691,-97.46;-126.086,-54.652;-79.036,46.596;53.508,177.806;73.414,92.487;91.559,47.577;96.987,206.934;243.415,120.765;318.343,77.8;370.639,75.809;224.93,161.122;156.933,169.476;418.86,31.322;434.427,66.733;138.79,140.441;103.297,126.1;-152.994,26.421;-177.997,-3.095;181.908,-176.046;181.908,-176.046" keyTimes="0;0.044117647;0.073529412;0.102941176;0.132352941;0.161764706;0.191176471;0.220588235;0.25;0.279411765;0.308823529;0.338235294;0.367647059;0.397058824;0.426470588;0.455882353;0.485294118;0.514705882;0.544117647;0.573529412;0.602941176;0.632352941;0.661764706;0.691176471;0.720588235;0.75;0.779411765;0.808823529;0.838235294;1" calcMode="discrete" begin="bt0.begin" dur="68s" fill="freeze" restart="always"/></circle><circle id="btlastdot2" cx="0" cy="0" r="3" fill="#00bfff" stroke="none" transform="translate(777,777)" pointer-events="none"><animateTransform attributeName="transform" attributeType="XML" type="translate" values="777,777;0,0;43.587,-36.922;98.796,-61.734;119.024,-111.801;80.214,-143.188;83.671,-192.618;3.181,83.524;-26.209,-42.709;-86.719,-101.57;-120.87,-144.489;-86.155,-5.252;-8.662,133.272;-12.934,194.371;31.687,231.813;192.257,23.148;236.877,60.59;284.955,48.603;304.99,10.726;348.345,26.39;276.084,150.817;139.829,220.536;354.142,-10.803;328.308,-43.655;183.143,138.239;-121.25,39.247;-147.73,104.787;-161.954,148.72;181.908,-176.046;181.908,-176.046" keyTimes="0;0.044117647;0.073529412;0.102941176;0.132352941;0.161764706;0.191176471;0.220588235;0.25;0.279411765;0.308823529;0.338235294;0.367647059;0.397058824;0.426470588;0.455882353;0.485294118;0.514705882;0.544117647;0.573529412;0.602941176;0.632352941;0.661764706;0.691176471;0.720588235;0.75;0.779411765;0.808823529;0.838235294;1" calcMode="discrete" begin="bt0.begin" dur="68s" fill="freeze" restart="always"/></circle></g></g>
      <animate id="bt0" href="#etanidrop" begin="0s;btend.end" attributeName="opacity" values="0;0;1" fill="freeze" dur="2s"/><g id="btstatus" clip-path="url(#btclip)"><path d="M0,0h480v60h-480z" fill="#faf8d7" stroke="none"/><g id="svg-dinger" transform="translate(210,0) scale(0.5)">
                <defs>
                  <linearGradient id="gradTop" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stop-color="#7289da"/>
                    <stop offset="100%" stop-color="rgba(114, 137, 218, 0)"/>
                  </linearGradient>
                </defs>
                <g>
                  <path d="M4,4 L 14.397,36 h 91.205 L 116,4" fill="none" stroke="#7289da" stroke-width="4"/>
                  <polygon points="32.707,120 60,36 87.293,120" fill="url(#gradTop)" stroke="none"/>
                </g>
              </g><use href="#tile0" transform="translate(350,30) scale(0.28) scale(-1,1) translate(14.562,0)"/><use href="#tile1" transform="translate(402,30) scale(0.28) scale(-1,1) translate(18,2.924)"/><use href="#tile2" transform="translate(454,30) scale(0.28) scale(-1,1) translate(11.781,2.02)"/><use href="#tile3" transform="translate(130,30) scale(0.28) scale(1,1) translate(14.562,0)"/><use href="#tile4" transform="translate(78,30) scale(0.28) scale(1,1) translate(18,2.924)"/><use href="#tile5" transform="translate(26,30) scale(0.28) scale(1,1) translate(11.781,2.02)"/><g id="btp1score"><text x="300" y="34" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">0</text><text x="300" y="-26" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">5</text><text x="300" y="-86" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">12</text><text x="300" y="-146" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">15</text><text x="300" y="-206" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">20</text><text x="300" y="-266" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">28</text><text x="300" y="-326" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">37</text><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,0" dur="1s" fill="freeze" begin="bt0.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,60" dur="1s" fill="freeze" begin="bt11.begin+1s"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,120" dur="1s" fill="freeze" begin="bt15.begin+1s"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,180" dur="1s" fill="freeze" begin="bt19.begin+1s"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,240" dur="1s" fill="freeze" begin="bt23.begin+1s"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,300" dur="1s" fill="freeze" begin="bt25.begin+1s"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,360" dur="1s" fill="freeze" begin="bt27.begin+1s"/></g><g id="btp2score"><text x="180" y="34" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">0</text><text x="180" y="-26" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">3</text><text x="180" y="-86" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">10</text><text x="180" y="-146" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">14</text><text x="180" y="-206" stroke="none" fill="#333" font-size="36" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">17</text><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,0" dur="1s" fill="freeze" begin="bt0.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,60" dur="1s" fill="freeze" begin="bt6.begin+1s"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,120" dur="1s" fill="freeze" begin="bt12.begin+1s"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,180" dur="1s" fill="freeze" begin="bt14.begin+1s"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,240" dur="1s" fill="freeze" begin="bt16.begin+1s"/></g><g id="btpiece0"><text x="350" y="34" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">9</text><text x="350" y="94" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">8</text><text x="350" y="154" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">7</text><text x="350" y="214" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">6</text><text x="350" y="274" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">5</text><text x="350" y="334" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">4</text><text x="350" y="394" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">3</text><text x="350" y="454" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">2</text><text x="350" y="514" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">1</text><text x="350" y="574" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">0</text><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,0" dur="1s" fill="freeze" begin="bt0.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-60" dur="1s" fill="freeze" begin="bt1.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-120" dur="1s" fill="freeze" begin="bt3.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-180" dur="1s" fill="freeze" begin="bt7.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-240" dur="1s" fill="freeze" begin="bt9.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-300" dur="1s" fill="freeze" begin="bt11.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-360" dur="1s" fill="freeze" begin="bt17.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-420" dur="1s" fill="freeze" begin="bt19.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-480" dur="1s" fill="freeze" begin="bt21.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-540" dur="1s" fill="freeze" begin="bt23.begin"/></g><g id="btpiece1"><text x="402" y="34" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">9</text><text x="402" y="94" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">8</text><text x="402" y="154" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">7</text><text x="402" y="214" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">6</text><text x="402" y="274" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">5</text><text x="402" y="334" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">4</text><text x="402" y="394" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">3</text><text x="402" y="454" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">2</text><text x="402" y="514" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">1</text><text x="402" y="574" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">0</text><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,0" dur="1s" fill="freeze" begin="bt0.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-60" dur="1s" fill="freeze" begin="bt3.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-120" dur="1s" fill="freeze" begin="bt5.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-180" dur="1s" fill="freeze" begin="bt9.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-240" dur="1s" fill="freeze" begin="bt11.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-360" dur="1s" fill="freeze" begin="bt13.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-420" dur="1s" fill="freeze" begin="bt15.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-480" dur="1s" fill="freeze" begin="bt17.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-540" dur="1s" fill="freeze" begin="bt19.begin"/></g><g id="btpiece2"><text x="454" y="34" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">9</text><text x="454" y="94" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">8</text><text x="454" y="154" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">7</text><text x="454" y="214" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">6</text><text x="454" y="274" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">5</text><text x="454" y="334" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">4</text><text x="454" y="394" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">3</text><text x="454" y="454" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">2</text><text x="454" y="514" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">1</text><text x="454" y="574" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">0</text><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,0" dur="1s" fill="freeze" begin="bt0.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-60" dur="1s" fill="freeze" begin="bt5.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-120" dur="1s" fill="freeze" begin="bt7.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-180" dur="1s" fill="freeze" begin="bt15.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-240" dur="1s" fill="freeze" begin="bt21.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-300" dur="1s" fill="freeze" begin="bt23.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-420" dur="1s" fill="freeze" begin="bt25.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-540" dur="1s" fill="freeze" begin="bt27.begin"/></g><g id="btpiece3"><text x="130" y="34" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">9</text><text x="130" y="94" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">8</text><text x="130" y="154" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">7</text><text x="130" y="214" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">6</text><text x="130" y="274" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">5</text><text x="130" y="334" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">4</text><text x="130" y="394" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">3</text><text x="130" y="454" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">2</text><text x="130" y="514" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">1</text><text x="130" y="574" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">0</text><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,0" dur="1s" fill="freeze" begin="bt0.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-60" dur="1s" fill="freeze" begin="bt4.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-120" dur="1s" fill="freeze" begin="bt10.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-180" dur="1s" fill="freeze" begin="bt14.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-240" dur="1s" fill="freeze" begin="bt16.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-300" dur="1s" fill="freeze" begin="bt18.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-420" dur="1s" fill="freeze" begin="bt20.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-480" dur="1s" fill="freeze" begin="bt26.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-540" dur="1s" fill="freeze" begin="bt28.begin"/></g><g id="btpiece4"><text x="78" y="34" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">9</text><text x="78" y="94" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">8</text><text x="78" y="154" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">7</text><text x="78" y="214" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">6</text><text x="78" y="274" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">5</text><text x="78" y="334" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">4</text><text x="78" y="394" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">3</text><text x="78" y="454" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">2</text><text x="78" y="514" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">1</text><text x="78" y="574" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">0</text><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,0" dur="1s" fill="freeze" begin="bt0.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-60" dur="1s" fill="freeze" begin="bt2.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-120" dur="1s" fill="freeze" begin="bt4.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-240" dur="1s" fill="freeze" begin="bt6.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-300" dur="1s" fill="freeze" begin="bt8.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-420" dur="1s" fill="freeze" begin="bt12.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-480" dur="1s" fill="freeze" begin="bt14.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-540" dur="1s" fill="freeze" begin="bt16.begin"/></g><g id="btpiece5"><text x="26" y="34" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">9</text><text x="26" y="94" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">8</text><text x="26" y="154" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">7</text><text x="26" y="214" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">6</text><text x="26" y="274" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">5</text><text x="26" y="334" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">4</text><text x="26" y="394" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">3</text><text x="26" y="454" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">2</text><text x="26" y="514" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">1</text><text x="26" y="574" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="middle">0</text><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,0" dur="1s" fill="freeze" begin="bt0.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-60" dur="1s" fill="freeze" begin="bt2.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-120" dur="1s" fill="freeze" begin="bt8.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-180" dur="1s" fill="freeze" begin="bt10.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-240" dur="1s" fill="freeze" begin="bt18.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-360" dur="1s" fill="freeze" begin="bt22.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-480" dur="1s" fill="freeze" begin="bt24.begin"/><animateTransform attributeName="transform" attributeType="XML" type="translate" to="0,-540" dur="1s" fill="freeze" begin="bt26.begin"/></g><animate href="#btstatus" begin="bt0.begin" attributeName="opacity" values="0;0;1" fill="freeze" dur="2s"/></g><animateTransform id="btcamera-pan" attributeName="transform" attributeType="XML" type="translate" href="#btcamera" values="126.318,207.145;240,270;240,270;207.537,278.393;207.537,278.393;188.534,332.347;188.534,332.347;126.264,332.998;126.264,332.998;139.963,412.056;139.963,412.056;141.883,456.868;141.883,456.868;218.058,202.789;218.058,202.789;263.71,340.51;263.71,340.51;295.671,385.569;295.671,385.569;354.592,377.23;354.592,377.23;334.064,296.549;334.064,296.549;278.867,190.284;278.867,190.284;222.018,105.054;222.018,105.054;193.42,126.273;193.42,126.273;114.215,238.655;114.215,238.655;92.034,151.435;92.034,151.435;5.83,194.937;5.83,194.937;-36.257,230.766;-36.257,230.766;-78.648,224.706;-78.648,224.706;17.954,131.751;17.954,131.751;108.477,97.15;108.477,97.15;-102.589,260.906;-102.589,260.906;-98.038,259.772;-98.038,259.772;97.322,146.491;97.322,146.491;247.957,196.72;247.957,196.72;373.278,211.85;373.278,211.85;390.664,205.46;390.664,205.46;78.76,426.045;78.76,426.045" dur="56s" fill="freeze" begin="bt0.end"/><animateTransform id="btcamera-scale" attributeName="transform" attributeType="XML" type="scale" href="#etanidrop" values="0.886385;0.886385" dur="56s" fill="freeze" begin="bt0.end"/><animateTransform attributeName="transform" attributeType="XML" type="translate" id="btfull" href="#btcamera" values="78.76,426.045;144.521,285.589" dur="2s" fill="freeze" begin="bt28.end+2s"/><animateTransform attributeName="transform" attributeType="XML" type="scale" href="#etanidrop" values="0.886385;0.661212" dur="2s" fill="freeze" begin="bt28.end+2s"/><animate id="btend" href="#etanidrop" begin="btfull.end+4s" attributeName="opacity" values="1;0" fill="freeze" dur="2s"/><animate href="#btstatus" begin="btend.begin" attributeName="opacity" values="1;0" fill="freeze" dur="2s"/><animateTransform attributeName="transform" attributeType="XML" type="translate" id="btreset" href="#btcamera" values="144.521,285.589;126.318,207.145" dur="1s" fill="freeze" begin="btend.end"/><animateTransform attributeName="transform" attributeType="XML" type="scale" href="#etanidrop" values="0.661212;0.886385" dur="1s" fill="freeze" begin="btend.end"/></svg>
SVG preview
附件
trigolden_game2666.html.7z
(68.8 KiB) 已下载 4 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀一枝独秀
帖子: 6605
注册时间: 2022年 11月 18日 17:36
联系:

Re: 一個新的遊戲創意

帖子 ejsoon »

代码: 全选

移除冗餘代碼
hasLegalContinuation() 檢查了錯誤局面

原程式:

function hasLegalContinuation(candidatePieces, forPlayer) {
  return withHypotheticalPieces(candidatePieces, () =>
    getAllValidMoves(forPlayer).length > 0
  );
}

看似把假設棋加入全域 pieces,但 getAllValidMoves() 現在使用的是:

createSearchStateFromGlobals(player)

這條舊四步驟 AI 流程沒有被 startAI() 使用,所以不會影響現行 MCTS。

替換 hasLegalContinuation()

找到:

function hasLegalContinuation(candidatePieces, forPlayer) {
  return withHypotheticalPieces(candidatePieces, () => getAllValidMoves(forPlayer).length > 0);
}

替換成:

function hasLegalContinuation(candidatePieces, forPlayer) {
  if (
    !Array.isArray(candidatePieces) ||
    candidatePieces.length === 0
  ) {
    return false;
  }

  const mover =
    candidatePieces[0].owner || currentPlayer;

  const state =
    createSearchStateFromGlobals(mover);

  try {
    /*
     * 真正在 SearchState 上完成假設回合,
     * 不再臨時修改全域 pieces。
     */
    const applied = applySearchTurnToState(
      state,
      candidatePieces,
      {
        validate: true
      }
    );

    const nextState =
      applied.state.mover === forPlayer
        ? applied.state
        : {
            ...applied.state,
            mover: forPlayer
          };

    /*
     * 正常完局不需要再留共活;
     * 尚未完局時,檢查對方是否仍有仲裁防禦。
     */
    return (
      isSearchGameComplete(nextState) ||
      hasArbitrationDefence(nextState)
    );
  } catch (error) {
    return false;
  }
}

原來的:

function withHypotheticalPieces(candidatePieces, fn) {
  ...
}

只有這個錯誤流程需要,可以直接刪除。

要清理的舊代碼

以下函式屬於舊四步驟選招流程,沒有被現行 startAI() 使用:

getScoringSingleCandidates
getAllSecondGhostCandidates
getScoringSecondGhost
getGhostsForOppPiece
getCrossTargetScoringCandidates
buildAIExceptionMoves
pickRandomSameTargetMove
withHypotheticalPieces

其中註解:
// 依四步驟流程組合出 AI 本回合真正送進搜尋的候選步伐清單

與實際程式不符,因為 startAI() 並沒有呼叫 buildAIExceptionMoves()。應將其刪除,否則日後很容易修改錯誤的 AI 流程。

確認以上說法是否正確,如正確則按以上說的修改代碼。

同時把所有注釋中的「單接」改為「單連」,把所有「互頂」改為「雙接」。

代码: 全选

Syntax checks out. Now let's do the terminology replacement (單接→單連, 互頂→雙接) in comments/text.Now fix the two duplicated "雙接/雙接" spots created by the merge:Also check line 6277, which now reads awkwardly since "互相形成雙接" already made sense before rename — let's verify it still reads fine, plus re-check syntax validity.All good — old terms are gone, syntax passes, and the merged "雙接/雙接" duplicates are cleaned up. Let me copy the final file to outputs and present it.我確認過了,說法正確,已完成修改。

**驗證結果:**
- `startAI()` 只呼叫 `createSearchStateFromGlobals` + `generateLegalTurns` / MCTS 流程,全檔搜尋後確認 `buildAIExceptionMoves` 沒有任何呼叫端——確實是死碼。
- 連帶的 `getScoringSingleCandidates`、`getAllSecondGhostCandidates`、`getScoringSecondGhost`、`getGhostsForOppPiece`、`getCrossTargetScoringCandidates`、`pickRandomSameTargetMove` 也只在這條死鏈內互相呼叫,沒有其他外部引用。

**已完成的修改:**
1. 刪除 `withHypotheticalPieces`(暫改全域 `pieces` 的舊寫法)。
2. `hasLegalContinuation()` 換成你給的新版本,直接在 `SearchState` 上呼叫 `applySearchTurnToState` 完成假設回合,再用 `isSearchGameComplete` / `hasArbitrationDefence` 判斷,不再依賴 `getAllValidMoves(forPlayer)`(那個走的是已被取代的舊流程)。
3. 整段刪除上述七個死碼函式,以及描述舊四步驟流程、現已對不上程式的註解。
4. 全檔「單接」→「單連」、「互頂」→「雙接」;原本兩處寫成「雙接/互頂」的註解,替換後會變成「雙接/雙接」重複,我已手動收斂成單一個「雙接」。

**有一點要提醒你:** 替換後的 `hasLegalContinuation()` 目前**沒有任何呼叫端**了——它原本的三個呼叫點(`buildAIExceptionMoves` 兩處、`pickRandomSameTargetMove` 一處)全部隨死碼一起刪掉了。函式本身邏輯正確、語法也通過檢查,但現狀它是孤兒函式。如果你只是想先把它修好留著備用(例如以後要重新接上某個呼叫點),那現況沒問題;如果你認為它也該一併刪除,請告訴我。

另外,`i18n` 裡的 `ai-thinking-step2-single`、`ai-thinking-step2-pair`、`ai-thinking-step3-cross`、`ai-thinking-step3-validate`、`ai-thinking-step4-fallback` 這幾組翻譯字串,也因為死碼被刪而變成無人使用,我沒有動它們(不在你列出的清理範圍內),但可以一併告訴你,之後想清可以再說一聲。

已用 `node -e "new Function(...)"` 對整份 `<script>` 做語法檢查,兩次修改後都通過。
如果hasLegalContinuation()是沒用的則把它刪掉,同時把i18n中不再使用的翻譯刪掉。

代码: 全选

Also check simulateScoreGain usage—still needed elsewhere? Let's confirm before removing surrounding block.Now let's remove the unused i18n entries.Good, step1 stays. Now delete the five unused keys (step2-single, step2-pair, step3-cross, step3-validate, step4-fallback).Now verify nothing else references these keys, and re-run syntax check.All clear — no remaining references, and the script parses correctly.已刪除:

1. **`hasLegalContinuation()`** 及其註解——確認全檔已無任何呼叫端後移除。
2. **五個孤兒 i18n key**:`ai-thinking-step2-single`、`ai-thinking-step2-pair`、`ai-thinking-step3-cross`、`ai-thinking-step3-validate`、`ai-thinking-step4-fallback`(`ai-thinking-step1` 仍在 `startAI()` 使用,保留)。

另外提醒一點:`simulateScoreGain()` 現在也變成沒有呼叫端的孤兒函式了(它原本被刪掉的四步驟流程使用,`hasLegalContinuation` 這次刪除前也沒用到它)。要不要一併刪除?

語法已用 `new Function()` 重新驗證通過。
把simulateScoreGain()刪掉,同時檢查下是否還存在孤兒函式,如果有一並刪除。

代码: 全选

Now let's do a systematic orphan-function scan across the whole file.Confirmed unused. Now check getMovePieces, getAllValidSingleMoves, getAllValidSingleMovesOnBoard context.Now let's do all the deletions. First let's confirm exact list to delete, and check `resetLifeTracking` and `getAllValidMoves` are kept (already confirmed used). Let's now perform deletions one by one.Now delete `getAllValidSingleMovesOnBoard` and `getAllValidSingleMoves`:Now delete `placeAISingleMove` and `getMovePieces`:Now delete `enumerateEdgeMethods`:Now delete `scheduleLifeTrackingUpdate` and `ensureLifeStatsReady`:Now delete `simulateScoringTriGolden`:Good, `getTreeDistance` is still used elsewhere — keep it. Now delete `simulateScoringTriGolden` only:Now delete `detectAIPresetType` and `updateAIParamInputsState`:Now delete the pure geometry orphans: `segmentsCrossStrict`, `pointStrictlyInConvexPolygon`, `shapesOverlap`:Good, still used elsewhere. Now `shapesOverlap`:Now `aiMaybeYield`:aiYield is still used elsewhere (in startAI etc). Delete only aiMaybeYield:到目前為止我已經確認並刪除了以下孤兒函式(全檔搜尋後確認除自身定義外沒有任何呼叫端):

**本輪新刪除的:**
1. `simulateScoreGain()` — 你要求的
2. `createSearchStateForBoard()` — 因刪除 `getAllValidSingleMovesOnBoard()` 而連帶變成孤兒
3. `getAllValidSingleMovesOnBoard()`、`getAllValidSingleMoves()`
4. `placeAISingleMove()`、`getMovePieces()`
5. `enumerateEdgeMethods()`(註解本身就寫「正式搜索不再依賴這個包裝」)
6. `scheduleLifeTrackingUpdate()`、`ensureLifeStatsReady()`
7. `simulateScoringTriGolden()`
8. `detectAIPresetType()`、`updateAIParamInputsState()`
9. `exactOrientation()`、`segmentsCrossStrict()`、`pointStrictlyInConvexPolygon()`(後兩者互相依賴 `exactOrientation`,三個一起變成孤兒)
10. `shapesOverlap()`
11. `aiMaybeYield()`

**已確認保留、不是孤兒的**(容易誤判,特別列出):
- `resetLifeTracking()` — 仍被呼叫(line ~8856 附近)
- `getAllValidMoves()` — 仍被 `aiTriggerArbitrationSuccess()` 等使用
- `getTreeDistance()` — 仍被其他計分邏輯使用
- `aiYield()` / `isImaginaryPartZero()` — 仍有其他呼叫端

**尚未完成的部分:** 由於工具呼叫次數已達本回覆上限,我還沒有:
- 對整份檔案重新跑一次完整的孤兒函式掃描(確認上述刪除沒有製造新的連鎖孤兒,例如 `checkSATCollision`、`getSAT` 等是否還有其他呼叫端)
- 用 `new Function()` 做最終語法驗證
- 把修改後的檔案複製到 `/mnt/user-data/outputs` 並重新 present 給你

檔案目前只存在 `/home/claude/trigolden_game2666.html`(工作目錄),**還沒有更新到你可以下載的輸出檔案**。請回覆「繼續」,我會接著做完整掃描、語法驗證,並把最終檔案交給你。

代码: 全选

All these are used via HTML `onclick` attributes (count ≥2 in the full file) — not orphans. Let's double check chained dependencies for what was just deleted, to make sure nothing else became newly orphaned.All still in use elsewhere (>1). Now full syntax validation:Syntax passes and no new orphans were introduced. Let's copy to outputs and present.已完成,總共從 10967 行清到 10429 行,減少 538 行。

**這次連鎖刪除的孤兒函式清單:**
- `simulateScoreGain()`
- `createSearchStateForBoard()`(因 `getAllValidSingleMovesOnBoard` 被刪而連帶孤立)
- `getAllValidSingleMovesOnBoard()`、`getAllValidSingleMoves()`
- `placeAISingleMove()`、`getMovePieces()`
- `enumerateEdgeMethods()`
- `scheduleLifeTrackingUpdate()`、`ensureLifeStatsReady()`
- `simulateScoringTriGolden()`
- `detectAIPresetType()`、`updateAIParamInputsState()`
- `exactOrientation()`、`segmentsCrossStrict()`、`pointStrictlyInConvexPolygon()`
- `shapesOverlap()`
- `aiMaybeYield()`

**驗證過程:**
1. 用正則掃描整份 `<script>` 區塊裡所有 `function`/箭頭函式宣告,統計每個名稱在原始碼中出現次數,`count ≤ 1`(只有定義、沒被呼叫)列為孤兒候選。
2. 逐一人工檢視候選,排除誤判——例如 `arbNav`、`selectTile`、`zoomBoard` 等一大批函式雖然 `<script>` 內只出現一次,但其實是被 HTML `onclick="..."` 屬性呼叫,這類我用整份檔案(含 HTML 標籤)重新計數確認 ≥2 次後排除,沒有誤刪。
3. 刪除後再次確認被刪函式內部依賴的其他函式(如 `checkSATCollision`、`getSAT`、`getTreeDistance`、`aiYield`、`isImaginaryPartZero`)是否仍有其他呼叫端,避免遺漏連鎖孤兒或誤刪仍在用的共用函式。
4. 最後重新跑一次全域孤兒掃描,確認沒有新孤兒殘留,並用 `new Function()` 驗證整個 `<script>` 語法正確。
附件
trigolden_game2667.html.7z
(64.15 KiB) 已下载 1 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀一枝独秀
帖子: 6605
注册时间: 2022年 11月 18日 17:36
联系:

Re: 一個新的遊戲創意

帖子 ejsoon »

在導出svg的「數字」選項中,數字已經碰到了棋子邊緣,把數字字體改小一點,使其不觸碰棋子邊緣。

代码: 全选

<?xml version="1.0" standalone="no"?>
<svg id="etanitrigolden" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" width="480" height="480" viewBox="0 0 480 480" preserveAspectRatio="xMidYMid meet"><style type="text/css" data-bt-font="Techfont">@font-face {
    font-family: 'Techfont';
    src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}</style>
        <defs>
          <g id="tile0" fill="#ffc2eb">
            <path d="M -61.687,17.119 L -14.563,-17.119 L 21.437,-17.119 L 32.562,17.119 Z"/>
          </g>
          <g id="tile1" fill="#ffc2eb">
            <path d="M -76.249,24.775 L -0.001,-30.623 L 29.124,-9.463 L 40.249,24.775 Z"/>
          </g>
          <g id="tile2" fill="#ffc2eb"><path d="M -55.468,19.14 L 2.781,-23.18 L 31.906,-2.02 L 2.781,19.14 Z"/></g>
          <g id="tile3" fill="#96ffbe">
            <path d="M -61.687,17.119 L -14.563,-17.119 L 21.437,-17.119 L 32.562,17.119 Z"/>
          </g>
          <g id="tile4" fill="#96ffbe">
            <path d="M -76.249,24.775 L -0.001,-30.623 L 29.124,-9.463 L 40.249,24.775 Z"/>
          </g>
          <g id="tile5" fill="#96ffbe"><path d="M -55.468,19.14 L 2.781,-23.18 L 31.906,-2.02 L 2.781,19.14 Z"/></g>
        </defs>
        <g class="etdrop" stroke="#777" stroke-width="2" stroke-linejoin="round" transform="translate(298.989,180.394) scale(0.565)" id="etanidrop"><use href="#tile0" transform="translate(0,0) scale(-1,1) rotate(320)"/><use href="#tile4" transform="translate(41.844,-27.211) scale(1,1) rotate(148.001)"/><use href="#tile3" transform="translate(-26.839,-33.694) scale(1,1) rotate(148.001)"/><use href="#tile1" transform="translate(-61.389,-61.505) scale(1,1) rotate(184.001)"/><use href="#tile2" transform="translate(-67.635,-12.058) scale(-1,1) rotate(176)"/><use href="#tile5" transform="translate(-120.476,-11.703) scale(-1,1) rotate(356)"/><use href="#tile5" transform="translate(-46.028,22.522) scale(1,1) rotate(292.001)"/><use href="#tile1" transform="translate(-137.861,-60.99) scale(1,1) rotate(76.001)"/><use href="#tile2" transform="translate(-123.147,26.484) scale(1,1) rotate(184)"/><use href="#tile5" transform="translate(-105.004,55.518) scale(-1,1) rotate(68)"/><use href="#tile3" transform="translate(-154.965,48.747) scale(1,1) rotate(40.001)"/><use href="#tile1" transform="translate(-90.436,95.815) scale(1,1) rotate(292.002)"/><use href="#tile0" transform="translate(97.054,-52.023) scale(-1,1) rotate(248)"/><use href="#tile4" transform="translate(-138.515,107.802) scale(-1,1) rotate(320)"/><use href="#tile3" transform="translate(128.799,-39.197) scale(1,1) rotate(292.001)"/><use href="#tile0" transform="translate(-190.69,74.016) scale(-1,1) rotate(140)"/><use href="#tile1" transform="translate(-142.787,168.9) scale(-1,1) rotate(140)"/><use href="#tile5" transform="translate(-139.329,214.414) scale(-1,1) rotate(320)"/><use href="#tile5" transform="translate(-94.59,195.08) scale(1,1) rotate(184)"/><use href="#tile2" transform="translate(133.727,-85.031) scale(-1,1) rotate(140)"/><use href="#tile0" transform="translate(160.544,-26.371) scale(-1,1) rotate(248)"/><use href="#tile4" transform="translate(176.554,-86.434) scale(1,1) rotate(220.001)"/><use href="#tile4" transform="translate(212.841,-28.363) scale(-1,1) rotate(356)"/><use href="#tile2" transform="translate(-39.361,160.569) scale(1,1) rotate(4)"/><use href="#tile2" transform="translate(-42.313,202.786) scale(1,1) rotate(4)"/><use href="#tile3" transform="translate(-249.101,25.003) scale(-1,1) rotate(320)"/><use href="#tile5" transform="translate(-216.96,96.668) scale(-1,1) rotate(176)"/><use href="#tile0" transform="translate(-217.833,-4.627) scale(1,1) rotate(148.001)"/><use href="#tile1" transform="translate(-282.912,-13.362) scale(-1,1) rotate(176)"/><use href="#tile3" transform="translate(-320.997,9.37) scale(-1,1) rotate(140)"/><use href="#tile4" transform="translate(-315.369,-65.302) scale(-1,1) rotate(356)"/><use href="#tile2" transform="translate(-375.147,-36.067) scale(1,1) rotate(256.001)"/><use href="#tile1" transform="translate(-345.293,43.671) scale(-1,1) rotate(284.001)"/><use href="#tile4" transform="translate(-299.351,62.233) scale(1,1) rotate(328.001)"/><use href="#tile5" transform="translate(54.15,45.437) scale(1,1) rotate(76.001)"/><use href="#tile1" transform="translate(-182.157,215.816) scale(-1,1) rotate(248)"/><use href="#tile0" transform="translate(-122.23,283.001) scale(1,1) rotate(76.001)"/><use href="#tile5" transform="translate(-83.402,283.691) scale(1,1) rotate(220)"/><use href="#tile5" transform="translate(-24.169,231.822) scale(-1,1) rotate(248)"/><use href="#tile0" transform="translate(-28.927,91.109) scale(-1,1) rotate(104.001)"/><use href="#tile2" transform="translate(32.141,71.665) scale(-1,1) rotate(176)"/><use href="#tile3" transform="translate(-109.541,325.538) scale(1,1) rotate(328.001)"/><use href="#tile4" transform="translate(-159.357,307.266) scale(1,1) rotate(112.002)"/><use href="#tile2" transform="translate(-6.027,260.857) scale(1,1) rotate(4)"/><use href="#tile0" transform="translate(20.944,222.345) scale(1,1) rotate(184)"/><use href="#tile3" transform="translate(52.388,190.223) scale(1,1) rotate(4)"/><use href="#tile4" transform="translate(49.526,120.952) scale(1,1) rotate(256.001)"/><use href="#tile1" transform="translate(-238.896,-65.817) scale(-1,1) rotate(176)"/><use href="#tile2" transform="translate(-344.919,-100.092) scale(-1,1) rotate(176)"/><use href="#tile3" transform="translate(-318.65,-122.744) scale(-1,1) rotate(140)"/><use href="#tile3" transform="translate(-399.069,-145.529) scale(1,1) rotate(220.001)"/><use href="#tile1" transform="translate(-418.602,-99.597) scale(-1,1) rotate(248)"/><use href="#tile0" transform="translate(-436.912,-166.11) scale(-1,1) rotate(248)"/><use href="#tile4" transform="translate(-57.933,-110.934) scale(-1,1) rotate(356)"/><text x="0" y="0" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">1</text><text x="41.844" y="-27.211" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">2</text><text x="-26.839" y="-33.694" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">3</text><text x="-61.389" y="-61.505" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">4</text><text x="-67.635" y="-12.058" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">5</text><text x="-120.476" y="-11.703" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">6</text><text x="-46.028" y="22.522" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">7</text><text x="-137.861" y="-60.99" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">8</text><text x="-123.147" y="26.484" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">9</text><text x="-105.004" y="55.518" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">10</text><text x="-154.965" y="48.747" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">11</text><text x="-90.436" y="95.815" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">12</text><text x="97.054" y="-52.023" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">13</text><text x="-138.515" y="107.802" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">14</text><text x="128.799" y="-39.197" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">15</text><text x="-190.69" y="74.016" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">16</text><text x="-142.787" y="168.9" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">17</text><text x="-139.329" y="214.414" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">18</text><text x="-94.59" y="195.08" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">19</text><text x="133.727" y="-85.031" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">20</text><text x="160.544" y="-26.371" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">21</text><text x="176.554" y="-86.434" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">22</text><text x="212.841" y="-28.363" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">23</text><text x="-39.361" y="160.569" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">24</text><text x="-42.313" y="202.786" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">25</text><text x="-249.101" y="25.003" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">26</text><text x="-216.96" y="96.668" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">27</text><text x="-217.833" y="-4.627" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">28</text><text x="-282.912" y="-13.362" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">29</text><text x="-320.997" y="9.37" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">30</text><text x="-315.369" y="-65.302" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">31</text><text x="-375.147" y="-36.067" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">32</text><text x="-345.293" y="43.671" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">33</text><text x="-299.351" y="62.233" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">34</text><text x="54.15" y="45.437" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">35</text><text x="-182.157" y="215.816" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">36</text><text x="-122.23" y="283.001" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">37</text><text x="-83.402" y="283.691" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">38</text><text x="-24.169" y="231.822" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">39</text><text x="-28.927" y="91.109" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">40</text><text x="32.141" y="71.665" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">41</text><text x="-109.541" y="325.538" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">42</text><text x="-159.357" y="307.266" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">43</text><text x="-6.027" y="260.857" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">44</text><text x="20.944" y="222.345" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">45</text><text x="52.388" y="190.223" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">46</text><text x="49.526" y="120.952" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">47</text><text x="-238.896" y="-65.817" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">48</text><text x="-344.919" y="-100.092" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">49</text><text x="-318.65" y="-122.744" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">50</text><text x="-399.069" y="-145.529" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">51</text><text x="-418.602" y="-99.597" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">52</text><text x="-436.912" y="-166.11" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">53</text><text x="-57.933" y="-110.934" stroke="none" fill="#333" font-size="24" font-family="Techfont" text-anchor="middle" dominant-baseline="central">54</text></g>
      </svg>
SVG preview

claude sonnet 5 (low)

代码: 全选

Now let's find where the piece number text is drawn in the SVG export (likely something creating a `<text>` element with the piece index, with a font-size).This confirms `addDownloadPieceNumbers` is the only place drawing the piece number labels. Let's reduce the font-size from 24 to 18.找到了 `addDownloadPieceNumbers()`(第 9066 行附近),這是 SVG 匯出「數字」模式唯一畫出棋子編號文字的地方。

改動:把該處 `text` 元素的 `'font-size'` 從 `'24'` 調小為 `'18'`(第 9093 行)。

確認過所有棋子在匯出時大小一致(`getDownloadPieceCenter()` 只做平移、沒有依層級縮放),所以單一固定字體大小的調整對整個棋盤是一致有效的,不會有部分棋子仍然太大的情況。已用 `new Function()` 驗證語法通過。
改完之後:

代码: 全选

<?xml version="1.0" standalone="no"?>
<svg id="etanitrigolden" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" width="480" height="480" viewBox="0 0 480 480" preserveAspectRatio="xMidYMid meet"><style type="text/css" data-bt-font="Techfont">@font-face {
    font-family: 'Techfont';
    src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}</style>
        <defs>
          <g id="tile0" fill="#ffc2eb">
            <path d="M -61.687,17.119 L -14.563,-17.119 L 21.437,-17.119 L 32.562,17.119 Z"/>
          </g>
          <g id="tile1" fill="#ffc2eb">
            <path d="M -76.249,24.775 L -0.001,-30.623 L 29.124,-9.463 L 40.249,24.775 Z"/>
          </g>
          <g id="tile2" fill="#ffc2eb"><path d="M -55.468,19.14 L 2.781,-23.18 L 31.906,-2.02 L 2.781,19.14 Z"/></g>
          <g id="tile3" fill="#96ffbe">
            <path d="M -61.687,17.119 L -14.563,-17.119 L 21.437,-17.119 L 32.562,17.119 Z"/>
          </g>
          <g id="tile4" fill="#96ffbe">
            <path d="M -76.249,24.775 L -0.001,-30.623 L 29.124,-9.463 L 40.249,24.775 Z"/>
          </g>
          <g id="tile5" fill="#96ffbe"><path d="M -55.468,19.14 L 2.781,-23.18 L 31.906,-2.02 L 2.781,19.14 Z"/></g>
        </defs>
        <g class="etdrop" stroke="#777" stroke-width="2" stroke-linejoin="round" transform="translate(284.09,195.402) scale(0.526)" id="etanidrop"><use href="#tile0" transform="translate(0,0) scale(-1,1) rotate(320)"/><use href="#tile4" transform="translate(41.844,-27.211) scale(1,1) rotate(148.001)"/><use href="#tile3" transform="translate(-26.839,-33.694) scale(1,1) rotate(148.001)"/><use href="#tile1" transform="translate(-61.389,-61.505) scale(1,1) rotate(184.001)"/><use href="#tile2" transform="translate(-67.635,-12.058) scale(-1,1) rotate(176)"/><use href="#tile5" transform="translate(-120.476,-11.703) scale(-1,1) rotate(356)"/><use href="#tile5" transform="translate(-46.028,22.522) scale(1,1) rotate(292.001)"/><use href="#tile1" transform="translate(-137.861,-60.99) scale(1,1) rotate(76.001)"/><use href="#tile2" transform="translate(-123.147,26.484) scale(1,1) rotate(184)"/><use href="#tile5" transform="translate(-105.004,55.518) scale(-1,1) rotate(68)"/><use href="#tile3" transform="translate(-154.965,48.747) scale(1,1) rotate(40.001)"/><use href="#tile1" transform="translate(-90.436,95.815) scale(1,1) rotate(292.002)"/><use href="#tile0" transform="translate(97.054,-52.023) scale(-1,1) rotate(248)"/><use href="#tile4" transform="translate(-138.515,107.802) scale(-1,1) rotate(320)"/><use href="#tile3" transform="translate(128.799,-39.197) scale(1,1) rotate(292.001)"/><use href="#tile0" transform="translate(-190.69,74.016) scale(-1,1) rotate(140)"/><use href="#tile1" transform="translate(-142.787,168.9) scale(-1,1) rotate(140)"/><use href="#tile5" transform="translate(-139.329,214.414) scale(-1,1) rotate(320)"/><use href="#tile5" transform="translate(-94.59,195.08) scale(1,1) rotate(184)"/><use href="#tile2" transform="translate(133.727,-85.031) scale(-1,1) rotate(140)"/><use href="#tile0" transform="translate(160.544,-26.371) scale(-1,1) rotate(248)"/><use href="#tile4" transform="translate(176.554,-86.434) scale(1,1) rotate(220.001)"/><use href="#tile4" transform="translate(212.841,-28.363) scale(-1,1) rotate(356)"/><use href="#tile2" transform="translate(-39.361,160.569) scale(1,1) rotate(4)"/><use href="#tile2" transform="translate(-42.313,202.786) scale(1,1) rotate(4)"/><use href="#tile3" transform="translate(-249.101,25.003) scale(-1,1) rotate(320)"/><use href="#tile5" transform="translate(-216.96,96.668) scale(-1,1) rotate(176)"/><use href="#tile0" transform="translate(-217.833,-4.627) scale(1,1) rotate(148.001)"/><use href="#tile1" transform="translate(-282.912,-13.362) scale(-1,1) rotate(176)"/><use href="#tile3" transform="translate(-320.997,9.37) scale(-1,1) rotate(140)"/><use href="#tile4" transform="translate(-315.369,-65.302) scale(-1,1) rotate(356)"/><use href="#tile2" transform="translate(-375.147,-36.067) scale(1,1) rotate(256.001)"/><use href="#tile1" transform="translate(-345.293,43.671) scale(-1,1) rotate(284.001)"/><use href="#tile4" transform="translate(-299.351,62.233) scale(1,1) rotate(328.001)"/><use href="#tile5" transform="translate(54.15,45.437) scale(1,1) rotate(76.001)"/><use href="#tile1" transform="translate(-182.157,215.816) scale(-1,1) rotate(248)"/><use href="#tile0" transform="translate(-122.23,283.001) scale(1,1) rotate(76.001)"/><use href="#tile5" transform="translate(-83.402,283.691) scale(1,1) rotate(220)"/><use href="#tile5" transform="translate(-24.169,231.822) scale(-1,1) rotate(248)"/><use href="#tile0" transform="translate(-28.927,91.109) scale(-1,1) rotate(104.001)"/><use href="#tile2" transform="translate(32.141,71.665) scale(-1,1) rotate(176)"/><use href="#tile3" transform="translate(-109.541,325.538) scale(1,1) rotate(328.001)"/><use href="#tile4" transform="translate(-159.357,307.266) scale(1,1) rotate(112.002)"/><use href="#tile2" transform="translate(-6.027,260.857) scale(1,1) rotate(4)"/><use href="#tile0" transform="translate(20.944,222.345) scale(1,1) rotate(184)"/><use href="#tile3" transform="translate(52.388,190.223) scale(1,1) rotate(4)"/><use href="#tile4" transform="translate(49.526,120.952) scale(1,1) rotate(256.001)"/><use href="#tile1" transform="translate(-238.896,-65.817) scale(-1,1) rotate(176)"/><use href="#tile2" transform="translate(-344.919,-100.092) scale(-1,1) rotate(176)"/><use href="#tile3" transform="translate(-318.65,-122.744) scale(-1,1) rotate(140)"/><use href="#tile3" transform="translate(-399.069,-145.529) scale(1,1) rotate(220.001)"/><use href="#tile1" transform="translate(-418.602,-99.597) scale(-1,1) rotate(248)"/><use href="#tile0" transform="translate(-436.912,-166.11) scale(-1,1) rotate(248)"/><use href="#tile4" transform="translate(-57.933,-110.934) scale(-1,1) rotate(356)"/><text x="0" y="0" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">1</text><text x="41.844" y="-27.211" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">2</text><text x="-26.839" y="-33.694" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">3</text><text x="-61.389" y="-61.505" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">4</text><text x="-67.635" y="-12.058" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">5</text><text x="-120.476" y="-11.703" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">6</text><text x="-46.028" y="22.522" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">7</text><text x="-137.861" y="-60.99" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">8</text><text x="-123.147" y="26.484" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">9</text><text x="-105.004" y="55.518" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">10</text><text x="-154.965" y="48.747" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">11</text><text x="-90.436" y="95.815" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">12</text><text x="97.054" y="-52.023" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">13</text><text x="-138.515" y="107.802" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">14</text><text x="128.799" y="-39.197" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">15</text><text x="-190.69" y="74.016" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">16</text><text x="-142.787" y="168.9" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">17</text><text x="-139.329" y="214.414" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">18</text><text x="-94.59" y="195.08" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">19</text><text x="133.727" y="-85.031" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">20</text><text x="160.544" y="-26.371" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">21</text><text x="176.554" y="-86.434" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">22</text><text x="212.841" y="-28.363" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">23</text><text x="-39.361" y="160.569" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">24</text><text x="-42.313" y="202.786" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">25</text><text x="-249.101" y="25.003" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">26</text><text x="-216.96" y="96.668" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">27</text><text x="-217.833" y="-4.627" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">28</text><text x="-282.912" y="-13.362" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">29</text><text x="-320.997" y="9.37" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">30</text><text x="-315.369" y="-65.302" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">31</text><text x="-375.147" y="-36.067" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">32</text><text x="-345.293" y="43.671" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">33</text><text x="-299.351" y="62.233" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">34</text><text x="54.15" y="45.437" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">35</text><text x="-182.157" y="215.816" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">36</text><text x="-122.23" y="283.001" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">37</text><text x="-83.402" y="283.691" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">38</text><text x="-24.169" y="231.822" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">39</text><text x="-28.927" y="91.109" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">40</text><text x="32.141" y="71.665" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">41</text><text x="-109.541" y="325.538" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">42</text><text x="-159.357" y="307.266" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">43</text><text x="-6.027" y="260.857" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">44</text><text x="20.944" y="222.345" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">45</text><text x="52.388" y="190.223" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">46</text><text x="49.526" y="120.952" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">47</text><text x="-238.896" y="-65.817" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">48</text><text x="-344.919" y="-100.092" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">49</text><text x="-318.65" y="-122.744" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">50</text><text x="-399.069" y="-145.529" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">51</text><text x="-418.602" y="-99.597" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">52</text><text x="-436.912" y="-166.11" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">53</text><text x="-57.933" y="-110.934" stroke="none" fill="#333" font-size="18" font-family="Techfont" text-anchor="middle" dominant-baseline="central">54</text></g>
      </svg>
SVG preview
附件
trigolden_game2668.html.7z
(64.15 KiB) 尚未被下载
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复
  • 相似主题
    回复总数
    阅读次数
    最新帖子

在线用户

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