見出し画像

Python×tkinterでビンゴカードを作ろう

PythonのGUIライブラリであるtkinterを使ってなんか作ろうシリーズです。今回はなんと、ビンゴカードを作ってしまおうという訳です。

「あれ?ちょっとビンゴしたい感じじゃない?」

そう、そんな時ありますよね。でも手元にビンゴカードはない。そんな時にPC上にビンゴカードを表示させたら、ビンゴゲームがすぐできる訳です。

そんな場面あるんすかね・・・それは置いておいて・・・

まあまあ、苦労したのでお披露目させてください。。。

1. ビンゴカードの要件

まずは要件を整理します。

・5×5マスのカードを用意する
・各マスには数字を表示する
・一番左の列は1~15までから5つの数字をランダムで記載
・上記同様順番に16~30、31~45, 46~60, 61~75から5つの数字をランダムで表示
・真ん中のマスは✕(初めから穴をあける)
・カードはプレイヤー人数分、複数枚用意できるようにする

2. 完成形イメージ

プログラム実行後、初期画面

(1) プレイヤー人数を入力し、カード作成ボタンを押下します。

画像3

(2) CUI(ターミナル・コマンドプロンプト等)上で、プレイヤー名を入力します。(人数分繰り返す)

画像3

(3) サブウインドウとして、プレイヤー名のカードが作成されます。

画像3

(4) カードの各マスはクリックすると穴をあける(ボタンを非活性化)ことができます。

画像4

3. ビンゴカード作成プログラムのポイント

大きくは以下3つがポイントかと思います。

・ビンゴカードをコードとしてどのように実装するか
・ビンゴカードをGUIとしてどのように実装するか
・サブウインドウの作成方法

それぞれのポイントについてコードの書き方と解説をし、最後に希望の方だけにコード全文を公開します。

4. ビンゴカードをコードとしてどのように実装するか

ビンゴカードは5×5のマトリクスです。
私はリスト型を使いました。リストにはリストを格納することができます。
これを「多重リスト」または「多次元リスト」などといいます。

先ほどコマンドプロンプト上にも表示させてましたが、上記のビンゴカードは実は以下のような多重リストです。

画像5

リストの中に、5個数字を保有するリストが格納されています。コードとしては以下のようになります。

import numpy as np
import random

class BingoCard:

   '''
       Bingoカードクラス
       1~15、16~30、31~45、46~60、61~75の範囲から5つ数字を選択し、
       5×5の多重リストを作成するとともに、サブウインドウを作成する
   '''
   #コンストラクタ
   def __init__(self):
       self.player_name = input("プレイヤー名を入力してください:")
       self.card = []

   #カード生成機能
   def generate_card(self):
       numbers = []
       card_numbers = []
       upper = 16
       under = 1
       
       while len(card_numbers) < 5:
           [numbers.append(number) for number in range(under, upper)]
           print(numbers)
           
           card_numbers.append(np.random.choice(numbers, 5 ,replace = False))
           print(card_numbers)
           upper += 15
           under += 15
           numbers = []
       else:
           self.card = np.array(card_numbers).T.tolist()
           self.card[2][2] = 'X'

       return self.card

(1) ビンゴカードクラス

人数分カードを作成する必要があるため、クラスを定義しています。
※クラスって何?という方は以下を参照ください。

(2)  インスタンス変数でプロパティを持たせる

    #コンストラクタ
   def __init__(self):
       self.player_name = input("プレイヤー名を入力してください:")
       self.card = []

プレイヤー名とカード情報をプロパティとして持たせます。
上の記事を見るのが面倒なあなたのために、簡単にいうとステータスみたいなものです。

ビンゴカードという設計図(クラス)を実体化させた場合に、そのビンゴカード(オブジェクト)には、プレイヤー名とカード情報というステータスを持たせたいよってことを書いてます。

(3) カード生成機能

   #カード生成機能
   def generate_card(self):
       numbers = []
       card_numbers = []
       upper = 16
       under = 1
       
       while len(card_numbers) < 5:
           [numbers.append(number) for number in range(under, upper)]
           
           card_numbers.append(np.random.choice(numbers, 5 ,replace = False))
           print(card_numbers)
           upper += 15
           under += 15
           numbers = []
       else:
           self.card = np.array(card_numbers).T.tolist()
           self.card[2][2] = 'X'

       return self.card

ビンゴカードクラスには、カード生成機能というメソッドを持たせています。

多重リストを扱う際には、numpyというPythonのモジュールを使うと非常に便利です。なお、標準ライブラリでないため、Anaconda等で一緒にインストールしていない場合は、pip installをしたうえで利用してください。
(分からない方は以下記事の項番3を参照ください。)

以下のような手順で、カードの数字を生成していってます。

ビンゴカード解説

これを5行分まずは繰り返します。

まずは、numbersというリストに1~15までの数字を全て格納し、numpyのrandom.choiceメソッドで、5つ数字をランダムに抽出したリストを、card_numbersリストに追加しています。
(オプションでreplace=Falseにすると、重複排除できます)

