Skip to content

Instantly share code, notes, and snippets.

@kujirahand
Created August 1, 2025 11:23
Show Gist options
  • Save kujirahand/5d451f4cfbecc9daeae99298a53c7546 to your computer and use it in GitHub Desktop.
Save kujirahand/5d451f4cfbecc9daeae99298a53c7546 to your computer and use it in GitHub Desktop.
ハノイの塔を遊べるブラウザゲーム
<!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