Yangbo's Blog!

Artificial Intelligence Board Game.

Programming game AI(FuzzyLogic) by (as3)example

November19

Fuzzy Logic :?:

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:

This page is wiki editable click here to edit this page.
posted under AI, Flex/AS3/Flash
3 Comments to

“Programming game AI(FuzzyLogic) by (as3)example”

  1. Avatar December 11th, 2009 at 21:17 trargaimb Says:

    Unadulterated words, some truthful words dude. You rocked my day.


  2. Avatar January 25th, 2010 at 11:13 Brufffrup Says:

    The information here is great. I will invite my friends here.

    Thanks


  3. Avatar April 28th, 2010 at 7:42 Пономарёв Says:

    В этом что-то есть. Понятно, спасибо за помощь в этом вопросе.


Email will not be published

Website example

Your Comment:

 

BLOG CALENDAR

September 2010
M T W T F S S
« Apr    
 12345
6789101112
13141516171819
20212223242526
27282930