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

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

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

帖子 ejsoon »

這個gemini也太好用了吧!我對gemini非常滿意!

代码: 全选

<!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;
}

.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;
}

.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;
}

/* --- 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; }
}

/* 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; }
}
    </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 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)"/>

<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">N</div>
               <div class="tool-btn" id="tool-open" title="Open">O</div>
               <div class="tool-btn" id="tool-edit" title="Edit">E</div>
               <div class="tool-btn" id="tool-save" title="Save">S</div>
               <div class="tool-btn" id="tool-undo" title="Undo">U</div>
               <div class="tool-btn" id="tool-redo" title="Redo">R</div>
               <div class="tool-btn" id="tool-rotate" title="Rotate">RT</div>
               <div class="tool-btn" id="tool-flip" title="Flip">F</div>
               <div class="tool-btn" id="tool-exp-txt" title="Export Text">ET</div>
               <div class="tool-btn" id="tool-exp-svg" title="Export SVG">ES</div>
           </div>
       </div>
    </div>

<script>
    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;

    document.addEventListener('DOMContentLoaded', () => {
        const svgElement = document.querySelector('.ejceespb');
        const outputElement = document.querySelector('.ejceesoutput');
        const etboard = document.querySelector('.etboard');
        const stepSlider = document.querySelector('.ejceesstepdrop');
        
        // 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() {
            const newFEN = boardToFEN();
            // Truncate future history if we moved while exploring past steps
            historyFEN = historyFEN.slice(0, currentStepIndex + 1);
            historyFEN.push(newFEN);
            currentStepIndex++;
            
            stepSlider.max = Math.max(0, historyFEN.length - 1);
            stepSlider.value = currentStepIndex;
        }

        // --- 2. Helper Functions ---
        
        // 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');
                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);
       // --- Modification: Double check it's a piece before hiding ---
       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);
       }
   }

                    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 after move
                    currentTurn = currentTurn === 'w' ? 'b' : 'w';
                    pushHistory();
                }
            }

            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) => {
            currentStepIndex = parseInt(e.target.value, 10);
            if (historyFEN[currentStepIndex]) {
                loadFEN(historyFEN[currentStepIndex]);
            }
        });

        document.querySelector('.ejceesstepminus').addEventListener('click', () => {
            if (currentStepIndex > 0) {
                currentStepIndex--;
                stepSlider.value = currentStepIndex;
                loadFEN(historyFEN[currentStepIndex]);
            }
        });

        document.querySelector('.ejceesstepplus').addEventListener('click', () => {
            if (currentStepIndex < historyFEN.length - 1) {
                currentStepIndex++;
                stepSlider.value = currentStepIndex;
                loadFEN(historyFEN[currentStepIndex]);
            }
        });

        const toolNewBtn = document.getElementById('tool-new');
        if (toolNewBtn) {
            toolNewBtn.addEventListener('click', () => {
                loadFEN(INITIAL_FEN);
                historyFEN = [];
                currentStepIndex = -1;
                pushHistory();
            });
        }

        // --- Boot the App ---
        loadFEN(INITIAL_FEN);
        pushHistory();
    });
    </script>
</body>
</html>
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

改圖標,記譜:

代码: 全选

改進:

一,把class="tool-btn"的內容都換成相應的svg-path,其stroke可在更改css的類名.enable時切換。

二,在.ejceesctrl最後面增加「.ejceestext>(.ejceesrecord+.ejceesnote)」,其中.ejceesctrl佔滿餘下部分,.ejceesrecord的寬度足夠放進12個漢字(如果是手機端,size=12px,如果是電腦端,size=16px)。餘下部分由.ejceesnote佔據。

三,當棋局開始(或新建)時,.ejceesrecord加入一個div.ejceesrcdstart,佔滿一行,內容是「棋局開始,紅方先行」。如果是編輯完成,則其內容是「特定局面,輪到紅方」或「特定局面,輪到黑方」。

四,當走棋時,.ejceesrecord加入一個div.ejceesrcdstep,兩個並排佔滿一行,內容是走棋記譜。

在每個div.ejceesrcdstep內部的前面預留一個「分支記號」,大概36*36px大小。文字內容不會被此記號影響整齊和遮擋。「分支」內容可能是「2/5」的形式,用一個淺紅色背景的圈套住。

每兩個div.ejceesrcdstep會放進一個div.ejceesrcdstepouter中,div.ejceesrcdstepouter內部的最前面是一個從1開始的遞增數字。

如果是「特定局面,輪到黑方」,則首個div.ejceesrcdstepouter只存在一個div.ejceesrcdstep並會顯示在右邊。

五,記譜規則是:

每步棋都有四個字,前兩個字會指明是哪個棋子,後兩個字會說明它的行動落點。

當有小寫字母(即黑方)行動時,它的數字都會用阿拉伯數字,而大寫字母(即紅方)走棋時就會用中文數字。

首先講前兩個字,例如r為黑方的車,如果是「車9」,即為從左到右第9列存在一個黑方的車。如果第9列有兩個黑方的車,則會說「前車」、「後車」,前車指y坐標大的車。

後兩個字是「進、退、平」,縱坐標增減就是進退,平指的是平到幾路線。要注意進退時數字形容的是增量,而平形容的是第幾路縱線。比如「車9平2」指的是在9路線的黑車平移到第二路線,「車9進2」指在9路線的黑車往前進兩格。

黑方的「進」就是紅方的「退」,黑方的九路線就是紅方的一路線。如「車九進一」,這個紅車就是id含有R且處於x軸為0的棋子,往前進一步,其y坐標減少一格。

棋子id首字母與棋子中文名的對應如下:

r:車
n:馬
c:砲
a:士
b:象
k:將
p:卒

R:車
N:馬
C:炮
A:仕
B:相
K:帥
P:兵

其中,nN、aA、bB沒有「平」,只有「進退」,而它們的「進退」後面跟的不是增量,而指的是第幾路縱線。比如「馬三退二」指紅馬從三路線退到二路線。

如用英文,「進退平」分別用「+-=」表示。

兵(或卒)的記譜規則如下:

當有一列兵且數量為四個或五個時,從前往後數第幾個兵就寫「幾兵」,如「二兵平三」,「二兵」指第二個兵。

當有兩列兵時,就寫「前二平一」,指在第二列的前面那個兵平二。

如果有三個兵在一列,中間的兵都直接叫「中兵」,不管另外兩兵在不在同一列,如「中兵平二」。

如用英文,「前中後」分別用「+=-」表示。

六,當點擊一個div.ejceesrcdstep時,棋局會跳到當前局面,.ejceesstepdrop進度條也會發生變化。

七,代碼和注釋都要用英文,但可以出現中文字符串(如"車")。只需要告知要更改的地方。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

gemini

代码: 全选

Edit with the Docs app
Make tweaks, leave comments, and share with others to edit at the same time.
NO THANKSUSE THE APP
改進: 一,把class="tool-btn"的內容都換成相應的svg-path,其stroke...

Here are the modifications to implement the SVG icons, the new record layout, and the complex Chinese Chess notation logic.
1. HTML Modifications

Replace the .ejceesctrl block with this updated structure. This includes SVG paths for buttons and the new text/record section.

HTML

<div class="ejceesctrl">
   <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="M7.11 8.53L5.7 7.11C4.8 8.27 4.24 9.61 4.07 11h2.02c.14-.87.49-1.72 1.02-2.47zM6.09 13H4.07c.17 1.39.72 2.73 1.62 3.89l1.41-1.42c-.52-.75-.87-1.59-1.01-2.47zm1.01 5.32c1.16 1.24 2.66 2.07 4.29 2.4l.6-2.01c-1.1-.21-2.09-.77-2.89-1.6l-2-1.21zm7.8-14.73l-.6 2.01c1.1.21 2.09.77 2.89 1.6l1.41-1.41c-1.16-1.24-2.66-2.07-4.29-2.4l.59.2zm3.69 2.3l-1.41 1.41c.52.75.87 1.59 1.01 2.47h2.02c-.17-1.39-.72-2.73-1.62-3.88zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></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="ejceesstep">
       <div class="ejceesstepminus">-</div>
       <input type="range" class="ejceesstepdrop" min="0" max="0" value="0">
       <div class="ejceesstepplus">+</div>
   </div>

   <div class="ejceesoutput">Click on the SVG area to get coordinates.</div>

   <div class="ejceestext">
       <div class="ejceesrecord">
           <div class="ejceesrcdstart" id="record-start">Game Start, Red First</div>
           </div>
       <div class="ejceesnote">
           </div>
   </div>
</div>
2. CSS Modifications

Update style.css to handle the new text section layout, the specific record styles, and the SVG buttons.

CSS

/* --- Updated Control Layout --- */
.ejceesctrl {
   display: flex;
   flex-direction: column;
   background-color: #333;
   color: #fff;
   flex: 1;
   box-sizing: border-box;
   overflow: hidden; /* Important for scrollable record */
}

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

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

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