whileループで、5つリストがcard_numbersリストに追加されるまでunderとupperに15ずつ加算しながら繰り返し処理しています。

       while len(card_numbers) < 5:
           [numbers.append(number) for number in range(under, upper)]
           
           card_numbers.append(np.random.choice(numbers, 5 ,replace = False))
           print(card_numbers)
           upper += 15
           under += 15
           numbers = []

多重リストができると以下のようになります。

画像7

これを・・・縦横変換して真ん中の数字を✕にすると・・・

        else:
           self.card = np.array(card_numbers).T.tolist()
           self.card[2][2] = 'X'

whileループの後のelse句は、ループが終了したら行われる処理となります。

再びnumpyのメソッドを利用し、(多重リスト).Tで行列の入れ替え、.tolist()でnumpyのarraryという型から、通常のリスト型に変換をしています。

そのうえで、3行目の3列目(インデックスの指定は[2][2]となる)の数値をXにしています。

これをBingoCardのプロパティである、self.cardに格納しreturnで戻り値として返すようにしています。
これにより、以下のようにビンゴカードの中身ができあがります。

画像8

コードを試してみましょう。
Pythonファイルを実行します。

画像9

まずは、card_1とcard_2変数を用意して、BingoCardクラスのインスタンスを作成します。

作成時にプレイヤー名の入力を求められるので入力します。
まずは、プロパティを確認してみましょう。

画像10

player_nameはそれぞれ、インスタンス作成時に入力したプレイヤー名が表示されました。cardプロパティは、まだ空のリストの状態です。

次に、generate_cardメソッドを実行して、再度cardプロパティを確認します。



「5個の数字を格納するリストを5個格納するリスト」、リスト内にリストを保有するものを多重リストや多次元リストといいます。以下のよう

画像11

はい、多重リストがそれぞれ作成され、オブジェクトのプロパティとしても更新されたことが確認できました。

ただ、ここまではあくまでCUI、ターミナルやPythonのIDLE上での話です。
これをGUI(画面)として表示する方法を次の項番で書いていきます。

5. ビンゴカードをGUIとしてどのように実装するか

次に、Pythonの標準ライブラリであるtkinterを使って画面表示されるようにしていきます。

(1) サブウインドウを表示させる

        #サブウインドウ作成
       window_name = self.player_name
       print(window_name)
       globals()[window_name] = Toplevel()
       globals()[window_name].title(window_name)
       globals()[window_name].geometry("460x475")
       
       #card_frame作成
       card_frame = ttk.Frame(globals()[window_name])
       card_frame.grid(row=0, column=0, sticky=(N,W,S,E))

サブウインドウを表示させるには、tkinterのToplevelを使用します。

globals()[window_name]はこのコードを発展させてサブウインドウ自体を指定する場合のためにプレイヤー名を変数として定義するためのものです。

また、この後ボタンを配置するために、frameを作成しています。

tkinterの詳細は、以下の記事に詳細を記載してます。良かったら、読んでみてください!

ボタンを配置していくフレームまで作ったら、あとは先ほど用意し5×5の数字をボタンに割り当てて、配置していきます。

       #button_style
       button_style = ttk.Style()
       button_style.configure("button_style.TButton", font=("Lucida Console", 18), width=6)
       
       #ボタンコマンド
       def hit_num(event):
           positions = event.widget.cget("text")
           event.widget.config(state=DISABLED)
       
       #5×5のボタンを作成して数字を格納していく
       i = 0 #行
       j = 0 #列
       for i in range(5):
           for j in range(5):
               space_name = str(i)+str(j)
               #globals()[space_name] = ttk.Label(card_frame, text=self.card[i][j], font=("Lucida Console",24))
               globals()[space_name] = ttk.Button(
                       card_frame, text = self.card[i][j],
                       style = "button_style.TButton",
                       padding=[0, 30, 0, 30]
                   )
               globals()[space_name].bind("<ButtonPress>", hit_num)
               
               globals()[space_name].grid(column=j, row=i)

ボタンのスタイル(見た目)を定義し、ボタンコマンドとして押下した場合に非活性化させるメソッドを定義しています。

最後に、5×5のボタンをforループ処理で一つ一つ、ボタンをウィジェットを作成して、gridで配置していっています。

gridの配置をする際に、column(列)とrow(行)のプロパティは、ループ変数のjとiを利用して定義しています。

ここまでが、BingoCardクラスと、generate_cardメソッドで中身と見た目(GUI)をサブウインドウで実装するためのものです。

6. メインプログラムの作成

では、最後にメインプログラムのコードを記載していきます。
あわせてBingoCardクラスとメソッドを定義したbingo_card_generateプログラム全文も併せて記載します。

また、おまけでビンゴマシーン的なプログラムも記載します。
カードとマシーンがあれば、どこでもビンゴができますね!!

ここから先は

5,296字 / 1画像

¥ 500

この記事が気に入ったらサポートをしてみませんか?