未知の目標地まで行ってみよう


目標検索命令 isTgtAhead()

コード 6.16で相手のスタート位置に行って戻る ミッションを達成することができました。 しかしあれは a25x70_level01.txt というマップに特化したものです。 例えば a25x70_level02.txt というマップがあります。 あれは俯瞰的には a25x70_level01.txt と同じですがロボットの初期位置が違います。

そのためコード 6.16 を動かしても明後日の方向に行って帰って来るだけです。

コンテストではその場でマップを発表する予定なので、ロボット自身が次に目指すべき目標 (往路なら相手スタート位置、復路なら自分のスタート位置) を探して動きを決めなくてはなりません。 そのヒントとなるのが目標検索命令 isTgtAhead() です。 isTgtAhead() は次に目指すべき目標がロボットの前にあるか後にあるのか知ることができる命令です。 なお真横は前方に入ります。

とりあえず目標物が前にあれば前進、無ければ右旋回させてみましょう。 コードは次のようになるはずです。

コード 8.1
void Loop(void){
  if (isTgtAhead())
    NaviCmd(1, 0);
  else
    NaviCmd(0, 1);
}

マップ a25x70_level01.txt で試してみると、途中まで上手くいってますが最後に失敗しています。 対応を考える前になぜこういう動きをしたのか一つ一つ考えてみましょう。

相手スタート位置に辿りつくまでは目標位置は緑のタイルです。 そのタイルは自分の前面 (灰色のタイル) に含まれているので「前進」します。

相手スタート位置に付きました。次なる目標値は自分のスタート位置 (黄色) です。 そのタイルは後方 (灰色ではないタイル) に含まれるので右旋回します。

右旋回しました。ロボットにとっての「前面」が変わります。 それでもまだ目標位置は全面に入りません。 そのためもう一度右旋回します。

右旋回するとさらにロボットにとっての「前面」が変わります。 今度は前面の中に目標位置が含まれます。 そこでロボットは前進を始めます。

まだ目標は前にあるので前進です。

後ろになったので右旋回します。

後ろになったので右旋回します。

後ろになったので右旋回します。

後ろになったので右旋回します。

後ろになったので右旋回します。

後ろになったので右旋回します。

後ろになったので右旋回します。

ということを繰り返して最後は 6 角形を描き続けます。

周囲情報 gSurTiles[]

今の描き続けて永遠にゴールにたどり着けない現象を往路で実現させたのが マップ a25x70_level03.txt です。 ロボットはゴールの回りを 2 タイル分離れて回ります。

マップ a25x70_level04.txt では隣を回り続けます。

ロボットは 18 近傍の情報を知ることができます。

この情報を使えば、a25x70_level03.txt やa25x70_level04.txt で発生する周囲を回り続ける現象から脱出できます。 a25x70_level03.txt ではロボットが目標に近づくと 8 番の位置に目標が見える時があります。 a25x70_level04.txt では 1 番の位置です。 この時、右旋回すれば目標は前面となるので回り続けることになりません。

コード 8.1
void Loop(void){
  if (gSurTiles[1] & NEXTFLAG_TILE)
    NaviCmd(0, 1);
  else if (gSurTiles[8] & NEXTFLAG_TILE)
    NaviCmd(0, 1);
  else if (isTgtAhead())
    NaviCmd(1,0);
  else
    NaviCmd(0,1);
}

N 番のタイルの情報は gSurTiles[N] と書けば得られます。 18 近傍しか得られませんので N は 0〜17 の数でなければなりません。 それ以外を指定するとプログラムがエラーで止まります (偶然止まらなくても、発見されるとレギュレーション違反でコンテスト失格になります)。 gSurTiles[1] にはタイル番号 1 番に関する情報が、 gSurTiles[8] にはタイル番号 8 番に関する情報が入ってます。 ここには様々な情報が入っているので、「今目指している場所か」という情報を抽出するには & NEXTFLAG_TILE という文字を添えます。

これでマップ a25x70_level01.txt〜a25x70_level04.txt すべてでゴール できるようになりました。

     

さてコード 8.1 ですが、最初の if 文で目標が 1 番のタイルにあれば右旋回、 二つ目の else if 文で 8 番のタイルにあれば右旋回と書いています。 同じ右旋回をするわけですから、もし 1 番のタイルか 8 番のタイルにあれば 右旋回と、次のように書いても構いません。

コード 8.2
void Loop(void){
  if ((gSurTiles[1] & NEXTFLAG_TILE) || (gSurTiles[8] & NEXTFLAG_TILE))
    NaviCmd(0,1);
  else if (isTgtAhead())
    NaviCmd(1,0);
  else
    NaviCmd(0,1);
}

もっと攻めると、 「1 番にあるか 8 番にあるか、もしくは後方にあれば」と以下のように 書こうと思ったら書けます。

コード 8.3
void Loop(void){
  if ((gSurTiles[1] & NEXTFLAG_TILE) || (gSurTiles[8] & NEXTFLAG_TILE) || !isTgtAhead())
    NaviCmd(0,1);
  else
    NaviCmd(1,0);
}

ただこれは個人的にあんまりお勧めしません。 ゴールが斜め前に見えた時と目標が後ろにある時は別の話で、 たまたまするべきことが同じだっただけです。 そういう時は処理を分けていた方がコードが見やすくなると思うからです。

目標が左前方にある時の動き

ところでマップ a25x70_level02.txt の軌跡は少しいびつです。 どうしてこんなことが起こるのでしょうか。 スタート後、目標が前にある間は前進します。

目標が後ろになりました。

目標が前に無ければ右旋回するというルールでした。 ロボットは目標が前後どちらかにあるかは分かりますが左右どちらかにあるかは 分からず、ヤマカンに頼るしかありません。

ヤマカンが外れました。 相変わらず目標は後ろです。 さらに右旋回します。

まだ後ろです。

やっと前になりました。 前進できます。

ところが 1 ステップ進んだだけでまた後ろになりました。 右旋回します。

そして前になったのでまた前進します。

という結果このような軌跡となったのです。


[戻る]