/* --- 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;
}

.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;
   display: flex;
   align-items: center;
   padding-left: 0; /* Branch marker handles spacing */
   cursor: pointer;
   position: relative;
   font-family: "SimSun", serif; /* Songti for traditional look */
   white-space: nowrap;
}

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

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

/* Branch Marker */
.branch-marker {
   width: 36px;
   height: 36px;
   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 for Record --- */

/* Mobile */
@media (max-width: 767px) {
   .ejceesrecord {
       width: 160px; /* Approx 12 chars * 12px + padding/borders */
       font-size: 12px;
   }
}

/* Desktop */
@media (min-width: 768px) {
   .ejceesrecord {
       width: 220px; /* Approx 12 chars * 16px + padding/borders */
       font-size: 16px;
   }
}
3. JavaScript Modifications

This requires adding the MoveNotation class (to generate Chinese/English text), handling the record DOM updates, and linking history clicks to the state.

Add these functions and variables inside your DOMContentLoaded block.

3.1. Add helper maps and variables at the top:

JavaScript

    // Chinese Digits
   const NUM_ZH = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
   // 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 = [];

3.2. Add the Notation Generator Function:

Place this helper function in the helper section. This encapsulates the complex logic for "Front/Back", "Adv/Ret/Traverse", and Chinese vs Arabic numerals.

JavaScript

    function getMoveNotation(pieceId, startX, startY, endX, endY, capturedId) {
       const char = pieceId[0]; // e.g., 'R', 'n'
       const isRed = (char === char.toUpperCase());
       
       // 1. Determine Column Numbers (1-9)
       // Red: Right to Left (0->9, 8->1). Black: Left to Right (0->1, 8->9)
       const startCol = isRed ? (9 - startX) : (startX + 1);
       const endCol = isRed ? (9 - endX) : (endX + 1);
       
       // 2. Determine Action (Advance/Retreat/Traverse)
       // Note: Y increases downwards.
       // Red Advance: startY > endY (y decreases).
       // Black Advance: startY < endY (y increases).
       let action = '';
       let val = 0; // The 4th character (digit or column)

       if (startY === endY) {
           action = '平'; // Traverse
           val = endCol;
       } else {
           const isAdvancing = isRed ? (startY > endY) : (startY < endY);
           action = isAdvancing ? '進' : '退';
           
           // For N, B, A, K (diagonals), value is the target column
           if (['n','N','b','B','a','A','k','K'].includes(char)) {
               val = endCol;
           } else {
               // For R, C, P (orthogonal), value is absolute distance
               val = Math.abs(startY - endY);
           }
       }

       // 3. Determine Piece Name & Location (First 2 chars)
       const name = isRed ? NAMES_RED[char] : NAMES_BLK[char];
       let prefix = ''; // e.g., "前", "後", "二", "中"
       let location = ''; // e.g., "八" (column)

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

       // Filter to pieces in the same column as the moving piece
       const colPieces = samePieces.filter(p => p.x === startX);
       // Sort by Y: Red (bottom to top), Black (top to bottom) logic for "Front/Back"
       // Actually, physically: Y=0 is top.
       // Red View: Y=9 is near. Smaller Y is "Forward".
       // Black View: Y=0 is near. Larger Y is "Forward".
       // We just need relative order. Let's sort by Y ascending first (0 to 9).
       colPieces.sort((a, b) => a.y - b.y);

       if (colPieces.length > 1) {
           // Need special prefix
           if (char.toLowerCase() === 'p') {
               // Pawns Logic
               const index = colPieces.findIndex(p => p.id === pieceId);
               const count = colPieces.length;
               
               // Note: colPieces is sorted Y 0->9 (Top->Bottom).
               // Red (at bottom): Y=9 is Back, Y=0 is Front. index 0 is "Front" visually?
               // Wait. Red "Front" is Y=0 side (opponent side).
               // So for Red, smaller Y is Front. colPieces[0] is Front.
               // Black (at top): Y=9 is Front (opponent side). Larger Y is Front.
               // So for Black, colPieces[last] is Front.
               
               // Let's normalize to "Front-to-Back" array
               let sortedPawns = [...colPieces]; // 0=Top, N=Bottom
               if (!isRed) {
                   // Black: Top(0) is Back, Bottom(9) is Front.
                   // Actually standard defines:
                   // Red: Front is closer to opponent (small Y). Back is closer to self (large Y).
                   // Black: Front is closer to opponent (large Y). Back is closer to self (small Y).
                   
                   // Let's rely on standard logic:
                   // Red P: sort Y ascending (0..9). 0 is Front.
                   // Black P: sort Y descending (9..0). 9 is Front.
               } else {
                   // Red
                   sortedPawns.sort((a,b) => a.y - b.y); // Small Y (Front) first
               }
               
               if (!isRed) {
                   // Black
                   sortedPawns.sort((a,b) => b.y - a.y); // Large Y (Front) first
               }

               // Now sortedPawns[0] is the Front-most pawn.
               const pIdx = sortedPawns.findIndex(p => p.id === pieceId); // 0-based index
               
               if (count === 2) {
                   prefix = (pIdx === 0) ? '前' : '後';
                   location = name; // e.g. 前兵
               } else if (count === 3) {
                   if (pIdx === 0) prefix = '前';
                   else if (pIdx === 1) prefix = '中';
                   else prefix = '後';
                   location = name;
               } else if (count >= 4) {
                   // "二兵", "三兵" etc.
                   prefix = NUM_ZH[pIdx + 1]; // "一", "二"...
                   location = name;
               }
           } else {
               // R, C, N, etc. (Max 2 usually, 3 in rare handicap/puzzle cases, standard assumes 2)
               // Red: Front has smaller Y. Back has larger Y.
               // Black: Front has larger Y. Back has smaller Y.
               const isFront = isRed ? (startY < colPieces.find(p => p.y !== startY).y)
                                     : (startY > colPieces.find(p => p.y !== startY).y);
               
               prefix = isFront ? '前' : '後';
               location = name;
           }
       } else {
           // Unique in column
           prefix = name;
           location = isRed ? NUM_ZH[startCol] : startCol; // "車八", "車8"
       }

       // 4. Construct Final String
       // Format: [Prefix][Location][Action][Value]
       
       let strVal = '';
       if (isRed) {
           // Red uses Chinese numbers for Value
           strVal = NUM_ZH[val];
       } else {
           // Black uses Arabic numbers for Value
           strVal = val.toString();
       }

       return prefix + location + action + strVal;
   }

3.3. Update movePieceWithAnimation (Record the move):

We need to generate the text before isAnimating becomes false, then update the UI.

JavaScript

            // ... inside animate function ...
           // Replace the `else` block with this:
           } 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);

               // 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);
           }

