きっかけ
terminal上にマインスイーパーを実装したくなったので,実装しました.実はプログラミング始めたての頃に,Cで一度実装したが完成してなかったこともあり,復習も兼ねて.
*備忘録用のブログのため,細かい話はありません!(え)
マインスイーパー概要
マインスイーパは1980年代に発明された、一人用のコンピュータゲームである。ゲームの目的は地雷原から地雷を取り除くことである。
筆者も小学生くらいの頃に遊んでました.(歳がバレる)
ところで,今の小学生だとどれくらい知ってるんだろう.
ちなみに,今だとChromeで遊べるので,ぜひ
実装概要
大枠は下記サイトと同じです(多分).
ターミナル上で遊べるマインスイーパ実装
アプリ概要
-
ファイルと,マス目の大きさ,ボムの比率を指定し,実行.
-
初期位置を指定.マスが表示される.
(*初期位置,および隣接マスには爆弾が配置されないように,設定しています.) -
以下,open位置を指定していく.
ハマリポイント
二次元配列の生成
実装時に必要となった二次元配列を生成するときに,下記のコードでは,一つのリストを更新するとすべてのリストが更新されてしまうため注意.
#爆弾の位置,周囲の爆弾数を保持する二次元配列
mine_list = [["N"] * args.n]
#マスがopen済みかどうかを保持する二次元配列
opened_ls = [[False] * args.n]
下記のように,リスト内包表記で初期化して事なきを得た.
#爆弾の位置,周囲の爆弾数を保持する二次元配列
mine_list = [["N"] * args.n for i in range(args.n)]
#マスがopen済みかどうかを保持する二次元配列
opened_ls = [[False] * args.n for i in range(args.n)]
感想
単純なゲームであるが,プログラムの基本を理解していないと実装できない.そのため,基礎復習の良いきっかけとなった.
課題,改善点
- 再帰処理が含まれるため,マスの大きさによっては最大再起回数を超える可能性あり.対応策
- フラグ設置機能の実装
Code
コードの全容は以下です.Githubでも公開しました.
import argparse
import random
import copy
import itertools
import time
def main(args):
def chk():
if args.n > 99:
args.n = 99
if args.bomb_rate >= 1 or args.bomb_rate <= 0:
args.bomb_rate = 0.5
return args
def create_mine_map(init_w, init_h):
def num_bomb(mine_list, iw, ih):
num_bomb = 0
for i in range(-1, 2):
for j in range(-1, 2):
if iw+i < 0 or iw+i >= args.n or ih+j < 0 or ih+j >= args.n:
continue
elif mine_list[iw+i][ih+j] == "B":
num_bomb += 1
return num_bomb
mine_list = [["N"] * args.n for i in range(args.n)]
# add bomb
n_bomb = int((args.n ** 2) * args.bomb_rate)
bomb_count = 0
for bomb_w in range(args.n):
for bomb_h in range(args.n):
# bomb設置
if bomb_count >= n_bomb:
break
if random.randint(0, 100) > 100 * (1 - args.bomb_rate):
# 初期入力位置と周辺は除外
if bomb_w != init_w and bomb_h != init_h and \
bomb_w != init_w - 1 and bomb_h != init_h - 1 and \
bomb_w != init_w + 1 and bomb_h != init_h + 1:
mine_list[bomb_w][bomb_h] = "B"
bomb_count += 1
# increment around bomb
for i in range(args.n):
for j in range(args.n):
if mine_list[i][j] == "N":
mine_list[i][j] = num_bomb(mine_list, i, j)
return mine_list, bomb_count
def open_map(mine_list, open_w, open_h, opened_ls):
if mine_list[open_w][open_h] == "B":
opened_ls = [[True] * args.n for i in range(args.n)]
return opened_ls
opened_ls[open_w][open_h] = True
if mine_list[open_w][open_h] == 0:
for i in range(-1, 2):
for j in range(-1, 2):
if open_w + i < 0 or open_w + i >= args.n or open_h + j < 0 or open_h + j >= args.n:
continue
elif not opened_ls[open_w + i][open_h + j]:
opened_ls = open_map(mine_list, open_w + i, open_h + j, opened_ls)
return opened_ls
def plt_mine(mine_list, opened_ls, play_mode=True):
h = args.n
mine_list_cp = copy.deepcopy(mine_list)
print(*["="]*(args.n+2))
if play_mode:
for i in range(h):
for j in range(h):
if not opened_ls[i][j]:
mine_list_cp[i][j] = "-"
print("PLOT MAP")
else:
print("PLOT MAP (All Opened)")
print(" ", " ", *list(range(0, args.n)))
print(*["="]*(args.n + 2))
for i in range(h):
print(i, ":", *mine_list_cp[:][i])
"chk args"
args = chk()
"while wait input(w, h)"
while True:
try:
init_w, init_h = map(int, input("input w h ({} ~ {}) >> ".format(0, args.n - 1)).split())
if init_w >= 0 and init_w < args.n and init_h >= 0 and init_h < args.n:
break
else:
print("Over" + str(args.n))
except ValueError:
print("input 2 numbers. 0 0")
"create mine"
opened_ls = [[False] * args.n for i in range(args.n)]
mine_list, n_bomb = create_mine_map(init_w, init_h)
opened_ls = open_map(mine_list, init_w, init_h, opened_ls)
"plot mine"
plt_mine(mine_list, opened_ls, play_mode=args.debug)
"while wait input(w, h)"
init_time = time.time()
init_opend_num = sum(list(itertools.chain.from_iterable(opened_ls)))
while True:
if all(list(itertools.chain.from_iterable(opened_ls))):
print("!!!!!!!!BOMBED!!!!!!!!")
break
elif sum(list(itertools.chain.from_iterable(opened_ls))) == args.n**2 - n_bomb:
end_time = time.time()
print("!!!!!!!!CLEARD!!!!!!!!")
print("YOUR TIME:{:0=3.2f}".format(end_time - init_time))
print("OPEND:{}".format(args.n**2 - init_opend_num - n_bomb))
break
try:
open_w, open_h = map(int, input("input w h ({} ~ {}) >> ".format(0, args.n - 1)).split())
if open_w >= 0 and open_w < args.n and open_h >= 0 and open_h < args.n:
"update mine"
opened_ls = open_map(mine_list, open_w, open_h, opened_ls)
"plot mine"
plt_mine(mine_list, opened_ls, play_mode=args.debug)
else:
print("Over " + str(args.n))
except ValueError:
print("input 2 numbers. 0 0")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, default=8, help="create (n, n) size")
parser.add_argument("-b", "--bomb_rate", type=float, default=0.1, help="how many bomb in the mine.")
parser.add_argument("-d", "--debug", action="store_false")
args = parser.parse_args()
main(args)