Programming game AI(FuzzyLogic) by (as3)example
Contents
[ hide ]
- 1 Example:
- 1.1 Variable;
- 1.2 VariableValue;
- 1.3 MemberShipFunctions;
- 1.4 FuzzyRule;
- 1.5 FuzzySet;
- 1.6 FuzzySystem;
- 1.7 IFuzzyBasicComparisonRules
- 2 Code snippet:
- 2.1 1.Variable;
- 2.2 2.VariableValue;
- 2.3 3.MemberShipFunctions;
- 2.4 4.FuzzyRule;
- 2.5 5.FuzzySet;
- 2.6 6.FuzzySystem;
- 3 Supplmentary interpretation
- 3.1 TriangleMemberShipFunction
- 3.2 TrapezoidalMemberShipFunction
- 3.3 LeftShoulderMemberShipFunction
- 3.4 LeftShoulderFlip270MemberShipFunction
- 3.5 RightShoulderMemberShipFunction
- 3.6 RightShoulderFlip90MemberShipFunction
- 3.7 InverseTrapezoidalMemberShipFunction
- 3.8 InverseTrapezoidalFlip90MemberShipFunction
Example:
First off,let's overview this simple as3-based Fuzzy Logic System:
Variable;
VariableValue;
MemberShipFunctions;
1.Linear;
2.Triangle;
3.Trapezoidal;
FuzzyRule;
FuzzySet;
FuzzySystem;
IFuzzyBasicComparisonRules
Next,how to construct Fuzzy Logic System using AS3 ![]()
Code snippet:
1.Variable;
/**
* Lingustic variables:
* these are simply variables whoes values are lingustic.
* e.g.:for the lingustic variable "direction" the possible values
* could be "north","west","south","east".
*
* @author Knight.zhou
*
*/
public class Variable
{
protected var values:Array;
public function Variable()
{
values = [];
}
public function addValue(value:VariableValue):void
{
values.push( value );
}
public function gett(index:String):VariableValue
{
for each( var value:VariableValue in values)
{
if(value.value == index)
{
return value;
}
}
return null;
}
}
2.VariableValue;
/**
* Define a linguistic value and has an IMemberShipFunction
* that define a MemberShip function for the sepcific value.
*
* @author Knight.zhou
*
*/
public class VariableValue
{
private var memberShip:IMemberShipFunction;
private var _value:String;
public function get value():String
{
return _value;
}
public function VariableValue(valueS:String,memberShip:IMemberShipFunction)
{
this._value = valueS;
this.memberShip = memberShip;
}
public function memberShipOf(valueN:Number):Number
{
return memberShip.memberShipOf(valueN);
}
}
3.MemberShipFunctions;
/**
* Define an interface that let you create your own membership function for the VariableValue.
* These functions take a crisp value(angle or others)
* as a parameter and output a number between 0 and 1.
* Each lingustic value has an assoicated MemberShip function.
* The idea behind the MemberShip function is to define
* how much a crisp value is related to a specific lingustic value.
* The most common MemberShip functions are:
* Linear,
* Triangular,
* Tapezoidal(Left shoulder,right shoulder)
*
*
* @author Knight.zhou
*
*/
public interface IMemberShipFunction
{
function memberShipOf(value:Number):Number;
}
4.FuzzyRule;
import mx.logging.ILogger;
/**
* Fuzzy rules are basically if<condition> then <decision>.
* The condition part is,for this example ,
* in form of <linguistic variable 1> = <linguistic value 1>
* AND <linguistic variable 2> = <linguistic value 2> AND...
* the decision part,in this Fuzzy system,
* is a assignment of a crisp value.
* e.g.: IF direction=north AND direction=west THEN turn:=-15°
* new FuzzyRule( 15, new Array( "east", "left" ))
*
* @author Knight.zhou
*
*/
public class FuzzyRule
{
private static const LOG:ILogger = LogUtil.getLogger(FuzzyRule);
private var ifValues:Array;
private var crispResult:Number;
private var vars:Array;
public function FuzzyRule(crispResult:Number,ifValues:Array,vars:Array=null)
{
this.crispResult = crispResult;
this.ifValues = ifValues;
this.vars = vars;
}
public function addIfValue(value:String):void
{
ifValues.push( value );
}
public function getWeight(sets:Array):Number
{
var res:Number = 1;
var i:Number = 0;
for each(var ifValue:String in ifValues)
{
var fs:FuzzySet = sets[i] as FuzzySet;
// LOG.debug("fs.getMemberShip({0}):{1}",ifValue,fs.getMemberShip(ifValue));
res *= fs.getMemberShip(ifValue);
i++;
}
return res;
}
public function get crispOutput():Number
{
return crispResult;
}
5.FuzzySet;
/**
* These are sets whose elements are all the possible linguistic
* value of a specific linguistic variable,each assoicated
* with a MemberShip function value of the same Crisp value.
* e.g.:the set defined by the direction linguistic variable
* and the crisp value 80° would be
* (direction, 80°) :== {(north, 0.9),(west,0),(south,0),(east,0.1)}
*
* @author Knight.zhou
*
*/
public class FuzzySet
{
private var variable:Variable;
private var crispValue:Number;
public function FuzzySet(variable:Variable,crispValue:Number)
{
this.variable = variable;
this.crispValue = crispValue;
}
public function getMemberShip(value:String):Number
{
var variableValue:VariableValue = variable.gett(value);
if(variableValue!=null)
{
return variableValue.memberShipOf(crispValue);
}else
{
throw new Error("INVALID_FUZZY_LOGIC_VALUE!");
}
return -1;
}
}
6.FuzzySystem;
/**
* This class lets you define a set of rules.
* all defining the same crisp variable and calculate
* the aggregated crisp output.
* e.g.:
* system.addRule(new FuzzyRule(15, new Array( "north", "left" ) ));
*
* @author Knight.zhou
*
*/
public class FuzzySystem
{
private var rules:Array;
public function FuzzySystem()
{
rules = [];
}
public function addRule(rule:FuzzyRule):void
{
rules.push( rule );
}
public function crispOutput(sets:Array):Number
{
var vals:Number=0.00;
var weights:Number=0.00;
for each(var rule:FuzzyRule in rules)
{
weights += rule.getWeight( sets );
vals += rule.getWeight( sets )*rule.crispOutput;
}
return vals/weights;
}
}
Supplmentary interpretation
TriangleMemberShipFunction
/**
* For instance the "north" lingustic value could have
* a Triangular membership function
* with parameter a=0°, b=90° and c=180°.
* This would mean that a 45° angle
* would have a 0.5 Membership to the linguistic value "north".
*
* *******e.g.*****
*
* a
* / \
* b-----c
*
* *****e.g.********
*
* @author Knight.zhou
*
*/
public class TriangleMemberShipFunction implements IMemberShipFunction
{
/*private var a:Number;
private var b:Number;
private var c:Number;*/
/*public function TriangleMemberShipFunction(a:Number,b:Number,c:Number)
{
this.a = a;
this.b = b;
this.c = c;
}
public function memberShipOf(value:Number):Number
{
if ((a < value) && (value <= b))
return (value - a) / (b - a);
if ((b < value) && (value < c))
return (c - value) / (c - b);
return 0;
}*/
private var leftOffset:Number;
private var rightOffset:Number;
private var peakPoint:Number;
public function TriangleMemberShipFunction(leftOffset:Number,b:Number,rightOffset:Number)
{
this.leftOffset = leftOffset;
this.peakPoint = peakPoint;
this.rightOffset = rightOffset;
}
public function memberShipOf(value:Number):Number
{
//check leftOffset or rightOffset equal to 0;
if ( ((this.leftOffset==0) && (value == this.peakPoint))
||((this.rightOffset==0) && (value == this.peakPoint))
)
{
return 1.0;
}
//check less than peakpoint;
if( (value<=this.peakPoint) && (value>=(this.peakPoint-this.leftOffset)) )
{
var gradLeft:Number = 1.0/this.leftOffset;
return gradLeft *(value-(this.peakPoint-this.leftOffset));
}
//check more than peakpoint;
else if( (value>this.peakPoint) && (value<(this.peakPoint+this.rightOffset)))
{
var gradRight:Number = 1.0/-this.rightOffset;
return gradRight*(value-this.peakPoint)+1.0;
}
//others
else
{
return 0;
}
return 0;
}
}
TrapezoidalMemberShipFunction
/**
* Trapezoidal member ship function.
*
* *****e.g.*******
*
* b------c
* / \
* a-----------d
*
* *****e.g.*******
*
* @author Knight.zhou
*
*/
public class TrapezoidalMemberShipFunction implements IMemberShipFunction
{
private var a:Number;
private var b:Number;
private var c:Number;
private var d:Number;
public function TrapezoidalMemberShipFunction(a:Number,b:Number,c:Number,d:Number)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public function memberShipOf(value:Number):Number
{
var result:Number=0;
if( ((a==b)&&(value==a))
|| ((c==d)&&(value==c))
)
{
result = 1.0;
}
if( value>=a && value<b)
{
result = (b - value) / (b - a);
}
else if(value>=b && value<c)
{
result = 1.0;
}else if(value>=c && value<d)
{
result = (value - c) / (d - c);
}
// LOG.debug("current value:{0}||result:{1}",value.toString(),result.toString());
return result;
}
}
LeftShoulderMemberShipFunction
/**
* Left shoulder member ship functions
*
*******e.g.*******
*
* -------a
* \
* b
*
*******e.g.*******
*
* @author Knight.zhou
*
*/
public class LeftShoulderMemberShipFunction implements IMemberShipFunction
{
private var peakPoint:Number;
private var leftOffset:Number;
private var rightOffset:Number;
private var midPoint:Number;
public function LeftShoulderMemberShipFunction(leftOffset:Number,peakPoint:Number,rightOffset:Number)
{
this.peakPoint = peakPoint;
this.leftOffset = leftOffset;
this.rightOffset = rightOffset;
// this.midPoint = (leftOffset+rightOffset)/2;
this.midPoint = peakPoint-leftOffset/2;
}
public function memberShipOf(value:Number):Number
{
//check offset equal 0;
if( (this.rightOffset==0) && (value==this.peakPoint)
||(this.leftOffset==0) && (value==this.peakPoint)
)
{
return 1.0;
}
//check value more than peak
if( (value>=this.peakPoint) && (value<=(this.peakPoint+this.rightOffset)) )
{
var grad:Number = -(1.0/this.rightOffset);
return grad * (value-this.peakPoint)+1.0;
}
//check value less than peak
else if( (value<this.peakPoint) && (value>(this.peakPoint-this.leftOffset)))
{
return 1.0;
}
return 0;
}
}
LeftShoulderFlip270MemberShipFunction
/**
* Left shoulder flip 270 degree member ship functions
*
*******e.g.*******
*
* a----b
* | |
* | d
* | *
* c
*
*******e.g.*******
*
* @author Knight.zhou
*
*/
public class LeftShoulderFlip270MemberShipFunction implements IMemberShipFunction
{
private var peakPoint:Number;
private var upOffset:Number;
private var downOffset:Number;
public function LeftShoulderFlip270MemberShipFunction(upOffset:Number,peakPoint:Number,downOffset:Number)
{
this.peakPoint = peakPoint;
this.upOffset = upOffset;
this.downOffset = downOffset;
}
public function memberShipOf(value:Number):Number
{
//check offset equal 0;
if( (this.downOffset==0) && (value==this.peakPoint)
||(this.upOffset==0) && (value==this.peakPoint)
)
{
return 1.0;
}
//check value more than peak
if( (value>=this.peakPoint) && (value<=(this.peakPoint+this.upOffset)) )
{
return 1.0;
}
//check value less than peak
else if( (value<this.peakPoint) && (value>(this.peakPoint-this.downOffset)))
{
var grad:Number = -(1.0/this.downOffset);
return grad * (value-this.peakPoint)+1.0;
}
return 0;
}
}
RightShoulderMemberShipFunction
/**
* Right shoulder member ship functions
*
* *******e.g.*****
*
* a------
* /
* b
*
*******e.g.*******
*
* @author Knight.zhou
*
*/
public class RightShoulderMemberShipFunction implements IMemberShipFunction
{
private static const LOG:ILogger = LogUtil.getLogger(RightShoulderMemberShipFunction);
private var peakPoint:Number;
private var leftOffset:Number;
private var rightOffset:Number;
private var midPoint:Number;
public function RightShoulderMemberShipFunction(leftOffset:Number,peakPoint:Number,rightOffset:Number)
{
this.peakPoint = peakPoint;
this.leftOffset = leftOffset;
this.rightOffset = rightOffset;
// this.midPoint = (leftOffset+rightOffset)/2;
this.midPoint = peakPoint+rightOffset/2;
}
public function memberShipOf(value:Number):Number
{
//check offset equal 0;
if( (this.leftOffset==0) && (value==this.midPoint) ||
(this.rightOffset==0) && (value==this.midPoint)
)
{
return 1.0;
}
//check value less than peak
if( (value<=this.peakPoint) && (value>=(this.peakPoint-this.leftOffset)) )
{
// LOG.debug("current value:{0}||this.peakPoint-this.leftOffset:{1}",value,(this.peakPoint-this.leftOffset));
var grad:Number = 1.0/this.leftOffset;
return grad * (value-(this.peakPoint-this.leftOffset));
}
//check value more than peak
else if( (value>this.peakPoint) && (value<=this.peakPoint+this.rightOffset) )
{
return 1.0;
}
return 0;
}
}
RightShoulderFlip90MemberShipFunction
/**
* Right shoulder flip 90 degress member ship functions
*
*******e.g.*******
*
* b
* | *
* | d
* | |
* a-----d
*
*******e.g.*******
*
* @author Knight.zhou
*
*/
public class RightShoulderFlip90MemberShipFunction implements IMemberShipFunction
{
private var peakPoint:Number;
private var upOffset:Number;
private var downOffset:Number;
public function RightShoulderFlip90MemberShipFunction(upOffset:Number,peakPoint:Number,downOffset:Number)
{
this.peakPoint = peakPoint;
this.upOffset = upOffset;
this.downOffset = downOffset;
}
public function memberShipOf(value:Number):Number
{
//check offset equal 0;
if( (this.downOffset==0) && (value==this.peakPoint)
||(this.upOffset==0) && (value==this.peakPoint)
)
{
return 1.0;
}
//check value more than peak
if( (value>this.peakPoint) && (value<=(this.peakPoint+this.upOffset)) )
{
var grad:Number = 1.0/this.upOffset;
return grad * (value-this.peakPoint);
}
//check value less than peak
else if( (value<=this.peakPoint) && (value>(this.peakPoint-this.downOffset)))
{
return 1.0;
}
return 0;
}
}
InverseTrapezoidalMemberShipFunction
/**
* Inverse trapezoidal member ship function.
*
* ******e.g.*******
*
* a-----------d
* \ /
* b------c
*
* *****e.g.********
*
* @author Knight.zhou
*
*/
public class InverseTrapezoidalMemberShipFunction implements IMemberShipFunction
{
private static const LOG:ILogger = LogUtil.getLogger(InverseTrapezoidalMemberShipFunction);
private var a:Number;
private var b:Number;
private var c:Number;
private var d:Number;
public function InverseTrapezoidalMemberShipFunction(a:Number,b:Number,c:Number,d:Number)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public function memberShipOf(value:Number):Number
{
var result:Number=0;
if( ((a==b)&&(value==a))
|| ((c==d)&&(value==c))
)
{
result = 1.0;
}
if( value>=a && value<b)
{
result = (b - value) / (b - a);
}
else if(value>=b && value<=c)
{
result = 1.0;
}else if(value>c && value<=d)
{
result = (value - c) / (d - c);
}else
{
result = 0.0;
}
// LOG.debug("current value:{0}||result:{1}",value.toString(),result.toString());
return result;
}
}
InverseTrapezoidalFlip90MemberShipFunction
/**
* Inverse trapezoidal member ship function.
*
* ******e.g.*******
*
* b
* /|
* / |
* a |
* | |
* | |
* c |
* \ |
* \|
* d
*
* *****e.g.********
*
* @author Knight.zhou
*
*/
public class InverseTrapezoidalFlip90MemberShipFunction implements IMemberShipFunction
{
private static const LOG:ILogger = LogUtil.getLogger(InverseTrapezoidalMemberShipFunction);
private var a:Number;
private var b:Number;
private var c:Number;
private var d:Number;
public function InverseTrapezoidalFlip90MemberShipFunction(a:Number,b:Number,c:Number,d:Number)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public function memberShipOf(value:Number):Number
{
var result:Number=0;
if( ((a==b)&&(value==a))
|| ((c==d)&&(value==c))
)
{
result = 1.0;
}
if( value>a && value<=b)
{
result = (b - value) / (b - a);
}
else if(value>c && value<=a)
{
result = 1.0;
}else if(value>d && value<=c)
{
result = (value - d) / (c - d);
}else
{
result = 0;
}
// LOG.debug("current value:{0}||result:{1}",value.toString(),result.toString());
return result;
}
}
p.s:
Defuzzation using IFuzzyBasicComparisonRules:
/**
* The IFuzzyBasicComparisonRulses interface works on the idea
* that it can be inherited by any object that we wish to implement to.
* The reason to use an interface is because at this moment in time
* we don't know what class the interface is going to be used
* in nor if the rules for the comparisons is even going to be used
* between objects that are of the same type.
* I think I should make it specifically clear at this point
* that what the interface implements is not the rule itself,
* because at this very low level of dealing with a simple number,
* it would involve too much twisting logic into the class to make it work.
* The idea being that at some point a class would use the FuzzyDouble class
* and deal with the Fuzzy Double depending on the rules set within the object.
* So at this point, we are merely setting the rules that the type
* is supposed to adhere to.
*
* @author knight.zhou
*
* @see http://www.codeproject.com/KB/recipes/fuzzylogicvadaline.aspx
*/
public interface IFuzzyBasicComparisonRules
{
function isGreaterThan(value:Number):Number;
function isLessThan(value:Number):Number;
function isEqual(value:Number):Number;
}
Application demo(with reference diagram):
http://www.lookbackon.com/lab/FuzzyLogicSystem/bin-release/FuzzyLogicSystem.swf
View Source:
Unadulterated words, some truthful words dude. You rocked my day.
The information here is great. I will invite my friends here.
Thanks
В этом что-то есть. Понятно, спасибо за помощь в этом вопросе.