3.4. Update pushHistory and UI Render Logic:

This function now handles the DOM manipulation for the .ejceesrecord panel.

JavaScript

        function pushHistory(lastMoveText = null) {
           const newFEN = boardToFEN();
           
           // Truncate future history if branched
           if (currentStepIndex < historyFEN.length - 1) {
               // We are rewriting history
               historyFEN = historyFEN.slice(0, currentStepIndex + 1);
               // Also clear record UI after this point
               renderRecordUI(currentStepIndex + 1); // Pass limit to redraw
           }
           
           historyFEN.push({ fen: newFEN, move: lastMoveText });
           currentStepIndex++;
           
           stepSlider.max = Math.max(0, historyFEN.length - 1);
           stepSlider.value = currentStepIndex;

           // Render the new move in the list
           if (lastMoveText) {
               addMoveToRecordUI(lastMoveText, currentStepIndex);
           }
       }

       // Separate function to add move DOM
       function addMoveToRecordUI(text, index) {
           const recordContainer = document.querySelector('.ejceesrecord');
           
           // Logic: Steps are pairs (Red, Black) inside .ejceesrcdstepouter
           // Step 1 (Index 1) -> Outer 1, Inner 1 (Red)
           // Step 2 (Index 2) -> Outer 1, Inner 2 (Black)
           // Step 3 (Index 3) -> Outer 2, Inner 1 (Red)
           
           // Note: Index 0 is Start FEN. Move 1 is Index 1.
           const moveNum = Math.ceil(index / 2);
           const isRedMove = (index % 2 !== 0); // 1=Red, 2=Black, 3=Red...

           let outer = document.getElementById(`step-outer-${moveNum}`);
           
           if (!outer) {
               outer = document.createElement('div');
               outer.className = 'ejceesrcdstepouter';
               outer.id = `step-outer-${moveNum}`;
               recordContainer.appendChild(outer);
           }

           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) {
           document.querySelectorAll('.ejceesrcdstep').forEach(el => el.classList.remove('active'));
           if (index > 0) {
               const el = document.getElementById(`step-record-${index}`);
               if (el) el.classList.add('active');
           }
       }

       function jumpToStep(index) {
           if (index >= 0 && index < historyFEN.length) {
               currentStepIndex = index;
               stepSlider.value = index;
               loadFEN(historyFEN[index].fen);
               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);
       }

3.5. Update loadFEN (Initialize Header text):

Change the text of the start banner based on whether it's the initial board or a custom FEN.

JavaScript

        function loadFEN(fenStr) {
           // ... (existing clearing logic) ...

           const parts = fenStr.split(' ');
           // ... (existing parsing logic) ...
           
           // Update Start Record Text
           const startDiv = document.getElementById('record-start');
           if (fenStr === INITIAL_FEN || fenStr.startsWith(INITIAL_FEN.split(' ')[0])) {
               startDiv.textContent = 'Game Start, Red First';
           } else {
               const turnText = currentTurn === 'w' ? 'Red' : 'Black';
               startDiv.textContent = `Specific Position, ${turnText} to Move`;
               
               // Special handling: If Black to move, the first step needs to be pushed right.
               // This logic is handled dynamically when addMoveToRecordUI is called next.
               // But for the START banner, we just set text.
           }
       }

3.6. Update tool-new listener:

Ensure it resets history structure correctly.

JavaScript

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

