Initial cleanup of fuzzy logic.

This commit is contained in:
Paul Chote
2013-08-24 10:05:54 +12:00
parent e667ed8f1a
commit d5f65eff88
2 changed files with 124 additions and 124 deletions

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2012 The OpenRA Developers (see AUTHORS) * Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -10,64 +10,65 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using AI.Fuzzy.Library;
using System.Text;
using OpenRA.Mods.RA.Move; using OpenRA.Mods.RA.Move;
using OpenRA.Traits; using OpenRA.Traits;
using AI.Fuzzy.Library;
namespace OpenRA.Mods.RA.AI namespace OpenRA.Mods.RA.AI
{ {
class AttackOrFleeFuzzy class AttackOrFleeFuzzy
{ {
protected MamdaniFuzzySystem fuzzyEngine; MamdaniFuzzySystem fuzzyEngine;
protected Dictionary<FuzzyVariable, double> result; Dictionary<FuzzyVariable, double> result;
public bool CanAttack
{
get
{
//not sure that this will happen (NaN), it's for the safety of
if (result[fuzzyEngine.OutputByName("AttackOrFlee")].ToString() != "NaN")
return result[fuzzyEngine.OutputByName("AttackOrFlee")] < 30.0;
return false;
}
}
public AttackOrFleeFuzzy() public AttackOrFleeFuzzy()
{ {
InitializateFuzzyVariables(); InitializateFuzzyVariables();
} }
public bool CanAttack
{
get
{
var attackChance = result[fuzzyEngine.OutputByName("AttackOrFlee")];
return !double.IsNaN(attackChance) && attackChance < 30.0;
}
}
protected void AddFuzzyRule(string rule)
{
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule(rule));
}
protected void InitializateFuzzyVariables() protected void InitializateFuzzyVariables()
{ {
fuzzyEngine = new MamdaniFuzzySystem(); fuzzyEngine = new MamdaniFuzzySystem();
FuzzyVariable playerHealthFuzzy = new FuzzyVariable("OwnHealth", 0.0, 100.0); var playerHealthFuzzy = new FuzzyVariable("OwnHealth", 0.0, 100.0);
playerHealthFuzzy.Terms.Add(new FuzzyTerm("NearDead", new TrapezoidMembershipFunction(0, 0, 20, 40))); playerHealthFuzzy.Terms.Add(new FuzzyTerm("NearDead", new TrapezoidMembershipFunction(0, 0, 20, 40)));
playerHealthFuzzy.Terms.Add(new FuzzyTerm("Injured", new TrapezoidMembershipFunction(30, 50, 50, 70))); playerHealthFuzzy.Terms.Add(new FuzzyTerm("Injured", new TrapezoidMembershipFunction(30, 50, 50, 70)));
playerHealthFuzzy.Terms.Add(new FuzzyTerm("Normal", new TrapezoidMembershipFunction(50, 80, 100, 100))); playerHealthFuzzy.Terms.Add(new FuzzyTerm("Normal", new TrapezoidMembershipFunction(50, 80, 100, 100)));
fuzzyEngine.Input.Add(playerHealthFuzzy); fuzzyEngine.Input.Add(playerHealthFuzzy);
FuzzyVariable enemyHealthFuzzy = new FuzzyVariable("EnemyHealth", 0.0, 100.0); var enemyHealthFuzzy = new FuzzyVariable("EnemyHealth", 0.0, 100.0);
enemyHealthFuzzy.Terms.Add(new FuzzyTerm("NearDead", new TrapezoidMembershipFunction(0, 0, 20, 40))); enemyHealthFuzzy.Terms.Add(new FuzzyTerm("NearDead", new TrapezoidMembershipFunction(0, 0, 20, 40)));
enemyHealthFuzzy.Terms.Add(new FuzzyTerm("Injured", new TrapezoidMembershipFunction(30, 50, 50, 70))); enemyHealthFuzzy.Terms.Add(new FuzzyTerm("Injured", new TrapezoidMembershipFunction(30, 50, 50, 70)));
enemyHealthFuzzy.Terms.Add(new FuzzyTerm("Normal", new TrapezoidMembershipFunction(50, 80, 100, 100))); enemyHealthFuzzy.Terms.Add(new FuzzyTerm("Normal", new TrapezoidMembershipFunction(50, 80, 100, 100)));
fuzzyEngine.Input.Add(enemyHealthFuzzy); fuzzyEngine.Input.Add(enemyHealthFuzzy);
FuzzyVariable relativeAttackPowerFuzzy = new FuzzyVariable("RelativeAttackPower", 0.0, 1000.0); var relativeAttackPowerFuzzy = new FuzzyVariable("RelativeAttackPower", 0.0, 1000.0);
relativeAttackPowerFuzzy.Terms.Add(new FuzzyTerm("Weak", new TrapezoidMembershipFunction(0, 0, 70, 90))); relativeAttackPowerFuzzy.Terms.Add(new FuzzyTerm("Weak", new TrapezoidMembershipFunction(0, 0, 70, 90)));
relativeAttackPowerFuzzy.Terms.Add(new FuzzyTerm("Equal", new TrapezoidMembershipFunction(85, 100, 100, 115))); relativeAttackPowerFuzzy.Terms.Add(new FuzzyTerm("Equal", new TrapezoidMembershipFunction(85, 100, 100, 115)));
relativeAttackPowerFuzzy.Terms.Add(new FuzzyTerm("Strong", new TrapezoidMembershipFunction(110, 150, 150, 1000))); relativeAttackPowerFuzzy.Terms.Add(new FuzzyTerm("Strong", new TrapezoidMembershipFunction(110, 150, 150, 1000)));
fuzzyEngine.Input.Add(relativeAttackPowerFuzzy); fuzzyEngine.Input.Add(relativeAttackPowerFuzzy);
FuzzyVariable relativeSpeedFuzzy = new FuzzyVariable("RelativeSpeed", 0.0, 1000.0); var relativeSpeedFuzzy = new FuzzyVariable("RelativeSpeed", 0.0, 1000.0);
relativeSpeedFuzzy.Terms.Add(new FuzzyTerm("Slow", new TrapezoidMembershipFunction(0, 0, 70, 90))); relativeSpeedFuzzy.Terms.Add(new FuzzyTerm("Slow", new TrapezoidMembershipFunction(0, 0, 70, 90)));
relativeSpeedFuzzy.Terms.Add(new FuzzyTerm("Equal", new TrapezoidMembershipFunction(85, 100, 100, 115))); relativeSpeedFuzzy.Terms.Add(new FuzzyTerm("Equal", new TrapezoidMembershipFunction(85, 100, 100, 115)));
relativeSpeedFuzzy.Terms.Add(new FuzzyTerm("Fast", new TrapezoidMembershipFunction(110, 150, 150, 1000))); relativeSpeedFuzzy.Terms.Add(new FuzzyTerm("Fast", new TrapezoidMembershipFunction(110, 150, 150, 1000)));
fuzzyEngine.Input.Add(relativeSpeedFuzzy); fuzzyEngine.Input.Add(relativeSpeedFuzzy);
FuzzyVariable attackOrFleeFuzzy = new FuzzyVariable("AttackOrFlee", 0.0, 50.0); var attackOrFleeFuzzy = new FuzzyVariable("AttackOrFlee", 0.0, 50.0);
attackOrFleeFuzzy.Terms.Add(new FuzzyTerm("Attack", new TrapezoidMembershipFunction(0, 15, 15, 30))); attackOrFleeFuzzy.Terms.Add(new FuzzyTerm("Attack", new TrapezoidMembershipFunction(0, 15, 15, 30)));
attackOrFleeFuzzy.Terms.Add(new FuzzyTerm("Flee", new TrapezoidMembershipFunction(25, 35, 35, 50))); attackOrFleeFuzzy.Terms.Add(new FuzzyTerm("Flee", new TrapezoidMembershipFunction(25, 35, 35, 50)));
fuzzyEngine.Output.Add(attackOrFleeFuzzy); fuzzyEngine.Output.Add(attackOrFleeFuzzy);
@@ -79,100 +80,94 @@ namespace OpenRA.Mods.RA.AI
protected virtual void AddingRulesForNormalOwnHealth() protected virtual void AddingRulesForNormalOwnHealth()
{ {
//1 OwnHealth is Normal AddFuzzyRule("if ((OwnHealth is Normal) " +
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is Normal) " + "and ((EnemyHealth is NearDead) or (EnemyHealth is Injured) or (EnemyHealth is Normal)) " +
"and ((EnemyHealth is NearDead) or (EnemyHealth is Injured) or (EnemyHealth is Normal)) " + "and ((RelativeAttackPower is Weak) or (RelativeAttackPower is Equal) or (RelativeAttackPower is Strong)) " +
"and ((RelativeAttackPower is Weak) or (RelativeAttackPower is Equal) or (RelativeAttackPower is Strong)) " + "and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " + "then AttackOrFlee is Attack");
"then AttackOrFlee is Attack"));
} }
protected virtual void AddingRulesForInjuredOwnHealth() protected virtual void AddingRulesForInjuredOwnHealth()
{ {
//OwnHealth is Injured AddFuzzyRule("if ((OwnHealth is Injured) " +
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is Injured) " + "and (EnemyHealth is NearDead) " +
"and (EnemyHealth is NearDead) " + "and ((RelativeAttackPower is Weak) or (RelativeAttackPower is Equal) or (RelativeAttackPower is Strong)) " +
"and ((RelativeAttackPower is Weak) or (RelativeAttackPower is Equal) or (RelativeAttackPower is Strong)) " + "and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " + "then AttackOrFlee is Attack");
"then AttackOrFlee is Attack"));
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is Injured) " + AddFuzzyRule("if ((OwnHealth is Injured) " +
"and ((EnemyHealth is Injured) or (EnemyHealth is Normal)) " + "and ((EnemyHealth is Injured) or (EnemyHealth is Normal)) " +
"and ((RelativeAttackPower is Equal) or (RelativeAttackPower is Strong)) " + "and ((RelativeAttackPower is Equal) or (RelativeAttackPower is Strong)) " +
"and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " + "and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"then AttackOrFlee is Attack")); "then AttackOrFlee is Attack");
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is Injured) " + AddFuzzyRule("if ((OwnHealth is Injured) " +
"and ((EnemyHealth is Injured) or (EnemyHealth is Normal)) " + "and ((EnemyHealth is Injured) or (EnemyHealth is Normal)) " +
"and (RelativeAttackPower is Weak) " + "and (RelativeAttackPower is Weak) " +
"and (RelativeSpeed is Slow)) " + "and (RelativeSpeed is Slow)) " +
"then AttackOrFlee is Attack")); "then AttackOrFlee is Attack");
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is Injured) " + AddFuzzyRule("if ((OwnHealth is Injured) " +
"and ((EnemyHealth is Injured) or (EnemyHealth is Normal)) " + "and ((EnemyHealth is Injured) or (EnemyHealth is Normal)) " +
"and (RelativeAttackPower is Weak) " + "and (RelativeAttackPower is Weak) " +
"and ((RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " + "and ((RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"then AttackOrFlee is Flee")); "then AttackOrFlee is Flee");
//2 AddFuzzyRule("if ((OwnHealth is Injured) " +
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is Injured) " + "and ((EnemyHealth is NearDead) or (EnemyHealth is Injured) or (EnemyHealth is Normal)) " +
"and ((EnemyHealth is NearDead) or (EnemyHealth is Injured) or (EnemyHealth is Normal)) " + "and ((RelativeAttackPower is Weak) or (RelativeAttackPower is Equal) or (RelativeAttackPower is Strong)) " +
"and ((RelativeAttackPower is Weak) or (RelativeAttackPower is Equal) or (RelativeAttackPower is Strong)) " + "and (RelativeSpeed is Slow)) " +
"and (RelativeSpeed is Slow)) " + "then AttackOrFlee is Attack");
"then AttackOrFlee is Attack"));
} }
protected virtual void AddingRulesForNearDeadOwnHealth() protected virtual void AddingRulesForNearDeadOwnHealth()
{ {
//3 OwnHealth is NearDead AddFuzzyRule("if ((OwnHealth is NearDead) " +
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is NearDead) " + "and (EnemyHealth is Injured) " +
"and (EnemyHealth is Injured) " + "and (RelativeAttackPower is Equal) " +
"and (RelativeAttackPower is Equal) " + "and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal))) " +
"and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal))) " + "then AttackOrFlee is Attack");
"then AttackOrFlee is Attack"));
//4
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is NearDead) " +
"and (EnemyHealth is NearDead) " +
"and (RelativeAttackPower is Weak) " +
"and ((RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"then AttackOrFlee is Flee"));
//5
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is NearDead) " +
"and (EnemyHealth is Injured) " +
"and (RelativeAttackPower is Weak) " +
"and ((RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"then AttackOrFlee is Flee"));
//6 AddFuzzyRule("if ((OwnHealth is NearDead) " +
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is NearDead) " + "and (EnemyHealth is NearDead) " +
"and (EnemyHealth is Normal) " + "and (RelativeAttackPower is Weak) " +
"and (RelativeAttackPower is Weak) " + "and ((RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"and ((RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " + "then AttackOrFlee is Flee");
"then AttackOrFlee is Flee"));
//7 AddFuzzyRule("if ((OwnHealth is NearDead) " +
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if (OwnHealth is NearDead) " + "and (EnemyHealth is Injured) " +
"and (EnemyHealth is Normal) " + "and (RelativeAttackPower is Weak) " +
"and (RelativeAttackPower is Equal) " + "and ((RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"and (RelativeSpeed is Fast) " + "then AttackOrFlee is Flee");
"then AttackOrFlee is Flee"));
//8
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if (OwnHealth is NearDead) " +
"and (EnemyHealth is Normal) " +
"and (RelativeAttackPower is Strong) " +
"and (RelativeSpeed is Fast) " +
"then AttackOrFlee is Flee"));
//9 AddFuzzyRule("if ((OwnHealth is NearDead) " +
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if (OwnHealth is NearDead) " + "and (EnemyHealth is Normal) " +
"and (EnemyHealth is Injured) " + "and (RelativeAttackPower is Weak) " +
"and (RelativeAttackPower is Equal) " + "and ((RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"and (RelativeSpeed is Fast) " + "then AttackOrFlee is Flee");
"then AttackOrFlee is Flee"));
AddFuzzyRule("if (OwnHealth is NearDead) " +
"and (EnemyHealth is Normal) " +
"and (RelativeAttackPower is Equal) " +
"and (RelativeSpeed is Fast) " +
"then AttackOrFlee is Flee");
AddFuzzyRule("if (OwnHealth is NearDead) " +
"and (EnemyHealth is Normal) " +
"and (RelativeAttackPower is Strong) " +
"and (RelativeSpeed is Fast) " +
"then AttackOrFlee is Flee");
AddFuzzyRule("if (OwnHealth is NearDead) " +
"and (EnemyHealth is Injured) " +
"and (RelativeAttackPower is Equal) " +
"and (RelativeSpeed is Fast) " +
"then AttackOrFlee is Flee");
} }
public void CalculateFuzzy(List<Actor> ownUnits, List<Actor> enemyUnits) public void CalculateFuzzy(List<Actor> ownUnits, List<Actor> enemyUnits)
{ {
Dictionary<FuzzyVariable, double> inputValues = new Dictionary<FuzzyVariable, double>(); var inputValues = new Dictionary<FuzzyVariable, double>();
inputValues.Add(fuzzyEngine.InputByName("OwnHealth"), (double)NormalizedHealth(ownUnits, 100)); inputValues.Add(fuzzyEngine.InputByName("OwnHealth"), (double)NormalizedHealth(ownUnits, 100));
inputValues.Add(fuzzyEngine.InputByName("EnemyHealth"), (double)NormalizedHealth(enemyUnits, 100)); inputValues.Add(fuzzyEngine.InputByName("EnemyHealth"), (double)NormalizedHealth(enemyUnits, 100));
inputValues.Add(fuzzyEngine.InputByName("RelativeAttackPower"), (double)RelativePower(ownUnits, enemyUnits)); inputValues.Add(fuzzyEngine.InputByName("RelativeAttackPower"), (double)RelativePower(ownUnits, enemyUnits));
@@ -183,15 +178,20 @@ namespace OpenRA.Mods.RA.AI
protected float NormalizedHealth(List<Actor> actors, float normalizeByValue) protected float NormalizedHealth(List<Actor> actors, float normalizeByValue)
{ {
int sumOfMaxHp = 0; var sumOfMaxHp = 0;
int sumOfHp = 0; var sumOfHp = 0;
foreach (var a in actors) foreach (var a in actors)
{
if (a.HasTrait<Health>()) if (a.HasTrait<Health>())
{ {
sumOfMaxHp += a.Trait<Health>().MaxHP; sumOfMaxHp += a.Trait<Health>().MaxHP;
sumOfHp += a.Trait<Health>().HP; sumOfHp += a.Trait<Health>().HP;
} }
if (sumOfMaxHp == 0) return 0.0f; }
if (sumOfMaxHp == 0)
return 0.0f;
return (sumOfHp * normalizeByValue) / sumOfMaxHp; return (sumOfHp * normalizeByValue) / sumOfMaxHp;
} }
@@ -218,33 +218,40 @@ namespace OpenRA.Mods.RA.AI
{ {
if (enemy.Count == 0) if (enemy.Count == 0)
return 999.0f; return 999.0f;
if (own.Count == 0) if (own.Count == 0)
return 0.0f; return 0.0f;
float relative = (relativeFunc(own, getValue) / relativeFunc(enemy, getValue)) * normalizeByValue; var relative = (relativeFunc(own, getValue) / relativeFunc(enemy, getValue)) * normalizeByValue;
return relative.Clamp(0.0f, 999.0f); return relative.Clamp(0.0f, 999.0f);
} }
protected float SumOfValues<Trait>(List<Actor> actors, Func<Actor, int> getValue) protected float SumOfValues<Trait>(List<Actor> actors, Func<Actor, int> getValue)
{ {
int sum = 0; var sum = 0;
foreach (var a in actors) foreach (var a in actors)
if (a.HasTrait<Trait>()) if (a.HasTrait<Trait>())
sum += getValue(a); sum += getValue(a);
return sum; return sum;
} }
protected float Average<Trait>(List<Actor> actors, Func<Actor, int> getValue) protected float Average<Trait>(List<Actor> actors, Func<Actor, int> getValue)
{ {
int sum = 0; var sum = 0;
int countActors = 0; var countActors = 0;
foreach (var a in actors) foreach (var a in actors)
{
if (a.HasTrait<Trait>()) if (a.HasTrait<Trait>())
{ {
sum += getValue(a); sum += getValue(a);
countActors++; countActors++;
} }
if (countActors == 0) return 0.0f; }
if (countActors == 0)
return 0.0f;
return sum / countActors; return sum / countActors;
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -8,12 +8,6 @@
*/ */
#endregion #endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AI.Fuzzy.Library;
namespace OpenRA.Mods.RA.AI namespace OpenRA.Mods.RA.AI
{ {
class RushFuzzy : AttackOrFleeFuzzy class RushFuzzy : AttackOrFleeFuzzy
@@ -22,18 +16,17 @@ namespace OpenRA.Mods.RA.AI
protected override void AddingRulesForNormalOwnHealth() protected override void AddingRulesForNormalOwnHealth()
{ {
//1 OwnHealth is Normal AddFuzzyRule("if ((OwnHealth is Normal) " +
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is Normal) " + "and ((EnemyHealth is NearDead) or (EnemyHealth is Injured) or (EnemyHealth is Normal)) " +
"and ((EnemyHealth is NearDead) or (EnemyHealth is Injured) or (EnemyHealth is Normal)) " + "and (RelativeAttackPower is Strong) " +
"and (RelativeAttackPower is Strong) " + "and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " + "then AttackOrFlee is Attack");
"then AttackOrFlee is Attack"));
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule("if ((OwnHealth is Normal) " + AddFuzzyRule("if ((OwnHealth is Normal) " +
"and ((EnemyHealth is NearDead) or (EnemyHealth is Injured) or (EnemyHealth is Normal)) " + "and ((EnemyHealth is NearDead) or (EnemyHealth is Injured) or (EnemyHealth is Normal)) " +
"and ((RelativeAttackPower is Weak) or (RelativeAttackPower is Equal)) " + "and ((RelativeAttackPower is Weak) or (RelativeAttackPower is Equal)) " +
"and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " + "and ((RelativeSpeed is Slow) or (RelativeSpeed is Equal) or (RelativeSpeed is Fast))) " +
"then AttackOrFlee is Flee")); "then AttackOrFlee is Flee");
} }
} }
} }