コンテンツにスキップ

見えない敵を探す

敵が壁のむこうにいて見えないときは、道をさがして近づきます。見えたら、止まって撃ちます。

Python で書く(そのまま動く bot)

Section titled “Python で書く(そのまま動く bot)”
見えたら撃つ。見えないときは、覚えておいた道を進む
path = []
残り = 0
def update(state):
global path, 残り
me = state.me
en = state.enemy
want = angle_to(me.x, me.y, en.x, en.y)
見える = line_of_sight(state.map.grid, me.x, me.y, en.x, en.y)
# 見えているあいだは、止まって狙って撃つ
if 見える:
合っている = abs(angle_diff(want, me.angle)) < 5
return {"drive": "stop", "steer": want, "fire": 合っている, "barrier": False}
# 見えないとき:10 回に 1 回だけ、道を探し直す
if 残り <= 0 or len(path) < 2:
path = find_path_bfs(state.map.grid, (me.grid_x, me.grid_y), (en.grid_x, en.grid_y))
残り = 10
残り = 残り - 1
if len(path) < 2:
return {"drive": "stop", "steer": me.angle, "fire": False, "barrier": False}
# すでに着いたマスは、道からはずす
nx, ny = path[1]
if me.grid_x == nx and me.grid_y == ny:
path = path[1:]
if len(path) < 2:
return {"drive": "stop", "steer": me.angle, "fire": False, "barrier": False}
nx, ny = path[1]
# マスの中心(+ 0.5)へ向かう
go = angle_to(me.x, me.y, nx + 0.5, ny + 0.5)
return {"drive": "forward", "steer": go, "fire": False, "barrier": False}
  1. まず line_of_sight 見えているなら、道をさがす必要はありません
  2. 見えないときだけ find_path_bfs で、通るマスのリストを出します
  3. path[0] はいま自分がいるマスです。進む先は path[1] です
  4. マスの中心は (x + 0.5, y + 0.5)+ 0.5 を忘れると、マスの角に向かってしまいます

アリーナの CPU「スナイパー」(動かずに、見えた相手だけを撃つ)と、いつものマップで 40 試合(左右を入れかえて 20 マップぶん)戦わせました。

勝ち
敵のほうを向いて、その場から撃つだけ(敵のほうを向く、向きの許容 3 度)6 / 40
この bot(見えないときは道を探して近づく)34 / 40

「CPU カード」の「くせ」(あいだに壁があるあいだは撃ってこない)を知って、近づいて見えるようにする作戦です。

道さがしは重い処理です。毎回やると、そのぶん命令数を使います。 そこで、10 回に 1 回だけ探し直して、あとは覚えておきます。 path残り が、その覚えておく変数です。global の宣言を忘れると、毎回まっさらになります(bot のかたち)。

命令数の測りかたと、書き方による違いは、経路探索と計算量にあります。

  • 101 に変えて、毎回探し直してみましょう。命令数のメーターは、どう変わるでしょうか(命令数の上限
  • find_path_bfsfind_path_astar に変えてみましょう。違いは経路探索と計算量にあります
  • 同じところをぐるぐる回る。 path[1] が、もう着いたマスのままになっていませんか
  • 道が見つからない([])。 敵と自分のあいだが、壁でふさがれているのかもしれません。そのときは、止まって待ちます