応用編

ケース分け

強力な AI とは、戦況の様々な変化に細かく対応するものです。しかし、最初から様々な戦況に対応しようと思って AI を書くのは難しいものです。そこでここでは、効率良く AI を強くしていく良い方法を解説したいと思います。

たとえば、あなたの AI で、漁船を 20隻まで作りたいとしましょう。そうすると、以下のような構文を書こうと思うはずです。

(defrule
  (unit-type-count-total fishing-ship < 20)
  (can-train fishing-ship)
  =>
  (train fishing-ship)
)

そうするとまず考えられるのは、漁船を守るためのガレーを生産することです。ところがガレー船は漁船を作るよりも多くの木と金を必要とします。漁船を作るための構文とガレーを作るための構文を AI に入れたとしても、タイミングがあわない限り、まず資源消費の少ない漁船が 20隻作られ、そのあとに資源が余ってきて初めてガレーを作ることが出来るようになります。そんなことをしていては、敵のガレー船にいいように自分の漁船を沈められて行ってしまいます。

そこで考えるのは、ある一定の数だけ漁船を作ったら、護衛用のガレー船が揃うまで漁船の作成を止めることです。そんなときは、漁船を作成するための構文を二つに分割します。

(defrule
  (unit-type-count-total fishing-ship < 10)
  (can-train fishing-ship)
  =>
  (train fishing-ship)
)

(defrule
  (unit-type-count-total fishing-ship < 20)
  (unit-type-count-total galley-line >= 6)
  (can-train fishing-ship)
  =>
  (train fishing-ship)
)

上の構文のうちの最初の構文は、10隻までの漁船を無条件に作成するためのものです。そして二番目の構文は、20隻までの漁船を条件付で作成するためのものです。その条件が、ガレー船を六隻以上持っているかどうか、という判断をしています。つまり、上の構文全体で、以下のようなことをすることになります。

1. 最終的に漁船を 20隻作る
2. ただし、10隻より多く作る前に、ガレー船を 6隻以上用意する

忘れてはいけないのは、上の AI にはガレー船を作るための構文が含まれていません。もしガレー船を作らなかったら、漁船は絶対に 10隻より多くは作られません。

このように、最終目的を達成するために、細かい条件付けを行うことで構文を分割していくと良いでしょう。
 

備考 ド・モルガンの法則

注意しなければならないのは、うまく分割したつもりでも、どこかで重複してしまっていないかどうかという点です。たとえば、以下の構文を見てください。

(defrule
  (unit-type-count-total fishing-ship < 20)
  (unit-type-count-total galley-line >= 6)
  (can-train fishing-ship)
  =>
  (train fishing-ship)
)

これはさきほどの漁船作成のための構文です。この構文は、ガレーを作成する構文と組み合わせて使うべきです。上の構文が実行されないときにガレーを作成する構文を書きたい場合はどうしましょう。これには、数学的にはド・モルガンの法則を適用することで論理的に作ることが出来ます。

(defrule
  (or
    (unit-type-count-total fishing-ship >= 20)
    (unit-type-count-total galley-line < 6)
  )
  (can-train fishing-ship)
  =>
  (train galley-line)
)

上の構文は、unit-type-count-total の部分をひっくり返した条件を持っています。条件をひっくりかえす、というのはつまり、ひっくり返した条件とひっくり返す前の条件とが、どんな状況でも両方ともは満たされることはなく、かつどんな状況でもどちらか片方は満たされるようにするということです。

条件をひっくり返すには、以下の方法を取ります。

1. and は or に、or は and にする
2. 全ての条件に not を付ける、または完全に条件をひっくり返す

さきほどの例を取れば、ひっくりかえす条件を単純に表すと

A and B

ですが、ひっくり返したあとでは

(not A) or (not B)

となります。
 

敵の調査

AI を強くするには、色々な方法があります。たとえば、選択した文明に最適な部隊編成をすることや、最適な編成に合わせて研究を行ったりすることです。しかし、敵の出方を見て対応することは戦略の基本といえます。ここでは、敵の出方を調べるための方法について解説します。

敵を調べるときに AI で使える構文は沢山あります。それらの一連の構文は、頭に player- や players- という前置詞がついています。たとえば、敵の村人の人数を調べるための条件は以下の通りです。

(players-civilian-population <player-number> <rel-op> <value>)

<player-number> には、プレイヤーの番号を指定します。ですから、一番のプレイヤーの村人数が 30人以上であるかどうかを調べるための条件は以下の通りです。

(players-civilian-population 1 >= 30)

しかし、プレイヤー番号を指定するのは実用的ではありません。そこで、ワイルドカードと呼ばれる、柔軟にプレイヤーを指定するための書き方があります。たとえば、敵のうち誰か一人でも村人数が 30人以上であるかどうかを調べるには、以下のように書きます。

(players-civilian-population any-enemy >= 30)

また、敵のプレイヤー全員が村人数 30人以上になっているかどうかを調べるには、以下のように書きます。

(players-civilian-population every-enemy >= 30)

他にも、同盟プレイヤーを特定したり、コンピュータプレイヤー、人間プレイヤーだけを調べたり、中立プレイヤーを調べたりすることも出来ます。

敵の進化状態を調べる

これらの条件の中で、実用的なのは大体限られてきます。たとえば、相手が自分よりも時代を進化させているかどうかを調べたいときには、相手がどの時代なのかを調べることによって構文を組みたてていきます。

(defrule
  (current-age == dark-age)
  (players-current-age any-enemy > dark-age)
  =>
  (chat-local-to-self "進化を急ぎます")
)

(defrule
  (current-age == feudal-age)
  (players-current-age any-enemy > feudal-age)
  =>
  (chat-local-to-self "進化を急ぎます")
)

(defrule
  (current-age == castle-age)
  (players-current-age any-enemy > castle-age)
  =>
  (chat-local-to-self "進化を急ぎます")
)

上の例は、敵が自分よりも進化しているかどうかを調べるための構文ですが、サンプルなので、敵が自分よりも進化していると分かっても、自分に言い聞かせるだけで何もしません。あとは、フラグを使用するなりなんなりして、村人の作成や研究を押さえたり、逆に進化を早まらせた相手に速攻を掛けたりして、自分の AI の戦略を組みたてて行くと良いでしょう。

敵の戦力を調べる

AI は、敵の特定のユニットがどのくらいあるかどうかを調べることが出来ます。そのための条件は以下の通りです。

(players-unit-type-count <player-number> <unit> <rel-op> <value>)

いきなり具体的な例を示した方が分かりやすいでしょう。たとえば、敵に騎士が多い時に、槍兵を長槍兵にアップグレードしたい場合は、以下の構文が良いでしょう。

(defrule
  (players-unit-type-count any-enemy knight-line > 10)
  (not (research-completed ri-pikeman))
  =>
  (research ri-pikeman)
)

これを応用すると、敵の戦力の不均衡を突いた部隊編成が可能になります。ただし、もちろんいくら AI といえども、実際に視界に入らない限り、正確な調査はできません。
 


 
gomi@din.or.jp