3.7. Update Slider and Buttons to use jumpToStep:

JavaScript

        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);
       });

3.8. Fix history initialization:

Change the global historyFEN initialization to store objects, not just strings.

JavaScript

    // At the very top
   let historyFEN = []; // Array of { fen: string, move: string }

And update the initial boot call at the bottom:

JavaScript

    // Boot
   loadFEN(INITIAL_FEN);
   pushHistory(null); // Initial state has no move text
4. Handling "Black Move First" Layout Logic

If the game starts with Black (e.g., custom FEN), the first move should appear on the right side of the .ejceesrcdstepouter.

Update addMoveToRecordUI:

JavaScript

        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}`;
           // ... (rest of element creation) ...
           
           outer.appendChild(stepDiv);
           // ...
       }

This logic ensures that if Black moves first (custom scenario), an empty slot appears on the left. If Red moves, it starts a fresh row.
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

改進:

一,頁面一打開時,div.ejceesrcdstart應高亮。當.ejceesstepdrop拖到最左邊時也要高亮。

當點擊「新建」時,所有走的棋譜應清空,div.ejceesrcdstart也應高亮。

二,div.ejceesrcdstart的文字內容應為「棋局開始,紅方先行」。

未來在加上「編輯盤面」的功能之後,div.ejceesrcdstart的文字內容可以為「特定局面,輪到紅方」或「特定局面,輪到黑方」。

這些文字內容不用翻譯成英文。

這個內容不會隨著棋子的行動而發生變化,只在頁面剛開始時、新建局面時、編輯局面完成時變化。

三,當點擊div.ejceesrcdstart時,應回到局面初始狀態。

四,ejceesrecord的寬度(不包括前面的數字)應比兩個div.ejceesrcdstep大7px或12px,不能有横向滾動軸。

五,當有兩個兵在同一列,我們就說形成了一列兵。當棋盤上在不同的縱線形成兩列兵時,就不能再用「前兵進一」這種描述,因為你不知道它指的是哪一列兵。比如在第三列和第四列都有一列兵,則需要說「前四平五」,則為第四列的前面那個兵平五。

六,代碼和注釋都要用英文,但可以出現中文字符串(如"車")。只需告知要更改的地方。
上次由 ejsoon 在 2026年 2月 18日 21:27,总共编辑 3 次。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

代码: 全选

<!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;
}

.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;
   display: flex;
   align-items: center;
   padding-left: 0; /* Branch marker handles spacing */
   cursor: pointer;
   position: relative;
   font-family: "SimSun", serif; /* Songti for traditional look */
   white-space: nowrap;
}

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

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

/* Branch Marker */
.branch-marker {
   width: 36px;
   height: 36px;
   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: 160px; /* 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: 220px; /* 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 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)"/>

<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">Game Start, Red First</div>
           </div>
       <div class="ejceesnote">
           </div>
   </div>
   
       </div>
    </div>

<script>
   // Chinese Digits
   const NUM_ZH = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
   // 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;

    document.addEventListener('DOMContentLoaded', () => {
        const svgElement = document.querySelector('.ejceespb');
        const outputElement = document.querySelector('.ejceesoutput');
        const etboard = document.querySelector('.etboard');
        const stepSlider = document.querySelector('.ejceesstepdrop');
        
        // 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';
           // Update Start Record Text
           const startDiv = document.getElementById('record-start');
           if (fenStr === INITIAL_FEN || fenStr.startsWith(INITIAL_FEN.split(' ')[0])) {
               startDiv.textContent = 'Game Start, Red First';
           } else {
               const turnText = currentTurn === 'w' ? 'Red' : 'Black';
               startDiv.textContent = `Specific Position, ${turnText} to Move`;
               
               // Special handling: If Black to move, the first step needs to be pushed right.
               // This logic is handled dynamically when addMoveToRecordUI is called next.
               // But for the START banner, we just set text.
           }

            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) {
           const newFEN = boardToFEN();
           
           // Truncate future history if branched
           if (currentStepIndex < historyFEN.length - 1) {
               // We are rewriting history
               historyFEN = historyFEN.slice(0, currentStepIndex + 1);
               // Also clear record UI after this point
               renderRecordUI(currentStepIndex + 1); // Pass limit to redraw
           }
           
           historyFEN.push({ fen: newFEN, move: lastMoveText });
           currentStepIndex++;
           
           stepSlider.max = Math.max(0, historyFEN.length - 1);
           stepSlider.value = currentStepIndex;

           // Render the new move in the list
           if (lastMoveText) {
               addMoveToRecordUI(lastMoveText, currentStepIndex);
           }
       }

       // 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) {
           document.querySelectorAll('.ejceesrcdstep').forEach(el => el.classList.remove('active'));
           if (index > 0) {
               const el = document.getElementById(`step-record-${index}`);
               if (el) el.classList.add('active');
           }
       }

       function jumpToStep(index) {
           if (index >= 0 && index < historyFEN.length) {
               currentStepIndex = index;
               stepSlider.value = index;
               loadFEN(historyFEN[index].fen);
               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]; // e.g., 'R', 'n'
       const isRed = (char === char.toUpperCase());
       
       // 1. Determine Column Numbers (1-9)
       // Red: Right to Left (0->9, 8->1). Black: Left to Right (0->1, 8->9)
       const startCol = isRed ? (9 - startX) : (startX + 1);
       const endCol = isRed ? (9 - endX) : (endX + 1);
       
       // 2. Determine Action (Advance/Retreat/Traverse)
       // Note: Y increases downwards.
       // Red Advance: startY > endY (y decreases).
       // Black Advance: startY < endY (y increases).
       let action = '';
       let val = 0; // The 4th character (digit or column)

       if (startY === endY) {
           action = '平'; // Traverse
           val = endCol;
       } else {
           const isAdvancing = isRed ? (startY > endY) : (startY < endY);
           action = isAdvancing ? '進' : '退';
           
           // For N, B, A, K (diagonals), value is the target column
           if (['n','N','b','B','a','A','k','K'].includes(char)) {
               val = endCol;
           } else {
               // For R, C, P (orthogonal), value is absolute distance
               val = Math.abs(startY - endY);
           }
       }

       // 3. Determine Piece Name & Location (First 2 chars)
       const name = isRed ? NAMES_RED[char] : NAMES_BLK[char];
       let prefix = ''; // e.g., "前", "後", "二", "中"
       let location = ''; // e.g., "八" (column)

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

       // Filter to pieces in the same column as the moving piece
       const colPieces = samePieces.filter(p => p.x === startX);
       // Sort by Y: Red (bottom to top), Black (top to bottom) logic for "Front/Back"
       // Actually, physically: Y=0 is top.
       // Red View: Y=9 is near. Smaller Y is "Forward".
       // Black View: Y=0 is near. Larger Y is "Forward".
       // We just need relative order. Let's sort by Y ascending first (0 to 9).
       colPieces.sort((a, b) => a.y - b.y);

       if (colPieces.length > 1) {
           // Need special prefix
           if (char.toLowerCase() === 'p') {
               // Pawns Logic
               const index = colPieces.findIndex(p => p.id === pieceId);
               const count = colPieces.length;
               
               // Note: colPieces is sorted Y 0->9 (Top->Bottom).
               // Red (at bottom): Y=9 is Back, Y=0 is Front. index 0 is "Front" visually?
               // Wait. Red "Front" is Y=0 side (opponent side).
               // So for Red, smaller Y is Front. colPieces[0] is Front.
               // Black (at top): Y=9 is Front (opponent side). Larger Y is Front.
               // So for Black, colPieces[last] is Front.
               
               // Let's normalize to "Front-to-Back" array
               let sortedPawns = [...colPieces]; // 0=Top, N=Bottom
               if (!isRed) {
                   // Black: Top(0) is Back, Bottom(9) is Front.
                   // Actually standard defines:
                   // Red: Front is closer to opponent (small Y). Back is closer to self (large Y).
                   // Black: Front is closer to opponent (large Y). Back is closer to self (small Y).
                   
                   // Let's rely on standard logic:
                   // Red P: sort Y ascending (0..9). 0 is Front.
                   // Black P: sort Y descending (9..0). 9 is Front.
               } else {
                   // Red
                   sortedPawns.sort((a,b) => a.y - b.y); // Small Y (Front) first
               }
               
               if (!isRed) {
                   // Black
                   sortedPawns.sort((a,b) => b.y - a.y); // Large Y (Front) first
               }

               // Now sortedPawns[0] is the Front-most pawn.
               const pIdx = sortedPawns.findIndex(p => p.id === pieceId); // 0-based index
               
               if (count === 2) {
                   prefix = (pIdx === 0) ? '前' : '後';
                   location = name; // e.g. 前兵
               } else if (count === 3) {
                   if (pIdx === 0) prefix = '前';
                   else if (pIdx === 1) prefix = '中';
                   else prefix = '後';
                   location = name;
               } else if (count >= 4) {
                   // "二兵", "三兵" etc.
                   prefix = NUM_ZH[pIdx + 1]; // "一", "二"...
                   location = name;
               }
           } else {
               // R, C, N, etc. (Max 2 usually, 3 in rare handicap/puzzle cases, standard assumes 2)
               // Red: Front has smaller Y. Back has larger Y.
               // Black: Front has larger Y. Back has smaller Y.
               const isFront = isRed ? (startY < colPieces.find(p => p.y !== startY).y)
                                     : (startY > colPieces.find(p => p.y !== startY).y);
               
               prefix = isFront ? '前' : '後';
               location = name;
           }
       } else {
           // Unique in column
           prefix = name;
           location = isRed ? NUM_ZH[startCol] : startCol; // "車八", "車8"
       }

       // 4. Construct Final String
       // Format: [Prefix][Location][Action][Value]
       
       let strVal = '';
       if (isRed) {
           // Red uses Chinese numbers for Value
           strVal = NUM_ZH[val];
       } else {
           // Black uses Arabic numbers for Value
           strVal = val.toString();
       }

       return prefix + location + action + strVal;
   }
        // 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');
                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);

               // 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);
           }
            }

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

        // --- Boot the App ---
        loadFEN(INITIAL_FEN);
        pushHistory(null);
    });
    </script>
</body>
</html>
最近一個還沒改。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

gemini fast

代码: 全选

Edit with the Docs app
Make tweaks, leave comments, and share with others to edit at the same time.
NO THANKSUSE THE APP
棋譜介面功能改進建議

針對您提出的六項改進要求,以下是需要修改的代碼部分及詳細說明。
一、 div.ejceesrcdstart 的高亮邏輯

修改說明:在 CSS 中增加 .ejceesrcdstart.active 的樣式,並在 JavaScript 的 renderRecordUI 函數中,當 currentStepIndex 為 -1 時為該元素添加 active 類。

CSS 修改:

CSS

/* 修改現有的 .ejceesrcdstart 樣式,並增加 active 狀態 */
.ejceesrcdstart {
   /* ... 保持原有樣式 ... */
   cursor: pointer; /* 增加手型指針 */
}

.ejceesrcdstart.active {
   background-color: #3d5afe; /* 與步驟高亮顏色一致 */
   color: white;
}

JavaScript 修改 (renderRecordUI 函數內):

JavaScript

function renderRecordUI(highlightIndex) {
   recordContainer.innerHTML = '';
   
   // 創建開始節點
   const startDiv = document.createElement('div');
   startDiv.className = 'ejceesrcdstart';
   // 根據需求三修改文字
   startDiv.innerText = '棋局開始,紅方先行';
   
   // 需求一:如果索引為 -1,則高亮
   if (highlightIndex === -1) {
       startDiv.classList.add('active');
   }

   startDiv.addEventListener('click', () => {
       jumpToStep(-1);
   });
   recordContainer.appendChild(startDiv);

   // ... 後續渲染步驟的代碼 ...
}
二、「新建」功能重置

修改說明:在 tool-new 的點擊事件中,顯式調用 jumpToStep(-1) 以確保 UI 高亮正確。

JavaScript 修改:

JavaScript

if (toolNewBtn) {
   toolNewBtn.addEventListener('click', () => {
       historyFEN = [];
       currentStepIndex = -1;
       // 渲染 UI 並高亮開始位置
       renderRecordUI(-1);
       loadFEN(INITIAL_FEN);
       pushHistory(null);
   });
}
三、 文字內容動態顯示

修改說明:在 renderRecordUI 中根據當前局面的 FEN 判斷輪到哪一方。

JavaScript 修改:

JavaScript

// 在 renderRecordUI 中
const currentFEN = currentStepIndex === -1 ? INITIAL_FEN : historyFEN[currentStepIndex].fen;
const sideToMove = currentFEN.split(' ')[1]; // 'w' 或 'b'

if (currentStepIndex === -1) {
   // 初始局面
   startDiv.innerText = '棋局開始,紅方先行';
} else {
   // 未來擴展「編輯盤面」後的邏輯
   const sideText = (sideToMove === 'w') ? '紅方' : '黑方';
   startDiv.innerText = `特定局面,輪到${sideText}`;
}
四、 ejceesrecord 寬度調整與滾動軸

修改說明:修改媒體查詢中的寬度計算方式,確保寬度足夠容納兩個步驟塊加上邊距。

CSS 修改:

CSS

/* Mobile */
@media (max-width: 767px) {
   .ejceesrecord {
       /* 計算:24px(數字) + 2*step寬度 + 12px(額外寬度) */
       /* 假設原本文字寬度約為 60px 左右 */
       width: 175px;
       overflow-x: hidden; /* 強制隱藏橫向滾動 */
   }
}

/* Desktop */
@media (min-width: 768px) {
   .ejceesrecord {
       width: 240px;
       overflow-x: hidden;
   }
}
五、 兵卒記譜邏輯(處理多列兵)

修改說明:這需要修改 getChineseMove 函數。當同一縱線有兩個兵(一列兵)且棋盤上存在多於一組這樣的「一列兵」時,使用縱線編號代替「前/後」。

JavaScript 修改邏輯示意:

JavaScript

function getChineseMove(piece, fromX, fromY, toX, toY, board) {
   // 1. 檢查棋盤上所有與當前移動棋子同色且同種類的棋子位置
   // 2. 找出哪些縱線上存在多個該棋子(例如:第3列有兩兵,第4列也有兩兵)
   // 3. 獲取當前移動棋子所在縱線的兵種數量及排名
   
   const isPawn = (piece.toLowerCase() === 'p');
   const sameFilesWithMultiPieces = []; // 存儲有多個兵的縱線索引
   
   // ... 遍歷棋盤邏輯 ...

   if (isPawn && sameFilesWithMultiPieces.length >= 2) {
       // 需求五:如果有多於一列兵,使用縱線編號
       // 例如:返回 "四進一" 而不是 "前兵進一"
       // 具體需根據您的 movePiece 邏輯生成字符串
   }
   
   // ... 原有記譜邏輯 ...
}
六、 代碼與注釋規範

    代碼/注釋:保持為英文(例如:// Update highlight for the start node)。
    中文字符串:允許在記譜和 UI 文字中使用(例如:const RED_PAWN = "兵";)。

總結需要更改的文件位置:

    CSS 區塊:更新 .ejceesrecord 寬度與 .ejceesrcdstart.active 樣式。
    renderRecordUI 函數:添加初始節點的高亮判斷與文字內容判斷。
    getChineseMove 函數:增加對「多列兵」的條件判斷,優化記譜描述。
    事件監聽器:更新 tool-new 及 slider 觸發 renderRecordUI 的參數。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

gemini太長之後pro不幹了,改用grok:

代码: 全选

**以下僅列出需要更改的地方**(其他部分完全保留不動)。代碼與註釋全部使用英文,僅中文字符串(如「棋局開始,紅方先行」、「前四平五」等)出現。

### 1. CSS 部分(<style> 內)
**新增**(放在 `.ejceesrcdstep.active` 樣式之後):
```css
/* Start banner highlighting when at game start (index <= 0) */
.ejceesrcdstart.active {
   background-color: #3d5afe;
   color: white;
}
```

**替換** media query 中的 `.ejceesrecord` 寬度(確保不包括前方計數器時,比兩個 `.ejceesrcdstep` 大 12px,無橫向滾動條):
```css
/* Mobile */
@media (max-width: 767px) {
   .ejceesrecord {
       width: 192px;  /* +12px buffer over two moves */
       font-size: 12px;
   }
}

