PySimpleGUIでマインスイーパ

PySimpleGUIとは

Pythonで簡単にGUIを作成するためのライブラリです。
https://pysimplegui.readthedocs.io/en/latest/

PythonでGUIを作成する際は、tkinter、Qt、Remi、WxPythonなどを使いますが、簡単な実装でもコード量が多くなりがちです。

それを解決するのがPySimpleGUIで、上記の軽量なラッパーとして機能します。

標準ライブラリではないので、各自インストールしてください。

1
2
3
pip install pysimplegui
or
pip3 install pysimplegui

基本的な使い方

sg.popup

1
2
3
4
5
6
7
8
sg.popup('popup')  # Shows OK button
sg.popup_ok('popup_ok') # Shows OK button
sg.popup_yes_no('popup_yes_no') # Shows Yes and No buttons
sg.popup_cancel('popup_cancel') # Shows Cancelled button
sg.popup_ok_cancel('popup_ok_cancel') # Shows OK and Cancel buttons
sg.popup_error('popup_error') # Shows red error button
sg.popup_timed('popup_timed') # Automatically closes
sg.popup_auto_close('popup_auto_close') # Same as PopupTimed

ボタンのメッセージやポップアップのタイトルを変えることもできます。

1
sg.popup('マスに対する操作を選択してください',custom_text=('open', 'flag'),title='')

sg.Button

例えば二つのボタンを並べて表示しする場合は以下のように使います。

1
layout =  [[sg.Button('0'),sg.Button('1')]]

マインスイーパ

公式のサンプルコードとしてマインスイーパがあるのですが、GUIを提供するのみでゲームロジックが組まれていませんでした。

そこでシンプルなルールのみで実装しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import PySimpleGUI as sg
from random import randint
import numpy as np
from scipy import signal
MAX_ROWS = MAX_COL = 10
board = [[randint(0,1) for j in range(MAX_COL)] for i in range(MAX_ROWS)]
board = np.zeros(MAX_ROWS*MAX_COL).astype(np.int)
idx = np.random.choice(len(board), 3, replace=False) # 爆弾をランダムに3箇所設置
board[idx] = 1
board = board.reshape(MAX_ROWS, MAX_COL).tolist()
filter = np.array([ # 二次元畳み込みで爆弾の数をカウント
[1,1,1],
[1,0,1],
[1,1,1]
])
counter = signal.convolve2d(board, filter, 'full') # 畳み込みとサイズが大きくなるので、
board = counter[1:-1,1:-1] - board # スライスする。ここでboardを引くことで、爆弾マスは-1になる
print (board)
board = board.tolist()
layout = [[sg.Button('?', size=(4, 2), key=(i,j), pad=(0,0)) for j in range(MAX_COL)] for i in range(MAX_ROWS)]
window = sg.Window('Minesweeper', layout)
while True:
event, values = window.read()
print (event,values)
if event in (None, 'Exit'):
break
select = sg.popup('マスに対する操作を選択してください',custom_text=('open', 'flag'),title='')
print('select', select)
if select is 'open':
window[event].update(board[event[0]][event[1]], button_color=('white','black'))
if board[event[0]][event[1]]==-1:
sg.popup("You Lose")
break
elif select is 'flag':
state = window[event].GetText()
if state == '?':
window[event].update('F', button_color=('red',None))
elif state == 'F':
window[event].update('?', button_color=('white',None))
window.close()

ポイント

爆弾のカウント

フィルタを用意して畳み込みを行うことで周囲の爆弾をカウントしています。

1
2
3
[1,1,1],
[1,0,1],
[1,1,1]

フラグ機能

マスをクリックするとポップアップが開き、「マスを開く」か「フラグをON/OFFする」か選択ができます。
GetTextで格納されている値が取得できるので、これを用いてF(フラグON)?(デフォルト、フラグOFF)を切り替えます。

1
2
3
4
5
state = window[event].GetText()
if state == '?':
window[event].update('F', button_color=('red',None))
elif state == 'F':
window[event].update('?', button_color=('white',None))

日本語版コマンドクリックリファレンス

日本語版のコマンドクイックリファレンスをKindleで購入できます。Kindle Unlimitedでも読むことができます。
ただし、kindleで販売されているものは内容は分割版(抜粋)となっています。内容が良さそうでしたら、フルのpdfを購入されると良いと思います。
購入方法も記載されています。

宣伝

機械学習エンジニアを目指している方向けに、書籍を出しました。
サクッと読める内容ですので是非お手に取ってみてください。

記事情報

  • 投稿日:2020年4月3日
  • 最終更新日:2020年8月28日