ルールベースAI

■概要

ルールベースAIとはルール毎に挙動を変更するAIのことで、
ルールはプログラム上では条件分岐により表現されます。
このAIはシンプルなゲームのAIに適しており、
複雑な仕様のAIを実現したい場合には不向きとされています。

●ルールの構成
	全てのルールは「条件」と「挙動」の二つで構成されており、
	この二つがあって初めて「ルール」として扱えます。

●サンプル
	ルールベースのサンプルコードを用意しています。

	・リンク
		サンプル

	・言語
		C++

	・環境
		VisualStudio2015

	・内容
		RuleBaseAI
			単純なRuleBaseAIの実装
	
		RuleBaseAI02
			ルール選択処理実装

■ルールベースの例

ルールベースを適応したAIは同じ種類のルールが同時に条件を満たすことがあります。
その場合、同時にルールを実行できないので、ルールの変更やルールの選択が求められます。
このような複数のルールが同時に条件を満たした状態のことを「競合」と呼びます。
※種類とは「移動方法」や「攻撃方法」などの挙動の内容を指します。

●競合がないケース
	・シューティングの敵AIその1
		・HP70%以上
			一定間隔で弾タイプ1を出す

		・HPが30%以上
			一定間隔で弾タイプ2と3を出す
	
		・HP30%未満
			・特定部位にしかダメージが入らない
			・一定間隔で弾タイプ4と5を出す

	・シューティングの敵AIその2
		・一定間隔で弾を撃つ
		・下に移動しながら左右移動を繰り返す
		 (ジグザグに移動する)

	上記の2つの例は特に問題がありません。
	その1はHPがルール実行の条件となっており、同時に条件を満たすことがありません。
	その2では同時に条件を満たすことがあるかもしれませんが、
	「攻撃方法」と「移動方法」に分かれているのでに挙動に不都合は生じません。
	※攻撃時は停止しないといけないという仕様があるのならば
	 「攻撃方法 + 移動方法」となるので同時に条件を満たした場合問題になります。

●競合があるケース
	・その1(アクションゲームの敵AI)
		・一定間隔で待機 => 右移動 => 待機 => 左移動を繰り返す
		・プレイヤーが索敵範囲に来たら向かっていく

	・その2(RPGの敵AI)
		・プレイヤーの平均レベルが○○以下の場合、物理攻撃しか使わない
		・HPが30%未満になった場合、10%の確率で特殊魔法を使用する

	上の例は二つともルール的に同時に生じる可能性があり、
	どちらも同じ種類の挙動なので、同時には実行できません。

	・各例の同時発生条件
		その1.
			移動中にプレイヤーが索敵範囲に入ってくる

		その2.
			指定レベル以下で10%の特殊魔法選択が選ばれる

	・問題点
		その1.
			左右移動と追跡が同時にできない

		その2.
			物理攻撃しかしてはいけないのに魔法攻撃を使う

	このような同じ種類の挙動に対して同時に条件を満たした場合は
	ルール自体を変更して同時に発生しないようにしたり、
	実行するルールを選択して同時発生した挙動をコントロールしたりします。

■ルールの選択

「■ルールベースの例」でルールに競合が発生した場合の解決策として紹介した
ルールの選択の内容をもう少し詳しく説明します。
ルールの選択は「静的優先順位」「動的優先順位」「ランダム」「交互設定」があります。

●静的優先順位
	静的優先順位は最初に優先順位を設定し、競合が発生した場合は
	優先順位の高いルールの挙動を実行します。

	・サンプル例
		RuleBaseAI02では「ActionID~」でルール挙動の宣言をしており、
		この値が大きいほうを優先して実行するようにしています。
		
		・例
			// 優先順位選択
			#elif RULE_SELECT == PRIORITY_SEELCT
				if (ActionIdSearchFood > ActionIdGoShop)
				{
					fire_rule = ActionIdSearchFood;
				} else {
					fire_rule = ActionIdGoShop;
				}

●動的優先順位
	動的優先順位は状況に応じて、優先順位を計算して順位が
	高いルールの挙動を実行します。

	・サンプル例
		お店の距離と食べ物の距離が近いほうを優先して
		実行するようにしています。

		// 評価選択
		#elif RULE_SELECT == EVALUTION_SELECT
			if (m_FoodManager->GetNearestDistance(this) <
				m_ShopManager->GetNearestDistance(this))
			{
				fire_rule = ActionIdSearchFood;
			} else {
				fire_rule = ActionIdGoShop;
			}

●ランダム
	競合した場合、ランダムで挙動を決定します。

	・サンプル例
		サンプルではお店と食べ物の選択をランダムに行っています。

		// ランダム選択
		#if RULE_SELECT == RANDOM_SELECT
			int random = rand() % 2;
			int base_shift = 1;
			fire_rule = 1 << (random + base_shift);

●交互設定
	競合した場合、交互に動作を実行します。

	・サンプル例
		サンプルではお店の挙動と食べ物の挙動の終了時に
		値を保存するようにして、次回の挙動で同じ内容の
		動作をしないようにしています。

		// 交互選択
		#elif RULE_SELECT == ALTERNATE_SELECT
			if (m_BeforeSelectAction == ActionIdSearchFood)
			{
				fire_rule = ActionIdGoShop;
			} else {
				fire_rule = ActionIdSearchFood;
			}