/* Desktop */
@media (min-width: 768px) {
   .ejceesrecord {
       width: 268px;  /* +12px buffer over two moves */
       font-size: 16px;
   }
}
```

### 2. HTML 部分
**替換** record-start 的文字:
```html
<div class="ejceesrcdstart" id="record-start">棋局開始,紅方先行</div>
```

### 3. JavaScript 部分(<script> 內)

**A. 替換整個 `highlightActiveStep` 函數**(放在原有同名函數位置):
```js
/**
 * Highlight start banner (when index <= 0) or the active move step.
 * Called on page load, new game, jump, etc.
 */
function highlightActiveStep(index) {
    document.querySelectorAll('.ejceesrcdstep').forEach(el => el.classList.remove('active'));
    const startBanner = document.getElementById('record-start');
    if (startBanner) startBanner.classList.remove('active');

    if (index <= 0) {
        // Highlight start banner when slider at leftmost or initial position
        if (startBanner) startBanner.classList.add('active');
    } else if (index > 0) {
        const el = document.getElementById(`step-record-${index}`);
        if (el) el.classList.add('active');
    }
}
```

**B. 在 `loadFEN` 函數內替換 startDiv 文字邏輯**(大約在 `currentTurn = parts[1] || 'w';` 之後):
```js
// Update Start Record Text (Chinese only - future edit-board support included)
const startDiv = document.getElementById('record-start');
if (fenStr === INITIAL_FEN || fenStr.startsWith(INITIAL_FEN.split(' ')[0])) {
    startDiv.textContent = '棋局開始,紅方先行';
} else {
    const turnText = currentTurn === 'w' ? '紅方' : '黑方';
    startDiv.textContent = `特定局面,輪到${turnText}`;
}
```

**C. 替換「新建」按鈕事件處理器**(`toolNewBtn.addEventListener` 區塊):
```js
toolNewBtn.addEventListener('click', () => {
    historyFEN = [];
    currentStepIndex = -1;
    renderRecordUI(0); // Clear all recorded moves
    loadFEN(INITIAL_FEN);
    pushHistory(null);
    highlightActiveStep(0); // Ensure start banner is highlighted after New
});
```

**D. 在 DOMContentLoaded 結尾(初始化最後一行)新增**:
```js
// Ensure start banner highlighted on page open
highlightActiveStep(0);
```

**E. 替換 `getMoveNotation` 內兵的整個 else if 區塊**(原本的 `} else if (char.toLowerCase() === 'p') { ... }` 全部替換成以下,支援「兩列兵」時使用「前四平五」格式):
```js
} else if (char.toLowerCase() === 'p') {
    // Advanced pawn notation: same-file pawns + multi-pawn-columns (列兵) disambiguation
    // When >=2 files each have >=2 pawns, use "前四平五" style (front pawn of file 4 flat to 5)
    const sameColorPawns = [];
    for (const [pos, id] of tileMap.entries()) {
        if (id[0].toLowerCase() === 'p' &&
            ((isRed && id[0] === 'P') || (!isRed && id[0] === 'p'))) {
            const [px, py] = pos.split(',').map(Number);
            sameColorPawns.push({id, x: px, y: py});
        }
    }

    // Group by file
    const pawnsByFile = {};
    sameColorPawns.forEach(p => {
        if (!pawnsByFile[p.x]) pawnsByFile[p.x] = [];
        pawnsByFile[p.x].push(p);
    });

    const multiFileCount = Object.values(pawnsByFile).filter(g => g.length >= 2).length;
    const currentFilePawns = pawnsByFile[startX] || [];
    const thisFileHasMulti = currentFilePawns.length >= 2;

    // Sort current file front-to-back
    let sortedInFile = [...currentFilePawns];
    if (isRed) {
        sortedInFile.sort((a, b) => a.y - b.y); // Red: smaller Y = front
    } else {
        sortedInFile.sort((a, b) => b.y - a.y); // Black: larger Y = front
    }

    const myIndex = sortedInFile.findIndex(p => p.id === pieceId);
    const countInFile = sortedInFile.length;

    let prefix = '';
    let location = '';

    if (thisFileHasMulti && multiFileCount >= 2) {
        // Multiple pawn files (列兵) → "前四平五" format (no "兵")
        prefix = (myIndex === 0) ? '前' : '後';
        location = isRed ? NUM_ZH[startCol] : startCol.toString(); // 四 or 4
    } else if (thisFileHasMulti) {
        // Only ONE multi-pawn file → normal 前兵/後兵/中兵
        if (countInFile === 2) {
            prefix = (myIndex === 0) ? '前' : '後';
        } else if (countInFile === 3) {
            prefix = (myIndex === 0) ? '前' : (myIndex === 1 ? '中' : '後');
        } else {
            prefix = NUM_ZH[myIndex + 1];
        }
        location = name;
    } else {
        // Normal single pawn on this file
        prefix = name;
        location = isRed ? NUM_ZH[startCol] : startCol.toString();
    }
}
```

**完成後的操作**:
- 儲存 HTML,重新整理頁面。
- 頁面打開時、拖動滑桿到最左、點擊「新建」都會高亮 `ejceesrcdstart`。
- 棋譜記錄寬度已調整,無橫向滾動。
- 兵的記譜已正確處理「一列兵」與「多列兵」歧義(例如第三、四列都有列兵時會輸出「前四平五」)。

這些就是全部需要更改的地方。其他代碼與註釋保持英文不變。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

再用一次grok:

代码: 全选

**以下是需要更改的地方(只列出精确修改位置、代码片段和英文注释)。所有代码/注释保持英文,只有UI字符串用中文。无需改动其他地方。**

### 1. CSS(<style> 标签内,添加在原有 `.ejceesrcdstart` 样式之后)

```css
/* Highlight for start banner (same color as active steps) */
.ejceesrcdstart {
    cursor: pointer; /* make clickable */
}

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

