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

分享和讨论Javascript相关的话题
回复
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

附件
ejcees20260401moblie99977741.7z
(44.52 KiB) 已下载 7 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

gemini逐步搞它:

代码: 全选

在上傳的文件中,結合importExportedText和document.getElementById('tool-exp-svg').addEventListener('click', () => {...})這兩個函式,做一個獨立的函式ejceesExpSvg(text)。

首先,在importExportedText函式中,它會接受各種格式的棋譜文本,把它轉換為historyFEN,把這段提取出來,最後把轉換結果console.log。

做一個html+js頁面,來測試ejceesExpSvg(text)函式。裡面需要一個textarae和一個button,以及一個div用來輸出svg。當按下button時執行這個函式。
附件
tmp2.txt
(36.04 KiB) 已下载 8 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

gemini thinking:

代码: 全选

<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>象棋 SVG 轉換測試</title>
    <style>
        body { font-family: sans-serif; padding: 20px; background: #f0f0f0; }
        textarea { width: 100%; height: 150px; margin-bottom: 10px; }
        #svg-container { margin-top: 20px; background: white; padding: 10px; border: 1px solid #ccc; min-height: 400px; }
        .controls { margin-bottom: 20px; }
    </style>
</head>
<body>

    <h2>ejceesExpSvg 測試工具</h2>
    <textarea id="inputText" placeholder="請貼入 FEN、JSON 或 著法文本 (如: 1. 炮二平五 馬8進7...)"></textarea>
    
    <div class="controls">
        <button onclick="handleConvert()">執行 ejceesExpSvg</button>
    </div>

    <h3>輸出結果:</h3>
    <div id="svg-container"></div>

    <script>
        // --- 模擬原始環境中缺少的輔助變數與函式 ---
        let historyFEN = null;
        let currentBranch = [];
        const moveInterval = 1.5; // [cite: 173]
        const moveSpeed = 0.5;    // 

        // 模擬基礎 SVG 模板 (對應原文中的 .ejceespb)
        const SVG_TEMPLATE = `
        <svg class="ejceespb" viewBox="0 0 432 480" xmlns="http://www.w3.org/2000/svg" style="width:400px">
            <rect width="432" height="480" fill="#f9d3a1" />
            <g class="etboard"></g>
            <g class="etdrop"></g>
        </svg>`;

        // 模擬字串編碼 [cite: 155]
        function b64EncodeUnicode(str) {
            return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => String.fromCharCode('0x' + p1)));
        }

        // 模擬座標轉換 
        function getVisualCoords(x, y) {
            return { x: x, y: y };
        }

        // --- 核心函式:ejceesExpSvg ---
        function ejceesExpSvg(text) {
            console.log("開始解析文本...");
            let movesStr = text.trim();

            // 1. 文本標準化 
            movesStr = movesStr.replace(/车/g, '車').replace(/马/g, '馬').replace(/帅/g, '帥').replace(/将/g, '將').replace(/后/g, '後').replace(/进/g, '進');

            // 2. 解析邏輯 (簡化版 importExportedText)
            let parsedHistory = null;

            if (movesStr.startsWith('{')) {
                // 處理 JSON 格式 [cite: 10]
                try {
                    const data = JSON.parse(movesStr);
                    function expand(node, parentFen) {
                        let newNode = {
                            fen: node.fen || parentFen,
                            move: node.m || null,
                            lastMove: node.lastMove || null, // 假設已有座標
                            c: node.c || "",
                            v: []
                        };
                        if (node.v) newNode.v = node.v.map(child => expand(child, newNode.fen));
                        return newNode;
                    }
                    parsedHistory = expand(data, data.fen); [cite: 20]
                } catch (e) { console.error("JSON 解析失敗"); }
            } else {
                // 處理純文本或 FEN [cite: 49, 111]
                // 此處簡化為建立一個基礎結構
                parsedHistory = { fen: movesStr, v: [], c: "啟始位置" };
            }

            console.log("解析結果 (historyFEN):", parsedHistory);
            historyFEN = parsedHistory;

            // 3. SVG 生成邏輯 [cite: 140]
            const parser = new DOMParser();
            const svgDoc = parser.parseFromString(SVG_TEMPLATE, 'image/svg+xml');
            const cloneSvg = svgDoc.querySelector('svg');
            
            cloneSvg.setAttribute('class', 'ejceespbanimate'); [cite: 158]
            const etboardClone = cloneSvg.querySelector('.etboard');
            const etdropClone = cloneSvg.querySelector('.etdrop');

            // 遍歷路徑節點 [cite: 168-172]
            let pathNodes = [];
            let curr = historyFEN;
            while (curr && curr.v && curr.v.length > 0) {
                curr = curr.v[0]; // 預設走第一條分支
                pathNodes.push(curr);
            }

            const movedPieceIds = new Set();
            const initialCoords = {};

            // 生成動畫指令 [cite: 174-188]
            pathNodes.forEach((node, i) => {
                let stepNum = i + 1;
                let moveData = node.lastMove; 
                if (!moveData) return;

                let pieceId = "p" + i; // 模擬 pieceId
                movedPieceIds.add(pieceId);
                
                let fromStr = `${moveData.startX * 48 + 24},${moveData.startY * 48 + 24}`;
                let toStr = `${moveData.endX * 48 + 24},${moveData.endY * 48 + 24}`;

                // 建立 SMIL 動畫 
                const animT = document.createElementNS('http://www.w3.org/2000/svg', 'animateTransform');
                animT.setAttribute('id', `step${stepNum}`);
                animT.setAttribute('attributeName', 'transform');
                animT.setAttribute('type', 'translate');
                animT.setAttribute('from', fromStr);
                animT.setAttribute('to', toStr);
                animT.setAttribute('dur', `${moveSpeed}s`);
                animT.setAttribute('fill', 'freeze');
                animT.setAttribute('data-comment', b64EncodeUnicode(node.c || ""));

                // 將動畫加入模擬的棋子中 (此處省略具體棋子創建,僅展示邏輯)
                console.log(`生成第 ${stepNum} 步動畫: ${fromStr} -> ${toStr}`);
            });

            // 序列化輸出 [cite: 201]
            const svgString = new XMLSerializer().serializeToString(cloneSvg);
            return svgString;
        }

        function handleConvert() {
            const text = document.getElementById('inputText').value;
            const container = document.getElementById('svg-container');
            
            // 執行函式
            const resultSvg = ejceesExpSvg(text);
            
            // 渲染到頁面
            container.innerHTML = resultSvg;
        }
    </script>
