《付録》Tetrimino.py

# -*- coding: utf-8 -*-
#===============================================================================
#    Copyright (C) 2000-2008, 小泉ひよ子とタマゴ倶楽部
#
# Change History: Games
#    1988/05, Smalltalk
#    2004/09, Java
#    2005/02, C#
#    2005/03, Jython
# Change History: WPF examples
#    2008/01/25, IronPython 1.1.1 (download)
#    2008/08/22, IronPython 1.1.2 (download)
#    2008/03/16, ver.2.0, WPF
#    2008/00/00, ver.2.1, IronPython 1.1.2 
#===============================================================================
from System.Windows import *
from System.Windows.Media import *
from System.Windows.Shapes import *

from hexagon import HexStone
## --------------------               
class Omino(object):
    _mat1 = [
        ( 1, -2),
        ( 2,  0),
        ( 1,  2),
        (-1,  2),
        (-2,  0),
        (-1, -2),
        ]
    _mat2 = [
        ( 0, -4),
        ( 2, -4),
        ( 3, -2),
        ( 4,  0),
        ( 3,  2),
        ( 2,  4),
        ( 0,  4),
        (-2,  4),
        (-3,  2),
        (-4,  0),
        (-3, -2),
        (-2, -4),
        ]
    _strokeColor = Brushes.Black

    def __init__(self, x, y, phase=0,
            color=None, mino1=, offset=1,
            ):
        self.phase  = phase
        self.color  = color
        self.mino1  = mino1
        self.offset = offset

        self.matrix1 = self._matrix(x, y, self._mat1)
        self.shape   = self._shape (x, y, self.phase)

    def _matrix(self, x, y, matrix):
        M = 2
        Ox = HexStone._dx; W = HexStone._width
        Oy = HexStone._dy; H = HexStone._height

        X = Ox + W*x
        Y = Oy + H*y
        self.pivot = self.pointCollection([Point(X+dx*M, Y+dy*M)
            for dx, dy in HexStone(X, Y, False).vertices])

        s = 
        for ix, iy in matrix:
            X = Ox + W*(x+ix)
            Y = Oy + H*(y+iy)
            points = self.pointCollection([Point(X+dx*M, Y+dy*M)
                for dx, dy in HexStone(X, Y, False).vertices])
            s.append(points)
        return s

    def _shape(self, x, y, phase):
        self.phase = self._rotate1(phase)
        
        s = 
        for n in self.mino1:
            s.append(Polygon(
                HorizontalAlignment=HorizontalAlignment.Center,
                VerticalAlignment=VerticalAlignment.Center,
                Stroke=self._strokeColor,
                Fill=self.color,
                Points=self.matrix1[self._rotate1(n)],
                ))
        return s

    def rotateClockwise(self):
        self.phase = self._rotate1(1)
        for e, n in zip(self.shape[self.offset:], self.mino1):
            e.Points = self.matrix1[self._rotate1(n)]

    def pointCollection(self, vertices):
        s = PointCollection()
        for e in vertices:
            s.Add(e)
        return s

    def _rotate1(self, n):
        return (n + self.phase) % 6

## --------------------               
class SigOmino(Omino):
    def _shape(self, x, y, phase):
        pivot = Polygon(
            HorizontalAlignment=HorizontalAlignment.Center,
            VerticalAlignment=VerticalAlignment.Center,
            Stroke=self._strokeColor,
            Fill=self.color,
            Points=self.pivot,
            )
        s = super(SigOmino, self)._shape(x, y, phase)
        return [pivot] + s
    
## --------------------               
class PiOmino(SigOmino):
    def __init__(self, x, y, phase=0,
            color=None, mino1=, offset=1,
            mino2=None,
            ):
        self.mino2   = mino2
        self.matrix2 = self._matrix(x, y, self._mat2)
        super(PiOmino, self).__init__(x, y, phase,
            color, mino1, offset)

    def _shape(self, x, y, phase):
        s = super(PiOmino, self)._shape(x, y, phase)
        for n in self.mino2:
            s.append(Polygon(
                HorizontalAlignment=HorizontalAlignment.Center,
                VerticalAlignment=VerticalAlignment.Center,
                Stroke=self._strokeColor,
                Fill=self.color,
                Points=self.matrix2[self._rotate2(n)],
                ))
        return s

    def rotateClockwise(self):
        super(PiOmino, self).rotateClockwise()
        for e, n in zip(self.shape[len(self.mino1)+1:], self.mino2):
            e.Points = self.matrix2[self._rotate2(n)]
    
    def _rotate2(self, n):
        return (n + self.phase*2) % 12

