技術的なメモ

OOEulerGetterの技術的な事に関してまとめて置きます. Plugin の作成や新しい実装の参考になればとおもいます. 往々にしてこういう文書のメンテナンスは放置されます. (0.0.5での情報/0.0.7以降で変更の予定あり)

基本的な方針などについて

まずユーザインターフェースに依存しない, ゲームそのものを司るclassについて説明します.

ゲームの進行について

ゲームを進めるにあたって, 重要な働きをするのは, 次のclass(もしくはそれを継承したclass)のinstanceです:

ゲームを進行するのは基本的にはRefereeです. Refereeはゲームの進行に合わせて, Player, Outsider のメソッドを呼びゲームを進めます. Player, Outsiderはサーバ, Refereeはクライアントとして振る舞います. 大雑把に言うと, Playerは, ゲームのプレーヤに相当するオブジェクトで, 各ターンでどの手を打つかを決めます. Outsiderは, ログを記録するなどのためのもので, 実質的にはゲームの進行に影響は与えませんが 各ターン毎の情報は報告されます. RefereeはPlayerクラスのオブジェクトを2つ (先手, 後手に相当) と, Outsiderクラスのオブジェクトを0以上知っています.

ゲームが開始された時点でRefereeは, Playerstart_gameを呼び, 使用するBoardおよび先手/後手を伝えます. また, Outsiderstart_gameを呼び, 使用するBoardを伝えます. もし, 使用するBoardを扱えない場合は, PlayerFalse この流れは, Refereeannounce_game_start として実装されています.

各ターンでは, Refereeは, 先手/後手に合わせ, 対応するPlayerplayを呼びます. このメソッドを呼ぶ際に, 現在のBoardが伝えられます. メソッドが返した値が適切か(ちゃんと置ける場所かなど)を判断した後 適切であればそのcellを置きます. もし適切でなければRefereeが勝手に決めます. また, timeoutが0以上の場合は, timeoutまでに値が帰ってこない場合も Refereeが勝手に決めます. (現状ではtimeoutはmultiprocessを使って実装しているので, 標準入力を使った入力を伴うPlayerには, timeoutを設定できません.) cellを追加した後, Refereeは すべてのPlayerとすべてのOutsiderplayedを呼び出し, 現在のBoardとどのcellが置かれたかを伝えます. この流れは, Refereeplay_turnannounce_turn_end として実装されています.

置けるcellが無くなったとき, Refereeは すべてのPlayerとすべてのOutsidergame_endを呼び出し, 現在のBoardと勝者を伝えます. この流れは, Refereeannounce_game_end として実装されています.

これら (announce_game_startから announce_game_endまでの一連の流れ) は, Refereestart_gameで呼び出せます.

ゲーム盤について

ゲームに使用される盤に関して重要な役割を担っているclassは次の二つです:

Boardはゲーム盤(と勝敗のルール)を実装したものです. BoardはPolyhedronをメンバとして持っています. Polyhedronは多面体(分割)を実装したもので, 面の情報, 辺の情報, 頂点の情報, どのように描画するべきかという情報を持っています.

Boardは, 各playerのcellを持っており, 各playerの得点を計算することが主な役割です. get_point が呼ばれると, PolyhedoronにEuler数を計算させ, その値を元にplayerの得点を計算します. もしコミ(ハンデ)を実装するのであれば, このメソッドを拡張するだけで, よいと思います.

Polyhedronは, 多面体の実装です. 次の5つのメンバが本質的です:

新しいPolyhedronを使いたい時には, これらを書くだけで基本的には終了です.

facesは, facesの (ラベル) の集合frozensetです. (万一の破壊的なメソッドによる不正を未然に防ぐためfrozensetを使います.) face_as_edgesは, 各facesの (ラベル) に対し, それらに含まれる辺 (のラベル) の集合 (frozenset) を対応させるDictionaryです. face_as_verticesは, 各facesの (ラベル) に対し, それらに含まれる頂点 (のラベル) の集合 (frozenset) を対応させるDictionaryです. 面, 辺, 頂点は単なるラベルで構いません (座標などを気にする必要はありません). これだけの情報があれば, Euler数を計算するのに必要な情報は揃うからです.

layout_hintlayout_hint_typeは, 本質的にはゲームとは関係ないのですが, 多面体を表示する際に必要となります. 前述の通り, 多面体の面などの情報はラベルで管理され, その座標などの情報は無いため, ユーザインタフェースとして 実際にその多面体を表示するための情報を layout_hintlayout_hint_typeで提供します. デフォルトでは, layout_hint_type"multihex"です. layout_hint_type"multihex"のとき layout_hintは, faceのラベルのリストのリストNのリストです. 各faceのラベルのリストのリストNは, 多面体の (ある面の近傍での) 展開図を表します. 各展開図Nは, 次の様な六角形の座標系で どこにどの面を表示するかという情報を持ちます. 具体的には座標(i,j)に面N[i][j]を表示するべきであるという 情報を表します. もし空白にしたい場合はNoneとします.

(0,0)(0,1)(0,2)
(1,0)(1,1)(1,2)
(2,0)(2,1)(2,2)
(3,0)(3,1)(3,2)
(4,0)(4,1)(4,2)
                                                 

ゲームの各ステップにおいて PlayerやOutsiderに伝えられる BoardはRefereeの持っているBoardではなく, そのコピーです. 破壊的なメソッドによる不正を未然に防ぐためです. get_copied_boardをオーバライドするときは その点に注意する必要があります.

UIなどを含む方針について.

ここでは, ユーザインターフェースを含めた方針を簡単に説明します.

ユーザインタフェースでは, 使用するPolyhedron, Board, Playerを選んだ後, これらの情報を持った, Refereeを作ります. (また, ログを残す場合やゲームの進行状況をユーザに伝える場合は, そのためのものもOutsiderとして指定しておきます.) そのRefereeのstart_gameを呼びゲームを進めます. これが, 基本的な流れです. 必要に応じてこれを繰り返します.

ゲームの進行状況に応じて, 盤面を表示するためのユーザインターフェースは, Outsiderとして実装します.

各ターンでの手を決めるためのユーザインターフェースは, Playerとして実装します. Event drivenなプログラミングだと少しトリックが必要かもしれません.

Plugin

コンソール版のUIでは, 多面体, Board (得点の計算方法), Aiについてはプラグインとして追加可能なようにしています. プラグインの書き方について述べます.

polyhedrons, board_factories, ai_enginesの中にあるmoduleがプラグインとして使われます. それぞれのディレクトリに, 適当な名前のディレクトリを作り, __init__.py と適当な名前の本体ファイルを置きます. __init__.pyと本体ファイルの どちらからもooeulergetterをimportします. __init__.pyでは 次のメソッドを必ず実装します:

get_descriptionは辞書を返します. プラグインを選択する際に表示する情報を, 返します. 内容は以下の通りです.

"name"
プラグインの名称
"description"
プラグインの詳しい説明

get_optionsは 文字列とget_instanceの引数として渡す値の候補とのタプルのリストです. 文字列は, 引数として渡す値の説明です.

get_instanceは, 引数を一つとります. PlayerやPolyhedronのpluginであれば, それらのオブジェクトを返します. Boardのpluginであれば, "Polyhedronに対しBoardのオブジェクトを一つ返す関数" を返します.

思いつく今後の課題

以下に簡単に思いつく今後の課題を列挙します. これらは, もとのソースを変更することなく, 必要なclassを継承しオーバライドすることでも実装可能です. 私はつかれたので手を出さないかもしれない.


[TOP Page]