Working with fuzzy logic

There are times when we have to deal with gray areas, instead of binary-based values, to make decisions, and fuzzy logic is a set of mathematical techniques that help us with this task.

Imagine that we're developing an automated driver. A couple of available actions are steering and speed control, both of which have a range of degrees. Deciding how to take a turn, and at which speed, is what will make our driver different and possibly smarter. That's the type of gray area that fuzzy logic helps represent and handle.

Getting ready

This recipe requires a set of states indexed by continuous integer numbers. As this representation varies from game to game, we handle the raw input from such states, along with their fuzzification, in order to have a good general-purpose fuzzy decision maker. Finally, the decision maker returns a set of fuzzy values representing the degree of membership of each state.

How to do it...

We will create two base classes and our fuzzy decision maker:

  1. Create the parent class, MembershipFunction:
    using UnityEngine;
    using System.Collections;
    
    public class MembershipFunction : MonoBehaviour
    {
        public int stateId;
        public virtual float GetDOM(object input)
        {
            return 0f;
        }
    }
  2. Implement the FuzzyRule class:
    using System.Collections;
    using System.Collections.Generic;
    
    public class FuzzyRule
    {
        public List<int> stateIds;
        public int conclusionStateId;
    }
  3. Create the FuzzyDecisionMaker class:
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    public class FuzzyDecisionMaker : MonoBehaviour
    {
    }
  4. Define the decision-making function signature and its member variables:
    public Dictionary<int,float> MakeDecision(object[] inputs, MembershipFunction[][] mfList, FuzzyRule[] rules)
    {
        Dictionary<int, float> inputDOM = new Dictionary<int, float>();
        Dictionary<int, float> outputDOM = new Dictionary<int, float>();
        MembershipFunction memberFunc;
        // next steps
    }
  5. Implement the loops for traversing the inputs and populate the initial degree of membership (DOM) for each state:
    foreach (object input in inputs)
    {
        int r, c;
        for (r = 0; r < mfList.Length; r++)
        {
            for (c = 0; c < mfList[r].Length; c++)
            {
                // next step
            }
        }
    }
    // steps after next
  6. Define the body of the innermost loop, which makes use of the proper membership functions to set (or update) the degrees of membership:
    memberFunc = mfList[r][c];
    int mfId = memberFunc.stateId;
    float dom = memberFunc.GetDOM(input);
    if (!inputDOM.ContainsKey(mfId))
    {
        inputDOM.Add(mfId, dom);
        outputDOM.Add(mfId, 0f);
    }
    else
        inputDOM[mfId] = dom;
  7. Traverse the rules for setting the output degrees of membership:
    foreach (FuzzyRule rule in rules)
    {
        int outputId = rule.conclusionStateId;
        float best = outputDOM[outputId];
        float min = 1f;
        foreach (int state in rule.stateIds)
        {
            float dom = inputDOM[state];
            if (dom < best)
                continue;
            if (dom < min)
                min = dom;
        }
        outputDOM[outputId] = min;
    }
  8. Finally, return the set of degrees of membership:
    return outputDOM;

How it works...

We make use of the boxing/unboxing technique for handling any input via the object data type. The fuzzification process is done with the help of our own membership functions, derived from the base class that we created in the beginning. Then, we take the minimum degree of membership for the input state for each rule and calculate the final degree of membership for each output state given the maximum output from any of the applicable rules.

There's more...

We can create an example membership function to define whether an enemy is in enraged mode, knowing that its life points (ranging from 0 to 100) are equal to or less than 30.

The following is the code for the example MFEnraged class:

using UnityEngine;
using System;
using System.Collections;

public class MFEnraged : MembershipFunction
{
    public override float GetDOM(object input)
    {
        if ((int)input <= 30)
            return 1f;
        return 0f;
    }
}

It's worth noting that it is a common requirement to have a complete set of rules; one for each combination of states from each input. This makes the recipe lack in scalability, but it works well for a smaller number of input variables and a small number of states per variable.

See also

For more theoretical insights regarding (de)fuzzification and scalability weaknesses, please refer to Ian Millington's book, Artificial Intelligence for Games.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.126.211