纠正仓库内容为衍生项目,标明 campaign-200-r2 最新版
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 266.66666666666663" role="img" aria-label="先踩开关再过门"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><line x1="246.66666666666666" y1="152.6" x2="246.66666666666666" y2="227.4" stroke="#cf7824" stroke-width="7" transform="rotate(0 246.66666666666666 190)"/><text x="254.66666666666666" y="182" font-size="12">门1</text><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">开1</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 266.66666666666663" role="img" aria-label="先踩开关再过门"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="190,76.66666666666666 76.66666666666666,76.66666666666666 76.66666666666666,190 190,190 303.3333333333333,190 303.3333333333333,76.66666666666666" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><line x1="246.66666666666666" y1="152.6" x2="246.66666666666666" y2="227.4" stroke="#cf7824" stroke-width="7" transform="rotate(0 246.66666666666666 190)"/><text x="254.66666666666666" y="182" font-size="12">门1</text><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0 · 开1</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="绕行到开关"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,246.66666666666666 133.33333333333331,246.66666666666666 133.33333333333331,360 20,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,246.66666666666666 246.66666666666666,246.66666666666666 246.66666666666666,360 133.33333333333331,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><line x1="190" y1="209.26666666666665" x2="190" y2="284.06666666666666" stroke="#cf7824" stroke-width="7" transform="rotate(-90 190 246.66666666666666)"/><text x="198" y="238.66666666666666" font-size="12">门1</text><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">开1</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="76.66666666666666" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="76.66666666666666" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="190" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text></svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="绕行到开关"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,246.66666666666666 133.33333333333331,246.66666666666666 133.33333333333331,360 20,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,246.66666666666666 246.66666666666666,246.66666666666666 246.66666666666666,360 133.33333333333331,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="303.3333333333333,76.66666666666666 190,76.66666666666666 76.66666666666666,76.66666666666666 76.66666666666666,190 76.66666666666666,303.3333333333333 190,303.3333333333333 190,190 303.3333333333333,190" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><line x1="190" y1="209.26666666666665" x2="190" y2="284.06666666666666" stroke="#cf7824" stroke-width="7" transform="rotate(-90 190 246.66666666666666)"/><text x="198" y="238.66666666666666" font-size="12">门1</text><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0 · 开1</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="76.66666666666666" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="76.66666666666666" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="190" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="190" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text></svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="开门后不能立刻穿过去"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><line x1="232.5" y1="161.95" x2="232.5" y2="218.05" stroke="#cf7824" stroke-width="7" transform="rotate(90 232.5 190)"/><text x="240.5" y="182" font-size="12">门1</text><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">开1</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text></svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="开门后不能立刻穿过去"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="62.5,62.5 147.5,62.5 232.5,62.5 317.5,62.5 317.5,147.5 232.5,147.5 232.5,232.5 147.5,232.5 147.5,147.5 62.5,147.5" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><line x1="232.5" y1="161.95" x2="232.5" y2="218.05" stroke="#cf7824" stroke-width="7" transform="rotate(90 232.5 190)"/><text x="240.5" y="182" font-size="12">门1</text><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3 · 开1</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">10</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">9</text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text></svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两对独立门与开关"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><line x1="105" y1="204.45" x2="105" y2="260.55" stroke="#cf7824" stroke-width="7" transform="rotate(0 105 232.5)"/><text x="113" y="224.5" font-size="12">门1</text><line x1="275" y1="34.45" x2="275" y2="90.55" stroke="#cf7824" stroke-width="7" transform="rotate(0 275 62.5)"/><text x="283" y="54.5" font-size="12">门2</text><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">开1</text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">开2</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两对独立门与开关"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="62.5,62.5 147.5,62.5 147.5,147.5 62.5,147.5 62.5,232.5 147.5,232.5 232.5,232.5 232.5,147.5 232.5,62.5 317.5,62.5 317.5,147.5 317.5,232.5" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><line x1="105" y1="204.45" x2="105" y2="260.55" stroke="#cf7824" stroke-width="7" transform="rotate(0 105 232.5)"/><text x="113" y="224.5" font-size="12">门1</text><line x1="275" y1="34.45" x2="275" y2="90.55" stroke="#cf7824" stroke-width="7" transform="rotate(0 275 62.5)"/><text x="283" y="54.5" font-size="12">门2</text><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1 · 开1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">9</text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">10</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4 · 开2</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">11</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">12</text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text></svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
@@ -0,0 +1,181 @@
|
||||
# 路径开门
|
||||
|
||||
每格一次。门初始关闭,走到编号对应的开关后永久打开;撤销会恢复门的状态。
|
||||
|
||||
固定数字表示必须在该步到达;每格一次、共边移动、完整路线唯一。所有关卡开放。
|
||||
|
||||
这些是机制验证候选关,难度标签按设计任务编排,尚未经真人校准。
|
||||
|
||||
## 01 · 入门 · 先踩开关再过门
|
||||
|
||||
格数 6;基础完整路线 16 条;新约束 1 项。
|
||||
|
||||
作者解:c1 → c0 → c3 → c4 → c5 → c2。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- 先在第 2 步踩 c0,第 5 步跨门 c4—c5
|
||||
|
||||
关键分叉:第 2 步应选 c0,解除机制后的诱惑路线选择 c2。排除依据:开关尚未打开此门。
|
||||
|
||||
诱惑路线:c1 → c2 → c5 → c4 → c3 → c0。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 gate:0 后可走 c1 → c2 → c5 → c4 → c3 → c0
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 02 · 入门 · 绕行到开关
|
||||
|
||||
格数 8;基础完整路线 24 条;新约束 1 项。
|
||||
|
||||
作者解:c2 → c1 → c0 → c3 → c6 → c7 → c4 → c5。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- 先在第 3 步踩 c0,第 7 步跨门 c7—c4
|
||||
|
||||
关键分叉:第 2 步应选 c1,解除机制后的诱惑路线选择 c5。排除依据:开关尚未打开此门。
|
||||
|
||||
诱惑路线:c2 → c5 → c4 → c7 → c6 → c3 → c0 → c1。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 gate:0 后可走 c2 → c5 → c4 → c7 → c6 → c3 → c0 → c1
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 03 · 普通 · 开门后不能立刻穿过去
|
||||
|
||||
格数 10;基础完整路线 34 条;新约束 1 项。
|
||||
|
||||
作者解:c0 → c1 → c2 → c3 → c7 → c6 → c9 → c8 → c5 → c4。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- 先在第 4 步踩 c3,第 7 步跨门 c6—c9
|
||||
|
||||
关键分叉:第 2 步应选 c1,解除机制后的诱惑路线选择 c4。排除依据:开关尚未打开此门。
|
||||
|
||||
诱惑路线:c0 → c4 → c5 → c8 → c9 → c6 → c7 → c3 → c2 → c1。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 gate:0 后可走 c0 → c4 → c5 → c8 → c9 → c6 → c7 → c3 → c2 → c1
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 04 · 普通 · 两对独立门与开关
|
||||
|
||||
格数 12;基础完整路线 124 条;新约束 2 项。
|
||||
|
||||
作者解:c0 → c1 → c5 → c4 → c8 → c9 → c10 → c6 → c2 → c3 → c7 → c11。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- 先在第 2 步踩 c1,第 6 步跨门 c8—c9
|
||||
- 先在第 4 步踩 c4,第 10 步跨门 c2—c3
|
||||
|
||||
关键分叉:第 2 步应选 c1,解除机制后的诱惑路线选择 c4。排除依据:开关尚未打开此门。
|
||||
|
||||
诱惑路线:c0 → c4 → c8 → c9 → c5 → c1 → c2 → c6 → c10 → c11 → c7 → c3。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 gate:0 后可走 c0 → c4 → c8 → c9 → c5 → c1 → c2 → c6 → c10 → c11 → c7 → c3
|
||||
- 解除 gate:1 后可走 c0 → c1 → c2 → c3 → c7 → c11 → c10 → c6 → c5 → c4 → c8 → c9
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 05 · 进阶 · 第一门后才能到第二开关
|
||||
|
||||
格数 14;基础完整路线 100 条;新约束 2 项。
|
||||
|
||||
作者解:c6 → c5 → c9 → c8 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c10 → c13 → c12。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- 先在第 3 步踩 c9,第 6 步跨门 c4—c0
|
||||
- 先在第 6 步踩 c0,第 11 步跨门 c7—c11
|
||||
|
||||
关键分叉:第 3 步应选 c9,解除机制后的诱惑路线选择 c1。排除依据:开关尚未打开此门。
|
||||
|
||||
诱惑路线:c6 → c5 → c1 → c0 → c4 → c8 → c9 → c12 → c13 → c10 → c11 → c7 → c3 → c2。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 gate:0 后可走 c6 → c5 → c1 → c0 → c4 → c8 → c9 → c12 → c13 → c10 → c11 → c7 → c3 → c2
|
||||
- 解除 gate:1 后可走 c6 → c2 → c3 → c7 → c11 → c10 → c13 → c12 → c9 → c8 → c4 → c0 → c1 → c5
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 06 · 进阶 · 开关顺序与区域收尾
|
||||
|
||||
格数 16;基础完整路线 552 条;新约束 2 项。
|
||||
|
||||
作者解:c5 → c1 → c0 → c4 → c8 → c12 → c13 → c14 → c15 → c11 → c7 → c3 → c2 → c6 → c10 → c9。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- 先在第 5 步踩 c8,第 7 步跨门 c12—c13
|
||||
- 先在第 7 步踩 c13,第 13 步跨门 c3—c2
|
||||
|
||||
关键分叉:第 2 步应选 c1,解除机制后的诱惑路线选择 c9。排除依据:开关尚未打开此门。
|
||||
|
||||
诱惑路线:c5 → c9 → c13 → c12 → c8 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c15 → c14 → c10 → c6。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 gate:0 后可走 c5 → c9 → c13 → c12 → c8 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c15 → c14 → c10 → c6
|
||||
- 解除 gate:1 后可走 c5 → c6 → c7 → c3 → c2 → c1 → c0 → c4 → c8 → c12 → c13 → c14 → c15 → c11 → c10 → c9
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 07 · 难 · 三对门形成依赖链
|
||||
|
||||
格数 18;基础完整路线 464 条;新约束 3 项。
|
||||
|
||||
作者解:c5 → c6 → c7 → c3 → c2 → c1 → c0 → c4 → c8 → c12 → c13 → c9 → c10 → c11 → c15 → c14 → c17 → c16。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- 先在第 3 步踩 c7,第 6 步跨门 c2—c1
|
||||
- 先在第 9 步踩 c8,第 11 步跨门 c12—c13
|
||||
- 先在第 12 步踩 c9,第 15 步跨门 c11—c15
|
||||
|
||||
关键分叉:第 2 步应选 c6,解除机制后的诱惑路线选择 c4。排除依据:开关尚未打开此门。
|
||||
|
||||
诱惑路线:c5 → c4 → c0 → c1 → c2 → c6 → c10 → c9 → c8 → c12 → c13 → c16 → c17 → c14 → c15 → c11 → c7 → c3。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 gate:0 后可走 c5 → c4 → c0 → c1 → c2 → c6 → c10 → c9 → c8 → c12 → c13 → c16 → c17 → c14 → c15 → c11 → c7 → c3
|
||||
- 解除 gate:1 后可走 c5 → c9 → c10 → c6 → c7 → c11 → c15 → c14 → c17 → c16 → c13 → c12 → c8 → c4 → c0 → c1 → c2 → c3
|
||||
- 解除 gate:2 后可走 c5 → c6 → c7 → c3 → c2 → c1 → c0 → c4 → c8 → c12 → c13 → c16 → c17 → c14 → c15 → c11 → c10 → c9
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 08 · 难 · 两条开门依赖交织
|
||||
|
||||
格数 19;基础完整路线 544 条;新约束 3 项。
|
||||
|
||||
作者解:c2 → c3 → c7 → c11 → c15 → c14 → c18 → c17 → c16 → c12 → c13 → c9 → c8 → c4 → c0 → c1 → c5 → c6 → c10。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- 先在第 3 步踩 c7,第 6 步跨门 c15—c14
|
||||
- 先在第 7 步踩 c18,第 11 步跨门 c12—c13
|
||||
- 先在第 6 步踩 c14,第 16 步跨门 c0—c1
|
||||
|
||||
关键分叉:第 1 步应选 c2,解除机制后的诱惑路线选择 c5。排除依据:开关尚未打开此门。
|
||||
|
||||
诱惑路线:c5 → c6 → c10 → c11 → c15 → c14 → c18 → c17 → c16 → c12 → c13 → c9 → c8 → c4 → c0 → c1 → c2 → c3 → c7。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 gate:0 后可走 c5 → c6 → c10 → c11 → c15 → c14 → c18 → c17 → c16 → c12 → c13 → c9 → c8 → c4 → c0 → c1 → c2 → c3 → c7
|
||||
- 解除 gate:1 后可走 c2 → c3 → c7 → c11 → c15 → c14 → c10 → c6 → c5 → c1 → c0 → c4 → c8 → c9 → c13 → c12 → c16 → c17 → c18
|
||||
- 解除 gate:2 后可走 c13 → c9 → c10 → c11 → c7 → c3 → c2 → c6 → c5 → c1 → c0 → c4 → c8 → c12 → c16 → c17 → c18 → c14 → c15
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
@@ -0,0 +1,943 @@
|
||||
{
|
||||
"mode": "gates",
|
||||
"title": "路径开门",
|
||||
"rule": "每格一次。门初始关闭,走到编号对应的开关后永久打开;撤销会恢复门的状态。",
|
||||
"levels": [
|
||||
{
|
||||
"id": "01",
|
||||
"mode": "gates",
|
||||
"geometry": "square",
|
||||
"difficulty": "入门",
|
||||
"title": "先踩开关再过门",
|
||||
"caption": "每格一次。门初始关闭,走到编号对应的开关后永久打开;撤销会恢复门的状态。",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"edge": [
|
||||
"c4",
|
||||
"c5"
|
||||
],
|
||||
"switchCell": "c0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "02",
|
||||
"mode": "gates",
|
||||
"geometry": "square",
|
||||
"difficulty": "入门",
|
||||
"title": "绕行到开关",
|
||||
"caption": "每格一次。门初始关闭,走到编号对应的开关后永久打开;撤销会恢复门的状态。",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 5
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"edge": [
|
||||
"c7",
|
||||
"c4"
|
||||
],
|
||||
"switchCell": "c0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "03",
|
||||
"mode": "gates",
|
||||
"geometry": "square",
|
||||
"difficulty": "普通",
|
||||
"title": "开门后不能立刻穿过去",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 6
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"edge": [
|
||||
"c6",
|
||||
"c9"
|
||||
],
|
||||
"switchCell": "c3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "04",
|
||||
"mode": "gates",
|
||||
"geometry": "square",
|
||||
"difficulty": "普通",
|
||||
"title": "两对独立门与开关",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 8
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"edge": [
|
||||
"c8",
|
||||
"c9"
|
||||
],
|
||||
"switchCell": "c1"
|
||||
},
|
||||
{
|
||||
"edge": [
|
||||
"c2",
|
||||
"c3"
|
||||
],
|
||||
"switchCell": "c4"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "05",
|
||||
"mode": "gates",
|
||||
"geometry": "square",
|
||||
"difficulty": "进阶",
|
||||
"title": "第一门后才能到第二开关",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"edge": [
|
||||
"c4",
|
||||
"c0"
|
||||
],
|
||||
"switchCell": "c9"
|
||||
},
|
||||
{
|
||||
"edge": [
|
||||
"c7",
|
||||
"c11"
|
||||
],
|
||||
"switchCell": "c0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "06",
|
||||
"mode": "gates",
|
||||
"geometry": "square",
|
||||
"difficulty": "进阶",
|
||||
"title": "开关顺序与区域收尾",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 15
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c14",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c15",
|
||||
"x": 3,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"edge": [
|
||||
"c12",
|
||||
"c13"
|
||||
],
|
||||
"switchCell": "c8"
|
||||
},
|
||||
{
|
||||
"edge": [
|
||||
"c3",
|
||||
"c2"
|
||||
],
|
||||
"switchCell": "c13"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "07",
|
||||
"mode": "gates",
|
||||
"geometry": "square",
|
||||
"difficulty": "难",
|
||||
"title": "三对门形成依赖链",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 11
|
||||
},
|
||||
{
|
||||
"id": "c14",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c15",
|
||||
"x": 3,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c16",
|
||||
"x": 1,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c17",
|
||||
"x": 2,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"edge": [
|
||||
"c2",
|
||||
"c1"
|
||||
],
|
||||
"switchCell": "c7"
|
||||
},
|
||||
{
|
||||
"edge": [
|
||||
"c12",
|
||||
"c13"
|
||||
],
|
||||
"switchCell": "c8"
|
||||
},
|
||||
{
|
||||
"edge": [
|
||||
"c11",
|
||||
"c15"
|
||||
],
|
||||
"switchCell": "c9"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "08",
|
||||
"mode": "gates",
|
||||
"geometry": "square",
|
||||
"difficulty": "难",
|
||||
"title": "两条开门依赖交织",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 13
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 4
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c14",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c15",
|
||||
"x": 3,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c16",
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c17",
|
||||
"x": 1,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c18",
|
||||
"x": 2,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"edge": [
|
||||
"c15",
|
||||
"c14"
|
||||
],
|
||||
"switchCell": "c7"
|
||||
},
|
||||
{
|
||||
"edge": [
|
||||
"c12",
|
||||
"c13"
|
||||
],
|
||||
"switchCell": "c18"
|
||||
},
|
||||
{
|
||||
"edge": [
|
||||
"c0",
|
||||
"c1"
|
||||
],
|
||||
"switchCell": "c14"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Pure rules / geometry / solver. Each mode receives its own standalone copy.
|
||||
export function units(c){return c.shape?c.shape.map(([x,y])=>[c.x+x,c.y+y]):Array.from({length:(c.w||1)*(c.h||1)},(_,i)=>[c.x+i%(c.w||1),c.y+Math.floor(i/(c.w||1))]);}
|
||||
export function graph(level){
|
||||
const cells=level.cells, ids=new Map(cells.map((c,i)=>[c.id,i]));
|
||||
if(ids.size!==cells.length)throw Error('重复格 id');
|
||||
const adj=cells.map(()=>[]), occupancy=new Map();
|
||||
if(level.geometry!=='hex') for(let i=0;i<cells.length;i++)for(const [x,y]of units(cells[i])){const k=x+','+y;if(occupancy.has(k))throw Error('拼块重叠');occupancy.set(k,i);}
|
||||
for(let i=0;i<cells.length;i++)for(let j=i+1;j<cells.length;j++){
|
||||
let connected=false;
|
||||
if(level.geometry==='hex'){let dq=cells[j].q-cells[i].q,dr=cells[j].r-cells[i].r;connected=[[1,0],[-1,0],[0,1],[0,-1],[1,-1],[-1,1]].some(([a,b])=>a===dq&&b===dr);}
|
||||
else connected=units(cells[i]).some(([x,y])=>[[1,0],[-1,0],[0,1],[0,-1]].some(([a,b])=>occupancy.get((x+a)+','+(y+b))===j));
|
||||
if(connected){adj[i].push(j);adj[j].push(i);}
|
||||
}
|
||||
const clues=new Map();for(const c of cells)if(c.clue!==undefined){if(!Number.isInteger(c.clue)||c.clue<1||c.clue>cells.length||clues.has(c.clue))throw Error('数字范围或重复');clues.set(c.clue,ids.get(c.id));}
|
||||
return {cells,ids,adj,clues};
|
||||
}
|
||||
export function constraints(level){
|
||||
const result=[];
|
||||
for(let i=0;i<(level.relations||[]).length;i++)result.push({key:'relation:'+i,kind:'relation',...level.relations[i]});
|
||||
for(let i=0;i<(level.orderChains||[]).length;i++)for(let j=0;j<level.orderChains[i].length-1;j++)result.push({key:'order:'+i+':'+j,kind:'order',from:level.orderChains[i][j],to:level.orderChains[i][j+1]});
|
||||
for(let i=0;i<(level.windows||[]).length;i++)result.push({key:'window:'+i,kind:'window',...level.windows[i]});
|
||||
for(const c of level.cells)if(c.direction)result.push({key:'direction:'+c.id,kind:'direction',cell:c.id,value:c.direction});
|
||||
if(level.mode.includes('alternation'))result.push({key:'alternation',kind:'alternation'});
|
||||
for(let i=0;i<(level.gates||[]).length;i++)result.push({key:'gate:'+i,kind:'gate',...level.gates[i]});
|
||||
return result;
|
||||
}
|
||||
export function directionAt(level,path,k){
|
||||
if(k===0||k===path.length-1)return null;
|
||||
const [a,b,c]=[path[k-1],path[k],path[k+1]].map(id=>level.cells.find(x=>x.id===id));
|
||||
return b.x-a.x===c.x-b.x&&b.y-a.y===c.y-b.y?'straight':'turn';
|
||||
}
|
||||
export function violations(level,path,{omit=null,complete=path.length===level.cells.length,ignoreClues=false}={}){
|
||||
const positions=new Map(path.map((id,i)=>[id,i+1])),bad=[];
|
||||
if(!ignoreClues)for(const c of level.cells)if(c.clue!==undefined){const at=positions.get(c.id);if(at!==undefined&&at!==c.clue||path.length>=c.clue&&path[c.clue-1]!==c.id)bad.push({key:'clue:'+c.id,cells:[c.id,path[c.clue-1]].filter(Boolean),message:'固定数字步号不符'});}
|
||||
for(const r of constraints(level)){
|
||||
if(r.key===omit)continue;
|
||||
let fail=false,where=[];
|
||||
if(r.kind==='relation'){
|
||||
const a=positions.get(r.from),b=positions.get(r.to);where=[r.from,r.to];
|
||||
fail=b!==undefined&&(a===undefined||b-a!==r.delta)||a!==undefined&&b===undefined&&path.length>=a+r.delta;
|
||||
}else if(r.kind==='order'){where=[r.from,r.to];fail=positions.has(r.to)&&(!positions.has(r.from)||positions.get(r.from)>positions.get(r.to));}
|
||||
else if(r.kind==='window'){where=[r.cell];const p=positions.get(r.cell);fail=p!==undefined?(p<r.min||p>r.max):path.length>=r.max;}
|
||||
else if(r.kind==='direction'){where=[r.cell];const p=positions.get(r.cell);fail=p===1||p!==undefined&&p<path.length&&directionAt(level,path,p-1)!==r.value||complete&&p===path.length;}
|
||||
else if(r.kind==='alternation'){for(let i=1;i<path.length;i++){const a=level.cells.find(c=>c.id===path[i-1]),b=level.cells.find(c=>c.id===path[i]);if(a.category===b.category){fail=true;where.push(a.id,b.id);}}}
|
||||
else if(r.kind==='gate')for(let i=1;i<path.length;i++){if(r.edge.includes(path[i-1])&&r.edge.includes(path[i])){const s=positions.get(r.switchCell);if(s===undefined||s>i){fail=true;where=r.edge;}}}
|
||||
if(fail)bad.push({key:r.key,cells:where,message:r.kind==='relation'?'相对步差不符':r.kind==='order'?'标记次序不符':r.kind==='window'?'到达窗口不符':r.kind==='direction'?'进出方向不符':r.kind==='gate'?'开关尚未打开此门':'类别没有交替'});
|
||||
}
|
||||
return bad;
|
||||
}
|
||||
export function canAppend(level,path,id){
|
||||
const g=graph(level);if(path.includes(id)||!g.ids.has(id))return false;
|
||||
if(!path.length)return !g.clues.has(1)||g.cells[g.clues.get(1)].id===id;
|
||||
const last=path.at(-1);if(!g.adj[g.ids.get(last)].includes(g.ids.get(id)))return false;
|
||||
if(level.mode.includes('alternation')&&g.cells[g.ids.get(last)].category===g.cells[g.ids.get(id)].category)return false;
|
||||
for(const gate of level.gates||[])if(gate.edge.includes(last)&&gate.edge.includes(id)&&!path.includes(gate.switchCell))return false;
|
||||
return true;
|
||||
}
|
||||
export function solve(level,{limit=2,omit=null,ignoreClues=false,prune=true,nodeLimit=Infinity}={}){
|
||||
const g=graph(level),n=g.cells.length,results=[],path=[],seen=new Set();let nodes=0;
|
||||
const active=constraints(level).filter(r=>r.key!==omit),alternating=active.some(r=>r.kind==='alternation');
|
||||
const adj=g.adj.map((a,i)=>a.filter(j=>!alternating||g.cells[i].category!==g.cells[j].category));
|
||||
const forced=ignoreClues?new Map():g.clues;
|
||||
function connected(cur){const reached=new Set([cur]),queue=[cur];for(let k=0;k<queue.length;k++)for(const j of adj[queue[k]])if(!seen.has(j)&&!reached.has(j)){reached.add(j);queue.push(j);}return reached.size===n-path.length+1;}
|
||||
function visit(i){if(++nodes>nodeLimit)throw Error('求解节点预算耗尽,不能视为唯一');seen.add(i);path.push(g.cells[i].id);
|
||||
const k=path.length;
|
||||
if(!violations(level,path,{omit,ignoreClues}).length){
|
||||
if(k===n)results.push([...path]);
|
||||
else if(!prune||connected(i)){
|
||||
const f=forced.get(k+1);let next=adj[i].filter(j=>!seen.has(j)&&(f===undefined||j===f));
|
||||
next.sort((a,b)=>adj[a].filter(x=>!seen.has(x)).length-adj[b].filter(x=>!seen.has(x)).length);
|
||||
for(const j of next){if(!ignoreClues&&g.cells[j].clue!==undefined&&g.cells[j].clue!==k+1)continue;
|
||||
let blocked=false;for(const r of active)if(r.kind==='gate'&&r.edge.includes(g.cells[i].id)&&r.edge.includes(g.cells[j].id)&&!path.includes(r.switchCell))blocked=true;
|
||||
if(!blocked)visit(j);if(results.length>=limit)break;
|
||||
}
|
||||
}
|
||||
}
|
||||
path.pop();seen.delete(i);
|
||||
}
|
||||
let starts=forced.has(1)?[forced.get(1)]:g.cells.map((_,i)=>i);
|
||||
for(const i of starts){if(!ignoreClues&&g.cells[i].clue!==undefined&&g.cells[i].clue!==1)continue;visit(i);if(results.length>=limit)break;}
|
||||
return results;
|
||||
}
|
||||
export function hint(solution,path){let k=0;while(k<path.length&&path[k]===solution[k])k++;if(k<path.length)return {kind:k?'backtrack':'restart',cell:k?path[k-1]:solution[0]};return k===solution.length?{kind:'none'}:{kind:k?'next':'start',cell:solution[k]};}
|
||||
export function assertStructure(level){
|
||||
const g=graph(level),n=g.cells.length;if(n<3||n>20)throw Error('格数须为3–20');
|
||||
if(level.geometry==='hex'){const keys=new Set();for(const c of g.cells){if(!Number.isInteger(c.q)||!Number.isInteger(c.r))throw Error('六边坐标');const key=c.q+','+c.r;if(keys.has(key))throw Error('六边重叠');keys.add(key);}}
|
||||
else for(const c of g.cells){if(!Number.isInteger(c.x)||!Number.isInteger(c.y))throw Error('坐标');const u=units(c);if(!u.length||new Set(u.map(p=>p.join(','))).size!==u.length)throw Error('异形重复');const seen=new Set([u[0].join(',')]);for(let changed=true;changed;){changed=false;for(const [x,y]of u)if(!seen.has(x+','+y)&&[[1,0],[-1,0],[0,1],[0,-1]].some(([a,b])=>seen.has((x+a)+','+(y+b)))){seen.add(x+','+y);changed=true;}}if(seen.size!==u.length)throw Error('异形不连通');}
|
||||
const reach=new Set([0]),queue=[0];for(let k=0;k<queue.length;k++)for(const j of g.adj[queue[k]])if(!reach.has(j)){reach.add(j);queue.push(j);}if(reach.size!==n)throw Error('盘面不连通');
|
||||
const pairs=[];
|
||||
for(const r of constraints(level)){
|
||||
for(const id of [r.cell,r.from,r.to,r.switchCell,...(r.edge||[])].filter(Boolean))if(!g.ids.has(id))throw Error('约束引用不存在');
|
||||
if(r.kind==='relation'&&(!Number.isInteger(r.delta)||r.delta<1||r.delta>=n||r.from===r.to))throw Error('步差');
|
||||
if(r.kind==='order')pairs.push([r.from,r.to]);
|
||||
if(r.kind==='window'&&(!Number.isInteger(r.min)||!Number.isInteger(r.max)||r.min<1||r.max>n||r.max-r.min<2||g.cells[g.ids.get(r.cell)].clue!==undefined))throw Error('窗口需允许至少两个同奇偶步号且不与数字重叠');
|
||||
if(r.kind==='direction'&&!['straight','turn'].includes(r.value))throw Error('方向');
|
||||
if(r.kind==='gate'&&(r.edge.length!==2||!g.adj[g.ids.get(r.edge[0])].includes(g.ids.get(r.edge[1]))))throw Error('门不在共边');
|
||||
}
|
||||
if((level.gates||[]).length>3||new Set((level.gates||[]).map(r=>r.switchCell)).size!==(level.gates||[]).length)throw Error('最多三门且开关一对一');
|
||||
if(level.mode.includes('alternation')&&g.cells.some(c=>!['circle','triangle'].includes(c.category)))throw Error('缺少类别');
|
||||
function cycle(v,stack=new Set(),done=new Set()){if(stack.has(v))throw Error('先后链循环');if(done.has(v))return;stack.add(v);for(const [a,b]of pairs)if(a===v)cycle(b,stack,done);stack.delete(v);done.add(v);}for(const [a]of pairs)cycle(a);
|
||||
return g;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {graph,units,solve,violations,canAppend,hint} from './rules.mjs';
|
||||
// The build embeds this module together with the local rule file and JSON.
|
||||
const pack=PACK;
|
||||
const $=id=>document.getElementById(id),canvas=$('board'),ctx=canvas.getContext('2d');
|
||||
let level,solution,path=[],index=0,selected='入门',polygons=[],centers=new Map(),unit=1,focus=[],drag=false,lastPoint=null,cleared=false;
|
||||
const memoryKey='variant:'+pack.mode;let completed=[];try{completed=JSON.parse(localStorage.getItem(memoryKey)||'[]');}catch{}
|
||||
const labels=new Map();const metrics={};let started=0,record=null;
|
||||
function setStatus(s){$('status').textContent=s;}
|
||||
function modeMarkers(){labels.clear();let ids=[...(level.relations||[]).flatMap(r=>[r.from,r.to]),...(level.orderChains||[]).flat()];for(const id of ids)if(!labels.has(id))labels.set(id,String.fromCharCode(65+labels.size));}
|
||||
function makeRecord(){return {mode:pack.mode,level:level.id,difficulty:level.difficulty,title:level.title,startedAt:new Date().toISOString(),elapsedSeconds:0,firstError:null,backtracks:0,restarts:0,hints:0,completed:false,reasoning:''};}
|
||||
function start(i){index=i;level=pack.levels[i];solution=solve(level)[0];if(!solution)throw Error('没有合法路线');path=[];focus=[];cleared=false;started=performance.now();record=makeRecord();$('reasoning').value='';modeMarkers();$('goal').textContent=level.title;$('caption').textContent=level.caption||'先观察,再画;错了可以退回。';$('title').textContent=pack.title;$('rule-text').textContent=pack.rule;setStatus('从数字 1 开始;没有 1 时,可尝试任意格。');renderConstraints();renderSelectors();draw();}
|
||||
function renderSelectors(){
|
||||
$('difficulties').replaceChildren();for(const d of ['入门','普通','进阶','难']){const b=document.createElement('button');b.textContent=d;b.className=d===selected?'active':'';b.onclick=()=>{selected=d;start(pack.levels.findIndex(l=>l.difficulty===d));};$('difficulties').append(b);}
|
||||
$('levels').replaceChildren();for(let i=0;i<pack.levels.length;i++){const l=pack.levels[i];if(l.difficulty!==selected)continue;const b=document.createElement('button');b.textContent=`第 ${i%2+1} 关${completed.includes(l.id)?' ✓':''}`;b.className=i===index?'active':'';b.onclick=()=>start(i);$('levels').append(b);}
|
||||
$('position').textContent=`${level.difficulty} · ${index%2+1}/2`;
|
||||
}
|
||||
function renderConstraints(){const list=$('constraints');list.replaceChildren();
|
||||
const add=(text,ids)=>{const b=document.createElement('button');b.className='constraint';b.textContent=text;b.onclick=()=>{focus=ids;draw();};list.append(b);};
|
||||
for(const r of level.relations||[])add(`${labels.get(r.from)} → ${labels.get(r.to)} +${r.delta}`,[r.from,r.to]);
|
||||
for(const chain of level.orderChains||[])add(chain.map(id=>labels.get(id)).join(' < '),chain);
|
||||
for(const w of level.windows||[])add(`窗口 ${w.min}–${w.max}`,[w.cell]);
|
||||
for(const c of level.cells)if(c.direction)add(c.direction==='straight'?'↔ 必须直行':'↱ 必须转弯',[c.id]);
|
||||
if(level.mode.includes('alternation'))add('○ → △ → ○ · 每步换一类',[]);
|
||||
for(let i=0;i<(level.gates||[]).length;i++)add(`开关 ${i+1} → 门 ${i+1}`,[level.gates[i].switchCell,...level.gates[i].edge]);
|
||||
}
|
||||
function localGeometry(){
|
||||
const raw=[],center=new Map();for(const c of level.cells){if(level.geometry==='hex'){
|
||||
// Rotate the axial strip into portrait orientation; adjacency remains axial.
|
||||
const x=1.5*c.r,y=Math.sqrt(3)*(c.q+c.r/2);center.set(c.id,[x,y]);let p=[];for(let a=0;a<6;a++){const angle=a*Math.PI/3;p.push([x+Math.cos(angle),y+Math.sin(angle)]);}raw.push({id:c.id,p});
|
||||
}else{const u=units(c);center.set(c.id,[u.reduce((s,p)=>s+p[0]+.5,0)/u.length,u.reduce((s,p)=>s+p[1]+.5,0)/u.length]);for(const [x,y]of u)raw.push({id:c.id,p:[[x,y],[x+1,y],[x+1,y+1],[x,y+1]]});}}
|
||||
return {raw,center};
|
||||
}
|
||||
function draw(){if(!level)return;const width=canvas.clientWidth,height=canvas.clientHeight,dpr=window.devicePixelRatio||1;canvas.width=Math.round(width*dpr);canvas.height=Math.round(height*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,width,height);
|
||||
const {raw,center}=localGeometry(),all=raw.flatMap(r=>r.p),xmin=Math.min(...all.map(p=>p[0])),xmax=Math.max(...all.map(p=>p[0])),ymin=Math.min(...all.map(p=>p[1])),ymax=Math.max(...all.map(p=>p[1]));unit=Math.min((width-28)/(xmax-xmin),(height-24)/(ymax-ymin));const ox=(width-unit*(xmax-xmin))/2-unit*xmin,oy=(height-unit*(ymax-ymin))/2-unit*ymin;
|
||||
const tr=([x,y])=>[ox+x*unit,oy+y*unit];polygons=raw.map(r=>({...r,p:r.p.map(tr)}));centers=new Map([...center].map(([id,p])=>[id,tr(p)]));
|
||||
const bad=violations(level,path),badIds=new Set(bad.flatMap(b=>b.cells));
|
||||
for(const shape of polygons){const c=level.cells.find(c=>c.id===shape.id),visited=path.includes(c.id);ctx.beginPath();shape.p.forEach(([x,y],i)=>i?ctx.lineTo(x,y):ctx.moveTo(x,y));ctx.closePath();ctx.fillStyle=badIds.has(c.id)?'#ffe0dc':visited?'#ddd8ff':c.category==='circle'?'#e4f3ff':c.category==='triangle'?'#fff0d4':'#f0f0f6';ctx.fill();}
|
||||
// Draw only external unit edges, preserving the outline of an entire large block.
|
||||
const edgeCounts=new Map();for(const shape of polygons)for(let i=0;i<shape.p.length;i++){let a=shape.p[i],b=shape.p[(i+1)%shape.p.length];let key=shape.id+':'+[a.map(v=>v.toFixed(3)).join(','),b.map(v=>v.toFixed(3)).join(',')].sort().join('|');edgeCounts.set(key,(edgeCounts.get(key)||0)+1);}
|
||||
for(const shape of polygons)for(let i=0;i<shape.p.length;i++){const a=shape.p[i],b=shape.p[(i+1)%shape.p.length],key=shape.id+':'+[a.map(v=>v.toFixed(3)).join(','),b.map(v=>v.toFixed(3)).join(',')].sort().join('|');if(edgeCounts.get(key)>1)continue;ctx.beginPath();ctx.moveTo(...a);ctx.lineTo(...b);ctx.strokeStyle=focus.includes(shape.id)?'#f59c00':badIds.has(shape.id)?'#c74537':path.includes(shape.id)?'#7762db':'#b4b6cb';ctx.lineWidth=focus.includes(shape.id)?4:2;ctx.stroke();}
|
||||
if(path.length){ctx.beginPath();path.forEach((id,i)=>{const p=centers.get(id);i?ctx.lineTo(...p):ctx.moveTo(...p);});ctx.lineWidth=5;ctx.lineCap='round';ctx.lineJoin='round';ctx.strokeStyle='#7562db';ctx.globalAlpha=.32;ctx.stroke();ctx.globalAlpha=1;}
|
||||
for(let i=0;i<(level.gates||[]).length;i++){const gate=level.gates[i],a=centers.get(gate.edge[0]),b=centers.get(gate.edge[1]),mx=(a[0]+b[0])/2,my=(a[1]+b[1])/2,dx=b[0]-a[0],dy=b[1]-a[1],len=Math.hypot(dx,dy),open=path.includes(gate.switchCell);ctx.save();ctx.translate(mx,my);ctx.rotate(Math.atan2(dy,dx));ctx.beginPath();ctx.moveTo(0,-unit*.34);ctx.lineTo(0,unit*.34);ctx.lineWidth=7;ctx.strokeStyle=open?'#349375':'#d98123';if(open)ctx.setLineDash([3,5]);ctx.stroke();ctx.restore();ctx.fillStyle=open?'#25745c':'#8c4917';ctx.font='bold 12px system-ui';ctx.textAlign='center';ctx.fillText(String(i+1),mx+9,my-8);}
|
||||
for(const c of level.cells){const [x,y]=centers.get(c.id),step=path.indexOf(c.id)+1;const font=Math.max(14,Math.min(24,unit*.34));let symbol=labels.get(c.id)||'';const w=(level.windows||[]).find(w=>w.cell===c.id);if(w)symbol=`${w.min}–${w.max}`;if(c.direction)symbol=c.direction==='straight'?'↔':'↱';if(c.category)symbol=c.category==='circle'?'○':'△';const gateIndex=(level.gates||[]).findIndex(g=>g.switchCell===c.id);if(gateIndex!==-1)symbol=`开${gateIndex+1}`;
|
||||
ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillStyle=badIds.has(c.id)?'#a33127':'#30304e';ctx.font=`700 ${font}px system-ui`;if(step){ctx.fillText(String(step),x,y+ (symbol?5:0));if(symbol){ctx.font=`600 ${Math.max(11,font*.65)}px system-ui`;ctx.fillText(symbol,x,y-font*.65);}}else if(symbol)ctx.fillText(symbol,x,y);
|
||||
if(c.clue!==undefined){if(!step&&!symbol)ctx.fillText(String(c.clue),x,y);else{ctx.font=`700 ${Math.max(11,font*.65)}px system-ui`;ctx.fillText('需'+c.clue,x,y+font*.8);}}
|
||||
}
|
||||
$('remaining').textContent=cleared?'完成 ✓':`剩余 ${level.cells.length-path.length} 格`;
|
||||
if(bad.length)setStatus(bad[0].message+',可以点已走格退回。');
|
||||
$('undo').disabled=!path.length;$('next').hidden=!cleared||index===7;
|
||||
}
|
||||
function inside(x,y,p){let hit=false;for(let i=0,j=p.length-1;i<p.length;j=i++){const [a,b]=p[i],[c,d]=p[j];if((b>y)!==(d>y)&&x<(c-a)*(y-b)/(d-b)+a)hit=!hit;}return hit;}
|
||||
function hit(x,y){return polygons.find(s=>inside(x,y,s.p))?.id;}
|
||||
function markError(id,message){if(!record.firstError)record.firstError={step:path.length+1,cell:id,message};setStatus(message);}
|
||||
function advance(id){if(!id||cleared)return;const at=path.indexOf(id);if(at!==-1){if(at<path.length-1){record.backtracks++;path=path.slice(0,at+1);focus=[];setStatus('已回退,继续观察剩余格。');draw();}return;}
|
||||
if(!canAppend(level,path,id)){const g=graph(level),adjacent=path.length&&g.adj[g.ids.get(path.at(-1))].includes(g.ids.get(id));if(adjacent||!path.length){markError(id,path.length?'此处类别相同或门尚未打开。':'请从数字 1 开始。');}return;}
|
||||
if(!path.length)started=performance.now();path.push(id);focus=[];const bad=violations(level,path);if(bad.length)markError(id,bad[0].message);else setStatus('可松手续画;点已走格可回退。');if(path.length===level.cells.length&&!bad.length){cleared=true;record.completed=true;record.elapsedSeconds=Math.round((performance.now()-started)/1000);if(!completed.includes(level.id)){completed.push(level.id);try{localStorage.setItem(memoryKey,JSON.stringify(completed));}catch{}}setStatus('完成!可以进入下一关,或记录你的推理。');renderSelectors();}draw();}
|
||||
function point(e){const r=canvas.getBoundingClientRect();return [e.clientX-r.left,e.clientY-r.top];}
|
||||
canvas.addEventListener('pointerdown',e=>{e.preventDefault();drag=true;canvas.setPointerCapture(e.pointerId);lastPoint=point(e);advance(hit(...lastPoint));});
|
||||
canvas.addEventListener('pointermove',e=>{if(!drag)return;e.preventDefault();const p=point(e),a=lastPoint||p,d=Math.hypot(p[0]-a[0],p[1]-a[1]),steps=Math.ceil(d/Math.max(3,unit*.1));for(let i=1;i<=steps;i++)advance(hit(a[0]+(p[0]-a[0])*i/steps,a[1]+(p[1]-a[1])*i/steps));lastPoint=p;});
|
||||
function end(){drag=false;lastPoint=null;}canvas.addEventListener('pointerup',end);canvas.addEventListener('pointercancel',end);
|
||||
$('undo').onclick=()=>{if(path.length){path.pop();record.backtracks++;record.completed=false;cleared=false;focus=[];setStatus('已撤销一步。');draw();}};
|
||||
$('restart').onclick=()=>{path=[];cleared=false;focus=[];record.restarts++;record.completed=false;started=performance.now();setStatus('已重开。');draw();};
|
||||
$('hint').onclick=()=>{record.hints++;const h=hint(solution,path);focus=h.kind==='none'?[]:[h.cell];setStatus(h.kind==='backtrack'?'请退回黄色边框格,再继续。':h.kind==='restart'?'起点需要调整,请重开并从黄色边框格开始。':h.kind==='none'?'已经完成。':'下一步在黄色边框格;提示不会自动画路。');draw();};
|
||||
$('next').onclick=()=>{selected=pack.levels[index+1].difficulty;start(index+1);};
|
||||
$('export').onclick=()=>{record.elapsedSeconds=record.completed?record.elapsedSeconds:Math.round((performance.now()-started)/1000);record.reasoning=$('reasoning').value;const blob=new Blob([JSON.stringify(record,null,2)],{type:'application/json'}),a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=pack.mode+'-'+level.id+'-试玩记录.json';a.click();setTimeout(()=>URL.revokeObjectURL(a.href),1000);};
|
||||
window.addEventListener('resize',draw);start(0);
|
||||
// Read-only snapshot used for interaction verification; never supplies an answer.
|
||||
window.variantSnapshot=()=>({mode:pack.mode,level:level.id,path:[...path],violations:violations(level,path),cleared,centers:Object.fromEntries(centers),unit,openedGates:(level.gates||[]).map(g=>path.includes(g.switchCell))});
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 266.66666666666663" role="img" aria-label="A 必须先于 B"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text></svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 266.66666666666663" role="img" aria-label="A 必须先于 B"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="303.3333333333333,76.66666666666666 303.3333333333333,190 190,190 76.66666666666666,190 76.66666666666666,76.66666666666666 190,76.66666666666666" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="靠近 B 也要先绕到 A"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,246.66666666666666 133.33333333333331,246.66666666666666 133.33333333333331,360 20,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,246.66666666666666 246.66666666666666,246.66666666666666 246.66666666666666,360 133.33333333333331,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="76.66666666666666" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="190" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text></svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="靠近 B 也要先绕到 A"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,246.66666666666666 133.33333333333331,246.66666666666666 133.33333333333331,360 20,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,246.66666666666666 246.66666666666666,246.66666666666666 246.66666666666666,360 133.33333333333331,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="76.66666666666666,190 76.66666666666666,76.66666666666666 190,76.66666666666666 303.3333333333333,76.66666666666666 303.3333333333333,190 190,190 190,303.3333333333333 76.66666666666666,303.3333333333333" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="76.66666666666666" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="76.66666666666666" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="190" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="190" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="三标记决定区域顺序"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text></svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="三标记决定区域顺序"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="147.5,232.5 232.5,232.5 232.5,147.5 147.5,147.5 62.5,147.5 62.5,62.5 147.5,62.5 232.5,62.5 317.5,62.5 317.5,147.5" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">9</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">10</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text></svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两条独立先后关系"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两条独立先后关系"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="147.5,232.5 62.5,232.5 62.5,147.5 62.5,62.5 147.5,62.5 147.5,147.5 232.5,147.5 232.5,232.5 317.5,232.5 317.5,147.5 317.5,62.5 232.5,62.5" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">12</text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">11</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">10</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">9</text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="共享标记形成部分顺序"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,275 190,275 190,360 105,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,275 275,275 275,360 190,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">13</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text><text x="147.5" y="309.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="327.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c12</text><text x="232.5" y="309.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="327.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c13</text></svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,187 @@
|
||||
# 先后链
|
||||
|
||||
每格一次。链中的标记必须依次访问,例如 A < B < C;相隔几步不受限制。
|
||||
|
||||
固定数字表示必须在该步到达;每格一次、共边移动、完整路线唯一。所有关卡开放。
|
||||
|
||||
这些是机制验证候选关,难度标签按设计任务编排,尚未经真人校准。
|
||||
|
||||
## 01 · 入门 · A 必须先于 B
|
||||
|
||||
格数 6;基础完整路线 16 条;新约束 1 项。
|
||||
|
||||
作者解:c2 → c5 → c4 → c3 → c0 → c1。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c5 先于 c0,作者步号 2<5
|
||||
|
||||
关键分叉:第 2 步应选 c5,解除机制后的诱惑路线选择 c1。排除依据:标记次序不符。
|
||||
|
||||
诱惑路线:c2 → c1 → c0 → c3 → c4 → c5。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 order:0:0 后可走 c2 → c1 → c0 → c3 → c4 → c5
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 02 · 入门 · 靠近 B 也要先绕到 A
|
||||
|
||||
格数 8;基础完整路线 24 条;新约束 1 项。
|
||||
|
||||
作者解:c3 → c0 → c1 → c2 → c5 → c4 → c7 → c6。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c0 先于 c6,作者步号 2<8
|
||||
|
||||
关键分叉:第 2 步应选 c0,解除机制后的诱惑路线选择 c6。排除依据:标记次序不符。
|
||||
|
||||
诱惑路线:c3 → c6 → c7 → c4 → c5 → c2 → c1 → c0。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 order:0:0 后可走 c3 → c6 → c7 → c4 → c5 → c2 → c1 → c0
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 03 · 普通 · 三标记决定区域顺序
|
||||
|
||||
格数 10;基础完整路线 34 条;新约束 2 项。
|
||||
|
||||
作者解:c8 → c9 → c6 → c5 → c4 → c0 → c1 → c2 → c3 → c7。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c9 先于 c0,作者步号 2<6
|
||||
- c0 先于 c3,作者步号 6<9
|
||||
|
||||
关键分叉:第 2 步应选 c9,解除机制后的诱惑路线选择 c5。排除依据:标记次序不符。
|
||||
|
||||
诱惑路线:c8 → c5 → c4 → c0 → c1 → c2 → c3 → c7 → c6 → c9。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 order:0:0 后可走 c8 → c5 → c4 → c0 → c1 → c2 → c3 → c7 → c6 → c9
|
||||
- 解除 order:0:1 后可走 c8 → c9 → c6 → c7 → c3 → c2 → c1 → c0 → c4 → c5
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 04 · 普通 · 两条独立先后关系
|
||||
|
||||
格数 12;基础完整路线 124 条;新约束 2 项。
|
||||
|
||||
作者解:c9 → c8 → c4 → c0 → c1 → c5 → c6 → c10 → c11 → c7 → c3 → c2。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c8 先于 c6,作者步号 2<7
|
||||
- c10 先于 c3,作者步号 8<11
|
||||
|
||||
关键分叉:第 2 步应选 c8,解除机制后的诱惑路线选择 c10。排除依据:标记次序不符。
|
||||
|
||||
诱惑路线:c9 → c10 → c11 → c7 → c3 → c2 → c6 → c5 → c1 → c0 → c4 → c8。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 order:0:0 后可走 c9 → c10 → c11 → c7 → c3 → c2 → c6 → c5 → c1 → c0 → c4 → c8
|
||||
- 解除 order:1:0 后可走 c9 → c8 → c4 → c0 → c1 → c5 → c6 → c2 → c3 → c7 → c11 → c10
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 05 · 进阶 · 共享标记形成部分顺序
|
||||
|
||||
格数 14;基础完整路线 100 条;新约束 2 项。
|
||||
|
||||
作者解:c5 → c6 → c2 → c1 → c0 → c4 → c8 → c9 → c12 → c13 → c10 → c11 → c7 → c3。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c6 先于 c9,作者步号 2<8
|
||||
- c1 先于 c9,作者步号 4<8
|
||||
|
||||
关键分叉:第 2 步应选 c6,解除机制后的诱惑路线选择 c1。排除依据:标记次序不符。
|
||||
|
||||
诱惑路线:c5 → c1 → c0 → c4 → c8 → c9 → c12 → c13 → c10 → c6 → c2 → c3 → c7 → c11。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 order:0:0 后可走 c5 → c1 → c0 → c4 → c8 → c9 → c12 → c13 → c10 → c6 → c2 → c3 → c7 → c11
|
||||
- 解除 order:1:0 后可走 c5 → c6 → c10 → c13 → c12 → c9 → c8 → c4 → c0 → c1 → c2 → c3 → c7 → c11
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 06 · 进阶 · 访问标记时保留后续出口
|
||||
|
||||
格数 16;基础完整路线 552 条;新约束 2 项。
|
||||
|
||||
作者解:c6 → c5 → c9 → c10 → c14 → c15 → c11 → c7 → c3 → c2 → c1 → c0 → c4 → c8 → c12 → c13。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c9 先于 c3,作者步号 3<9
|
||||
- c3 先于 c12,作者步号 9<15
|
||||
|
||||
关键分叉:第 2 步应选 c5,解除机制后的诱惑路线选择 c2。排除依据:标记次序不符。
|
||||
|
||||
诱惑路线:c6 → c2 → c3 → c7 → c11 → c15 → c14 → c10 → c9 → c5 → c1 → c0 → c4 → c8 → c12 → c13。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 order:0:0 后可走 c6 → c2 → c3 → c7 → c11 → c15 → c14 → c10 → c9 → c5 → c1 → c0 → c4 → c8 → c12 → c13
|
||||
- 解除 order:0:1 后可走 c6 → c5 → c9 → c10 → c11 → c15 → c14 → c13 → c12 → c8 → c4 → c0 → c1 → c2 → c3 → c7
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 07 · 难 · 两条链交错安排
|
||||
|
||||
格数 18;基础完整路线 464 条;新约束 4 项。
|
||||
|
||||
作者解:c16 → c17 → c14 → c13 → c12 → c8 → c4 → c0 → c1 → c5 → c9 → c10 → c6 → c2 → c3 → c7 → c11 → c15。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c14 先于 c1,作者步号 3<9
|
||||
- c1 先于 c10,作者步号 9<12
|
||||
- c13 先于 c5,作者步号 4<10
|
||||
- c5 先于 c6,作者步号 10<13
|
||||
|
||||
关键分叉:第 2 步应选 c17,解除机制后的诱惑路线选择 c13。排除依据:标记次序不符。
|
||||
|
||||
诱惑路线:c16 → c13 → c12 → c8 → c9 → c5 → c4 → c0 → c1 → c2 → c3 → c7 → c6 → c10 → c11 → c15 → c14 → c17。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 order:0:0 后可走 c16 → c13 → c12 → c8 → c9 → c5 → c4 → c0 → c1 → c2 → c3 → c7 → c6 → c10 → c11 → c15 → c14 → c17
|
||||
- 解除 order:0:1 后可走 c16 → c17 → c14 → c15 → c11 → c10 → c9 → c13 → c12 → c8 → c4 → c5 → c6 → c7 → c3 → c2 → c1 → c0
|
||||
- 解除 order:1:0 后可走 c16 → c17 → c14 → c15 → c11 → c7 → c3 → c2 → c1 → c0 → c4 → c5 → c6 → c10 → c9 → c8 → c12 → c13
|
||||
- 解除 order:1:1 后可走 c16 → c17 → c14 → c13 → c12 → c8 → c4 → c0 → c1 → c2 → c3 → c7 → c6 → c5 → c9 → c10 → c11 → c15
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 08 · 难 · 无固定起点,由次序确定全路
|
||||
|
||||
格数 19;基础完整路线 544 条;新约束 4 项。
|
||||
|
||||
作者解:c8 → c12 → c16 → c17 → c13 → c9 → c10 → c6 → c5 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c15 → c14 → c18。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c12 先于 c13,作者步号 2<5
|
||||
- c13 先于 c3,作者步号 5<14
|
||||
- c16 先于 c1,作者步号 3<12
|
||||
- c1 先于 c14,作者步号 12<18
|
||||
|
||||
关键分叉:第 1 步应选 c8,解除机制后的诱惑路线选择 c13。排除依据:标记次序不符。
|
||||
|
||||
诱惑路线:c13 → c17 → c16 → c12 → c8 → c9 → c10 → c6 → c5 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c15 → c14 → c18。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 order:0:0 后可走 c13 → c17 → c16 → c12 → c8 → c9 → c10 → c6 → c5 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c15 → c14 → c18
|
||||
- 解除 order:0:1 后可走 c18 → c17 → c16 → c12 → c8 → c9 → c10 → c6 → c5 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c15 → c14 → c13
|
||||
- 解除 order:1:0 后可走 c0 → c4 → c8 → c12 → c13 → c9 → c10 → c6 → c5 → c1 → c2 → c3 → c7 → c11 → c15 → c14 → c18 → c17 → c16
|
||||
- 解除 order:1:1 后可走 c16 → c12 → c8 → c4 → c5 → c6 → c10 → c9 → c13 → c17 → c18 → c14 → c15 → c11 → c7 → c3 → c2 → c1 → c0
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
@@ -0,0 +1,891 @@
|
||||
{
|
||||
"mode": "order",
|
||||
"title": "先后链",
|
||||
"rule": "每格一次。链中的标记必须依次访问,例如 A < B < C;相隔几步不受限制。",
|
||||
"levels": [
|
||||
{
|
||||
"id": "01",
|
||||
"mode": "order",
|
||||
"geometry": "square",
|
||||
"difficulty": "入门",
|
||||
"title": "A 必须先于 B",
|
||||
"caption": "每格一次。链中的标记必须依次访问,例如 A < B < C;相隔几步不受限制。",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 4
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"orderChains": [
|
||||
[
|
||||
"c5",
|
||||
"c0"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "02",
|
||||
"mode": "order",
|
||||
"geometry": "square",
|
||||
"difficulty": "入门",
|
||||
"title": "靠近 B 也要先绕到 A",
|
||||
"caption": "每格一次。链中的标记必须依次访问,例如 A < B < C;相隔几步不受限制。",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"orderChains": [
|
||||
[
|
||||
"c0",
|
||||
"c6"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "03",
|
||||
"mode": "order",
|
||||
"geometry": "square",
|
||||
"difficulty": "普通",
|
||||
"title": "三标记决定区域顺序",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"orderChains": [
|
||||
[
|
||||
"c9",
|
||||
"c0",
|
||||
"c3"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "04",
|
||||
"mode": "order",
|
||||
"geometry": "square",
|
||||
"difficulty": "普通",
|
||||
"title": "两条独立先后关系",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 7
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"orderChains": [
|
||||
[
|
||||
"c8",
|
||||
"c6"
|
||||
],
|
||||
[
|
||||
"c10",
|
||||
"c3"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "05",
|
||||
"mode": "order",
|
||||
"geometry": "square",
|
||||
"difficulty": "进阶",
|
||||
"title": "共享标记形成部分顺序",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 13
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"orderChains": [
|
||||
[
|
||||
"c6",
|
||||
"c9"
|
||||
],
|
||||
[
|
||||
"c1",
|
||||
"c9"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "06",
|
||||
"mode": "order",
|
||||
"geometry": "square",
|
||||
"difficulty": "进阶",
|
||||
"title": "访问标记时保留后续出口",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 12
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c14",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c15",
|
||||
"x": 3,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"orderChains": [
|
||||
[
|
||||
"c9",
|
||||
"c3",
|
||||
"c12"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "07",
|
||||
"mode": "order",
|
||||
"geometry": "square",
|
||||
"difficulty": "难",
|
||||
"title": "两条链交错安排",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 13
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c14",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c15",
|
||||
"x": 3,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c16",
|
||||
"x": 1,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c17",
|
||||
"x": 2,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"orderChains": [
|
||||
[
|
||||
"c14",
|
||||
"c1",
|
||||
"c10"
|
||||
],
|
||||
[
|
||||
"c13",
|
||||
"c5",
|
||||
"c6"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "08",
|
||||
"mode": "order",
|
||||
"geometry": "square",
|
||||
"difficulty": "难",
|
||||
"title": "无固定起点,由次序确定全路",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 7
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c14",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c15",
|
||||
"x": 3,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c16",
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c17",
|
||||
"x": 1,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c18",
|
||||
"x": 2,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"orderChains": [
|
||||
[
|
||||
"c12",
|
||||
"c13",
|
||||
"c3"
|
||||
],
|
||||
[
|
||||
"c16",
|
||||
"c1",
|
||||
"c14"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Pure rules / geometry / solver. Each mode receives its own standalone copy.
|
||||
export function units(c){return c.shape?c.shape.map(([x,y])=>[c.x+x,c.y+y]):Array.from({length:(c.w||1)*(c.h||1)},(_,i)=>[c.x+i%(c.w||1),c.y+Math.floor(i/(c.w||1))]);}
|
||||
export function graph(level){
|
||||
const cells=level.cells, ids=new Map(cells.map((c,i)=>[c.id,i]));
|
||||
if(ids.size!==cells.length)throw Error('重复格 id');
|
||||
const adj=cells.map(()=>[]), occupancy=new Map();
|
||||
if(level.geometry!=='hex') for(let i=0;i<cells.length;i++)for(const [x,y]of units(cells[i])){const k=x+','+y;if(occupancy.has(k))throw Error('拼块重叠');occupancy.set(k,i);}
|
||||
for(let i=0;i<cells.length;i++)for(let j=i+1;j<cells.length;j++){
|
||||
let connected=false;
|
||||
if(level.geometry==='hex'){let dq=cells[j].q-cells[i].q,dr=cells[j].r-cells[i].r;connected=[[1,0],[-1,0],[0,1],[0,-1],[1,-1],[-1,1]].some(([a,b])=>a===dq&&b===dr);}
|
||||
else connected=units(cells[i]).some(([x,y])=>[[1,0],[-1,0],[0,1],[0,-1]].some(([a,b])=>occupancy.get((x+a)+','+(y+b))===j));
|
||||
if(connected){adj[i].push(j);adj[j].push(i);}
|
||||
}
|
||||
const clues=new Map();for(const c of cells)if(c.clue!==undefined){if(!Number.isInteger(c.clue)||c.clue<1||c.clue>cells.length||clues.has(c.clue))throw Error('数字范围或重复');clues.set(c.clue,ids.get(c.id));}
|
||||
return {cells,ids,adj,clues};
|
||||
}
|
||||
export function constraints(level){
|
||||
const result=[];
|
||||
for(let i=0;i<(level.relations||[]).length;i++)result.push({key:'relation:'+i,kind:'relation',...level.relations[i]});
|
||||
for(let i=0;i<(level.orderChains||[]).length;i++)for(let j=0;j<level.orderChains[i].length-1;j++)result.push({key:'order:'+i+':'+j,kind:'order',from:level.orderChains[i][j],to:level.orderChains[i][j+1]});
|
||||
for(let i=0;i<(level.windows||[]).length;i++)result.push({key:'window:'+i,kind:'window',...level.windows[i]});
|
||||
for(const c of level.cells)if(c.direction)result.push({key:'direction:'+c.id,kind:'direction',cell:c.id,value:c.direction});
|
||||
if(level.mode.includes('alternation'))result.push({key:'alternation',kind:'alternation'});
|
||||
for(let i=0;i<(level.gates||[]).length;i++)result.push({key:'gate:'+i,kind:'gate',...level.gates[i]});
|
||||
return result;
|
||||
}
|
||||
export function directionAt(level,path,k){
|
||||
if(k===0||k===path.length-1)return null;
|
||||
const [a,b,c]=[path[k-1],path[k],path[k+1]].map(id=>level.cells.find(x=>x.id===id));
|
||||
return b.x-a.x===c.x-b.x&&b.y-a.y===c.y-b.y?'straight':'turn';
|
||||
}
|
||||
export function violations(level,path,{omit=null,complete=path.length===level.cells.length,ignoreClues=false}={}){
|
||||
const positions=new Map(path.map((id,i)=>[id,i+1])),bad=[];
|
||||
if(!ignoreClues)for(const c of level.cells)if(c.clue!==undefined){const at=positions.get(c.id);if(at!==undefined&&at!==c.clue||path.length>=c.clue&&path[c.clue-1]!==c.id)bad.push({key:'clue:'+c.id,cells:[c.id,path[c.clue-1]].filter(Boolean),message:'固定数字步号不符'});}
|
||||
for(const r of constraints(level)){
|
||||
if(r.key===omit)continue;
|
||||
let fail=false,where=[];
|
||||
if(r.kind==='relation'){
|
||||
const a=positions.get(r.from),b=positions.get(r.to);where=[r.from,r.to];
|
||||
fail=b!==undefined&&(a===undefined||b-a!==r.delta)||a!==undefined&&b===undefined&&path.length>=a+r.delta;
|
||||
}else if(r.kind==='order'){where=[r.from,r.to];fail=positions.has(r.to)&&(!positions.has(r.from)||positions.get(r.from)>positions.get(r.to));}
|
||||
else if(r.kind==='window'){where=[r.cell];const p=positions.get(r.cell);fail=p!==undefined?(p<r.min||p>r.max):path.length>=r.max;}
|
||||
else if(r.kind==='direction'){where=[r.cell];const p=positions.get(r.cell);fail=p===1||p!==undefined&&p<path.length&&directionAt(level,path,p-1)!==r.value||complete&&p===path.length;}
|
||||
else if(r.kind==='alternation'){for(let i=1;i<path.length;i++){const a=level.cells.find(c=>c.id===path[i-1]),b=level.cells.find(c=>c.id===path[i]);if(a.category===b.category){fail=true;where.push(a.id,b.id);}}}
|
||||
else if(r.kind==='gate')for(let i=1;i<path.length;i++){if(r.edge.includes(path[i-1])&&r.edge.includes(path[i])){const s=positions.get(r.switchCell);if(s===undefined||s>i){fail=true;where=r.edge;}}}
|
||||
if(fail)bad.push({key:r.key,cells:where,message:r.kind==='relation'?'相对步差不符':r.kind==='order'?'标记次序不符':r.kind==='window'?'到达窗口不符':r.kind==='direction'?'进出方向不符':r.kind==='gate'?'开关尚未打开此门':'类别没有交替'});
|
||||
}
|
||||
return bad;
|
||||
}
|
||||
export function canAppend(level,path,id){
|
||||
const g=graph(level);if(path.includes(id)||!g.ids.has(id))return false;
|
||||
if(!path.length)return !g.clues.has(1)||g.cells[g.clues.get(1)].id===id;
|
||||
const last=path.at(-1);if(!g.adj[g.ids.get(last)].includes(g.ids.get(id)))return false;
|
||||
if(level.mode.includes('alternation')&&g.cells[g.ids.get(last)].category===g.cells[g.ids.get(id)].category)return false;
|
||||
for(const gate of level.gates||[])if(gate.edge.includes(last)&&gate.edge.includes(id)&&!path.includes(gate.switchCell))return false;
|
||||
return true;
|
||||
}
|
||||
export function solve(level,{limit=2,omit=null,ignoreClues=false,prune=true,nodeLimit=Infinity}={}){
|
||||
const g=graph(level),n=g.cells.length,results=[],path=[],seen=new Set();let nodes=0;
|
||||
const active=constraints(level).filter(r=>r.key!==omit),alternating=active.some(r=>r.kind==='alternation');
|
||||
const adj=g.adj.map((a,i)=>a.filter(j=>!alternating||g.cells[i].category!==g.cells[j].category));
|
||||
const forced=ignoreClues?new Map():g.clues;
|
||||
function connected(cur){const reached=new Set([cur]),queue=[cur];for(let k=0;k<queue.length;k++)for(const j of adj[queue[k]])if(!seen.has(j)&&!reached.has(j)){reached.add(j);queue.push(j);}return reached.size===n-path.length+1;}
|
||||
function visit(i){if(++nodes>nodeLimit)throw Error('求解节点预算耗尽,不能视为唯一');seen.add(i);path.push(g.cells[i].id);
|
||||
const k=path.length;
|
||||
if(!violations(level,path,{omit,ignoreClues}).length){
|
||||
if(k===n)results.push([...path]);
|
||||
else if(!prune||connected(i)){
|
||||
const f=forced.get(k+1);let next=adj[i].filter(j=>!seen.has(j)&&(f===undefined||j===f));
|
||||
next.sort((a,b)=>adj[a].filter(x=>!seen.has(x)).length-adj[b].filter(x=>!seen.has(x)).length);
|
||||
for(const j of next){if(!ignoreClues&&g.cells[j].clue!==undefined&&g.cells[j].clue!==k+1)continue;
|
||||
let blocked=false;for(const r of active)if(r.kind==='gate'&&r.edge.includes(g.cells[i].id)&&r.edge.includes(g.cells[j].id)&&!path.includes(r.switchCell))blocked=true;
|
||||
if(!blocked)visit(j);if(results.length>=limit)break;
|
||||
}
|
||||
}
|
||||
}
|
||||
path.pop();seen.delete(i);
|
||||
}
|
||||
let starts=forced.has(1)?[forced.get(1)]:g.cells.map((_,i)=>i);
|
||||
for(const i of starts){if(!ignoreClues&&g.cells[i].clue!==undefined&&g.cells[i].clue!==1)continue;visit(i);if(results.length>=limit)break;}
|
||||
return results;
|
||||
}
|
||||
export function hint(solution,path){let k=0;while(k<path.length&&path[k]===solution[k])k++;if(k<path.length)return {kind:k?'backtrack':'restart',cell:k?path[k-1]:solution[0]};return k===solution.length?{kind:'none'}:{kind:k?'next':'start',cell:solution[k]};}
|
||||
export function assertStructure(level){
|
||||
const g=graph(level),n=g.cells.length;if(n<3||n>20)throw Error('格数须为3–20');
|
||||
if(level.geometry==='hex'){const keys=new Set();for(const c of g.cells){if(!Number.isInteger(c.q)||!Number.isInteger(c.r))throw Error('六边坐标');const key=c.q+','+c.r;if(keys.has(key))throw Error('六边重叠');keys.add(key);}}
|
||||
else for(const c of g.cells){if(!Number.isInteger(c.x)||!Number.isInteger(c.y))throw Error('坐标');const u=units(c);if(!u.length||new Set(u.map(p=>p.join(','))).size!==u.length)throw Error('异形重复');const seen=new Set([u[0].join(',')]);for(let changed=true;changed;){changed=false;for(const [x,y]of u)if(!seen.has(x+','+y)&&[[1,0],[-1,0],[0,1],[0,-1]].some(([a,b])=>seen.has((x+a)+','+(y+b)))){seen.add(x+','+y);changed=true;}}if(seen.size!==u.length)throw Error('异形不连通');}
|
||||
const reach=new Set([0]),queue=[0];for(let k=0;k<queue.length;k++)for(const j of g.adj[queue[k]])if(!reach.has(j)){reach.add(j);queue.push(j);}if(reach.size!==n)throw Error('盘面不连通');
|
||||
const pairs=[];
|
||||
for(const r of constraints(level)){
|
||||
for(const id of [r.cell,r.from,r.to,r.switchCell,...(r.edge||[])].filter(Boolean))if(!g.ids.has(id))throw Error('约束引用不存在');
|
||||
if(r.kind==='relation'&&(!Number.isInteger(r.delta)||r.delta<1||r.delta>=n||r.from===r.to))throw Error('步差');
|
||||
if(r.kind==='order')pairs.push([r.from,r.to]);
|
||||
if(r.kind==='window'&&(!Number.isInteger(r.min)||!Number.isInteger(r.max)||r.min<1||r.max>n||r.max-r.min<2||g.cells[g.ids.get(r.cell)].clue!==undefined))throw Error('窗口需允许至少两个同奇偶步号且不与数字重叠');
|
||||
if(r.kind==='direction'&&!['straight','turn'].includes(r.value))throw Error('方向');
|
||||
if(r.kind==='gate'&&(r.edge.length!==2||!g.adj[g.ids.get(r.edge[0])].includes(g.ids.get(r.edge[1]))))throw Error('门不在共边');
|
||||
}
|
||||
if((level.gates||[]).length>3||new Set((level.gates||[]).map(r=>r.switchCell)).size!==(level.gates||[]).length)throw Error('最多三门且开关一对一');
|
||||
if(level.mode.includes('alternation')&&g.cells.some(c=>!['circle','triangle'].includes(c.category)))throw Error('缺少类别');
|
||||
function cycle(v,stack=new Set(),done=new Set()){if(stack.has(v))throw Error('先后链循环');if(done.has(v))return;stack.add(v);for(const [a,b]of pairs)if(a===v)cycle(b,stack,done);stack.delete(v);done.add(v);}for(const [a]of pairs)cycle(a);
|
||||
return g;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {graph,units,solve,violations,canAppend,hint} from './rules.mjs';
|
||||
// The build embeds this module together with the local rule file and JSON.
|
||||
const pack=PACK;
|
||||
const $=id=>document.getElementById(id),canvas=$('board'),ctx=canvas.getContext('2d');
|
||||
let level,solution,path=[],index=0,selected='入门',polygons=[],centers=new Map(),unit=1,focus=[],drag=false,lastPoint=null,cleared=false;
|
||||
const memoryKey='variant:'+pack.mode;let completed=[];try{completed=JSON.parse(localStorage.getItem(memoryKey)||'[]');}catch{}
|
||||
const labels=new Map();const metrics={};let started=0,record=null;
|
||||
function setStatus(s){$('status').textContent=s;}
|
||||
function modeMarkers(){labels.clear();let ids=[...(level.relations||[]).flatMap(r=>[r.from,r.to]),...(level.orderChains||[]).flat()];for(const id of ids)if(!labels.has(id))labels.set(id,String.fromCharCode(65+labels.size));}
|
||||
function makeRecord(){return {mode:pack.mode,level:level.id,difficulty:level.difficulty,title:level.title,startedAt:new Date().toISOString(),elapsedSeconds:0,firstError:null,backtracks:0,restarts:0,hints:0,completed:false,reasoning:''};}
|
||||
function start(i){index=i;level=pack.levels[i];solution=solve(level)[0];if(!solution)throw Error('没有合法路线');path=[];focus=[];cleared=false;started=performance.now();record=makeRecord();$('reasoning').value='';modeMarkers();$('goal').textContent=level.title;$('caption').textContent=level.caption||'先观察,再画;错了可以退回。';$('title').textContent=pack.title;$('rule-text').textContent=pack.rule;setStatus('从数字 1 开始;没有 1 时,可尝试任意格。');renderConstraints();renderSelectors();draw();}
|
||||
function renderSelectors(){
|
||||
$('difficulties').replaceChildren();for(const d of ['入门','普通','进阶','难']){const b=document.createElement('button');b.textContent=d;b.className=d===selected?'active':'';b.onclick=()=>{selected=d;start(pack.levels.findIndex(l=>l.difficulty===d));};$('difficulties').append(b);}
|
||||
$('levels').replaceChildren();for(let i=0;i<pack.levels.length;i++){const l=pack.levels[i];if(l.difficulty!==selected)continue;const b=document.createElement('button');b.textContent=`第 ${i%2+1} 关${completed.includes(l.id)?' ✓':''}`;b.className=i===index?'active':'';b.onclick=()=>start(i);$('levels').append(b);}
|
||||
$('position').textContent=`${level.difficulty} · ${index%2+1}/2`;
|
||||
}
|
||||
function renderConstraints(){const list=$('constraints');list.replaceChildren();
|
||||
const add=(text,ids)=>{const b=document.createElement('button');b.className='constraint';b.textContent=text;b.onclick=()=>{focus=ids;draw();};list.append(b);};
|
||||
for(const r of level.relations||[])add(`${labels.get(r.from)} → ${labels.get(r.to)} +${r.delta}`,[r.from,r.to]);
|
||||
for(const chain of level.orderChains||[])add(chain.map(id=>labels.get(id)).join(' < '),chain);
|
||||
for(const w of level.windows||[])add(`窗口 ${w.min}–${w.max}`,[w.cell]);
|
||||
for(const c of level.cells)if(c.direction)add(c.direction==='straight'?'↔ 必须直行':'↱ 必须转弯',[c.id]);
|
||||
if(level.mode.includes('alternation'))add('○ → △ → ○ · 每步换一类',[]);
|
||||
for(let i=0;i<(level.gates||[]).length;i++)add(`开关 ${i+1} → 门 ${i+1}`,[level.gates[i].switchCell,...level.gates[i].edge]);
|
||||
}
|
||||
function localGeometry(){
|
||||
const raw=[],center=new Map();for(const c of level.cells){if(level.geometry==='hex'){
|
||||
// Rotate the axial strip into portrait orientation; adjacency remains axial.
|
||||
const x=1.5*c.r,y=Math.sqrt(3)*(c.q+c.r/2);center.set(c.id,[x,y]);let p=[];for(let a=0;a<6;a++){const angle=a*Math.PI/3;p.push([x+Math.cos(angle),y+Math.sin(angle)]);}raw.push({id:c.id,p});
|
||||
}else{const u=units(c);center.set(c.id,[u.reduce((s,p)=>s+p[0]+.5,0)/u.length,u.reduce((s,p)=>s+p[1]+.5,0)/u.length]);for(const [x,y]of u)raw.push({id:c.id,p:[[x,y],[x+1,y],[x+1,y+1],[x,y+1]]});}}
|
||||
return {raw,center};
|
||||
}
|
||||
function draw(){if(!level)return;const width=canvas.clientWidth,height=canvas.clientHeight,dpr=window.devicePixelRatio||1;canvas.width=Math.round(width*dpr);canvas.height=Math.round(height*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,width,height);
|
||||
const {raw,center}=localGeometry(),all=raw.flatMap(r=>r.p),xmin=Math.min(...all.map(p=>p[0])),xmax=Math.max(...all.map(p=>p[0])),ymin=Math.min(...all.map(p=>p[1])),ymax=Math.max(...all.map(p=>p[1]));unit=Math.min((width-28)/(xmax-xmin),(height-24)/(ymax-ymin));const ox=(width-unit*(xmax-xmin))/2-unit*xmin,oy=(height-unit*(ymax-ymin))/2-unit*ymin;
|
||||
const tr=([x,y])=>[ox+x*unit,oy+y*unit];polygons=raw.map(r=>({...r,p:r.p.map(tr)}));centers=new Map([...center].map(([id,p])=>[id,tr(p)]));
|
||||
const bad=violations(level,path),badIds=new Set(bad.flatMap(b=>b.cells));
|
||||
for(const shape of polygons){const c=level.cells.find(c=>c.id===shape.id),visited=path.includes(c.id);ctx.beginPath();shape.p.forEach(([x,y],i)=>i?ctx.lineTo(x,y):ctx.moveTo(x,y));ctx.closePath();ctx.fillStyle=badIds.has(c.id)?'#ffe0dc':visited?'#ddd8ff':c.category==='circle'?'#e4f3ff':c.category==='triangle'?'#fff0d4':'#f0f0f6';ctx.fill();}
|
||||
// Draw only external unit edges, preserving the outline of an entire large block.
|
||||
const edgeCounts=new Map();for(const shape of polygons)for(let i=0;i<shape.p.length;i++){let a=shape.p[i],b=shape.p[(i+1)%shape.p.length];let key=shape.id+':'+[a.map(v=>v.toFixed(3)).join(','),b.map(v=>v.toFixed(3)).join(',')].sort().join('|');edgeCounts.set(key,(edgeCounts.get(key)||0)+1);}
|
||||
for(const shape of polygons)for(let i=0;i<shape.p.length;i++){const a=shape.p[i],b=shape.p[(i+1)%shape.p.length],key=shape.id+':'+[a.map(v=>v.toFixed(3)).join(','),b.map(v=>v.toFixed(3)).join(',')].sort().join('|');if(edgeCounts.get(key)>1)continue;ctx.beginPath();ctx.moveTo(...a);ctx.lineTo(...b);ctx.strokeStyle=focus.includes(shape.id)?'#f59c00':badIds.has(shape.id)?'#c74537':path.includes(shape.id)?'#7762db':'#b4b6cb';ctx.lineWidth=focus.includes(shape.id)?4:2;ctx.stroke();}
|
||||
if(path.length){ctx.beginPath();path.forEach((id,i)=>{const p=centers.get(id);i?ctx.lineTo(...p):ctx.moveTo(...p);});ctx.lineWidth=5;ctx.lineCap='round';ctx.lineJoin='round';ctx.strokeStyle='#7562db';ctx.globalAlpha=.32;ctx.stroke();ctx.globalAlpha=1;}
|
||||
for(let i=0;i<(level.gates||[]).length;i++){const gate=level.gates[i],a=centers.get(gate.edge[0]),b=centers.get(gate.edge[1]),mx=(a[0]+b[0])/2,my=(a[1]+b[1])/2,dx=b[0]-a[0],dy=b[1]-a[1],len=Math.hypot(dx,dy),open=path.includes(gate.switchCell);ctx.save();ctx.translate(mx,my);ctx.rotate(Math.atan2(dy,dx));ctx.beginPath();ctx.moveTo(0,-unit*.34);ctx.lineTo(0,unit*.34);ctx.lineWidth=7;ctx.strokeStyle=open?'#349375':'#d98123';if(open)ctx.setLineDash([3,5]);ctx.stroke();ctx.restore();ctx.fillStyle=open?'#25745c':'#8c4917';ctx.font='bold 12px system-ui';ctx.textAlign='center';ctx.fillText(String(i+1),mx+9,my-8);}
|
||||
for(const c of level.cells){const [x,y]=centers.get(c.id),step=path.indexOf(c.id)+1;const font=Math.max(14,Math.min(24,unit*.34));let symbol=labels.get(c.id)||'';const w=(level.windows||[]).find(w=>w.cell===c.id);if(w)symbol=`${w.min}–${w.max}`;if(c.direction)symbol=c.direction==='straight'?'↔':'↱';if(c.category)symbol=c.category==='circle'?'○':'△';const gateIndex=(level.gates||[]).findIndex(g=>g.switchCell===c.id);if(gateIndex!==-1)symbol=`开${gateIndex+1}`;
|
||||
ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillStyle=badIds.has(c.id)?'#a33127':'#30304e';ctx.font=`700 ${font}px system-ui`;if(step){ctx.fillText(String(step),x,y+ (symbol?5:0));if(symbol){ctx.font=`600 ${Math.max(11,font*.65)}px system-ui`;ctx.fillText(symbol,x,y-font*.65);}}else if(symbol)ctx.fillText(symbol,x,y);
|
||||
if(c.clue!==undefined){if(!step&&!symbol)ctx.fillText(String(c.clue),x,y);else{ctx.font=`700 ${Math.max(11,font*.65)}px system-ui`;ctx.fillText('需'+c.clue,x,y+font*.8);}}
|
||||
}
|
||||
$('remaining').textContent=cleared?'完成 ✓':`剩余 ${level.cells.length-path.length} 格`;
|
||||
if(bad.length)setStatus(bad[0].message+',可以点已走格退回。');
|
||||
$('undo').disabled=!path.length;$('next').hidden=!cleared||index===7;
|
||||
}
|
||||
function inside(x,y,p){let hit=false;for(let i=0,j=p.length-1;i<p.length;j=i++){const [a,b]=p[i],[c,d]=p[j];if((b>y)!==(d>y)&&x<(c-a)*(y-b)/(d-b)+a)hit=!hit;}return hit;}
|
||||
function hit(x,y){return polygons.find(s=>inside(x,y,s.p))?.id;}
|
||||
function markError(id,message){if(!record.firstError)record.firstError={step:path.length+1,cell:id,message};setStatus(message);}
|
||||
function advance(id){if(!id||cleared)return;const at=path.indexOf(id);if(at!==-1){if(at<path.length-1){record.backtracks++;path=path.slice(0,at+1);focus=[];setStatus('已回退,继续观察剩余格。');draw();}return;}
|
||||
if(!canAppend(level,path,id)){const g=graph(level),adjacent=path.length&&g.adj[g.ids.get(path.at(-1))].includes(g.ids.get(id));if(adjacent||!path.length){markError(id,path.length?'此处类别相同或门尚未打开。':'请从数字 1 开始。');}return;}
|
||||
if(!path.length)started=performance.now();path.push(id);focus=[];const bad=violations(level,path);if(bad.length)markError(id,bad[0].message);else setStatus('可松手续画;点已走格可回退。');if(path.length===level.cells.length&&!bad.length){cleared=true;record.completed=true;record.elapsedSeconds=Math.round((performance.now()-started)/1000);if(!completed.includes(level.id)){completed.push(level.id);try{localStorage.setItem(memoryKey,JSON.stringify(completed));}catch{}}setStatus('完成!可以进入下一关,或记录你的推理。');renderSelectors();}draw();}
|
||||
function point(e){const r=canvas.getBoundingClientRect();return [e.clientX-r.left,e.clientY-r.top];}
|
||||
canvas.addEventListener('pointerdown',e=>{e.preventDefault();drag=true;canvas.setPointerCapture(e.pointerId);lastPoint=point(e);advance(hit(...lastPoint));});
|
||||
canvas.addEventListener('pointermove',e=>{if(!drag)return;e.preventDefault();const p=point(e),a=lastPoint||p,d=Math.hypot(p[0]-a[0],p[1]-a[1]),steps=Math.ceil(d/Math.max(3,unit*.1));for(let i=1;i<=steps;i++)advance(hit(a[0]+(p[0]-a[0])*i/steps,a[1]+(p[1]-a[1])*i/steps));lastPoint=p;});
|
||||
function end(){drag=false;lastPoint=null;}canvas.addEventListener('pointerup',end);canvas.addEventListener('pointercancel',end);
|
||||
$('undo').onclick=()=>{if(path.length){path.pop();record.backtracks++;record.completed=false;cleared=false;focus=[];setStatus('已撤销一步。');draw();}};
|
||||
$('restart').onclick=()=>{path=[];cleared=false;focus=[];record.restarts++;record.completed=false;started=performance.now();setStatus('已重开。');draw();};
|
||||
$('hint').onclick=()=>{record.hints++;const h=hint(solution,path);focus=h.kind==='none'?[]:[h.cell];setStatus(h.kind==='backtrack'?'请退回黄色边框格,再继续。':h.kind==='restart'?'起点需要调整,请重开并从黄色边框格开始。':h.kind==='none'?'已经完成。':'下一步在黄色边框格;提示不会自动画路。');draw();};
|
||||
$('next').onclick=()=>{selected=pack.levels[index+1].difficulty;start(index+1);};
|
||||
$('export').onclick=()=>{record.elapsedSeconds=record.completed?record.elapsedSeconds:Math.round((performance.now()-started)/1000);record.reasoning=$('reasoning').value;const blob=new Blob([JSON.stringify(record,null,2)],{type:'application/json'}),a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=pack.mode+'-'+level.id+'-试玩记录.json';a.click();setTimeout(()=>URL.revokeObjectURL(a.href),1000);};
|
||||
window.addEventListener('resize',draw);start(0);
|
||||
// Read-only snapshot used for interaction verification; never supplies an answer.
|
||||
window.variantSnapshot=()=>({mode:pack.mode,level:level.id,path:[...path],violations:violations(level,path),cleared,centers:Object.fromEntries(centers),unit,openedGates:(level.gates||[]).map(g=>path.includes(g.switchCell))});
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 266.66666666666663" role="img" aria-label="认识差值与中间格数量"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text></svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 266.66666666666663" role="img" aria-label="认识差值与中间格数量"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="303.3333333333333,76.66666666666666 190,76.66666666666666 76.66666666666666,76.66666666666666 76.66666666666666,190 190,190 303.3333333333333,190" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="用间隔排除直达路线"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,246.66666666666666 133.33333333333331,246.66666666666666 133.33333333333331,360 20,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,246.66666666666666 246.66666666666666,246.66666666666666 246.66666666666666,360 133.33333333333331,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="76.66666666666666" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="190" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text></svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="用间隔排除直达路线"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,246.66666666666666 133.33333333333331,246.66666666666666 133.33333333333331,360 20,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,246.66666666666666 246.66666666666666,246.66666666666666 246.66666666666666,360 133.33333333333331,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="190,76.66666666666666 76.66666666666666,76.66666666666666 76.66666666666666,190 76.66666666666666,303.3333333333333 190,303.3333333333333 190,190 303.3333333333333,190 303.3333333333333,76.66666666666666" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="76.66666666666666" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="76.66666666666666" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="190" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="190" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两端绝对步号都未知"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text></svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两端绝对步号都未知"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="62.5,62.5 62.5,147.5 147.5,147.5 147.5,62.5 232.5,62.5 317.5,62.5 317.5,147.5 232.5,147.5 232.5,232.5 147.5,232.5" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">10</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">9</text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text></svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两条独立关系共同选路"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两条独立关系共同选路"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="317.5,62.5 232.5,62.5 147.5,62.5 62.5,62.5 62.5,147.5 62.5,232.5 147.5,232.5 232.5,232.5 317.5,232.5 317.5,147.5 232.5,147.5 147.5,147.5" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">12</text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">11</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">10</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">9</text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="共享端点的关系联动"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,275 190,275 190,360 105,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,275 275,275 275,360 190,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text><text x="147.5" y="309.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="327.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c12</text><text x="232.5" y="309.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="327.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c13</text></svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,179 @@
|
||||
# 相对步差
|
||||
|
||||
每格一次。A → B +d:B 的步号减去 A 的步号等于 d;+4 是四次移动,中间有三格。
|
||||
|
||||
固定数字表示必须在该步到达;每格一次、共边移动、完整路线唯一。所有关卡开放。
|
||||
|
||||
这些是机制验证候选关,难度标签按设计任务编排,尚未经真人校准。
|
||||
|
||||
## 01 · 入门 · 认识差值与中间格数量
|
||||
|
||||
格数 6;基础完整路线 16 条;新约束 1 项。
|
||||
|
||||
作者解:c2 → c1 → c0 → c3 → c4 → c5。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c1→c4:3 次移动,作者步号 2→5
|
||||
|
||||
关键分叉:第 2 步应选 c1,解除机制后的诱惑路线选择 c5。排除依据:相对步差不符。
|
||||
|
||||
诱惑路线:c2 → c5 → c4 → c1 → c0 → c3。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 relation:0 后可走 c2 → c5 → c4 → c1 → c0 → c3
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 02 · 入门 · 用间隔排除直达路线
|
||||
|
||||
格数 8;基础完整路线 24 条;新约束 1 项。
|
||||
|
||||
作者解:c1 → c0 → c3 → c6 → c7 → c4 → c5 → c2。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c3→c4:3 次移动,作者步号 3→6
|
||||
|
||||
关键分叉:第 2 步应选 c0,解除机制后的诱惑路线选择 c2。排除依据:相对步差不符。
|
||||
|
||||
诱惑路线:c1 → c2 → c5 → c4 → c7 → c6 → c3 → c0。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 relation:0 后可走 c1 → c2 → c5 → c4 → c7 → c6 → c3 → c0
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 03 · 普通 · 两端绝对步号都未知
|
||||
|
||||
格数 10;基础完整路线 34 条;新约束 1 项。
|
||||
|
||||
作者解:c0 → c4 → c5 → c1 → c2 → c3 → c7 → c6 → c9 → c8。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c5→c6:5 次移动,作者步号 3→8
|
||||
|
||||
关键分叉:第 4 步应选 c1,解除机制后的诱惑路线选择 c8。排除依据:相对步差不符。
|
||||
|
||||
诱惑路线:c0 → c4 → c5 → c8 → c9 → c6 → c7 → c3 → c2 → c1。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 relation:0 后可走 c0 → c4 → c5 → c8 → c9 → c6 → c7 → c3 → c2 → c1
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 04 · 普通 · 两条独立关系共同选路
|
||||
|
||||
格数 12;基础完整路线 124 条;新约束 2 项。
|
||||
|
||||
作者解:c3 → c2 → c1 → c0 → c4 → c8 → c9 → c10 → c11 → c7 → c6 → c5。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c1→c9:4 次移动,作者步号 3→7
|
||||
- c10→c6:3 次移动,作者步号 8→11
|
||||
|
||||
关键分叉:第 2 步应选 c2,解除机制后的诱惑路线选择 c7。排除依据:相对步差不符。
|
||||
|
||||
诱惑路线:c3 → c7 → c11 → c10 → c9 → c5 → c6 → c2 → c1 → c0 → c4 → c8。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 relation:0 后可走 c3 → c7 → c11 → c10 → c9 → c5 → c6 → c2 → c1 → c0 → c4 → c8
|
||||
- 解除 relation:1 后可走 c3 → c2 → c1 → c0 → c4 → c8 → c9 → c5 → c6 → c7 → c11 → c10
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 05 · 进阶 · 共享端点的关系联动
|
||||
|
||||
格数 14;基础完整路线 100 条;新约束 2 项。
|
||||
|
||||
作者解:c8 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c10 → c6 → c5 → c9 → c12 → c13。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c0→c11:5 次移动,作者步号 3→8
|
||||
- c0→c12:10 次移动,作者步号 3→13
|
||||
|
||||
关键分叉:第 5 步应选 c2,解除机制后的诱惑路线选择 c5。排除依据:相对步差不符。
|
||||
|
||||
诱惑路线:c8 → c4 → c0 → c1 → c5 → c6 → c2 → c3 → c7 → c11 → c10 → c9 → c12 → c13。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 relation:0 后可走 c8 → c4 → c0 → c1 → c5 → c6 → c2 → c3 → c7 → c11 → c10 → c9 → c12 → c13
|
||||
- 解除 relation:1 后可走 c8 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c10 → c13 → c12 → c9 → c5 → c6
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 06 · 进阶 · 间隔决定先处理哪个区域
|
||||
|
||||
格数 16;基础完整路线 552 条;新约束 2 项。
|
||||
|
||||
作者解:c8 → c12 → c13 → c9 → c10 → c14 → c15 → c11 → c7 → c3 → c2 → c1 → c0 → c4 → c5 → c6。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c13→c3:7 次移动,作者步号 3→10
|
||||
- c11→c5:7 次移动,作者步号 8→15
|
||||
|
||||
关键分叉:第 4 步应选 c9,解除机制后的诱惑路线选择 c14。排除依据:相对步差不符。
|
||||
|
||||
诱惑路线:c8 → c12 → c13 → c14 → c15 → c11 → c7 → c3 → c2 → c1 → c0 → c4 → c5 → c6 → c10 → c9。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 relation:0 后可走 c8 → c12 → c13 → c14 → c15 → c11 → c7 → c3 → c2 → c1 → c0 → c4 → c5 → c6 → c10 → c9
|
||||
- 解除 relation:1 后可走 c8 → c12 → c13 → c9 → c5 → c4 → c0 → c1 → c2 → c3 → c7 → c6 → c10 → c11 → c15 → c14
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 07 · 难 · 三条关系跨区联动
|
||||
|
||||
格数 18;基础完整路线 464 条;新约束 3 项。
|
||||
|
||||
作者解:c15 → c11 → c7 → c3 → c2 → c6 → c10 → c9 → c5 → c1 → c0 → c4 → c8 → c12 → c13 → c16 → c17 → c14。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c11→c1:8 次移动,作者步号 2→10
|
||||
- c3→c17:13 次移动,作者步号 4→17
|
||||
- c1→c14:8 次移动,作者步号 10→18
|
||||
|
||||
关键分叉:第 7 步应选 c10,解除机制后的诱惑路线选择 c5。排除依据:相对步差不符。
|
||||
|
||||
诱惑路线:c15 → c11 → c7 → c3 → c2 → c6 → c5 → c1 → c0 → c4 → c8 → c12 → c13 → c9 → c10 → c14 → c17 → c16。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 relation:0 后可走 c15 → c11 → c7 → c3 → c2 → c6 → c5 → c1 → c0 → c4 → c8 → c12 → c13 → c9 → c10 → c14 → c17 → c16
|
||||
- 解除 relation:1 后可走 c15 → c11 → c10 → c9 → c5 → c6 → c7 → c3 → c2 → c1 → c0 → c4 → c8 → c12 → c13 → c16 → c17 → c14
|
||||
- 解除 relation:2 后可走 c15 → c11 → c7 → c3 → c2 → c6 → c10 → c9 → c5 → c1 → c0 → c4 → c8 → c12 → c13 → c14 → c17 → c16
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
|
||||
## 08 · 难 · 无固定起点,联合推断关系位置
|
||||
|
||||
格数 19;基础完整路线 544 条;新约束 2 项。
|
||||
|
||||
作者解:c15 → c11 → c7 → c3 → c2 → c1 → c0 → c4 → c8 → c12 → c13 → c9 → c5 → c6 → c10 → c14 → c18 → c17 → c16。
|
||||
|
||||
推理检查点:
|
||||
|
||||
- c3→c13:7 次移动,作者步号 4→11
|
||||
- c8→c17:9 次移动,作者步号 9→18
|
||||
|
||||
关键分叉:第 1 步应选 c15,解除机制后的诱惑路线选择 c8。排除依据:相对步差不符。
|
||||
|
||||
诱惑路线:c8 → c4 → c0 → c1 → c5 → c9 → c13 → c12 → c16 → c17 → c18 → c14 → c15 → c11 → c10 → c6 → c2 → c3 → c7。这是符合几何与辅助数字的完整路线,由新机制排除。
|
||||
|
||||
逐项必要性证据:
|
||||
|
||||
- 解除 relation:0 后可走 c8 → c4 → c0 → c1 → c5 → c9 → c13 → c12 → c16 → c17 → c18 → c14 → c15 → c11 → c10 → c6 → c2 → c3 → c7
|
||||
- 解除 relation:1 后可走 c15 → c14 → c18 → c17 → c16 → c12 → c8 → c4 → c0 → c1 → c2 → c3 → c7 → c11 → c10 → c6 → c5 → c9 → c13
|
||||
|
||||
人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。
|
||||
@@ -0,0 +1,902 @@
|
||||
{
|
||||
"mode": "relative",
|
||||
"title": "相对步差",
|
||||
"rule": "每格一次。A → B +d:B 的步号减去 A 的步号等于 d;+4 是四次移动,中间有三格。",
|
||||
"levels": [
|
||||
{
|
||||
"id": "01",
|
||||
"mode": "relative",
|
||||
"geometry": "square",
|
||||
"difficulty": "入门",
|
||||
"title": "认识差值与中间格数量",
|
||||
"caption": "每格一次。A → B +d:B 的步号减去 A 的步号等于 d;+4 是四次移动,中间有三格。",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"relations": [
|
||||
{
|
||||
"from": "c1",
|
||||
"to": "c4",
|
||||
"delta": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "02",
|
||||
"mode": "relative",
|
||||
"geometry": "square",
|
||||
"difficulty": "入门",
|
||||
"title": "用间隔排除直达路线",
|
||||
"caption": "每格一次。A → B +d:B 的步号减去 A 的步号等于 d;+4 是四次移动,中间有三格。",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"relations": [
|
||||
{
|
||||
"from": "c3",
|
||||
"to": "c4",
|
||||
"delta": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "03",
|
||||
"mode": "relative",
|
||||
"geometry": "square",
|
||||
"difficulty": "普通",
|
||||
"title": "两端绝对步号都未知",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"relations": [
|
||||
{
|
||||
"from": "c5",
|
||||
"to": "c6",
|
||||
"delta": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "04",
|
||||
"mode": "relative",
|
||||
"geometry": "square",
|
||||
"difficulty": "普通",
|
||||
"title": "两条独立关系共同选路",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"relations": [
|
||||
{
|
||||
"from": "c1",
|
||||
"to": "c9",
|
||||
"delta": 4
|
||||
},
|
||||
{
|
||||
"from": "c10",
|
||||
"to": "c6",
|
||||
"delta": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "05",
|
||||
"mode": "relative",
|
||||
"geometry": "square",
|
||||
"difficulty": "进阶",
|
||||
"title": "共享端点的关系联动",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"relations": [
|
||||
{
|
||||
"from": "c0",
|
||||
"to": "c11",
|
||||
"delta": 5
|
||||
},
|
||||
{
|
||||
"from": "c0",
|
||||
"to": "c12",
|
||||
"delta": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "06",
|
||||
"mode": "relative",
|
||||
"geometry": "square",
|
||||
"difficulty": "进阶",
|
||||
"title": "间隔决定先处理哪个区域",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c14",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c15",
|
||||
"x": 3,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"relations": [
|
||||
{
|
||||
"from": "c13",
|
||||
"to": "c3",
|
||||
"delta": 7
|
||||
},
|
||||
{
|
||||
"from": "c11",
|
||||
"to": "c5",
|
||||
"delta": 7
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "07",
|
||||
"mode": "relative",
|
||||
"geometry": "square",
|
||||
"difficulty": "难",
|
||||
"title": "三条关系跨区联动",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c14",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c15",
|
||||
"x": 3,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 1
|
||||
},
|
||||
{
|
||||
"id": "c16",
|
||||
"x": 1,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c17",
|
||||
"x": 2,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"relations": [
|
||||
{
|
||||
"from": "c11",
|
||||
"to": "c1",
|
||||
"delta": 8
|
||||
},
|
||||
{
|
||||
"from": "c3",
|
||||
"to": "c17",
|
||||
"delta": 13
|
||||
},
|
||||
{
|
||||
"from": "c1",
|
||||
"to": "c14",
|
||||
"delta": 8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "08",
|
||||
"mode": "relative",
|
||||
"geometry": "square",
|
||||
"difficulty": "难",
|
||||
"title": "无固定起点,联合推断关系位置",
|
||||
"caption": "",
|
||||
"cells": [
|
||||
{
|
||||
"id": "c0",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"x": 2,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c6",
|
||||
"x": 2,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c7",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c8",
|
||||
"x": 0,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c9",
|
||||
"x": 1,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c10",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"clue": 15
|
||||
},
|
||||
{
|
||||
"id": "c11",
|
||||
"x": 3,
|
||||
"y": 2,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c12",
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c13",
|
||||
"x": 1,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c14",
|
||||
"x": 2,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c15",
|
||||
"x": 3,
|
||||
"y": 3,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c16",
|
||||
"x": 0,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c17",
|
||||
"x": 1,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
},
|
||||
{
|
||||
"id": "c18",
|
||||
"x": 2,
|
||||
"y": 4,
|
||||
"w": 1,
|
||||
"h": 1
|
||||
}
|
||||
],
|
||||
"relations": [
|
||||
{
|
||||
"from": "c3",
|
||||
"to": "c13",
|
||||
"delta": 7
|
||||
},
|
||||
{
|
||||
"from": "c8",
|
||||
"to": "c17",
|
||||
"delta": 9
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Pure rules / geometry / solver. Each mode receives its own standalone copy.
|
||||
export function units(c){return c.shape?c.shape.map(([x,y])=>[c.x+x,c.y+y]):Array.from({length:(c.w||1)*(c.h||1)},(_,i)=>[c.x+i%(c.w||1),c.y+Math.floor(i/(c.w||1))]);}
|
||||
export function graph(level){
|
||||
const cells=level.cells, ids=new Map(cells.map((c,i)=>[c.id,i]));
|
||||
if(ids.size!==cells.length)throw Error('重复格 id');
|
||||
const adj=cells.map(()=>[]), occupancy=new Map();
|
||||
if(level.geometry!=='hex') for(let i=0;i<cells.length;i++)for(const [x,y]of units(cells[i])){const k=x+','+y;if(occupancy.has(k))throw Error('拼块重叠');occupancy.set(k,i);}
|
||||
for(let i=0;i<cells.length;i++)for(let j=i+1;j<cells.length;j++){
|
||||
let connected=false;
|
||||
if(level.geometry==='hex'){let dq=cells[j].q-cells[i].q,dr=cells[j].r-cells[i].r;connected=[[1,0],[-1,0],[0,1],[0,-1],[1,-1],[-1,1]].some(([a,b])=>a===dq&&b===dr);}
|
||||
else connected=units(cells[i]).some(([x,y])=>[[1,0],[-1,0],[0,1],[0,-1]].some(([a,b])=>occupancy.get((x+a)+','+(y+b))===j));
|
||||
if(connected){adj[i].push(j);adj[j].push(i);}
|
||||
}
|
||||
const clues=new Map();for(const c of cells)if(c.clue!==undefined){if(!Number.isInteger(c.clue)||c.clue<1||c.clue>cells.length||clues.has(c.clue))throw Error('数字范围或重复');clues.set(c.clue,ids.get(c.id));}
|
||||
return {cells,ids,adj,clues};
|
||||
}
|
||||
export function constraints(level){
|
||||
const result=[];
|
||||
for(let i=0;i<(level.relations||[]).length;i++)result.push({key:'relation:'+i,kind:'relation',...level.relations[i]});
|
||||
for(let i=0;i<(level.orderChains||[]).length;i++)for(let j=0;j<level.orderChains[i].length-1;j++)result.push({key:'order:'+i+':'+j,kind:'order',from:level.orderChains[i][j],to:level.orderChains[i][j+1]});
|
||||
for(let i=0;i<(level.windows||[]).length;i++)result.push({key:'window:'+i,kind:'window',...level.windows[i]});
|
||||
for(const c of level.cells)if(c.direction)result.push({key:'direction:'+c.id,kind:'direction',cell:c.id,value:c.direction});
|
||||
if(level.mode.includes('alternation'))result.push({key:'alternation',kind:'alternation'});
|
||||
for(let i=0;i<(level.gates||[]).length;i++)result.push({key:'gate:'+i,kind:'gate',...level.gates[i]});
|
||||
return result;
|
||||
}
|
||||
export function directionAt(level,path,k){
|
||||
if(k===0||k===path.length-1)return null;
|
||||
const [a,b,c]=[path[k-1],path[k],path[k+1]].map(id=>level.cells.find(x=>x.id===id));
|
||||
return b.x-a.x===c.x-b.x&&b.y-a.y===c.y-b.y?'straight':'turn';
|
||||
}
|
||||
export function violations(level,path,{omit=null,complete=path.length===level.cells.length,ignoreClues=false}={}){
|
||||
const positions=new Map(path.map((id,i)=>[id,i+1])),bad=[];
|
||||
if(!ignoreClues)for(const c of level.cells)if(c.clue!==undefined){const at=positions.get(c.id);if(at!==undefined&&at!==c.clue||path.length>=c.clue&&path[c.clue-1]!==c.id)bad.push({key:'clue:'+c.id,cells:[c.id,path[c.clue-1]].filter(Boolean),message:'固定数字步号不符'});}
|
||||
for(const r of constraints(level)){
|
||||
if(r.key===omit)continue;
|
||||
let fail=false,where=[];
|
||||
if(r.kind==='relation'){
|
||||
const a=positions.get(r.from),b=positions.get(r.to);where=[r.from,r.to];
|
||||
fail=b!==undefined&&(a===undefined||b-a!==r.delta)||a!==undefined&&b===undefined&&path.length>=a+r.delta;
|
||||
}else if(r.kind==='order'){where=[r.from,r.to];fail=positions.has(r.to)&&(!positions.has(r.from)||positions.get(r.from)>positions.get(r.to));}
|
||||
else if(r.kind==='window'){where=[r.cell];const p=positions.get(r.cell);fail=p!==undefined?(p<r.min||p>r.max):path.length>=r.max;}
|
||||
else if(r.kind==='direction'){where=[r.cell];const p=positions.get(r.cell);fail=p===1||p!==undefined&&p<path.length&&directionAt(level,path,p-1)!==r.value||complete&&p===path.length;}
|
||||
else if(r.kind==='alternation'){for(let i=1;i<path.length;i++){const a=level.cells.find(c=>c.id===path[i-1]),b=level.cells.find(c=>c.id===path[i]);if(a.category===b.category){fail=true;where.push(a.id,b.id);}}}
|
||||
else if(r.kind==='gate')for(let i=1;i<path.length;i++){if(r.edge.includes(path[i-1])&&r.edge.includes(path[i])){const s=positions.get(r.switchCell);if(s===undefined||s>i){fail=true;where=r.edge;}}}
|
||||
if(fail)bad.push({key:r.key,cells:where,message:r.kind==='relation'?'相对步差不符':r.kind==='order'?'标记次序不符':r.kind==='window'?'到达窗口不符':r.kind==='direction'?'进出方向不符':r.kind==='gate'?'开关尚未打开此门':'类别没有交替'});
|
||||
}
|
||||
return bad;
|
||||
}
|
||||
export function canAppend(level,path,id){
|
||||
const g=graph(level);if(path.includes(id)||!g.ids.has(id))return false;
|
||||
if(!path.length)return !g.clues.has(1)||g.cells[g.clues.get(1)].id===id;
|
||||
const last=path.at(-1);if(!g.adj[g.ids.get(last)].includes(g.ids.get(id)))return false;
|
||||
if(level.mode.includes('alternation')&&g.cells[g.ids.get(last)].category===g.cells[g.ids.get(id)].category)return false;
|
||||
for(const gate of level.gates||[])if(gate.edge.includes(last)&&gate.edge.includes(id)&&!path.includes(gate.switchCell))return false;
|
||||
return true;
|
||||
}
|
||||
export function solve(level,{limit=2,omit=null,ignoreClues=false,prune=true,nodeLimit=Infinity}={}){
|
||||
const g=graph(level),n=g.cells.length,results=[],path=[],seen=new Set();let nodes=0;
|
||||
const active=constraints(level).filter(r=>r.key!==omit),alternating=active.some(r=>r.kind==='alternation');
|
||||
const adj=g.adj.map((a,i)=>a.filter(j=>!alternating||g.cells[i].category!==g.cells[j].category));
|
||||
const forced=ignoreClues?new Map():g.clues;
|
||||
function connected(cur){const reached=new Set([cur]),queue=[cur];for(let k=0;k<queue.length;k++)for(const j of adj[queue[k]])if(!seen.has(j)&&!reached.has(j)){reached.add(j);queue.push(j);}return reached.size===n-path.length+1;}
|
||||
function visit(i){if(++nodes>nodeLimit)throw Error('求解节点预算耗尽,不能视为唯一');seen.add(i);path.push(g.cells[i].id);
|
||||
const k=path.length;
|
||||
if(!violations(level,path,{omit,ignoreClues}).length){
|
||||
if(k===n)results.push([...path]);
|
||||
else if(!prune||connected(i)){
|
||||
const f=forced.get(k+1);let next=adj[i].filter(j=>!seen.has(j)&&(f===undefined||j===f));
|
||||
next.sort((a,b)=>adj[a].filter(x=>!seen.has(x)).length-adj[b].filter(x=>!seen.has(x)).length);
|
||||
for(const j of next){if(!ignoreClues&&g.cells[j].clue!==undefined&&g.cells[j].clue!==k+1)continue;
|
||||
let blocked=false;for(const r of active)if(r.kind==='gate'&&r.edge.includes(g.cells[i].id)&&r.edge.includes(g.cells[j].id)&&!path.includes(r.switchCell))blocked=true;
|
||||
if(!blocked)visit(j);if(results.length>=limit)break;
|
||||
}
|
||||
}
|
||||
}
|
||||
path.pop();seen.delete(i);
|
||||
}
|
||||
let starts=forced.has(1)?[forced.get(1)]:g.cells.map((_,i)=>i);
|
||||
for(const i of starts){if(!ignoreClues&&g.cells[i].clue!==undefined&&g.cells[i].clue!==1)continue;visit(i);if(results.length>=limit)break;}
|
||||
return results;
|
||||
}
|
||||
export function hint(solution,path){let k=0;while(k<path.length&&path[k]===solution[k])k++;if(k<path.length)return {kind:k?'backtrack':'restart',cell:k?path[k-1]:solution[0]};return k===solution.length?{kind:'none'}:{kind:k?'next':'start',cell:solution[k]};}
|
||||
export function assertStructure(level){
|
||||
const g=graph(level),n=g.cells.length;if(n<3||n>20)throw Error('格数须为3–20');
|
||||
if(level.geometry==='hex'){const keys=new Set();for(const c of g.cells){if(!Number.isInteger(c.q)||!Number.isInteger(c.r))throw Error('六边坐标');const key=c.q+','+c.r;if(keys.has(key))throw Error('六边重叠');keys.add(key);}}
|
||||
else for(const c of g.cells){if(!Number.isInteger(c.x)||!Number.isInteger(c.y))throw Error('坐标');const u=units(c);if(!u.length||new Set(u.map(p=>p.join(','))).size!==u.length)throw Error('异形重复');const seen=new Set([u[0].join(',')]);for(let changed=true;changed;){changed=false;for(const [x,y]of u)if(!seen.has(x+','+y)&&[[1,0],[-1,0],[0,1],[0,-1]].some(([a,b])=>seen.has((x+a)+','+(y+b)))){seen.add(x+','+y);changed=true;}}if(seen.size!==u.length)throw Error('异形不连通');}
|
||||
const reach=new Set([0]),queue=[0];for(let k=0;k<queue.length;k++)for(const j of g.adj[queue[k]])if(!reach.has(j)){reach.add(j);queue.push(j);}if(reach.size!==n)throw Error('盘面不连通');
|
||||
const pairs=[];
|
||||
for(const r of constraints(level)){
|
||||
for(const id of [r.cell,r.from,r.to,r.switchCell,...(r.edge||[])].filter(Boolean))if(!g.ids.has(id))throw Error('约束引用不存在');
|
||||
if(r.kind==='relation'&&(!Number.isInteger(r.delta)||r.delta<1||r.delta>=n||r.from===r.to))throw Error('步差');
|
||||
if(r.kind==='order')pairs.push([r.from,r.to]);
|
||||
if(r.kind==='window'&&(!Number.isInteger(r.min)||!Number.isInteger(r.max)||r.min<1||r.max>n||r.max-r.min<2||g.cells[g.ids.get(r.cell)].clue!==undefined))throw Error('窗口需允许至少两个同奇偶步号且不与数字重叠');
|
||||
if(r.kind==='direction'&&!['straight','turn'].includes(r.value))throw Error('方向');
|
||||
if(r.kind==='gate'&&(r.edge.length!==2||!g.adj[g.ids.get(r.edge[0])].includes(g.ids.get(r.edge[1]))))throw Error('门不在共边');
|
||||
}
|
||||
if((level.gates||[]).length>3||new Set((level.gates||[]).map(r=>r.switchCell)).size!==(level.gates||[]).length)throw Error('最多三门且开关一对一');
|
||||
if(level.mode.includes('alternation')&&g.cells.some(c=>!['circle','triangle'].includes(c.category)))throw Error('缺少类别');
|
||||
function cycle(v,stack=new Set(),done=new Set()){if(stack.has(v))throw Error('先后链循环');if(done.has(v))return;stack.add(v);for(const [a,b]of pairs)if(a===v)cycle(b,stack,done);stack.delete(v);done.add(v);}for(const [a]of pairs)cycle(a);
|
||||
return g;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {graph,units,solve,violations,canAppend,hint} from './rules.mjs';
|
||||
// The build embeds this module together with the local rule file and JSON.
|
||||
const pack=PACK;
|
||||
const $=id=>document.getElementById(id),canvas=$('board'),ctx=canvas.getContext('2d');
|
||||
let level,solution,path=[],index=0,selected='入门',polygons=[],centers=new Map(),unit=1,focus=[],drag=false,lastPoint=null,cleared=false;
|
||||
const memoryKey='variant:'+pack.mode;let completed=[];try{completed=JSON.parse(localStorage.getItem(memoryKey)||'[]');}catch{}
|
||||
const labels=new Map();const metrics={};let started=0,record=null;
|
||||
function setStatus(s){$('status').textContent=s;}
|
||||
function modeMarkers(){labels.clear();let ids=[...(level.relations||[]).flatMap(r=>[r.from,r.to]),...(level.orderChains||[]).flat()];for(const id of ids)if(!labels.has(id))labels.set(id,String.fromCharCode(65+labels.size));}
|
||||
function makeRecord(){return {mode:pack.mode,level:level.id,difficulty:level.difficulty,title:level.title,startedAt:new Date().toISOString(),elapsedSeconds:0,firstError:null,backtracks:0,restarts:0,hints:0,completed:false,reasoning:''};}
|
||||
function start(i){index=i;level=pack.levels[i];solution=solve(level)[0];if(!solution)throw Error('没有合法路线');path=[];focus=[];cleared=false;started=performance.now();record=makeRecord();$('reasoning').value='';modeMarkers();$('goal').textContent=level.title;$('caption').textContent=level.caption||'先观察,再画;错了可以退回。';$('title').textContent=pack.title;$('rule-text').textContent=pack.rule;setStatus('从数字 1 开始;没有 1 时,可尝试任意格。');renderConstraints();renderSelectors();draw();}
|
||||
function renderSelectors(){
|
||||
$('difficulties').replaceChildren();for(const d of ['入门','普通','进阶','难']){const b=document.createElement('button');b.textContent=d;b.className=d===selected?'active':'';b.onclick=()=>{selected=d;start(pack.levels.findIndex(l=>l.difficulty===d));};$('difficulties').append(b);}
|
||||
$('levels').replaceChildren();for(let i=0;i<pack.levels.length;i++){const l=pack.levels[i];if(l.difficulty!==selected)continue;const b=document.createElement('button');b.textContent=`第 ${i%2+1} 关${completed.includes(l.id)?' ✓':''}`;b.className=i===index?'active':'';b.onclick=()=>start(i);$('levels').append(b);}
|
||||
$('position').textContent=`${level.difficulty} · ${index%2+1}/2`;
|
||||
}
|
||||
function renderConstraints(){const list=$('constraints');list.replaceChildren();
|
||||
const add=(text,ids)=>{const b=document.createElement('button');b.className='constraint';b.textContent=text;b.onclick=()=>{focus=ids;draw();};list.append(b);};
|
||||
for(const r of level.relations||[])add(`${labels.get(r.from)} → ${labels.get(r.to)} +${r.delta}`,[r.from,r.to]);
|
||||
for(const chain of level.orderChains||[])add(chain.map(id=>labels.get(id)).join(' < '),chain);
|
||||
for(const w of level.windows||[])add(`窗口 ${w.min}–${w.max}`,[w.cell]);
|
||||
for(const c of level.cells)if(c.direction)add(c.direction==='straight'?'↔ 必须直行':'↱ 必须转弯',[c.id]);
|
||||
if(level.mode.includes('alternation'))add('○ → △ → ○ · 每步换一类',[]);
|
||||
for(let i=0;i<(level.gates||[]).length;i++)add(`开关 ${i+1} → 门 ${i+1}`,[level.gates[i].switchCell,...level.gates[i].edge]);
|
||||
}
|
||||
function localGeometry(){
|
||||
const raw=[],center=new Map();for(const c of level.cells){if(level.geometry==='hex'){
|
||||
// Rotate the axial strip into portrait orientation; adjacency remains axial.
|
||||
const x=1.5*c.r,y=Math.sqrt(3)*(c.q+c.r/2);center.set(c.id,[x,y]);let p=[];for(let a=0;a<6;a++){const angle=a*Math.PI/3;p.push([x+Math.cos(angle),y+Math.sin(angle)]);}raw.push({id:c.id,p});
|
||||
}else{const u=units(c);center.set(c.id,[u.reduce((s,p)=>s+p[0]+.5,0)/u.length,u.reduce((s,p)=>s+p[1]+.5,0)/u.length]);for(const [x,y]of u)raw.push({id:c.id,p:[[x,y],[x+1,y],[x+1,y+1],[x,y+1]]});}}
|
||||
return {raw,center};
|
||||
}
|
||||
function draw(){if(!level)return;const width=canvas.clientWidth,height=canvas.clientHeight,dpr=window.devicePixelRatio||1;canvas.width=Math.round(width*dpr);canvas.height=Math.round(height*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,width,height);
|
||||
const {raw,center}=localGeometry(),all=raw.flatMap(r=>r.p),xmin=Math.min(...all.map(p=>p[0])),xmax=Math.max(...all.map(p=>p[0])),ymin=Math.min(...all.map(p=>p[1])),ymax=Math.max(...all.map(p=>p[1]));unit=Math.min((width-28)/(xmax-xmin),(height-24)/(ymax-ymin));const ox=(width-unit*(xmax-xmin))/2-unit*xmin,oy=(height-unit*(ymax-ymin))/2-unit*ymin;
|
||||
const tr=([x,y])=>[ox+x*unit,oy+y*unit];polygons=raw.map(r=>({...r,p:r.p.map(tr)}));centers=new Map([...center].map(([id,p])=>[id,tr(p)]));
|
||||
const bad=violations(level,path),badIds=new Set(bad.flatMap(b=>b.cells));
|
||||
for(const shape of polygons){const c=level.cells.find(c=>c.id===shape.id),visited=path.includes(c.id);ctx.beginPath();shape.p.forEach(([x,y],i)=>i?ctx.lineTo(x,y):ctx.moveTo(x,y));ctx.closePath();ctx.fillStyle=badIds.has(c.id)?'#ffe0dc':visited?'#ddd8ff':c.category==='circle'?'#e4f3ff':c.category==='triangle'?'#fff0d4':'#f0f0f6';ctx.fill();}
|
||||
// Draw only external unit edges, preserving the outline of an entire large block.
|
||||
const edgeCounts=new Map();for(const shape of polygons)for(let i=0;i<shape.p.length;i++){let a=shape.p[i],b=shape.p[(i+1)%shape.p.length];let key=shape.id+':'+[a.map(v=>v.toFixed(3)).join(','),b.map(v=>v.toFixed(3)).join(',')].sort().join('|');edgeCounts.set(key,(edgeCounts.get(key)||0)+1);}
|
||||
for(const shape of polygons)for(let i=0;i<shape.p.length;i++){const a=shape.p[i],b=shape.p[(i+1)%shape.p.length],key=shape.id+':'+[a.map(v=>v.toFixed(3)).join(','),b.map(v=>v.toFixed(3)).join(',')].sort().join('|');if(edgeCounts.get(key)>1)continue;ctx.beginPath();ctx.moveTo(...a);ctx.lineTo(...b);ctx.strokeStyle=focus.includes(shape.id)?'#f59c00':badIds.has(shape.id)?'#c74537':path.includes(shape.id)?'#7762db':'#b4b6cb';ctx.lineWidth=focus.includes(shape.id)?4:2;ctx.stroke();}
|
||||
if(path.length){ctx.beginPath();path.forEach((id,i)=>{const p=centers.get(id);i?ctx.lineTo(...p):ctx.moveTo(...p);});ctx.lineWidth=5;ctx.lineCap='round';ctx.lineJoin='round';ctx.strokeStyle='#7562db';ctx.globalAlpha=.32;ctx.stroke();ctx.globalAlpha=1;}
|
||||
for(let i=0;i<(level.gates||[]).length;i++){const gate=level.gates[i],a=centers.get(gate.edge[0]),b=centers.get(gate.edge[1]),mx=(a[0]+b[0])/2,my=(a[1]+b[1])/2,dx=b[0]-a[0],dy=b[1]-a[1],len=Math.hypot(dx,dy),open=path.includes(gate.switchCell);ctx.save();ctx.translate(mx,my);ctx.rotate(Math.atan2(dy,dx));ctx.beginPath();ctx.moveTo(0,-unit*.34);ctx.lineTo(0,unit*.34);ctx.lineWidth=7;ctx.strokeStyle=open?'#349375':'#d98123';if(open)ctx.setLineDash([3,5]);ctx.stroke();ctx.restore();ctx.fillStyle=open?'#25745c':'#8c4917';ctx.font='bold 12px system-ui';ctx.textAlign='center';ctx.fillText(String(i+1),mx+9,my-8);}
|
||||
for(const c of level.cells){const [x,y]=centers.get(c.id),step=path.indexOf(c.id)+1;const font=Math.max(14,Math.min(24,unit*.34));let symbol=labels.get(c.id)||'';const w=(level.windows||[]).find(w=>w.cell===c.id);if(w)symbol=`${w.min}–${w.max}`;if(c.direction)symbol=c.direction==='straight'?'↔':'↱';if(c.category)symbol=c.category==='circle'?'○':'△';const gateIndex=(level.gates||[]).findIndex(g=>g.switchCell===c.id);if(gateIndex!==-1)symbol=`开${gateIndex+1}`;
|
||||
ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillStyle=badIds.has(c.id)?'#a33127':'#30304e';ctx.font=`700 ${font}px system-ui`;if(step){ctx.fillText(String(step),x,y+ (symbol?5:0));if(symbol){ctx.font=`600 ${Math.max(11,font*.65)}px system-ui`;ctx.fillText(symbol,x,y-font*.65);}}else if(symbol)ctx.fillText(symbol,x,y);
|
||||
if(c.clue!==undefined){if(!step&&!symbol)ctx.fillText(String(c.clue),x,y);else{ctx.font=`700 ${Math.max(11,font*.65)}px system-ui`;ctx.fillText('需'+c.clue,x,y+font*.8);}}
|
||||
}
|
||||
$('remaining').textContent=cleared?'完成 ✓':`剩余 ${level.cells.length-path.length} 格`;
|
||||
if(bad.length)setStatus(bad[0].message+',可以点已走格退回。');
|
||||
$('undo').disabled=!path.length;$('next').hidden=!cleared||index===7;
|
||||
}
|
||||
function inside(x,y,p){let hit=false;for(let i=0,j=p.length-1;i<p.length;j=i++){const [a,b]=p[i],[c,d]=p[j];if((b>y)!==(d>y)&&x<(c-a)*(y-b)/(d-b)+a)hit=!hit;}return hit;}
|
||||
function hit(x,y){return polygons.find(s=>inside(x,y,s.p))?.id;}
|
||||
function markError(id,message){if(!record.firstError)record.firstError={step:path.length+1,cell:id,message};setStatus(message);}
|
||||
function advance(id){if(!id||cleared)return;const at=path.indexOf(id);if(at!==-1){if(at<path.length-1){record.backtracks++;path=path.slice(0,at+1);focus=[];setStatus('已回退,继续观察剩余格。');draw();}return;}
|
||||
if(!canAppend(level,path,id)){const g=graph(level),adjacent=path.length&&g.adj[g.ids.get(path.at(-1))].includes(g.ids.get(id));if(adjacent||!path.length){markError(id,path.length?'此处类别相同或门尚未打开。':'请从数字 1 开始。');}return;}
|
||||
if(!path.length)started=performance.now();path.push(id);focus=[];const bad=violations(level,path);if(bad.length)markError(id,bad[0].message);else setStatus('可松手续画;点已走格可回退。');if(path.length===level.cells.length&&!bad.length){cleared=true;record.completed=true;record.elapsedSeconds=Math.round((performance.now()-started)/1000);if(!completed.includes(level.id)){completed.push(level.id);try{localStorage.setItem(memoryKey,JSON.stringify(completed));}catch{}}setStatus('完成!可以进入下一关,或记录你的推理。');renderSelectors();}draw();}
|
||||
function point(e){const r=canvas.getBoundingClientRect();return [e.clientX-r.left,e.clientY-r.top];}
|
||||
canvas.addEventListener('pointerdown',e=>{e.preventDefault();drag=true;canvas.setPointerCapture(e.pointerId);lastPoint=point(e);advance(hit(...lastPoint));});
|
||||
canvas.addEventListener('pointermove',e=>{if(!drag)return;e.preventDefault();const p=point(e),a=lastPoint||p,d=Math.hypot(p[0]-a[0],p[1]-a[1]),steps=Math.ceil(d/Math.max(3,unit*.1));for(let i=1;i<=steps;i++)advance(hit(a[0]+(p[0]-a[0])*i/steps,a[1]+(p[1]-a[1])*i/steps));lastPoint=p;});
|
||||
function end(){drag=false;lastPoint=null;}canvas.addEventListener('pointerup',end);canvas.addEventListener('pointercancel',end);
|
||||
$('undo').onclick=()=>{if(path.length){path.pop();record.backtracks++;record.completed=false;cleared=false;focus=[];setStatus('已撤销一步。');draw();}};
|
||||
$('restart').onclick=()=>{path=[];cleared=false;focus=[];record.restarts++;record.completed=false;started=performance.now();setStatus('已重开。');draw();};
|
||||
$('hint').onclick=()=>{record.hints++;const h=hint(solution,path);focus=h.kind==='none'?[]:[h.cell];setStatus(h.kind==='backtrack'?'请退回黄色边框格,再继续。':h.kind==='restart'?'起点需要调整,请重开并从黄色边框格开始。':h.kind==='none'?'已经完成。':'下一步在黄色边框格;提示不会自动画路。');draw();};
|
||||
$('next').onclick=()=>{selected=pack.levels[index+1].difficulty;start(index+1);};
|
||||
$('export').onclick=()=>{record.elapsedSeconds=record.completed?record.elapsedSeconds:Math.round((performance.now()-started)/1000);record.reasoning=$('reasoning').value;const blob=new Blob([JSON.stringify(record,null,2)],{type:'application/json'}),a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=pack.mode+'-'+level.id+'-试玩记录.json';a.click();setTimeout(()=>URL.revokeObjectURL(a.href),1000);};
|
||||
window.addEventListener('resize',draw);start(0);
|
||||
// Read-only snapshot used for interaction verification; never supplies an answer.
|
||||
window.variantSnapshot=()=>({mode:pack.mode,level:level.id,path:[...path],violations:violations(level,path),cleared,centers:Object.fromEntries(centers),unit,openedGates:(level.gates||[]).map(g=>path.includes(g.switchCell))});
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# 原项目隔离校验
|
||||
|
||||
核对 91 个原项目文件的SHA-256与文件清单。
|
||||
|
||||
修改 0,删除 0,新增 0。
|
||||
|
||||
通过:原项目受检查文件全部保持一致。
|
||||
|
||||
.git与node_modules未纳入内容遍历;本任务未执行原项目Git写入或依赖操作。所有任务输出都在NumberSwipe-Variants。
|
||||
@@ -0,0 +1,12 @@
|
||||
# 最终检查摘要
|
||||
|
||||
本次完成于2026-10-13。
|
||||
|
||||
- 7个独立原型,56关;每个玩法入门、普通、进阶、难各两关。
|
||||
- 31项测试通过:22项纯规则测试、9项离线界面脚本测试。
|
||||
- 56/56关自动验收通过:有序完整解唯一,所有新机制逐项必要。
|
||||
- 7套单文件离线试玩页;7套设计评审页;56张原始盘面、56张作者解SVG。
|
||||
- 原NumberSwipe项目91个受检查文件,修改0、删除0、新增0。
|
||||
- 浏览器file协议访问被工具策略阻止;未完成实际浏览器视觉及真机触感检查。人工难度校准待试玩。
|
||||
|
||||
详见VALIDATION.md、validation.json、TESTING.md、ISOLATION.md及各玩法DESIGN.html。
|
||||
@@ -0,0 +1,29 @@
|
||||
# 验证范围与限制
|
||||
|
||||
## 已完成
|
||||
|
||||
- 56/56关合法、有序完整解唯一;每项新机制单独解除后至少两解。
|
||||
- 非教学辅助数字逐个删除检查;每个玩法最后一关无起点1。
|
||||
- 22项纯规则测试,包括7个玩法各两关的独立完整枚举对照。
|
||||
- 9项离线界面脚本测试:实际运行play.html的内嵌脚本,在独立DOM/Canvas替身中逐关完成全部56关。
|
||||
- 界面测试覆盖难度与选关、点按、回退、撤销、重开、提示、通关后恢复、松手续画、快速拖动插值、关闭门与开关状态。
|
||||
- 340px画布、420px高度布局坐标检查;节点中心均在盘面内。
|
||||
- 单文件内无远程脚本、模块加载或fetch;作者解字段不进入试玩包。
|
||||
|
||||
## 未完成
|
||||
|
||||
浏览器工具拒绝file://协议,无法执行实际浏览器显示与交互验收。没有通过其他协议或工具绕过这项限制。DOM/Canvas替身验证的是脚本逻辑和几何坐标,不能验证浏览器字体、像素、下载行为、真实触屏命中或系统差异。
|
||||
|
||||
未完成3–5位新玩家试玩;四档难度是设计标签,思考时间、推理质量、触感和难度曲线仍需人工验证。
|
||||
|
||||
## 推荐人工检查
|
||||
|
||||
双击index.html打开入口;每个玩法入门两关确认规则易懂;进阶/难关检查标记是否挤压;手机检查大格凹口、六边边界及开门反馈。使用“记录本关体验”导出反馈,观察者补充口述推理。
|
||||
|
||||
## 可复现命令
|
||||
|
||||
node --test tests/*.test.mjs
|
||||
|
||||
node tools/validate.mjs
|
||||
|
||||
node tools/isolation.mjs
|
||||
@@ -0,0 +1,64 @@
|
||||
# 关卡自动校验报告
|
||||
|
||||
通过 56/56 关,7 个玩法,每档两关。每关完整有序解唯一,逐项解除新机制后均至少出现两条完整解。辅助数字除教学起点外逐项必要。
|
||||
|
||||
| 玩法 | 关 | 难度 | 格数 | 必要机制数 | 起点 |
|
||||
|---|---|---|---:|---:|---|
|
||||
| 相对步差 | 01 | 入门 | 6 | 1 | 给定 |
|
||||
| 相对步差 | 02 | 入门 | 8 | 1 | 给定 |
|
||||
| 相对步差 | 03 | 普通 | 10 | 1 | 给定 |
|
||||
| 相对步差 | 04 | 普通 | 12 | 2 | 给定 |
|
||||
| 相对步差 | 05 | 进阶 | 14 | 2 | 给定 |
|
||||
| 相对步差 | 06 | 进阶 | 16 | 2 | 给定 |
|
||||
| 相对步差 | 07 | 难 | 18 | 3 | 给定 |
|
||||
| 相对步差 | 08 | 难 | 19 | 2 | 未知 |
|
||||
| 先后链 | 01 | 入门 | 6 | 1 | 给定 |
|
||||
| 先后链 | 02 | 入门 | 8 | 1 | 给定 |
|
||||
| 先后链 | 03 | 普通 | 10 | 2 | 给定 |
|
||||
| 先后链 | 04 | 普通 | 12 | 2 | 给定 |
|
||||
| 先后链 | 05 | 进阶 | 14 | 2 | 给定 |
|
||||
| 先后链 | 06 | 进阶 | 16 | 2 | 给定 |
|
||||
| 先后链 | 07 | 难 | 18 | 4 | 给定 |
|
||||
| 先后链 | 08 | 难 | 19 | 4 | 未知 |
|
||||
| 到达窗口 | 01 | 入门 | 6 | 1 | 给定 |
|
||||
| 到达窗口 | 02 | 入门 | 8 | 1 | 给定 |
|
||||
| 到达窗口 | 03 | 普通 | 10 | 1 | 给定 |
|
||||
| 到达窗口 | 04 | 普通 | 12 | 2 | 给定 |
|
||||
| 到达窗口 | 05 | 进阶 | 14 | 2 | 给定 |
|
||||
| 到达窗口 | 06 | 进阶 | 16 | 2 | 给定 |
|
||||
| 到达窗口 | 07 | 难 | 18 | 3 | 给定 |
|
||||
| 到达窗口 | 08 | 难 | 19 | 3 | 未知 |
|
||||
| 直行/转弯 | 01 | 入门 | 6 | 1 | 给定 |
|
||||
| 直行/转弯 | 02 | 入门 | 8 | 1 | 给定 |
|
||||
| 直行/转弯 | 03 | 普通 | 10 | 2 | 给定 |
|
||||
| 直行/转弯 | 04 | 普通 | 12 | 2 | 给定 |
|
||||
| 直行/转弯 | 05 | 进阶 | 14 | 2 | 给定 |
|
||||
| 直行/转弯 | 06 | 进阶 | 16 | 2 | 给定 |
|
||||
| 直行/转弯 | 07 | 难 | 18 | 3 | 给定 |
|
||||
| 直行/转弯 | 08 | 难 | 19 | 3 | 未知 |
|
||||
| 大格交替 | 01 | 入门 | 7 | 1 | 给定 |
|
||||
| 大格交替 | 02 | 入门 | 9 | 1 | 给定 |
|
||||
| 大格交替 | 03 | 普通 | 11 | 1 | 给定 |
|
||||
| 大格交替 | 04 | 普通 | 13 | 1 | 给定 |
|
||||
| 大格交替 | 05 | 进阶 | 13 | 1 | 给定 |
|
||||
| 大格交替 | 06 | 进阶 | 15 | 1 | 给定 |
|
||||
| 大格交替 | 07 | 难 | 16 | 1 | 给定 |
|
||||
| 大格交替 | 08 | 难 | 17 | 1 | 未知 |
|
||||
| 六边交替 | 01 | 入门 | 6 | 1 | 给定 |
|
||||
| 六边交替 | 02 | 入门 | 8 | 1 | 给定 |
|
||||
| 六边交替 | 03 | 普通 | 11 | 1 | 给定 |
|
||||
| 六边交替 | 04 | 普通 | 12 | 1 | 给定 |
|
||||
| 六边交替 | 05 | 进阶 | 14 | 1 | 给定 |
|
||||
| 六边交替 | 06 | 进阶 | 16 | 1 | 给定 |
|
||||
| 六边交替 | 07 | 难 | 18 | 1 | 给定 |
|
||||
| 六边交替 | 08 | 难 | 19 | 1 | 未知 |
|
||||
| 路径开门 | 01 | 入门 | 6 | 1 | 给定 |
|
||||
| 路径开门 | 02 | 入门 | 8 | 1 | 给定 |
|
||||
| 路径开门 | 03 | 普通 | 10 | 1 | 给定 |
|
||||
| 路径开门 | 04 | 普通 | 12 | 2 | 给定 |
|
||||
| 路径开门 | 05 | 进阶 | 14 | 2 | 给定 |
|
||||
| 路径开门 | 06 | 进阶 | 16 | 2 | 给定 |
|
||||
| 路径开门 | 07 | 难 | 18 | 3 | 给定 |
|
||||
| 路径开门 | 08 | 难 | 19 | 3 | 未知 |
|
||||
|
||||
详细作者解与机制解除反例见 validation.json。自动通过不代表真人难度或推理品质已校准。
|
||||
@@ -0,0 +1,37 @@
|
||||
[
|
||||
{
|
||||
"mode": "relative",
|
||||
"title": "相对步差",
|
||||
"count": 8
|
||||
},
|
||||
{
|
||||
"mode": "order",
|
||||
"title": "先后链",
|
||||
"count": 8
|
||||
},
|
||||
{
|
||||
"mode": "window",
|
||||
"title": "到达窗口",
|
||||
"count": 8
|
||||
},
|
||||
{
|
||||
"mode": "direction",
|
||||
"title": "直行/转弯",
|
||||
"count": 8
|
||||
},
|
||||
{
|
||||
"mode": "block-alternation",
|
||||
"title": "大格交替",
|
||||
"count": 8
|
||||
},
|
||||
{
|
||||
"mode": "hex-alternation",
|
||||
"title": "六边交替",
|
||||
"count": 8
|
||||
},
|
||||
{
|
||||
"mode": "gates",
|
||||
"title": "路径开门",
|
||||
"count": 8
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"checkedAt": "2026-10-13T02:43:10.033Z",
|
||||
"source": "G:\\项目\\NumberSwipe",
|
||||
"checkedFiles": 91,
|
||||
"changed": [],
|
||||
"missing": [],
|
||||
"added": [],
|
||||
"unchanged": true,
|
||||
"excluded": [
|
||||
".git",
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
[
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\.gitignore",
|
||||
"hash": "55548CBD9EE5309615D9011CF230986AFE028C6DBC93AE70172B18E2699711BF"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\README.md",
|
||||
"hash": "B9445AD67AE6CEAAE7BEA8B59DB1E9FD6DB11D2166A775CCFE7D1898897ABCAA"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\docs\\关卡设计规范.md",
|
||||
"hash": "E04C2BB9688987E667CCA35F5B9EF2BC69529D4F7D0EDC767E7ED61EEF6F6EBF"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\docs\\design.html",
|
||||
"hash": "E2C5EC8A6396A6956A44FE3AE9D943BE68730851DB491EA956F209D52F105BD2"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\docs\\design.md",
|
||||
"hash": "B50E38B60DA3AAE5CEAE0286E01FE85795CD5087B6216E11360D8DF9F49B2A8A"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\docs\\PRODUCT.md",
|
||||
"hash": "0569851C3BFCDC93846524047C6D1692714D93D5D0D63444219ABFD044EBF044"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\.gitignore",
|
||||
"hash": "D420BD58545C4F89CE045197D412BB0321E0B931BBBC5BBD437B6AD79B7D4969"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\package-lock.json",
|
||||
"hash": "33C8A7A0407DC36AE762507AF47819216E7DC74EDFD6C52413CF9C2CD6346B80"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\package.json",
|
||||
"hash": "D3125C545563AE26D8196F684D73D6502CFCDF0377CBC18180EFE27A81D9FFAE"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\README.md",
|
||||
"hash": "0B5D29597C855736FCEFC7CECB1A5FE205E9CFA4C8A3654C0DE1A6FBFF752694"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tsconfig.json",
|
||||
"hash": "FC799CE19916A443B1C96451DEB8972CB204B5EAA6A1B0610249EAA496B9AC91"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tsconfig.src.json",
|
||||
"hash": "1B16113A622C95D80C3CFCE3ABEDC6F8319271867E9C63EB75E90675F9537246"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\docs\\GDD.md",
|
||||
"hash": "4E16F1202840047FCCCAE4F4B16D31440A67FD7EF1AD561494D50E37290D913B"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\docs\\HANDOFF.md",
|
||||
"hash": "2BAB4F8011A9FDDD60E842794DDADE68F44B6B78E3E1BBA745F8CD74423A0812"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\levels\\pack-1-tutorial.json",
|
||||
"hash": "6664FC07F5931456BF353C86951E35F3076D8A6113C18F149D5717A6CD671B2E"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\levels\\pack-2-advanced.json",
|
||||
"hash": "3DD5C499DB17DD6F54E84086D0853F7609B2FABBD08688C87C0299DC0049AB22"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\levels\\pack-3-parity.json",
|
||||
"hash": "78E8F822B4A9798F570269AB6801006D6453779A7BC803EDD51F6D43548C550B"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\scripts\\build.mjs",
|
||||
"hash": "3C012B79676662237A823A5FC7DFC9C4794A6918C0382C51E5091A22D71A7425"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\scripts\\standalone.mjs",
|
||||
"hash": "DA214CE1E6879EE3490C897AB8EDECBB542FBFCAEC4A085FD94749031951D07B"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\board.ts",
|
||||
"hash": "9BFD683CB4E9B87A22A9737A34F870ACFBB631EBDA1A1FDC43F2787468240E48"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\clue-hint.ts",
|
||||
"hash": "DB0FCF8DB9FA07356F6167B8288F7F95F97CCC322EED6ADD43D62FD388FAA69E"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\clue.ts",
|
||||
"hash": "8AAA9357B899AD601463F0917C4438BF6770336E5EBA3D0253768088A030A783"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\game.ts",
|
||||
"hash": "C58A85AA5B47AC5D169DFED4B9F9B1BA032CAFD3A68E886C1D97E14BAE977EA4"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\input.ts",
|
||||
"hash": "86D5597908786DFD296489CA06A8B2ECEEC5D4882C5CBE4BB4F50135C317FAF7"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\layout.ts",
|
||||
"hash": "70C0ABEC51F70ADBC5EB2CE976684FA0A6B9809E22C409D8939B498A29166698"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\levels.ts",
|
||||
"hash": "B6416DC7B4FFCB9154D98222B091C0AD51287F94C244CD3FC2814FF0987DB734"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\painter.ts",
|
||||
"hash": "7994476FDF687FADC1CA6002188FAABD0C6CDB9F3D59A0F29CE0B6AEBF133461"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\progress.ts",
|
||||
"hash": "C9780B5EF751D5FAAE00AFF81B6883DB0CD808EDF4326E4A6B321D9146B1E650"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\solver.ts",
|
||||
"hash": "E79CE0BAFB55AC88DF0449F04E080C85B93519CD35F1C93316859B117D559561"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\theme.ts",
|
||||
"hash": "7EA3A99963F9E03BE2C1BED170F4E9899FAE3F7EA3D8427DFBB4CA1C863DB5AC"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\types.ts",
|
||||
"hash": "3B95E5F2CD42E83749F64B5F4ACD28AA94E1F015488BBD328B063639E3BB8DD2"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\src\\view.ts",
|
||||
"hash": "6C1FD1C4A4EC2E5B22F5C22F3C89013CA8C01A15E852B08B50AE52AAFD2E43FA"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\board.test.ts",
|
||||
"hash": "A7274A58591886743F96AF0375C0A87C11739D5727A170FBFF14E4E7567F1062"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\clue-hint.test.ts",
|
||||
"hash": "86DF4DA9CD7685715EC0083487F295A42EDA42FEFEA33F50A96DA5BC99375987"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\game.test.ts",
|
||||
"hash": "7946ACC582DB75394AC261DBCCD0C7414CB5E61D4EF9FFB8AA3B9289EF6F2D95"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\input.test.ts",
|
||||
"hash": "6FF683492B7A2DB002FFB11C6F2C8C32B41A1737A108CF556891C510AAE471D6"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\layout.test.ts",
|
||||
"hash": "8D239FFCF6AF9F24ADD01068E15B73977EA726C78D9A7236C4EC4EFABD44B93B"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\parity.test.ts",
|
||||
"hash": "9F04D2F4B6ACB4541CC8151CC635F72089C7D2DAF9D4F1EB5DC1ADE5A8BC3A39"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\progress.test.ts",
|
||||
"hash": "6FE2A71C0EF7DF7F8696452C1E9B99E167EA6FE92281FC4654E169E12A326A0B"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\reserved-step.test.ts",
|
||||
"hash": "D70727ABDCD642604CF74AE9E0DF2B9A789A416AC9BCDA26AF218D181EF0AA4E"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\solver.test.ts",
|
||||
"hash": "FF6227D715EF295F8349CB90781E60FAAFEB89C32C1279EA3F74F6DF6448792D"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tests\\theme.test.ts",
|
||||
"hash": "1A41979F0996D6ED2EE228F3FA8A7EAB686F1CEB7FFEC624E2440D0FAC04516F"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tools\\ascii.ts",
|
||||
"hash": "40ECD6E5F773BBFE24578717F786396BA387B06976BE4B42D691B4F7F3132A34"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tools\\design.ts",
|
||||
"hash": "38D7C3953018CAE33F185FACFDE0BD1006E278A739D04A729A6F78D1291B2D6E"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tools\\generate.ts",
|
||||
"hash": "60F651E88A9C6E00A5D2C5B648B30E67C916882C401036317E389F16412D3890"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\tools\\validate.ts",
|
||||
"hash": "B4F35C43F4C8F508A075E857E4FBBAAA63163AD0948EF4CEFC37047538CD6CBE"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\web\\audio.ts",
|
||||
"hash": "9E2E257BA976A65DC1540B72BABDFD31AD5F8CAC747D1FB15076DB4E072EF386"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\web\\canvas-painter.ts",
|
||||
"hash": "B024CB61421FB8C32396EA189D587891FA09E3BB966D916DC9229693C664D2DD"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\web\\index.html",
|
||||
"hash": "B43A1D86A69A446791C3C4C096DBE1A6812EB9FBC690696947DC353E8F5AA706"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\web\\main.ts",
|
||||
"hash": "ABF9038EED5B6536F47DA8812D3D75BC4086E144707FA97681276C4010CC9DD9"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\01-number-stroke\\web\\style.css",
|
||||
"hash": "93BF49331040D426D0DC9EDBBC8465C4E38B69B24E0CF011C8DA9E00669A36E3"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\.gitignore",
|
||||
"hash": "D420BD58545C4F89CE045197D412BB0321E0B931BBBC5BBD437B6AD79B7D4969"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\package-lock.json",
|
||||
"hash": "E9EE452E29528007515CF94D6BD78EFB894248DD677B63CF5882AB92D9FC0958"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\package.json",
|
||||
"hash": "745D6AB416533BCD4B55FA4CE712BDCBFE5FD6E7D6DAF712E33C9CB69C4F7416"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\README.md",
|
||||
"hash": "0DFCA0CA92E3A7A5DCBC7F04D27FF1C13B609DBC8C7FADAF3F6E49BA709613F4"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tsconfig.json",
|
||||
"hash": "FC799CE19916A443B1C96451DEB8972CB204B5EAA6A1B0610249EAA496B9AC91"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tsconfig.src.json",
|
||||
"hash": "1B16113A622C95D80C3CFCE3ABEDC6F8319271867E9C63EB75E90675F9537246"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\docs\\GDD.md",
|
||||
"hash": "2E00C4CB9BFDA49690B4C24141F177ADCB22F578546044151B5DFA0FF12FCAEC"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\docs\\HANDOFF.md",
|
||||
"hash": "A4417298C6FD7886C290A92782F2F56DAC6975228C9F7A6D1D902500C6E7ECEE"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\levels\\pack-1-tutorial.json",
|
||||
"hash": "C6EC79A1AE01FA0DFA69C1EA46926AD941AFB07B53A8E332788342AAD78740C0"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\levels\\pack-2-spinner.json",
|
||||
"hash": "CDAADA71CB6FDF2735077233BC95FA4F272ED12A9030CE7E252896D602C149D5"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\scripts\\build.mjs",
|
||||
"hash": "AB4250CA1DEE80FBD8A0A84CC1261C1D8F291E2EFC103BC1415BA63C5A126E19"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\board.ts",
|
||||
"hash": "0E855EC1B1A420C25BAA82B978BA036A89CA00AB855B148EFE1C79EB2F04D8CE"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\game.ts",
|
||||
"hash": "80EF43C7D1CCB3C82B535465B1DBA98EA509CB79FF7A2A653309D6DCAAF9ED24"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\geom.ts",
|
||||
"hash": "55A00D57523AA2EE6B434AC05F723D77876BE357B383E72DCAD68F2F9D75AB3E"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\input.ts",
|
||||
"hash": "9555C7B5B82C2A3F3A743C12F2775A172554AFBEC957E4FF065D44E7BCF7F61B"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\layout.ts",
|
||||
"hash": "B6BECDF619FCD503AD25FD5C3F3575F7B84DD879DC1FEC4C68774B09CF7B4799"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\levels.ts",
|
||||
"hash": "DD8CCB28B127ECF206B20110B3FBC61056218AFBA9F0551216CC703F7BB66C30"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\painter.ts",
|
||||
"hash": "CFEFCBB96B1E851B0ED658DBED4CABB51D8A0492E4433F8E64BAB7B3BE870CA6"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\progress.ts",
|
||||
"hash": "1457F4015D1401544A5F5E7CD1240603DE2538B0256380A37A875836B4B284A2"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\solver.ts",
|
||||
"hash": "A6A595BC30CDAF630E7EB0B0400854890CE260A9EC943AF246A7CC371E3DBEC2"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\theme.ts",
|
||||
"hash": "455CD9D15ED403FADACA03C9125197CEBFEFCAC0B9442DD7527FCA40CA4F4FB6"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\types.ts",
|
||||
"hash": "5DD777AE6249ABBD13D99CC92CDB78BD99EB86882C10CB093706B1E555961D0A"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\src\\view.ts",
|
||||
"hash": "319017E6B4B999CC4772CE4AD8297210993A19EF4ACCA2560ABAFABB77FD3BE1"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tests\\board.test.ts",
|
||||
"hash": "4B8E6D53C5D6D72402D5E67AFA08BBEBED36106EE436024F8E630440FAD3824D"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tests\\game.test.ts",
|
||||
"hash": "004B53DCE7BBC79A1A4494FF86286F6FB33BF326F99B0DF7247BB0078F03C73B"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tests\\input.test.ts",
|
||||
"hash": "DFDC0F64C22F5BFD0F6D2C2AE53E2A6B8747A632CCF6FBBEC790B32F8432D563"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tests\\layout.test.ts",
|
||||
"hash": "EBDAF872F55798F6D2252EB74BCD8C8BEE280FDFFF023D17200CC75DCD1713D4"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tests\\progress.test.ts",
|
||||
"hash": "81A7F2CD0231B833CE8E0799A4F060618E5AF0E31C588612830FAFFE0114EC61"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tests\\solver.test.ts",
|
||||
"hash": "F8A3FB6B3254BA4762F7ACF1DFB82C98B596D24C84074D7FEF340029B11030C0"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tools\\ascii.ts",
|
||||
"hash": "DAF190AF785B52E91105036C8BE228771A53662716FFBC1AF571EF46CFA8953E"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tools\\design.ts",
|
||||
"hash": "B820D8F684DC4E3DE19F3EEECEE0C40525A0140850088F4F40561587D78BFE65"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tools\\generate.ts",
|
||||
"hash": "7E48D5587167AD332CFE5DEBB8A59D98952C36CBC1F36DE873539290B88D088B"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\tools\\validate.ts",
|
||||
"hash": "500AE3D03DA1CEC78F52FC3EA4F4AC10A07BB636D4C7BB1DD01F1DA3452DDE48"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\web\\audio.ts",
|
||||
"hash": "E4F6D9AB157970EC7ADF1F3D968CDF002CC332C1EB937DC9F853D8343AB7BD4C"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\web\\canvas-painter.ts",
|
||||
"hash": "5D6F473E914F9CF788E2CE3AC95B3E8142ADBAC11D620AC110CFECA4DCB37C93"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\web\\index.html",
|
||||
"hash": "E055A3E7F752B81E96A7BD8DD7DA5C08BE3217FFF933210B7073F1836896424D"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\web\\main.ts",
|
||||
"hash": "0AFDD7625020D5DC4ACA23CA088C843DC6D5AE3F667102F4555D881E89AF3C41"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\modes\\03-hex-walls\\web\\style.css",
|
||||
"hash": "BA7C42FF71CF732CAC94C82E7EE02C6F7DE61C05BF9B1732B27F54349EADF836"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\releases\\数字一笔-试玩版.html",
|
||||
"hash": "0C835D234238F7A094466AE10FAA5F797BBCF144E61213E8369C77A0DE53DC15"
|
||||
},
|
||||
{
|
||||
"path": "G:\\项目\\NumberSwipe\\shared\\README.md",
|
||||
"hash": "2F0A4351C2ECBD06EE0083BBBC240A7D0F26A4E09413AEF679D0876E21F302F5"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
participant,mode,level,difficulty,thinking_seconds,first_error_step,first_error_reason,backtracks,restarts,hints,completed,rule_understood,key_reasoning,boundary_readability,comments
|
||||
|
@@ -0,0 +1,30 @@
|
||||
import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';import vm from 'node:vm';import {definitions} from '../tools/definitions.mjs';import {solve,graph} from '../tools/engine-template.mjs';
|
||||
function boot(mode,width=340){const draws=[],elements=new Map();let context;
|
||||
class Element{constructor(id=''){this.id=id;this.children=[];this.handlers={};this.hidden=false;this.disabled=false;this.value='';this.textContent='';this.className='';}replaceChildren(){this.children=[];}append(e){this.children.push(e);}addEventListener(n,f){this.handlers[n]=f;}setPointerCapture(){}getBoundingClientRect(){return {left:0,top:0,width,height:420};}click(){this.onclick?.();}}
|
||||
const ctx={globalAlpha:1,lineWidth:1,font:'',fillStyle:'',strokeStyle:'',textAlign:'center',textBaseline:'middle',setTransform(){},clearRect(){draws.length=0;},beginPath(){this.p=[];},moveTo(x,y){this.p.push([x,y]);},lineTo(x,y){this.p.push([x,y]);},closePath(){},fill(){draws.push({kind:'fill',points:this.p,color:this.fillStyle});},stroke(){draws.push({kind:'stroke',points:this.p,color:this.strokeStyle});},fillText(s,x,y){draws.push({kind:'text',text:s,x,y,font:this.font});},save(){},restore(){},translate(){},rotate(){},setLineDash(){}};
|
||||
function get(id){if(!elements.has(id))elements.set(id,new Element(id));return elements.get(id);}
|
||||
const canvas=get('board');canvas.clientWidth=width;canvas.clientHeight=420;canvas.getContext=()=>ctx;
|
||||
const document={getElementById:get,createElement:()=>new Element()},window={devicePixelRatio:1,addEventListener(){}};
|
||||
context=vm.createContext({document,window,localStorage:{getItem:()=>null,setItem(){}},performance:{now:()=>100},setTimeout,URL,Blob,console});
|
||||
const html=fs.readFileSync(new URL('../modes/'+mode+'/play.html',import.meta.url),'utf8');const script=html.match(/<script>([\s\S]*)<\/script>/)[1];new vm.Script(script).runInContext(context);
|
||||
const snapshot=()=>JSON.parse(JSON.stringify(window.variantSnapshot()));
|
||||
function press(id){const p=snapshot().centers[id];canvas.handlers.pointerdown({preventDefault(){},pointerId:1,clientX:p[0],clientY:p[1]});canvas.handlers.pointerup({});}
|
||||
return {get,snapshot,press,canvas,draws,width};
|
||||
}
|
||||
for(const def of definitions)test('embedded page lifecycle and all 8 levels '+def.id,()=>{const ui=boot(def.id);const pack=JSON.parse(fs.readFileSync(new URL('../modes/'+def.id+'/levels.json',import.meta.url),'utf8'));
|
||||
for(let i=0;i<8;i++){ui.get('difficulties').children[Math.floor(i/2)].click();ui.get('levels').children[i%2].click();const l=pack.levels[i],p=solve(l)[0];assert.equal(ui.snapshot().level,l.id);assert.equal(ui.get('levels').children.length,2);assert.equal(ui.get('difficulties').children.length,4);
|
||||
const before=ui.snapshot();for(const center of Object.values(before.centers)){assert.ok(center[0]>=0&¢er[0]<=ui.width);assert.ok(center[1]>=0&¢er[1]<=420);}assert.ok(before.unit>=20);
|
||||
ui.get('hint').click();assert.match(ui.get('status').textContent,/黄色/);
|
||||
ui.press(p[0]);ui.press(p[1]);assert.deepEqual(ui.snapshot().path,p.slice(0,2));ui.press(p[0]);assert.deepEqual(ui.snapshot().path,p.slice(0,1));ui.get('undo').click();assert.deepEqual(ui.snapshot().path,[]);
|
||||
for(const id of p)ui.press(id);assert.equal(ui.snapshot().cleared,true);assert.equal(ui.get('remaining').textContent,'完成 ✓');
|
||||
ui.get('undo').click();assert.equal(ui.snapshot().cleared,false);assert.equal(ui.snapshot().path.length,p.length-1);ui.press(p.at(-1));assert.equal(ui.snapshot().cleared,true);
|
||||
ui.get('restart').click();assert.deepEqual(ui.snapshot().path,[]);assert.equal(ui.snapshot().cleared,false);assert.ok(ui.snapshot().openedGates.every(v=>!v));
|
||||
if(l.geometry==='blocks')assert.ok(l.cells.some(c=>c.w>1));
|
||||
}
|
||||
});
|
||||
test('fast drag samples every crossed cell, release preserves prefix',()=>{const ui=boot('relative'),p=solve(JSON.parse(fs.readFileSync(new URL('../modes/relative/levels.json',import.meta.url),'utf8')).levels[0])[0];const start=ui.snapshot().centers[p[0]],next=ui.snapshot().centers[p[1]];ui.canvas.handlers.pointerdown({preventDefault(){},pointerId:1,clientX:start[0],clientY:start[1]});ui.canvas.handlers.pointermove({preventDefault(){},clientX:next[0],clientY:next[1]});ui.canvas.handlers.pointerup({});assert.deepEqual(ui.snapshot().path,p.slice(0,2));});
|
||||
test('real UI ignores closed door, opens after switch and restores on undo',()=>{const pack=JSON.parse(fs.readFileSync(new URL('../modes/gates/levels.json',import.meta.url),'utf8'));const ui=boot('gates');const l=pack.levels[0],p=solve(l)[0],gate=l.gates[0];for(const id of p)ui.press(id);assert.ok(ui.snapshot().openedGates[0]);ui.get('restart').click();assert.equal(ui.snapshot().openedGates[0],false);
|
||||
// Follow a geometrically valid prefix reaching one gate side without its switch.
|
||||
const g=graph(l),prefixes=[];function find(ids){if(gate.edge.includes(ids.at(-1))&&!ids.includes(gate.switchCell)){prefixes.push(ids);return;}for(const j of g.adj[g.ids.get(ids.at(-1))]){const id=l.cells[j].id;if(!ids.includes(id)&&id!==gate.switchCell)find([...ids,id]);}}
|
||||
find([p[0]]);if(prefixes.length){for(const id of prefixes[0])ui.press(id);const before=ui.snapshot().path;const other=gate.edge.find(id=>id!==before.at(-1));ui.press(other);assert.deepEqual(ui.snapshot().path,before);}
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';import {definitions} from '../tools/definitions.mjs';import {solve,canAppend,violations,hint,assertStructure} from '../tools/engine-template.mjs';
|
||||
const packs=definitions.map(d=>JSON.parse(fs.readFileSync(new URL('../modes/'+d.id+'/levels.json',import.meta.url),'utf8')));
|
||||
// Independent tiny-board reference: its own geometry and full-path rule checks.
|
||||
function reference(level){const cells=level.cells,at=new Map(cells.map(c=>[c.id,c]));function occupied(c){if(c.shape)return c.shape.map(([x,y])=>[c.x+x,c.y+y]);let u=[];for(let y=0;y<(c.h||1);y++)for(let x=0;x<(c.w||1);x++)u.push([c.x+x,c.y+y]);return u;}
|
||||
const adjacent=(a,b)=>level.geometry==='hex'?[[1,0],[-1,0],[0,1],[0,-1],[1,-1],[-1,1]].some(([q,r])=>b.q-a.q===q&&b.r-a.r===r):occupied(a).some(([x,y])=>occupied(b).some(([xx,yy])=>Math.abs(xx-x)+Math.abs(yy-y)===1));
|
||||
const adj=new Map(cells.map(c=>[c.id,cells.filter(b=>b!==c&&adjacent(c,b)).map(c=>c.id)]));const answers=[],p=[];
|
||||
function accept(){const pos=id=>p.indexOf(id)+1;for(const c of cells)if(c.clue&&pos(c.id)!==c.clue)return false;for(const r of level.relations||[])if(pos(r.to)-pos(r.from)!==r.delta)return false;for(const chain of level.orderChains||[])for(let i=1;i<chain.length;i++)if(pos(chain[i-1])>=pos(chain[i]))return false;for(const w of level.windows||[])if(pos(w.cell)<w.min||pos(w.cell)>w.max)return false;
|
||||
for(const c of cells)if(c.direction){const i=pos(c.id)-1;if(i===0||i===p.length-1)return false;const a=at.get(p[i-1]),b=c,d=at.get(p[i+1]);const straight=(a.x===b.x&&b.x===d.x)||(a.y===b.y&&b.y===d.y);if(straight!==(c.direction==='straight'))return false;}
|
||||
if(level.mode.includes('alternation'))for(let i=1;i<p.length;i++)if(at.get(p[i-1]).category===at.get(p[i]).category)return false;
|
||||
for(const g of level.gates||[])for(let i=1;i<p.length;i++)if(g.edge.includes(p[i-1])&&g.edge.includes(p[i])&&pos(g.switchCell)>i)return false;return true;}
|
||||
function walk(id){p.push(id);if(p.length===cells.length){if(accept())answers.push([...p]);}else for(const next of adj.get(id))if(!p.includes(next))walk(next);p.pop();}for(const c of cells)walk(c.id);return answers;
|
||||
}
|
||||
for(const pack of packs)for(const level of pack.levels.slice(0,2))test('independent exhaustive reference '+pack.mode+' '+level.id,()=>{assert.deepEqual(solve(level,{limit:Infinity}),reference(level));});
|
||||
test('all 56 author routes playable and undo states recomputed',()=>{let n=0;for(const pack of packs)for(const l of pack.levels){assertStructure(l);const p=solve(l)[0];for(let i=0;i<p.length;i++)assert.equal(canAppend(l,p.slice(0,i),p[i]),true);assert.deepEqual(violations(l,p),[]);const wrong=[...p];[wrong[0],wrong[1]]=[wrong[1],wrong[0]];assert.ok(['restart','backtrack'].includes(hint(p,wrong).kind));assert.equal(hint(p,p.slice(0,2)).cell,p[2]);assert.equal(hint(p,p).kind,'none');n++;}assert.equal(n,56);});
|
||||
test('relative difference is transitions, not intermediate count',()=>{const l={mode:'relative',geometry:'square',cells:[0,1,2,3,4].map(i=>({id:''+i,x:i,y:0,w:1,h:1})),relations:[{from:'0',to:'4',delta:4}]};assert.equal(violations(l,['0','1','2','3','4']).length,0);l.relations[0].delta=3;assert.ok(violations(l,['0','1','2','3','4']).length);});
|
||||
test('window inclusive edges, expiration and shape rejection',()=>{const l={mode:'window',geometry:'square',cells:[0,1,2,3,4].map(i=>({id:''+i,x:i,y:0})),windows:[{cell:'2',min:2,max:4}]};assert.equal(violations(l,['0','2']).length,0);assert.equal(violations(l,['0','1','3','2']).length,0);assert.ok(violations(l,['0','1','3','4']).length);l.windows[0].max=2;assert.throws(()=>assertStructure(l));});
|
||||
test('direction waits for exit, excludes both endpoints',()=>{const l={mode:'direction',geometry:'square',cells:[{id:'a',x:0,y:0},{id:'b',x:1,y:0,direction:'turn'},{id:'c',x:1,y:1}]};assert.equal(violations(l,['a','b']).length,0);assert.equal(violations(l,['a','b','c']).length,0);assert.ok(violations(l,['b']).length);assert.ok(violations(l,['a','c','b']).length);});
|
||||
test('closed gate cannot open itself on arrival; undo closes it',()=>{const l={mode:'gates',geometry:'square',cells:[{id:'a',x:0,y:0},{id:'b',x:1,y:0},{id:'s',x:0,y:1},{id:'d',x:1,y:1}],gates:[{edge:['a','b'],switchCell:'s'}]};assert.equal(canAppend(l,['a'],'b'),false);assert.equal(canAppend(l,['s','d','a'],'b'),true);assert.equal(canAppend(l,['a'],'b'),false);l.gates[0].switchCell='b';assert.equal(canAppend(l,['a'],'b'),false);});
|
||||
test('dynamic connectivity retains future door opportunities',()=>{const l=packs.find(p=>p.mode==='gates').levels[6];assert.deepEqual(solve(l,{prune:true}),solve(l,{prune:false}));});
|
||||
test('alternating classes are hard transitions; whole block counts once',()=>{const l=packs.find(p=>p.mode==='block-alternation').levels[0];const p=solve(l)[0];assert.equal(p.length,l.cells.length);const g=l.cells.find(c=>c.w>1);assert.ok(g);const target=l.cells.find(c=>c.category===g.category&&c.id!==g.id);assert.equal(canAppend(l,[g.id],target.id),false);});
|
||||
test('reject overlap, cycles and nonexistent edges',()=>{assert.throws(()=>assertStructure({mode:'order',geometry:'square',cells:[{id:'a',x:0,y:0},{id:'b',x:0,y:0},{id:'c',x:1,y:0}]}));const l={mode:'order',geometry:'square',cells:[0,1,2].map(i=>({id:''+i,x:i,y:0})),orderChains:[['0','1'],['1','0']]};assert.throws(()=>assertStructure(l));delete l.orderChains;l.gates=[{edge:['0','2'],switchCell:'1'}];assert.throws(()=>assertStructure(l));});
|
||||
@@ -0,0 +1,13 @@
|
||||
import fs from 'node:fs';import path from 'node:path';import {fileURLToPath} from 'node:url';import {definitions} from './definitions.mjs';
|
||||
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
||||
const css=`*{box-sizing:border-box}body{margin:0;background:#f6f5fb;color:#292a43;font-family:system-ui,'Microsoft YaHei',sans-serif}main{max-width:620px;margin:auto;padding:20px 18px 32px}a{color:#6650bf}header{display:flex;justify-content:space-between;align-items:center}h1{font-size:26px;margin:10px 0 4px}h2{font-size:18px;margin:14px 0 8px}p{line-height:1.6;margin:8px 0;color:#66677e}.eyebrow{font-size:12px;letter-spacing:.1em;color:#7e728f}button{font:inherit;min-height:44px;border:1px solid #d8d5e5;border-radius:12px;background:white;color:#47405f;padding:9px 15px;cursor:pointer}button:focus-visible,a:focus-visible,textarea:focus-visible{outline:3px solid #a586ed}button.active{background:#6c56c8;border-color:#6c56c8;color:white}button:disabled{opacity:.4}nav,.controls,#constraints{display:flex;gap:8px;flex-wrap:wrap;margin:10px 0}#difficulties{display:grid;grid-template-columns:repeat(4,1fr)}#levels{margin-bottom:18px}.card{padding:14px;border:1px solid #e4e0ef;border-radius:20px;background:white;box-shadow:0 6px 26px #41336508}.meta{display:flex;justify-content:space-between;align-items:center;font-size:13px;color:#767286}#remaining{background:#ece7ff;color:#6650bf;padding:6px 10px;border-radius:20px;font-weight:700}canvas{display:block;width:100%;height:470px;touch-action:none}#status{min-height:48px;font-size:14px;color:#554b70;line-height:1.7}#caption{font-size:13px}#constraints{margin-bottom:0}.constraint{font-size:13px;min-height:36px;padding:6px 10px;background:#f7f4ff}.controls button{flex:1;padding:10px 8px}.controls button.primary{background:#6c56c8;color:white}details{margin-top:16px;padding:12px;background:#eeebf6;border-radius:12px}summary{cursor:pointer;font-weight:600;min-height:28px}textarea{width:100%;min-height:80px;font:inherit;margin:8px 0;border:1px solid #d5cce6;border-radius:8px;padding:10px}.small{font-size:12px}.cards{display:grid;gap:14px}.cards a{display:block;text-decoration:none;color:inherit}.cards .card:hover{border-color:#8d78cf}.pill{font-size:12px;color:#7c659a}footer{margin-top:22px;font-size:12px;color:#857d93}@media(max-width:400px){main{padding:12px}canvas{height:420px}h1{font-size:23px}button{padding:8px 10px}}@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}`;
|
||||
const esc=s=>s.replaceAll('&','&').replaceAll('<','<').replaceAll('>','>');
|
||||
for(const def of definitions){const folder=path.join(root,'modes',def.id),pack=JSON.parse(fs.readFileSync(path.join(folder,'levels.json'),'utf8').replace(/^\uFEFF/,''));
|
||||
fs.copyFileSync(path.join(root,'tools','ui-template.mjs'),path.join(folder,'ui.mjs'));
|
||||
let engine=fs.readFileSync(path.join(folder,'rules.mjs'),'utf8').replaceAll('export ','');let ui=fs.readFileSync(path.join(folder,'ui.mjs'),'utf8').replace(/^import[^\n]+\n/,'');
|
||||
const script=`const PACK=${JSON.stringify(pack).replaceAll('<','\\u003c')};\n${engine}\n${ui}`;
|
||||
const html=`<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${def.title} · 衍生玩法验证</title><style>${css}</style><main><header><a href="../../index.html">← 全部玩法</a><span class="eyebrow">路径推理实验室</span></header><h1 id="title"></h1><p class="small">四个难度 · 每档两关 · 全部开放</p><nav id="difficulties" aria-label="难度"></nav><nav id="levels" aria-label="关卡"></nav><section class="card"><div class="meta"><span id="position"></span><span id="remaining" aria-live="polite"></span></div><h2 id="goal"></h2><p id="caption"></p><div id="constraints" aria-label="本关条件"></div><canvas id="board" aria-label="关卡盘面;拖动或点按相邻格画路"></canvas><div id="status" role="status" aria-live="polite"></div></section><div class="controls"><button id="undo">撤销</button><button id="restart">重开</button><button id="hint">指路</button><button id="next" class="primary" hidden>下一关</button></div><p class="small">点按或拖动相邻格 · 松手可继续 · 点已走格回退</p><details open><summary>玩法规则</summary><p id="rule-text"></p><p class="small">固定数字必须在对应步到达。所有格各走一次。门和类别限制会阻止移动;其他条件画错可继续,错误处标红。</p></details><details><summary>记录本关体验</summary><p class="small">用自己的话说明关键分叉为什么只能这样走。导出记录含首次错误、回退和提示次数。</p><textarea id="reasoning" aria-label="推理与试玩反馈" placeholder="我先排除了哪条路线?依据是什么?"></textarea><button id="export">导出试玩记录</button></details><footer>原创候选关 · 难度等待真人试玩校准 · 离线可用</footer></main><script>${script}</script></html>`;
|
||||
fs.writeFileSync(path.join(folder,'play.html'),html);
|
||||
}
|
||||
fs.writeFileSync(path.join(root,'index.html'),`<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>一笔路径 · 衍生玩法验证</title><style>${css}</style><main><span class="eyebrow">NUMBER SWIPE · VARIANT LAB</span><h1>一笔路径,七种推理</h1><p>每个玩法 8 关:入门、普通、进阶、难,各 2 关。先观察,再一笔完成。</p><div class="cards">${definitions.map((d,i)=>`<a href="modes/${d.id}/play.html" class="card"><span class="pill">0${i+1} · 8 关</span><h2>${d.title}</h2><p>${esc(d.rule)}</p></a>`).join('')}</div><footer>56 个机制验证候选关 · 无计时、体力、分数 · 所有玩法均可离线打开</footer></main></html>`);
|
||||
console.log('Built 7 standalone pages + index.');
|
||||
@@ -0,0 +1,10 @@
|
||||
export const definitions = [
|
||||
{id:'relative',title:'相对步差',geometry:'square',rule:'每格一次。A → B +d:B 的步号减去 A 的步号等于 d;+4 是四次移动,中间有三格。',goals:['认识差值与中间格数量','用间隔排除直达路线','两端绝对步号都未知','两条独立关系共同选路','共享端点的关系联动','间隔决定先处理哪个区域','三条关系跨区联动','无固定起点,联合推断关系位置']},
|
||||
{id:'order',title:'先后链',geometry:'square',rule:'每格一次。链中的标记必须依次访问,例如 A < B < C;相隔几步不受限制。',goals:['A 必须先于 B','靠近 B 也要先绕到 A','三标记决定区域顺序','两条独立先后关系','共享标记形成部分顺序','访问标记时保留后续出口','两条链交错安排','无固定起点,由次序确定全路']},
|
||||
{id:'window',title:'到达窗口',geometry:'square',rule:'每格一次。写着 a–b 的格必须在第 a 到第 b 步之间到达,包含两个边界。',goals:['认识允许范围','排除过早到达','窗口迫使先绕行','两个窗口限制早到与晚到','宽窗口与跨区顺序联动','重叠窗口决定访问次序','三个窗口分配路线区间','无固定起点,由窗口确定路线']},
|
||||
{id:'direction',title:'直行/转弯',geometry:'square',rule:'每格一次。↔ 格必须直行,↱ 格必须转 90°;这些格不能是起终点,到离开时再检查方向。',goals:['直行格决定出口','转弯格排除直行','两个直行格联动','直行与转弯共同选路','方向要求迫使绕行','方向决定区域入口出口','多个方向跨区联动','无固定起点,方向确定两端']},
|
||||
{id:'block-alternation',title:'大格交替',geometry:'blocks',rule:'整块只算一步,每块一次。相邻两步必须交替访问 ○ 和 △;共边同类也不能走。',goals:['认识交替、整块一步','大格旁同类相邻但不能走','类别数量决定端点类别','大格出口由类别筛选','异形凹口与交替联动','跨区保留异类出口','多个大格异形分配路线','无固定起点,由类别确定两端']},
|
||||
{id:'hex-alternation',title:'六边交替',geometry:'hex',rule:'每个六边形一步,每格一次。六向共边相邻,但下一步必须换类别:○ 与 △ 交替。',goals:['六向邻接中的异类选择','三角邻接的同类边被排除','类别数量限制端点','空洞附近选择异类格','局部类别决定跨区顺序','连续分叉中保留出口','三个区域类别出口联动','无固定起点,由类别确定全路']},
|
||||
{id:'gates',title:'路径开门',geometry:'square',rule:'每格一次。门初始关闭,走到编号对应的开关后永久打开;撤销会恢复门的状态。',goals:['先踩开关再过门','绕行到开关','开门后不能立刻穿过去','两对独立门与开关','第一门后才能到第二开关','开关顺序与区域收尾','三对门形成依赖链','两条开门依赖交织']}
|
||||
];
|
||||
export const difficulties=['入门','普通','进阶','难'];
|
||||
@@ -0,0 +1,15 @@
|
||||
import fs from 'node:fs';import path from 'node:path';import {fileURLToPath} from 'node:url';import {definitions} from './definitions.mjs';import {units,constraints} from './engine-template.mjs';
|
||||
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
||||
const esc=s=>String(s).replaceAll('&','&').replaceAll('<','<').replaceAll('>','>');
|
||||
function diagram(l,author=null){let shapes=[],centers=new Map();for(const c of l.cells){if(l.geometry==='hex'){const x=1.5*c.r,y=Math.sqrt(3)*(c.q+c.r/2);centers.set(c.id,[x,y]);let p=[];for(let a=0;a<6;a++)p.push([x+Math.cos(a*Math.PI/3),y+Math.sin(a*Math.PI/3)]);shapes.push({id:c.id,p});}else{const u=units(c);centers.set(c.id,[u.reduce((s,p)=>s+p[0]+.5,0)/u.length,u.reduce((s,p)=>s+p[1]+.5,0)/u.length]);for(const [x,y]of u)shapes.push({id:c.id,p:[[x,y],[x+1,y],[x+1,y+1],[x,y+1]]});}}
|
||||
const all=shapes.flatMap(s=>s.p),xmin=Math.min(...all.map(p=>p[0])),xmax=Math.max(...all.map(p=>p[0])),ymin=Math.min(...all.map(p=>p[1])),ymax=Math.max(...all.map(p=>p[1])),s=Math.min(340/(xmax-xmin),410/(ymax-ymin)),tr=([x,y])=>[(x-xmin)*s+20,(y-ymin)*s+20];const width=(xmax-xmin)*s+40,height=(ymax-ymin)*s+40;
|
||||
let svg=`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" role="img" aria-label="${esc(l.title)}">`;
|
||||
for(const item of shapes){const c=l.cells.find(c=>c.id===item.id);svg+=`<polygon points="${item.p.map(p=>tr(p).join(',')).join(' ')}" fill="${c.category==='circle'?'#e4f3ff':c.category==='triangle'?'#fff0d4':'#f1eef9'}" stroke="#aaa1c7" stroke-width="2"/>`;}
|
||||
if(author)svg+=`<polyline points="${author.map(id=>tr(centers.get(id)).join(',')).join(' ')}" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/>`;
|
||||
for(let i=0;i<(l.gates||[]).length;i++){const r=l.gates[i],[a,b]=r.edge.map(id=>tr(centers.get(id))),x=(a[0]+b[0])/2,y=(a[1]+b[1])/2,angle=Math.atan2(b[1]-a[1],b[0]-a[0])*180/Math.PI;svg+=`<line x1="${x}" y1="${y-s*.33}" x2="${x}" y2="${y+s*.33}" stroke="#cf7824" stroke-width="7" transform="rotate(${angle} ${x} ${y})"/><text x="${x+8}" y="${y-8}" font-size="12">门${i+1}</text>`;}
|
||||
for(const c of l.cells){const [x,y]=tr(centers.get(c.id)),w=(l.windows||[]).find(r=>r.cell===c.id),sw=(l.gates||[]).findIndex(r=>r.switchCell===c.id);let label=author?String(author.indexOf(c.id)+1):c.clue===undefined?'':String(c.clue);let tags=[w?`${w.min}–${w.max}`:'',c.direction==='straight'?'↔':c.direction==='turn'?'↱':'',c.category==='circle'?'○':c.category==='triangle'?'△':'',sw>=0?`开${sw+1}`:''].filter(Boolean).join(' ');svg+=`<text x="${x}" y="${y-8}" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="${Math.max(11,Math.min(21,s*.29))}" font-weight="700">${esc(label||tags)}</text><text x="${x}" y="${y+10}" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">${c.id}${label&&tags?' · '+esc(tags):''}</text>`;}
|
||||
return svg+'</svg>';}
|
||||
for(const def of definitions){const folder=path.join(root,'modes',def.id),pack=JSON.parse(fs.readFileSync(path.join(folder,'levels.json'),'utf8')),notes=JSON.parse(fs.readFileSync(path.join(folder,'design.json'),'utf8'));let articles='';for(let i=0;i<8;i++){const l=pack.levels[i],n=notes[i];fs.writeFileSync(path.join(folder,l.id+'-board.svg'),diagram(l));fs.writeFileSync(path.join(folder,l.id+'-solution.svg'),diagram(l,n.author));
|
||||
articles+=`<article><h2>${l.id} · ${l.difficulty} · ${esc(l.title)}</h2><div class="boards"><figure>${diagram(l)}<figcaption>原始盘面 · 格id用于对照JSON</figcaption></figure><details><summary>展开作者解与路线</summary><figure>${diagram(l,n.author)}</figure><p>${n.author.join(' → ')}</p></details></div><h3>推理依据</h3><ul>${n.breakdown.map(t=>`<li>${esc(t)}</li>`).join('')}</ul><p>关键分叉:第 ${n.keyFork.step} 步应走 ${n.keyFork.correct},诱惑路线走 ${n.keyFork.tempting}。排除依据:${esc(n.reason)}。</p><details><summary>逐项机制必要性反例</summary>${n.witnesses.map(w=>`<p>解除 ${w.constraint} 后:${w.path.join(' → ')}</p>`).join('')}</details></article>`;}
|
||||
fs.writeFileSync(path.join(folder,'DESIGN.html'),`<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${def.title} · 设计评审</title><style>body{max-width:920px;margin:24px auto;padding:0 18px;background:#f7f5fb;color:#303047;font:15px/1.7 system-ui,'Microsoft YaHei'}article{background:white;border:1px solid #ddd5ed;border-radius:16px;padding:20px;margin:20px 0;break-inside:avoid}.boards{display:flex;gap:24px;flex-wrap:wrap}figure{margin:0;width:380px;max-width:100%}svg{max-height:450px;width:100%}details{cursor:pointer}a{color:#6552aa}@media print{body{background:white}article{border:none;page-break-after:always}} </style><a href="play.html">进入试玩</a><h1>${def.title} · 8关设计评审</h1><p>${esc(def.rule)}</p><p>原始盘面和作者解分开展示。标题是设计任务,真人推理路径与难度仍需试玩验证。数字、窗口、方向、类别和开关显示在格内;步差与先后关系见逐关推理依据。</p>${articles}</html>`);}
|
||||
console.log('Generated 56 original boards, 56 solutions and 7 design review pages.');
|
||||
@@ -0,0 +1,102 @@
|
||||
// Pure rules / geometry / solver. Each mode receives its own standalone copy.
|
||||
export function units(c){return c.shape?c.shape.map(([x,y])=>[c.x+x,c.y+y]):Array.from({length:(c.w||1)*(c.h||1)},(_,i)=>[c.x+i%(c.w||1),c.y+Math.floor(i/(c.w||1))]);}
|
||||
export function graph(level){
|
||||
const cells=level.cells, ids=new Map(cells.map((c,i)=>[c.id,i]));
|
||||
if(ids.size!==cells.length)throw Error('重复格 id');
|
||||
const adj=cells.map(()=>[]), occupancy=new Map();
|
||||
if(level.geometry!=='hex') for(let i=0;i<cells.length;i++)for(const [x,y]of units(cells[i])){const k=x+','+y;if(occupancy.has(k))throw Error('拼块重叠');occupancy.set(k,i);}
|
||||
for(let i=0;i<cells.length;i++)for(let j=i+1;j<cells.length;j++){
|
||||
let connected=false;
|
||||
if(level.geometry==='hex'){let dq=cells[j].q-cells[i].q,dr=cells[j].r-cells[i].r;connected=[[1,0],[-1,0],[0,1],[0,-1],[1,-1],[-1,1]].some(([a,b])=>a===dq&&b===dr);}
|
||||
else connected=units(cells[i]).some(([x,y])=>[[1,0],[-1,0],[0,1],[0,-1]].some(([a,b])=>occupancy.get((x+a)+','+(y+b))===j));
|
||||
if(connected){adj[i].push(j);adj[j].push(i);}
|
||||
}
|
||||
const clues=new Map();for(const c of cells)if(c.clue!==undefined){if(!Number.isInteger(c.clue)||c.clue<1||c.clue>cells.length||clues.has(c.clue))throw Error('数字范围或重复');clues.set(c.clue,ids.get(c.id));}
|
||||
return {cells,ids,adj,clues};
|
||||
}
|
||||
export function constraints(level){
|
||||
const result=[];
|
||||
for(let i=0;i<(level.relations||[]).length;i++)result.push({key:'relation:'+i,kind:'relation',...level.relations[i]});
|
||||
for(let i=0;i<(level.orderChains||[]).length;i++)for(let j=0;j<level.orderChains[i].length-1;j++)result.push({key:'order:'+i+':'+j,kind:'order',from:level.orderChains[i][j],to:level.orderChains[i][j+1]});
|
||||
for(let i=0;i<(level.windows||[]).length;i++)result.push({key:'window:'+i,kind:'window',...level.windows[i]});
|
||||
for(const c of level.cells)if(c.direction)result.push({key:'direction:'+c.id,kind:'direction',cell:c.id,value:c.direction});
|
||||
if(level.mode.includes('alternation'))result.push({key:'alternation',kind:'alternation'});
|
||||
for(let i=0;i<(level.gates||[]).length;i++)result.push({key:'gate:'+i,kind:'gate',...level.gates[i]});
|
||||
return result;
|
||||
}
|
||||
export function directionAt(level,path,k){
|
||||
if(k===0||k===path.length-1)return null;
|
||||
const [a,b,c]=[path[k-1],path[k],path[k+1]].map(id=>level.cells.find(x=>x.id===id));
|
||||
return b.x-a.x===c.x-b.x&&b.y-a.y===c.y-b.y?'straight':'turn';
|
||||
}
|
||||
export function violations(level,path,{omit=null,complete=path.length===level.cells.length,ignoreClues=false}={}){
|
||||
const positions=new Map(path.map((id,i)=>[id,i+1])),bad=[];
|
||||
if(!ignoreClues)for(const c of level.cells)if(c.clue!==undefined){const at=positions.get(c.id);if(at!==undefined&&at!==c.clue||path.length>=c.clue&&path[c.clue-1]!==c.id)bad.push({key:'clue:'+c.id,cells:[c.id,path[c.clue-1]].filter(Boolean),message:'固定数字步号不符'});}
|
||||
for(const r of constraints(level)){
|
||||
if(r.key===omit)continue;
|
||||
let fail=false,where=[];
|
||||
if(r.kind==='relation'){
|
||||
const a=positions.get(r.from),b=positions.get(r.to);where=[r.from,r.to];
|
||||
fail=b!==undefined&&(a===undefined||b-a!==r.delta)||a!==undefined&&b===undefined&&path.length>=a+r.delta;
|
||||
}else if(r.kind==='order'){where=[r.from,r.to];fail=positions.has(r.to)&&(!positions.has(r.from)||positions.get(r.from)>positions.get(r.to));}
|
||||
else if(r.kind==='window'){where=[r.cell];const p=positions.get(r.cell);fail=p!==undefined?(p<r.min||p>r.max):path.length>=r.max;}
|
||||
else if(r.kind==='direction'){where=[r.cell];const p=positions.get(r.cell);fail=p===1||p!==undefined&&p<path.length&&directionAt(level,path,p-1)!==r.value||complete&&p===path.length;}
|
||||
else if(r.kind==='alternation'){for(let i=1;i<path.length;i++){const a=level.cells.find(c=>c.id===path[i-1]),b=level.cells.find(c=>c.id===path[i]);if(a.category===b.category){fail=true;where.push(a.id,b.id);}}}
|
||||
else if(r.kind==='gate')for(let i=1;i<path.length;i++){if(r.edge.includes(path[i-1])&&r.edge.includes(path[i])){const s=positions.get(r.switchCell);if(s===undefined||s>i){fail=true;where=r.edge;}}}
|
||||
if(fail)bad.push({key:r.key,cells:where,message:r.kind==='relation'?'相对步差不符':r.kind==='order'?'标记次序不符':r.kind==='window'?'到达窗口不符':r.kind==='direction'?'进出方向不符':r.kind==='gate'?'开关尚未打开此门':'类别没有交替'});
|
||||
}
|
||||
return bad;
|
||||
}
|
||||
export function canAppend(level,path,id){
|
||||
const g=graph(level);if(path.includes(id)||!g.ids.has(id))return false;
|
||||
if(!path.length)return !g.clues.has(1)||g.cells[g.clues.get(1)].id===id;
|
||||
const last=path.at(-1);if(!g.adj[g.ids.get(last)].includes(g.ids.get(id)))return false;
|
||||
if(level.mode.includes('alternation')&&g.cells[g.ids.get(last)].category===g.cells[g.ids.get(id)].category)return false;
|
||||
for(const gate of level.gates||[])if(gate.edge.includes(last)&&gate.edge.includes(id)&&!path.includes(gate.switchCell))return false;
|
||||
return true;
|
||||
}
|
||||
export function solve(level,{limit=2,omit=null,ignoreClues=false,prune=true,nodeLimit=Infinity}={}){
|
||||
const g=graph(level),n=g.cells.length,results=[],path=[],seen=new Set();let nodes=0;
|
||||
const active=constraints(level).filter(r=>r.key!==omit),alternating=active.some(r=>r.kind==='alternation');
|
||||
const adj=g.adj.map((a,i)=>a.filter(j=>!alternating||g.cells[i].category!==g.cells[j].category));
|
||||
const forced=ignoreClues?new Map():g.clues;
|
||||
function connected(cur){const reached=new Set([cur]),queue=[cur];for(let k=0;k<queue.length;k++)for(const j of adj[queue[k]])if(!seen.has(j)&&!reached.has(j)){reached.add(j);queue.push(j);}return reached.size===n-path.length+1;}
|
||||
function visit(i){if(++nodes>nodeLimit)throw Error('求解节点预算耗尽,不能视为唯一');seen.add(i);path.push(g.cells[i].id);
|
||||
const k=path.length;
|
||||
if(!violations(level,path,{omit,ignoreClues}).length){
|
||||
if(k===n)results.push([...path]);
|
||||
else if(!prune||connected(i)){
|
||||
const f=forced.get(k+1);let next=adj[i].filter(j=>!seen.has(j)&&(f===undefined||j===f));
|
||||
next.sort((a,b)=>adj[a].filter(x=>!seen.has(x)).length-adj[b].filter(x=>!seen.has(x)).length);
|
||||
for(const j of next){if(!ignoreClues&&g.cells[j].clue!==undefined&&g.cells[j].clue!==k+1)continue;
|
||||
let blocked=false;for(const r of active)if(r.kind==='gate'&&r.edge.includes(g.cells[i].id)&&r.edge.includes(g.cells[j].id)&&!path.includes(r.switchCell))blocked=true;
|
||||
if(!blocked)visit(j);if(results.length>=limit)break;
|
||||
}
|
||||
}
|
||||
}
|
||||
path.pop();seen.delete(i);
|
||||
}
|
||||
let starts=forced.has(1)?[forced.get(1)]:g.cells.map((_,i)=>i);
|
||||
for(const i of starts){if(!ignoreClues&&g.cells[i].clue!==undefined&&g.cells[i].clue!==1)continue;visit(i);if(results.length>=limit)break;}
|
||||
return results;
|
||||
}
|
||||
export function hint(solution,path){let k=0;while(k<path.length&&path[k]===solution[k])k++;if(k<path.length)return {kind:k?'backtrack':'restart',cell:k?path[k-1]:solution[0]};return k===solution.length?{kind:'none'}:{kind:k?'next':'start',cell:solution[k]};}
|
||||
export function assertStructure(level){
|
||||
const g=graph(level),n=g.cells.length;if(n<3||n>20)throw Error('格数须为3–20');
|
||||
if(level.geometry==='hex'){const keys=new Set();for(const c of g.cells){if(!Number.isInteger(c.q)||!Number.isInteger(c.r))throw Error('六边坐标');const key=c.q+','+c.r;if(keys.has(key))throw Error('六边重叠');keys.add(key);}}
|
||||
else for(const c of g.cells){if(!Number.isInteger(c.x)||!Number.isInteger(c.y))throw Error('坐标');const u=units(c);if(!u.length||new Set(u.map(p=>p.join(','))).size!==u.length)throw Error('异形重复');const seen=new Set([u[0].join(',')]);for(let changed=true;changed;){changed=false;for(const [x,y]of u)if(!seen.has(x+','+y)&&[[1,0],[-1,0],[0,1],[0,-1]].some(([a,b])=>seen.has((x+a)+','+(y+b)))){seen.add(x+','+y);changed=true;}}if(seen.size!==u.length)throw Error('异形不连通');}
|
||||
const reach=new Set([0]),queue=[0];for(let k=0;k<queue.length;k++)for(const j of g.adj[queue[k]])if(!reach.has(j)){reach.add(j);queue.push(j);}if(reach.size!==n)throw Error('盘面不连通');
|
||||
const pairs=[];
|
||||
for(const r of constraints(level)){
|
||||
for(const id of [r.cell,r.from,r.to,r.switchCell,...(r.edge||[])].filter(Boolean))if(!g.ids.has(id))throw Error('约束引用不存在');
|
||||
if(r.kind==='relation'&&(!Number.isInteger(r.delta)||r.delta<1||r.delta>=n||r.from===r.to))throw Error('步差');
|
||||
if(r.kind==='order')pairs.push([r.from,r.to]);
|
||||
if(r.kind==='window'&&(!Number.isInteger(r.min)||!Number.isInteger(r.max)||r.min<1||r.max>n||r.max-r.min<2||g.cells[g.ids.get(r.cell)].clue!==undefined))throw Error('窗口需允许至少两个同奇偶步号且不与数字重叠');
|
||||
if(r.kind==='direction'&&!['straight','turn'].includes(r.value))throw Error('方向');
|
||||
if(r.kind==='gate'&&(r.edge.length!==2||!g.adj[g.ids.get(r.edge[0])].includes(g.ids.get(r.edge[1]))))throw Error('门不在共边');
|
||||
}
|
||||
if((level.gates||[]).length>3||new Set((level.gates||[]).map(r=>r.switchCell)).size!==(level.gates||[]).length)throw Error('最多三门且开关一对一');
|
||||
if(level.mode.includes('alternation')&&g.cells.some(c=>!['circle','triangle'].includes(c.category)))throw Error('缺少类别');
|
||||
function cycle(v,stack=new Set(),done=new Set()){if(stack.has(v))throw Error('先后链循环');if(done.has(v))return;stack.add(v);for(const [a,b]of pairs)if(a===v)cycle(b,stack,done);stack.delete(v);done.add(v);}for(const [a]of pairs)cycle(a);
|
||||
return g;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import {definitions,difficulties} from './definitions.mjs';
|
||||
import {solve,violations,constraints,directionAt,assertStructure,graph} from './engine-template.mjs';
|
||||
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
||||
let seed=20261012;function random(){seed=(Math.imul(seed,1664525)+1013904223)>>>0;return seed/4294967296;}function pick(a){return a[Math.floor(random()*a.length)];}function shuffle(a){return [...a].sort(()=>random()-.5);}
|
||||
function square(index){const dims=[[3,2],[3,3],[4,3],[4,3],[4,4],[4,4],[4,5],[4,5]][index],removed=[[],[[2,2]],[[0,2],[3,2]],[],[[0,3],[3,3]],[],[[0,4],[3,4]],[[3,4]]][index];let cells=[];for(let y=0;y<dims[1];y++)for(let x=0;x<dims[0];x++)if(!removed.some(p=>p[0]===x&&p[1]===y))cells.push({id:'c'+cells.length,x,y,w:1,h:1});return cells;}
|
||||
function hex(index){if(index>=4){let coords=[];const heights=index===4?[2,2,2]:index===5?[3,2,2]:[3,2,3];for(let region=0;region<3;region++){for(let q=region*3;q<region*3+2;q++)for(let r=0;r<heights[region];r++)coords.push([q,r]);if(region<2)coords.push([region*3+2,0]);}if(index===7)coords.push([8,1]);return coords.map(([q,r],i)=>({id:'c'+i,q,r}));}const count=[6,8,11,12,14,16,18,19][index];let cells=[];for(let q=0;cells.length<count;q++)for(let r=0;r<2&&cells.length<count;r++)cells.push({id:'c'+cells.length,q,r});if(index===3){cells[5].r=2;cells[5].q=1;}return cells;}
|
||||
function blocks(index,variant){let cells=square(Math.min(index+1,7));if(index===7)cells.push({id:'extra',x:3,y:4,w:1,h:1});const owner=new Map(cells.map(c=>[c.x+','+c.y,c]));
|
||||
const merges=index<4?1:2;for(let m=0;m<merges;m++){
|
||||
const choices=cells.filter(c=>c.w===1&&c.h===1&&!c.shape&&owner.has((c.x+1)+','+c.y)).filter(c=>{let b=owner.get((c.x+1)+','+c.y);return b&&b!==c&&b.w===1&&b.h===1&&!b.shape;});if(!choices.length)break;
|
||||
const a=choices[(variant+m*3)%choices.length],b=owner.get((a.x+1)+','+a.y);a.w=2;cells=cells.filter(c=>c!==b);owner.set(b.x+','+b.y,a);
|
||||
}
|
||||
if(index>=4){const a=cells.find(c=>c.w===2&&!c.shape&&cells.some(b=>b.x===c.x&&b.y===c.y+1&&b.w===1&&b.h===1&&!b.shape));if(a){const b=cells.find(b=>b.x===a.x&&b.y===a.y+1&&b.w===1&&b.h===1&&!b.shape);a.shape=[[0,0],[1,0],[0,1]];a.h=2;cells=cells.filter(c=>c!==b);}}
|
||||
return cells.map((c,i)=>({...c,id:'c'+i}));}
|
||||
function setup(def,index,cells,author){const level={id:String(index+1).padStart(2,'0'),mode:def.id,geometry:def.geometry,difficulty:difficulties[Math.floor(index/2)],title:def.goals[index],caption:index<2?def.rule:'',cells:structuredClone(cells)};
|
||||
if(index!==7)level.cells.find(c=>c.id===author[0]).clue=1;
|
||||
const n=author.length;const at=i=>author[Math.max(0,Math.min(n-1,i))];
|
||||
if(def.id==='relative'){
|
||||
let pairs=index===0?[[1,4]]:index===1?[[1,5]]:index===2?[[2,n-3]]:index===3?[[1,Math.floor(n/2)],[Math.floor(n/2)+1,n-2]]:index===4?[[2,Math.floor(n/2)],[2,n-2]]:index===6?[[1,Math.floor(n/2)],[3,n-2],[Math.floor(n/2),n-1]]:[[2,Math.floor(n/2)+1],[Math.floor(n/2)-1,n-2]];
|
||||
if(random()<.6&&index>1)pairs=pairs.map(([a,b])=>[Math.max(1,a+Math.floor(random()*2)),Math.min(n-1,b)]);
|
||||
if(index===1){const g=graph(level),near=[];for(let a=1;a<n-2;a++)for(let b=a+3;b<n;b++)if(g.adj[g.ids.get(at(a))].includes(g.ids.get(at(b))))near.push([a,b]);if(!near.length)return null;pairs=[pick(near)];}level.relations=pairs.map(([a,b])=>({from:at(a),to:at(b),delta:b-a}));
|
||||
}else if(def.id==='order'){
|
||||
const offsets=index===0?[1,4]:index===1?[1,n-2]:[1,Math.floor(n/2),n-2];
|
||||
level.orderChains=index<3?[offsets.map(at)]:index===3?[[at(1),at(Math.floor(n/2))],[at(Math.floor(n/2)+1),at(n-2)]]:index===4?[[at(1),at(Math.floor(n/2))],[at(3),at(Math.floor(n/2))]]:index>=6?[[at(1),at(Math.floor(n/2)),at(n-2)],[at(3),at(Math.floor(n/2)+2),at(n-1)]]:[[at(2),at(Math.floor(n/2)),at(n-2)]];
|
||||
if(index===1){const g=graph(level),near=author.map((id,i)=>i).filter(i=>i>=3&&g.adj[g.ids.get(at(0))].includes(g.ids.get(at(i))));if(!near.length)return null;const b=pick(near);level.orderChains=[[at(1+Math.floor(random()*(b-1))),at(b)]];}if(index>=6){const markers=shuffle(Array.from({length:n-2},(_,i)=>i+1)).slice(0,6).sort((a,b)=>a-b);level.orderChains=[[at(markers[0]),at(markers[2]),at(markers[4])],[at(markers[1]),at(markers[3]),at(markers[5])]];}
|
||||
}else if(def.id==='window'){
|
||||
const count=index<3?1:index<6?2:3;let indices=shuffle(Array.from({length:n-3},(_,i)=>i+2)).slice(0,count);
|
||||
if(index===5)indices=[Math.floor(n/2)-1,Math.floor(n/2)+1];
|
||||
level.windows=indices.map(i=>{const span=index<2?2:index===4?4:2+Math.floor(random()*3);const lo=Math.max(1,Math.min(n-span,i+1-Math.floor(random()*(span+1))));return {cell:at(i),min:lo,max:lo+span};});
|
||||
}else if(def.id==='direction'){
|
||||
const straight=[],turn=[];for(let i=1;i<n-1;i++)(directionAt(level,author,i)==='straight'?straight:turn).push(author[i]);
|
||||
let pattern=index===0?['straight']:index===1?['turn']:index===2?['straight','straight']:index===3?['straight','turn']:index<6?['straight','turn']:['straight','turn','turn'];
|
||||
for(const kind of pattern){const arr=kind==='straight'?straight:turn;if(!arr.length)return null;const id=pick(arr);arr.splice(arr.indexOf(id),1);level.cells.find(c=>c.id===id).direction=kind;}
|
||||
}else if(def.id.includes('alternation'))for(let i=0;i<n;i++)level.cells.find(c=>c.id===author[i]).category=i%2?'triangle':'circle';
|
||||
else if(def.id==='gates'){
|
||||
let count=index<3?1:index<6?2:3;const gates=[];const used=new Set();
|
||||
const crossed=[];for(let j=0;j<count;j++){let e=Math.floor((j+1)*n/(count+1))+Math.floor(random()*2);e=Math.min(n-2,Math.max(3,e));if(crossed.includes(e))return null;crossed.push(e);let low=(index===4||index===6)?(j?crossed[j-1]+1:1):1;if(index===7&&j>0)low=crossed[0]+1;let high=e-1;if(index===3&&j>0)high=Math.min(high,crossed[0]-1);if(index===7&&j===2)high=Math.min(high,crossed[1]-1);let candidates=Array.from({length:Math.max(0,high-low+1)},(_,k)=>low+k).filter(k=>!used.has(k));if(!candidates.length)return null;let s=index===7&&j===2?pick(candidates.filter(k=>k>crossed[0]) .length?candidates.filter(k=>k>crossed[0]):candidates):pick(candidates);used.add(s);gates.push({edge:[at(e),at(e+1)],switchCell:at(s)});}
|
||||
level.gates=gates;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
function selectClues(level,author,base,index){
|
||||
let paths=base;if(index!==7)paths=paths.filter(p=>p[0]===author[0]);
|
||||
const atoms=constraints(level),keys=atoms.map(a=>a.key);if(!keys.length)return null;
|
||||
const masks=paths.map(p=>new Set(violations(level,p,{ignoreClues:true}).map(v=>v.key)));
|
||||
let survivors=paths.map((_,i)=>i).filter(i=>masks[i].size===0);
|
||||
let witnesses=keys.map(key=>paths.map((_,i)=>i).filter(i=>masks[i].size===1&&masks[i].has(key)));
|
||||
if(witnesses.some(w=>!w.length))return null;if(level.mode==='window'&&index===1){witnesses=witnesses.map((w,j)=>w.filter(i=>{const r=level.windows[j];return paths[i].indexOf(r.cell)+1<r.min;}));if(witnesses.some(w=>!w.length))return null;}
|
||||
const forbidden=new Set([...(level.relations||[]).flatMap(r=>[r.from,r.to]),...(level.windows||[]).map(w=>w.cell)]);
|
||||
const candidates=shuffle(author.map((id,i)=>({id,step:i+1})).filter(c=>!forbidden.has(c.id)&&c.step!==1&&c.step!==author.length));
|
||||
let chosen=[];while(survivors.length>1){let best=null;
|
||||
for(const c of candidates.filter(c=>!chosen.includes(c))){let matched=i=>paths[i][c.step-1]===c.id;const kept=survivors.filter(matched);if(kept.length===survivors.length)continue;const ws=witnesses.map(w=>w.filter(matched));if(ws.some(w=>!w.length))continue;
|
||||
if(!best||kept.length<best.kept.length)best={c,kept,ws};
|
||||
}
|
||||
if(!best)return null;chosen.push(best.c);survivors=best.kept;witnesses=best.ws;
|
||||
if(chosen.length> (index<2?3:index<6?4:5))return null;
|
||||
}
|
||||
for(const c of chosen)level.cells.find(x=>x.id===c.id).clue=c.step;
|
||||
if(survivors.length!==1||paths[survivors[0]].join()!==author.join())return null;
|
||||
// Remove individually redundant numerical clues, keeping entry start teaching.
|
||||
for(const c of shuffle(level.cells.filter(c=>c.clue!==undefined&&(c.clue!==1||index===7)))){const old=c.clue;delete c.clue;const remaining=paths.filter(p=>!violations(level,p).length);if(remaining.length!==1)c.clue=old;}
|
||||
const finalWitnesses=keys.map(key=>{const p=paths.find(p=>!violations(level,p,{omit:key}).length&&violations(level,p).length);return {constraint:key,path:p};});
|
||||
if(finalWitnesses.some(w=>!w.path))return null;
|
||||
return finalWitnesses;
|
||||
}
|
||||
const cache=new Map();function basePaths(def,index,variant){const cells=def.geometry==='hex'?hex(index):def.geometry==='blocks'?blocks(index,variant):square(index);let key=JSON.stringify(cells);if(!cache.has(key)){const blank={mode:'base',geometry:def.geometry,cells};let paths;try{paths=solve(blank,{limit:60001,nodeLimit:8000000});}catch{return null;}if(paths.length>60000||!paths.length)return null;cache.set(key,{cells,paths});}return cache.get(key);}
|
||||
const archive=[];
|
||||
for(const def of definitions){const folder=path.join(root,'modes',def.id);fs.mkdirSync(folder,{recursive:true});fs.copyFileSync(path.join(root,'tools','engine-template.mjs'),path.join(folder,'rules.mjs'));let levels=[],notes=[];
|
||||
for(let index=0;index<8;index++){
|
||||
let accepted=null;for(let attempt=0;attempt<2400;attempt++){
|
||||
const base=basePaths(def,index,Math.floor(attempt/120));if(!base)continue;
|
||||
const author=pick(base.paths),level=setup(def,index,base.cells,author);if(!level)continue;
|
||||
const witnesses=selectClues(level,author,base.paths,index);if(!witnesses)continue;
|
||||
try{assertStructure(level);}catch{continue;}
|
||||
const solution=solve(level,{limit:2});if(solution.length!==1)throw Error('筛选与求解不一致');
|
||||
accepted={level,author,witnesses,basePathCount:base.paths.length,attempt};break;
|
||||
}
|
||||
if(!accepted)throw Error('无法生成 '+def.id+' '+(index+1));
|
||||
const {level,author,witnesses}=accepted;levels.push(level);
|
||||
const breakdown=constraints(level).map(r=>{if(r.kind==='relation')return `${r.from}→${r.to}:${r.delta} 次移动,作者步号 ${author.indexOf(r.from)+1}→${author.indexOf(r.to)+1}`;if(r.kind==='order')return `${r.from} 先于 ${r.to},作者步号 ${author.indexOf(r.from)+1}<${author.indexOf(r.to)+1}`;if(r.kind==='window')return `${r.cell} 窗口 ${r.min}–${r.max},作者第 ${author.indexOf(r.cell)+1} 步到达`;if(r.kind==='direction')return `${r.cell} 在第 ${author.indexOf(r.cell)+1} 步,必须${r.value==='straight'?'直行':'转弯'}`;if(r.kind==='gate')return `先在第 ${author.indexOf(r.switchCell)+1} 步踩 ${r.switchCell},第 ${Math.max(...r.edge.map(id=>author.indexOf(id)))+1} 步跨门 ${r.edge.join('—')}`;return '○/△每步交替,按节点数量推断端点类别';});
|
||||
const keyWitness=witnesses[0],fork=keyWitness.path.findIndex((id,i)=>id!==author[i]);
|
||||
notes.push({...accepted,breakdown,keyFork:{step:fork+1,correct:author[fork],tempting:keyWitness.path[fork]},reason:violations(level,keyWitness.path).map(v=>v.message).join(';'),review:'自动验证通过;难度与人类推理顺序待真人试玩确认'});
|
||||
console.log(def.id,level.id,level.difficulty,'N='+level.cells.length,'constraints='+constraints(level).length,'clues='+level.cells.filter(c=>c.clue!==undefined).length,'attempt='+accepted.attempt);
|
||||
}
|
||||
fs.writeFileSync(path.join(folder,'levels.json'),JSON.stringify({mode:def.id,title:def.title,rule:def.rule,levels},null,2));
|
||||
fs.writeFileSync(path.join(folder,'design.json'),JSON.stringify(notes,null,2));archive.push({mode:def.id,title:def.title,count:levels.length});
|
||||
let md=`# ${def.title}\n\n${def.rule}\n\n固定数字表示必须在该步到达;每格一次、共边移动、完整路线唯一。所有关卡开放。\n\n这些是机制验证候选关,难度标签按设计任务编排,尚未经真人校准。\n`;
|
||||
for(const item of notes){const l=item.level;md+=`\n## ${l.id} · ${l.difficulty} · ${l.title}\n\n格数 ${l.cells.length};基础完整路线 ${item.basePathCount} 条;新约束 ${constraints(l).length} 项。\n\n作者解:${item.author.join(' → ')}。\n\n推理检查点:\n\n${item.breakdown.map(s=>'- '+s).join('\n')}\n\n关键分叉:第 ${item.keyFork.step} 步应选 ${item.keyFork.correct},解除机制后的诱惑路线选择 ${item.keyFork.tempting}。排除依据:${item.reason}。\n\n诱惑路线:${item.witnesses[0].path.join(' → ')}。这是符合几何与辅助数字的完整路线,由新机制排除。\n\n逐项必要性证据:\n\n${item.witnesses.map(w=>'- 解除 '+w.constraint+' 后可走 '+w.path.join(' → ')).join('\n')}\n\n人工评审:请先隐藏作者解,依据盘面复述排除理由;记录是否主要依靠试错。\n`;}
|
||||
fs.writeFileSync(path.join(folder,'DESIGN.md'),md);
|
||||
}
|
||||
fs.writeFileSync(path.join(root,'reports','catalog.json'),JSON.stringify(archive,null,2));
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import fs from 'node:fs';import path from 'node:path';import crypto from 'node:crypto';import {fileURLToPath} from 'node:url';
|
||||
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..'),source='G:\\项目\\NumberSwipe';
|
||||
const before=JSON.parse(fs.readFileSync(path.join(root,'reports','original-before.json'),'utf8').replace(/^\uFEFF/,''));
|
||||
const hash=file=>crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex').toUpperCase();
|
||||
const current=[];function walk(dir){for(const item of fs.readdirSync(dir,{withFileTypes:true})){if(item.isDirectory()&&['.git','node_modules'].includes(item.name))continue;const full=path.join(dir,item.name);if(item.isDirectory())walk(full);else if(item.isFile())current.push(full);}}walk(source);
|
||||
const expected=new Set(before.map(r=>r.path)),changed=[],missing=[];for(const item of before){if(!fs.existsSync(item.path))missing.push(item.path);else if(hash(item.path)!==item.hash)changed.push(item.path);}const added=current.filter(p=>!expected.has(p));
|
||||
const report={checkedAt:new Date().toISOString(),source,checkedFiles:before.length,changed,missing,added,unchanged:!changed.length&&!missing.length&&!added.length,excluded:['.git','node_modules']};fs.writeFileSync(path.join(root,'reports','isolation.json'),JSON.stringify(report,null,2));
|
||||
fs.writeFileSync(path.join(root,'reports','ISOLATION.md'),`# 原项目隔离校验\n\n核对 ${before.length} 个原项目文件的SHA-256与文件清单。\n\n修改 ${changed.length},删除 ${missing.length},新增 ${added.length}。\n\n${report.unchanged?'通过:原项目受检查文件全部保持一致。':'检测到外部修改,请查看isolation.json。'}\n\n.git与node_modules未纳入内容遍历;本任务未执行原项目Git写入或依赖操作。所有任务输出都在NumberSwipe-Variants。\n`);
|
||||
console.log(JSON.stringify(report));if(!report.unchanged)process.exitCode=1;
|
||||
@@ -0,0 +1,70 @@
|
||||
import {graph,units,solve,violations,canAppend,hint} from './rules.mjs';
|
||||
// The build embeds this module together with the local rule file and JSON.
|
||||
const pack=PACK;
|
||||
const $=id=>document.getElementById(id),canvas=$('board'),ctx=canvas.getContext('2d');
|
||||
let level,solution,path=[],index=0,selected='入门',polygons=[],centers=new Map(),unit=1,focus=[],drag=false,lastPoint=null,cleared=false;
|
||||
const memoryKey='variant:'+pack.mode;let completed=[];try{completed=JSON.parse(localStorage.getItem(memoryKey)||'[]');}catch{}
|
||||
const labels=new Map();const metrics={};let started=0,record=null;
|
||||
function setStatus(s){$('status').textContent=s;}
|
||||
function modeMarkers(){labels.clear();let ids=[...(level.relations||[]).flatMap(r=>[r.from,r.to]),...(level.orderChains||[]).flat()];for(const id of ids)if(!labels.has(id))labels.set(id,String.fromCharCode(65+labels.size));}
|
||||
function makeRecord(){return {mode:pack.mode,level:level.id,difficulty:level.difficulty,title:level.title,startedAt:new Date().toISOString(),elapsedSeconds:0,firstError:null,backtracks:0,restarts:0,hints:0,completed:false,reasoning:''};}
|
||||
function start(i){index=i;level=pack.levels[i];solution=solve(level)[0];if(!solution)throw Error('没有合法路线');path=[];focus=[];cleared=false;started=performance.now();record=makeRecord();$('reasoning').value='';modeMarkers();$('goal').textContent=level.title;$('caption').textContent=level.caption||'先观察,再画;错了可以退回。';$('title').textContent=pack.title;$('rule-text').textContent=pack.rule;setStatus('从数字 1 开始;没有 1 时,可尝试任意格。');renderConstraints();renderSelectors();draw();}
|
||||
function renderSelectors(){
|
||||
$('difficulties').replaceChildren();for(const d of ['入门','普通','进阶','难']){const b=document.createElement('button');b.textContent=d;b.className=d===selected?'active':'';b.onclick=()=>{selected=d;start(pack.levels.findIndex(l=>l.difficulty===d));};$('difficulties').append(b);}
|
||||
$('levels').replaceChildren();for(let i=0;i<pack.levels.length;i++){const l=pack.levels[i];if(l.difficulty!==selected)continue;const b=document.createElement('button');b.textContent=`第 ${i%2+1} 关${completed.includes(l.id)?' ✓':''}`;b.className=i===index?'active':'';b.onclick=()=>start(i);$('levels').append(b);}
|
||||
$('position').textContent=`${level.difficulty} · ${index%2+1}/2`;
|
||||
}
|
||||
function renderConstraints(){const list=$('constraints');list.replaceChildren();
|
||||
const add=(text,ids)=>{const b=document.createElement('button');b.className='constraint';b.textContent=text;b.onclick=()=>{focus=ids;draw();};list.append(b);};
|
||||
for(const r of level.relations||[])add(`${labels.get(r.from)} → ${labels.get(r.to)} +${r.delta}`,[r.from,r.to]);
|
||||
for(const chain of level.orderChains||[])add(chain.map(id=>labels.get(id)).join(' < '),chain);
|
||||
for(const w of level.windows||[])add(`窗口 ${w.min}–${w.max}`,[w.cell]);
|
||||
for(const c of level.cells)if(c.direction)add(c.direction==='straight'?'↔ 必须直行':'↱ 必须转弯',[c.id]);
|
||||
if(level.mode.includes('alternation'))add('○ → △ → ○ · 每步换一类',[]);
|
||||
for(let i=0;i<(level.gates||[]).length;i++)add(`开关 ${i+1} → 门 ${i+1}`,[level.gates[i].switchCell,...level.gates[i].edge]);
|
||||
}
|
||||
function localGeometry(){
|
||||
const raw=[],center=new Map();for(const c of level.cells){if(level.geometry==='hex'){
|
||||
// Rotate the axial strip into portrait orientation; adjacency remains axial.
|
||||
const x=1.5*c.r,y=Math.sqrt(3)*(c.q+c.r/2);center.set(c.id,[x,y]);let p=[];for(let a=0;a<6;a++){const angle=a*Math.PI/3;p.push([x+Math.cos(angle),y+Math.sin(angle)]);}raw.push({id:c.id,p});
|
||||
}else{const u=units(c);center.set(c.id,[u.reduce((s,p)=>s+p[0]+.5,0)/u.length,u.reduce((s,p)=>s+p[1]+.5,0)/u.length]);for(const [x,y]of u)raw.push({id:c.id,p:[[x,y],[x+1,y],[x+1,y+1],[x,y+1]]});}}
|
||||
return {raw,center};
|
||||
}
|
||||
function draw(){if(!level)return;const width=canvas.clientWidth,height=canvas.clientHeight,dpr=window.devicePixelRatio||1;canvas.width=Math.round(width*dpr);canvas.height=Math.round(height*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,width,height);
|
||||
const {raw,center}=localGeometry(),all=raw.flatMap(r=>r.p),xmin=Math.min(...all.map(p=>p[0])),xmax=Math.max(...all.map(p=>p[0])),ymin=Math.min(...all.map(p=>p[1])),ymax=Math.max(...all.map(p=>p[1]));unit=Math.min((width-28)/(xmax-xmin),(height-24)/(ymax-ymin));const ox=(width-unit*(xmax-xmin))/2-unit*xmin,oy=(height-unit*(ymax-ymin))/2-unit*ymin;
|
||||
const tr=([x,y])=>[ox+x*unit,oy+y*unit];polygons=raw.map(r=>({...r,p:r.p.map(tr)}));centers=new Map([...center].map(([id,p])=>[id,tr(p)]));
|
||||
const bad=violations(level,path),badIds=new Set(bad.flatMap(b=>b.cells));
|
||||
for(const shape of polygons){const c=level.cells.find(c=>c.id===shape.id),visited=path.includes(c.id);ctx.beginPath();shape.p.forEach(([x,y],i)=>i?ctx.lineTo(x,y):ctx.moveTo(x,y));ctx.closePath();ctx.fillStyle=badIds.has(c.id)?'#ffe0dc':visited?'#ddd8ff':c.category==='circle'?'#e4f3ff':c.category==='triangle'?'#fff0d4':'#f0f0f6';ctx.fill();}
|
||||
// Draw only external unit edges, preserving the outline of an entire large block.
|
||||
const edgeCounts=new Map();for(const shape of polygons)for(let i=0;i<shape.p.length;i++){let a=shape.p[i],b=shape.p[(i+1)%shape.p.length];let key=shape.id+':'+[a.map(v=>v.toFixed(3)).join(','),b.map(v=>v.toFixed(3)).join(',')].sort().join('|');edgeCounts.set(key,(edgeCounts.get(key)||0)+1);}
|
||||
for(const shape of polygons)for(let i=0;i<shape.p.length;i++){const a=shape.p[i],b=shape.p[(i+1)%shape.p.length],key=shape.id+':'+[a.map(v=>v.toFixed(3)).join(','),b.map(v=>v.toFixed(3)).join(',')].sort().join('|');if(edgeCounts.get(key)>1)continue;ctx.beginPath();ctx.moveTo(...a);ctx.lineTo(...b);ctx.strokeStyle=focus.includes(shape.id)?'#f59c00':badIds.has(shape.id)?'#c74537':path.includes(shape.id)?'#7762db':'#b4b6cb';ctx.lineWidth=focus.includes(shape.id)?4:2;ctx.stroke();}
|
||||
if(path.length){ctx.beginPath();path.forEach((id,i)=>{const p=centers.get(id);i?ctx.lineTo(...p):ctx.moveTo(...p);});ctx.lineWidth=5;ctx.lineCap='round';ctx.lineJoin='round';ctx.strokeStyle='#7562db';ctx.globalAlpha=.32;ctx.stroke();ctx.globalAlpha=1;}
|
||||
for(let i=0;i<(level.gates||[]).length;i++){const gate=level.gates[i],a=centers.get(gate.edge[0]),b=centers.get(gate.edge[1]),mx=(a[0]+b[0])/2,my=(a[1]+b[1])/2,dx=b[0]-a[0],dy=b[1]-a[1],len=Math.hypot(dx,dy),open=path.includes(gate.switchCell);ctx.save();ctx.translate(mx,my);ctx.rotate(Math.atan2(dy,dx));ctx.beginPath();ctx.moveTo(0,-unit*.34);ctx.lineTo(0,unit*.34);ctx.lineWidth=7;ctx.strokeStyle=open?'#349375':'#d98123';if(open)ctx.setLineDash([3,5]);ctx.stroke();ctx.restore();ctx.fillStyle=open?'#25745c':'#8c4917';ctx.font='bold 12px system-ui';ctx.textAlign='center';ctx.fillText(String(i+1),mx+9,my-8);}
|
||||
for(const c of level.cells){const [x,y]=centers.get(c.id),step=path.indexOf(c.id)+1;const font=Math.max(14,Math.min(24,unit*.34));let symbol=labels.get(c.id)||'';const w=(level.windows||[]).find(w=>w.cell===c.id);if(w)symbol=`${w.min}–${w.max}`;if(c.direction)symbol=c.direction==='straight'?'↔':'↱';if(c.category)symbol=c.category==='circle'?'○':'△';const gateIndex=(level.gates||[]).findIndex(g=>g.switchCell===c.id);if(gateIndex!==-1)symbol=`开${gateIndex+1}`;
|
||||
ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillStyle=badIds.has(c.id)?'#a33127':'#30304e';ctx.font=`700 ${font}px system-ui`;if(step){ctx.fillText(String(step),x,y+ (symbol?5:0));if(symbol){ctx.font=`600 ${Math.max(11,font*.65)}px system-ui`;ctx.fillText(symbol,x,y-font*.65);}}else if(symbol)ctx.fillText(symbol,x,y);
|
||||
if(c.clue!==undefined){if(!step&&!symbol)ctx.fillText(String(c.clue),x,y);else{ctx.font=`700 ${Math.max(11,font*.65)}px system-ui`;ctx.fillText('需'+c.clue,x,y+font*.8);}}
|
||||
}
|
||||
$('remaining').textContent=cleared?'完成 ✓':`剩余 ${level.cells.length-path.length} 格`;
|
||||
if(bad.length)setStatus(bad[0].message+',可以点已走格退回。');
|
||||
$('undo').disabled=!path.length;$('next').hidden=!cleared||index===7;
|
||||
}
|
||||
function inside(x,y,p){let hit=false;for(let i=0,j=p.length-1;i<p.length;j=i++){const [a,b]=p[i],[c,d]=p[j];if((b>y)!==(d>y)&&x<(c-a)*(y-b)/(d-b)+a)hit=!hit;}return hit;}
|
||||
function hit(x,y){return polygons.find(s=>inside(x,y,s.p))?.id;}
|
||||
function markError(id,message){if(!record.firstError)record.firstError={step:path.length+1,cell:id,message};setStatus(message);}
|
||||
function advance(id){if(!id||cleared)return;const at=path.indexOf(id);if(at!==-1){if(at<path.length-1){record.backtracks++;path=path.slice(0,at+1);focus=[];setStatus('已回退,继续观察剩余格。');draw();}return;}
|
||||
if(!canAppend(level,path,id)){const g=graph(level),adjacent=path.length&&g.adj[g.ids.get(path.at(-1))].includes(g.ids.get(id));if(adjacent||!path.length){markError(id,path.length?'此处类别相同或门尚未打开。':'请从数字 1 开始。');}return;}
|
||||
if(!path.length)started=performance.now();path.push(id);focus=[];const bad=violations(level,path);if(bad.length)markError(id,bad[0].message);else setStatus('可松手续画;点已走格可回退。');if(path.length===level.cells.length&&!bad.length){cleared=true;record.completed=true;record.elapsedSeconds=Math.round((performance.now()-started)/1000);if(!completed.includes(level.id)){completed.push(level.id);try{localStorage.setItem(memoryKey,JSON.stringify(completed));}catch{}}setStatus('完成!可以进入下一关,或记录你的推理。');renderSelectors();}draw();}
|
||||
function point(e){const r=canvas.getBoundingClientRect();return [e.clientX-r.left,e.clientY-r.top];}
|
||||
canvas.addEventListener('pointerdown',e=>{e.preventDefault();drag=true;canvas.setPointerCapture(e.pointerId);lastPoint=point(e);advance(hit(...lastPoint));});
|
||||
canvas.addEventListener('pointermove',e=>{if(!drag)return;e.preventDefault();const p=point(e),a=lastPoint||p,d=Math.hypot(p[0]-a[0],p[1]-a[1]),steps=Math.ceil(d/Math.max(3,unit*.1));for(let i=1;i<=steps;i++)advance(hit(a[0]+(p[0]-a[0])*i/steps,a[1]+(p[1]-a[1])*i/steps));lastPoint=p;});
|
||||
function end(){drag=false;lastPoint=null;}canvas.addEventListener('pointerup',end);canvas.addEventListener('pointercancel',end);
|
||||
$('undo').onclick=()=>{if(path.length){path.pop();record.backtracks++;record.completed=false;cleared=false;focus=[];setStatus('已撤销一步。');draw();}};
|
||||
$('restart').onclick=()=>{path=[];cleared=false;focus=[];record.restarts++;record.completed=false;started=performance.now();setStatus('已重开。');draw();};
|
||||
$('hint').onclick=()=>{record.hints++;const h=hint(solution,path);focus=h.kind==='none'?[]:[h.cell];setStatus(h.kind==='backtrack'?'请退回黄色边框格,再继续。':h.kind==='restart'?'起点需要调整,请重开并从黄色边框格开始。':h.kind==='none'?'已经完成。':'下一步在黄色边框格;提示不会自动画路。');draw();};
|
||||
$('next').onclick=()=>{selected=pack.levels[index+1].difficulty;start(index+1);};
|
||||
$('export').onclick=()=>{record.elapsedSeconds=record.completed?record.elapsedSeconds:Math.round((performance.now()-started)/1000);record.reasoning=$('reasoning').value;const blob=new Blob([JSON.stringify(record,null,2)],{type:'application/json'}),a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=pack.mode+'-'+level.id+'-试玩记录.json';a.click();setTimeout(()=>URL.revokeObjectURL(a.href),1000);};
|
||||
window.addEventListener('resize',draw);start(0);
|
||||
// Read-only snapshot used for interaction verification; never supplies an answer.
|
||||
window.variantSnapshot=()=>({mode:pack.mode,level:level.id,path:[...path],violations:violations(level,path),cleared,centers:Object.fromEntries(centers),unit,openedGates:(level.gates||[]).map(g=>path.includes(g.switchCell))});
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import fs from 'node:fs';import path from 'node:path';import {fileURLToPath} from 'node:url';import {definitions} from './definitions.mjs';
|
||||
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');const result=[];let total=0;
|
||||
for(const def of definitions){const folder=path.join(root,'modes',def.id),rules=await import('../modes/'+def.id+'/rules.mjs'),pack=JSON.parse(fs.readFileSync(path.join(folder,'levels.json'),'utf8')),notes=JSON.parse(fs.readFileSync(path.join(folder,'design.json'),'utf8'));if(pack.levels.length!==8)throw Error('必须8关');const ids=new Set();
|
||||
for(let i=0;i<8;i++){const l=pack.levels[i];if(ids.has(l.id))throw Error('重复关id');ids.add(l.id);rules.assertStructure(l);if(l.difficulty!==['入门','普通','进阶','难'][Math.floor(i/2)])throw Error('难度顺序');
|
||||
const paths=rules.solve(l,{limit:2}),author=notes[i].author;if(paths.length!==1)throw Error('非唯一 '+def.id+' '+l.id);if(paths[0].join()!==author.join())throw Error('作者解不匹配');
|
||||
const necessary=[];for(const r of rules.constraints(l)){const alternatives=rules.solve(l,{limit:2,omit:r.key});if(alternatives.length<2)throw Error('冗余机制 '+def.id+' '+l.id+' '+r.key);const saved=notes[i].witnesses.find(w=>w.constraint===r.key)?.path;if(!saved||rules.violations(l,saved,{omit:r.key}).length||!rules.violations(l,saved).length)throw Error('必要性证据无效');necessary.push({constraint:r.key,solutionsAfterRemoval:'至少2',witness:saved});}
|
||||
const digital=[];for(const c of l.cells.filter(c=>c.clue!==undefined&&c.clue!==1)){const copy=structuredClone(l);delete copy.cells.find(x=>x.id===c.id).clue;const minimal=rules.solve(copy,{limit:2}).length>=2;if(!minimal)throw Error('冗余辅助数字');digital.push({cell:c.id,step:c.clue,necessary:minimal});}
|
||||
if(i<7&&!l.cells.some(c=>c.clue===1)||i===7&&l.cells.some(c=>c.clue===1))throw Error('起点教学策略不符');
|
||||
for(let k=0;k<author.length;k++)if(!rules.canAppend(l,author.slice(0,k),author[k]))throw Error('作者解与操作不一致');
|
||||
result.push({mode:def.id,title:def.title,level:l.id,difficulty:l.difficulty,n:l.cells.length,unique:true,mechanisms:necessary,digital,goal:l.title,authorPath:author,unknownStart:i===7});total++;}
|
||||
const html=fs.readFileSync(path.join(folder,'play.html'),'utf8');if(/<script[^>]+src=|fetch\(|type="module"/.test(html))throw Error('非单文件离线页面');if(html.includes('"author":')||html.includes('"witnesses":'))throw Error('作者资料进入试玩包');
|
||||
}
|
||||
const report={generatedAt:new Date().toISOString(),levels:total,passed:total,modeCount:definitions.length,uniqueAll:true,mechanismsNecessaryAll:true,humanDifficultyCalibrated:false,results:result};
|
||||
fs.writeFileSync(path.join(root,'reports','validation.json'),JSON.stringify(report,null,2));
|
||||
fs.writeFileSync(path.join(root,'reports','VALIDATION.md'),'# 关卡自动校验报告\n\n'+`通过 ${total}/${total} 关,7 个玩法,每档两关。每关完整有序解唯一,逐项解除新机制后均至少出现两条完整解。辅助数字除教学起点外逐项必要。\n\n`+'| 玩法 | 关 | 难度 | 格数 | 必要机制数 | 起点 |\n|---|---|---|---:|---:|---|\n'+result.map(r=>`| ${r.title} | ${r.level} | ${r.difficulty} | ${r.n} | ${r.mechanisms.length} | ${r.unknownStart?'未知':'给定'} |`).join('\n')+'\n\n详细作者解与机制解除反例见 validation.json。自动通过不代表真人难度或推理品质已校准。\n');
|
||||
console.log(`PASS ${total}/${total} levels; all mechanisms necessary; numeric clues minimized.`);
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 266.66666666666663" role="img" aria-label="认识允许范围"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2–4</text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text></svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 266.66666666666663" role="img" aria-label="认识允许范围"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="76.66666666666666,76.66666666666666 76.66666666666666,190 190,190 303.3333333333333,190 303.3333333333333,76.66666666666666 190,76.66666666666666" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4 · 2–4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="排除过早到达"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,246.66666666666666 133.33333333333331,246.66666666666666 133.33333333333331,360 20,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,246.66666666666666 246.66666666666666,246.66666666666666 246.66666666666666,360 133.33333333333331,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6–8</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="76.66666666666666" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="76.66666666666666" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="190" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="190" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text></svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="排除过早到达"><polygon points="20,20 133.33333333333331,20 133.33333333333331,133.33333333333331 20,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,20 246.66666666666666,20 246.66666666666666,133.33333333333331 133.33333333333331,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,20 360,20 360,133.33333333333331 246.66666666666666,133.33333333333331" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,133.33333333333331 133.33333333333331,133.33333333333331 133.33333333333331,246.66666666666666 20,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,133.33333333333331 246.66666666666666,133.33333333333331 246.66666666666666,246.66666666666666 133.33333333333331,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="246.66666666666666,133.33333333333331 360,133.33333333333331 360,246.66666666666666 246.66666666666666,246.66666666666666" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,246.66666666666666 133.33333333333331,246.66666666666666 133.33333333333331,360 20,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="133.33333333333331,246.66666666666666 246.66666666666666,246.66666666666666 246.66666666666666,360 133.33333333333331,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="190,76.66666666666666 303.3333333333333,76.66666666666666 303.3333333333333,190 190,190 190,303.3333333333333 76.66666666666666,303.3333333333333 76.66666666666666,190 76.66666666666666,76.66666666666666" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="76.66666666666666" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="76.66666666666666" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="190" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="190" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="303.3333333333333" y="68.66666666666666" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="303.3333333333333" y="86.66666666666666" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="76.66666666666666" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="76.66666666666666" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3 · 6–8</text><text x="190" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="190" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="303.3333333333333" y="182" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="303.3333333333333" y="200" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="76.66666666666666" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="76.66666666666666" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="190" y="295.3333333333333" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="190" y="313.3333333333333" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="窗口迫使先绕行"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6–10</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text></svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="窗口迫使先绕行"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="232.5,232.5 147.5,232.5 147.5,147.5 232.5,147.5 317.5,147.5 317.5,62.5 232.5,62.5 147.5,62.5 62.5,62.5 62.5,147.5" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">9</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0 · 6–10</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">10</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text></svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两个窗口限制早到与晚到"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">9–12</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3–5</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text></svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 295" role="img" aria-label="两个窗口限制早到与晚到"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polyline points="232.5,147.5 147.5,147.5 147.5,232.5 62.5,232.5 62.5,147.5 62.5,62.5 147.5,62.5 232.5,62.5 317.5,62.5 317.5,147.5 317.5,232.5 232.5,232.5" fill="none" stroke="#7963c9" stroke-width="5" opacity=".4"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">6</text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">7</text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">8</text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">9</text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">5</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">2</text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">10</text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7 · 9–12</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4</text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3</text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9 · 3–5</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">12</text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">11</text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380" role="img" aria-label="宽窗口与跨区顺序联动"><polygon points="20,20 105,20 105,105 20,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,20 190,20 190,105 105,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,20 275,20 275,105 190,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,20 360,20 360,105 275,105" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,105 105,105 105,190 20,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,105 190,105 190,190 105,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,105 275,105 275,190 190,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,105 360,105 360,190 275,190" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="20,190 105,190 105,275 20,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,190 190,190 190,275 105,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,190 275,190 275,275 190,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="275,190 360,190 360,275 275,275" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="105,275 190,275 190,360 105,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><polygon points="190,275 275,275 275,360 190,360" fill="#f1eef9" stroke="#aaa1c7" stroke-width="2"/><text x="62.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c0</text><text x="147.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c1</text><text x="232.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c2</text><text x="317.5" y="54.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="72.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c3</text><text x="62.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">4–8</text><text x="62.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c4</text><text x="147.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c5</text><text x="232.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">3–7</text><text x="232.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c6</text><text x="317.5" y="139.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="157.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c7</text><text x="62.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="62.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c8</text><text x="147.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c9</text><text x="232.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="232.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c10</text><text x="317.5" y="224.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="317.5" y="242.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c11</text><text x="147.5" y="309.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700"></text><text x="147.5" y="327.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c12</text><text x="232.5" y="309.5" text-anchor="middle" font-family="system-ui,Microsoft YaHei" font-size="21" font-weight="700">1</text><text x="232.5" y="327.5" text-anchor="middle" font-family="system-ui" font-size="10" fill="#665c78">c13</text></svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 5.7 KiB |