## --------------------               
class TetriminoC(Omino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 1, 2, 3), color=Brushes.Brown,
            offset=0,
            )
## --------------------               
class TetriminoO(SigOmino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 1, 2), color=Brushes.Yellow,
            )
class TetriminoP(SigOmino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 1, 3), color=Brushes.Magenta,
            )
class TetriminoB(SigOmino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 2, 3), color=Brushes.Purple,
            )
class TetriminoY(SigOmino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 2, 4), color=Brushes.Gray,
            )
## --------------------               
class TetriminoZ(PiOmino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 2), mino2=(0,), color=Brushes.Red,
            )
class TetriminoL(PiOmino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 3), mino2=(0,), color=Brushes.Orange,
            )
class TetriminoI(PiOmino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 3), mino2=(1,), color=Brushes.Cyan,
            )
class TetriminoJ(PiOmino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 3), mino2=(2,), color=Brushes.Blue,
            )
class TetriminoS(PiOmino):
    def __init__(self, x, y, phase=0):
        super(self.__class__, self).__init__(x, y, phase,
            mino1=(0, 4), mino2=(2,), color=Brushes.Lime,
            )
## --------------------               

テストケースを俯瞰する(1)

既存のアプリケーションに組み込む前に、それとは独立した環境下のテストケースで、新規のモジュールの動作を確認します。


既存のモジュール hexagon.py を再利用しながら、新たなモジュールの動作を検証するために、テストケースを作成します。


《読者への課題》類似する複数のクラスが存在するのは不適切と考えて、共通のクラスから各種のテトリミノを生成する方針を採用すると、どのような長所/短所があるか検討してください。また、長所/短所の判断が逆転するのは、将来どのような仕様変更があった場合か考察してください。□

テストケースを記述する(1)

Jython で作成した)既存のモジュール hexagon.py を再利用しながら、新たなモジュールの動作を検証するために、テストケースを作成します。

class ExWindow(Window):
    def init(self):
        target = "tabControl", "button",
        self._Controls(target)

        self.items = {}
        for e, mino in TestCase():
            self.items[e] = mino
            item = TabItem(Header=e)
            self.tabControl.Items.Add(item)            
            panel = Canvas()
            item.Content = panel
            self.addMino(panel, mino)

        self.tabControl.SelectionChanged += self.selectionChanged
        self.button.Click += self.click

ジェネレーター TestCase によって、10種類のテトリミノが得られます。

    def selectionChanged(self, sender, e):
        self.mino = self.items[sender.SelectedItem.Header]
        self.button.Content = self.state()

任意のタブを選択すると、各テトリミノの状態がボタンに表示されます。

   def click(self, sender, e):
        self.mino.rotateClockwise()
        sender.Content = self.state()

ボタンをクリックすると、テトリミノが(時計回りに)回転 rotateClockwise して、その状態が再表示されます。

class TestCase:
    types = "COpbYZLIJS"

    def __iter__(self):
        m = self.items()
        for e in self.types:
            yield e, m[e]

    def items(self):
        return dict((e, eval("Tetrimino%s(4, 4)"%e.upper()))
            for e in self.types)

10種類のテトリミノを生成するだけで、その扱いはクライアントに委ねます。


《Note》 第1章で紹介した、 Iterator パターンを実践しています。話が横道に逸れるのは、第7章で取り上げるテストケースが、各章の内容の再確認を兼ねているからです。そのため、プログラム(完成されたゲーム)には興味があっても、プログラミングに興味のないみなさんには、焦れったい思いがするかもしれません。プログラミングに関する部分は読み飛ばして、先にプログラムだけを読み進むのも自由です。その意味でも第7章は、読者のみなさんが「プロダクト指向」か「プロセス指向」かを映し出す鏡になるかもしれません。□


Previous|3/36|Next