はじめに
娘が迷路に興味を持ち始め、私も強化学習で迷路を自動生成していたので…
折角だからExcelに出力して、印刷して、一緒に遊んでしまおう!という動機です。
また、私のゲーミングPCにはExcelが入っていないので、妻の端末を借りています。
流石に妻の端末をpythonで侵すわけにはいかなかったので、google の colaboratory を使用してみました。
結論
自動生成のルール
- Excelのセルを部屋と考え、inputで1辺のセル数Nを決める。
- 2つの変数
部屋番号
と壁
を使用する。- 部屋番号
- 初期値
[0, 1, 2, 3, ...]
のN*Nの連番とする。
- 初期値
- 壁
- N個のセルに壁4つを設定する。
- 壁は
[1, 1, 1, 1] = [上, 右, 下, 左]
として1:壁/0:壁無し
を設定。 - データのイメージは
[[1,1,1,1][1,1,1,1]...]
- データ数は[N]個(表記方法合ってる?)
- 部屋番号
- ランダムに部屋を選択
- ランダムに突き破る壁を選択
- 部屋番号を小さい方に統一させる
- 例えば 3*3 の迷路を想定し、部屋2 の 下向き を突き破る場合
- 部屋番号 [0, 1, 2, 3, 4, 2, 6, 7, 8]
- 壁 [[略][略][1, 1, 0, 1][略][略][0, 1, 1, 1][略][略][略]] となる。
- これを繰り返し、部屋番号が全て0になるまで繰り返す。ただし、同じ部屋番号の場合は処理しない。
- 全て0になったら、Excelに罫線で壁を記載して完了とする。
コーディング
colaboratoryからgoogleドライブを使えるようにする。
External data: Drive, Sheets, and Cloud Storage にアクセスし、Mounting Google Drive locally
を処理する。
from google.colab import drive
drive.mount('/content/gdrive')
を実行すると、authorization code
を聞いてくる。
Go to this URL in a browser:
後ろのURLにアクセスし、authorization code
を取得し、コピペする。
colaboratory の左メニューでファイル
を見るとgdrive
がある。
あとは通常のファイルのようにアクセスする。
ライブラリ
import openpyxl as xl
import numpy as np
from datetime import datetime
進行方向を取得
def direction():
'''
進行方向を選択。
戻り値は順に「現在地の破壊壁方向」「進行部屋の壁破壊方向」「進行部屋の部屋番号」
'''
rand = np.random.randint(0, high=4)
if rand == 0: return 0, 2, -1 * INT_MAZE_SIZE # 上
if rand == 1: return 1, -1, 1 # 右
if rand == 2: return 2, 0, INT_MAZE_SIZE # 下
return -1, 1, -1 # 左
進めるかチェック
def check_cell(cells, c_cell, d_cell, dire):
''' 進めるかチェック '''
# 進んだ部屋が迷路外かどうか
# 上下へのはみ出し
if d_cell < 0 or INT_MAZE_CELL <= d_cell: return False
# 部屋番号+1が迷路サイズで割り切れる場合、右端の部屋。右には進めない。
if (c_cell+1) % INT_MAZE_SIZE == 0 and dire == 1: return False
# 部屋番号+1が迷路サイズで割り、余り1の場合、左端の部屋。左には進めない。
if (c_cell+1) % INT_MAZE_SIZE == 1 and dire == -1: return False
# 現在地部屋番号 = 進んだ部屋番号。同じ部屋の場合は処理しない
if cells[c_cell] == cells[d_cell]: return False
# それ以外は進める
return True
小さい部屋番号を選択
def choice_cell_no(cells, c_cell, d_cell):
''' 小さい方の部屋番号を取得 '''
if cells[c_cell] < cells[d_cell]:
return cells[c_cell], cells[d_cell]
return cells[d_cell], cells[c_cell]
迷路を生成する
def create_maze():
''' 迷路を生成 '''
# 初期状態を生成
# [上, 右, 下, 左] = 1:壁 / 0:通路
lst_maze = np.ones([INT_MAZE_CELL, 4])
# 部屋のナンバリング、最終的に全部0にする。
lst_cells = np.array([i for i in range(0, INT_MAZE_CELL)])
while True:
# すべての部屋が番号0の時は処理終了
if np.sum(lst_cells) == 0: break
# 壁を破る元のセルを選択(現在地)
int_choice_cell = np.random.randint(0, high=INT_MAZE_CELL)
# 既にスタート地点とつながっている(0)時は処理しない。
if lst_cells[int_choice_cell] == 0: continue
# 進行方向を選択
int_now_wall, int_direction_wall, int_direction = direction()
# 進んだときのセル
int_direction_cell = int_choice_cell + int_direction
# 値のチェック
if not check_cell(lst_cells, int_choice_cell, int_direction_cell, int_direction):
continue
# 部屋番号を確保
int_min_value, int_max_value = choice_cell_no(lst_cells, int_choice_cell, int_direction_cell)
# 小さい方の部屋番号に統一する
lst_cells[lst_cells == int_max_value] = int_min_value
# 壁も更新する
lst_maze[int_choice_cell][int_now_wall] = 0
lst_maze[int_direction_cell][int_direction_wall] = 0
return lst_maze
Excelに出力
def out_excel(maze):
# 現在時刻をファイル名に使う
str_now = datetime.now().strftime("%Y%m%d%H%M%S")
# Excelを開く
file_pass = '/content/gdrive/My Drive/Colab Notebooks/'
xlsx_workbook = xl.load_workbook(file_pass+'maze.xlsx')
xlsx_worksheet = xlsx_workbook.active
# スタートとゴールを記載する
cell_font = xl.styles.Font(name='Meiryo UI')
cell_alig = xl.styles.Alignment(horizontal='center', \
vertical = 'center', \
wrap_text=True)
# スタート
start_cell = xlsx_worksheet.cell(row=1, column=1)
start_cell.value = 'S'
start_cell.font = cell_font
start_cell.alignment = cell_alig
# ゴール
end_cell = xlsx_worksheet.cell(row=INT_MAZE_SIZE, column=INT_MAZE_SIZE)
end_cell.value = 'G'
end_cell.font = cell_font
end_cell.alignment = cell_alig
# Excelに描画する。
# セル数ループ
r = 0
for c, cell in enumerate(maze):
# 罫線の変数
border = xl.styles.Border()
# 壁の数だけループ
for d, wall in enumerate(cell):
# 壁だけ処理
if wall == 1:
cell_style = xl.styles.Side(border_style='thin', color='000000')
if d == 0: # 上
border.top = cell_style
elif d == 1: # 右
border.right = cell_style
elif d == 2: # 下
border.bottom = cell_style
else: # 左
border.left = cell_style
# 罫線を引くセルを選択
xlsx_cell = xlsx_worksheet.cell(row=r+1, column=c-r*INT_MAZE_SIZE+1)
xlsx_cell.border = border
if (c+1) % INT_MAZE_SIZE == 0: r += 1
xlsx_workbook.save(file_pass+'maze_'+str_now+'.xlsx')
いざ実行
INT_MAZE_SIZE = int(input('maze size->')) # 迷路の1辺の長さ
INT_MAZE_CELL = INT_MAZE_SIZE * INT_MAZE_SIZE # 迷路のセル数
# 迷路を生成
out_maze = create_maze()
out_excel(out_maze)
補足
- なぜかgoogle driveで開くと罫線が欠ける
- colaboratoryのファイルをダブルクリックで開くと、ちゃんと罫線が引けている。謎。
-
壁
が強化学習で使用できる。 - A4でいい感じに印刷できるのはN=33ぐらい?