Tomonori IZUMI
(Ritsumeikan University)
2013.04.09a
SImpuleBlokER (シンプルブロッカー)は、 ICFPT / RECONF研の Blokus コンテストのための対戦テスト用として、 また、ImpulseC を用いた Blokus Player 開発のためのサンプルとして 開発した Bolkus Player です。
ソースコードはハードウェアに合成可能な ImpulseC で記述しています。 FPGAボードに実装するには、 ImpulseC から生成したハードウェア記述に 対象FPGAとFPGAボードにあわせた UART通信モジュール、 トップ記述、ピンアサインなどを加えて合成・書込します。
別途、汎用のUART通信モジュールと TERASIC DE2-115用トップ記述、ピンアサイン等を提供しています。 DE2-115 向けにコンパイル済みの sof ファイルも提供しますので、 実機テスト用の対戦相手として使用するだけなら、 これをQuartus のプログラマでボードに書き込めばすぐに使用できます。
コンテスト用の Impulse CoDeveloper ライセンスや TERASIC DE2-115 FPGAボード の貸し出しがあります。 詳しくはそれぞれのサイトをご覧ください。
TERASIC社DE2-115用に合成済みのコンフィギュレーションデータを 提供しています。 QuartusII の Programmer を用いて Blokus.sof をボードに書き込んでください。 このPlayerのチームコードは'ZZ'となっています。
審判プログラムは ICFPT2013 Design Competition のウェブサイト (琉球大学長名先生提供)から入手して下さい。 簡便には、TeraTermなどの端末ソフトでも人間とプレイできます。 コマンド間のスペースや改行コードなどは無視します。
ボード上で使用可能なスイッチや表示は次の通りです。
KEY0 | リセットスイッチ |
KEY3-0 | 動作チェック用(押すとLEDGがカウンタ表示) |
LEDG7 | UART RXD (受信) |
LEDG6 | Input Stream eos |
LEDG5 | Input Stream en |
LEDG4 | Input Stream rdy |
LEDG3 | UART TXD (送信) |
LEDG2 | Output Stream eos |
LEDG1 | Output Stream en |
LEDG0 | Output Stream rdy |
HEX7-6 | 受信文字コード |
HEX5-4 | 送信文字コード |
HEX3-0 | ImpulseC 記述ハードウェアからの出力(デバグ用) |
LEDR17-0 | ImpulseC 記述ハードウェアからの出力(デバグ用) |
SW17-0 | ImpulseC 記述ハードウェアへの入力(デバグ用) |
まず、「ImpulseC用RS232Cハードウェアラッパーキット」で、全体の流れを確認してください。
次のファイルを、 ラッパーキットで作成した Impulse_project フォルダ内の ものと差し替えます。
ソフトウェアシミュレーションの際に、Application Monitorを用いると便利です。 次のように設定します。
入力の例 | ||||||||||||||||
|
ソフトウェアシミュレーションを実行すると、 端末画面と Application Monitor 画面が現れます。 Application Monitor の Producer, Consumer タブの下部画面で 入出力を確認できます。 Blokus タブの下部画面ではゲームの進行状況が確認できます。
ハードウェアを生成し、エクスポートします。
Impulseのプロジェクトフォルダの export フォルダ以下を QuartusII のプロジェクトフォルダに上書きコピーします。 合成します。
QuartusII Programmer を起動し、Blokus.sof をボードに書き込みます。
TeraTermなどの端末ソフトを使って、動作を確認します。 ボードをリセット状態から、 テスト用入力(上述filter_in.datの内容)を TeraTermの画面にタイプして入力します。 入力に応じた応答(例:'0'に対して'1ZZ'など)が返ってくることを確認します。
この設計はコンテストに興味を持っていただいた方が開発する際の サンプルとして提供しています。 対戦アルゴリズムを独自に開発するベースとして、 この設計の、 ホストとの通信やゲームの流れの制御部分、デバッグ用の部分などを 利用して頂いて構いません。(利用した場合は謝辞などに記載願います)
本設計の Blokus Player のコードは Blokus_hw.c に収められています。 主な変数、関数を次に挙げます。
対戦の流れの制御(Blokus())は、 コンテストの通信規約にのっとって送受信を行い、 相手の手を受信して盤面に置く、 自分の手を決定する関数を呼び出してその手を盤面に置き送信する、 などの処理を行います。 このBlokus()を(必要に応じて改造して)用いて、 DecideNextMove()を自身のアルゴリズムに置き換えることで、 対戦アルゴリズムに専念できます。 通信手順・方法の詳細は ICFPT2013 Design Competition の ウェブサイトをご参照ください。 なお、このコードでは1手目は固定的に定め、送信しています。
DE2-115の16進表示器、発光ダイオード、スライドスイッチを ImpulseC コーディングのデバグ等で使うために、 register型のインターフェースで接続しています。 Blokus()関数の中で co_register_read() するとその時点でのスライドスイッチの値を読み出すことができ、 また、co_register_write() するとその値を16進表示器や発光ダイオードに表示することができます。
本設計では、 次に置く駒と場所の決定(SIBR_DeceideNextMove())は、 「置ける駒・場所を見つけて、置く」だけの愚直なアルゴリズムです。 次の疑似コードのような単純な4重ループで決定しています。
foreach tile t if (not used) foreach position (x,y) foreach rotation r if acceptable put(x,y,t,r) |
ハードウェア化するプロセスにおいては記述法に次のような制約があります。
本来は、複雑な処理を設計し高性能化を目指すのであれば、 処理をプロセスとして分割することを検討すべきです。 しかし、簡便には、関数をそのまま展開することでハードウェア化できます。 それには co primitive あるいは co inline プラグマで指定します。 結果として呼び出し回数分が全てインスタンス化されるため、 場合によっては回路規模が膨大になってしまうことに注意が必要です。
入力は単純型か静的配列(int *x ではなく int x[ ])の引数で与え、 出力は単純型の戻り値で返すか、引数の静的配列に書き込みます。
/* 上記方針に沿った関数の実装例 */ void sort ( int n, char a[ ] ) { #pragma co primitive int i, j ; char tmp ; for ( i = 0 ; i < n - 1 ; i ++ ) for ( j = i + 1 ; j < n ; j ++ ) if ( a [ i ] > a [ j ] ) { tmp = a [ i ] ; a [ i ] = a [ j ] ; a [ j ] = tmp ; } } void hw_process ( co_stream Ist , co_stream Ost ) { char a [ 256 ] ; /* 静的配列 */ char b [ 1024 ] ; /* 静的配列 */ int n , m ; /* データ列を a に、個数を n に読み込み */ sort ( n , a ) ; sort ( m , b ) ; /* データ列を a から書き出し */ } |