このページは本家wikiのFormulaAIのページを訳したものです。
Wesnoth Formula AIは、WesnothのAIを簡単に開発出来るAIフレームワークです。
WesnothはすでにPythonでのAI作成をサポートしていますが、これは2つの問題を抱えています。
Wesnoth Formula AIの狙いは、AIを実行できる、簡単で純粋な関数型言語をつくることであり、また、比較的専門的なスキルがあまり無いような人でも、AIの変更や微調整が可能なことです。WMLを使う事ができる人たちが、シナリオと共に、シナリオに沿ったAIを作成できるようになるべきではないでしょうか。
Wesnoth Formula AIは現在、開発の実験段階にあり、基礎的なAIであればプレイする事が出来ますので、フィードバックがもらえたら幸いです。
Formula AIを使っての開発には、[side]内で、ai_algorithm=formula_aiをセットします。
Formula AIを使うためには、[side]タグ内に[ai]タグをひとつ入れる必要があります。[ai]タグの中には、どのようにAIが動くのかを'move'属性で指定する必要があります。AIが動くたびに、この式が作動すると、moveが実行されます。移動が可能な限り式は作動し続け、移動できなくなった時点でAIはターンを終了します。また、ターンが終わってすぐの時点で式を返すコマンドがあります。
Wolf Riderを動かすだけのサンプルAIは以下のようになります。
[side]
...
ai_algorithm=formula_ai
[ai]
move="recruit('Wolf Rider')"
[/ai]
[/side]
formulaをデバッグするのに便利なのは、Wesnoth内からformulaを作動させ、結果を見る事です。formulaを作動させるには、ゲームが始まったときに'f'キーを押します。コマンドテキストボックスが現れ、そこに式を打つ事が可能になります。そして、結果が表示されます。例えば、
8 + 4
と打つと、'12'が表示されます。あなたは今、Wesnothを電卓のように使えたわけです。(*1)
Formula言語は+, -, *, /, %, ^のような、基本的な算術演算はサポートしていますが、小数や浮動小数はサポートしていません。
例
4 + 8*7 #evaluates to 60
(4 + 8)*7 #evaluates to 84
8 % 6 #evaluates to 2
5 / 2 #evaluates to 2
3 ^ 2 #evaluates to 9
また、等式をサポートしており、=と!=、それと比較演算子として、<, >, <=, >=が使用可能です。'false'は0かnullになります。他の値はtrueになります。また、and,or,notもサポートしています。
2 = 4 #evaluates to 0
2 <= 3 #evaluates to 1
0 != 1 #evaluates to 1
not 4 #evaluates to 0
not 0 #evaluates to 1
(2 < 4) and (3 > 6) #evaluates to 1 and 0 which evaluates to 0
(2 < 4) or (3 > 6) #evaluates to 1 or 0 which evaluates to 1
Formula言語は'dice'演算子をサポートしています。使用法としては、
3d5
とすると、5面のサイコロを3回振った値が結果として与えられます。意味としては、3から15までのランダムな値と同等です。
Formulaシステムは、変数を格納できるものと、計算を行うものという、違うタイプのデータをサポートしています。
'this is a text string'
[4,8,7]
のようにします。また、
[]
で空のリストになります。様々な関数はリストで機能します。
リストの特定の要素にアクセスする場合は、
my_list[0]
のようにします。これはリストの最初の要素を返します。
[ 10, 20, 30, 40][2]
とすると、「30」を返します。
辞書は対になった配列で、キーに値を割り当てます。例えば、
[ 'Elvish Fighter' -> 50, 'Elvish Archer' -> 60 ]
とすると、辞書は二つのペアで定義されます。'Elvish Fighter'には50という値が割り当てられ、'Elvish Archer'には60が割り当てられます。
キーから割り当てられた値を参照したい時は、[]を使い、
[ 'Elvish Fighter' -> 50, 'Elvish Archer' -> 60 ][ 'Elvish Fighter' ]
とすると、「50」を返します。
Formula言語は、知的な決定をするようにプレイできるシナリオについての情報が利用できるようにしなければなりません。従って、様々な入力が利用できます。入力の簡単な例として、現在のターン数なら、turnと入力します。'f'キーを使ってformulaコマンドラインを起動して、
turn
と入力すれば、AIのターン数が表示されます。
'turn'は単純な数字です。しかしながら、入力には他の入力を含んだ複雑なタイプや、リスト型の入力もあります。例えば、'my_units'には全てのAIユニットのリストが含まれています。
複雑な入力とは、様々な入力が含まれたユニットのようなものを指します。ユニットのインスタンスを'u'とすると、'u.loc.x'とすることでユニットのx座標を取得できます。 unitオブジェクトにはlocationというオブジェクトが入っていて、locationオブジェクトにはxとyという入力が入っています。こうしてユニットのx,y座標が取得できるという訳です。
利用可能な値
これらの値は、AI formulaか、コマンドラインから呼び出す事ができます。
Formula言語には多くの複雑なタスクを実行する組み込み関数が数多く含まれています。これらの関数については、こちらで簡単な例と用法が参照できます。
あなたは自分の関数を定義できます。関数はパラメーターとしていくつかの入力を行える式です。ユニットにいくつかの値を与えた関数を定義したいなら、[ai]タグの次に、
[function]
name=value_unit
inputs="unit"
formula="unit.hitpoints + unit.level*4"
[/function]
のように書き加えます。定義したこの新しい関数は、入力としてユニットを取り、その上で計算を実行します。
複数の入力を関数で使いたい時は、カンマで区切ってリストにします。
inputs="attacker,defender"
この場合、'attacker'と'defender'という入力を新しい関数で定義したことになります。
インプットをよく使う場合は、以下のようにできます。先ほどの例を改善すると、
[function]
name=value_unit
inputs="unit*"
formula="hitpoints + level*4"
[/function]
のようになります。入力の最後に'*'を入れると、その入力がデフォルトになりますので、'unit.hitpoints'のunitが省略できるというわけです。ただし、これは一つの関数に一回しか使えません。
知っておくべき重要なことは、「自作の関数で定義した式」と、「[ai]タグ内の'move='で定義した式」の違いです。例えば、AIメンバー内のリーダーについての情報を得たいと思った場合、'my_leader'という式を書きます。自作の関数内で'my_leader'を使うなら、関数内での入力に'ai'を追加します。
inputs="ai"
こうすることで、リーダーの情報である'ai.my_leader'を利用できます。もしくは、
inputs="ai*"
と書けば、シンプルに'my_leader'でも情報を利用できます。
自作の関数で定義できる'def'については、keywordの項を見てください。
Formula AI スクリプトでは、#で囲むとコメントになります。
#Define opening move#&br;def opening(*ai)
if(turn = 1,
move(loc(11,23), loc(14,22)),
[])
コメントは、行の最後に入れる事も出来ます。
def opening(*ai)
if(turn = 1,
move(loc(11,23), loc(14,22)), #capture village#
[]) #do nothing#
式の中に入れる事も出来ます。
def opening(*ai)
if(turn = 1,
move(loc(11,23) #my_leader#, loc(14,24) #closest village#),
[] #do nothing# )
Formula言語には、プリミティブな機能のための予約語があります。いまのところ、以下のようになっています。
式の中のステートメントを定義します。カンマで区切ると複数定義できます。
formula> where <comma-separated list of statements>
式の例は以下のようになります。
a + b where a = 2, b = 4
結果は6になります。
AIが利用できる全ての組み込み関数、自作の関数のリストを返します。
以下のような構文を使うと、関数を作成できます。
def function_name(arg1, arg2, ....) function_body
例
def sum(x,y) x + y
この場合、2つの引数を与えると、合計を返します。
いくつかのユニットには式を指定できます。それを実現する簡単な方法は、以下のようにします。
[unit]
...~ formula="move(me.loc, loc(me.loc.x, me.loc.y - 1))"~[/unit]
自作のユニット式はターンの最初に実行されます。上記の式は毎ターンごとにユニットを1hex北に移動させるというものです。'me'を使うと、そのユニット自身の情報を利用できます。
また、特定のユニットのAIを式で定義できます。
[unit]
...~ formula="if(attack, attack, move(me.loc, choose(unit_moves(me.loc), -distance_between(self, me.vars.guard_loc))))~ where attack = choose(filter(attacks, units = [me.loc] and distance_between(me.vars.guard_loc, target) <= me.vars.guard_radius and unit_at(target).side=me.vars.hostile_side-1 ), avg_damage_inflicted)"~ [ai_vars]~ guard_radius=3~ guard_loc="loc(8,5)"~ hostile_side=1~ [/ai_vars]~[/unit]
この式は、guard_locで位置情報を取得して、ユニットがguard_lecから3hex以内に1面からのみ攻撃できる相手がいるかどうか確かめます。
サポートしている値のタイプは以下のものです。
variable=3
name="'I am variable'"
number_list=[ 1, 2, 3]
map=[ 'Elvish Archer' -> 70, 'Elvish Shaman' -> 60 ]
place="loc(X,Y)"
式は違うファイルとして分割する事が出来ます。例えば、
[unit]
...~ formula={my_unit_formula.fai}~[/unit]
とすると、ユニットの式はmy_unit_formula.faiというファイルを参照します。
必須ではありませんが、faiファイル内では次のようにするのがおすすめです。
faifile '<filename>'
...
faiend
こうすると、formulaシステムがファイルを実行したときにエラーを報告してくれるので、大変便利です。my_unit_formula.faiというファイルであれば、
faifile 'my_unit_formula.fai'
...
faiend
とします。
ctagsの基礎的なサポートとして、次の内容を.ctagsに追加できます。(ファイルが無い場合は作成してください)
--langdef=formulaai --langmap=formulaai:.fai --regex-formulaai=/^def[ \t]*([a-zA-Z0-9_]+)/\1/d,definition/
Vimのようなctagsのプラグインを使えるエディタでは特に有効です。
total: - today: - yesterday: -
*1 ver1.5.9 Mac OS Xではfキーを押しても変化はありませんでした。