岬めぐり:コードを巡る旅の縁(よすが)に《承前》
ゲームの達人:ゲームを始める前に
ゲームを始める前に、適当にコマを配置します。適当とは言っても、出鱈目という訳ではありません。整然とした状態から始めて、コマを無秩序に移動させたものを、初期状態とします。そして「整然とした状態に戻す」ことを目的(ゴール)とします。
class PuzzlePanel(JPanel): def initialize(self): self.items = [] for y in range(self.dim): for x in range(self.dim): value = x + y*self.dim + 1 if value == self.dim2: break self.items.append(Tile(x, y, value)) self.items.append(Null)
メソッドinitializeは、コマを順番(1~15)に配置します。このとき、ボードからはみ出さないように注意します。最後に、空いたマス目Nullを配置します。
ゲームの達人:if/forが消え、最後に生き残るのは
まだ「forと別れる方法」を紹介していないので、ここでは手を加えずに(必要悪としてforを残したまま)記述しています。先に「ifを卒業した」ことで、コードの見通しがよくなるだけでなく、バグの温床となるのを防ぐ術を紹介しました。後に「forを卒業した」その日には、さらにスリムになったコードに遭遇するでしょう。今のうちに、伝統的なスタイルを模した光景を、眼に焼き付けておくのも一興です。
class PuzzlePanel(JPanel): def shuffle(self, count): for e in range(count): r = randint(0, self.dim2-1) self.detect(r%self.dim, r/self.dim).move()
メソッドshuffleは、順番に並んだコマを適当に移動させて、ゲームを開始できる状態にします。生成した乱数に従って、該当するコマを特定detectして、それを移動moveさせます。ただし、隣接するマス目が空いていないと、移動できません。
イベント処理
何かと厄介なイベント処理ですが、うまく付き合えば強力な味方となります。
class PuzzlePanel(JPanel): def this_mouseClicked(self, e): tile = self.detectTile(e) print ">>>", tile if tile.move(): self.repaint()
メソッドthis_mouseClickedは、マウスでクリックしたコマを移動します。ここでは、detectTileを再利用して、イベントが発生したコマを特定します。隣に空いたマス目があると、そのコマを移動できます。そして、移動moveできたときには、repaintを利用して、ゲーム盤を再描画します。
class PuzzlePanel(JPanel): def paintComponent(self, g): self.paintItems(g) def paintItems(self, g): for e in self.items: e.paint(g)
repaintを呼び出すと、Swingのフレームワークに準拠してpaintComponentが呼び出されます。そのため、任意のコンポーネントを再描画したいときには、このメソッドを再定義しておきます。実際の処理はpaintItemsに委ね、ボードに配置したすべてのコマを再描画paintします。
class PuzzlePanel(JPanel): def detect(self, x, y): obj = None for e in self.items: if e.detect(x, y): obj = e; break return obj class Tile(Shape): def detect(self, x, y): return self.x == x and self.y == y
メソッドdetectは、指定した座標(x, y)を持つコマを特定します。ただし、実際の処理は、各Tileインスタンスeに委ねます。最後に、その条件を満たすコマobjをリターン値とします。その一方で、クラスTileでは、指定した座標(x, y)と比較して、自分selfがその条件を満たすかどうか判定した結果をリターン値とします。
ところが、このメソッドにはNoneをリターン値とする可能性があります。これは、とても危険な香りが漂います。なぜなら、Noneにメソッド呼び出しを適用しようとすると、例外を生成するからです。再びArrayIndexOutOfBoundsExceptionさえ、あのジェイソンのように復活しそうな、嫌な予感がします。
しかし、その心配は杞憂に終わります。というのは、モジュールに対する表明や単体テストだけに頼らず、形式手法を駆使するなら、これが正しく動作することを証明できるからです。これらの話題については、後の連載で紹介します。
