SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

よろずプログラマーのためのPython導入ガイド

Java meets Python - 第2回 配列と別れる50の方法

よろずプログラマーのためのPython導入ガイド (4)

岬めぐり:コードを巡る旅の縁(よすが)に《承前》

ゲームの達人:ゲームを始める前に

 ゲームを始める前に、適当にコマを配置します。適当とは言っても、出鱈目という訳ではありません。整然とした状態から始めて、コマを無秩序に移動させたものを、初期状態とします。そして「整然とした状態に戻す」ことを目的(ゴール)とします。

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させます。ただし、隣接するマス目が空いていないと、移動できません。

適当 vs. 出鱈目
 皆さんは「適当に…」と言われて、戸惑ったりしませんか。後になってそれは「うまい具合に」という意味だったのか「雑でかまわない」だったのか、悩みの種になるからです。「出鱈目」というのは、もともとサイコロを振って「出たら出たその目」に従うというもので、成り行き任せという姿勢が伺えます。最も、試験のときに鉛筆を転がして答を決めるなど、出鱈目のご利益に肖ったという人も少なくないことでしょう。あっ、シャーペンの時代ですから、鉛筆はもう…。

イベント処理

 何かと厄介なイベント処理ですが、うまく付き合えば強力な味方となります。

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さえ、あのジェイソンのように復活しそうな、嫌な予感がします。

 しかし、その心配は杞憂に終わります。というのは、モジュールに対する表明や単体テストだけに頼らず、形式手法を駆使するなら、これが正しく動作することを証明できるからです。これらの話題については、後の連載で紹介します。

次のページ
ビューの構成

この記事は参考になりましたか?

よろずプログラマーのためのPython導入ガイド連載記事一覧

もっと読む

この記事の著者

小泉ひよ子とタマゴ倶楽部(コイズミヒヨコトタマゴクラブ)

http://tamago-club.cocolog-nifty.com/「楽しくなければ仕事じゃない」が私たちのモットー。99%の苦悩の連続も、1%の成功に報われます。だからこそ、この仕事が楽しくて仕方がないのです。楽をするための努力なら惜しみません。何もせず楽をしているのと、努力をしたから楽ができるのと...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

伊藤うさぎ(イトウ ウサギ)

ペンネームの「由来は」と言うと。苗字の方は、セミナー研修で同じチームになった、3人の合体ユニット名 [I:石塚, T:田川, O:尾沢] から来ています。名前の方は、同じ干支(卯:1987 年生)に因んだものです。既に2人は卒業して、残る1人がその名跡を継承しています。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1860 2007/11/13 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー