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

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

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

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

備忘:

✔ 黑方數字要用全形。

✔ 白點去掉動畫,不改opacity,只改display和位置。

✔ 棋盤加個外邊框。

✔ 棋譜回合數字寬度加寬。手機端行高縮小。
上次由 ejsoon 在 2026年 2月 22日 16:38,总共编辑 6 次。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

改進:

當一個棋子移動結束時,給它加一個currentmove="1"屬性,它的stroke將等於lightgreen(而不是none)。

將它移動前的translate位置坐標賦值給svg內的白點cricle#ejceesstartdot的cx,cy。同

當下一個棋子移動時,前一個棋子的currentmove="1"屬性將去掉,stroke="none"。

當.ejceesstepdrop拖動或div.ejceesrcdstep點擊時,將需要顯示剛走完的棋子的stroke=lightgreen,以及白點要放到移動前的位置。

注意,在history中要用坐標來指定「剛走的棋子」而不是用id,因為每次棋局渲染時會改變棋子id。

白點只需要改變cx和cy屬性,其它不變,不需要為其加css。

在初始局面時,cx和cy都回到它的初始量。初始量需要在頁面加載時獲取。
上次由 ejsoon 在 2026年 2月 20日 13:03,总共编辑 1 次。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

終於加好白點了:

(臨時文件)

https://ejsoon.vip/ejcees20260220wdot6-1/

代码: 全选

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ejcees</title>
    <style>
        /* Base Reset */
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body,
        html {
            width: 100%;
            height: 100%;
            font-family: sans-serif;
        }

        /* Main Container */
        .ejceesmain {
            display: flex;
            width: 100vw;
            height: 100vh;
            background-color: #f4f4f9;
            overflow: hidden;
            /* Prevents scrolling */
        }

        .ejceessvg {
            display: flex;
            justify-content: center;
            align-items: center;
            box-sizing: border-box;
            background-color: transparent;
        }

        .ejceespb {
            display: block;
            aspect-ratio: 432 / 480;
            /* Maintains exact proportion without white edges */
            background-color: transparent;
            cursor: crosshair;
        }

        .ejceesctrl {
            display: flex;
            flex-direction: column;
            background-color: #333;
            color: #fff;
            flex: 1;
            /* Takes up all remaining space */
            box-sizing: border-box;
            overflow: hidden;
        }

        .ejceestool {
            display: flex;
            justify-content: space-between;
            align-items: center;
            height: 48px;
            width: 100%;
        }

        .tool-btn {
            width: 32px;
            height: 32px;
            border-radius: 50%;
            background-color: #555;
            color: white;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 11px;
            cursor: pointer;
            user-select: none;
        }

        /* --- SVG Tool Buttons --- */
        .tool-btn svg {
            width: 20px;
            height: 20px;
            fill: #ccc;
            stroke: #ccc;
            stroke-width: 0;
            stroke-linecap: round;
            stroke-linejoin: round;
            pointer-events: none;
        }

        /* Class to enable/highlight button state */
        .tool-btn.enable svg {
            fill: #4CAF50;
            /* Green for active/enabled */
        }

        .tool-btn:hover svg {
            fill: #fff;
        }

        .ejceesstep {
            display: flex;
            align-items: center;
            height: 48px;
            width: 100%;
        }

        .ejceesstepminus,
        .ejceesstepplus {
            width: 48px;
            height: 48px;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: #444;
            cursor: pointer;
            font-weight: bold;
            font-size: 24px;
            user-select: none;
        }

        .ejceesstepdrop {
            flex: 1;
            -webkit-appearance: none;
            appearance: none;
            height: 6px;
            background: #666;
            outline: none;
            border-radius: 3px;
            cursor: pointer;
        }

        /* Sky-blue draggable thumb */
        .ejceesstepdrop::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background: skyblue;
            cursor: pointer;
        }

        .ejceesstepdrop::-moz-range-thumb {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background: skyblue;
            cursor: pointer;
        }

        .ejceesoutput {
            height: 36px;
            display: flex;
            align-items: center;
            font-family: monospace;
            box-sizing: border-box;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        /* --- Text & Record Section --- */
        .ejceestext {
            flex: 1;
            /* Takes remaining height */
            display: flex;
            flex-direction: row;
            /* Side by side */
            border-top: 1px solid #555;
            overflow: hidden;
        }

        .ejceesrecord {
            /* Width is handled by media queries below */
            background-color: #2a2a2a;
            overflow-y: auto;
            border-right: 1px solid #444;
            display: flex;
            flex-direction: column;
        }

        .ejceesnote {
            flex: 1;
            /* Takes remaining width */
            background-color: #333;
            padding: 8px;
            overflow-y: auto;
            font-size: 14px;
            color: #ddd;
        }

        /* --- Record Steps Styling --- */
        .ejceesrcdstart {
            width: 100%;
            padding: 8px;
            box-sizing: border-box;
            text-align: center;
            font-weight: bold;
            border-bottom: 1px solid #444;
            color: #aaa;
            font-size: 14px;
            cursor: pointer;
        }

        .ejceesrcdstart.active {
            background-color: #3d5afe;
            color: white;
        }

        .ejceesrcdstepouter {
            display: flex;
            width: 100%;
            border-bottom: 1px solid #3e3e3e;
            min-height: 36px;
            align-items: stretch;
        }

        /* Step Number counter */
        .ejceesrcdstepouter::before {
            counter-increment: step-counter;
            content: counter(step-counter);
            width: 24px;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 10px;
            color: #777;
            background-color: #222;
            border-right: 1px solid #3e3e3e;
            flex-shrink: 0;
        }

        /* Reset counter on parent */
        .ejceesrecord {
            counter-reset: step-counter;
        }

        .ejceesrcdstep {
            flex: 1;
            max-width: calc((100% - 24px) / 2);
            display: flex;
            align-items: center;
            cursor: pointer;
            position: relative;
            font-family: "SimSun", serif;
            /* Songti for traditional look */
            white-space: nowrap;
            border-right: 1px solid #444;
        }

        .ejceesrcdstep:hover {
            background-color: #444;
        }

        .ejceesrcdstep.active {
            background-color: #3d5afe;
            color: white;
        }

        /* Branch Marker */
        .branch-marker {
            width: 24px;
            height: 24px;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 9px;
            color: #333;
            flex-shrink: 0;
        }

        .branch-marker span {
            background-color: #ffcdd2;
            /* Light red */
            border-radius: 50%;
            padding: 1px 3px;
            font-weight: bold;
        }

        /* --- Responsive Layout Rules --- */

        /* Mobile (Narrow Screen) */
        @media (max-width: 767px) {
            .ejceesmain {
                flex-direction: column;
            }

            .ejceessvg {
                width: 100%;
                height: auto;
                padding: 7px;
            }

            .ejceespb {
                width: 100%;
                height: auto;
                /* Height is calculated automatically based on 100% width & aspect ratio */
            }

            .ejceestool,
            .ejceesstep,
            .ejceesoutput {
                padding: 0 7px;
            }

            .ejceesstep {
                gap: 7px;
            }

            .ejceesrecord {
                width: 200px;
                /* Approx 12 chars * 12px + padding/borders */
                font-size: 12px;
            }
        }

        /* Desktop (Wide Screen) */
        @media (min-width: 768px) {
            .ejceesmain {
                flex-direction: row;
            }

            .ejceessvg {
                height: 100%;
                width: auto;
                padding: 12px;
            }

            .ejceespb {
                height: 100%;
                width: auto;
                /* Width is calculated automatically based on 100% height & aspect ratio */
            }

            .ejceestool,
            .ejceesstep,
            .ejceesoutput {
                padding: 0 12px;
            }

            .ejceesstep {
                gap: 12px;
            }

            .ejceesrecord {
                width: 268px;
                /* Approx 12 chars * 16px + padding/borders */
                font-size: 16px;
            }
        }
    </style>
</head>

<body>

    <div class="ejceesmain">
        <div class="ejceessvg">
            <svg class="ejceespb" viewBox="0 0 432 480" preserveAspectRatio="xMidYMid meet" id="etani" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" width="480" height="432" rfm="repeat">
                <defs mode="0" rotatestep="1" rotatestart="0" stepvalue="90" snapto="0" displaywait="1">
                    <g id="tile0" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z" />
                        <path fill="none" d="M 12,5 h -7 v 7 M -12,5 h 7 v 7 M 12,-5 h -7 v -7 M -12,-5 h 7 v -7" />
                    </g>
                    <g id="tile1" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z" />
                        <path fill="none" d="M 12,5 h -7 v 7 M 12,-5 h -7 v -7" />
                    </g>
                    <g id="tile2" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="black" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="M 4.41,6.71 Q 4.33,8.35 4.49,9.11 4.52,9.27 4.59,9.38 4.74,9.53 4.82,9.32 4.97,8.96 4.96,6.66 5.7,6.6 8.21,6.59 8.44,6.6 8.5,6.5 8.56,6.36 8.38,6.2 7.64,5.64 7.21,5.86 6.32,6.01 4.96,6.21 q -0,-0.34 -0,-0.73 0.41,-0.05 0.97,-0.08 0,0.02 0.01,0.06 0.03,0.16 0.17,0.15 0.14,-0 0.23,-0.15 Q 6.51,5.29 6.78,4.46 6.97,3.92 7.35,3.63 7.56,3.46 7.4,3.29 7.22,3.11 6.69,2.82 6.53,2.7 5.88,2.86 5.4,2.89 4.94,2.99 4.95,2.59 4.97,2.29 5.51,2.18 6.08,2.08 6.56,1.97 6.63,1.91 6.72,1.85 6.67,1.76 6.61,1.65 6.33,1.57 6.04,1.51 5.76,1.61 5.37,1.73 4.98,1.84 q 0,-0 0,-0.01 Q 5,1.49 5.18,1.2 5.24,1.03 5.04,0.88 4.79,0.69 4.36,0.58 4.16,0.53 4.03,0.68 3.94,0.76 4.07,0.88 4.36,1.13 4.39,1.49 q 0,0.2 0,0.48 -0.56,0.09 -1.18,0.14 -0.34,0.03 -0.1,0.21 0.36,0.22 1.04,0.09 0.11,-0.02 0.24,-0.02 0,0.3 0,0.68 Q 4.12,3.12 3.88,3.18 3.23,3.3 2.79,3.36 2.56,3.25 2.35,3.22 q -0.06,-0 -0.12,0.03 -0.06,0.06 0.03,0.16 0.3,0.36 0.53,1.58 0.06,0.61 0.45,0.84 0,0 0.02,0 0.13,0.02 0.15,-0.17 0.06,0 0.13,-0 0.31,-0.08 0.83,-0.13 0,0.39 -0,0.75 Q 2.84,6.47 0.99,6.68 0.74,6.69 0.93,6.9 1.32,7.26 1.73,7.14 3.32,6.72 4.13,6.72 4.25,6.7 4.41,6.71 Z M 4.94,4.42 Q 5.29,4.35 5.62,4.32 5.83,4.3 5.76,4.18 5.67,4.04 5.43,3.98 5.25,3.94 4.94,4.02 q 0,-0.34 0,-0.66 1.19,-0.19 1.41,-0.08 0.09,0.06 0.02,0.39 -0.27,1.19 -0.4,1.46 -0.27,-0.2 -1.03,-0 0,-0.37 0,-0.69 z M 4.41,4.15 q -0.39,0.09 -0.74,0.16 -0.14,0.03 0.02,0.17 0.09,0.06 0.28,0.04 0.2,-0.03 0.42,-0.05 0,0.36 0,0.73 Q 3.88,5.31 3.42,5.37 3.38,5.14 3.3,4.91 3.12,3.93 3.08,3.72 3.56,3.57 4.41,3.44 q 0,0.31 0,0.7 z" />
                    </g>
                    <g id="tile3" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="red" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="M 4.41,6.71 Q 4.33,8.35 4.49,9.11 4.52,9.27 4.59,9.38 4.74,9.53 4.82,9.32 4.97,8.96 4.96,6.66 5.7,6.6 8.21,6.59 8.44,6.6 8.5,6.5 8.56,6.36 8.38,6.2 7.64,5.64 7.21,5.86 6.32,6.01 4.96,6.21 q -0,-0.34 -0,-0.73 0.41,-0.05 0.97,-0.08 0,0.02 0.01,0.06 0.03,0.16 0.17,0.15 0.14,-0 0.23,-0.15 Q 6.51,5.29 6.78,4.46 6.97,3.92 7.35,3.63 7.56,3.46 7.4,3.29 7.22,3.11 6.69,2.82 6.53,2.7 5.88,2.86 5.4,2.89 4.94,2.99 4.95,2.59 4.97,2.29 5.51,2.18 6.08,2.08 6.56,1.97 6.63,1.91 6.72,1.85 6.67,1.76 6.61,1.65 6.33,1.57 6.04,1.51 5.76,1.61 5.37,1.73 4.98,1.84 q 0,-0 0,-0.01 Q 5,1.49 5.18,1.2 5.24,1.03 5.04,0.88 4.79,0.69 4.36,0.58 4.16,0.53 4.03,0.68 3.94,0.76 4.07,0.88 4.36,1.13 4.39,1.49 q 0,0.2 0,0.48 -0.56,0.09 -1.18,0.14 -0.34,0.03 -0.1,0.21 0.36,0.22 1.04,0.09 0.11,-0.02 0.24,-0.02 0,0.3 0,0.68 Q 4.12,3.12 3.88,3.18 3.23,3.3 2.79,3.36 2.56,3.25 2.35,3.22 q -0.06,-0 -0.12,0.03 -0.06,0.06 0.03,0.16 0.3,0.36 0.53,1.58 0.06,0.61 0.45,0.84 0,0 0.02,0 0.13,0.02 0.15,-0.17 0.06,0 0.13,-0 0.31,-0.08 0.83,-0.13 0,0.39 -0,0.75 Q 2.84,6.47 0.99,6.68 0.74,6.69 0.93,6.9 1.32,7.26 1.73,7.14 3.32,6.72 4.13,6.72 4.25,6.7 4.41,6.71 Z M 4.94,4.42 Q 5.29,4.35 5.62,4.32 5.83,4.3 5.76,4.18 5.67,4.04 5.43,3.98 5.25,3.94 4.94,4.02 q 0,-0.34 0,-0.66 1.19,-0.19 1.41,-0.08 0.09,0.06 0.02,0.39 -0.27,1.19 -0.4,1.46 -0.27,-0.2 -1.03,-0 0,-0.37 0,-0.69 z M 4.41,4.15 q -0.39,0.09 -0.74,0.16 -0.14,0.03 0.02,0.17 0.09,0.06 0.28,0.04 0.2,-0.03 0.42,-0.05 0,0.36 0,0.73 Q 3.88,5.31 3.42,5.37 3.38,5.14 3.3,4.91 3.12,3.93 3.08,3.72 3.56,3.57 4.41,3.44 q 0,0.31 0,0.7 z" />
                    </g>
                    <g id="tile4" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="black" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="M 5.07,3.57 Q 5.07,3.15 5.1,2.79 6.06,2.54 6.08,2.53 6.14,2.46 6.12,2.4 6.05,2.31 5.84,2.27 5.56,2.21 5.11,2.42 q 0,-0.16 0.02,-0.41 0.06,-0.19 -0.19,-0.4 l -0,-0 Q 6.69,1.25 6.74,1.22 6.8,1.15 6.78,1.08 6.72,0.97 6.48,0.9 6.28,0.85 5.25,1.12 4.16,1.35 3.24,1.41 q -0.02,0 -0.04,0.01 Q 3.16,1.39 3.09,1.36 2.75,1.12 2.56,1.1 2.42,1.11 2.4,1.21 2.37,1.32 2.46,1.4 2.61,1.65 2.65,1.85 2.79,3.34 2.5,4.97 q -0.06,0.23 -0.05,0.27 0,0.09 0.14,0.2 0.14,0.12 0.21,0.13 1.64,-0.61 4.14,-0.72 0.13,0 0.29,0.09 0.12,0.09 0.1,0.26 0,0.16 -0.24,1.61 Q 6.92,7.71 6.71,8.03 6.62,8.23 6.38,8.21 6.12,8.13 5.86,8.06 5.68,8 5.64,8.04 5.6,8.09 5.76,8.21 6.33,8.95 6.37,9.17 6.4,9.26 6.51,9.28 6.67,9.32 7.01,8.99 7.33,8.68 7.44,8.27 8.03,5.27 8.25,5.12 8.34,5.06 8.33,4.98 8.34,4.9 8.01,4.65 7.57,4.37 7.28,4.46 q -0.22,0.02 -0.46,0.05 -0.75,0 -1.77,0.2 Q 5.05,4.31 5.06,3.93 5.4,3.84 5.77,3.77 6.08,3.68 6.12,3.64 6.18,3.58 6.15,3.51 6.09,3.43 5.87,3.38 5.65,3.35 5.07,3.57 Z M 4.61,3.69 Q 4.22,3.76 3.8,3.82 3.54,3.86 3.73,3.99 3.98,4.11 4.58,4.03 q 0,0 0.02,0 0,0.34 0,0.75 Q 3.94,4.86 3.48,4.98 3.14,5.02 3.12,4.77 3.08,4.51 3.14,3.11 3.23,1.96 3.31,1.85 q 0.03,-0.06 0.06,-0.13 0.14,0.03 1.02,-0.04 0.02,-0 0.07,-0 0.09,0.16 0.13,0.9 -0.31,0.06 -0.69,0.11 -0.26,0.05 -0.07,0.17 0.3,0.16 0.73,0.05 0,-0 0.03,0 0,0.31 0,0.79 z M 1.55,5.95 Q 1.46,6.49 1.25,7.01 1.14,7.36 1.38,7.65 1.49,7.8 1.66,7.64 1.84,7.43 1.9,7.11 1.97,6.73 1.91,6.44 1.9,6.12 1.77,5.9 1.71,5.83 1.65,5.83 1.57,5.87 1.55,5.95 Z m 1.11,0.42 q 0.09,0.55 0.25,0.75 0.08,0.08 0.19,0.02 Q 3.21,7.11 3.26,6.98 3.33,6.62 2.94,6.21 2.93,6.18 2.91,6.18 2.81,6.08 2.74,6.06 q -0.03,-0 -0.07,0.07 -0.03,0.08 -2e-7,0.24 z M 3.77,6.17 Q 3.94,6.62 4.16,6.85 4.27,6.92 4.38,6.84 4.46,6.75 4.48,6.62 4.49,6.42 4.4,6.27 4.26,6.07 3.97,5.91 3.86,5.82 3.77,5.83 q -0.05,-0 -0.06,0.09 -0,0.09 0.06,0.24 z m 1.32,-0.3 q 0.26,0.58 0.53,0.81 0.12,0.06 0.23,-0.02 0.09,-0.09 0.11,-0.25 0,-0.39 -0.66,-0.82 l -0,-0 q -0.12,-0.08 -0.22,-0.07 -0.05,0 -0.06,0.11 -0,0.09 0.08,0.26 z" />
                    </g>
                    <g id="tile5" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="red" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="M 5.07,3.57 Q 5.07,3.15 5.1,2.79 6.06,2.54 6.08,2.53 6.14,2.46 6.12,2.4 6.05,2.31 5.84,2.27 5.56,2.21 5.11,2.42 q 0,-0.16 0.02,-0.41 0.06,-0.19 -0.19,-0.4 l -0,-0 Q 6.69,1.25 6.74,1.22 6.8,1.15 6.78,1.08 6.72,0.97 6.48,0.9 6.28,0.85 5.25,1.12 4.16,1.35 3.24,1.41 q -0.02,0 -0.04,0.01 Q 3.16,1.39 3.09,1.36 2.75,1.12 2.56,1.1 2.42,1.11 2.4,1.21 2.37,1.32 2.46,1.4 2.61,1.65 2.65,1.85 2.79,3.34 2.5,4.97 q -0.06,0.23 -0.05,0.27 0,0.09 0.14,0.2 0.14,0.12 0.21,0.13 1.64,-0.61 4.14,-0.72 0.13,0 0.29,0.09 0.12,0.09 0.1,0.26 0,0.16 -0.24,1.61 Q 6.92,7.71 6.71,8.03 6.62,8.23 6.38,8.21 6.12,8.13 5.86,8.06 5.68,8 5.64,8.04 5.6,8.09 5.76,8.21 6.33,8.95 6.37,9.17 6.4,9.26 6.51,9.28 6.67,9.32 7.01,8.99 7.33,8.68 7.44,8.27 8.03,5.27 8.25,5.12 8.34,5.06 8.33,4.98 8.34,4.9 8.01,4.65 7.57,4.37 7.28,4.46 q -0.22,0.02 -0.46,0.05 -0.75,0 -1.77,0.2 Q 5.05,4.31 5.06,3.93 5.4,3.84 5.77,3.77 6.08,3.68 6.12,3.64 6.18,3.58 6.15,3.51 6.09,3.43 5.87,3.38 5.65,3.35 5.07,3.57 Z M 4.61,3.69 Q 4.22,3.76 3.8,3.82 3.54,3.86 3.73,3.99 3.98,4.11 4.58,4.03 q 0,0 0.02,0 0,0.34 0,0.75 Q 3.94,4.86 3.48,4.98 3.14,5.02 3.12,4.77 3.08,4.51 3.14,3.11 3.23,1.96 3.31,1.85 q 0.03,-0.06 0.06,-0.13 0.14,0.03 1.02,-0.04 0.02,-0 0.07,-0 0.09,0.16 0.13,0.9 -0.31,0.06 -0.69,0.11 -0.26,0.05 -0.07,0.17 0.3,0.16 0.73,0.05 0,-0 0.03,0 0,0.31 0,0.79 z M 1.55,5.95 Q 1.46,6.49 1.25,7.01 1.14,7.36 1.38,7.65 1.49,7.8 1.66,7.64 1.84,7.43 1.9,7.11 1.97,6.73 1.91,6.44 1.9,6.12 1.77,5.9 1.71,5.83 1.65,5.83 1.57,5.87 1.55,5.95 Z m 1.11,0.42 q 0.09,0.55 0.25,0.75 0.08,0.08 0.19,0.02 Q 3.21,7.11 3.26,6.98 3.33,6.62 2.94,6.21 2.93,6.18 2.91,6.18 2.81,6.08 2.74,6.06 q -0.03,-0 -0.07,0.07 -0.03,0.08 -2e-7,0.24 z M 3.77,6.17 Q 3.94,6.62 4.16,6.85 4.27,6.92 4.38,6.84 4.46,6.75 4.48,6.62 4.49,6.42 4.4,6.27 4.26,6.07 3.97,5.91 3.86,5.82 3.77,5.83 q -0.05,-0 -0.06,0.09 -0,0.09 0.06,0.24 z m 1.32,-0.3 q 0.26,0.58 0.53,0.81 0.12,0.06 0.23,-0.02 0.09,-0.09 0.11,-0.25 0,-0.39 -0.66,-0.82 l -0,-0 q -0.12,-0.08 -0.22,-0.07 -0.05,0 -0.06,0.11 -0,0.09 0.08,0.26 z" />
                    </g>
                    <g id="tile6" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="black" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="m 2.22,6.87 q 0.08,0 0.18,-0.01 0.36,-0.11 1.14,-0.23 0.09,-0 0.1,-0.1 0,-0.06 -0.23,-0.18 Q 3.6,5.66 3.8,5.47 4,5.24 3.79,5.12 3.62,5.03 3.39,4.87 3.22,4.76 2.84,5 2.48,5.14 2.04,5.26 2.29,4.73 2.62,3.91 2.74,3.57 2.75,3.55 2.66,3.38 2.48,3.35 3.96,2.93 4.01,2.88 4.1,2.8 4.04,2.72 3.98,2.61 3.69,2.57 3.4,2.53 3.12,2.66 2.75,2.81 2.34,2.94 1.88,3.06 1.35,3.14 1.01,3.23 1.27,3.37 1.52,3.51 1.98,3.48 2.12,3.95 1.64,5.15 q -0.02,-0 -0.06,-0.01 -0.11,-0.02 -0.13,0.01 -0.05,0.03 0,0.18 0,0.03 0.04,0.12 -0.61,1 -1.22,1.81 -0.06,0.05 0.03,0.06 0.5,-0.16 1.32,-1.33 0,-0.02 0.02,-0.04 0.09,0.39 0.17,0.92 0,0.28 0.17,0.47 0.13,0.19 0.18,0.05 0.06,-0.17 0.01,-0.53 z M 2.91,6.32 Q 2.51,6.47 2.17,6.57 2.05,5.81 2.02,5.53 q 0.05,-0 0.13,-0.03 0.37,-0.09 0.7,-0.17 0.22,-0.05 0.26,0.02 0.06,0.05 -0,0.35 -0.06,0.27 -0.14,0.6 -0.03,0 -0.06,0.01 z M 4.81,3.38 Q 4.87,3.42 4.97,3.47 5.18,3.59 5.34,3.53 7.1,3.03 7.21,3.18 7.31,3.3 7.28,3.65 7.24,4.6 7.03,5.03 6.88,5.32 6.63,5.29 q -0.11,0 -0.21,-0 Q 6.18,5.22 6.35,5.43 6.61,5.77 6.73,6.01 6.91,6.18 7.07,6.03 7.6,5.64 7.78,4.27 7.82,3.38 8.01,3.14 8.13,3.02 8.06,2.93 7.98,2.84 7.5,2.64 7.33,2.58 7.15,2.7 6.97,2.81 6.27,2.98 5.56,3.16 4.95,3.22 5.3,2.79 5.7,2.16 5.87,1.84 6.01,1.69 6.11,1.58 6.05,1.46 6.01,1.34 5.74,1.13 5.47,0.97 5.3,0.98 5.12,1 5.21,1.21 5.32,1.41 5.24,1.59 5.04,2.2 4.71,2.78 4.4,3.34 3.95,3.95 3.86,4.04 3.84,4.12 3.81,4.21 3.94,4.19 4.17,4.14 4.81,3.38 Z M 4.65,4.58 Q 4.55,4.5 4.41,4.44 4.28,4.41 4.22,4.45 4.15,4.49 4.2,4.65 4.31,4.99 4.28,5.32 4.14,7.24 4.31,7.78 4.35,8.06 4.62,8.3 5.46,8.99 7.45,8.73 7.62,8.7 7.83,8.65 8.31,8.51 8.79,8.18 8.98,8.04 8.85,7.78 8.73,7.59 8.55,6.97 8.54,6.82 8.48,6.76 8.42,6.72 8.38,6.9 8.1,7.58 7.94,7.78 7.77,7.99 7.28,8.12 6.14,8.4 5.2,8.03 4.82,7.85 4.73,7.62 4.51,7.14 4.62,6.03 4.74,6.08 4.9,6.05 5.26,5.97 6.03,5.9 q 0.09,-0 0.09,-0.09 0,-0.06 -0.19,-0.18 0.17,-0.64 0.37,-0.83 0.17,-0.2 0,-0.32 Q 6.15,4.36 5.92,4.21 5.77,4.13 5.61,4.22 5.42,4.34 5.16,4.44 4.93,4.5 4.65,4.58 Z m 0.13,0.3 q 0,-0.03 0,-0.08 0.02,-0.02 0.66,-0.16 0.19,-0.03 0.24,0.02 0.05,0.05 -0.01,0.32 -0.06,0.26 -0.15,0.56 -0.02,0 -0.05,0.01 Q 5,5.72 4.65,5.79 4.68,5.37 4.78,4.89 Z" />
                    </g>
                    <g id="tile7" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="red" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="M 4.74,3.46 Q 5.06,3.69 5.32,3.61 6.88,3.12 6.99,3.32 7.07,3.47 7.05,3.86 6.99,5.04 6.74,5.58 6.62,5.84 6.4,5.83 6.23,5.79 6.08,5.74 5.86,5.64 6,5.89 q 0.33,0.39 0.48,0.68 0.22,0.2 0.39,0 Q 7.17,6.32 7.34,5.91 7.55,5.42 7.59,3.97 7.6,3.46 7.76,3.2 7.87,3.08 7.81,2.98 7.72,2.88 7.21,2.69 7.01,2.62 6.84,2.75 6.67,2.85 6.04,3.02 5.42,3.2 4.89,3.26 4.9,3.24 4.93,3.21 5.24,2.78 5.6,2.13 5.74,1.82 5.87,1.67 5.95,1.56 5.89,1.44 5.84,1.32 5.55,1.13 5.29,0.98 5.1,1.01 4.93,1.05 5.03,1.25 5.22,1.55 4.67,2.76 4.41,3.3 4.04,3.91 3.95,4.03 3.94,4.08 3.91,4.19 4.05,4.16 4.3,4.04 4.74,3.46 Z M 4.23,4.69 Q 4.14,4.6 4,4.57 q -0.11,-0.03 -0.17,0 -0.05,0.02 -0,0.17 0.11,0.33 0.08,0.64 -0.11,1.9 0.05,2.42 0.03,0.28 0.32,0.53 1.06,0.76 3.22,0.32 Q 7.57,8.65 7.66,8.64 8.17,8.49 8.67,8.15 8.85,8.03 8.75,7.77 8.64,7.46 8.54,6.52 q 0,-0.14 -0.06,-0.2 Q 8.42,6.28 8.37,6.44 8.05,7.43 7.85,7.73 7.65,7.96 7.12,8.11 5.89,8.43 4.85,8.08 4.45,7.91 4.35,7.66 4.1,7.16 4.22,6.15 q 0.12,0.05 0.26,0 0.37,-0.09 1.2,-0.18 0.09,-0 0.1,-0.09 0,-0.06 -0.21,-0.18 0.17,-0.56 0.36,-0.73 0.2,-0.22 0,-0.35 Q 5.78,4.51 5.55,4.35 5.38,4.26 4.99,4.47 4.63,4.61 4.23,4.69 Z m 0.13,0.29 q 0,-0.02 0,-0.04 0.09,-0.02 0.68,-0.15 0.17,-0.03 0.23,0.03 0.02,0.02 -0.15,0.84 Q 4.65,5.8 4.24,5.9 4.27,5.47 4.37,4.98 Z M 1.2,3.42 Q 1.14,3.99 0.98,4.52 0.87,4.83 1.12,5.1 1.23,5.24 1.38,5.07 1.74,4.59 1.56,3.85 1.52,3.56 1.4,3.37 1.34,3.3 1.29,3.31 1.22,3.35 1.2,3.42 Z m 1.32,2.53 q 0.03,0.06 0.14,0.16 0.26,0.27 0.54,0.61 0.08,0.09 0.19,0.11 0.06,0 0.12,-0.08 0.05,-0.09 0.01,-0.31 Q 3.52,6.25 3.26,6.07 2.64,5.73 2.54,5.76 2.63,5.16 2.67,3.88 3.02,3.68 3.23,3.55 3.65,3.22 3.88,3.14 4.05,3.08 3.97,2.94 3.87,2.82 3.69,2.7 3.51,2.58 3.42,2.6 q -0.09,0 -0.06,0.11 0,0.23 -0.37,0.64 Q 2.83,3.51 2.69,3.68 2.7,2.58 2.93,2.03 2.99,1.91 2.89,1.8 2.68,1.64 2.34,1.54 2.12,1.49 2.04,1.55 1.96,1.62 2.03,1.72 2.57,2.56 2.11,5.8 1.8,7.01 1.48,7.52 1.21,7.89 0.81,8.28 q -0.09,0.08 -0.08,0.12 0,0.03 0.11,0.01 Q 0.95,8.45 1.22,8.3 2.23,7.76 2.52,5.96 Z" />
                    </g>
                    <g id="tile8" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="black" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="M 4.28,7.72 Q 3.27,7.84 2.14,7.99 1.9,8.02 2.07,8.2 2.39,8.52 2.93,8.4 4.19,8.08 6.78,8.08 q 0.14,0 0.29,0 0.22,0 0.26,-0.09 Q 7.41,7.88 7.23,7.72 6.64,7.27 6,7.42 5.49,7.51 4.8,7.64 4.83,6.24 4.85,4.97 6.83,4.71 8.48,4.9 8.73,4.93 8.8,4.84 8.86,4.69 8.74,4.58 8.42,4.29 7.97,4.09 7.82,4.03 7.55,4.12 q -0.34,0.09 -2.68,0.39 0,-1.93 0.18,-2.53 Q 5.17,1.76 5.02,1.62 4.63,1.31 4.31,1.22 4.14,1.18 3.99,1.32 q -0.06,0.08 0,0.2 0.3,0.5 0.29,1.06 0,0.95 0,1.99 -1.06,0.12 -1.85,0.24 -0.93,0.14 -1.63,0.14 -0.14,0 -0.15,0.12 0,0.12 0.19,0.26 0.41,0.27 0.9,0.16 0.89,-0.33 2.54,-0.5 0,1.29 0,2.67 z" />
                    </g>
                    <g id="tile9" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="red" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="m 1.97,4.26 q 0.3,0.65 0.09,2.37 -0.06,0.5 -0.2,1.01 -0.13,0.45 0.2,0.9 0,0.02 0.04,0.05 Q 2.28,8.75 2.4,8.49 2.54,8.1 2.54,7.65 2.56,5.23 2.65,4.69 2.71,4.43 2.62,4.32 2.45,4.12 2.32,3.99 2.28,3.93 2.22,3.91 2.62,3.32 3.19,2.2 3.38,1.84 3.37,1.81 3.34,1.72 3.2,1.6 3.03,1.48 2.83,1.44 2.61,1.38 2.5,1.42 2.41,1.46 2.44,1.58 2.67,2.42 1.51,4.12 1.48,4.15 1.47,4.18 1.12,4.67 0.25,5.62 0.16,5.68 0.28,5.69 0.66,5.64 1.63,4.67 1.78,4.47 1.97,4.26 Z m 3.35,2.83 q -0.79,0.09 -1.69,0.22 -0.19,0 -0.04,0.17 0.12,0.12 0.29,0.17 0.2,0.05 0.34,0 1.22,-0.3 3.59,-0.3 0.17,0 0.23,-0.07 Q 8.11,7.19 7.95,7.06 7.44,6.67 6.9,6.83 6.44,6.91 5.8,7.02 5.84,5.92 5.87,4.9 6.39,4.82 8.17,4.62 8.28,4.65 8.39,4.5 8.4,4.38 8.15,4.25 7.75,4.02 7.12,4.2 5.94,4.44 5.88,4.47 q 0,-1.42 0.25,-2.52 Q 6.17,1.83 5.94,1.65 5.54,1.45 5.25,1.4 5.07,1.37 4.97,1.47 q -0.09,0.09 0,0.26 0.33,0.45 0.33,0.79 0.05,0.98 0.06,2.04 Q 4.87,4.63 4.38,4.69 3.82,4.72 3.21,4.79 2.89,4.83 3.13,5.02 3.49,5.3 3.74,5.27 3.86,5.21 5.38,4.98 5.37,6 5.33,7.1 Z" />
                    </g>
                    <g id="tile10" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="black" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="M 5.32,6.58 Q 5.93,7.23 6.58,7.81 6.81,8.02 7.2,8.01 7.9,7.98 8.61,7.78 8.79,7.75 8.8,7.7 8.81,7.64 8.67,7.58 6.82,7.16 5.88,6.58 5.77,6.51 5.67,6.44 5.3,6.18 5.12,6.06 5.06,5.92 4.98,5.86 5.9,5.36 6.34,5.19 6.52,5.16 6.5,5.05 6.46,4.88 6.29,4.68 6.13,4.47 5.9,4.43 5.75,4.42 5.74,4.6 5.75,4.83 4.85,5.71 4.35,5.14 3.62,4.94 3.82,4.73 3.99,4.68 q 0.06,-0 0.07,-0.1 0,-0.13 -0.13,-0.28 0.69,-0.09 1.7,-0.16 l 0,0 q 0.02,0.14 0.14,0.15 Q 5.94,4.28 6.27,3.89 6.46,3.43 6.82,3.17 7.02,3.05 6.88,2.88 6.73,2.71 6.28,2.42 6.14,2.31 5.94,2.36 5.6,2.43 5.17,2.49 4.91,2.52 4.67,2.57 5.64,1.73 5.87,1.63 6.03,1.53 5.93,1.4 5.81,1.28 5.47,1.07 5.33,1 5.19,1.09 q -0.08,0.03 -0.74,0.23 -0.16,0.03 -0.33,0.06 0,-0.09 -0.13,-0.31 Q 3.83,0.89 3.71,0.85 3.57,0.82 3.58,0.98 3.61,1.22 2.98,1.81 2.67,2.07 2.29,2.34 2.2,2.38 2.17,2.43 2.14,2.49 2.23,2.51 2.65,2.57 3.74,1.72 3.85,1.63 3.96,1.57 4.15,1.65 4.81,1.54 4.95,1.49 5,1.53 5.03,1.56 4.97,1.64 4.81,1.91 4.36,2.6 3.85,2.67 3.39,2.76 2.5,2.93 1.92,2.93 q -0.12,-0 -0.13,0.03 -0.05,0.06 0.04,0.19 0.26,0.37 0.48,1.02 0.05,0.19 0.19,0.31 0.17,0.17 0.21,0.02 0,-0.03 0,-0.08 0,0 0.73,-0.09 -0,0 -0.01,0.04 -0.59,0.78 -2.04,1.4 -0.06,0 -0,0.06 0.47,0.09 1.43,-0.4 0.02,-0.02 0.59,-0.38 0.02,0.05 0.16,0.13 Q 3.27,5.94 1.56,6.96 1.39,7.05 1.6,7.04 2.22,7.07 3.5,6.03 3.74,5.78 4.09,5.53 q 0,-0 0,-0 Q 4.25,5.64 4.38,5.77 3.57,7.05 1.56,8.31 1.42,8.41 1.51,8.44 1.6,8.5 1.72,8.45 2.97,8.14 4.28,6.7 4.7,6.25 4.69,6.22 q 0,-0 0,-0.02 0.33,0.58 0.07,1.72 -0.08,0.34 -0.19,0.39 -0.12,0.06 -0.68,-0 -0.47,-0.08 -0.48,0 -0,0.08 0.13,0.19 0.65,0.45 0.94,0.78 Q 4.63,9.46 4.77,9.4 4.94,9.34 5.12,9.05 5.3,8.68 5.38,8.08 5.47,7.26 5.37,6.78 5.34,6.69 5.32,6.58 Z M 5.53,3.77 Q 5.39,3.68 5.12,3.75 4.75,3.83 4.39,3.91 4.44,3.08 4.44,3.07 4.36,2.98 4.27,2.91 5.73,2.65 5.99,2.79 q 0.03,0.03 0,0.22 Q 5.78,3.68 5.7,3.8 5.69,3.83 5.66,3.87 5.6,3.83 5.53,3.77 Z M 2.7,4.17 Q 2.58,3.8 2.48,3.26 2.99,3.1 3.9,2.97 q 0,0.02 0.11,1.01 -0.72,0.11 -1.31,0.18 z" />
                    </g>
                    <g id="tile11" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="red" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="m 3.17,5.18 q 0.06,0.06 0.2,0.17 0.23,0.19 0.5,0.43 0.11,0.09 0.24,0.09 0.08,-0 0.12,-0.12 Q 4.27,5.64 4.2,5.41 4.12,5.16 3.33,4.98 3.23,4.95 3.18,4.97 3.19,4.52 3.21,4.03 3.44,3.96 3.73,3.91 4.18,3.79 4.26,3.73 4.35,3.64 4.3,3.55 4.22,3.43 3.91,3.36 3.54,3.3 3.22,3.45 q 0,-1.03 0.21,-1.77 Q 3.56,1.42 3.33,1.25 3.18,1.14 2.9,0.97 2.67,0.79 2.47,0.95 q -0.05,0.03 0,0.19 0.3,0.47 0.3,0.93 0,0.67 0,1.46 -0,0 -0,0.05 Q 1.36,3.94 0.83,4.01 0.44,4.07 0.72,4.25 1.14,4.47 1.86,4.31 2.19,4.23 2.56,4.18 1.92,5.49 0.39,7.19 0.32,7.28 0.43,7.31 0.64,7.34 1.74,6.26 2.26,5.73 2.74,4.91 2.7,5.62 2.7,6.39 2.65,7.12 2.44,7.81 2.33,8.2 2.57,8.8 2.64,8.97 2.71,9 2.78,9.07 2.85,8.98 3.1,8.82 3.12,8.2 3.11,7.83 3.17,5.18 Z M 5.24,7.6 Q 6.11,7.51 6.75,7.47 7.03,7.81 7.23,8.15 7.33,8.32 7.43,8.31 7.6,8.31 7.74,7.97 7.91,7.6 7.87,7.14 7.78,5.96 7.71,3.79 q -0,-0.3 0.14,-0.52 0.09,-0.12 0,-0.22 -0.17,-0.19 -0.69,-0.41 -0.17,-0.09 -0.32,0 Q 6.57,2.74 5.18,3.09 5.07,2.98 4.86,2.91 q -0.09,-0.03 -0.19,0 -0.06,0.03 -0.01,0.15 0.02,0.11 0.07,0.21 Q 5,3.79 4.83,6.33 4.8,6.68 4.69,7.12 4.57,7.65 4.91,8.07 q 0.09,0.12 0.2,0 0.12,-0.14 0.12,-0.36 0,-0.05 0,-0.11 z M 6.68,7.05 q -0.39,-0.08 -1.43,0.23 0,-0.11 0,-1.17 0,0 1.34,-0.1 Q 6.87,5.97 6.77,5.82 6.66,5.67 6.38,5.62 5.96,5.55 5.26,5.87 5.27,5.25 5.28,4.82 L 6.59,4.64 Q 6.82,4.6 6.73,4.47 6.62,4.33 6.38,4.3 6.04,4.26 5.29,4.56 q 0,-0.94 0.02,-1.05 0,-0.06 0,-0.11 Q 5.58,3.37 6.74,3.13 6.94,3.1 7.03,3.17 7.31,3.42 7.22,6.16 7.23,6.89 7.17,7.12 7.14,7.24 7.05,7.25 6.94,7.11 6.68,7.05 Z" />
                    </g>
                    <g id="tile12" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="black" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="m 1.56,5.78 q 0.05,0.05 0.03,0.31 -0.13,1 -1.06,2.34 -0.03,0.03 -0.03,0.1 0,0.03 0.05,0.03 Q 1.17,8.32 2.01,6.56 q 0,-0.08 0.2,-0.44 Q 2.26,6.02 2.18,5.91 2.03,5.73 1.89,5.64 2.74,5.34 2.99,5.26 2.95,6.77 2.86,7.27 2.69,8.11 2.82,8.45 q 0.02,0.06 0.06,0.16 0.06,0.16 0.13,0.19 0.05,0.06 0.12,-0.01 0.06,-0.03 0.15,-0.22 0.05,-0.13 0.08,-1.46 0,-2.1 0.03,-2.14 -0,-0 -0.01,-0 0,-0.7 0.02,-1.57 0,-0.87 0.17,-1.46 0.08,-0.13 0.04,-0.26 -0.05,-0.09 -0.56,-0.39 -0.2,-0.14 -0.39,-0 -0.03,0.03 0.01,0.18 0.28,0.42 0.29,0.87 0,0.58 0.01,1.26 -1,0.19 -1.02,0.17 -0.08,-0.08 0.05,-1.12 0.08,-0.19 -0.42,-0.44 -0.16,-0.08 -0.25,-0.04 -0.11,0.05 -0.01,0.19 0.23,0.37 0.21,0.9 0,0.42 -0.12,0.65 -0.08,0.17 0.01,0.37 0.13,0.22 0.28,0.11 Q 2.16,4.18 3.03,3.86 3.02,4.42 3,5.01 0.85,5.49 0.52,5.46 0.41,5.47 0.39,5.55 0.36,5.66 0.43,5.74 0.61,5.89 0.88,6.06 0.96,6.1 1.07,6.04 1.3,5.89 1.56,5.78 Z M 4.31,3.45 q 0.47,0.5 0.67,0.5 0.12,0 0.16,-0.16 0,-0.12 -0.09,-0.28 -0.17,-0.23 -0.72,-0.29 -0.09,-0 -0.08,0.1 -0,0.06 0.05,0.14 z M 5.38,2.89 q 0.09,0.13 0.22,0.26 0.13,0.12 0.26,0.11 0.11,0 0.14,-0.13 0,-0.09 -0.07,-0.25 Q 5.77,2.69 5.4,2.69 q -0.06,-0 -0.07,0.08 -0,0.06 0.05,0.12 z M 5.66,2.26 Q 5.87,2.46 6.67,2.31 6.84,2.23 6.92,2.35 6.94,2.41 6.83,2.54 6.02,3.69 3.88,4.96 3.74,5.04 3.87,5.07 q 0.7,0 2.39,-1.26 0.02,-0.03 0.06,-0.05 Q 6.82,3.33 7.33,2.82 7.53,2.59 7.86,2.4 8,2.31 7.96,2.23 7.95,2.15 7.46,1.88 7.31,1.77 7.13,1.87 6.93,1.96 6.64,2.02 6.21,2.08 5.82,2.12 5.96,2 6.12,1.84 6.27,1.66 6.4,1.58 6.49,1.51 6.47,1.39 6.46,1.3 6.27,1.08 6.09,0.9 5.94,0.87 5.79,0.86 5.83,1.04 5.89,1.32 5.29,2.05 5,2.39 4.62,2.75 q -0.09,0.06 -0.12,0.11 -0.03,0.08 0.07,0.08 0.19,0 1.08,-0.68 z m 0.68,3.17 Q 6.46,7.92 6.23,8.14 6.14,8.2 6,8.17 5.72,8.13 5.45,8.08 5.33,8.07 5.34,8.14 5.34,8.2 5.45,8.28 6.12,8.84 6.33,9.17 6.48,9.32 6.63,9.19 6.92,8.88 6.93,8.1 6.84,7.21 6.84,5.38 7.43,5.32 8.19,5.3 q 0.51,0 0.57,-0.09 Q 8.8,5.11 8.67,5 8.15,4.66 7.61,4.84 7.28,4.9 6.88,5.01 6.89,4.84 6.95,4.72 7.04,4.55 6.87,4.42 6.62,4.21 6.33,4.11 6.15,4.04 6.07,4.13 q -0.05,0.06 0.04,0.21 0.22,0.36 0.22,0.77 -0.12,0 -0.24,0.03 -1.01,0.16 -2.22,0.36 -0.16,0 -0.03,0.15 0.12,0.11 0.26,0.14 0.17,0.03 0.31,0 Q 5.32,5.54 6.35,5.43 Z M 4.49,6.71 q 0.22,0.25 0.44,0.53 0.11,0.13 0.25,0.14 0.09,0 0.16,-0.11 Q 5.42,7.17 5.37,6.87 5.31,6.57 4.51,6.27 4.37,6.21 4.3,6.23 q -0.05,0.02 -0.04,0.14 0,0.11 0.23,0.33 z" />
                    </g>
                    <g id="tile13" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="red" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="m 1.87,7.41 q 0.09,0.02 0.22,-0 0.51,-0.12 1.64,-0.22 0.09,-0 0.11,-0.09 0,-0.08 -0.24,-0.23 Q 3.84,6.23 4.1,6 4.25,5.83 4.11,5.68 3.44,5.18 2.95,5.47 2.28,5.69 1.9,5.78 q 0,-0.51 0.01,-0.89 0.14,0.03 0.29,0 0.48,-0.13 1.54,-0.27 0.09,-0 0.1,-0.1 0,-0.06 -0.24,-0.2 Q 3.85,3.65 4.11,3.5 4.25,3.35 4.12,3.2 3.5,2.7 3.04,3 2.76,3.11 2.03,3.36 2.42,2.9 2.65,2.59 3.1,1.91 3.32,1.74 3.48,1.59 3.34,1.46 3.16,1.34 2.89,1.28 q -0.25,-0.05 -0.36,0 -0.11,0.05 -0.03,0.15 0.13,0.28 -0.62,1.88 -0,0 -0,0 Q 1.72,3.2 1.51,3.13 q -0.09,-0.03 -0.2,-0 -0.06,0.05 -0,0.16 0.17,0.65 0.19,0.86 0.11,1.89 -0.17,3.03 -0.13,0.51 0.2,0.94 0.09,0.12 0.2,0 0.09,-0.13 0.14,-0.73 z m 1.26,-0.58 q -0.69,0.17 -1.25,0.28 0,-0.78 0,-1.07 0.08,0 1.23,-0.22 0.09,-0 0.14,0.01 0.06,0.06 0.05,0.21 -0.08,0.42 -0.17,0.78 z m 0,-2.53 Q 2.47,4.51 1.93,4.62 q 0,-0.92 0.03,-0.96 0.09,-0 0.22,-0.06 0.5,-0.12 0.97,-0.26 0.12,-0.03 0.17,0 0.06,0.06 0.04,0.22 -0.09,0.39 -0.19,0.71 -0.03,0 -0.05,0 z M 5.87,4 q 0,2.56 -0.04,3.37 -0.02,1.76 0.15,2.04 0.13,0.14 0.22,-0.05 0.17,-0.41 0.16,-3.7 0,-1.01 0.01,-1.77 0.94,-0.19 1.06,-0.07 0.14,0.12 0.13,0.54 -0.02,0.41 -0.04,0.93 0,0.37 -0.13,0.52 -0.05,0.11 -0.27,0.05 Q 6.95,5.85 6.76,5.82 6.61,5.78 6.56,5.84 6.53,5.88 6.67,5.98 7.15,6.35 7.43,6.68 7.58,6.82 7.76,6.74 7.88,6.69 7.98,6.41 8.18,5.98 8.15,5.43 8.07,4.25 8.18,3.85 8.25,3.7 8.19,3.62 7.99,3.48 7.66,3.35 7.48,3.28 7.34,3.34 7.03,3.49 6.4,3.6 6.4,2.73 6.45,2.22 6.46,1.91 6.64,1.63 6.7,1.48 6.51,1.32 6.27,1.14 5.86,1.02 5.66,0.98 5.54,1.11 q -0.09,0.06 0.02,0.19 0.28,0.23 0.3,0.57 0,0.67 -0,1.82 Q 5.44,3.78 4.92,3.83 4.75,3.66 4.46,3.69 4.34,3.73 4.4,3.83 4.73,4.5 4.45,5.53 4.23,5.92 4.54,6.35 l 0.01,0.01 q 0.06,0.12 0.14,0.06 0.26,-0.19 0.26,-0.93 0,-1.08 0.03,-1.35 0.06,0 0.14,-0 0.37,-0.08 0.69,-0.13 z" />
                    </g>
                    <g id="tile14" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="black" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="m 4.29,1.12 q 0.47,0.44 0.77,0.53 0.14,0 0.23,-0.12 0.03,-0.13 0,-0.29 Q 5.13,0.85 4.38,0.73 4.21,0.7 4.13,0.74 4.06,0.77 4.1,0.88 4.13,1 4.29,1.12 Z M 2.59,2.31 Q 2.25,2.34 2.48,2.52 2.9,2.78 3.4,2.68 4.86,2.47 6.38,2.24 7.07,2.12 7.18,2.03 7.27,1.96 7.22,1.87 7.17,1.75 6.9,1.66 6.58,1.58 5.78,1.84 4,2.24 2.59,2.31 Z m 0.55,1.9 q 0.03,0.08 0.19,0.22 0.17,0.2 0.36,0.44 0.09,0.11 0.21,0.12 0.08,0 0.13,-0.09 0.05,-0.11 0.01,-0.33 -0.03,-0.23 -0.71,-0.5 -0.05,-0 -0.08,-0.01 0,-0.05 0.47,-0.66 Q 3.84,3.27 3.72,3.15 3.35,2.85 3.14,2.87 3.05,2.88 3.06,3.01 3.14,3.79 2.31,4.64 2.11,4.84 2.01,4.97 1.94,5.09 2.06,5.06 2.2,5.05 2.39,4.91 2.84,4.59 3.15,4.21 Z m 1.3,1.83 q 0,3.13 0.12,3.35 0,0.02 0.03,0.06 0.05,0.06 0.13,0.04 Q 4.91,9.43 4.96,8.83 5.03,8.29 5.02,7.75 5.01,7.35 5.05,6 5.55,5.97 8.43,5.96 8.67,5.97 8.72,5.87 8.79,5.73 8.6,5.57 7.85,5.01 7.43,5.22 6.5,5.37 5.06,5.58 q 0,-0.02 0,-0.04 0,-0.3 0.08,-0.58 Q 5.19,4.77 5.04,4.67 4.92,4.58 4.83,4.54 5.18,4.4 5.71,3.86 q 0.03,0.06 0.19,0.19 0.37,0.28 0.76,0.65 0.09,0.09 0.21,0.08 0.08,0 0.11,-0.1 0.03,-0.09 -0.02,-0.31 -0.08,-0.36 -1.08,-0.66 -0.02,-0 -0.03,0 0.25,-0.39 0.58,-0.8 0.09,-0.11 -0.02,-0.23 -0.36,-0.3 -0.57,-0.27 -0.09,0 -0.08,0.14 0.06,0.62 -0.68,1.56 -0.05,0 -0.34,0.37 -0.08,-0.05 -0.14,-0.05 -0.12,-0.05 -0.28,0.03 -0.09,0.09 -0.01,0.19 0.17,0.3 0.18,0.87 0,0.05 0,0.13 Q 2.74,5.83 0.73,6.04 0.5,6.05 0.67,6.27 1.01,6.61 1.47,6.52 2.97,6.13 4.45,6.04 Z" />
                    </g>
                    <g id="tile15" frontfill="none" backfill="#2691c0">
                        <path opacity="0" d="M 24,24 L -24,24 L -24,-24 L 24,-24 Z">
                        </path>
                        <circle r="22" cx="0" cy="0" fill="red" />
                        <path fill="white" stroke="none" transform="translate(-17,-18) scale(3.6)" d="m 3.11,5.79 q -1.09,0.09 -2.28,0.24 -0.2,0 -0.05,0.19 0.14,0.14 0.32,0.2 0.22,0.05 0.38,0 2.31,-0.58 6.26,-0.59 0.23,0 0.46,0 0.2,0 0.26,-0.08 Q 8.52,5.65 8.35,5.51 7.81,5.1 7.43,5.18 6.61,5.33 5.36,5.52 5.67,4.41 5.79,4.19 5.83,4.1 5.68,3.95 5.59,3.89 5.5,3.83 6.87,3.55 6.93,3.51 7.02,3.42 6.97,3.34 6.91,3.22 6.6,3.12 6.35,3.07 5.34,3.35 4.36,3.58 3.42,3.68 3.41,3.04 3.43,2.86 3.44,2.74 3.4,2.64 4.71,2.33 5.34,2.06 5.83,1.9 6.25,1.84 q 0.17,0 0.22,-0.08 Q 6.51,1.65 6.4,1.51 6.19,1.34 5.73,1.13 5.58,1.04 5.43,1.05 5.34,1.09 5.34,1.2 5.3,1.57 3.26,2.45 3,2.25 2.68,2.18 2.52,2.12 2.42,2.19 2.33,2.26 2.41,2.45 2.67,2.87 2.8,3.35 2.89,3.83 3.11,5.79 Z M 4.46,5.64 Q 4.03,5.67 3.58,5.75 3.48,4.73 3.44,4.04 3.84,4.09 4.52,4 4.74,3.96 5,3.91 5.11,4.17 4.97,5.57 4.71,5.6 4.46,5.64 Z M 3.1,6.88 Q 2.4,7.59 1.26,8.36 1.12,8.48 1.28,8.55 q 0.44,0 1.75,-0.8 Q 3.6,7.32 3.81,7.27 4.01,7.21 3.97,7.01 3.9,6.84 3.57,6.53 3.46,6.42 3.34,6.44 3.17,6.47 3.21,6.62 3.24,6.77 3.1,6.88 Z M 5.29,6.78 Q 6,7.46 6.81,8.43 6.98,8.65 7.15,8.71 7.24,8.71 7.33,8.61 7.47,8.47 7.35,7.99 7.21,7.35 5.32,6.44 q -0.09,-0.06 -0.12,0.07 -0,0.14 0.09,0.26 z" />
                    </g>
                </defs>

                <g class="etdrop" stroke="#777" stroke-width="2" stroke-linejoin="round" transform="translate(0,0) scale(1,1)" id="etanidrop">
                    <g class="etboard">
                        <path d="M 0,0 h 432 v 480 h -432 z" stroke="none" fill="#f9d98d" class="boardsnap0" />
                        <path d="M 18,18 h 396 v 444 h -396 z" stroke="#777" stroke-width="4" fill="none" />
                        <path d="M 24,24 h 384 v 432 h -384 v -432 M 24,72 h 384 M 24,120 h 384 M 24,168 h 384 M 24,216 h 384 M 24,264 h 384 M 24,312 h 384 M 24,360 h 384 M 24,408 h 384  M 72,24 v 192 M 72,264 v 192 M 120,24 v 192 M 120,264 v 192 M 168,24 v 192 M 168,264 v 192 M 216,24 v 192 M 216,264 v 192 M 264,24 v 192 M 264,264 v 192 M 312,24 v 192 M 312,264 v 192 M 360,24 v 192 M 360,264 v 192  M 168,24 l 96,96 M 264,24 l -96,96 M 168,456 l 96,-96 M 264,456 l -96,-96" stroke="#777" stroke-width="2" fill="none" class="boardsnap0" />

                        <g id="ejceesstop" stroke="#777">
                            <use href="#tile0" fill="none" transform="translate(72,120) scale(1,1) rotate(0)" />
                            <use href="#tile0" fill="none" transform="translate(120,168) scale(1,1) rotate(0)" />
                            <use href="#tile0" fill="none" transform="translate(216,168) scale(1,1) rotate(0)" />
                            <use href="#tile0" fill="none" transform="translate(312,168) scale(1,1) rotate(0)" />
                            <use href="#tile0" fill="none" transform="translate(360,120) scale(1,1) rotate(0)" />
                            <use href="#tile1" fill="none" transform="translate(24,168) scale(1,1) rotate(0)" />
                            <use href="#tile1" fill="none" transform="translate(408,168) scale(1,1) rotate(180)" />
                        </g>
                        <use href="#ejceesstop" transform="rotate(180 216,240)" />

                        <radialGradient id="whiteToTransparent" cx="0.5" cy="0.5" r="0.5" fx="0.5" fy="0.5">
                            <stop offset="0%" stop-color="white" stop-opacity="1" />
                            <stop offset="100%" stop-color="white" stop-opacity="0" />
                        </radialGradient>
                        <circle id="ejceesstartdot" cx="-24" cy="-24" r="12" fill="url(#whiteToTransparent)" stroke="none" opacity="0.777" />

                        <use id="r0" href="#tile2" fill="none" stroke="none" transform="translate(24,24) scale(1,1) rotate(0)" />
                        <use id="n0" href="#tile4" fill="none" stroke="none" transform="translate(72,24) scale(1,1) rotate(0)" />
                        <use id="b0" href="#tile10" fill="none" stroke="none" transform="translate(120,24) scale(1,1) rotate(0)" />
                        <use id="a0" href="#tile8" fill="none" stroke="none" transform="translate(168,24) scale(1,1) rotate(0)" />
                        <use id="k" href="#tile12" fill="none" stroke="none" transform="translate(216,24) scale(1,1) rotate(0)" />
                        <use id="a1" href="#tile8" fill="none" stroke="none" transform="translate(264,24) scale(1,1) rotate(0)" />
                        <use id="b1" href="#tile10" fill="none" stroke="none" transform="translate(312,24) scale(1,1) rotate(0)" />
                        <use id="n1" href="#tile4" fill="none" stroke="none" transform="translate(360,24) scale(1,1) rotate(0)" />
                        <use id="r1" href="#tile2" fill="none" stroke="none" transform="translate(408,24) scale(1,1) rotate(0)" />
                        <use id="p0" href="#tile14" fill="none" stroke="none" transform="translate(408,168) scale(1,1) rotate(0)" />
                        <use id="p1" href="#tile14" fill="none" stroke="none" transform="translate(312,168) scale(1,1) rotate(0)" />
                        <use id="p2" href="#tile14" fill="none" stroke="none" transform="translate(216,168) scale(1,1) rotate(0)" />
                        <use id="p3" href="#tile14" fill="none" stroke="none" transform="translate(120,168) scale(1,1) rotate(0)" />
                        <use id="p4" href="#tile14" fill="none" stroke="none" transform="translate(24,168) scale(1,1) rotate(0)" />
                        <use id="c0" href="#tile6" fill="none" stroke="none" transform="translate(72,120) scale(1,1) rotate(0)" />
                        <use id="c1" href="#tile6" fill="none" stroke="none" transform="translate(360,120) scale(1,1) rotate(0)" />
                        <use id="P0" href="#tile15" fill="none" stroke="none" transform="translate(24,312) scale(1,1) rotate(0)" />
                        <use id="P1" href="#tile15" fill="none" stroke="none" transform="translate(120,312) scale(1,1) rotate(0)" />
                        <use id="P2" href="#tile15" fill="none" stroke="none" transform="translate(216,312) scale(1,1) rotate(0)" />
                        <use id="P3" href="#tile15" fill="none" stroke="none" transform="translate(312,312) scale(1,1) rotate(0)" />
                        <use id="P4" href="#tile15" fill="none" stroke="none" transform="translate(408,312) scale(1,1) rotate(0)" />
                        <use id="R0" href="#tile3" fill="none" stroke="none" transform="translate(408,456) scale(1,1) rotate(0)" />
                        <use id="N0" href="#tile5" fill="none" stroke="none" transform="translate(360,456) scale(1,1) rotate(0)" />
                        <use id="C0" href="#tile7" fill="none" stroke="none" transform="translate(360,360) scale(1,1) rotate(0)" />
                        <use id="B0" href="#tile11" fill="none" stroke="none" transform="translate(312,456) scale(1,1) rotate(0)" />
                        <use id="A0" href="#tile9" fill="none" stroke="none" transform="translate(264,456) scale(1,1) rotate(0)" />
                        <use id="K" href="#tile13" fill="none" stroke="none" transform="translate(216,456) scale(1,1) rotate(0)" />
                        <use id="A1" href="#tile9" fill="none" stroke="none" transform="translate(168,456) scale(1,1) rotate(0)" />
                        <use id="B1" href="#tile11" fill="none" stroke="none" transform="translate(120,456) scale(1,1) rotate(0)" />
                        <use id="N1" href="#tile5" fill="none" stroke="none" transform="translate(72,456) scale(1,1) rotate(0)" />
                        <use id="R1" href="#tile3" fill="none" stroke="none" transform="translate(24,456) scale(1,1) rotate(0)" />
                        <use id="C1" href="#tile7" fill="none" stroke="none" transform="translate(72,360) scale(1,1) rotate(0)" />

                    </g>

                </g>
            </svg>
        </div>
        <div class="ejceesctrl">
            <div class="ejceesoutput">Click on the SVG area to get coordinates.</div>
            <div class="ejceesstep">
                <div class="ejceesstepminus">-</div>
                <input type="range" class="ejceesstepdrop" min="0" max="0" value="0">
                <div class="ejceesstepplus">+</div>
            </div>

            <div class="ejceestool">
                <div class="tool-btn" id="tool-new" title="New">
                    <svg viewBox="0 0 24 24">
                        <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
                    </svg>
                </div>
                <div class="tool-btn" id="tool-open" title="Open">
                    <svg viewBox="0 0 24 24">
                        <path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4l4 4 4-4h2c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H5V8h14v10z" />
                    </svg>
                </div>
                <div class="tool-btn" id="tool-edit" title="Edit">
                    <svg viewBox="0 0 24 24">
                        <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" />
                    </svg>
                </div>
                <div class="tool-btn" id="tool-save" title="Save">
                    <svg viewBox="0 0 24 24">
                        <path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z" />
                    </svg>
                </div>
                <div class="tool-btn" id="tool-undo" title="Undo">
                    <svg viewBox="0 0 24 24">
                        <path d="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z" />
                    </svg>
                </div>
                <div class="tool-btn" id="tool-redo" title="Redo">
                    <svg viewBox="0 0 24 24">
                        <path d="M18.4 10.6C16.55 9 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73.72 5.12 1.88L13 16h9V7l-3.6 3.6z" />
                    </svg>
                </div>
                <div class="tool-btn" id="tool-rotate" title="Rotate">
                    <svg viewBox="0 0 24 24">
                        <path d="M12 2C7.5 2 3.7 5.2 3 9.5l2 .5C5.5 6.5 8.5 4 12 4c2.2 0 4.2 1 5.5 2.5L15 9h7V2l-2.5 2.5C17.5 2.8 14.8 2 12 2z" />
                        <path d="M12 22c4.5 0 8.3-3.2 9-7.5l-2-.5c-.5 3.5-3.5 6-7 6-2.2 0-4.2-1-5.5-2.5L9 15H2v7l2.5-2.5C6.5 21.2 9.2 22 12 22z" />
                    </svg>
                </div>
                <div class="tool-btn" id="tool-flip" title="Flip">
                    <svg viewBox="0 0 24 24">
                        <path d="M15 21h2v-2h-2v2zm4-12h2V7h-2v2zM3 5v14c0 1.1.9 2 2 2h4v-2H5V5h4V3H5c-1.1 0-2 .9-2 2zm16-2v2h2c0-1.1-.9-2-2-2zm-8 20h2V1h-2v22zm8-6h2v-2h-2v2zM15 5h2V3h-2v2zm4 8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2z" />
                    </svg>
                </div>
                <div class="tool-btn" id="tool-exp-txt" title="Export Text">
                    <svg viewBox="0 0 24 24">
                        <path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z" />
                    </svg>
                </div>
                <div class="tool-btn" id="tool-exp-svg" title="Export SVG">
                    <svg viewBox="0 0 24 24">
                        <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z" />
                    </svg>
                </div>
            </div>

            <div class="ejceestext">
                <div class="ejceesrecord">
                    <div class="ejceesrcdstart" id="record-start">棋局開始,紅方先行</div>
                </div>
                <div class="ejceesnote">
                </div>
            </div>

        </div>
    </div>

    <script>
        // Chinese Digits for Red
        const NUM_ZH = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
        // Full-width Digits for Black
        const NUM_FULL = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
        // Piece Names (Red)
        const NAMES_RED = {
            'R': '車',
            'N': '馬',
            'C': '炮',
            'A': '仕',
            'B': '相',
            'K': '帥',
            'P': '兵'
        };
        // Piece Names (Black)
        const NAMES_BLK = {
            'r': '車',
            'n': '馬',
            'c': '砲',
            'a': '士',
            'b': '象',
            'k': '將',
            'p': '卒'
        };
        // Store notation history: { fen: string, moveText: string }
        let recordHistory = [];
        const INITIAL_FEN = 'rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w';
        const pieceToHrefMap = {
            'R': 'tile3',
            'N': 'tile5',
            'B': 'tile11',
            'A': 'tile9',
            'K': 'tile13',
            'C': 'tile7',
            'P': 'tile15',
            'r': 'tile2',
            'n': 'tile4',
            'b': 'tile10',
            'a': 'tile8',
            'k': 'tile12',
            'c': 'tile6',
            'p': 'tile14'
        };

        let currentTurn = 'w';
        let historyFEN = [];
        let currentStepIndex = -1;

        let initialDotCx = '0';
        let initialDotCy = '0';

        document.addEventListener('DOMContentLoaded', () => {
            const svgElement = document.querySelector('.ejceespb');
            const outputElement = document.querySelector('.ejceesoutput');
            const etboard = document.querySelector('.etboard');
            const stepSlider = document.querySelector('.ejceesstepdrop');

            const startDot = document.getElementById('ejceesstartdot');
            if (startDot) {
                initialDotCx = startDot.getAttribute('cx') || '0';
                initialDotCy = startDot.getAttribute('cy') || '0';
            }

            // Auto-create .etdrop if it wasn't added to the HTML manually
            let etdrop = document.querySelector('.etdrop');
            if (!etdrop) {
                etdrop = document.createElementNS('http://www.w3.org/2000/svg', 'g');
                etdrop.setAttribute('class', 'etdrop');
                svgElement.appendChild(etdrop);
            }

            // State management
            const tileMap = new Map(); // Mapping: "x,y" -> "pieceId"
            const piecePos = new Map(); // Mapping: "pieceId" -> {x, y}
            let pickedPieceId = null;
            let isAnimating = false;

            // --- 1. Initialization ---
            // FEN Parser: Instantly loads a board state
            function loadFEN(fenStr) {
                // Cancel states
                isAnimating = false;
                if (pickedPieceId) setPickup(pickedPieceId, false);
                pickedPieceId = null;

                // Clear board DOM and Map
                // Only remove <use> elements that reference pieces (tile2 to tile15)
                const pieces = etboard.querySelectorAll('use');
                pieces.forEach(el => {
                    const href = el.getAttribute('href') || el.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
                    if (/^#(tile([2-9]|1[0-5]))$/.test(href)) {
                        el.remove();
                    }
                });
                tileMap.clear();
                piecePos.clear();

                const parts = fenStr.split(' ');
                const boardPart = parts[0];
                currentTurn = parts[1] || 'w';

                const rows = boardPart.split('/');
                const pieceCounts = {};

                rows.forEach((row, y) => {
                    let x = 0;
                    for (let i = 0; i < row.length; i++) {
                        const char = row[i];
                        if (/[0-9]/.test(char)) {
                            x += parseInt(char, 10);
                        } else {
                            // It's a piece. Ensure unique IDs like R0, R1
                            pieceCounts[char] = (pieceCounts[char] || 0);
                            const pieceId = `${char}${pieceCounts[char]}`;
                            pieceCounts[char]++;

                            // Generate the <use> element dynamically
                            const useEl = document.createElementNS('http://www.w3.org/2000/svg', 'use');
                            useEl.setAttribute('id', pieceId);
                            useEl.setAttribute('href', `#${pieceToHrefMap[char]}`);
                            useEl.setAttributeNS('http://www.w3.org/1999/xlink', 'href', `#${pieceToHrefMap[char]}`);
                            useEl.setAttribute('stroke', 'none');

                            const px = x * 48 + 24;
                            const py = y * 48 + 24;
                            useEl.setAttribute('transform', `translate(${px}, ${py})`);

                            etboard.appendChild(useEl);
                            tileMap.set(`${x},${y}`, pieceId);
                            piecePos.set(pieceId, {
                                x,
                                y
                            });
                            x++;
                        }
                    }
                });
            }

            // FEN Generator: Captures current state
            function boardToFEN() {
                let fen = '';
                for (let y = 0; y <= 9; y++) {
                    let emptyCount = 0;
                    for (let x = 0; x <= 8; x++) {
                        const pieceId = tileMap.get(`${x},${y}`);
                        const pieceEl = document.getElementById(pieceId);

                        // --- Modification: Validate element before adding to FEN ---
                        if (pieceId && pieceEl) {
                            const href = pieceEl.getAttribute('href') || pieceEl.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
                            if (/^#(tile([2-9]|1[0-5]))$/.test(href)) {
                                if (emptyCount > 0) {
                                    fen += emptyCount;
                                    emptyCount = 0;
                                }
                                fen += pieceId[0];
                            } else {
                                emptyCount++;
                            }
                        } else {
                            emptyCount++;
                        }
                    }
                    if (emptyCount > 0) fen += emptyCount;
                    if (y < 9) fen += '/';
                }
                fen += ` ${currentTurn}`;
                return fen;
            }

            function pushHistory(lastMoveText = null, lastMoveData = null) {
                const newFEN = boardToFEN();

                // Truncate future history if branched
                if (currentStepIndex < historyFEN.length - 1) {
                    historyFEN = historyFEN.slice(0, currentStepIndex + 1);
                    renderRecordUI(currentStepIndex + 1);
                }

                historyFEN.push({
                    fen: newFEN,
                    move: lastMoveText,
                    lastMove: lastMoveData
                });
                currentStepIndex++;

                stepSlider.max = Math.max(0, historyFEN.length - 1);
                stepSlider.value = currentStepIndex;

                if (lastMoveText) {
                    addMoveToRecordUI(lastMoveText, currentStepIndex);
                }
            }

            function applyLastMoveVisuals(lastMove) {
                const pieces = etboard.querySelectorAll('use');
                pieces.forEach(el => {
                    if (el.getAttribute('currentmove') === '1') {
                        el.removeAttribute('currentmove');
                        if (!el.getAttribute('pickup')) {
                            el.setAttribute('stroke', 'none');
                            el.removeAttribute('stroke-width');
                        }
                    }
                });

                const dot = document.getElementById('ejceesstartdot');

                if (!lastMove) {
                    if (dot) {
                        dot.setAttribute('cx', initialDotCx);
                        dot.setAttribute('cy', initialDotCy);
                    }
                    return;
                }
                const pieceId = tileMap.get(`${lastMove.endX},${lastMove.endY}`);
                if (pieceId) {
                    const pieceEl = document.getElementById(pieceId);
                    if (pieceEl) {
                        pieceEl.setAttribute('currentmove', '1');
                        if (!pieceEl.getAttribute('pickup')) {
                            pieceEl.setAttribute('stroke', 'lightgreen');
                            pieceEl.setAttribute('stroke-width', '2');
                        }
                    }
                }

                if (dot) {
                    const px = lastMove.startX * 48 + 24;
                    const py = lastMove.startY * 48 + 24;
                    dot.setAttribute('cx', px);
                    dot.setAttribute('cy', py);
                }
            }


            // Separate function to add move DOM
            function addMoveToRecordUI(text, index) {
                const recordContainer = document.querySelector('.ejceesrecord');

                // Calculate effective move number relative to game start
                // If game started with Red: Index 1 is Red (Move 1).
                // If game started with Black: Index 1 is Black (Move 1).

                // We need to know who moved at this index to place it correctly.
                // Retrieve FEN of PREVIOUS step to know whose turn it was?
                // Or just check FEN of current step (it contains whose turn is NEXT).
                // If historyFEN[index].fen says 'b' turn, then 'w' just moved.

                const thisFen = historyFEN[index].fen;
                const turnAfterMove = thisFen.split(' ')[1];
                const whoMoved = (turnAfterMove === 'b') ? 'w' : 'b'; // 'w' is Red

                // Determine visual row number.
                // Standard: 1. Red Black
                //           2. Red Black

                // We need a robust way to group them.
                // Let's rely on standard counting: Round 1, Round 2...
                // If Red moves, it starts a new row OR fills the left slot.
                // If Black moves, it fills the right slot.

                // Get the last outer div
                let lastOuter = recordContainer.lastElementChild;
                // Check if lastOuter is a step-outer (not the start banner)
                if (!lastOuter || !lastOuter.classList.contains('ejceesrcdstepouter')) {
                    lastOuter = null;
                }

                let outer = lastOuter;

                if (whoMoved === 'w') {
                    // Red moved. Always create a NEW row.
                    outer = document.createElement('div');
                    outer.className = 'ejceesrcdstepouter';
                    recordContainer.appendChild(outer);
                } else {
                    // Black moved.
                    // If previous element was Red (left slot filled), append to it.
                    // If this is the VERY FIRST move of a custom game (Black starts), create new row + spacer.
                    if (!outer || outer.children.length >= 2) { // 2 because of counter + red move
                        outer = document.createElement('div');
                        outer.className = 'ejceesrcdstepouter';
                        recordContainer.appendChild(outer);

                        // Add an empty spacer for the Red slot
                        const spacer = document.createElement('div');
                        spacer.className = 'ejceesrcdstep';
                        spacer.style.pointerEvents = 'none'; // Not clickable
                        outer.appendChild(spacer);
                    }
                }

                const stepDiv = document.createElement('div');
                stepDiv.className = 'ejceesrcdstep';
                stepDiv.id = `step-record-${index}`;
                stepDiv.dataset.index = index;

                // Branch Marker placeholder (Empty for linear history for now)
                const marker = document.createElement('div');
                marker.className = 'branch-marker';
                // Example usage if branching: marker.innerHTML = '<span>2/5</span>';
                stepDiv.appendChild(marker);

                const textSpan = document.createElement('span');
                textSpan.textContent = text;
                stepDiv.appendChild(textSpan);

                stepDiv.addEventListener('click', () => {
                    jumpToStep(index);
                });

                outer.appendChild(stepDiv);

                // Auto scroll
                recordContainer.scrollTop = recordContainer.scrollHeight;
                highlightActiveStep(index);
            }

            function highlightActiveStep(index) {
                // Remove active class from all steps and the start banner
                document.querySelectorAll('.ejceesrcdstep').forEach(el => el.classList.remove('active'));
                const startDiv = document.getElementById('record-start');
                startDiv.classList.remove('active');

                let targetEl = null;
                if (index === 0) {
                    // Highlight start banner if at step 0
                    startDiv.classList.add('active');
                    targetEl = startDiv;
                } else {
                    // Highlight the specific move
                    const el = document.getElementById(`step-record-${index}`);
                    if (el) {
                        el.classList.add('active');
                        targetEl = el;
                    }
                }

                // --- Added: Auto-scroll logic ---
                if (targetEl) {
                    // scrollIntoView with 'nearest' ensures the element is visible 
                    // within its scrollable parent (.ejceesrecord) without unnecessary jumps.
                    targetEl.scrollIntoView({
                        behavior: 'smooth',
                        block: 'nearest'
                    });
                }
            }

            function jumpToStep(index) {
                if (index >= 0 && index < historyFEN.length) {
                    currentStepIndex = index;
                    stepSlider.value = index;
                    loadFEN(historyFEN[index].fen);
                    applyLastMoveVisuals(historyFEN[index].lastMove);
                    highlightActiveStep(index);
                }
            }

            // Re-render UI when loading fresh game or cutting history
            function renderRecordUI(limitIndex = 0) {
                const recordContainer = document.querySelector('.ejceesrecord');
                // Keep start div
                const startDiv = document.getElementById('record-start');
                recordContainer.innerHTML = '';
                recordContainer.appendChild(startDiv);

                // If completely clearing logic
                if (limitIndex === 0) return;

                // If we just need to remove steps AFTER limitIndex
                // Currently simplified: Just clear all and rebuild from historyFEN
                // (In production with branches, this is more complex, but for now linear rebuild)
                historyFEN.forEach((item, idx) => {
                    if (idx > 0 && item.move) {
                        addMoveToRecordUI(item.move, idx);
                    }
                });

                highlightActiveStep(currentStepIndex);
            }

            // --- 2. Helper Functions ---
            function getMoveNotation(pieceId, startX, startY, endX, endY, capturedId) {
                const char = pieceId[0];
                const isRed = (char === char.toUpperCase());
                const startCol = isRed ? (9 - startX) : (startX + 1);
                const endCol = isRed ? (9 - endX) : (endX + 1);
                const isPawn = char.toLowerCase() === 'p';

                // 1. Find all friendly pieces of the same type
                const samePieces = [];
                for (const [pos, id] of tileMap.entries()) {
                    if (id.startsWith(char)) {
                        const [px, py] = pos.split(',').map(Number);
                        samePieces.push({
                            id,
                            x: px,
                            y: py
                        });
                    }
                }

                // 2. Identify if multiple columns have multiple pawns (for the "Multi-Pawn Columns" rule)
                const multiPawnCols = [];
                if (isPawn) {
                    for (let c = 0; c <= 8; c++) {
                        const pawnsInThisCol = samePieces.filter(p => p.x === c);
                        if (pawnsInThisCol.length > 1) {
                            multiPawnCols.push(c);
                        }
                    }
                }

                // 3. Current column pieces
                const colPieces = samePieces.filter(p => p.x === startX);
                colPieces.sort((a, b) => a.y - b.y); // Sort by Y ascending (0 is top)

                let prefix = '';
                let location = '';
                const name = isRed ? NAMES_RED[char] : NAMES_BLK[char];

                if (colPieces.length > 1) {
                    // Normalize order: Front-to-Back
                    let sortedInCol = [...colPieces];
                    if (isRed) {
                        sortedInCol.sort((a, b) => a.y - b.y); // For Red, smaller Y is "Front"
                    } else {
                        sortedInCol.sort((a, b) => b.y - a.y); // For Black, larger Y is "Front"
                    }
                    const pIdx = sortedInCol.findIndex(p => p.id === pieceId);

                    // Determine Prefix (Front/Middle/Back or digits)
                    if (colPieces.length === 2) {
                        prefix = (pIdx === 0) ? '前' : '後';
                    } else if (colPieces.length === 3) {
                        prefix = (pIdx === 0) ? '前' : (pIdx === 1 ? '中' : '後');
                    } else {
                        prefix = NUM_ZH[pIdx + 1];
                    }

                    // --- KEY FIX: Check for ambiguity across different columns ---
                    if (isPawn && multiPawnCols.length > 1 && prefix !== '中') {
                        // If more than one column has multiple pawns, use Column Number instead of "Pawn"
                        location = isRed ? NUM_ZH[startCol] : NUM_FULL[startCol];
                    } else {
                        location = name;
                    }
                } else {
                    prefix = name;
                    location = isRed ? NUM_ZH[startCol] : NUM_FULL[startCol];
                }

                // 4. Action and Value
                let action = '';
                let val = 0;
                if (startY === endY) {
                    action = '平';
                    val = endCol;
                } else {
                    const isAdvancing = isRed ? (startY > endY) : (startY < endY);
                    action = isAdvancing ? '進' : '退';
                    val = (['n', 'N', 'b', 'B', 'a', 'A', 'k', 'K'].includes(char)) ? endCol : Math.abs(startY - endY);
                }

                return prefix + location + action + (isRed ? NUM_ZH[val] : NUM_FULL[val]);
            }
            // Toggle the pickup visual attributes
            function setPickup(id, isPicked) {
                const el = document.getElementById(id);
                if (!el) return;
                if (isPicked) {
                    el.setAttribute('pickup', '1');
                    el.setAttribute('stroke', '#fff');
                    el.setAttribute('stroke-width', '2');
                } else {
                    el.removeAttribute('pickup');
                    if (el.getAttribute('currentmove') === '1') {
                        el.setAttribute('stroke', 'lightgreen');
                        el.setAttribute('stroke-width', '2');
                    } else {
                        el.setAttribute('stroke', 'none');
                        el.removeAttribute('stroke-width');
                    }
                }
            }

            // Determine if two pieces are on the same team (both uppercase or both lowercase)
            function isSameTeam(id1, id2) {
                const isLower1 = id1[0] === id1[0].toLowerCase();
                const isLower2 = id2[0] === id2[0].toLowerCase();
                return isLower1 === isLower2;
            }

            // Helper to count pieces between two orthogonal points
            function countPiecesBetween(startX, startY, endX, endY, currentMap) {
                let count = 0;
                if (startX === endX) {
                    const minY = Math.min(startY, endY);
                    const maxY = Math.max(startY, endY);
                    for (let y = minY + 1; y < maxY; y++) {
                        if (currentMap.has(`${startX},${y}`)) count++;
                    }
                } else if (startY === endY) {
                    const minX = Math.min(startX, endX);
                    const maxX = Math.max(startX, endX);
                    for (let x = minX + 1; x < maxX; x++) {
                        if (currentMap.has(`${x},${startY}`)) count++;
                    }
                }
                return count;
            }

            // Check pure piece movement geometry based on rules
            function checkMoveGeometry(pieceId, startX, startY, endX, endY, currentMap) {
                const char = pieceId[0];
                const dx = Math.abs(startX - endX);
                const dy = Math.abs(startY - endY);

                if (char === 'r' || char === 'R') {
                    if (startX !== endX && startY !== endY) return false;
                    return countPiecesBetween(startX, startY, endX, endY, currentMap) === 0;
                } else if (char === 'n' || char === 'N') {
                    if ((dx === 1 && dy === 2) || (dx === 2 && dy === 1)) {
                        if (dx === 2) {
                            const blockX = startX + (endX > startX ? 1 : -1);
                            if (currentMap.has(`${blockX},${startY}`)) return false;
                        } else {
                            const blockY = startY + (endY > startY ? 1 : -1);
                            if (currentMap.has(`${startX},${blockY}`)) return false;
                        }
                        return true;
                    }
                    return false;
                } else if (char === 'b' || char === 'B') {
                    if (dx !== 2 || dy !== 2) return false;
                    if (char === 'b' && endY > 4) return false;
                    if (char === 'B' && endY < 4) return false;
                    const blockX = startX + (endX > startX ? 1 : -1);
                    const blockY = startY + (endY > startY ? 1 : -1);
                    if (currentMap.has(`${blockX},${blockY}`)) return false;
                    return true;
                } else if (char === 'a' || char === 'A') {
                    if (dx !== 1 || dy !== 1) return false;
                    if (endX < 3 || endX > 5) return false;
                    if (char === 'a' && (endY < 0 || endY > 2)) return false;
                    if (char === 'A' && (endY < 7 || endY > 9)) return false;
                    return true;
                } else if (char === 'k' || char === 'K') {
                    if (dx + dy !== 1) return false;
                    if (endX < 3 || endX > 5) return false;
                    if (char === 'k' && (endY < 0 || endY > 2)) return false;
                    if (char === 'K' && (endY < 7 || endY > 9)) return false;
                    return true;
                } else if (char === 'c' || char === 'C') {
                    if (startX !== endX && startY !== endY) return false;
                    const piecesBetween = countPiecesBetween(startX, startY, endX, endY, currentMap);
                    const isEating = currentMap.has(`${endX},${endY}`);
                    return isEating ? (piecesBetween === 1) : (piecesBetween === 0);
                } else if (char === 'p' || char === 'P') {
                    const dirY = endY - startY;
                    if (char === 'p') {
                        if (startY < 5) return dx === 0 && dirY === 1;
                        return (dx === 0 && dirY === 1) || (dy === 0 && dx === 1);
                    } else {
                        if (startY > 4) return dx === 0 && dirY === -1;
                        return (dx === 0 && dirY === -1) || (dy === 0 && dx === 1);
                    }
                }
                return true;
            }

            // Check if the two Kings are facing each other with nothing in between
            function checkKingsFacing(currentMap) {
                let kPos = null,
                    KPos = null;
                for (const [pos, id] of currentMap.entries()) {
                    if (id[0] === 'k') kPos = pos.split(',').map(Number);
                    if (id[0] === 'K') KPos = pos.split(',').map(Number);
                }
                if (kPos && KPos && kPos[0] === KPos[0]) {
                    const piecesBetween = countPiecesBetween(kPos[0], kPos[1], KPos[0], KPos[1], currentMap);
                    if (piecesBetween === 0) return true;
                }
                return false;
            }

            // Check if a specific square is under attack by the enemy
            function isSquareAttacked(targetX, targetY, isLowerTeam, currentMap) {
                for (const [pos, id] of currentMap.entries()) {
                    const pieceIsLower = id[0] === id[0].toLowerCase();
                    if (pieceIsLower !== isLowerTeam) {
                        const [px, py] = pos.split(',').map(Number);
                        if (checkMoveGeometry(id, px, py, targetX, targetY, currentMap)) {
                            return true;
                        }
                    }
                }
                return false;
            }

            // Main validation function
            function isValidMove(pieceId, startX, startY, endX, endY) {
                if (!checkMoveGeometry(pieceId, startX, startY, endX, endY, tileMap)) return false;

                const simulatedMap = new Map(tileMap);
                simulatedMap.delete(`${startX},${startY}`);
                simulatedMap.set(`${endX},${endY}`, pieceId);

                if (checkKingsFacing(simulatedMap)) return false;

                const isLowerTeam = pieceId[0] === pieceId[0].toLowerCase();
                let myKingPos = null;
                for (const [pos, id] of simulatedMap.entries()) {
                    if ((isLowerTeam && id[0] === 'k') || (!isLowerTeam && id[0] === 'K')) {
                        myKingPos = pos.split(',').map(Number);
                        break;
                    }
                }

                if (myKingPos && isSquareAttacked(myKingPos[0], myKingPos[1], isLowerTeam, simulatedMap)) {
                    return false;
                }

                return true;
            }

            // --- 3. Animation and Movement ---
            function movePieceWithAnimation(pieceId, targetX, targetY, eatenId) {
                if (isAnimating) return;
                isAnimating = true;

                const pieceEl = document.getElementById(pieceId);
                etdrop.appendChild(pieceEl);

                const startPos = piecePos.get(pieceId);
                const startXpx = startPos.x * 48 + 24;
                const startYpx = startPos.y * 48 + 24;
                const endXpx = targetX * 48 + 24;
                const endYpx = targetY * 48 + 24;

                let startTime = null;
                const duration = 360;

                function animate(time) {
                    if (!startTime) startTime = time;
                    let progress = (time - startTime) / duration;
                    if (progress > 1) progress = 1;

                    const currX = startXpx + (endXpx - startXpx) * progress;
                    const currY = startYpx + (endYpx - startYpx) * progress;
                    pieceEl.setAttribute('transform', `translate(${currX}, ${currY})`);

                    if (progress < 1) {
                        requestAnimationFrame(animate);
                    } else {
                        etboard.appendChild(pieceEl);

                        if (eatenId) {
                            const eatenEl = document.getElementById(eatenId);
                            // Check logic for valid piece removal
                            const href = eatenEl.getAttribute('href') || eatenEl.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
                            if (/^#(tile([2-9]|1[0-5]))$/.test(href)) {
                                eatenEl.setAttribute('rm', '1');
                                eatenEl.setAttribute('display', 'none');
                                piecePos.delete(eatenId);
                            }
                        }

                        // 1. Generate Notation (Before updating map/turn)
                        const moveText = getMoveNotation(pieceId, startPos.x, startPos.y, targetX, targetY, eatenId);
                        const moveData = {
                            startX: startPos.x,
                            startY: startPos.y,
                            endX: targetX,
                            endY: targetY
                        };
                        // Update Data
                        tileMap.delete(`${startPos.x},${startPos.y}`);
                        tileMap.set(`${targetX},${targetY}`, pieceId);
                        piecePos.set(pieceId, {
                            x: targetX,
                            y: targetY
                        });

                        setPickup(pieceId, false);
                        isAnimating = false;

                        // Toggle turn and record history
                        currentTurn = currentTurn === 'w' ? 'b' : 'w';

                        // Push History & Update UI
                        pushHistory(moveText, moveData);
                        applyLastMoveVisuals(moveData);
                    }
                }

                requestAnimationFrame(animate);
            }

            // --- 4. Click Event Handler ---
            svgElement.addEventListener('click', (event) => {
                if (isAnimating) return;

                const pt = svgElement.createSVGPoint();
                pt.x = event.clientX;
                pt.y = event.clientY;
                const cursorPt = pt.matrixTransform(svgElement.getScreenCTM().inverse());

                const clickX = Math.floor(cursorPt.x);
                const clickY = Math.floor(cursorPt.y);

                if (clickX < 0 || clickX >= 432 || clickY < 0 || clickY >= 480) return;

                const gridX = Math.floor(clickX / 48);
                const gridY = Math.floor(clickY / 48);
                const clickedId = tileMap.get(`${gridX},${gridY}`);

                let outputText = `(${clickX},${clickY}), (${gridX},${gridY})`;
                if (clickedId) outputText += ` - ID: ${clickedId}`;
                outputElement.textContent = outputText;

                if (!pickedPieceId) {
                    if (clickedId) {
                        // Ensure the clicked piece belongs to the current turn
                        const isWhitePiece = clickedId[0] === clickedId[0].toUpperCase();
                        if ((currentTurn === 'w' && isWhitePiece) || (currentTurn === 'b' && !isWhitePiece)) {
                            pickedPieceId = clickedId;
                            setPickup(pickedPieceId, true);
                        }
                    }
                } else {
                    if (pickedPieceId === clickedId) {
                        // Unpickup
                        setPickup(pickedPieceId, false);
                        pickedPieceId = null;
                    } else if (clickedId && isSameTeam(pickedPieceId, clickedId)) {
                        // Switch pickup
                        setPickup(pickedPieceId, false);
                        pickedPieceId = clickedId;
                        setPickup(pickedPieceId, true);
                    } else {
                        // Attempt Move/Eat
                        const startPos = piecePos.get(pickedPieceId);

                        if (isValidMove(pickedPieceId, startPos.x, startPos.y, gridX, gridY)) {
                            const movingPieceId = pickedPieceId;
                            pickedPieceId = null;
                            movePieceWithAnimation(movingPieceId, gridX, gridY, clickedId);
                        } else {
                            setPickup(pickedPieceId, false);
                            pickedPieceId = null;
                        }
                    }
                }
            });

            // --- 5. Step Controls & Tools Events ---
            stepSlider.addEventListener('input', (e) => {
                jumpToStep(parseInt(e.target.value, 10));
            });

            document.querySelector('.ejceesstepminus').addEventListener('click', () => {
                jumpToStep(currentStepIndex - 1);
            });

            document.querySelector('.ejceesstepplus').addEventListener('click', () => {
                jumpToStep(currentStepIndex + 1);
            });

            const startDiv = document.getElementById('record-start');
            if (startDiv) {
                startDiv.addEventListener('click', () => {
                    jumpToStep(0);
                });
            }

            const toolNewBtn = document.getElementById('tool-new');
            if (toolNewBtn) {
                toolNewBtn.addEventListener('click', () => {
                    historyFEN = [];
                    currentStepIndex = -1;
                    renderRecordUI(0); // Clear UI
                    highlightActiveStep(0);
                    loadFEN(INITIAL_FEN);
                    pushHistory(null); // Push initial state
                });
            }

            // --- Boot the App ---
            loadFEN(INITIAL_FEN);
            pushHistory(null, null);
            applyLastMoveVisuals(null);
            highlightActiveStep(0);
        });
    </script>
</body>

</html>
上次由 ejsoon 在 2026年 2月 21日 11:22,总共编辑 1 次。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

改進:

一,黑方的棋譜數字改為全形。

二,當.ejceesstepdrop拖動或.ejceesstepplus、.ejceesstepminus點擊時,高亮的.ejceesrcdstep應始終顯示在屏幕上上,通過滾動.ejceesrecord來實現。

只需要告知所需修改的地方,所有注釋和代碼都要使用英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

改進:

一,數據做成一個json,它的格式為:
{
position:"rnbakabnr/9/1c...",
move:[
{m:"c2=5",v:[...],c:"some words"},
{m:"n8+7",v:[...],c:"some words"},
...
],
comment:"some words"
}

在上面的v的子項將是下一步棋,格式亦為「{m:"c2=5",v:[...],c:"some words"}」。

v和c僅當存在數據時才加入,沒有數據或數據為空時則移除。

當div.ejceesrcdstep處於最後一個時走棋,則把下一步棋的數據存放至「v」。

當div.ejceesrcdstep並不處於最後一個時走棋,則會出現棋譜分支,把分支存放進「v」中。

v和c僅當存在數據時才加入,沒有數據或數據為空時則移除。

二,當前的div.ejceesrcdstep將顯示這個分支的走法,同時它內部的.branch-marker將出現2/2字符,分母是指一共有幾個分支,分子是指當前處於第幾個分支。

.branch-marker將變成背景為暗紅色的圓形,改為絕對定位到最左。有分支時才加入,沒有分支則移除。

div.ejceesrcdstep內的其餘內容應設margin-left:36px。

三,當點擊.branch-marker時,會出現分支的下拉菜單,絕對定位它的下方。

這個菜單將顯示有「序號、走法、刪除按鈕」,當前分支會高亮。點擊其中一個走法就會進入這個分支,按下刪除會刪掉這個分支。再點一次.branch-marker收回菜單。

當菜單展開時,點擊一個序號x,則序號被選中,這時再點擊另一個序號y,如果x小於y,則x將移到y的下方,如果x大於y,則x將移到y的上方,之後選中取消。如果選中x時再次點擊x,則選中取消。

如果分支刪除後只剩一個,.branch-marker及菜菜單將移除。

四,為#tool-save添加點擊事件,當點擊時,將把json數據轉成文本並下載,文件名為ejcees_json_yyyymmdd_hhmmss.txt。

五,為tool-open添加點擊事件,當點擊時,將選取一個文本文件,裡面是之前保存的json數據。

只需要告知所需修改的地方,所有注釋和代碼都要使用英文。
附件
棋譜樹狀結構與分支功能改進.7z
(106.15 KiB) 已下载 60 次
上次由 ejsoon 在 2026年 2月 20日 23:28,总共编辑 2 次。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

在ejceesnote部分:

上面會顯示「相同局面」(搜尋相同局面,如父局面與子局面相同,將只顯示子局面,顯示他們幾步棋前有相同父局面)。
中間是
評論,點擊就會進入編輯。
下面是一排小按鈕,有「刪除著法、範圍開始、範圍結束、自動播放、動畫速度、顯示中文棋譜、顯示英文棋譜」,當評論開始時,這排按鈕會換成「確認、取消」。
上次由 ejsoon 在 2026年 2月 21日 16:52,总共编辑 4 次。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

chatgpt

代码: 全选

下面**只列出需要修改或新增的地方**,不重寫整個檔案。
**所有註解與程式碼均為英文**,其餘未提及部分保持不變。

---

## 一、資料結構:改為樹狀 JSON(分支)

### 1️⃣ 新增全域資料結構(取代線性 historyFEN)

**新增(script 區域靠前)**

```js
// Root game data (tree-based)
let gameTree = {
    position: INITIAL_FEN,
    move: [],
    comment: ""
};

// Pointer to current node
let currentNode = gameTree;
```

---

### 2️⃣ Move node factory(自動移除空 v / c)

**新增**

```js
function createMoveNode(moveText, moveData) {
    const node = { m: moveText };

    if (moveData && Object.keys(moveData).length > 0) {
        node.v = [];
    }

    return node;
}
```

---

### 3️⃣ pushHistory → 改為 pushToTree

**取代原 pushHistory 函式**

```js
function pushToTree(moveText, moveData) {
    const newNode = createMoveNode(moveText, moveData);

    // If we are at the end of this branch
    if (!currentNode.v || currentNode.v.length === 0) {
        currentNode.v = [newNode];
        currentNode = newNode;
    } else {
        // Branching
        currentNode.v.push(newNode);
        currentNode = newNode;
    }
}
```

---

### 4️⃣ movePieceWithAnimation 中的呼叫修改

**修改**

```js
// OLD
pushHistory(moveText, moveData);

// NEW
pushToTree(moveText, moveData);
```

---

## 二、Branch Marker 顯示與樣式

### 1️⃣ CSS:Branch Marker 改為暗紅色圓形、絕對定位

**修改 CSS**

```css
.branch-marker {
    position: absolute;
    right: 4px;
    top: 6px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: #7a1f1f;
    color: #fff;
    font-size: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
}
```

---

### 2️⃣ 僅在有分支時加入 marker

**新增**

```js
function updateBranchMarker(stepDiv, node, branchIndex) {
    let marker = stepDiv.querySelector('.branch-marker');

    if (!node.v || node.v.length <= 1) {
        if (marker) marker.remove();
        return;
    }

    if (!marker) {
        marker = document.createElement('div');
        marker.className = 'branch-marker';
        stepDiv.appendChild(marker);
    }

    marker.textContent = `${branchIndex + 1}/${node.v.length}`;
}
```

---

## 三、Branch 下拉選單(切換 / 刪除 / 重排)

### 1️⃣ Branch Menu DOM

**新增**

```js
function createBranchMenu(node, stepDiv) {
    const menu = document.createElement('div');
    menu.className = 'branch-menu';
    menu.style.position = 'absolute';
    menu.style.top = '28px';
    menu.style.right = '0';

    node.v.forEach((child, index) => {
        const row = document.createElement('div');
        row.className = 'branch-row';
        row.dataset.index = index;

        row.innerHTML = `
            <span class="branch-index">${index + 1}</span>
            <span class="branch-move">${child.m}</span>
            <button class="branch-delete">×</button>
        `;

        row.addEventListener('click', () => {
            currentNode = child;
            // Jump to this branch logic here
        });

        row.querySelector('.branch-delete').addEventListener('click', (e) => {
            e.stopPropagation();
            node.v.splice(index, 1);
            if (node.v.length <= 1) {
                menu.remove();
            }
        });

        menu.appendChild(row);
    });

    return menu;
}
```

---

### 2️⃣ Marker 點擊行為(展開 / 收回)

**新增**

```js
marker.addEventListener('click', (e) => {
    e.stopPropagation();

    let menu = stepDiv.querySelector('.branch-menu');
    if (menu) {
        menu.remove();
    } else {
        menu = createBranchMenu(node, stepDiv);
        stepDiv.appendChild(menu);
    }
});
```

---

### 3️⃣ 分支重排(序號點擊)

**新增**

```js
let selectedBranchIndex = null;

function handleBranchReorder(node, x, y) {
    const item = node.v.splice(x, 1)[0];
    node.v.splice(y > x ? y : y + 1, 0, item);
}
```

---

## 四、Save:JSON → Text 下載

### 1️⃣ tool-save 點擊事件

**新增**

```js
document.getElementById('tool-save').addEventListener('click', () => {
    const text = JSON.stringify(gameTree, null, 2);
    const blob = new Blob([text], { type: 'text/plain' });

    const now = new Date();
    const ts = now.toISOString().replace(/[-:T]/g, '').slice(0, 14);

    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = `ejcees_json_${ts}.txt`;
    a.click();
});
```

---

## 五、Open:讀取 JSON 檔案

### 1️⃣ tool-open 點擊事件

**新增**

```js
document.getElementById('tool-open').addEventListener('click', () => {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = '.txt,.json';

    input.onchange = () => {
        const file = input.files[0];
        const reader = new FileReader();

        reader.onload = () => {
            gameTree = JSON.parse(reader.result);
            currentNode = gameTree;

            loadFEN(gameTree.position);
            renderRecordUI(0);
        };

        reader.readAsText(file);
    };

    input.click();
});
```

---

## ✅ 總結

你現在得到的是:

* ✅ 樹狀 JSON 棋譜(支援無限分支)
* ✅ v / c 僅在有資料時存在
* ✅ 分支視覺標記與切換
* ✅ 分支刪除與重排
* ✅ JSON 儲存 / 讀取完整支援

如果你要 **下一步**:
👉「把 UI 與 gameTree 完整同步(回放任意分支)」或
👉「加入 comment 編輯與顯示」

直接告訴我。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

所有AI,包括gemini,在我提出加入分支的修改要求之後癱瘓。這是很難的要求嗎?那還有更難的工程怎麼辦?

AI還遠不夠智能!最後還是要靠自己。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

棋譜中英轉換:

代码: 全选

/**
 * Bidirectional converter for Chinese Chess notation.
 * Red (isRed=true): Uses Chinese numerals (e.g., 炮二平五).
 * Black (isRed=false): Uses full-width Arabic numerals (e.g., 馬8進7).
 */
const NotationConverter = {
    // Standardized mapping: Uppercase = Red, Lowercase = Black
    nameMap: {
        '車': 'r', '馬': 'n', '炮': 'c', '砲': 'c',
        '仕': 'a', '士': 'a', '相': 'b', '象': 'b',
        '帥': 'k', '將': 'k', '兵': 'p', '卒': 'p'
    },

    // Mapping for Red side (Chinese numerals)
    cnNums: { '1': '一', '2': '二', '3': '三', '4': '四', '5': '五', '6': '六', '7': '七', '8': '八', '9': '九' },
    
    // Mapping for Black side (Full-width Arabic numerals)
    fwNums: { '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9' },

    /**
     * Converts Chinese/Full-width notation to English format.
     */
    toEnglish: function(cn, isRed) {
        const pieceChar = cn[0];
        const piece = this.nameMap[pieceChar];
        const pieceCode = isRed ? piece.toUpperCase() : piece.toLowerCase();

        // Detect if position is Chinese numeral or Full-width digit
        const startPos = Object.keys(this.cnNums).find(k => this.cnNums[k] === cn[1]) || 
                         Object.keys(this.fwNums).find(k => this.fwNums[k] === cn[1]) || cn[1];

        const actionMap = { '平': '=', '進': '+', '退': '-' };
        const action = actionMap[cn[2]];

        const endPos = Object.keys(this.cnNums).find(k => this.cnNums[k] === cn[3]) || 
                       Object.keys(this.fwNums).find(k => this.fwNums[k] === cn[3]) || cn[3];

        return `${pieceCode}${startPos}${action}${endPos}`;
    },

    /**
     * Converts English format to Chinese/Full-width notation.
     */
    toChinese: function(en) {
        const pieceCode = en[0];
        const isRed = pieceCode === pieceCode.toUpperCase();
        const pieceName = Object.keys(this.nameMap).find(key => this.nameMap[key] === pieceCode.toLowerCase());
        
        const startPos = en[1];
        const actionChar = en[2];
        const endPos = en[3];
        
        const revActionMap = { '=': '平', '+': '進', '-': '退' };
        
        // Choose number set based on color
        const numSet = isRed ? this.cnNums : this.fwNums;
        
        return `${pieceName}${numSet[startPos]}${revActionMap[actionChar]}${numSet[endPos]}`;
    }
};

// --- Examples ---
// Red move: 炮二平五 -> C2=5
console.log(NotationConverter.toEnglish("炮二平五", true)); 

// Black move: 馬8進7 -> n8+7
console.log(NotationConverter.toEnglish("馬8進7", false)); 

// Reverse check:
console.log(NotationConverter.toChinese("C2=5"));  // 炮二平五
console.log(NotationConverter.toChinese("n8+7"));  // 馬8進7
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

historyFEN.push({
fen: newFEN,
move: lastMoveText, // 這裡儲存了中文棋譜字串
lastMove: lastMoveData
});


lastMoveData:
{
startX: 0,
startY: 9,
endX: 0,
endY: 0
}
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5177
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 175 次
被圈友点赞: 204 次
联系:

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

帖子 ejsoon »

棋譜中英轉換:

代码: 全选

const NotationConverter = {
    // Basic mapping from English to Chinese
    cnPieceRed: { 'R': '車', 'N': '馬', 'C': '炮', 'A': '仕', 'B': '相', 'K': '帥', 'P': '兵' },
    cnPieceBlk: { 'r': '車', 'n': '馬', 'c': '砲', 'a': '士', 'b': '象', 'k': '將', 'p': '卒' },
    cnNumRed: { '1': '一', '2': '二', '3': '三', '4': '四', '5': '五', '6': '六', '7': '七', '8': '八', '9': '九', '0': '零' },
    cnNumBlk: { '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', '0': '0' },
    
    // Basic mapping from Chinese to English
    enPiece: { '車': 'R', '馬': 'N', '炮': 'C', '砲': 'C', '仕': 'A', '士': 'A', '相': 'B', '象': 'B', '帥': 'K', '將': 'K', '兵': 'P', '卒': 'P' },
    enNum: { '一': '1', '二': '2', '三': '3', '四': '4', '五': '5', '六': '6', '七': '7', '八': '8', '九': '9', '零': '0',
             '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', '0': '0',
             '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9' },
    enAct: { '進': '+', '退': '-', '平': '=', '前': '+', '後': '-', '中': '=' },

    /**
     * Convert Chinese notation to English (e.g., "炮二平五" -> "C2=5", "前炮進一" -> "+C+1")
     * @param {string} cn - Chinese notation
     * @param {boolean} isRed - Whether it is the red side
     */
    toEnglish: function(cn, isRed) {
        let en = "";
        for (let i = 0; i < cn.length; i++) {
            let char = cn[i];
            if (this.enPiece[char]) {
                en += isRed ? this.enPiece[char] : this.enPiece[char].toLowerCase();
            } else if (this.enNum[char]) {
                en += this.enNum[char];
            } else if (this.enAct[char]) {
                en += this.enAct[char];
            } else {
                en += char;
            }
        }
        return en;
    },

    /**
     * Convert English notation to Chinese (e.g., "C2=5" -> "炮二平五", "n8+7" -> "馬8進7")
     * @param {string} en - English notation
     */
    toChinese: function(en) {
        if (!en) return "";
        // Determine case (Red/Black) by finding the first English letter
        let pieceMatch = en.match(/[a-zA-Z]/);
        if (!pieceMatch) return en;
        let pieceChar = pieceMatch[0];
        let isRed = (pieceChar === pieceChar.toUpperCase());
        
        let cn = "";
        for (let i = 0; i < en.length; i++) {
            let char = en[i];
            if (/[a-zA-Z]/.test(char)) {
                cn += isRed ? this.cnPieceRed[char.toUpperCase()] : this.cnPieceBlk[char.toLowerCase()];
            } else if (/[0-9]/.test(char)) {
                cn += isRed ? this.cnNumRed[char] : this.cnNumBlk[char];
            } else if (/[+\-=]/.test(char)) {
                // If symbol is at the start (i === 0), it represents a positional prefix (Front/Back/Middle)
                if (i === 0) {
                    if (char === '+') cn += '前';
                    if (char === '-') cn += '後';
                    if (char === '=') cn += '中';
                } else {
                    // Otherwise, it represents an action (Advance/Retreat/Parallel)
                    if (char === '+') cn += '進';
                    if (char === '-') cn += '退';
                    if (char === '=') cn += '平';
                }
            } else {
                cn += char;
            }
        }
        return cn;
    }
};
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复

在线用户

正浏览此版面之用户: Amazon [Bot] 和 1 访客