{"id":25977,"date":"2025-05-03T06:39:35","date_gmt":"2025-05-02T21:39:35","guid":{"rendered":"http:\/\/www.tyosuke20xx.com\/blog\/?p=25977"},"modified":"2025-05-03T06:39:36","modified_gmt":"2025-05-02T21:39:36","slug":"15%e3%83%91%e3%82%ba%e3%83%ab-javascript","status":"publish","type":"post","link":"http:\/\/www.tyosuke20xx.com\/blog\/?p=25977","title":{"rendered":"15\u30d1\u30ba\u30eb javascript"},"content":{"rendered":"\n<p>index.html<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html>\n&lt;html lang=\"ja\">\n\n&lt;head>\n  &lt;meta charset=\"utf-8\">\n  &lt;title>15Puzzle&lt;\/title>\n  &lt;style>\n    canvas {\n      background: pink;\n      display: block;\n      margin: 0 auto;\n      cursor: pointer;\n    }\n  &lt;\/style>\n&lt;\/head>\n\n&lt;body>\n  &lt;canvas width=\"280\" height=\"280\">\n    Canvas not supported.\n  &lt;\/canvas>\n\n  &lt;script src=\"js\/main.js\">&lt;\/script>\n&lt;\/body>\n\n&lt;\/html><\/code><\/pre>\n\n\n\n<p>main.js<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'use strict';\n\n(() => {\n    class PuzzleRenderer {\n        constructor(puzzle, canvas) {\n            this.puzzle = puzzle;\n            this.canvas = canvas;\n            this.ctx = this.canvas.getContext('2d');\n            this.TILE_SIZE = 70;\n            this.img = document.createElement('img');\n            this.img.src = 'img\/animal1.png';\n            this.img.addEventListener('load', () => {\n                this.render();\n            });\n            this.canvas.addEventListener('click', e => {\n                if (this.puzzle.getCompletedStatus()) {\n                    return;\n                }\n\n                const rect = this.canvas.getBoundingClientRect();\n                const col = Math.floor((e.clientX - rect.left) \/ this.TILE_SIZE);\n                const row = Math.floor((e.clientY - rect.top) \/ this.TILE_SIZE);\n                this.puzzle.swapTiles(col, row);\n                this.render();\n\n                if (this.puzzle.isComplete()) {\n                    this.puzzle.setCompletedStatus(true);\n                    this.renderGameClear();\n                }\n            });\n        }\n\n        renderGameClear() {\n            this.ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';\n            this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n            this.ctx.font = '28px Arial';\n            this.ctx.fillStyle = '#fff';\n            this.ctx.fillText('GAME CLEAR!!', 40, 150);\n        }\n\n        render() {\n            for (let row = 0; row &lt; this.puzzle.getBoardSize(); row++) {\n                for (let col = 0; col &lt; this.puzzle.getBoardSize(); col++) {\n                    this.renderTile(this.puzzle.getTile(row, col), col, row);\n                }\n            }\n        }\n\n        renderTile(n, col, row) {\n            if (n === this.puzzle.getBlankIndex()) {\n                this.ctx.fillStyle = '#eeeeee';\n                this.ctx.fillRect(\n                    col * this.TILE_SIZE,\n                    row * this.TILE_SIZE,\n                    this.TILE_SIZE,\n                    this.TILE_SIZE\n                );\n            } else {\n                this.ctx.drawImage(\n                    this.img,\n                    (n % this.puzzle.getBoardSize()) * this.TILE_SIZE,\n                    Math.floor(n \/ this.puzzle.getBoardSize()) * this.TILE_SIZE,\n                    this.TILE_SIZE,\n                    this.TILE_SIZE,\n                    col * this.TILE_SIZE,\n                    row * this.TILE_SIZE,\n                    this.TILE_SIZE,\n                    this.TILE_SIZE\n                );\n            }\n        }\n    }\n\n    class Puzzle {\n        constructor(level) {\n            this.level = level;\n            this.tiles = &#91;\n                &#91;0, 1, 2, 3],\n                &#91;4, 5, 6, 7],\n                &#91;8, 9, 10, 11],\n                &#91;12, 13, 14, 15],\n            ];\n            this.UDLR = &#91;\n                &#91;0, -1], \/\/ up\n                &#91;0, 1], \/\/ down\n                &#91;-1, 0], \/\/ left\n                &#91;1, 0], \/\/ right\n            ];\n            this.isCompleted = false;\n            this.BOARD_SIZE = this.tiles.length;\n            this.BLANK_INDEX = this.BOARD_SIZE ** 2 - 1;\n            do {\n                this.shuffle(this.level);\n            } while (this.isComplete());\n        }\n\n        getBoardSize() {\n            return this.BOARD_SIZE;\n        }\n\n        getBlankIndex() {\n            return this.BLANK_INDEX;\n        }\n\n        getCompletedStatus() {\n            return this.isCompleted;\n        }\n\n        setCompletedStatus(value) {\n            this.isCompleted = value;\n        }\n\n        getTile(row, col) {\n            return this.tiles&#91;row]&#91;col];\n        }\n\n        shuffle(n) {\n            let blankCol = this.BOARD_SIZE - 1;\n            let blankRow = this.BOARD_SIZE - 1;\n\n            for (let i = 0; i &lt; n; i++) {\n                let destCol;\n                let destRow;\n                do {\n                    const dir = Math.floor(Math.random() * this.UDLR.length);\n                    destCol = blankCol + this.UDLR&#91;dir]&#91;0];\n                    destRow = blankRow + this.UDLR&#91;dir]&#91;1];\n                } while (this.isOutside(destCol, destRow));\n\n                &#91;\n                    this.tiles&#91;blankRow]&#91;blankCol],\n                    this.tiles&#91;destRow]&#91;destCol],\n                ] = &#91;\n                        this.tiles&#91;destRow]&#91;destCol],\n                        this.tiles&#91;blankRow]&#91;blankCol],\n                    ];\n\n                &#91;blankCol, blankRow] = &#91;destCol, destRow];\n            }\n        }\n\n        swapTiles(col, row) {\n            if (this.tiles&#91;row]&#91;col] === this.BLANK_INDEX) {\n                return;\n            }\n\n            for (let i = 0; i &lt; this.UDLR.length; i++) {\n                const destCol = col + this.UDLR&#91;i]&#91;0];\n                const destRow = row + this.UDLR&#91;i]&#91;1];\n\n                if (this.isOutside(destCol, destRow)) {\n                    continue;\n                }\n\n                if (this.tiles&#91;destRow]&#91;destCol] === this.BLANK_INDEX) {\n                    &#91;\n                        this.tiles&#91;row]&#91;col],\n                        this.tiles&#91;destRow]&#91;destCol],\n                    ] = &#91;\n                            this.tiles&#91;destRow]&#91;destCol],\n                            this.tiles&#91;row]&#91;col],\n                        ];\n                    break;\n                }\n            }\n        }\n\n        isOutside(destCol, destRow) {\n            return (\n                destCol &lt; 0 || destCol > this.BOARD_SIZE - 1 ||\n                destRow &lt; 0 || destRow > this.BOARD_SIZE - 1\n            );\n        }\n\n        isComplete() {\n            let i = 0;\n            for (let row = 0; row &lt; this.BOARD_SIZE; row++) {\n                for (let col = 0; col &lt; this.BOARD_SIZE; col++) {\n                    if (this.tiles&#91;row]&#91;col] !== i++) {\n                        return false;\n                    }\n                }\n            }\n            return true;\n        }\n    }\n\n    const canvas = document.querySelector('canvas');\n    if (typeof canvas.getContext === 'undefined') {\n        return;\n    }\n\n    new PuzzleRenderer(new Puzzle(2), canvas);\n})();<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>index.html main.js<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[1],"tags":[41,3],"class_list":["post-25977","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-javascript","tag-programming"],"aioseo_notices":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/25977","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=25977"}],"version-history":[{"count":2,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/25977\/revisions"}],"predecessor-version":[{"id":25979,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/25977\/revisions\/25979"}],"wp:attachment":[{"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=25977"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=25977"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.tyosuke20xx.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=25977"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}