ロボットシミュレーターを書いてみましょう。
ロボット工場の試験施設では、ロボットの動きを検証するプログラムが必要です。
ロボットにできる動きは3つあります。
ロボットは、無限に広がる仮想のグリッド上に置かれ、{x,y}座標(たとえば{3,8})の位置で、north、east、south、westのいずれかの方向を向いています。座標は北と東に向かって大きくなります。
その後、ロボットはいくつかの命令を受け取り、試験施設はロボットの新しい位置と、向いている方向を検証します。
テストは3つのステップに分かれています。
すべてのテストを実行するにはgo testを使います。特定のテストだけを実行したい場合は、-tagsオプションを使います。
例:
go test # run all tests
go test -tags step1 # run just step 1 tests.
go test -tags 'step1 step2' # run step1 and step2 tests
テストプログラムが必要とするさまざまなものを定義したソースファイルdefs.goが与えられます。このファイルは、ステップごとに3つのセクションに分かれています。
ステップ1を完了するには、Right、Left、Advance、N、S、E、W、Dirを定義します。ステップ2に進む前に、ステップ1を完了してください。
ステップ1ではロボットの動きを実装しましたが、まだシミュレーションとは言えません。たとえば、ソースコードのどこが「ロボット」なのでしょうか? 「グリッド」はどこにあるのでしょうか? ロボットの行動をグリッド上の位置に変換する計算は、ロボットの中にあるのでしょうか、それともグリッドの中にあるのでしょうか? 現実の世界は、これとは違います。
ステップ2では、「部屋」を導入します。小さな追加のように思えますが、「部屋」「ロボット」「テストプログラム」それぞれの役割をはっきりさせ、シミュレーションの物理を明らかにし始めるために、大きな変更を加えます。
RoomとRobotは関数として定義します。テストプログラムはこれらをゴルーチンとして起動することで「実体化」させます。情報は、Goのチャネルを通じて、テストプログラム、ロボット、部屋の間を行き来します。
Roomは「物理エンジン」だと考えてください。壁とロボットがある物理的な部屋をモデル化し、シミュレーションするものです。部屋の座標空間、ロボットと壁の位置を何らかの形でモデル化し、たとえばロボットが壁を通り抜けないようにする必要があります。Robotは行動を実行するエージェントであってほしいのですが、Roomには整合性の取れた真実を保ってほしいのです。
テストプログラムがチャネルを作成し、RoomとRobotの両方を起動します。次に、テストプログラムがRobotにコマンドを送ります。コマンドを送り終えると、コマンドチャネルを閉じます。Robotはコマンドを受け取り、実行しようとしている行動をRoomに伝えなければなりません。コマンドチャネルが閉じたことを検知すると、自分自身を終了しなければなりません。部屋は、ロボットの行動が物理的にどんな結果をもたらすかを解釈しなければなりません。ロボットが終了したことを検知すると、ロボットの最終的な位置と向きを伝える最終レポートをテストプログラムに送り返します。
ステップ3には3つの大きな変更があります。
Room3から送られる最終的な位置レポートでは、ロボットチャネルから受け取ったのと同じスライスを、位置と向きを更新したうえでそのまま返してかまいません。次の場合には、ログチャネルにメッセージを送る必要があります。