Created
August 1, 2025 11:23
-
-
Save kujirahand/5d451f4cfbecc9daeae99298a53c7546 to your computer and use it in GitHub Desktop.
ハノイの塔を遊べるブラウザゲーム
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html><head><meta charset="utf-8" /> | |
| <title>ハノイの塔(PyScript版)</title> | |
| <link rel="stylesheet" href="https://pyscript.net/releases/2025.7.3/core.css"> | |
| <script type="module" src="https://pyscript.net/releases/2025.7.3/core.js"></script> | |
| <style> | |
| canvas { border: 1px solid #000; background: #f8f8f8; } | |
| #controls { margin: 1em; } | |
| #message { color: red; margin-top: 1em; } | |
| </style> | |
| </head><body> | |
| <h1>ハノイの塔</h1> | |
| <div id="controls"><button id="start_btn">開始</button></div> | |
| <canvas id="gameCanvas" width="500" height="300"></canvas> | |
| <div id="message"></div> | |
| <script type="py"> | |
| # --- ここからPythonのコードを記述 --- (*1) | |
| from js import document | |
| from pyodide.ffi import create_proxy | |
| # canvasなどのDOMオブジェクトを取得 --- (*2) | |
| canvas = document.getElementById("gameCanvas") | |
| ctx = canvas.getContext("2d") # 描画用コンテキストを取得 | |
| message = document.getElementById("message") # メッセージの表示オブジェクト | |
| # グローバル変数の初期化 --- (*3) | |
| NUM_DISKS = 3 # 円盤の数 | |
| pegs = [[], [], []] # 各棒にあるディスクを管理 | |
| selected_peg = None # 選択中の棒 | |
| def draw(): | |
| """ 画面の描画処理 """ # --- (*4) | |
| ctx.clearRect(0, 0, 500, 300) | |
| for i in range(3): | |
| # 棒の描画 --- (*5) | |
| x = 100 + i * 150 | |
| if selected_peg == i: | |
| ctx.fillStyle = "red" | |
| else: | |
| ctx.fillStyle = "#000" | |
| ctx.fillRect(x - 5, 100, 10, 150) | |
| # 円盤(disk)の描画 --- (*6) | |
| for j, disk in enumerate(pegs[i]): | |
| y = 240 - j * 20 | |
| width = 20 + disk * 30 | |
| ctx.fillStyle = f"rgb({100+disk*50},100,200)" | |
| ctx.fillRect(x - width//2, y, width, 15) | |
| def move(from_peg, to_peg): | |
| """ ディスクを移動する処理 """ # --- (*7) | |
| if not pegs[from_peg]: | |
| message.innerText = "ディスクがありません。" | |
| return | |
| if pegs[to_peg] and pegs[to_peg][-1] < pegs[from_peg][-1]: | |
| message.innerText = "大きいディスクは小さいディスクの上に置けません。" | |
| return | |
| disk = pegs[from_peg].pop() | |
| pegs[to_peg].append(disk) | |
| message.innerText = "" | |
| draw() | |
| def handle_click(event): | |
| """ クリックイベントの処理 """ # --- (*8) | |
| global selected_peg | |
| rect = canvas.getBoundingClientRect() | |
| x = event.clientX - rect.left | |
| peg_clicked = int(x // (500 // 3)) | |
| # 選択中の棒がなければ選択して、選択したものがあれば移動を試みる --- (*9) | |
| if selected_peg is None: | |
| selected_peg = peg_clicked | |
| else: | |
| move(selected_peg, peg_clicked) | |
| selected_peg = None | |
| draw() | |
| # キャンバスをクリック↓時の処理を設定 --- (*10) | |
| canvas.addEventListener("click", create_proxy(handle_click)) | |
| def start_game(event=None): | |
| """ ゲームを開始する処理 """ # --- (*11) | |
| global selected_peg | |
| selected_peg = None | |
| pegs[0] = list(reversed(range(NUM_DISKS))) | |
| pegs[1] = [] | |
| pegs[2] = [] | |
| message.innerText = "" | |
| draw() | |
| # スタートボタンを押した時の処理を記述 --- (*12) | |
| document.getElementById("start_btn").addEventListener( | |
| "click", create_proxy(start_game)) | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment