Stage 3 盤面のクラスの基本機能を作る
3-1 新しい数を挿入する
さて、これからは board.py ファイルに移りましょう。
おいおい、ちょっとみてみたけどさ......
いかがしました?
こんな長いの全部コーディングすんの...?
まあ何回かにわけてやりますから、安心してください。
まずはいろいろインポートして、このファイルのデフォルトのロガーを先ほど 2-1 でセッティングした logger_setup 関数で local_logger を宣言します。
from config import *
import copy
import random
local_logger = logger_setup(__name__, level=INFO)
logger_setup の引数の __name__ は、このファイルの名前 board をそのまま入れてくれるものでしたね。
~~~~~~~~~~~~~~~
さて、ここからは Board というクラスに入っていきます。
クラスを使う理由はやはり「盤面の情報を一元管理しやすい」というのが大きいでしょうか。このようなゲームではクラスを使うのが常套手段と考えてもらってもいいくらいかもしれません。
で、
クラスの最初はコンストラクタっしょ
と思われるかもしれませんが、よく考えてください。盤面は最初に 2 つ数字を入れるんでしたよね。コンストラクタでその機能どうやって使うんですか?
~~~~~~~~~~~~~~~
ということで盤面に新しい数を入れていく機能を先に作ります。insert メソッドです。
def insert(self, *, logger=None):
logger = logger or self.logger
# searching for empty square
emptied = []
for row in range(self.size):
for col in range(self.size):
if self.board[row][col] == EMPTY:
emptied.append([row, col])
# when there is no empty square, you cannot insert a number
if emptied == []:
logger.info('there is no empty square')
return FAILED
# selecting an empty square randomly
target = random.choice(emptied)
# inserting 4 if random No < prob4
if random.random() < self.prob4:
self.board[target[ROW]][target[COL]] = 4
else:
self.board[target[ROW]][target[COL]] = 2
logger.info('target {} <- No. {}'.format(target, self.board[target[ROW]][target[COL]]))
return SUCCEEDED
最初に引数から見ていきましょうか。
def insert(self, *, logger=None):
キーワード引数に logger を入れているのは
この関数はさっき作った local_logger じゃなくてこのロガーを使って欲しい
と言われた時でも対応できるようにするためです。これ、バグチェック用にもう一つファイルを作ることになったときにすごく便利ですよ。
~~~~~~~~~~~~~~~
中身ではまずはロガーの整理をします。
logger = logger or self.logger
引数でロガーが指定されたときは、指定のロガーをそのまま使います。指定されていなければ引数には None が入っていますから、クラスで主に使う self.logger を使うようにしています。
~~~~~~~~~~~~~~~
続きをご覧ください。
# searching for empty square
emptied = []
for row in range(self.size):
for col in range(self.size):
if self.board[row][col] == EMPTY:
emptied.append([row, col])
空のリスト emptied は「盤面の空いてるマスは全部ここに格納してやれ」っていうものです。2 重の for ループで巡回して空きマスがあったら append メソッドで [row, col] の情報を次々入れていきます。
途中の self.board は盤上の数のリスト、self.size は盤面の大きさです。
そしてループを抜け切って、emptied に要素が何もなかったら「数字の追加失敗」FAILED をリターンします。
# when there is no empty square, you cannot insert a number
if emptied == []:
logger.info('there is no empty square')
return FAILED
空いてるところがなかったら追加もクソもないですからね。
~~~~~~~~~~~~~~~
その次に target と題して、空きマスのリスト emptied の中からランダムに 1 つ選びます。
# selecting an empty square randomly
target = random.choice(emptied)
ここで使っている random.choice は、リストの中から一つ要素をランダムに選んでリターンしてくれるものです。
数字を入れるマスが決まったので、今度は入れる数字を決めます。
# inserting 4 if random No < prob4
if random.random() < self.prob4:
self.board[target[ROW]][target[COL]] = 4
else:
self.board[target[ROW]][target[COL]] = 2
条件分岐の上側が 4 を、下が 2 を入れる場合ですね。
random.random は 0 ~ 1 でランダムに数を出してくれるメソッドです。
あとで self.prob4 と称し、盤面に 4 を挿入する確率を決めます。例えば self.prob4 == 1/4 であれば、random.random() < self.prob4 となる確率が 1/4 ですから、このときに 4 を盤面に入れようとなるわけです。
~~~~~~~~~~~~~~~
全て終わったら「挿入成功」SUCCEEDED をリターンします。
logger.info('target {} <- No. {}'.format(target, self.board[target[ROW]][target[COL]]))
return SUCCEEDED
これにて insert メソッドはコーディング終了となります。お疲れ様でした。