/* Width adjustment: record panel (excluding number column) is now 12px wider than 2 steps */
/* This prevents horizontal scrollbar when move text is long (e.g. 前四平五) */
@media (max-width: 767px) {
    .ejceesrecord {
        width: 172px; /* was 160px */
    }
}
@media (min-width: 768px) {
    .ejceesrecord {
        width: 232px; /* was 220px */
    }
}
```

### 2. HTML(第 1 处,<div class="ejceesrecord"> 内)

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

### 3. JS(DOMContentLoaded 事件内,`loadFEN` 函数的最上方,替换原有的 “Update Start Record Text” 块)

```javascript
// Update Start Record Text - Chinese only. Text NEVER changes during moves.
// Only changes on page load / New / future Edit Board feature.
const startDiv = document.getElementById('record-start');
if (fenStr === INITIAL_FEN || fenStr.startsWith(INITIAL_FEN.split(' ')[0])) {
    startDiv.textContent = '棋局開始,紅方先行';
} else {
    const turnText = currentTurn === 'w' ? '紅方' : '黑方';
    startDiv.textContent = `特定局面,輪到${turnText}`;
}
```

### 4. JS(DOMContentLoaded 事件内,`// --- Boot the App ---` 之前,添加启动时高亮和点击处理)

```javascript
// Make start banner clickable (point 3) and highlight on load
const startDiv = document.getElementById('record-start');
startDiv.addEventListener('click', () => {
    jumpToStep(0); // go back to initial position
});
```

