{"id":798,"date":"2025-07-14T06:09:39","date_gmt":"2025-07-14T06:09:39","guid":{"rendered":"https:\/\/ouyangminwei.com\/?p=798"},"modified":"2025-07-14T06:36:05","modified_gmt":"2025-07-14T06:36:05","slug":"rockpaperscissorswarsimulator","status":"publish","type":"post","link":"https:\/\/ouyangminwei.com\/index.php\/2025\/07\/14\/rockpaperscissorswarsimulator\/","title":{"rendered":"\u526a\u5200\u77f3\u982d\u5e03\u6230\u722d\u6a21\u64ec"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"zh-TW\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>\u53ef\u8abf\u901f Emoji \u526a\u5200\u77f3\u982d\u5e03\u9663\u71df\u5927\u6230<\/title>\n<style>\n    body {\n        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n        display: flex;\n        justify-content: center;\n        align-items: flex-start;\n        flex-wrap: wrap; \/* \u5728\u5c0f\u87a2\u5e55\u4e0a\u63db\u884c *\/\n        margin: 0;\n        padding: 20px;\n        background-color: #f4f6f8;\n    }\n    #controls {\n        padding: 20px;\n        background-color: #ffffff;\n        border-radius: 8px;\n        box-shadow: 0 4px S15px rgba(0,0,0,0.1);\n        margin-right: 20px;\n        min-width: 260px; \/* \u7a0d\u5fae\u52a0\u5bec\u4ee5\u5bb9\u7d0d\u65b0\u63a7\u5236\u9805 *\/\n    }\n    h2 {\n        text-align: center;\n        margin-top: 0;\n        color: #333;\n    }\n    .input-group {\n        display: flex;\n        align-items: center;\n        justify-content: space-between;\n        margin-bottom: 15px;\n    }\n    .input-group label {\n        font-weight: 500;\n        font-size: 18px;\n    }\n    input[type=\"number\"] {\n        width: 80px;\n        padding: 8px;\n        border: 1px solid #ccc;\n        border-radius: 5px;\n        font-size: 16px;\n        text-align: center;\n    }\n    input[type=\"range\"] {\n        flex-grow: 1; \/* \u8b93\u6ed1\u687f\u586b\u6eff\u5269\u9918\u7a7a\u9593 *\/\n        margin: 0 10px;\n    }\n    #speed-value {\n        font-weight: bold;\n        color: #007bff;\n        min-width: 35px; \/* \u7d66\u4e88\u56fa\u5b9a\u5bec\u5ea6\u907f\u514d\u8df3\u52d5 *\/\n        text-align: right;\n    }\n    .button-group {\n        display: flex;\n        flex-direction: column;\n        gap: 10px;\n        margin-top: 20px;\n    }\n    button {\n        padding: 12px 20px;\n        border: none;\n        border-radius: 5px;\n        cursor: pointer;\n        font-size: 16px;\n        font-weight: bold;\n        transition: background-color 0.3s, transform 0.1s;\n    }\n    #start-btn { background-color: #28a745; color: white; }\n    #start-btn:hover { background-color: #218838; }\n    #stop-btn { background-color: #dc3545; color: white; }\n    #stop-btn:hover { background-color: #c82333; }\n    button:active { transform: scale(0.98); }\n\n    #simulation-container {\n        display: flex;\n        flex-direction: column;\n        align-items: center;\n    }\n    #battlefield {\n        border: 2px solid #333;\n        background-color: #ffffff;\n        image-rendering: pixelated;\n    }\n<\/style>\n<\/head>\n<body>\n\n<div id=\"controls\">\n    <h2>\u9663\u71df\u8a2d\u5b9a<\/h2>\n    <div class=\"input-group\">\n        <label for=\"scissors-count\">\u2702\ufe0f \u526a\u5200:<\/label>\n        <input type=\"number\" id=\"scissors-count\" value=\"50\">\n    <\/div>\n    <div class=\"input-group\">\n        <label for=\"rock-count\">\ud83d\udc4a \u77f3\u982d:<\/label>\n        <input type=\"number\" id=\"rock-count\" value=\"50\">\n    <\/div>\n    <div class=\"input-group\">\n        <label for=\"paper-count\">\ud83d\udcc4 \u5e03:<\/label>\n        <input type=\"number\" id=\"paper-count\" value=\"50\">\n    <\/div>\n\n    <hr style=\"border: 1px solid #eee; margin: 20px 0;\">\n    \n    <div class=\"input-group\">\n        <label for=\"speed-control\">\ud83c\udfc3 \u901f\u5ea6:<\/label>\n        <input type=\"range\" id=\"speed-control\" min=\"0.1\" max=\"5\" value=\"1.2\" step=\"0.1\">\n        <span id=\"speed-value\">1.2<\/span>\n    <\/div>\n\n    <div class=\"button-group\">\n        <button id=\"start-btn\">\u958b\u59cb \/ \u91cd\u8a2d<\/button>\n        <button id=\"stop-btn\">\u66ab\u505c<\/button>\n    <\/div>\n<\/div>\n\n<div id=\"simulation-container\">\n    <canvas id=\"battlefield\" width=\"600\" height=\"600\"><\/canvas>\n<\/div>\n\n<script>\n    const canvas = document.getElementById('battlefield');\n    const ctx = canvas.getContext('2d');\n    const startBtn = document.getElementById('start-btn');\n    const stopBtn = document.getElementById('stop-btn');\n    \n    \/\/ === \u65b0\u589e\uff1a\u7372\u53d6\u901f\u5ea6\u63a7\u5236\u76f8\u95dc\u5143\u7d20 ===\n    const speedControl = document.getElementById('speed-control');\n    const speedValueDisplay = document.getElementById('speed-value');\n\n    const emojiSize = 16;\n    const canvasWidth = canvas.width;\n    const canvasHeight = canvas.height;\n\n    let units = [];\n    let animationFrameId;\n    let currentSpeed = parseFloat(speedControl.value); \/\/ \u521d\u59cb\u5316\u76ee\u524d\u901f\u5ea6\n\n    \/\/ === \u65b0\u589e\uff1a\u76e3\u807d\u6ed1\u687f\u7684\u8b8a\u5316 ===\n    speedControl.addEventListener('input', (e) => {\n        currentSpeed = parseFloat(e.target.value);\n        speedValueDisplay.textContent = currentSpeed.toFixed(1); \/\/ \u66f4\u65b0\u986f\u793a\u7684\u6578\u5b57\uff0c\u4fdd\u7559\u4e00\u4f4d\u5c0f\u6578\n    });\n\n\n    const types = { ROCK: 'rock', PAPER: 'paper', SCISSORS: 'scissors' };\n    const preyOf = { [types.SCISSORS]: types.PAPER, [types.PAPER]: types.ROCK, [types.ROCK]: types.SCISSORS };\n    const styles = { [types.SCISSORS]: { emoji: '\u2702\ufe0f' }, [types.ROCK]: { emoji: '\ud83d\udc4a' }, [types.PAPER]: { emoji: '\ud83d\udcc4' } };\n\n    class Unit {\n        constructor(x, y, type) {\n            this.x = x;\n            this.y = y;\n            this.type = type;\n        }\n\n        draw() {\n            ctx.font = `${emojiSize}px sans-serif`;\n            ctx.textAlign = 'center';\n            ctx.textBaseline = 'middle';\n            ctx.fillText(styles[this.type].emoji, this.x, this.y);\n        }\n\n        move() {\n            const targetType = preyOf[this.type];\n            let closestTarget = null;\n            let minDistanceSq = Infinity;\n\n            for (const other of units) {\n                if (other.type === targetType) {\n                    const dx = other.x - this.x;\n                    const dy = other.y - this.y;\n                    const distanceSq = dx * dx + dy * dy;\n                    if (distanceSq < minDistanceSq) {\n                        minDistanceSq = distanceSq;\n                        closestTarget = other;\n                    }\n                }\n            }\n\n            if (closestTarget) {\n                const distance = Math.sqrt(minDistanceSq);\n                if (distance < emojiSize) {\n                    closestTarget.type = this.type;\n                } else {\n                    \/\/ === \u4fee\u6539\uff1a\u4f7f\u7528 currentSpeed \u8b8a\u6578\u4f86\u63a7\u5236\u79fb\u52d5\u901f\u5ea6 ===\n                    this.x += (closestTarget.x - this.x) \/ distance * currentSpeed;\n                    this.y += (closestTarget.y - this.y) \/ distance * currentSpeed;\n                }\n            }\n            \n            this.x = Math.max(emojiSize \/ 2, Math.min(canvasWidth - emojiSize \/ 2, this.x));\n            this.y = Math.max(emojiSize \/ 2, Math.min(canvasHeight - emojiSize \/ 2, this.y));\n        }\n    }\n\n    function init() {\n        units = [];\n        const scissorsCount = parseInt(document.getElementById('scissors-count').value) || 0;\n        const rockCount = parseInt(document.getElementById('rock-count').value) || 0;\n        const paperCount = parseInt(document.getElementById('paper-count').value) || 0;\n        const createUnits = (count, type) => {\n            for (let i = 0; i < count; i++) {\n                units.push(new Unit(\n                    Math.random() * canvasWidth,\n                    Math.random() * canvasHeight,\n                    type\n                ));\n            }\n        };\n        createUnits(scissorsCount, types.SCISSORS);\n        createUnits(rockCount, types.ROCK);\n        createUnits(paperCount, types.PAPER);\n    }\n\n    function animate() {\n        ctx.clearRect(0, 0, canvasWidth, canvasHeight);\n        units.forEach(unit => {\n            unit.move();\n            unit.draw();\n        });\n        animationFrameId = requestAnimationFrame(animate);\n    }\n\n    startBtn.addEventListener('click', () => {\n        if (animationFrameId) { cancelAnimationFrame(animationFrameId); }\n        init();\n        animate();\n    });\n\n    stopBtn.addEventListener('click', () => {\n        if (animationFrameId) { cancelAnimationFrame(animationFrameId); animationFrameId = null; }\n    });\n\n<\/script>\n\n<\/body>\n<\/html>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u53ef\u8abf\u901f Emoji \u526a\u5200\u77f3\u982d\u5e03\u9663\u71df\u5927\u6230 \u9663\u71df\u8a2d\u5b9a \u2702\ufe0f \u526a\u5200: \ud83d\udc4a \u77f3\u982d: \ud83d\udcc4  &hellip; <a href=\"https:\/\/ouyangminwei.com\/index.php\/2025\/07\/14\/rockpaperscissorswarsimulator\/\">\u95b1\u8b80\u5168\u6587 <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[1],"tags":[],"post_format":[],"class_list":["post-798","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_edit_lock":"1752475383:1","_edit_last":"1","_aioseo_title":"#post_title #separator_sa #site_title","_aioseo_description":"#post_excerpt","_aioseo_keywords":"","_aioseo_og_title":"","_aioseo_og_description":"","_aioseo_og_article_section":"","_aioseo_og_article_tags":"","_aioseo_twitter_title":"","_aioseo_twitter_description":"","_oembed_2544c1d0cb3503ab4c4d558c3b3c8873":"","_oembed_time_2544c1d0cb3503ab4c4d558c3b3c8873":"","_oembed_99481806ecbe6ce4ee46f8588d320993":"","_oembed_db663acf973e82e6d9d80df71945dfb8":"","_oembed_16cdfab488f57db73586f4286af2704f":"","_wp_old_slug":"%e5%89%aa%e5%88%80%e7%9f%b3%e9%a0%ad%e5%b8%83%e6%88%b0%e7%88%ad%e6%a8%a1%e6%93%ac","_links":{"self":[{"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/posts\/798","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/comments?post=798"}],"version-history":[{"count":17,"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/posts\/798\/revisions"}],"predecessor-version":[{"id":819,"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/posts\/798\/revisions\/819"}],"wp:attachment":[{"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/media?parent=798"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/categories?post=798"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/tags?post=798"},{"taxonomy":"post_format","embeddable":true,"href":"https:\/\/ouyangminwei.com\/index.php\/wp-json\/wp\/v2\/post_format?post=798"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}