{"id":80455,"date":"2026-03-05T07:17:20","date_gmt":"2026-03-04T23:17:20","guid":{"rendered":"https:\/\/www.wsisp.com\/helps\/80455.html"},"modified":"2026-03-05T07:17:20","modified_gmt":"2026-03-04T23:17:20","slug":"deepseek%e6%b7%b1%e5%ba%a6%e8%ae%ad%e7%bb%83%e7%9a%84%e7%bd%91%e9%a1%b5%e5%b0%8f%e6%b8%b8%e6%88%8f-black-8%e9%ab%98%e7%ba%a7%e7%89%88%ef%bc%89","status":"publish","type":"post","link":"https:\/\/www.wsisp.com\/helps\/80455.html","title":{"rendered":"DeepSeek\u6df1\u5ea6\u8bad\u7ec3\u7684\u7f51\u9875\u5c0f\u6e38\u620f -- Black 8(\u9ad8\u7ea7\u7248\uff09"},"content":{"rendered":"<\/p>\n<p>Black 8 (Advanced)\u00a0&#8212; \u7ee7Black 8 Pro\u4e4b\u540e\u7ee7\u7eed\u6df1\u5ea6\u8bad\u7ec3\u7684\u4f5c\u54c1<\/p>\n<p>\u4e3b\u8981\u529f\u80fd\u63d0\u5347&#xff1a;<\/p>\n<p>\u589e\u5f3a\u4e86\u7403\u6746\u62c9\u5347\u52a8\u753b\u548c\u51fb\u7403\u97f3\u6548\u7b49&#xff0c;\u63d0\u5347\u6c89\u6d78\u5f0f\u4f53\u9a8c\u3002<\/p>\n<p>\u589e\u52a0\u4e86\u6311\u6218\u6a21\u5f0f&#xff0c;\u5b9e\u73b0\u66f4\u591a\u73a9\u6cd5\u3002<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" alt=\"\" height=\"829\" src=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260304231719-69a8bd7f59f5c.png\" width=\"855\" \/><\/p>\n<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html lang&#061;&#034;en&#034;&gt;<br \/>\n&lt;head&gt;<br \/>\n    &lt;meta charset&#061;&#034;UTF-8&#034;&gt;<br \/>\n    &lt;meta name&#061;&#034;viewport&#034; content&#061;&#034;width&#061;device-width, initial-scale&#061;1.0, maximum-scale&#061;1.0, user-scalable&#061;no&#034;&gt;<br \/>\n    &lt;meta name&#061;&#034;apple-mobile-web-app-capable&#034; content&#061;&#034;yes&#034;&gt;<br \/>\n    &lt;meta name&#061;&#034;apple-mobile-web-app-status-bar-style&#034; content&#061;&#034;black-translucent&#034;&gt;<br \/>\n    &lt;title&gt;Black 8 \u00b7 Back to Splash&lt;\/title&gt;<br \/>\n    &lt;style&gt;<br \/>\n        * {<br \/>\n            box-sizing: border-box;<br \/>\n            -webkit-tap-highlight-color: transparent;<br \/>\n            user-select: none;<br \/>\n        }<br \/>\n        html, body {<br \/>\n            margin: 0;<br \/>\n            padding: 0;<br \/>\n            width: 100%;<br \/>\n            height: 100%;<br \/>\n            overflow: hidden;<br \/>\n        }<br \/>\n        body {<br \/>\n            background: url(&#039;https:\/\/amitofoicu.github.io\/home\/lianchi6.jpg&#039;) no-repeat center center fixed;<br \/>\n            background-size: cover;<br \/>\n            display: flex;<br \/>\n            align-items: center;<br \/>\n            justify-content: center;<br \/>\n            font-family: &#039;Segoe UI&#039;, Roboto, system-ui, sans-serif;<br \/>\n            position: relative;<br \/>\n        }<br \/>\n        .loading-overlay {<br \/>\n            position: fixed;<br \/>\n            top: 0; left: 0; width: 100%; height: 100%;<br \/>\n            background: rgba(31, 34, 51, 0.95);<br \/>\n            display: flex;<br \/>\n            flex-direction: column;<br \/>\n            justify-content: center;<br \/>\n            align-items: center;<br \/>\n            z-index: 9999;<br \/>\n            transition: opacity 0.5s ease;<br \/>\n            font-size: 4vmin;<br \/>\n            color: #ffd966;<br \/>\n            backdrop-filter: blur(5px);<br \/>\n        }<br \/>\n        .spinner {<br \/>\n            width: 10vmin;<br \/>\n            height: 10vmin;<br \/>\n            border: 1vmin solid rgba(255,209,102,0.3);<br \/>\n            border-top-color: #ffd166;<br \/>\n            border-radius: 50%;<br \/>\n            animation: spin 1s linear infinite;<br \/>\n            margin-bottom: 2vmin;<br \/>\n        }<br \/>\n        &#064;keyframes spin { to { transform: rotate(360deg); } }<br \/>\n        .loading-text {<br \/>\n            font-size: 4vmin;<br \/>\n            margin-top: 2vmin;<br \/>\n        }<\/p>\n<p>        \/* splash screen (mode select) *\/<br \/>\n        .splash-screen {<br \/>\n            position: fixed;<br \/>\n            top: 0; left: 0; width: 100%; height: 100%;<br \/>\n            background: rgba(10, 20, 10, 0.9);<br \/>\n            backdrop-filter: blur(8px);<br \/>\n            display: flex;<br \/>\n            align-items: center;<br \/>\n            justify-content: center;<br \/>\n            z-index: 10000;<br \/>\n            transition: opacity 0.4s ease;<br \/>\n        }<br \/>\n        .splash-card {<br \/>\n            background: #2d4a2d;<br \/>\n            border: 4px solid #ffd966;<br \/>\n            border-radius: 8vmin;<br \/>\n            padding: 6vmin 8vmin;<br \/>\n            text-align: center;<br \/>\n            box-shadow: 0 20px 40px black;<br \/>\n            max-width: 600px;<br \/>\n            width: 85%;<br \/>\n        }<br \/>\n        .splash-title {<br \/>\n            color: #ffd966;<br \/>\n            font-size: 8vmin;<br \/>\n            font-weight: bold;<br \/>\n            margin-bottom: 5vmin;<br \/>\n            text-shadow: 3px 3px 0 #1f3a1f;<br \/>\n        }<br \/>\n        .mode-buttons {<br \/>\n            display: flex;<br \/>\n            gap: 4vmin;<br \/>\n            justify-content: center;<br \/>\n            flex-wrap: wrap;<br \/>\n        }<br \/>\n        .mode-btn {<br \/>\n            background: #3d5c3a;<br \/>\n            border: 3px solid #e3b87c;<br \/>\n            color: #ffefc0;<br \/>\n            font-size: 5vmin;<br \/>\n            font-weight: bold;<br \/>\n            padding: 3vmin 6vmin;<br \/>\n            border-radius: 6vmin;<br \/>\n            box-shadow: 0 5px 0 #1d2e1b;<br \/>\n            cursor: pointer;<br \/>\n            transition: 0.1s;<br \/>\n            min-width: 26vmin;<br \/>\n        }<br \/>\n        .mode-btn:active {<br \/>\n            transform: translateY(5px);<br \/>\n            box-shadow: 0 1px 0 #1d2e1b;<br \/>\n        }<br \/>\n        .mode-desc {<br \/>\n            color: #ddb87b;<br \/>\n            font-size: 3.5vmin;<br \/>\n            margin-top: 5vmin;<br \/>\n            border-top: 2px dashed #b88c4a;<br \/>\n            padding-top: 3vmin;<br \/>\n        }<\/p>\n<p>        .game-container {<br \/>\n            background: rgba(61, 43, 26, 0.85);<br \/>\n            backdrop-filter: blur(5px);<br \/>\n            padding: 1.5vmin 2vmin 2vmin 2vmin;<br \/>\n            border-radius: 4vmin;<br \/>\n            box-shadow: 0 20px 30px rgba(0,0,0,0.6), inset 2px 2px 8px #b87c4b;<br \/>\n            border: 2px solid #aa6e3a;<br \/>\n            position: relative;<br \/>\n            width: 95%;<br \/>\n            max-width: 820px;<br \/>\n            margin: 0 auto;<br \/>\n        }<br \/>\n        .game-header {<br \/>\n            display: flex;<br \/>\n            align-items: center;<br \/>\n            justify-content: space-between;<br \/>\n            margin-bottom: 1vmin;<br \/>\n        }<br \/>\n        .header-logo {<br \/>\n            height: 7vmin;<br \/>\n            width: auto;<br \/>\n            border-radius: 1vmin;<br \/>\n            border: 0.2vmin solid rgba(255,209,102,0.4);<br \/>\n            background: rgba(0,0,0,0.2);<br \/>\n            cursor: pointer;<br \/>\n        }<br \/>\n        .game-title {<br \/>\n            text-align: center;<br \/>\n            color: #ffd966;<br \/>\n            font-size: 5vmin;<br \/>\n            font-weight: bold;<br \/>\n            text-shadow: 3px 3px 0 #4f3a1e;<br \/>\n            letter-spacing: 2px;<br \/>\n            flex: 1;<br \/>\n        }<br \/>\n        canvas {<br \/>\n            display: block;<br \/>\n            width: 100%;<br \/>\n            height: auto;<br \/>\n            border-radius: 2.5vmin;<br \/>\n            background: #1e3b2a;<br \/>\n            box-shadow: inset 0 0 0 2px #7b5a3c, 0 10px 15px rgba(0,0,0,0.5);<br \/>\n            touch-action: none;<br \/>\n            cursor: crosshair;<br \/>\n        }<br \/>\n        .status-bar {<br \/>\n            display: flex;<br \/>\n            justify-content: space-between;<br \/>\n            align-items: center;<br \/>\n            padding: 2vmin 1vmin 1vmin 1vmin;<br \/>\n            color: #f7e9c3;<br \/>\n            text-shadow: 2px 2px 0 #4f3a1e;<br \/>\n            font-weight: bold;<br \/>\n            font-size: 5vmin;<br \/>\n        }<br \/>\n        .stroke-box {<br \/>\n            background: #2f4d2e;<br \/>\n            padding: 1vmin 5vmin;<br \/>\n            border-radius: 10vmin;<br \/>\n            border: 2px solid #dbb062;<br \/>\n            box-shadow: inset 0 2px 5px #0f2b0e;<br \/>\n            letter-spacing: 2px;<br \/>\n        }<br \/>\n        .bottom-panel {<br \/>\n            display: flex;<br \/>\n            align-items: center;<br \/>\n            justify-content: space-between;<br \/>\n            padding: 1vmin 1vmin 0 1vmin;<br \/>\n            gap: 2vmin;<br \/>\n            flex-wrap: wrap;<br \/>\n        }<br \/>\n        .power-meter {<br \/>\n            background: #5b4330;<br \/>\n            padding: 1vmin 3vmin;<br \/>\n            border-radius: 8vmin;<br \/>\n            border: 2px solid #edc27a;<br \/>\n            display: flex;<br \/>\n            align-items: center;<br \/>\n            gap: 2vmin;<br \/>\n            color: #ffdd99;<br \/>\n            font-size: 4vmin;<br \/>\n            font-weight: bold;<br \/>\n            flex: 1;<br \/>\n            min-width: 200px;<br \/>\n        }<br \/>\n        .bar-bg {<br \/>\n            width: 100%;<br \/>\n            height: 4vmin;<br \/>\n            background: #2a1e12;<br \/>\n            border-radius: 2vmin;<br \/>\n            border: 1px solid #ac8b5b;<br \/>\n            overflow: hidden;<br \/>\n        }<br \/>\n        .bar-fill {<br \/>\n            width: 20%;<br \/>\n            height: 100%;<br \/>\n            background: linear-gradient(90deg, #f9b81b, #f55d3e);<br \/>\n            transition: width 0.03s;<br \/>\n        }<br \/>\n        .button-group {<br \/>\n            display: flex;<br \/>\n            align-items: center;<br \/>\n            gap: 1.5vmin;<br \/>\n            flex-shrink: 0;<br \/>\n        }<br \/>\n        .action-btn {<br \/>\n            background: #3d5c3a;<br \/>\n            border: 2px solid #e3b87c;<br \/>\n            color: #ffefc0;<br \/>\n            font-size: 3.5vmin;<br \/>\n            padding: 0.8vmin 2.5vmin;<br \/>\n            border-radius: 5vmin;<br \/>\n            font-weight: bold;<br \/>\n            box-shadow: 0 3px 0 #1d2e1b;<br \/>\n            cursor: pointer;<br \/>\n            white-space: nowrap;<br \/>\n            min-width: 10vmin;<br \/>\n            text-align: center;<br \/>\n            transition: all 0.1s ease;<br \/>\n        }<br \/>\n        .action-btn:active {<br \/>\n            transform: translateY(3px);<br \/>\n            box-shadow: 0 1px 0 #1d2e1b;<br \/>\n        }<br \/>\n        .action-btn.mute {<br \/>\n            background: #5b4330;<br \/>\n            border-color: #edc27a;<br \/>\n            min-width: 8vmin;<br \/>\n            padding: 0.8vmin 1.5vmin;<br \/>\n        }<br \/>\n        .action-btn.next {<br \/>\n            background: #4a7a4a;<br \/>\n            border-color: #ffd966;<br \/>\n        }<br \/>\n        .hint {<br \/>\n            color: #ffd966;<br \/>\n            font-size: 3.2vmin;<br \/>\n            padding: 1vmin;<br \/>\n            background: #2d4a2d;<br \/>\n            border-radius: 4vmin;<br \/>\n            margin: 1vmin 0;<br \/>\n            text-align: center;<br \/>\n            border: 1px solid #b88c4a;<br \/>\n        }<br \/>\n        .again-overlay {<br \/>\n            position: absolute;<br \/>\n            top: 0; left: 0; width: 100%; height: 100%;<br \/>\n            background: rgba(0,0,0,0.4);<br \/>\n            display: flex;<br \/>\n            align-items: center;<br \/>\n            justify-content: center;<br \/>\n            z-index: 20;<br \/>\n            backdrop-filter: blur(3px);<br \/>\n            transition: opacity 0.3s;<br \/>\n        }<br \/>\n        .again-card {<br \/>\n            background: #3d5c3a;<br \/>\n            border: 4px solid #ffd966;<br \/>\n            border-radius: 8vmin;<br \/>\n            padding: 5vmin 10vmin;<br \/>\n            text-align: center;<br \/>\n            box-shadow: 0 10px 30px black;<br \/>\n        }<br \/>\n        .again-card .again-title {<br \/>\n            color: #ffd966;<br \/>\n            font-size: 7vmin;<br \/>\n            font-weight: bold;<br \/>\n            margin-bottom: 5vmin;<br \/>\n            text-shadow: 2px 2px 0 #1f3a1f;<br \/>\n        }<br \/>\n        .again-btn {<br \/>\n            background: #f7b731;<br \/>\n            border: none;<br \/>\n            color: #1e3b1e;<br \/>\n            font-size: 6vmin;<br \/>\n            font-weight: bold;<br \/>\n            padding: 2vmin 8vmin;<br \/>\n            border-radius: 6vmin;<br \/>\n            box-shadow: 0 5px 0 #8b5a2b;<br \/>\n            cursor: pointer;<br \/>\n            transition: 0.1s;<br \/>\n            border: 2px solid #ffe49e;<br \/>\n        }<br \/>\n        .again-btn:active {<br \/>\n            transform: translateY(5px);<br \/>\n            box-shadow: 0 1px 0 #8b5a2b;<br \/>\n        }<br \/>\n        .mode-indicator {<br \/>\n            background: #2d4a2d;<br \/>\n            padding: 1vmin 4vmin;<br \/>\n            border-radius: 5vmin;<br \/>\n            border: 1px solid #ffd966;<br \/>\n            font-size: 3.5vmin;<br \/>\n            color: #ffdba0;<br \/>\n        }<br \/>\n        .lives-container {<br \/>\n            display: inline-flex;<br \/>\n            align-items: center;<br \/>\n            gap: 1vmin;<br \/>\n            background: #3d2b1c;<br \/>\n            padding: 0.5vmin 3vmin;<br \/>\n            border-radius: 5vmin;<br \/>\n            border: 1px solid #edc27a;<br \/>\n            margin-left: 2vmin;<br \/>\n            font-size: 4vmin;<br \/>\n        }<br \/>\n        .heart {<br \/>\n            color: #ff6b6b;<br \/>\n            filter: drop-shadow(0 0 4px #ffaaaa);<br \/>\n        }<br \/>\n        &#064;media (max-width: 600px) {<br \/>\n            .status-bar { font-size: 6vmin; }<br \/>\n            .stroke-box { padding: 0.8vmin 4vmin; }<br \/>\n            .action-btn { font-size: 4vmin; padding: 0.6vmin 2vmin; }<br \/>\n            .bottom-panel { gap: 1vmin; }<br \/>\n            .power-meter { font-size: 3.5vmin; padding: 0.8vmin 2vmin; }<br \/>\n        }<br \/>\n    &lt;\/style&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<\/p>\n<p>&lt;!&#8211; loading overlay &#8211;&gt;<br \/>\n&lt;div class&#061;&#034;loading-overlay&#034; id&#061;&#034;loadingOverlay&#034;&gt;<br \/>\n    &lt;div class&#061;&#034;spinner&#034;&gt;&lt;\/div&gt;<br \/>\n    &lt;div class&#061;&#034;loading-text&#034;&gt;Loading Game&#8230;&lt;\/div&gt;<br \/>\n&lt;\/div&gt;<\/p>\n<p>&lt;!&#8211; splash screen: mode selection &#8211;&gt;<br \/>\n&lt;div class&#061;&#034;splash-screen&#034; id&#061;&#034;splashScreen&#034; style&#061;&#034;display: flex;&#034;&gt;<br \/>\n    &lt;div class&#061;&#034;splash-card&#034;&gt;<br \/>\n        &lt;div class&#061;&#034;splash-title&#034;&gt;&#x1f3b1; BLACK 8&lt;\/div&gt;<br \/>\n        &lt;div class&#061;&#034;mode-buttons&#034;&gt;<br \/>\n            &lt;button class&#061;&#034;mode-btn&#034; id&#061;&#034;practiceModeBtn&#034;&gt;- PRACTICE -&lt;\/button&gt;<br \/>\n            &lt;button class&#061;&#034;mode-btn&#034; id&#061;&#034;challengeModeBtn&#034;&gt;CHALLENGE&lt;\/button&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n        &lt;div class&#061;&#034;mode-desc&#034; id&#061;&#034;modeDesc&#034;&gt;Practice: clear all balls&lt;br&gt;Challenge: 3 lives&lt;\/div&gt;<br \/>\n    &lt;\/div&gt;<br \/>\n&lt;\/div&gt;<\/p>\n<p>&lt;div class&#061;&#034;game-container&#034; style&#061;&#034;position: relative;&#034;&gt;<br \/>\n    &lt;div class&#061;&#034;game-header&#034;&gt;<br \/>\n        &lt;a href&#061;&#034;https:\/\/amitofoicu.github.io\/home\/main.html&#034; target&#061;&#034;_blank&#034; rel&#061;&#034;noopener noreferrer&#034;&gt;<br \/>\n            &lt;img src&#061;&#034;https:\/\/amitofoicu.github.io\/home\/logo.jpg&#034; alt&#061;&#034;Logo&#034; class&#061;&#034;header-logo&#034;&gt;<br \/>\n        &lt;\/a&gt;<br \/>\n        &lt;div class&#061;&#034;game-title&#034;&gt;&#x1f3b1; Black 8&lt;\/div&gt;<br \/>\n        &lt;div class&#061;&#034;mode-indicator&#034; id&#061;&#034;modeIndicator&#034;&gt;PRACTICE&lt;\/div&gt;<br \/>\n    &lt;\/div&gt;<br \/>\n    &lt;canvas id&#061;&#034;poolCanvas&#034; width&#061;&#034;800&#034; height&#061;&#034;450&#034;&gt;&lt;\/canvas&gt;<\/p>\n<p>    &lt;div class&#061;&#034;hint&#034; id&#061;&#034;hintText&#034;&gt;<br \/>\n        &#x1f446; Drag cue ball \u00b7 Pull back for power \u00b7 Release to shoot<br \/>\n    &lt;\/div&gt;<\/p>\n<p>    &lt;div class&#061;&#034;status-bar&#034;&gt;<br \/>\n        &lt;!&#8211; dynamic content: practice shows strokes, challenge shows lives &#8211;&gt;<br \/>\n        &lt;span id&#061;&#034;statusLeftText&#034;&gt;Clear all&#x1f3b1; to win!&lt;\/span&gt;<br \/>\n        &lt;span id&#061;&#034;strokeBox&#034; class&#061;&#034;stroke-box&#034;&gt;0&lt;\/span&gt;<br \/>\n        &lt;span id&#061;&#034;livesDisplay&#034; class&#061;&#034;lives-container&#034; style&#061;&#034;display: none;&#034;&gt;\u2764\ufe0f&lt;span id&#061;&#034;livesCount&#034;&gt;3&lt;\/span&gt;&lt;\/span&gt;<br \/>\n    &lt;\/div&gt;<\/p>\n<p>    &lt;!&#8211; \u79fb\u9664 &#034;Miss strike&#034; \u53ca\u5176\u6240\u6709\u76f8\u5173\u5143\u7d20&#xff1a;\u539f challengeCounter \u6574\u4e2adiv\u5df2\u5220\u9664 &#8211;&gt;<\/p>\n<p>    &lt;div class&#061;&#034;bottom-panel&#034;&gt;<br \/>\n        &lt;div class&#061;&#034;power-meter&#034;&gt;<br \/>\n            &lt;span&gt;&#x1f4aa;&lt;\/span&gt;<br \/>\n            &lt;div class&#061;&#034;bar-bg&#034;&gt;&lt;div class&#061;&#034;bar-fill&#034; id&#061;&#034;powerFill&#034; style&#061;&#034;width: 20%;&#034;&gt;&lt;\/div&gt;&lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n        &lt;div class&#061;&#034;button-group&#034;&gt;<br \/>\n            &lt;button class&#061;&#034;action-btn mute&#034; id&#061;&#034;muteBtn&#034;&gt;&#x1f50a;&lt;\/button&gt;<br \/>\n            &lt;button class&#061;&#034;action-btn&#034; id&#061;&#034;backBtn&#034;&gt;&#x1f519; Back&lt;\/button&gt;  &lt;!&#8211; \u6539\u70ba Back \u6309\u9215 &#8211;&gt;<br \/>\n            &lt;button class&#061;&#034;action-btn next&#034; id&#061;&#034;nextBtn&#034; style&#061;&#034;display: none;&#034;&gt;\u2728 Next&lt;\/button&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n    &lt;\/div&gt;<\/p>\n<p>    &lt;!&#8211; Play again overlay (foul \/ victory \/ challenge fail) &#8211;&gt;<br \/>\n    &lt;div id&#061;&#034;againOverlay&#034; class&#061;&#034;again-overlay&#034; style&#061;&#034;display: none;&#034;&gt;<br \/>\n        &lt;div class&#061;&#034;again-card&#034;&gt;<br \/>\n            &lt;div class&#061;&#034;again-title&#034; id&#061;&#034;againMessage&#034;&gt;&#x1f3c6; Play Again&lt;\/div&gt;<br \/>\n            &lt;button class&#061;&#034;again-btn&#034; id&#061;&#034;againPlayBtn&#034;&gt;&#x1f3b1; Play Again&lt;\/button&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n    &lt;\/div&gt;<br \/>\n&lt;\/div&gt;<\/p>\n<p>&lt;script&gt;<br \/>\n    (function() {<br \/>\n        \/\/ &#8212;&#8211; DOM elements &#8212;&#8211;<br \/>\n        const canvas &#061; document.getElementById(&#039;poolCanvas&#039;);<br \/>\n        const ctx &#061; canvas.getContext(&#039;2d&#039;);<br \/>\n        const strokeBox &#061; document.getElementById(&#039;strokeBox&#039;);<br \/>\n        const powerFill &#061; document.getElementById(&#039;powerFill&#039;);<br \/>\n        const nextBtn &#061; document.getElementById(&#039;nextBtn&#039;);<br \/>\n        const backBtn &#061; document.getElementById(&#039;backBtn&#039;);  \/\/ \u6539\u540d<br \/>\n        const muteBtn &#061; document.getElementById(&#039;muteBtn&#039;);<br \/>\n        const loadingOverlay &#061; document.getElementById(&#039;loadingOverlay&#039;);<br \/>\n        const splashScreen &#061; document.getElementById(&#039;splashScreen&#039;);<br \/>\n        const modeIndicator &#061; document.getElementById(&#039;modeIndicator&#039;);<br \/>\n        \/\/ \u79fb\u9664\u4e86 challengeCounterDiv \u548c blackChancesSpan \u7684\u83b7\u53d6<br \/>\n        const againOverlay &#061; document.getElementById(&#039;againOverlay&#039;);<br \/>\n        const againMessage &#061; document.getElementById(&#039;againMessage&#039;);<br \/>\n        const againPlayBtn &#061; document.getElementById(&#039;againPlayBtn&#039;);<br \/>\n        const hintText &#061; document.getElementById(&#039;hintText&#039;);<br \/>\n        const livesDisplay &#061; document.getElementById(&#039;livesDisplay&#039;);<br \/>\n        const livesCountSpan &#061; document.getElementById(&#039;livesCount&#039;);<br \/>\n        const statusLeftText &#061; document.getElementById(&#039;statusLeftText&#039;);<\/p>\n<p>        \/\/ &#8212;&#8211; mode variables &#8212;&#8211;<br \/>\n        let gameMode &#061; &#039;practice&#039;;      \/\/ &#039;practice&#039; or &#039;challenge&#039;<br \/>\n        \/\/ \u79fb\u9664 blackMissCount \u76f8\u5173&#xff0c;\u4f46\u4fdd\u7559 lives \u903b\u8f91&#xff08;\u5185\u90e8\u4f7f\u7528\u53d8\u91cf blackMissCount \u53ea\u7528\u4e8e\u903b\u8f91&#xff0c;\u4e0d\u518d\u663e\u793a&#xff09;<br \/>\n        let challengeLives &#061; 3;           \/\/ total lives<br \/>\n        \/\/ \u6ce8\u610f&#xff1a;miss count \u4e0d\u518d\u7528\u4e8e\u663e\u793a&#xff0c;\u4f46\u5185\u90e8\u4ecd\u53ef\u7528\u4e8e\u5224\u65ad\u8fde\u51fb&#xff08;\u5982\u679c\u9700\u8981\u53ef\u4ee5\u4fdd\u7559&#xff0c;\u4f46\u4e3a\u4e86\u5b8c\u5168\u79fb\u9664GUI&#xff0c;\u6211\u4eec\u628a\u76f8\u5173\u663e\u793a\u53bb\u6389&#xff0c;\u5185\u90e8\u903b\u8f91\u7b80\u5316&#xff09;<br \/>\n        \/\/ \u4e3a\u4e86\u7b80\u5355\u4e14\u4e0d\u5f71\u54cd\u539f\u6709\u6263\u8840\u903b\u8f91&#xff0c;\u6211\u4eec\u4fdd\u7559 blackMissCount \u4f46\u4e0d\u5c55\u793a\u3002<br \/>\n        let blackMissCount &#061; 0;           \/\/ \u5185\u90e8\u8bb0\u5f55&#xff0c;\u4e0d\u518d\u663e\u793a<\/p>\n<p>        \/\/ &#8212;&#8211; audio management &#8212;&#8211;<br \/>\n        let isMuted &#061; false;<br \/>\n        let userInteracted &#061; false;<br \/>\n        let hasShot &#061; false; <\/p>\n<p>        let bgmAudio &#061; null;<br \/>\n        let winAudio &#061; null;          \/\/ win.mp3 (black potted)<br \/>\n        let xiaochuAudio &#061; null;      \/\/ xiaochu.mp3 (victory fanfare)<br \/>\n        let collisionAudio &#061; null;    \/\/ jiqiu.mp3 (ball collision)<\/p>\n<p>        function initAudio() {<br \/>\n            bgmAudio &#061; new Audio(&#039;https:\/\/amitofoicu.github.io\/home\/beijing.ogg&#039;);<br \/>\n            bgmAudio.loop &#061; true;<br \/>\n            bgmAudio.volume &#061; 1.0;<br \/>\n            bgmAudio.setAttribute(&#039;playsinline&#039;, &#039;&#039;);<br \/>\n            bgmAudio.setAttribute(&#039;webkit-playsinline&#039;, &#039;&#039;);<br \/>\n            bgmAudio.load();<\/p>\n<p>            winAudio &#061; new Audio(&#039;https:\/\/amitofoicu.github.io\/home\/win.mp3&#039;);<br \/>\n            winAudio.volume &#061; 0.7;<br \/>\n            winAudio.load();<\/p>\n<p>            xiaochuAudio &#061; new Audio(&#039;https:\/\/amitofoicu.github.io\/home\/xiaochu.mp3&#039;);<br \/>\n            xiaochuAudio.volume &#061; 1.0;<br \/>\n            xiaochuAudio.load();<\/p>\n<p>            collisionAudio &#061; new Audio(&#039;https:\/\/amitofoicu.github.io\/home\/jiqiu.mp3&#039;);<br \/>\n            collisionAudio.volume &#061; 0.6;<br \/>\n            collisionAudio.load();<\/p>\n<p>            muteBtn.textContent &#061; isMuted ? &#039;&#x1f507;&#039; : &#039;&#x1f50a;&#039;;<br \/>\n        }<\/p>\n<p>        function playBGM() {<br \/>\n            if (!hasShot || isMuted || !bgmAudio) return;<br \/>\n            try {<br \/>\n                if (!bgmAudio.paused) return;<br \/>\n                bgmAudio.currentTime &#061; 0;<br \/>\n                const playPromise &#061; bgmAudio.play();<br \/>\n                if (playPromise !&#061;&#061; undefined) playPromise.catch(e &#061;&gt; console.log(&#039;BGM play failed:&#039;, e));<br \/>\n            } catch (e) {}<br \/>\n        }<\/p>\n<p>        function stopBGM() {<br \/>\n            if (!bgmAudio) return;<br \/>\n            try { bgmAudio.pause(); bgmAudio.currentTime &#061; 0; } catch (e) {}<br \/>\n        }<\/p>\n<p>        function playSound(audio) {<br \/>\n            if (!userInteracted || isMuted || !audio) return;<br \/>\n            try {<br \/>\n                const clone &#061; audio.cloneNode();<br \/>\n                clone.volume &#061; audio.volume;<br \/>\n                const playPromise &#061; clone.play();<br \/>\n                if (playPromise !&#061;&#061; undefined) playPromise.catch(e &#061;&gt; console.log(&#039;sound play failed:&#039;, e));<br \/>\n                setTimeout(() &#061;&gt; { clone.pause(); clone.src &#061; &#039;&#039;; }, 3000);<br \/>\n            } catch (e) {}<br \/>\n        }<\/p>\n<p>        function playWinSound() { playSound(winAudio); }<br \/>\n        function playXiaochuSound() { playSound(xiaochuAudio); }<br \/>\n        function playCollisionSound() { playSound(collisionAudio); }<\/p>\n<p>        function toggleMute() {<br \/>\n            isMuted &#061; !isMuted;<br \/>\n            muteBtn.textContent &#061; isMuted ? &#039;&#x1f507;&#039; : &#039;&#x1f50a;&#039;;<br \/>\n            if (isMuted) stopBGM(); else if (hasShot) playBGM();<br \/>\n        }<\/p>\n<p>        function handleUserInteraction() { if (!userInteracted) userInteracted &#061; true; }<\/p>\n<p>        \/\/ &#8212;&#8211; constants &amp; physics &#8212;&#8211;<br \/>\n        const CW &#061; 800, CH &#061; 450;<br \/>\n        const LEFT_WALL &#061; 40, RIGHT_WALL &#061; 760, TOP_WALL &#061; 40, BOTTOM_WALL &#061; 410;<br \/>\n        const BALL_RADIUS &#061; 14;<br \/>\n        const FRICTION &#061; 0.98;<br \/>\n        const MAX_POWER_SPEED &#061; 25;<\/p>\n<p>        const pockets &#061; [<br \/>\n            { x: LEFT_WALL, y: TOP_WALL }, { x: RIGHT_WALL, y: TOP_WALL },<br \/>\n            { x: LEFT_WALL, y: BOTTOM_WALL }, { x: RIGHT_WALL, y: BOTTOM_WALL },<br \/>\n            { x: (LEFT_WALL&#043;RIGHT_WALL)\/2, y: TOP_WALL }, { x: (LEFT_WALL&#043;RIGHT_WALL)\/2, y: BOTTOM_WALL }<br \/>\n        ];<br \/>\n        const POCKET_RADIUS &#061; 28;<\/p>\n<p>        \/\/ &#8212;&#8211; game state &#8212;&#8211;<br \/>\n        let white &#061; { x: 200, y: 220, vx: 0, vy: 0 };<br \/>\n        let blackBalls &#061; [];<br \/>\n        const BLACK_COUNT &#061; 7;<br \/>\n        let remainingBlacks &#061; BLACK_COUNT;<br \/>\n        let gameOver &#061; false;<br \/>\n        let winFlag &#061; false;<br \/>\n        let checkWhiteAfterStop &#061; false;<br \/>\n        let strokes &#061; 0;<br \/>\n        let whiteRemoved &#061; false;       \/\/ white ball removed (foul)<\/p>\n<p>        \/\/ &#8212;&#8211; aiming state &#8212;&#8211;<br \/>\n        let isDragging &#061; false;<br \/>\n        let dragX &#061; 0, dragY &#061; 0;<br \/>\n        let angle &#061; 0;<br \/>\n        let power &#061; 0.2;<br \/>\n        let isMouseOutside &#061; false;<\/p>\n<p>        \/\/ &#8212;&#8211; Realistic cue animation with spring physics &#8212;&#8211;<br \/>\n        const CUE_FIXED_LENGTH &#061; 200;           \/\/ total length constant<br \/>\n        \/\/ cueOffset: positive &#061; pulled back, negative &#061; pushed forward (spring)<br \/>\n        let cueOffset &#061; 0;                       \/\/ main animation offset<br \/>\n        let cueVelocity &#061; 0;                      \/\/ velocity for smooth physics<br \/>\n        let targetOffset &#061; 0;                     \/\/ target based on power<\/p>\n<p>        \/\/ Spring constants for realistic feel<br \/>\n        const CUE_SPRING_STRENGTH &#061; 0.2;<br \/>\n        const CUE_DAMPING &#061; 0.92;<\/p>\n<p>        \/\/ Flag for shot impulse<br \/>\n        let shotJustFired &#061; false;<\/p>\n<p>        \/\/ &#8212;&#8211; Challenge mode fix: track if current shot has been processed &#8212;&#8211;<br \/>\n        let shotProcessedForLives &#061; false;        \/\/ \u6807\u8bb0\u672c\u6746\u662f\u5426\u5df2\u5904\u7406\u6263\u8840\u903b\u8f91<\/p>\n<p>        \/\/ &#8212;&#8211; particle system (same) &#8212;&#8211;<br \/>\n        let particles &#061; [];<br \/>\n        const PARTICLE_COLORS &#061; [&#039;#FF69B4&#039;,&#039;#FFD700&#039;,&#039;#FF4500&#039;,&#039;#9370DB&#039;,&#039;#00FF7F&#039;,&#039;#FF1493&#039;,&#039;#FFA500&#039;,&#039;#32CD32&#039;,&#039;#FFB6C1&#039;,&#039;#87CEEB&#039;,&#039;#FF6346&#039;,&#039;#FFFF00&#039;,&#039;#FF00FF&#039;,&#039;#00FFFF&#039;,&#039;#FFDAB9&#039;];<\/p>\n<p>        class Particle {<br \/>\n            constructor(x, y, type &#061; &#039;explosion&#039;) {<br \/>\n                this.x &#061; x; this.y &#061; y; this.type &#061; type;<br \/>\n                if (type &#061;&#061;&#061; &#039;explosion&#039;) {<br \/>\n                    const angle &#061; Math.random() * Math.PI * 2;<br \/>\n                    const speed &#061; Math.random() * 8 &#043; 5;<br \/>\n                    this.vx &#061; Math.cos(angle) * speed;<br \/>\n                    this.vy &#061; Math.sin(angle) * speed;<br \/>\n                    this.size &#061; Math.random() * 10 &#043; 5;<br \/>\n                    this.fadeSpeed &#061; 0.01 &#043; Math.random() * 0.01;<br \/>\n                } else {<br \/>\n                    this.vx &#061; (Math.random() &#8211; 0.5) * 1.5;<br \/>\n                    this.vy &#061; Math.random() * 2 &#043; 1.5;<br \/>\n                    this.size &#061; Math.random() * 8 &#043; 4;<br \/>\n                    this.fadeSpeed &#061; 0.001;<br \/>\n                }<br \/>\n                this.color &#061; PARTICLE_COLORS[Math.floor(Math.random() * PARTICLE_COLORS.length)];<br \/>\n                this.rotation &#061; Math.random() * Math.PI * 2;<br \/>\n                this.rotationSpeed &#061; (Math.random() &#8211; 0.5) * 0.05;<br \/>\n                this.gravity &#061; 0.05;<br \/>\n                this.life &#061; 1.0;<br \/>\n            }<br \/>\n            update() {<br \/>\n                this.x &#043;&#061; this.vx; this.y &#043;&#061; this.vy;<br \/>\n                this.vy &#043;&#061; this.gravity;<br \/>\n                this.rotation &#043;&#061; this.rotationSpeed;<br \/>\n                if (this.type &#061;&#061;&#061; &#039;explosion&#039;) this.life -&#061; this.fadeSpeed;<br \/>\n                if (this.type &#061;&#061;&#061; &#039;rain&#039;) {<br \/>\n                    if (this.y &gt; CH &#043; 30) { this.y &#061; -20; this.x &#061; Math.random() * CW; this.vx &#061; (Math.random()-0.5)*1.5; this.vy &#061; Math.random()*2&#043;1.5; }<br \/>\n                    if (this.x &lt; 0 || this.x &gt; CW) this.vx *&#061; -0.8;<br \/>\n                    return true;<br \/>\n                } else {<br \/>\n                    if (this.y &gt; CH &#043; 50) this.life &#061; 0;<br \/>\n                    return this.life &gt; 0;<br \/>\n                }<br \/>\n            }<br \/>\n            draw() {<br \/>\n                ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.rotation); ctx.globalAlpha &#061; this.life;<br \/>\n                ctx.beginPath();<br \/>\n                for (let i &#061; 0; i &lt; 5; i&#043;&#043;) {<br \/>\n                    let angle &#061; (i\/5)*Math.PI*2;<br \/>\n                    let petalLength &#061; this.size, petalWidth &#061; this.size*0.6;<br \/>\n                    let x &#061; Math.cos(angle)*petalLength, y &#061; Math.sin(angle)*petalLength;<br \/>\n                    let cx1 &#061; Math.cos(angle&#043;0.5)*petalWidth, cy1 &#061; Math.sin(angle&#043;0.5)*petalWidth;<br \/>\n                    let cx2 &#061; Math.cos(angle-0.5)*petalWidth, cy2 &#061; Math.sin(angle-0.5)*petalWidth;<br \/>\n                    ctx.moveTo(0,0); ctx.quadraticCurveTo(cx1, cy1, x, y); ctx.quadraticCurveTo(cx2, cy2, 0, 0);<br \/>\n                }<br \/>\n                ctx.fillStyle &#061; this.color; ctx.shadowColor &#061; &#039;rgba(255, 255, 255, 0.5)&#039;; ctx.shadowBlur &#061; 10; ctx.fill();<br \/>\n                ctx.restore();<br \/>\n            }<br \/>\n        }<\/p>\n<p>        function explodeFlowersFromPocket(pocketX, pocketY, count &#061; 15) {<br \/>\n            for (let i&#061;0; i&lt;count; i&#043;&#043;) particles.push(new Particle(pocketX, pocketY, &#039;explosion&#039;));<br \/>\n        }<br \/>\n        function startRainFlowers(count &#061; 40) {<br \/>\n            particles &#061; particles.filter(p &#061;&gt; p.type &#061;&#061;&#061; &#039;rain&#039;);<br \/>\n            for (let i&#061;0; i&lt;count; i&#043;&#043;) particles.push(new Particle(Math.random()*CW, Math.random()*CH-CH, &#039;rain&#039;));<br \/>\n        }<br \/>\n        function stopRainFlowers() { particles &#061; particles.filter(p &#061;&gt; p.type !&#061;&#061; &#039;rain&#039;); }<br \/>\n        function updateParticles() { particles &#061; particles.filter(p &#061;&gt; p.update()); }<br \/>\n        function drawParticles() { for (let p of particles) p.draw(); ctx.globalAlpha &#061; 1.0; }<\/p>\n<p>        \/\/ &#8212;&#8211; helpers &#8212;&#8211;<br \/>\n        function randomBlackPositions(count, cueX, cueY) {<br \/>\n            let positions &#061; [];<br \/>\n            const minDist &#061; BALL_RADIUS * 2 &#043; 15;<br \/>\n            let attempts &#061; 0, maxAttempts &#061; 5000;<br \/>\n            for (let i&#061;0; i&lt;count; i&#043;&#043;) {<br \/>\n                let placed &#061; false;<br \/>\n                while (!placed &amp;&amp; attempts &lt; maxAttempts) {<br \/>\n                    attempts&#043;&#043;;<br \/>\n                    let newX &#061; LEFT_WALL &#043; Math.random() * (RIGHT_WALL &#8211; LEFT_WALL);<br \/>\n                    let newY &#061; TOP_WALL &#043; Math.random() * (BOTTOM_WALL &#8211; TOP_WALL);<br \/>\n                    let distCue &#061; Math.hypot(newX &#8211; cueX, newY &#8211; cueY);<br \/>\n                    if (distCue &lt; minDist) continue;<br \/>\n                    let ok &#061; true;<br \/>\n                    for (let j&#061;0; j&lt;positions.length; j&#043;&#043;) {<br \/>\n                        if (Math.hypot(newX &#8211; positions[j].x, newY &#8211; positions[j].y) &lt; minDist) { ok &#061; false; break; }<br \/>\n                    }<br \/>\n                    if (ok) { positions.push({ x: newX, y: newY, vx:0, vy:0, active:true }); placed &#061; true; }<br \/>\n                }<br \/>\n                if (!placed) positions.push({ x:400&#043;(i*20), y:200&#043;(i*15), vx:0, vy:0, active:true });<br \/>\n            }<br \/>\n            return positions;<br \/>\n        }<\/p>\n<p>        \/\/ reset game (keep mode) &#8211; \u4f46 Back \u6309\u9215\u6703\u56de\u5230\u4e3b\u756b\u9762&#xff0c;\u6240\u4ee5\u9019\u500b\u4e3b\u8981\u7528\u65bc\u6a21\u5f0f\u5167\u91cd\u555f<br \/>\n        function resetGame() {<br \/>\n            white &#061; { x: 200, y: 220, vx: 0, vy: 0 };<br \/>\n            whiteRemoved &#061; false;<br \/>\n            blackBalls &#061; randomBlackPositions(BLACK_COUNT, white.x, white.y);<br \/>\n            remainingBlacks &#061; BLACK_COUNT;<br \/>\n            gameOver &#061; false;<br \/>\n            winFlag &#061; false;<br \/>\n            checkWhiteAfterStop &#061; false;<br \/>\n            strokes &#061; 0;<br \/>\n            strokeBox.innerText &#061; strokes;<br \/>\n            isDragging &#061; false;<br \/>\n            power &#061; 0.2;<br \/>\n            targetOffset &#061; 0;<br \/>\n            cueOffset &#061; 0;<br \/>\n            cueVelocity &#061; 0;<br \/>\n            powerFill.style.width &#061; &#039;20%&#039;;<br \/>\n            nextBtn.style.display &#061; &#039;none&#039;;<br \/>\n            backBtn.style.display &#061; &#039;inline-block&#039;;  \/\/ \u78ba\u4fdd Back \u986f\u793a<br \/>\n            stopRainFlowers();<br \/>\n            againOverlay.style.display &#061; &#039;none&#039;;<br \/>\n            shotJustFired &#061; false;<br \/>\n            shotProcessedForLives &#061; false;<\/p>\n<p>            \/\/ reset challenge lives &amp; misses (miss\u4e0d\u518d\u663e\u793a)<br \/>\n            blackMissCount &#061; 0;<br \/>\n            challengeLives &#061; 3;<br \/>\n            if (gameMode &#061;&#061;&#061; &#039;challenge&#039;) {<br \/>\n                \/\/ \u79fb\u9664\u4e86 challengeCounterDiv \u7684\u663e\u793a&#xff0c;\u53ea\u663e\u793a lives<br \/>\n                livesDisplay.style.display &#061; &#039;inline-flex&#039;;<br \/>\n                livesCountSpan.innerText &#061; challengeLives;<br \/>\n                \/\/ hide stroke<br \/>\n                strokeBox.style.display &#061; &#039;none&#039;;<br \/>\n                statusLeftText.innerText &#061; &#039;\u2764\ufe0f Lives left:&#039;;<br \/>\n            } else {<br \/>\n                livesDisplay.style.display &#061; &#039;none&#039;;<br \/>\n                strokeBox.style.display &#061; &#039;inline-block&#039;;<br \/>\n                statusLeftText.innerText &#061; &#039;Clear all&#x1f3b1; to win! Strokes&#039;;<br \/>\n            }<br \/>\n        }<\/p>\n<p>        \/\/ \u8fd4\u56de\u4e3b\u756b\u9762&#xff08;\u986f\u793a\u6a21\u5f0f\u9078\u64c7&#xff09;<br \/>\n        function goBackToSplash() {<br \/>\n            \/\/ \u505c\u6b62\u6240\u6709\u904b\u52d5<br \/>\n            white.vx &#061; 0; white.vy &#061; 0;<br \/>\n            blackBalls.forEach(b &#061;&gt; { if(b.active) { b.vx &#061; 0; b.vy &#061; 0; } });<br \/>\n            isDragging &#061; false;<\/p>\n<p>            \/\/ \u986f\u793a\u555f\u52d5\u756b\u9762<br \/>\n            splashScreen.style.display &#061; &#039;flex&#039;;<br \/>\n            splashScreen.style.opacity &#061; &#039;1&#039;;<\/p>\n<p>            \/\/ \u53ef\u9078&#xff1a;\u505c\u6b62\u80cc\u666f\u97f3\u6a02<br \/>\n            stopBGM();<br \/>\n            hasShot &#061; false;<br \/>\n        }<\/p>\n<p>        \/\/ mode selection<br \/>\n        function startGameWithMode(mode) {<br \/>\n            gameMode &#061; mode;<br \/>\n            modeIndicator.innerText &#061; (mode &#061;&#061;&#061; &#039;practice&#039;) ? &#039;PRACTICE&#039; : &#039;CHALLENGE&#039;;<br \/>\n            if (mode &#061;&#061;&#061; &#039;challenge&#039;) {<br \/>\n                \/\/ \u79fb\u9664\u4e86 challengeCounterDiv \u7684\u663e\u793a\u76f8\u5173\u4ee3\u7801<br \/>\n                livesDisplay.style.display &#061; &#039;inline-flex&#039;;<br \/>\n                livesCountSpan.innerText &#061; &#039;3&#039;;<br \/>\n                strokeBox.style.display &#061; &#039;none&#039;;<br \/>\n                statusLeftText.innerText &#061; &#039;\u2764\ufe0f Lives left:&#039;;<br \/>\n                hintText.innerText &#061; &#039;&#x1f446; Drag cue ball \u00b7 Pull back for power \u00b7 Release to shoot&#039;;<br \/>\n            } else {<br \/>\n                livesDisplay.style.display &#061; &#039;none&#039;;<br \/>\n                strokeBox.style.display &#061; &#039;inline-block&#039;;<br \/>\n                statusLeftText.innerText &#061; &#039;Clear all&#x1f3b1; to win! Strokes&#039;;<br \/>\n                hintText.innerText &#061; &#039;&#x1f446; Drag cue ball \u00b7 Pull back for power \u00b7 Release to shoot&#039;;<br \/>\n            }<br \/>\n            resetGame();<br \/>\n            splashScreen.style.opacity &#061; &#039;0&#039;;<br \/>\n            setTimeout(() &#061;&gt; splashScreen.style.display &#061; &#039;none&#039;, 400);<br \/>\n        }<\/p>\n<p>        function showAgainOverlay(reason) {<br \/>\n            if (reason &#061;&#061;&#061; &#039;foul&#039;) againMessage.innerText &#061; &#039;\u26aa Cue Ball Foul&#039;;<br \/>\n            else if (reason &#061;&#061;&#061; &#039;victory&#039;) againMessage.innerText &#061; &#039;&#x1f3c6; VICTORY &#x1f3c6;&#039;;<br \/>\n            else if (reason &#061;&#061;&#061; &#039;challengeFail&#039;) againMessage.innerText &#061; &#039;\u2764\ufe0f Challenge Failed&#039;;<br \/>\n            againOverlay.style.display &#061; &#039;flex&#039;;<br \/>\n        }<\/p>\n<p>        function handleWhiteFoul() {<br \/>\n            whiteRemoved &#061; true;<br \/>\n            white.vx &#061; 0; white.vy &#061; 0;<br \/>\n            blackBalls.forEach(b &#061;&gt; { if(b.active) { b.vx &#061; 0; b.vy &#061; 0; } });<br \/>\n            showAgainOverlay(&#039;foul&#039;);<br \/>\n        }<\/p>\n<p>        function handleChallengeFail() {<br \/>\n            gameOver &#061; true;<br \/>\n            whiteRemoved &#061; true;<br \/>\n            white.vx &#061; white.vy &#061; 0;<br \/>\n            blackBalls.forEach(b &#061;&gt; { if(b.active) { b.vx &#061; 0; b.vy &#061; 0; } });<br \/>\n            showAgainOverlay(&#039;challengeFail&#039;);<br \/>\n        }<\/p>\n<p>        \/\/ &#8212;&#8211; pocket check &#8212;&#8211;<br \/>\n        function checkPocket(ball) {<br \/>\n            for (let p of pockets) if (Math.hypot(ball.x-p.x, ball.y-p.y) &lt; POCKET_RADIUS) return p;<br \/>\n            return null;<br \/>\n        }<\/p>\n<p>        let lastCollisionTime &#061; 0;<br \/>\n        const COLLISION_THROTTLE &#061; 80;<br \/>\n        function tryPlayCollisionEffect() {<br \/>\n            const now &#061; Date.now();<br \/>\n            if (now &#8211; lastCollisionTime &gt; COLLISION_THROTTLE) { playCollisionSound(); lastCollisionTime &#061; now; }<br \/>\n        }<\/p>\n<p>        function ballsAreStationary() {<br \/>\n            if (!whiteRemoved &amp;&amp; (Math.abs(white.vx)&gt;0.1 || Math.abs(white.vy)&gt;0.1)) return false;<br \/>\n            for (let b of blackBalls) if (b.active &amp;&amp; (Math.abs(b.vx)&gt;0.1 || Math.abs(b.vy)&gt;0.1)) return false;<br \/>\n            return true;<br \/>\n        }<\/p>\n<p>        let blacksBeforeShot &#061; BLACK_COUNT;<\/p>\n<p>        \/\/ &#8212;&#8211; physics update (challenge lives logic: miss &#061; lose 1 life) &#8212;&#8211;<br \/>\n        function updatePhysics() {<br \/>\n            if (whiteRemoved) {<br \/>\n                updateParticles();<br \/>\n                return;<br \/>\n            }<\/p>\n<p>            if (!gameOver || checkWhiteAfterStop) {<br \/>\n                white.x &#043;&#061; white.vx; white.y &#043;&#061; white.vy;<br \/>\n                for (let b of blackBalls) if (b.active) { b.x &#043;&#061; b.vx; b.y &#043;&#061; b.vy; }<\/p>\n<p>                \/\/ wall collisions<br \/>\n                if (white.x &#8211; BALL_RADIUS &lt; LEFT_WALL) { white.x &#061; LEFT_WALL &#043; BALL_RADIUS; white.vx &#061; -white.vx * 0.92; }<br \/>\n                if (white.x &#043; BALL_RADIUS &gt; RIGHT_WALL) { white.x &#061; RIGHT_WALL &#8211; BALL_RADIUS; white.vx &#061; -white.vx * 0.92; }<br \/>\n                if (white.y &#8211; BALL_RADIUS &lt; TOP_WALL) { white.y &#061; TOP_WALL &#043; BALL_RADIUS; white.vy &#061; -white.vy * 0.92; }<br \/>\n                if (white.y &#043; BALL_RADIUS &gt; BOTTOM_WALL) { white.y &#061; BOTTOM_WALL &#8211; BALL_RADIUS; white.vy &#061; -white.vy * 0.92; }<br \/>\n                for (let b of blackBalls) {<br \/>\n                    if (!b.active) continue;<br \/>\n                    if (b.x &#8211; BALL_RADIUS &lt; LEFT_WALL) { b.x &#061; LEFT_WALL &#043; BALL_RADIUS; b.vx &#061; -b.vx * 0.92; }<br \/>\n                    if (b.x &#043; BALL_RADIUS &gt; RIGHT_WALL) { b.x &#061; RIGHT_WALL &#8211; BALL_RADIUS; b.vx &#061; -b.vx * 0.92; }<br \/>\n                    if (b.y &#8211; BALL_RADIUS &lt; TOP_WALL) { b.y &#061; TOP_WALL &#043; BALL_RADIUS; b.vy &#061; -b.vy * 0.92; }<br \/>\n                    if (b.y &#043; BALL_RADIUS &gt; BOTTOM_WALL) { b.y &#061; BOTTOM_WALL &#8211; BALL_RADIUS; b.vy &#061; -b.vy * 0.92; }<br \/>\n                }<\/p>\n<p>                \/\/ white vs black<br \/>\n                for (let b of blackBalls) {<br \/>\n                    if (!b.active) continue;<br \/>\n                    const dx &#061; b.x &#8211; white.x, dy &#061; b.y &#8211; white.y, dist &#061; Math.hypot(dx, dy);<br \/>\n                    if (dist &lt; BALL_RADIUS*2 &amp;&amp; dist &gt; 0.001) {<br \/>\n                        const nx &#061; dx\/dist, ny &#061; dy\/dist;<br \/>\n                        const vrelx &#061; white.vx &#8211; b.vx, vrely &#061; white.vy &#8211; b.vy, vn &#061; vrelx*nx &#043; vrely*ny;<br \/>\n                        if (vn &gt; 0) { const imp &#061; (2*vn)\/2*0.96; white.vx -&#061; imp*nx; white.vy -&#061; imp*ny; b.vx &#043;&#061; imp*nx; b.vy &#043;&#061; imp*ny; tryPlayCollisionEffect(); }<br \/>\n                        const overlap &#061; BALL_RADIUS*2 &#8211; dist;<br \/>\n                        if (overlap &gt; 0) { const sepX &#061; nx*overlap*0.5, sepY &#061; ny*overlap*0.5; white.x -&#061; sepX; white.y -&#061; sepY; b.x &#043;&#061; sepX; b.y &#043;&#061; sepY; }<br \/>\n                    }<br \/>\n                }<\/p>\n<p>                \/\/ black vs black<br \/>\n                for (let i&#061;0; i&lt;blackBalls.length; i&#043;&#043;) {<br \/>\n                    if (!blackBalls[i].active) continue;<br \/>\n                    for (let j&#061;i&#043;1; j&lt;blackBalls.length; j&#043;&#043;) {<br \/>\n                        if (!blackBalls[j].active) continue;<br \/>\n                        const b1&#061;blackBalls[i], b2&#061;blackBalls[j];<br \/>\n                        const dx &#061; b2.x-b1.x, dy &#061; b2.y-b1.y, dist &#061; Math.hypot(dx,dy);<br \/>\n                        if (dist &lt; BALL_RADIUS*2 &amp;&amp; dist &gt; 0.001) {<br \/>\n                            const nx &#061; dx\/dist, ny &#061; dy\/dist;<br \/>\n                            const vrelx &#061; b1.vx &#8211; b2.vx, vrely &#061; b1.vy &#8211; b2.vy, vn &#061; vrelx*nx &#043; vrely*ny;<br \/>\n                            if (vn &gt; 0) { const imp &#061; (2*vn)\/2*0.96; b1.vx -&#061; imp*nx; b1.vy -&#061; imp*ny; b2.vx &#043;&#061; imp*nx; b2.vy &#043;&#061; imp*ny; tryPlayCollisionEffect(); }<br \/>\n                            const overlap &#061; BALL_RADIUS*2 &#8211; dist;<br \/>\n                            if (overlap &gt; 0) { const sepX &#061; nx*overlap*0.5, sepY &#061; ny*overlap*0.5; b1.x -&#061; sepX; b1.y -&#061; sepY; b2.x &#043;&#061; sepX; b2.y &#043;&#061; sepY; }<br \/>\n                        }<br \/>\n                    }<br \/>\n                }<\/p>\n<p>                white.vx *&#061; FRICTION; white.vy *&#061; FRICTION;<br \/>\n                for (let b of blackBalls) if (b.active) { b.vx *&#061; FRICTION; b.vy *&#061; FRICTION; }<br \/>\n                if (Math.abs(white.vx) &lt; 0.1) white.vx &#061; 0;<br \/>\n                if (Math.abs(white.vy) &lt; 0.1) white.vy &#061; 0;<br \/>\n                for (let b of blackBalls) if (b.active) { if (Math.abs(b.vx) &lt; 0.1) b.vx &#061; 0; if (Math.abs(b.vy) &lt; 0.1) b.vy &#061; 0; }<\/p>\n<p>                \/\/ black pockets<br \/>\n                for (let b of blackBalls) {<br \/>\n                    if (!b.active) continue;<br \/>\n                    const pocket &#061; checkPocket(b);<br \/>\n                    if (pocket) {<br \/>\n                        b.active &#061; false;<br \/>\n                        remainingBlacks&#8211;;<br \/>\n                        playWinSound();<br \/>\n                        explodeFlowersFromPocket(pocket.x, pocket.y, 12);<br \/>\n                    }<br \/>\n                }<\/p>\n<p>                \/\/ white pocket \u2192 foul<br \/>\n                if (checkPocket(white)) { handleWhiteFoul(); return; }<\/p>\n<p>                if (remainingBlacks &#061;&#061;&#061; 0 &amp;&amp; !winFlag &amp;&amp; !checkWhiteAfterStop) {<br \/>\n                    checkWhiteAfterStop &#061; true;<br \/>\n                }<br \/>\n            }<\/p>\n<p>            if (checkWhiteAfterStop &amp;&amp; ballsAreStationary()) {<br \/>\n                if (checkPocket(white)) {<br \/>\n                    handleWhiteFoul();<br \/>\n                } else {<br \/>\n                    playXiaochuSound();<br \/>\n                    gameOver &#061; true; winFlag &#061; true;<br \/>\n                    startRainFlowers(50);<br \/>\n                    showAgainOverlay(&#039;victory&#039;);<br \/>\n                    nextBtn.style.display &#061; &#039;none&#039;;<br \/>\n                    backBtn.style.display &#061; &#039;inline-block&#039;; \/\/ \u4fdd\u6301 Back \u53ef\u898b<br \/>\n                }<br \/>\n                checkWhiteAfterStop &#061; false;<br \/>\n            }<\/p>\n<p>            \/\/ &#8212;&#8211; \u4fee\u590d\u6311\u6218\u6a21\u5f0f\u6263\u8840\u903b\u8f91&#xff1a;\u4f7f\u7528 shotProcessedForLives \u786e\u4fdd\u6bcf\u6746\u53ea\u5904\u7406\u4e00\u6b21 &#8212;&#8211;<br \/>\n            if (gameMode &#061;&#061;&#061; &#039;challenge&#039; &amp;&amp; !whiteRemoved &amp;&amp; ballsAreStationary() &amp;&amp; strokes &gt; 0 &amp;&amp; !gameOver &amp;&amp; !winFlag) {<br \/>\n                \/\/ \u53ea\u6709\u5f53\u672c\u6746\u5c1a\u672a\u5904\u7406\u65f6&#xff0c;\u624d\u8fdb\u5165\u5224\u65ad<br \/>\n                if (!shotProcessedForLives) {<br \/>\n                    let currentBlacks &#061; blackBalls.filter(b &#061;&gt; b.active).length;<br \/>\n                    if (currentBlacks &#061;&#061;&#061; blacksBeforeShot) {<br \/>\n                        \/\/ \u6ca1\u6709\u9ed1\u7403\u5165\u888b&#xff1a;\u6263\u8840<br \/>\n                        challengeLives&#8211;;<br \/>\n                        if (challengeLives &lt; 0) challengeLives &#061; 0;<br \/>\n                        livesCountSpan.innerText &#061; challengeLives;<br \/>\n                        \/\/ \u5185\u90e8miss\u8ba1\u6570\u4fdd\u7559\u4f46\u4e0d\u518d\u663e\u793a<br \/>\n                        blackMissCount&#043;&#043;;<br \/>\n                        if (challengeLives &lt;&#061; 0) {<br \/>\n                            handleChallengeFail();<br \/>\n                        }<br \/>\n                    } else {<br \/>\n                        \/\/ \u6709\u9ed1\u7403\u5165\u888b&#xff1a;\u91cd\u7f6e\u8fde\u51fb\u8ba1\u6570<br \/>\n                        blackMissCount &#061; 0;<br \/>\n                        \/\/ \u751f\u547d\u503c\u4e0d\u53d8<br \/>\n                    }<br \/>\n                    \/\/ \u6807\u8bb0\u672c\u6746\u5df2\u5904\u7406&#xff0c;\u9632\u6b62\u91cd\u590d\u6263\u8840<br \/>\n                    shotProcessedForLives &#061; true;<br \/>\n                }<br \/>\n            }<\/p>\n<p>            \/\/ Realistic cue animation with spring physics<br \/>\n            if (isDragging) {<br \/>\n                \/\/ Map power 0.2-1.2 to offset 0-120 (pulled back)<br \/>\n                targetOffset &#061; (power &#8211; 0.2) * 120; \/\/ max 120 pullback<br \/>\n            } else {<br \/>\n                targetOffset &#061; 0;<br \/>\n            }<\/p>\n<p>            \/\/ Spring physics for smooth, realistic motion<br \/>\n            let force &#061; (targetOffset &#8211; cueOffset) * CUE_SPRING_STRENGTH;<br \/>\n            cueVelocity &#043;&#061; force;<br \/>\n            cueVelocity *&#061; CUE_DAMPING;<br \/>\n            cueOffset &#043;&#061; cueVelocity;<\/p>\n<p>            \/\/ Apply shot impulse if shot just fired<br \/>\n            if (shotJustFired) {<br \/>\n                cueVelocity &#043;&#061; -25; \/\/ Strong forward impulse<br \/>\n                shotJustFired &#061; false;<br \/>\n            }<\/p>\n<p>            \/\/ Limit extreme offsets<br \/>\n            if (cueOffset &gt; 150) cueOffset &#061; 150;<br \/>\n            if (cueOffset &lt; -50) cueOffset &#061; -50;<\/p>\n<p>            updateParticles();<br \/>\n        }<\/p>\n<p>        \/\/ &#8212;&#8211; shoot (trigger spring release) &#8212;&#8211;<br \/>\n        function shoot() {<br \/>\n            if (gameOver || whiteRemoved) return;<br \/>\n            if (white.vx !&#061;&#061; 0 || white.vy !&#061;&#061; 0) return;<br \/>\n            if (remainingBlacks &#061;&#061;&#061; 0) return;<\/p>\n<p>            \/\/ Set flag for forward impulse on next physics update<br \/>\n            shotJustFired &#061; true;<\/p>\n<p>            const speed &#061; power * MAX_POWER_SPEED;<br \/>\n            white.vx &#061; Math.cos(angle) * speed;<br \/>\n            white.vy &#061; Math.sin(angle) * speed;<br \/>\n            strokes&#043;&#043;;<br \/>\n            strokeBox.innerText &#061; strokes;<\/p>\n<p>            blacksBeforeShot &#061; blackBalls.filter(b &#061;&gt; b.active).length;<\/p>\n<p>            \/\/ \u65b0\u7684\u4e00\u6746&#xff0c;\u91cd\u7f6e\u6263\u8840\u5904\u7406\u6807\u8bb0<br \/>\n            shotProcessedForLives &#061; false;<\/p>\n<p>            if (!hasShot) { hasShot &#061; true; setTimeout(() &#061;&gt; { if (!isMuted) playBGM(); }, 200); }<br \/>\n        }<\/p>\n<p>        function updateAim() {<br \/>\n            if (!isDragging) return;<br \/>\n            const dx &#061; white.x &#8211; dragX, dy &#061; white.y &#8211; dragY;<br \/>\n            let dist &#061; Math.hypot(dx, dy);<br \/>\n            if (dist &lt; 1) return;<br \/>\n            angle &#061; Math.atan2(dy, dx);<br \/>\n            const MIN_POWER &#061; 0.2, MAX_POWER &#061; 1.2, OPTIMAL_DIST &#061; 180;<br \/>\n            let rawPower &#061; dist \/ OPTIMAL_DIST;<br \/>\n            if (rawPower &lt; 0.5) power &#061; MIN_POWER &#043; (rawPower\/0.5)*0.3;<br \/>\n            else if (rawPower &lt; 1.2) power &#061; MIN_POWER &#043; 0.3 &#043; ((rawPower-0.5)\/0.7)*0.5;<br \/>\n            else power &#061; MIN_POWER &#043; 0.8 &#043; Math.min(rawPower-1.2,0.5)*0.4;<br \/>\n            power &#061; Math.max(MIN_POWER, Math.min(MAX_POWER, power));<br \/>\n            let visualPower &#061; Math.min(power, 1.0);<br \/>\n            powerFill.style.width &#061; (visualPower * 100) &#043; &#039;%&#039;;<br \/>\n        }<\/p>\n<p>        \/\/ &#8212;&#8211; enhanced aiming: cue stick &amp; collision prediction &#8212;&#8211;<br \/>\n        function predictCollision() {<br \/>\n            const dirX &#061; Math.cos(angle);<br \/>\n            const dirY &#061; Math.sin(angle);<br \/>\n            const startX &#061; white.x &#043; dirX * BALL_RADIUS;<br \/>\n            const startY &#061; white.y &#043; dirY * BALL_RADIUS;<\/p>\n<p>            let closestHit &#061; null;<br \/>\n            let minDist &#061; Infinity;<\/p>\n<p>            for (let b of blackBalls) {<br \/>\n                if (!b.active) continue;<br \/>\n                const tx &#061; b.x;<br \/>\n                const ty &#061; b.y;<br \/>\n                const toTargetX &#061; tx &#8211; startX;<br \/>\n                const toTargetY &#061; ty &#8211; startY;<br \/>\n                const proj &#061; toTargetX * dirX &#043; toTargetY * dirY;<br \/>\n                if (proj &lt; 0) continue;<br \/>\n                const closestX &#061; startX &#043; dirX * proj;<br \/>\n                const closestY &#061; startY &#043; dirY * proj;<br \/>\n                const perpDist &#061; Math.hypot(tx &#8211; closestX, ty &#8211; closestY);<br \/>\n                if (perpDist &gt; BALL_RADIUS * 2) continue;<br \/>\n                const hitDist &#061; proj &#8211; Math.sqrt(Math.max(0, Math.pow(BALL_RADIUS * 2, 2) &#8211; perpDist * perpDist));<br \/>\n                if (hitDist &lt; 0) continue;<br \/>\n                if (hitDist &lt; minDist) {<br \/>\n                    minDist &#061; hitDist;<br \/>\n                    closestHit &#061; { hitX: startX &#043; dirX * hitDist, hitY: startY &#043; dirY * hitDist };<br \/>\n                }<br \/>\n            }<br \/>\n            return closestHit;<br \/>\n        }<\/p>\n<p>        \/\/ &#8212;&#8211; draw with realistic cue animation &#8212;&#8211;<br \/>\n        function draw() {<br \/>\n            ctx.clearRect(0,0,CW,CH);<br \/>\n            ctx.fillStyle&#061;&#039;#1e5a3a&#039;; ctx.fillRect(0,0,CW,CH);<br \/>\n            ctx.strokeStyle&#061;&#039;#dbb06b&#039;; ctx.lineWidth&#061;3; ctx.strokeRect(LEFT_WALL-2,TOP_WALL-2,RIGHT_WALL-LEFT_WALL&#043;4,BOTTOM_WALL-TOP_WALL&#043;4);<br \/>\n            ctx.shadowColor&#061;&#039;#00000080&#039;; ctx.shadowBlur&#061;10;<br \/>\n            pockets.forEach(p&#061;&gt;{ ctx.beginPath(); ctx.arc(p.x,p.y,POCKET_RADIUS-4,0,Math.PI*2); ctx.fillStyle&#061;&#039;#1f140e&#039;; ctx.fill(); ctx.shadowBlur&#061;5; ctx.fillStyle&#061;&#039;#4a3c2b&#039;; ctx.arc(p.x,p.y,POCKET_RADIUS-8,0,Math.PI*2); ctx.fill(); });<br \/>\n            ctx.shadowBlur&#061;0;<br \/>\n            for (let b of blackBalls) if(b.active) {<br \/>\n                ctx.shadowColor&#061;&#039;#333&#039;; ctx.shadowBlur&#061;15; ctx.beginPath(); ctx.arc(b.x,b.y,BALL_RADIUS,0,Math.PI*2);<br \/>\n                const grad&#061;ctx.createRadialGradient(b.x-3,b.y-3,3,b.x,b.y,BALL_RADIUS&#043;2); grad.addColorStop(0,&#039;#222&#039;); grad.addColorStop(0.7,&#039;#000&#039;); ctx.fillStyle&#061;grad; ctx.fill();<br \/>\n                ctx.shadowBlur&#061;5; ctx.font&#061;&#039;bold 16px &#034;Segoe UI&#034;,Arial&#039;; ctx.fillStyle&#061;&#039;white&#039;; ctx.textAlign&#061;&#039;center&#039;; ctx.textBaseline&#061;&#039;middle&#039;; ctx.fillText(&#039;8&#039;,b.x,b.y);<br \/>\n                ctx.beginPath(); ctx.arc(b.x-3,b.y-3,4,0,Math.PI*2); ctx.fillStyle&#061;&#039;#fff9e6&#039;; ctx.globalAlpha&#061;0.3; ctx.fill(); ctx.globalAlpha&#061;1;<br \/>\n            }<br \/>\n            if (!whiteRemoved) {<br \/>\n                ctx.shadowColor&#061;&#039;#ccc&#039;; ctx.shadowBlur&#061;18; ctx.beginPath(); ctx.arc(white.x,white.y,BALL_RADIUS,0,Math.PI*2);<br \/>\n                const wgrad&#061;ctx.createRadialGradient(white.x-4,white.y-4,4,white.x,white.y,BALL_RADIUS&#043;2); wgrad.addColorStop(0,&#039;#fafaf5&#039;); wgrad.addColorStop(0.8,&#039;#c0c0c0&#039;); ctx.fillStyle&#061;wgrad; ctx.fill();<br \/>\n                ctx.shadowBlur&#061;8; ctx.beginPath(); ctx.arc(white.x-1,white.y-1,5,0,Math.PI*2); ctx.fillStyle&#061;&#039;#22222260&#039;; ctx.fill();<br \/>\n            }<\/p>\n<p>            \/\/ Aiming guide &amp; cue stick (only when white exists, not moving)<br \/>\n            if (!whiteRemoved &amp;&amp; isDragging &amp;&amp; white.vx &#061;&#061;&#061; 0 &amp;&amp; white.vy &#061;&#061;&#061; 0 &amp;&amp; remainingBlacks &gt; 0 &amp;&amp; !gameOver) {<br \/>\n                \/\/ draw power ring at drag position<br \/>\n                let visualPower &#061; Math.min(power, 1.0);<br \/>\n                ctx.beginPath();<br \/>\n                ctx.arc(dragX, dragY, 15 &#043; visualPower * 20, 0, Math.PI * 2);<br \/>\n                ctx.strokeStyle &#061; &#039;rgba(255, 200, 0, 0.5)&#039;;<br \/>\n                ctx.lineWidth &#061; 3;<br \/>\n                ctx.stroke();<\/p>\n<p>                \/\/ dashed guide line from white through drag<br \/>\n                ctx.setLineDash([8, 6]);<br \/>\n                ctx.beginPath();<br \/>\n                ctx.moveTo(white.x, white.y);<br \/>\n                ctx.lineTo(white.x &#043; Math.cos(angle) * 500, white.y &#043; Math.sin(angle) * 500);<br \/>\n                ctx.strokeStyle &#061; &#039;rgba(255, 255, 255, 0.6)&#039;;<br \/>\n                ctx.stroke();<br \/>\n                ctx.setLineDash([]);<\/p>\n<p>                \/\/ Draw cue with spring animation<br \/>\n                const cueLength &#061; CUE_FIXED_LENGTH &#043; cueOffset;<br \/>\n                const backX &#061; white.x &#8211; Math.cos(angle) * cueLength;<br \/>\n                const backY &#061; white.y &#8211; Math.sin(angle) * cueLength;<\/p>\n<p>                \/\/ cue shaft with gradient for 3D effect<br \/>\n                ctx.shadowBlur &#061; 15;<br \/>\n                ctx.shadowColor &#061; &#039;#222&#039;;<br \/>\n                ctx.beginPath();<br \/>\n                ctx.moveTo(backX, backY);<br \/>\n                ctx.lineTo(white.x, white.y);<\/p>\n<p>                \/\/ Create gradient along cue<br \/>\n                const gradient &#061; ctx.createLinearGradient(backX, backY, white.x, white.y);<br \/>\n                gradient.addColorStop(0, &#039;#8B5A2B&#039;);<br \/>\n                gradient.addColorStop(0.5, &#039;#B08D57&#039;);<br \/>\n                gradient.addColorStop(1, &#039;#D2B48C&#039;);<br \/>\n                ctx.strokeStyle &#061; gradient;<br \/>\n                ctx.lineWidth &#061; 12;<br \/>\n                ctx.stroke();<\/p>\n<p>                \/\/ Add cue wrap pattern<br \/>\n                ctx.beginPath();<br \/>\n                ctx.moveTo(backX &#043; (white.x-backX)*0.3, backY &#043; (white.y-backY)*0.3);<br \/>\n                ctx.lineTo(backX &#043; (white.x-backX)*0.35, backY &#043; (white.y-backY)*0.35);<br \/>\n                ctx.strokeStyle &#061; &#039;#654321&#039;;<br \/>\n                ctx.lineWidth &#061; 2;<br \/>\n                ctx.stroke();<\/p>\n<p>                \/\/ cue tip (ferrule)<br \/>\n                ctx.beginPath();<br \/>\n                ctx.arc(white.x &#043; Math.cos(angle) * 4, white.y &#043; Math.sin(angle) * 4, 5, 0, 2*Math.PI);<br \/>\n                ctx.fillStyle &#061; &#039;#DEB887&#039;;<br \/>\n                ctx.shadowBlur &#061; 12;<br \/>\n                ctx.fill();<\/p>\n<p>                \/\/ white leather tip<br \/>\n                ctx.beginPath();<br \/>\n                ctx.arc(white.x &#043; Math.cos(angle) * 8, white.y &#043; Math.sin(angle) * 8, 3, 0, 2*Math.PI);<br \/>\n                ctx.fillStyle &#061; &#039;#F5DEB3&#039;;<br \/>\n                ctx.fill();<\/p>\n<p>                \/\/ draw collision prediction marker<br \/>\n                const hit &#061; predictCollision();<br \/>\n                if (hit) {<br \/>\n                    ctx.beginPath();<br \/>\n                    ctx.arc(hit.hitX, hit.hitY, 8, 0, Math.PI * 2);<br \/>\n                    ctx.fillStyle &#061; &#039;#FFD966&#039;;<br \/>\n                    ctx.shadowColor &#061; &#039;black&#039;;<br \/>\n                    ctx.shadowBlur &#061; 12;<br \/>\n                    ctx.fill();<br \/>\n                }<\/p>\n<p>                ctx.shadowBlur &#061; 0;<br \/>\n                ctx.setLineDash([]);<br \/>\n            }<\/p>\n<p>            drawParticles();<br \/>\n            if (winFlag) {<br \/>\n                ctx.fillStyle&#061;&#039;rgba(0,0,0,0.3)&#039;; ctx.fillRect(0,0,CW,CH);<br \/>\n                ctx.shadowBlur&#061;30; ctx.font&#061;&#039;bold 52px &#034;Segoe UI&#034;,Verdana&#039;; ctx.fillStyle&#061;&#039;#FFD966&#039;; ctx.strokeStyle&#061;&#039;#8B4513&#039;; ctx.lineWidth&#061;6; ctx.textAlign&#061;&#039;center&#039;;<br \/>\n                ctx.strokeText(&#039;&#x1f389; VICTORY!&#039;,CW\/2,150); ctx.fillText(&#039;&#x1f389; VICTORY!&#039;,CW\/2,150);<br \/>\n            }<br \/>\n        }<\/p>\n<p>        \/\/ &#8212;&#8211; event listeners (same) &#8212;&#8211;<br \/>\n        function getCanvasCoords(e) {<br \/>\n            const rect &#061; canvas.getBoundingClientRect();<br \/>\n            const scaleX &#061; canvas.width \/ rect.width, scaleY &#061; canvas.height \/ rect.height;<br \/>\n            let clientX, clientY;<br \/>\n            if (e.touches) { clientX &#061; e.touches[0].clientX; clientY &#061; e.touches[0].clientY; }<br \/>\n            else { clientX &#061; e.clientX; clientY &#061; e.clientY; }<br \/>\n            return { x: (clientX &#8211; rect.left) * scaleX, y: (clientY &#8211; rect.top) * scaleY };<br \/>\n        }<br \/>\n        function onMouseDown(e){ e.preventDefault(); handleUserInteraction(); if(gameOver||whiteRemoved||white.vx!&#061;&#061;0||white.vy!&#061;&#061;0||remainingBlacks&#061;&#061;&#061;0) return; const c&#061;getCanvasCoords(e); dragX&#061;c.x; dragY&#061;c.y; isDragging&#061;true; }<br \/>\n        function onMouseMove(e){ e.preventDefault(); if(!isDragging) return; const c&#061;getCanvasCoords(e); dragX&#061;c.x; dragY&#061;c.y; updateAim(); }<br \/>\n        function onMouseUp(e){ e.preventDefault(); if(!isDragging) return; isDragging&#061;false; shoot(); }<br \/>\n        canvas.addEventListener(&#039;mousedown&#039;,onMouseDown);<br \/>\n        canvas.addEventListener(&#039;mousemove&#039;,onMouseMove);<br \/>\n        canvas.addEventListener(&#039;mouseup&#039;,onMouseUp);<br \/>\n        canvas.addEventListener(&#039;touchstart&#039;,(e)&#061;&gt;{ e.preventDefault(); handleUserInteraction(); if(gameOver||whiteRemoved||white.vx!&#061;&#061;0||white.vy!&#061;&#061;0||remainingBlacks&#061;&#061;&#061;0) return; const c&#061;getCanvasCoords(e); dragX&#061;c.x; dragY&#061;c.y; isDragging&#061;true; },{passive:false});<br \/>\n        canvas.addEventListener(&#039;touchmove&#039;,(e)&#061;&gt;{ e.preventDefault(); if(!isDragging) return; const c&#061;getCanvasCoords(e); dragX&#061;c.x; dragY&#061;c.y; updateAim(); },{passive:false});<br \/>\n        canvas.addEventListener(&#039;touchend&#039;,(e)&#061;&gt;{ e.preventDefault(); if(!isDragging) return; isDragging&#061;false; shoot(); });<br \/>\n        window.addEventListener(&#039;mousemove&#039;,(e)&#061;&gt;{ if(isDragging){ const c&#061;getCanvasCoords(e); dragX&#061;c.x; dragY&#061;c.y; updateAim(); } });<br \/>\n        window.addEventListener(&#039;mouseup&#039;,(e)&#061;&gt;{ if(isDragging){ isDragging&#061;false; shoot(); } });<\/p>\n<p>        \/\/ Back \u6309\u9215&#xff1a;\u8fd4\u56de\u4e3b\u756b\u9762<br \/>\n        backBtn.addEventListener(&#039;click&#039;, (e) &#061;&gt; {<br \/>\n            e.preventDefault();<br \/>\n            handleUserInteraction();<br \/>\n            goBackToSplash();<br \/>\n        });<\/p>\n<p>        \/\/ \u6ce8\u610f&#xff1a;\u539f\u672c\u7684 restartBtn \u5df2\u7d93\u79fb\u9664&#xff0c;\u6240\u4ee5\u6c92\u6709\u885d\u7a81<br \/>\n        \/\/ nextBtn \u4fdd\u7559&#xff08;\u4f46\u9810\u8a2d\u96b1\u85cf&#xff09;<br \/>\n        nextBtn.addEventListener(&#039;click&#039;, (e) &#061;&gt; {<br \/>\n            e.preventDefault();<br \/>\n            handleUserInteraction();<br \/>\n            resetGame();  \/\/ \u5982\u679c\u6709\u7684\u8a71&#xff0c;\u9084\u662f\u91cd\u7f6e\u7576\u524d\u6a21\u5f0f<br \/>\n        });<\/p>\n<p>        muteBtn.addEventListener(&#039;click&#039;, (e) &#061;&gt; {<br \/>\n            e.preventDefault();<br \/>\n            handleUserInteraction();<br \/>\n            toggleMute();<br \/>\n        });<\/p>\n<p>        againPlayBtn.addEventListener(&#039;click&#039;, (e) &#061;&gt; {<br \/>\n            e.preventDefault();<br \/>\n            handleUserInteraction();<br \/>\n            resetGame();<br \/>\n        });<\/p>\n<p>        document.addEventListener(&#039;touchend&#039;, (e) &#061;&gt; {<br \/>\n            const now &#061; Date.now();<br \/>\n            if (now &#8211; lastTouchEnd &lt;&#061; 300) e.preventDefault();<br \/>\n            lastTouchEnd &#061; now;<br \/>\n        }, false);<\/p>\n<p>        document.addEventListener(&#039;contextmenu&#039;, e &#061;&gt; e.preventDefault());<br \/>\n        document.body.addEventListener(&#039;touchmove&#039;, (e) &#061;&gt; {<br \/>\n            if (e.target &#061;&#061;&#061; document.body) e.preventDefault();<br \/>\n        }, { passive: false });<\/p>\n<p>        \/\/ mode selection buttons<br \/>\n        document.getElementById(&#039;practiceModeBtn&#039;).addEventListener(&#039;click&#039;, ()&#061;&gt;{<br \/>\n            startGameWithMode(&#039;practice&#039;);<br \/>\n        });<br \/>\n        document.getElementById(&#039;challengeModeBtn&#039;).addEventListener(&#039;click&#039;, ()&#061;&gt;{<br \/>\n            startGameWithMode(&#039;challenge&#039;);<br \/>\n        });<\/p>\n<p>        \/\/ initialization<br \/>\n        initAudio();<br \/>\n        setTimeout(() &#061;&gt; {<br \/>\n            loadingOverlay.style.opacity &#061; &#039;0&#039;;<br \/>\n            setTimeout(() &#061;&gt; {<br \/>\n                loadingOverlay.style.display &#061; &#039;none&#039;;<br \/>\n            }, 500);<br \/>\n        }, 1000);<\/p>\n<p>        \/\/ \u78ba\u4fdd splash \u986f\u793a<br \/>\n        splashScreen.style.display &#061; &#039;flex&#039;;<br \/>\n        splashScreen.style.opacity &#061; &#039;1&#039;;<\/p>\n<p>        let lastTouchEnd &#061; 0;<br \/>\n        let victoryTimer &#061; null;<\/p>\n<p>        function animate() {<br \/>\n            updatePhysics();<br \/>\n            draw();<br \/>\n            requestAnimationFrame(animate);<br \/>\n        }<br \/>\n        animate();<br \/>\n    })();<br \/>\n&lt;\/script&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt; <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Black 8 (Advanced)\u00a0&#8212; \u7ee7Black 8 Pro\u4e4b\u540e\u7ee7\u7eed\u6df1\u5ea6\u8bad\u7ec3\u7684\u4f5c\u54c1<br \/>\n\u4e3b\u8981\u529f\u80fd\u63d0\u5347&#xff1a;<br \/>\n\u589e\u5f3a\u4e86\u7403\u6746\u62c9\u5347\u52a8\u753b\u548c\u51fb\u7403\u97f3\u6548\u7b49&#xff0c;\u63d0\u5347\u6c89\u6d78\u5f0f\u4f53\u9a8c\u3002<br \/>\n\u589e\u52a0\u4e86\u6311\u6218\u6a21\u5f0f&#xff0c;\u5b9e\u73b0\u66f4\u591a\u73a9\u6cd5\u3002 <\/p>\n<p>&lt;meta name\\&quot;viewport\\&quot; content\\&quot;widthdevice-width, initial-scale1<\/p>\n","protected":false},"author":2,"featured_media":80454,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[573,964,187,248],"topic":[],"class_list":["post-80455","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-server","tag-css","tag-html5","tag-javascript","tag-248"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>DeepSeek\u6df1\u5ea6\u8bad\u7ec3\u7684\u7f51\u9875\u5c0f\u6e38\u620f - Black 8(\u9ad8\u7ea7\u7248\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.wsisp.com\/helps\/80455.html\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"DeepSeek\u6df1\u5ea6\u8bad\u7ec3\u7684\u7f51\u9875\u5c0f\u6e38\u620f - Black 8(\u9ad8\u7ea7\u7248\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"og:description\" content=\"Black 8 (Advanced)\u00a0-- \u7ee7Black 8 Pro\u4e4b\u540e\u7ee7\u7eed\u6df1\u5ea6\u8bad\u7ec3\u7684\u4f5c\u54c1 \u4e3b\u8981\u529f\u80fd\u63d0\u5347&#xff1a; \u589e\u5f3a\u4e86\u7403\u6746\u62c9\u5347\u52a8\u753b\u548c\u51fb\u7403\u97f3\u6548\u7b49&#xff0c;\u63d0\u5347\u6c89\u6d78\u5f0f\u4f53\u9a8c\u3002 \u589e\u52a0\u4e86\u6311\u6218\u6a21\u5f0f&#xff0c;\u5b9e\u73b0\u66f4\u591a\u73a9\u6cd5\u3002  &lt;meta name&quot;viewport&quot; content&quot;widthdevice-width, initial-scale1\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.wsisp.com\/helps\/80455.html\" \/>\n<meta property=\"og:site_name\" content=\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"article:published_time\" content=\"2026-03-04T23:17:20+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260304231719-69a8bd7f59f5c.png\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"24 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/80455.html\",\"url\":\"https:\/\/www.wsisp.com\/helps\/80455.html\",\"name\":\"DeepSeek\u6df1\u5ea6\u8bad\u7ec3\u7684\u7f51\u9875\u5c0f\u6e38\u620f - Black 8(\u9ad8\u7ea7\u7248\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"isPartOf\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\"},\"datePublished\":\"2026-03-04T23:17:20+00:00\",\"dateModified\":\"2026-03-04T23:17:20+00:00\",\"author\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/80455.html#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.wsisp.com\/helps\/80455.html\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/80455.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.wsisp.com\/helps\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"DeepSeek\u6df1\u5ea6\u8bad\u7ec3\u7684\u7f51\u9875\u5c0f\u6e38\u620f -- Black 8(\u9ad8\u7ea7\u7248\uff09\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\",\"url\":\"https:\/\/www.wsisp.com\/helps\/\",\"name\":\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"description\":\"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"contentUrl\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"caption\":\"admin\"},\"sameAs\":[\"http:\/\/wp.wsisp.com\"],\"url\":\"https:\/\/www.wsisp.com\/helps\/author\/admin\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"DeepSeek\u6df1\u5ea6\u8bad\u7ec3\u7684\u7f51\u9875\u5c0f\u6e38\u620f - Black 8(\u9ad8\u7ea7\u7248\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.wsisp.com\/helps\/80455.html","og_locale":"zh_CN","og_type":"article","og_title":"DeepSeek\u6df1\u5ea6\u8bad\u7ec3\u7684\u7f51\u9875\u5c0f\u6e38\u620f - Black 8(\u9ad8\u7ea7\u7248\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","og_description":"Black 8 (Advanced)\u00a0-- \u7ee7Black 8 Pro\u4e4b\u540e\u7ee7\u7eed\u6df1\u5ea6\u8bad\u7ec3\u7684\u4f5c\u54c1 \u4e3b\u8981\u529f\u80fd\u63d0\u5347&#xff1a; \u589e\u5f3a\u4e86\u7403\u6746\u62c9\u5347\u52a8\u753b\u548c\u51fb\u7403\u97f3\u6548\u7b49&#xff0c;\u63d0\u5347\u6c89\u6d78\u5f0f\u4f53\u9a8c\u3002 \u589e\u52a0\u4e86\u6311\u6218\u6a21\u5f0f&#xff0c;\u5b9e\u73b0\u66f4\u591a\u73a9\u6cd5\u3002  &lt;meta name&quot;viewport&quot; content&quot;widthdevice-width, initial-scale1","og_url":"https:\/\/www.wsisp.com\/helps\/80455.html","og_site_name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","article_published_time":"2026-03-04T23:17:20+00:00","og_image":[{"url":"https:\/\/www.wsisp.com\/helps\/wp-content\/uploads\/2026\/03\/20260304231719-69a8bd7f59f5c.png"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"admin","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"24 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.wsisp.com\/helps\/80455.html","url":"https:\/\/www.wsisp.com\/helps\/80455.html","name":"DeepSeek\u6df1\u5ea6\u8bad\u7ec3\u7684\u7f51\u9875\u5c0f\u6e38\u620f - Black 8(\u9ad8\u7ea7\u7248\uff09 - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","isPartOf":{"@id":"https:\/\/www.wsisp.com\/helps\/#website"},"datePublished":"2026-03-04T23:17:20+00:00","dateModified":"2026-03-04T23:17:20+00:00","author":{"@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41"},"breadcrumb":{"@id":"https:\/\/www.wsisp.com\/helps\/80455.html#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.wsisp.com\/helps\/80455.html"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.wsisp.com\/helps\/80455.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.wsisp.com\/helps"},{"@type":"ListItem","position":2,"name":"DeepSeek\u6df1\u5ea6\u8bad\u7ec3\u7684\u7f51\u9875\u5c0f\u6e38\u620f -- Black 8(\u9ad8\u7ea7\u7248\uff09"}]},{"@type":"WebSite","@id":"https:\/\/www.wsisp.com\/helps\/#website","url":"https:\/\/www.wsisp.com\/helps\/","name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","description":"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41","name":"admin","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/","url":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","contentUrl":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","caption":"admin"},"sameAs":["http:\/\/wp.wsisp.com"],"url":"https:\/\/www.wsisp.com\/helps\/author\/admin"}]}},"_links":{"self":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/80455","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/comments?post=80455"}],"version-history":[{"count":0,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/80455\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media\/80454"}],"wp:attachment":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media?parent=80455"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/categories?post=80455"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/tags?post=80455"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/topic?post=80455"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}