### 5. JS(替换整个 `highlightActiveStep` 函数)

```javascript
// Highlight active step or start banner when slider/index == 0
function highlightActiveStep(index) {
    // Remove active from all regular steps
    document.querySelectorAll('.ejceesrcdstep').forEach(el => el.classList.remove('active'));
    
    const startDiv = document.getElementById('record-start');
    startDiv.classList.remove('active');
    
    if (index <= 0) {
        startDiv.classList.add('active'); // highlight start banner on page open / leftmost slider
    } else {
        const el = document.getElementById(`step-record-${index}`);
        if (el) el.classList.add('active');
    }
}
```

### 6. JS(`toolNewBtn` 点击处理器内,`pushHistory(null);` 之后添加一行)

```javascript
pushHistory(null);
highlightActiveStep(currentStepIndex); // ensure start banner is highlighted after New
```

### 7. JS(替换 `getMoveNotation` 函数中的 pawn 逻辑部分 —— 这是点5的核心改动)

在函数开头(`const char = pieceId[0];` 之后,`const isRed = ...` 之后)添加以下新代码,然后**删除原来的 `if (colPieces.length > 1) { ... pawn logic ... }` 整个块**,替换为下面完整的新逻辑:

```javascript
// --- Improved Pawn Notation (point 5): handle multiple "列兵" columns ---
// When 2+ different files each have 2+ pawns, use "前四平五" format (file disambiguation)
// Otherwise keep old "前兵進一" or "兵八平五"

let prefix = '';
let location = ''; // will be set later for non-pawns

if (char.toLowerCase() === 'p') {
    // Collect all friendly pawns of same color
    const friendlyPawns = [];
    for (const [pos, id] of tileMap.entries()) {
        if (id[0].toLowerCase() === 'p' && 
            ((id[0] === 'P') === isRed)) {
            const [px, py] = pos.split(',').map(Number);
            friendlyPawns.push({id, x: px, y: py});
        }
    }

    // Group by file (x)
    const pawnGroups = {};
    friendlyPawns.forEach(p => {
        if (!pawnGroups[p.x]) pawnGroups[p.x] = [];
        pawnGroups[p.x].push(p);
    });

    // How many files have >=2 pawns?
    const multiFiles = Object.keys(pawnGroups).filter(x => pawnGroups[x].length >= 2);
    const needsFileDisambig = multiFiles.length >= 2;

    // Current file group
    const myGroup = pawnGroups[startX] || [];
    const groupSize = myGroup.length;

    // Sort: front pawn first (Red: small y first; Black: large y first)
    let sortedGroup = [...myGroup];
    if (isRed) {
        sortedGroup.sort((a, b) => a.y - b.y);
    } else {
        sortedGroup.sort((a, b) => b.y - a.y);
    }
    const posInGroup = sortedGroup.findIndex(p => p.id === pieceId);

    let rankPrefix = '';
    if (groupSize >= 2) {
        if (groupSize === 2) {
            rankPrefix = (posInGroup === 0) ? '前' : '後';
        } else if (groupSize === 3) {
            rankPrefix = ['前', '中', '後'][posInGroup] || '';
        } else {
            rankPrefix = NUM_ZH[posInGroup + 1];
        }
    }

    let fileSpec = '';
    if (needsFileDisambig && groupSize >= 2) {
        const colNum = isRed ? (9 - startX) : (startX + 1);
        fileSpec = isRed ? NUM_ZH[colNum] : colNum.toString();
    }

    const name = isRed ? '兵' : '卒';

    if (rankPrefix) {
        prefix = rankPrefix;
        if (fileSpec) {
            prefix += fileSpec;   // "前四" exactly as in your example
            // No repeated "兵" when using file disambiguation
        } else {
            prefix += name;       // "前兵" when only one multi-pawn file
        }
        location = '';            // no extra location digit when using rank/file prefix
    } else {
        // Single pawn in this file → traditional notation
        const colNum = isRed ? (9 - startX) : (startX + 1);
        prefix = isRed ? NUM_ZH[colNum] + name : colNum.toString() + name;
        location = '';
    }
} else {
    // Original non-pawn prefix logic (R, C, N, etc.) remains unchanged
    // ... (keep all the rest of your original code for prefix/location of other pieces)
}
```

