テトリスのソースを読む(その1)

全体で450行程度のコード(http://www.zetcode.com/wxpython/thetetrisgame/)であるが、最初から読んでいく。とりあえずはコードの解説は読まずにコードだけに注目。

import wx
import random

class Tetris(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(180, 380))

        self.statusbar = self.CreateStatusBar()  # ステータスバーを作る
        self.statusbar.SetStatusText('0')    # ステータスバーに0を表示する
        self.board = Board(self)        # Boardクラスのインスタンスオブジェクト
        self.board.SetFocus()     # ???
        self.board.start()       # クラスBoardのメソッドstart

        self.Centre()                     # 画面の表示位置を中央にする
        self.Show(True)

...(略)...

app = wx.App()
Tetris(None, -1, 'Tetris')
app.MainLoop()

まずは、外枠となっているTetrisクラスから。これでメインのフレームが表示される。
ここで、self.board.SetFocus()なのだが、Boardはwx.Panelのサブクラスとなっていて、SetFocusはwx.Panelのメソッドのようだ。役割がよくわからないから後で考える。

次にBoardクラスの最初の部分(コンストラクタ)を見る。

class Board(wx.Panel):
    BoardWidth = 10     # クラス定数
    BoardHeight = 22
    Speed = 300
    ID_TIMER = 1

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.timer = wx.Timer(self, Board.ID_TIMER) #クラス名.定数名でアトリビュートを呼び出す。タイマーらしきもの
        self.isWaitingAfterLine = False
        self.curPiece = Shape()         # ※1
        self.nextPiece = Shape()
        self.curX = 0
        self.curY = 0
        self.numLinesRemoved = 0
        self.board = []

        self.isStarted = False
        self.isPaused = False

        self.Bind(wx.EVT_PAINT, self.OnPaint)          # ※2
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.Bind(wx.EVT_TIMER, self.OnTimer, id=Board.ID_TIMER) 
        self.clearBoard()                    # ※3
...

ここで、

  • ※2のイベント、wx.EVT_PAINT、wx.EVT_KEY_DOWN、EVT_TIMERがどのような操作に対応しているか
  • wxTimerの使い方

が現時点で不明な点。

※1のShapeクラスの定義とその関連部分は、

class Shape(object):
    coordsTable = (                                 # タプルのネスト。
        ((0, 0), (0, 0), (0, 0), (0, 0)),           #  各ブロックの要素の中心座標を与えているような感じ
        ((0,-1), (0, 0), (-1, 0), (-1, 1)),
        ((0,-1), (0, 0), (1, 0), (1, 1)),
        ((0,-1), (0, 0), (0, 1), (0, 2)),
        ((0,-1), (0, 0), (1, 0), (0, 1)),
        ((0, 0), (1, 0), (0, 1), (1, 1)),
        ((-1,-1), (0, -1), (0, 0), (0, 1),
        ((1,-1), (0, -1), (0, 0), (0, 1))
    )
    def __init__(self):
        self.coords = [[0,0] for i in range(4)]
        self.pieceShape = Tetrominoes.NoShape
        self.setShape(Tetrominoes.NoShape)
...
    def setShape(self, shape):
        table = Shape.coordsTable[shape]
        for i in range(4):
            for j in range(2):
                self.coords[i][j] = table[i][j]  # ブロックを構成する要素の座標か(4つの要素の座標x,y)

        self.pieceShape = shape    # ブロックの形の情報
...

class Tetrominoes(object):       # ブロックの形を分類している
    NoShape = 0
    ZShape = 1
    SShape = 2
    LineShape = 3
    TShape = 4
    SquareShape = 5
    LShape = 6
    MirroredLShape = 7

このあたりはまだ初期の定義段階。
※3のclearBoardは、Boardクラスの中のメソッドであり、以下のようになっている。

def clearBoard(self):
        for i in range(Board.BoardHeight * Board.BoardWidth):   #22*10=220
	    self.board.append(Tetrominoes.NoShape)   # 結構大きなリストだなー。

リストself.boardを初期化している。
”クラス名.クラス定数”という書き方をいままで自分のコードでしたことがなかったことに気づく。