</body>
</html>
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

察看上傳的文件,繼續對其修改。

一,在ejceesExpSvg中,缺少的變量都寫到其內部的最前面。

二,在「 return movesStr;」下方是插入節點位置的邏輯,現在去掉這個邏輯,改為如果前面提取到fen局面,則以這個局面為根節點,如果沒有提取到,則以INITIAL_FEN為根節點。

僅需給出所要修改的地方。
附件
ejceeseExpSvg202604011309.txt
(21.66 KiB) 已下载 10 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

代码: 全选

function ejceesExpSvg(text) {
    // --- 內部使用的變量定義 ---
    let historyFEN = null;
    let currentBranch = [];
    let currentStepIndex = 0;
    let isRotateEnabled = false;
    let isFlipEnabled = false;
    let isRangeMode = false;
    let rangeStart = null;
    let rangeEnd = null;
    let isExportTextMode = false;
    let isEditingComment = false;
    let isEditMode = false;
    let isAutoPlaying = false;
    const INITIAL_FEN = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1";
    const moveInterval = 1.5;
    const moveSpeed = 0.5;
    // ------------------------

    // ... 隨後是 importExportedText 函式定義
根節點逻辑修改部分

在 importExportedText 函式內部,找到 return movesStr;(約第 808 行),將其及其下方的「2. Identify the Attach Point」邏輯刪除,替換為以下以 importedFen 或 INITIAL_FEN 為根節點的邏輯 :

代码: 全选

// 刪除原有的 return movesStr; 及其下方的節點查找邏輯
        // 2. Identify the Attach Point (Parent Node)
        // 改為:以提取到的局面或初始局面作為唯一的根節點
        let attachNode = {
            fen: importedFen || INITIAL_FEN,
            move: null,
            lastMove: null,
            c: "",
            v: []
        };
        
        historyFEN = attachNode;
        let attachIndex = 0;
        currentBranch = [];
        currentStepIndex = 0; // 重置當前步數索引

        // 3. Process move sequence text (後續邏輯保持不變,將從此根節點開始解析著法)
        // ...
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

察看上傳的文件,繼續對其修改。

在「return cloneSvg;」之前,對
jumpToStep(exportStart);
const cloneSvg = document.querySelector('.ejceespb').cloneNode(true);
進行更改。其意義:為先用jumpToStep改變當前局面的svg,然後再賦值給cloneSvg。

在jumpToStep()函式中,改變當前局面的svg要用到loadFEN()和applyLastMoveVisuals(),也要對其更改。

之前的svg是直接寫在頁面上,現在由於要整合進一個js的function中,所以svg不會出現在html頁面上。

僅需給出所要修改的地方。
附件
ejceeseExpSvg202604011420.txt
(54.46 KiB) 已下载 8 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

在某個html頁面中,有一個svg元素,當我要對其克隆操作時,我會用documnet.getElement...,現在我把這個svg元素的內容都轉成了js的一個字符串變量,則如何做到像之前documnet.getElement.clone的效果?

代码: 全选

// 1. 你的 SVG 字串變量
const svgString = '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><circle cx="50" cy="50" r="40" fill="red" /></svg>';

// 2. 初始化 DOMParser
const parser = new DOMParser();

// 3. 將字串解析為 XML (SVG 屬於 XML 格式)
const doc = parser.parseFromString(svgString, 'image/svg+xml');

// 4. 取得解析後的 SVG 元素
// parseFromString 會回傳一個完整的 document,我們需要取出它的 root 元素
const clonedSvg = doc.documentElement;

// 現在 clonedSvg 就是一個真正的 DOM 元素,你可以像以前一樣操作它
clonedSvg.style.border = "1px solid black";
document.body.appendChild(clonedSvg);
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

出現bug:某局面編輯失敗:

3a1k3/4a2R1/9/p1p4c1/8p/2P6/P5P1P/4C4/7r1/2BAKAB2 w - - 0 1
附件
Ejcees(3)(1).jpg
Ejcees(3)(1).jpg (42.18 KiB) 查看 204 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

附件
ejcees20260402editbug99977742.7z
(44.5 KiB) 已下载 8 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

附件
ejcees20260402checkbugagain99977744.7z
(44.5 KiB) 已下载 9 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

附件
ejcees20260403newrotate99977745.7z
(44.53 KiB) 已下载 6 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

增加功能:當進入編輯狀態、導出文本狀態、更改移動速度和間隔時,點擊ctrl+enter,其效果等同於點擊confirm。只需給出所要修改的地方,所有的代碼和注釋都要使用英文。

https://ejsoon.vip/wp-content/uploads/2 ... Enter.html
附件
CtrlEnter.7z
(100.53 KiB) 已下载 4 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

當點擊tool-exp-txt時,會出現以下四個切換按鈕。
<div class="exp-fmt-btn active" data-fmt="cn" data-odd="0">Chinese Notation</div>
<div class="exp-fmt-btn" data-fmt="en" data-odd="0">English Notation</div>
<div class="exp-fmt-btn" data-fmt="engine" data-odd="0">Engine Format</div>
<div class="exp-fmt-btn" data-fmt="url" data-odd="0">URL Parameter</div>

現在要增加功能,當點擊其中一個時,程式將記住這個選擇,下次點擊tool-exp-txt時,上回記住的按鈕將會是active。
整個renderExportTextUI()的代碼如下,只需給出所要修改的地方,所有的代碼和注釋都要使用英文。

代码: 全选

        // --- Refactored UI for Export Text ---
        function renderExportTextUI() {
            const recordContainer = document.querySelector('.ejceesrecord');
            const commentDiv = document.querySelector('.ejceescomment');
            const btnDiv = document.querySelector('.ejceestextbtn');

            const cnText = generateExportText(false);
            const cnTextWidthComment = generateCnTextWidthComment();
            const enText = generateExportText(true);
            const urlText = generateUrlHash();

            // Generate UCCI Engine Format String
            let path = getGamePath();
            let engineText = "position fen " + path.fen;
            let currNode = path;
            if (currNode && currNode.v && currNode.v.length > 0) {
                engineText += " moves";
            }
            while (currNode && currNode.v && currNode.v.length > 0) {
                currNode = currNode.v[0];
                let dc = currNode.lastMove;
                if (dc) {
                    engineText += " " + String.fromCharCode(97 + dc.startX) + (9 - dc.startY) + String.fromCharCode(97 + dc.endX) + (9 - dc.endY);
                }
            }

            // Create readonly textarea
            recordContainer.innerHTML = `<textarea class="ejceescomment-edit" id="export-textarea" style="width:100%; height:100%; resize:none; border:none; outline:none; background:#2a2a2a; color:#fff; padding:8px; font-family:monospace; font-size:14px; white-space:pre-wrap; overflow:auto;"></textarea>`;

            const textarea = document.getElementById('export-textarea');

            // Set up the 4 toggle buttons, distributed evenly vertically
            commentDiv.innerHTML = `<div class="exp-fmt-outer">
                <div class="exp-fmt-btn active" data-fmt="cn">Chinese Notation</div>
                <div class="exp-fmt-btn" data-fmt="en">English Notation</div>
                <div class="exp-fmt-btn" data-fmt="engine">Engine Format</div>
                <div class="exp-fmt-btn" data-fmt="url">URL Parameter</div>
               </div>`;

            const formats = {
                'cn': cnText,
                'en': enText,
                'engine': engineText,
                'url': urlText
            };

            const formatBtns = commentDiv.querySelectorAll('.exp-fmt-btn');

            formatBtns.forEach(btn => {
                btn.setAttribute('data-odd', '0');
                btn.addEventListener('click', () => {
                    const wasActive = btn.classList.contains('active');

                    // Update active styling and reset others
                    formatBtns.forEach(b => {
                        b.classList.remove('active');
                        if (!wasActive && b !== btn) {
                            b.setAttribute('data-odd', '0'); // Reset state of inactive buttons
                        }
                    });
                    btn.classList.add('active');

                    const fmt = btn.getAttribute('data-fmt');

                    // If the button was just activated, display default text
                    if (!wasActive) {
                        btn.setAttribute('data-odd', '0');
                        textarea.value = formats[fmt];
                    } else {
                        // Toggle logic for already active buttons
                        if (fmt === 'cn') {
                            if (btn.getAttribute('data-odd') === '0') {
                                btn.setAttribute('data-odd', '1');
                                textarea.value = cnTextWidthComment;
                            } else {
                                btn.setAttribute('data-odd', '0');
                                textarea.value = formats[fmt];
                            }
                        } else if (fmt === 'engine') {
                            // Append searchmoves for engine format
                            if (btn.getAttribute('data-odd') === '0') {
                                btn.setAttribute('data-odd', '1');
                                const isRed = currentTurn === 'w';
                                const moves = getAllLegalMoves(isRed, tileMap);

                                // Format array of moves into standard coordinates
                                const searchMovesStr = moves.map(m => {
                                    return String.fromCharCode(97 + m.startX) + (9 - m.startY) +
                                        String.fromCharCode(97 + m.endX) + (9 - m.endY);
                                }).join(' ');

                                textarea.value = formats[fmt] + (searchMovesStr ? "\ngo searchmoves " + searchMovesStr : "");
                            } else {
                                btn.setAttribute('data-odd', '0');
                                textarea.value = formats[fmt];
                            }
                        } else {
                            // Any other formats just stay the same on repeat clicks
                            textarea.value = formats[fmt];
                        }
                    }

                    textarea.blur();
                    setTimeout(() => {
                        textarea.focus();
                        textarea.select();
                    }, 12);
                });
            });

            // Initialize default state (Chinese)
            textarea.value = cnText;
            setTimeout(() => textarea.select(), 10);

            btnDiv.innerHTML = `
                   <div class="ejceestextbtninner">
                       <div class="note-btn btn-confirm" id="exp-confirm" title="Confirm">
                           <svg viewBox="0 0 24 24"><path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
                       </div>
                       <div class="note-btn btn-cancel" id="exp-cancel" title="Cancel">
                           <svg viewBox="0 0 24 24"><path fill="#F44336" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
                       </div>
                   </div>
               `;

            document.getElementById('exp-cancel').addEventListener('click', () => {
                isExportTextMode = false;
                renderRecordUI();
                renderNoteUI();
                updateToolHighlights();
            });

            document.getElementById('exp-confirm').addEventListener('click', () => {
                const text = textarea.value;
                isExportTextMode = false;
                importExportedText(text);
            });
        }
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

代码: 全选

// Global variable to store the last selected export format (default to 'cn')
let lastExportFormat = 'cn';

// --- Refactored UI for Export Text ---
        function renderExportTextUI() {
            const recordContainer = document.querySelector('.ejceesrecord');
            const commentDiv = document.querySelector('.ejceescomment');
            const btnDiv = document.querySelector('.ejceestextbtn');

            const cnText = generateExportText(false);
            const cnTextWidthComment = generateCnTextWidthComment();
            const enText = generateExportText(true);
            const urlText = generateUrlHash();

            // Generate UCCI Engine Format String
            let path = getGamePath();
            let engineText = "position fen " + path.fen;
            let currNode = path;
            if (currNode && currNode.v && currNode.v.length > 0) {
                engineText += " moves";
            }
            while (currNode && currNode.v && currNode.v.length > 0) {
                currNode = currNode.v[0];
                let dc = currNode.lastMove;
                if (dc) {
                    engineText += " " + String.fromCharCode(97 + dc.startX) + (9 - dc.startY) + String.fromCharCode(97 + dc.endX) + (9 - dc.endY);
                }
            }

            // Create readonly textarea
            recordContainer.innerHTML = `<textarea class="ejceescomment-edit" id="export-textarea" style="width:100%; height:100%; resize:none; border:none; outline:none; background:#2a2a2a; color:#fff; padding:8px; font-family:monospace; font-size:14px; white-space:pre-wrap; overflow:auto;"></textarea>`;

            const textarea = document.getElementById('export-textarea');

            // Set up the 4 toggle buttons, use lastExportFormat to determine the 'active' class
            commentDiv.innerHTML = `<div class="exp-fmt-outer">
                <div class="exp-fmt-btn ${lastExportFormat === 'cn' ? 'active' : ''}" data-fmt="cn">Chinese Notation</div>
                <div class="exp-fmt-btn ${lastExportFormat === 'en' ? 'active' : ''}" data-fmt="en">English Notation</div>
                <div class="exp-fmt-btn ${lastExportFormat === 'engine' ? 'active' : ''}" data-fmt="engine">Engine Format</div>
                <div class="exp-fmt-btn ${lastExportFormat === 'url' ? 'active' : ''}" data-fmt="url">URL Parameter</div>
               </div>`;

            const formats = {
                'cn': cnText,
                'en': enText,
                'engine': engineText,
                'url': urlText
            };

            const formatBtns = commentDiv.querySelectorAll('.exp-fmt-btn');

            formatBtns.forEach(btn => {
                btn.setAttribute('data-odd', '0');
                btn.addEventListener('click', () => {
                    const wasActive = btn.classList.contains('active');
                    const fmt = btn.getAttribute('data-fmt');
                    
                    // Remember the choice for next time
                    lastExportFormat = fmt;

                    // Update active styling and reset others
                    formatBtns.forEach(b => {
                        b.classList.remove('active');
                        if (!wasActive && b !== btn) {
                            b.setAttribute('data-odd', '0'); // Reset state of inactive buttons
                        }
                    });
                    btn.classList.add('active');

                    // If the button was just activated, display default text
                    if (!wasActive) {
                        btn.setAttribute('data-odd', '0');
                        textarea.value = formats[fmt];
                    } else {
                        // Toggle logic for already active buttons
                        if (fmt === 'cn') {
                            if (btn.getAttribute('data-odd') === '0') {
                                btn.setAttribute('data-odd', '1');
                                textarea.value = cnTextWidthComment;
                            } else {
                                btn.setAttribute('data-odd', '0');
                                textarea.value = formats[fmt];
                            }
                        } else if (fmt === 'engine') {
                            // Append searchmoves for engine format
                            if (btn.getAttribute('data-odd') === '0') {
                                btn.setAttribute('data-odd', '1');
                                const isRed = currentTurn === 'w';
                                const moves = getAllLegalMoves(isRed, tileMap);

                                // Format array of moves into standard coordinates
                                const searchMovesStr = moves.map(m => {
                                    return String.fromCharCode(97 + m.startX) + (9 - m.startY) +
                                        String.fromCharCode(97 + m.endX) + (9 - m.endY);
                                }).join(' ');

                                textarea.value = formats[fmt] + (searchMovesStr ? "\ngo searchmoves " + searchMovesStr : "");
                            } else {
                                btn.setAttribute('data-odd', '0');
                                textarea.value = formats[fmt];
                            }
                        } else {
                            textarea.value = formats[fmt];
                        }
                    }

                    textarea.blur();
                    setTimeout(() => {
                        textarea.focus();
                        textarea.select();
                    }, 12);
                });
            });

            // Initialize state using the remembered format
            textarea.value = formats[lastExportFormat];
            setTimeout(() => textarea.select(), 10);

            btnDiv.innerHTML = `
                   <div class="ejceestextbtninner">
                       <div class="note-btn btn-confirm" id="exp-confirm" title="Confirm">
                           <svg viewBox="0 0 24 24"><path fill="#4CAF50" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
                       </div>
                       <div class="note-btn btn-cancel" id="exp-cancel" title="Cancel">
                           <svg viewBox="0 0 24 24"><path fill="#F44336" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
                       </div>
                   </div>
               `;

            document.getElementById('exp-cancel').addEventListener('click', () => {
                isExportTextMode = false;
                renderRecordUI();
                renderNoteUI();
                updateToolHighlights();
            });

            document.getElementById('exp-confirm').addEventListener('click', () => {
                const text = textarea.value;
                isExportTextMode = false;
                importExportedText(text);
            });
        }
gemini fast
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4951
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 173 次
被圈友点赞: 202 次
联系:

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

帖子 ejsoon »

附件
ejcees20260403ctrlenter99977746.7z
(44.75 KiB) 已下载 3 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复

在线用户

正浏览此版面之用户: Baidu [Spider] 和 6 访客