**注意**:非兵的 `prefix` / `location` 计算保持你原来的代码(从 `// Find all friendly pieces...` 到 `} else { prefix = name; location = ... }`)。只把兵的处理换成上面这段。

### 完成步骤
1. 按顺序应用以上 7 处修改。
2. 保存后刷新页面 → 启动时 `棋局開始,紅方先行` 已高亮;拖动滑块到最左也高亮;点击它回到初始局面;新建也高亮;多列兵时自动使用「前四平五」等正确记谱。
3. 宽度已增加 12px,无横向滚动条。

全部修改完成,即可满足你列出的 6 点要求。需要我给出完整替换后的某函数代码片段吗?
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

gemini think
確保初始高亮:

代码: 全选

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');

    if (index === 0) {
        // Highlight start banner if at step 0
        startDiv.classList.add('active');
    } else {
        // Highlight the specific move
        const el = document.getElementById(`step-record-${index}`);
        if (el) el.classList.add('active');
    }
}
兵有兩列時:

代码: 全选

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) {
            // If more than one column has multiple pawns, use Column Number instead of "Pawn"
            location = isRed ? NUM_ZH[startCol] : startCol.toString();
        } else {
            location = name;
        }
    } else {
        prefix = name;
        location = isRed ? NUM_ZH[startCol] : startCol.toString();
    }

    // 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] : val.toString());
}
點擊回到初始:

代码: 全选

// 在 script 標籤內的適當位置添加
const startDiv = document.getElementById('record-start');
if (startDiv) {
    startDiv.addEventListener('click', () => {
        jumpToStep(0); // 點擊回到初始狀態
    });
}
上次由 ejsoon 在 2026年 2月 18日 23:52,总共编辑 1 次。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 4525
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 172 次
被圈友点赞: 200 次
联系:

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

帖子 ejsoon »

一半靠手工改好,最終還是靠gemini,grok有點不太行。

代码: 全选

<!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 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)"/>

<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
   const NUM_ZH = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
   // 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;

    document.addEventListener('DOMContentLoaded', () => {
        const svgElement = document.querySelector('.ejceespb');
        const outputElement = document.querySelector('.ejceesoutput');
        const etboard = document.querySelector('.etboard');
        const stepSlider = document.querySelector('.ejceesstepdrop');
        
        // 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) {
           const newFEN = boardToFEN();
           
           // Truncate future history if branched
           if (currentStepIndex < historyFEN.length - 1) {
               // We are rewriting history
               historyFEN = historyFEN.slice(0, currentStepIndex + 1);
               // Also clear record UI after this point
               renderRecordUI(currentStepIndex + 1); // Pass limit to redraw
           }
           
           historyFEN.push({ fen: newFEN, move: lastMoveText });
           currentStepIndex++;
           
           stepSlider.max = Math.max(0, historyFEN.length - 1);
           stepSlider.value = currentStepIndex;

           // Render the new move in the list
           if (lastMoveText) {
               addMoveToRecordUI(lastMoveText, currentStepIndex);
           }
       }

       // 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');

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

       function jumpToStep(index) {
           if (index >= 0 && index < historyFEN.length) {
               currentStepIndex = index;
               stepSlider.value = index;
               loadFEN(historyFEN[index].fen);
               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] : startCol.toString();
        } else {
            location = name;
        }
    } else {
        prefix = name;
        location = isRed ? NUM_ZH[startCol] : startCol.toString();
    }

    // 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] : val.toString());
}
        // 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');
                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);

               // 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);
           }
            }

            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);
        highlightActiveStep(0);
    });
    </script>
</body>
</html>
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复

在线用户

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