最終的に作りたい迷路は。
ずっと家に引きこもっていると・・・
たまには外に出て体を動かしたりした方がよいのかな?
なんて考えたりする。
ちょっとした罪悪感。
でも、外出はおっくうだ。
だから、引きこもっている。
そこで考えた。
3D迷路を作って、家に居ながらにして「散歩」気分を味わえないだろうか。
それで、この罪悪感がなくなるのかは疑問だけれど、ともかくきっかけはそんな感じだった。
まだ、立体的に・・・という部分については、しっかりとした方向性も見えていないんだけれど、まずは迷路を自動生成するところまで完成させてみよう。
今回のやり方は、穴掘り法というものであるらしい。
細かなところでボクのとは異なる点もあるが、たぶん問題はないだろう。
僕は迷路専門家じゃないから、わからんけれど。
今回も動画で解説。
YouTubeをはじめてみて思う。
こういう系の解説を文字で書くのは、結構骨が折れる。
そこへ行くと動画はいくぶん楽だ。
今回も解説は動画で行こうと思います。
タイムアッタク編はコチラ(↓)
本気で作る迷路。
https://youtu.be/0qduvs-UdQE
解説編はコチラ(↓)
穴掘り法で書いたコードの解説
https://youtu.be/iEOelFSQqlc
サンプルコードはコチラ(↓)
#################### MAZE ####################
import tkinter as tk
import random
from tkinter import messagebox
SIZE = 10 # 1マスの大きさ(ピクセル)
COLUMN = 101 # 列数(奇数を推奨)
ROW = 51 # 行数(奇数を推奨)
departure = [[1, 1]] # 迷路作りの起点
playerX = 1, playerY = 1, beforeX = 1, beforeY = 1
# リスト「maze」作成
# 二次元配列で「壁:WALL」「フィールド:FIELD」「通路:PATH」
maze = []
for y in range(ROW) :
sub = []
for x in range(COLUMN) :
if y==0 or y==ROW-1 or x==0 or x==COLUMN-1 :
sub.append("WALL")
else :
sub.append("FIELD")
maze.append(sub)
# 穴掘り関数(ランダムに通路を掘り進めて"PATH"に書き換える)
def dig(x, y) :
maze[y][x] = "PATH" # 引数で指定したポイントを書き換え
# 掘り進めることが可能な方向をチェック
canMove = []
if maze[y-1][x]=="FIELD" and maze[y-2][x]=="FIELD" :
canMove.append("UP")
if maze[y+1][x]=="FIELD" and maze[y+2][x]=="FIELD" :
canMove.append("DOWN")
if maze[y][x-1]=="FIELD" and maze[y][x-2]=="FIELD" :
canMove.append("LEFT")
if maze[y][x+1]=="FIELD" and maze[y][x+2]=="FIELD" :
canMove.append("RIGHT")
if len(canMove)==0 : # 可能な方向がなければ関数を抜ける
return
# ランダムに方向を決めて掘り進む
direction = random.choice(canMove)
if direction=="UP" :
maze[y-1][x]="PATH"
maze[y-2][x]="PATH"
y -= 2
if direction=="DOWN" :
maze[y+1][x]="PATH"
maze[y+2][x]="PATH"
y += 2
if direction=="LEFT" :
maze[y][x-1]="PATH"
maze[y][x-2]="PATH"
x -= 2
if direction=="RIGHT" :
maze[y][x+1]="PATH"
maze[y][x+2]="PATH"
x += 2
departure.append([x, y]) # 進んだ位置を穴掘りの起点に追加
dig(x, y) # 再帰(新たな地点から再び穴掘り)
# ゲームプレイ的な・・・
# イベント関数
def keyPress(event) :
global playerX,playerY,beforeX,beforeY
if event.keysym=="Up" and maze[playerY-1][playerX]=="PATH" :
beforeX = playerX
beforeY = playerY
playerY -=1
if event.keysym=="Down" and maze[playerY+1][playerX]=="PATH" :
beforeX = playerX
beforeY = playerY
playerY +=1
if event.keysym=="Left" and maze[playerY][playerX-1]=="PATH" :
beforeX = playerX
beforeY = playerY
playerX -=1
if event.keysym=="Right" and maze[playerY][playerX+1]=="PATH" :
beforeX = playerX
beforeY = playerY
playerX +=1
# プレイヤー移動とゴール
def move() :
can.create_oval(beforeX*SIZE+1, beforeY*SIZE+1, (beforeX+1)*SIZE-1, (beforeY+1)*SIZE-1, fill="white", width=0)
can.create_oval(playerX*SIZE+2, playerY*SIZE+2, (playerX+1)*SIZE-2, (playerY+1)*SIZE-2, fill="blue")
if playerX==COLUMN-2 and playerY==ROW-2 :
messagebox.showinfo("Information", "GOAL!!!!")
exit()
can.after(100, move) # 再帰
# ゲームウインドウ
win = tk.Tk()
can = tk.Canvas(win, width=COLUMN*SIZE, height=ROW*SIZE)
can.pack()
while len(departure)!=0 : # 新たな起点を呼び出す
start = departure.pop(0)
x = start[0]
y = start[1]
dig(x, y)
for y in range(ROW): # 迷路を描画する
for x in range(COLUMN):
if maze[y][x]=="PATH" :
color = "white"
else :
color = "black"
can.create_rectangle(x*SIZE, y*SIZE, (x+1)*SIZE, (y+1)*SIZE, fill=color, width=0)
can.create_rectangle((COLUMN-2)*SIZE, (ROW-2)*SIZE, (COLUMN-1)*SIZE, (ROW-1)*SIZE ,fill="red") # ゴール位置
move() # プレイヤー移動関数呼び出し
win.bind("<Any-KeyPress>", keyPress) # キープレスをバインド
win.mainloop() # ウインドウループ