fuzzy logic library: changed interface & added documentation

The interface now follows the Shape library's interface.
shapetextures
David Dormagen 2015-10-04 18:38:05 +02:00
parent 1ce2f8fe77
commit cb58bed40e
7 changed files with 206 additions and 117 deletions

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE doc
SYSTEM '../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../clonk.xsl"?>
<doc>
<title>Fuzzy Logic library</title>
<h>Fuzzy Logic library</h>
<part>
<text>The fuzzy logic library, defined in Objects.ocd/Libraries.ocd/FuzzyLogic.ocd provides functionality for declaring and evaluating fuzzy logic rules and actions that can be used to deduce an action to be executed from some observations. For example, the fish uses fuzzy logic to navigate in a Clonk landscape while avoiding predators and heading towards food. To make the fuzzy logic functions available in a script, you need to include the library.</text>
<h>Creating a new fuzzy logic instance</h>
<text>The first step when working with the fuzzy logic library is to create a new instance and save it. All interface functions will be called on that instance later.</text>
<code>var fuzzy_logic = FuzzyLogic->Init();</code>
<text></text>
<h>Set definitions</h>
<code>fuzzy_logic->AddSet(string set_name, string textual_value, array [[int first_value, int assignment], [int second_value, int assignment], [int third_value, int assignment]]);</code>
<text>First you have to define on which sets (imagine them like <i>categories</i>) your logic should work. One set could e.g. be <b>temperature</b> and the values in the set could be <b>{low, medium, high}</b>.</text>
<text>You also have to specify what numerical values describe the textual values in the set best. For example, we could say that a temperature between -20 and 0 is clearly <b>low</b> and then slowly becomes less <b>low</b> until 20. The definition would look like this:</text>
<code>fuzzy_logic->AddSet("temperature", "low", [[-20, 1], [0, 1], [20, 0]]);</code>
<text>The <i>assignment</i> parameter specifies how strongly the value belongs to the set (0 or 1). Values that lie between the given three are interpolated.</text>
<text>Similarly, we can also add definitions for <b>medium</b> and <b>high</b>.</text>
<code>
fuzzy_logic->AddSet("temperature", "medium", [[0, 0], [20, 1], [40, 0]]);
fuzzy_logic->AddSet("temperature", "high", [[10, 0], [40, 1], [100, 1]]);
</code>
<text>In a similar way, you also have to add sets for your actions (as opposed to your observations, like the <b>temperature</b> above). To continue our example, we will create an automatic heater control that turns the heater higher when it's cold. Let's say the heater's power can go from 0 to 100. Then we could define the following set:</text>
<code>
fuzzy_logic->AddSet("heater", "full_power", [[80, 0], [100, 1], [100, 1]]);
fuzzy_logic->AddSet("heater", "a_little", [[10, 0], [30, 1], [40, 0]]);
fuzzy_logic->AddSet("heater", "off", [[0, 1], [0, 1], [10, 0]]);
</code>
<h>Rule definition</h>
<code>fuzzy_logic->AddRule(string/array condition, string result);</code>
<text>Now that we have defined our sets, we can also define rules that are based on those sets. A rule consists of one (or more) condition in the form of "&lt;set_name&gt;=&lt;textual_value&gt;" (e.g. "temperature=high") and exactly one resulting action in the same format (e.g. "heater=off"). There can be multiple different rules defined and the end result will take into account every definition. For our example above, we could define the rules as follows:</text>
<code>
fuzzy_logic->AddRule("temperature=low", "heater=full_power");
fuzzy_logic->AddRule("temperature=medium", "heater=a_little");
fuzzy_logic->AddRule("temperature=high", "heater=off");
</code>
<h>Rule operators</h>
<code>
fuzzy_logic->And(condition1, condition2);
fuzzy_logic->Or(condition1, condition2);
fuzzy_logic->Not(condition);
</code>
<text>A simple example like above would obviously be boring. We can also combine different conditions into one rule. Imagine we would also have defined the set <b>window</b> with the textual value of <b>open</b>. Then we could adjust our rules as follows:</text>
<code>fuzzy_logic->AddRule(fuzzy_logic->And("temperature=medium", fuzzy_logic->Not("window=open")), "heater=a_little");</code>
<h>Setting Values</h>
<code>fuzzy_logic->Fuzzify(string set, int value);</code>
<text>After we have defined all sets and rules, we need to provide values for our sets. The function <i>Fuzzify</i> calculates how much a value fits to the different textual values of a set (e.g. how much the value 15 belongs to the textual <b>temperature</b> value <b>low</b>). The <i>Fuzzify</i> function can be called an arbitrary amount of times before the next <i>Execute</i>.</text>
<code>
local fuzzy_logic;
private func Construction()
{
fuzzy_logic = FuzzyLogic->Init();
// Define sets and rules here...
// ...
// Update temperature every second.
AddTimer("UpdateTemperature", 30);
}
private func UpdateTemperature()
{
fuzzy_logic->Fuzzify("temperature", <funclink>GetTemperature</funclink>());
}
</code>
<h>Getting actions</h>
<code>proplist fuzzy_logic->Execute();</code>
<text>After having set all necessary values, the resulting actions according to the rules are calculated when calling <i>Execute</i>. The return value is a proplist with all the sets that occur in the rule definitions as results. The values of the sets are the calculated values from set definitions.</text>
<code>
// Called regularly!
private func ExecuteActions()
{
// Calculate the resulting actions from our fuzzified values.
var actions = fuzzy_logic->Execute();
// The properties in 'actions' are the numerical values here.
Log("Turning the heater to %d%%!", actions["heater"]);
}
</code>
</part>
<author>Zapper</author><date>2015-09</date>
</doc>

View File

@ -3,8 +3,6 @@
Author: Zapper
*/
#include Library_FuzzyLogic
static const FISH_SWIM_MAX_ANGLE = 15;
static const FISH_SWIM_MAX_SPEED = 30;
static const FISH_VISION_MAX_ANGLE = 140;
@ -15,6 +13,7 @@ local current_angle, current_speed, current_direction;
local swim_animation;
local base_transform;
local brain;
func Place(int amount, proplist rectangle, proplist settings)
{
@ -68,10 +67,9 @@ func Construction()
SetComDir(COMD_None);
ScheduleCall(this, this.InitActivity, 1 + Random(10), 0);
AddTimer(this.UpdateSwim, 2);
// FUZZY LOGIC INIT BELOW
_inherited(...);
InitFuzzyRules();
return _inherited(...);
}
func InitActivity()
@ -118,22 +116,24 @@ public func NutritionalValue() { if (!GetAlive()) return 15; else return 0; }
func InitFuzzyRules()
{
// ACTION SETS
AddFuzzySet("swim", "left", [[-FISH_SWIM_MAX_ANGLE, 1], [-FISH_SWIM_MAX_ANGLE/2, 0], [FISH_SWIM_MAX_ANGLE, 0]]);
AddFuzzySet("swim", "straight", [[-5, 0], [0, 1], [5, 0]]);
AddFuzzySet("swim", "right", [[-FISH_SWIM_MAX_ANGLE, 0], [FISH_SWIM_MAX_ANGLE/2, 0], [FISH_SWIM_MAX_ANGLE, 1]]);
brain = FuzzyLogic->Init();
AddFuzzySet("speed", "slow", [[0, 1], [FISH_SWIM_MAX_SPEED/2, 0], [FISH_SWIM_MAX_SPEED, 0]]);
AddFuzzySet("speed", "fast", [[0, 0], [FISH_SWIM_MAX_SPEED/2, 0], [FISH_SWIM_MAX_SPEED, 1]]);
// ACTION SETS
brain->AddSet("swim", "left", [[-FISH_SWIM_MAX_ANGLE, 1], [-FISH_SWIM_MAX_ANGLE/2, 0], [FISH_SWIM_MAX_ANGLE, 0]]);
brain->AddSet("swim", "straight", [[-5, 0], [0, 1], [5, 0]]);
brain->AddSet("swim", "right", [[-FISH_SWIM_MAX_ANGLE, 0], [FISH_SWIM_MAX_ANGLE/2, 0], [FISH_SWIM_MAX_ANGLE, 1]]);
brain->AddSet("speed", "slow", [[0, 1], [FISH_SWIM_MAX_SPEED/2, 0], [FISH_SWIM_MAX_SPEED, 0]]);
brain->AddSet("speed", "fast", [[0, 0], [FISH_SWIM_MAX_SPEED/2, 0], [FISH_SWIM_MAX_SPEED, 1]]);
// RULE SETS
var directional_sets = ["friend", "enemy", "food", "wall"];
for (var set in directional_sets)
{
AddFuzzySet(set, "left", [[-FISH_VISION_MAX_ANGLE, 1], [0, 0], [FISH_VISION_MAX_ANGLE, 0]]);
AddFuzzySet(set, "straight", [[-5, 0], [0, 1], [5, 0]]);
AddFuzzySet(set, "right", [[-FISH_VISION_MAX_ANGLE, 0], [0, 0], [FISH_VISION_MAX_ANGLE, 1]]);
brain->AddSet(set, "left", [[-FISH_VISION_MAX_ANGLE, 1], [0, 0], [FISH_VISION_MAX_ANGLE, 0]]);
brain->AddSet(set, "straight", [[-5, 0], [0, 1], [5, 0]]);
brain->AddSet(set, "right", [[-FISH_VISION_MAX_ANGLE, 0], [0, 0], [FISH_VISION_MAX_ANGLE, 1]]);
}
var proximity_sets = ["friend_range", "enemy_range", "food_range"];
@ -142,23 +142,23 @@ func InitFuzzyRules()
for (var set in proximity_sets)
{
AddFuzzySet(set, "far", [[middle, 0], [FISH_VISION_MAX_RANGE, 1], [FISH_VISION_MAX_RANGE, 1]]);
AddFuzzySet(set, "medium", [[0, 0], [middle, 1], [FISH_VISION_MAX_RANGE, 0]]);
AddFuzzySet(set, "close", [[0, 1], [0, 1], [middle, 0]]);
brain->AddSet(set, "far", [[middle, 0], [FISH_VISION_MAX_RANGE, 1], [FISH_VISION_MAX_RANGE, 1]]);
brain->AddSet(set, "medium", [[0, 0], [middle, 1], [FISH_VISION_MAX_RANGE, 0]]);
brain->AddSet(set, "close", [[0, 1], [0, 1], [middle, 0]]);
}
AddFuzzySet("wall_range", "far", [[middle, 0], [FISH_VISION_MAX_RANGE, 1], [FISH_VISION_MAX_RANGE, 1]]);
AddFuzzySet("wall_range", "medium", [[0, 0], [middle, 1], [FISH_VISION_MAX_RANGE, 0]]);
AddFuzzySet("wall_range", "close", [[0, 1], [0, 1], [quarter, 0]]);
brain->AddSet("wall_range", "far", [[middle, 0], [FISH_VISION_MAX_RANGE, 1], [FISH_VISION_MAX_RANGE, 1]]);
brain->AddSet("wall_range", "medium", [[0, 0], [middle, 1], [FISH_VISION_MAX_RANGE, 0]]);
brain->AddSet("wall_range", "close", [[0, 1], [0, 1], [quarter, 0]]);
// RULES
AddFuzzyRule("enemy_range=close", "speed=fast");
AddFuzzyRule(FuzzyOr("friend_range=close", "food_range=close", "wall_range=close"), "speed=slow");
brain->AddRule("enemy_range=close", "speed=fast");
brain->AddRule(brain->Or("friend_range=close", "food_range=close", "wall_range=close"), "speed=slow");
AddFuzzyRule(FuzzyAnd(FuzzyNot("wall_range=close"), FuzzyOr("food=left", FuzzyAnd("friend=left", "enemy_range=far", "food_range=far"), "enemy=right")), "swim=left");
AddFuzzyRule(FuzzyAnd(FuzzyNot("wall_range=close"), FuzzyOr("food=right", FuzzyAnd("friend=right", "enemy_range=far", "food_range=far"), "enemy=left")), "swim=right");
AddFuzzyRule(FuzzyAnd(FuzzyOr("wall=straight", "wall=right"), "wall_range=close"), "swim=left");
AddFuzzyRule(FuzzyAnd("wall=left", "wall_range=close"), "swim=right");
brain->AddRule(brain->And(brain->Not("wall_range=close"), brain->Or("food=left", brain->And("friend=left", "enemy_range=far", "food_range=far"), "enemy=right")), "swim=left");
brain->AddRule(brain->And(brain->Not("wall_range=close"), brain->Or("food=right", brain->And("friend=right", "enemy_range=far", "food_range=far"), "enemy=left")), "swim=right");
brain->AddRule(brain->And(brain->Or("wall=straight", "wall=right"), "wall_range=close"), "swim=left");
brain->AddRule(brain->And("wall=left", "wall_range=close"), "swim=right");
}
@ -168,7 +168,7 @@ func Activity()
if (swimming)
{
UpdateVision();
var actions = FuzzyExec();
var actions = brain->Execute();
DoActions(actions);
}
else if (walking)
@ -232,8 +232,8 @@ func UpdateVisionFor(string set, string range_set, array objects, bool is_food)
//CreateParticle("MagicSpark", obj->GetX() - GetX(), obj->GetY() - GetY(), 0, 0, 60, RGB(0, 255, 0));
//this->Message("%s@%d (me %d, it %d)", obj->GetName(), d, current_angle, angle);
var distance = ObjectDistance(this, obj);
Fuzzify(set, d);
Fuzzify(range_set, distance);
brain->Fuzzify(set, d);
brain->Fuzzify(range_set, distance);
// now that we fuzzified our food - can we actually eat it, too???
if (is_food && distance < GetCon()/10)
@ -242,8 +242,8 @@ func UpdateVisionFor(string set, string range_set, array objects, bool is_food)
return true;
}
Fuzzify(set, 0);
Fuzzify(range_set, FISH_VISION_MAX_RANGE + 1);
brain->Fuzzify(set, 0);
brain->Fuzzify(range_set, FISH_VISION_MAX_RANGE + 1);
return false;
}
@ -300,8 +300,8 @@ func UpdateWallVision()
}
}
Fuzzify("wall", closest);
Fuzzify("wall_range", closest_distance);
brain->Fuzzify("wall", closest);
brain->Fuzzify("wall_range", closest_distance);
}
func DoActions(proplist actions)

View File

@ -5,9 +5,6 @@
#include Fish
#include Library_FuzzyLogic
local hunger;
func Construction()
@ -25,22 +22,24 @@ func MoreHunger()
func InitFuzzyRules()
{
// ACTION SETS
AddFuzzySet("swim", "left", [[-FISH_SWIM_MAX_ANGLE, 1], [-FISH_SWIM_MAX_ANGLE/2, 0], [FISH_SWIM_MAX_ANGLE, 0]]);
AddFuzzySet("swim", "straight", [[-5, 0], [0, 1], [5, 0]]);
AddFuzzySet("swim", "right", [[-FISH_SWIM_MAX_ANGLE, 0], [FISH_SWIM_MAX_ANGLE/2, 0], [FISH_SWIM_MAX_ANGLE, 1]]);
brain = FuzzyLogic->Init();
AddFuzzySet("speed", "slow", [[0, 1], [FISH_SWIM_MAX_SPEED/2, 0], [FISH_SWIM_MAX_SPEED, 0]]);
AddFuzzySet("speed", "fast", [[0, 0], [FISH_SWIM_MAX_SPEED/2, 0], [FISH_SWIM_MAX_SPEED, 1]]);
// ACTION SETS
brain->AddSet("swim", "left", [[-FISH_SWIM_MAX_ANGLE, 1], [-FISH_SWIM_MAX_ANGLE/2, 0], [FISH_SWIM_MAX_ANGLE, 0]]);
brain->AddSet("swim", "straight", [[-5, 0], [0, 1], [5, 0]]);
brain->AddSet("swim", "right", [[-FISH_SWIM_MAX_ANGLE, 0], [FISH_SWIM_MAX_ANGLE/2, 0], [FISH_SWIM_MAX_ANGLE, 1]]);
brain->AddSet("speed", "slow", [[0, 1], [FISH_SWIM_MAX_SPEED/2, 0], [FISH_SWIM_MAX_SPEED, 0]]);
brain->AddSet("speed", "fast", [[0, 0], [FISH_SWIM_MAX_SPEED/2, 0], [FISH_SWIM_MAX_SPEED, 1]]);
// RULE SETS
var directional_sets = ["food", "wall"];
for (var set in directional_sets)
{
AddFuzzySet(set, "left", [[-FISH_VISION_MAX_ANGLE, 1], [0, 0], [FISH_VISION_MAX_ANGLE, 0]]);
AddFuzzySet(set, "straight", [[-5, 0], [0, 1], [5, 0]]);
AddFuzzySet(set, "right", [[-FISH_VISION_MAX_ANGLE, 0], [0, 0], [FISH_VISION_MAX_ANGLE, 1]]);
brain->AddSet(set, "left", [[-FISH_VISION_MAX_ANGLE, 1], [0, 0], [FISH_VISION_MAX_ANGLE, 0]]);
brain->AddSet(set, "straight", [[-5, 0], [0, 1], [5, 0]]);
brain->AddSet(set, "right", [[-FISH_VISION_MAX_ANGLE, 0], [0, 0], [FISH_VISION_MAX_ANGLE, 1]]);
}
var proximity_sets = ["food_range"];
@ -49,31 +48,31 @@ func InitFuzzyRules()
for (var set in proximity_sets)
{
AddFuzzySet(set, "far", [[middle, 0], [FISH_VISION_MAX_RANGE, 1], [FISH_VISION_MAX_RANGE, 1]]);
AddFuzzySet(set, "medium", [[0, 0], [middle, 1], [FISH_VISION_MAX_RANGE, 0]]);
AddFuzzySet(set, "close", [[0, 1], [0, 1], [middle, 0]]);
brain->AddSet(set, "far", [[middle, 0], [FISH_VISION_MAX_RANGE, 1], [FISH_VISION_MAX_RANGE, 1]]);
brain->AddSet(set, "medium", [[0, 0], [middle, 1], [FISH_VISION_MAX_RANGE, 0]]);
brain->AddSet(set, "close", [[0, 1], [0, 1], [middle, 0]]);
}
AddFuzzySet("wall_range", "far", [[middle, 0], [FISH_VISION_MAX_RANGE, 1], [FISH_VISION_MAX_RANGE, 1]]);
AddFuzzySet("wall_range", "medium", [[0, 0], [middle, 1], [FISH_VISION_MAX_RANGE, 0]]);
AddFuzzySet("wall_range", "close", [[0, 1], [0, 1], [quarter, 0]]);
brain->AddSet("wall_range", "far", [[middle, 0], [FISH_VISION_MAX_RANGE, 1], [FISH_VISION_MAX_RANGE, 1]]);
brain->AddSet("wall_range", "medium", [[0, 0], [middle, 1], [FISH_VISION_MAX_RANGE, 0]]);
brain->AddSet("wall_range", "close", [[0, 1], [0, 1], [quarter, 0]]);
AddFuzzySet("hunger", "low", [[0, 1], [0, 1], [75, 0]]);
AddFuzzySet("hunger", "high", [[25, 0], [100, 1], [100, 1]]);
brain->AddSet("hunger", "low", [[0, 1], [0, 1], [75, 0]]);
brain->AddSet("hunger", "high", [[25, 0], [100, 1], [100, 1]]);
// RULES
AddFuzzyRule(FuzzyOr(FuzzyAnd("wall_range=close", "wall=left"), FuzzyAnd("hunger=high", "food=right")), "swim=right");
AddFuzzyRule(FuzzyOr(FuzzyAnd("wall_range=close", "wall=right"), FuzzyAnd("hunger=high", "food=left")), "swim=left");
AddFuzzyRule("hunger=high", "speed=fast");
AddFuzzyRule(FuzzyOr("wall_range=close", "hunger=low"), "speed=slow");
brain->AddRule(brain->Or(brain->And("wall_range=close", "wall=left"), brain->And("hunger=high", "food=right")), "swim=right");
brain->AddRule(brain->Or(brain->And("wall_range=close", "wall=right"), brain->And("hunger=high", "food=left")), "swim=left");
brain->AddRule("hunger=high", "speed=fast");
brain->AddRule(brain->Or("wall_range=close", "hunger=low"), "speed=slow");
}
func UpdateVision()
{
Fuzzify("hunger", hunger);
brain->Fuzzify("hunger", hunger);
UpdateVisionFor("food", "food_range", FindObjects(Find_Distance(FISH_VISION_MAX_RANGE), Find_OCF(OCF_Alive), Find_Func("IsPrey"), Find_NoContainer(), Sort_Distance()), true);
UpdateWallVision();
}

View File

@ -1,4 +1,4 @@
[DefCore]
id=Library_FuzzyLogic
Version=6,0
id=FuzzyLogic
Version=7,0
Category=C4D_StaticBack

View File

@ -1,9 +1,9 @@
/**
FuzzyLogic
Contains functions to evaluate fuzzy sets.
Objects using this library first need to declare fuzzy sets with AddFuzzySet and fuzzy rules with AddFuzzyRule
Objects using this library first need to declare fuzzy sets with AddSet and fuzzy rules with AddRule
and can then update the fuzzy values using Fuzzify.
FuzzyExec can then be called to evaluate the current upates and returns an array of actions based on the AddFuzzyRules calls.
FuzzyExec can then be called to evaluate the current upates and returns an array of actions based on the AddRule calls.
For an example, see the fish.
@ -20,11 +20,12 @@ static const FUZZY_NOT = 1;
static const FUZZY_AND = 2;
static const FUZZY_OR = 3;
local FuzzyLogic;
func Construction()
/*
Returns a new empty fuzzy logic object.
*/
public func Init()
{
FuzzyLogic =
return
{
rules = [],
sets = {},
@ -36,17 +37,23 @@ func Construction()
// which could happen quite often. (For example the "hunger" of an animal could be at the same value for quite some FuzzyExecs)
cache_defuz = {}, // optimizes defuzzification
cache_fuz = {}, // optimizes fuzzification
AddSet = FuzzyLogic.AddFuzzySet,
AddRule = FuzzyLogic.AddFuzzyRule,
Execute = FuzzyLogic.Execute,
Fuzzify = FuzzyLogic.Fuzzify,
And = FuzzyLogic.FuzzyAnd,
Or = FuzzyLogic.FuzzyOr,
Not = FuzzyLogic.FuzzyNot
};
return _inherited(...);
}
/*
AddFuzzySet("swim", "left", [[-1000, 1], [-500, 1], [0, 0]]);
*/
func AddFuzzySet(string set, string subset, array data, bool no_warn)
public func AddFuzzySet(string set, string subset, array data, bool no_warn)
{
if (!FuzzyLogic.sets[set])
FuzzyLogic.sets[set] = {};
if (!this.sets[set])
this.sets[set] = {};
// normaliz data
// the Y values should normally be 0 or 1 and are normalized to 0 and 1000 here
for (var item in data)
@ -56,23 +63,23 @@ func AddFuzzySet(string set, string subset, array data, bool no_warn)
else
item[1] *= 1000;
}
FuzzyLogic.sets[set][subset] = data;
this.sets[set][subset] = data;
// init cache for that entry
if (!FuzzyLogic.cache_defuz[set])
FuzzyLogic.cache_defuz[set] = {};
FuzzyLogic.cache_defuz[set][subset] = [-1, nil];
if (!this.cache_defuz[set])
this.cache_defuz[set] = {};
this.cache_defuz[set][subset] = [-1, nil];
if (!FuzzyLogic.cache_fuz[set])
FuzzyLogic.cache_fuz[set] = {};
FuzzyLogic.cache_fuz[set] = 0xffffff;
if (!this.cache_fuz[set])
this.cache_fuz[set] = {};
this.cache_fuz[set] = 0xffffff;
}
func FuzzyExec()
public func Execute()
{
var actions = {};
// apply all the rules to determine the action values
for (var rule in FuzzyLogic.rules)
for (var rule in this.rules)
{
if (!actions[rule[1][0]])
actions[rule[1][0]] = {};
@ -91,14 +98,14 @@ func FuzzyExec()
var defuz; // [centroid, weight]
// try the cache
if (FuzzyLogic.cache_defuz[action][subset][0] == strength)
if (this.cache_defuz[action][subset][0] == strength)
{
defuz = FuzzyLogic.cache_defuz[action][subset][1];
defuz = this.cache_defuz[action][subset][1];
}
else // cache miss
{
defuz = Defuzzify(action, subset, strength);
FuzzyLogic.cache_defuz[action][subset] = [strength, defuz];
this.cache_defuz[action][subset] = [strength, defuz];
}
weighted_sum += defuz[0] * defuz[1];
@ -107,23 +114,23 @@ func FuzzyExec()
}
if (!sum)
FuzzyLogic.actions[action] = 0;
this.actions[action] = 0;
else
{
FuzzyLogic.actions[action] = weighted_sum / sum;
this.actions[action] = weighted_sum / sum;
//Log("Action [%s] defuzzifies to %d", action, FuzzyLogic.actions[action]);
}
}
return FuzzyLogic.actions;
return this.actions;
}
/*
calculates the centroid of an action set
returns [centroid-X, weight]
*/
func Defuzzify(string action, string subset, int strength)
public func Defuzzify(string action, string subset, int strength)
{
var subset_data = FuzzyLogic.sets[action][subset];
var subset_data = this.sets[action][subset];
var centroid_data = [];
for (var i = 0; i <= 1; ++i)
{
@ -147,7 +154,7 @@ func Defuzzify(string action, string subset, int strength)
/*
calculates the [centroid, weight] from two points on a line and a fill-value
*/
func DefuzzifyGetCentroidFromGraph(array coords1, array coords2, int Y, bool is_good_triangle, bool is_main_block, bool is_left_block)
public func DefuzzifyGetCentroidFromGraph(array coords1, array coords2, int Y, bool is_good_triangle, bool is_main_block, bool is_left_block)
{
if ((Y == 0) || (coords1[0] == coords2[0]))
return [0, 0];
@ -194,7 +201,7 @@ func DefuzzifyGetCentroidFromGraph(array coords1, array coords2, int Y, bool is_
}
// returns value between 0 and 1000 for how much the rule fits
func ApplyFuzzyRule(array rule)
private func ApplyFuzzyRule(array rule)
{
if (rule[0] == FUZZY_AND)
{
@ -227,18 +234,18 @@ func ApplyFuzzyRule(array rule)
takes "position", 3
puts position = {left=[nil, 0], middle=[nil, 0.5], right=[nil, 0.9]} into updates (only for actually used subsets, though)
*/
func Fuzzify(string set, int value)
public func Fuzzify(string set, int value)
{
// does not need to change the updates at all if the value didn't change between two calls
if (FuzzyLogic.cache_fuz[set] == value) return true;
FuzzyLogic.cache_fuz[set] = value;
if (this.cache_fuz[set] == value) return true;
this.cache_fuz[set] = value;
// no rules for that set?
if (!FuzzyLogic.updates[set]) return true;
if (!this.updates[set]) return true;
for (var subset in GetProperties(FuzzyLogic.updates[set]))
for (var subset in GetProperties(this.updates[set]))
{
var subset_data = FuzzyLogic.sets[set][subset];
var subset_data = this.sets[set][subset];
var result_value = nil;
for (var i = 0; i <= 2; i += 2)
{
@ -263,7 +270,7 @@ func Fuzzify(string set, int value)
}
}
FuzzyLogic.updates[set][subset][1] = result_value;
this.updates[set][subset][1] = result_value;
//Log("Fuzzify %s/%s: %d [from value %d]", set, subset, result_value, value);
}
}
@ -272,7 +279,7 @@ func Fuzzify(string set, int value)
internal helper
calculates Y value for X on a line between coords1 and coords2
*/
func FuzzyCalcLine(array coords1, array coords2, int X)
public func FuzzyCalcLine(array coords1, array coords2, int X)
{
return
(
@ -286,14 +293,14 @@ func FuzzyCalcLine(array coords1, array coords2, int X)
internal helper
calculates X for intersection with Y-value of a line
*/
func FuzzyCalcLineIntersectY(array coords1, array coords2, int Y)
public func FuzzyCalcLineIntersectY(array coords1, array coords2, int Y)
{
var m = (coords2[1] - coords1[1]) / (coords2[0] - coords1[0]);
return coords1[0] + ((Y - coords1[1]) / m);
}
// takes "position=left", returns [value=0, usage_count=0] for !no_cache and ["position", "left"] for no_cache
func FuzzyStateStringToArray(state, bool no_cache)
public func FuzzyStateStringToArray(state, bool no_cache)
{
// already an array?
if (GetType(state) == C4V_Array) return state;
@ -319,36 +326,36 @@ func FuzzyStateStringToArray(state, bool no_cache)
// some sanity checks to prevent people from tearing out their hair over non-working rules
if (GetLength(first) == 0 || GetLength(second) == 0)
FatalError(Format("FuzzyLogic: Error in rule string: %s [%s=%s]", state, first, second));
if (!FuzzyLogic.sets[first])
if (!this.sets[first])
FatalError(Format("FuzzyLogic: Error in rule string: %s [unknown set %s]", state, first));
if (!FuzzyLogic.sets[first][second])
if (!this.sets[first][second])
FatalError(Format("FuzzyLogic: Error in rule string: %s [unknown subset %s]", state, second));
if (no_cache)
return [first, second];
// see whether that entry already exists in the cache
if (!FuzzyLogic.updates[first])
FuzzyLogic.updates[first] = {};
if (!FuzzyLogic.updates[first][second])
FuzzyLogic.updates[first][second] = [nil, 0, 0];
if (!this.updates[first])
this.updates[first] = {};
if (!this.updates[first][second])
this.updates[first][second] = [nil, 0, 0];
return FuzzyLogic.updates[first][second];
return this.updates[first][second];
}
func FuzzyAnd(condA, condB)
public func FuzzyAnd(condA, condB)
{
if (!condB) return FuzzyStateStringToArray(condA);
return [FUZZY_AND, FuzzyStateStringToArray(condA), FuzzyAnd(condB, ...)];
}
func FuzzyOr(condA, condB)
public func FuzzyOr(condA, condB)
{
if (!condB) return FuzzyStateStringToArray(condA);
return [FUZZY_OR, FuzzyStateStringToArray(condA), FuzzyOr(condB, ...)];
}
func FuzzyNot(condA, int nope)
public func FuzzyNot(condA, int nope)
{
return [FUZZY_NOT, FuzzyStateStringToArray(condA)];
}
@ -357,16 +364,16 @@ func FuzzyNot(condA, int nope)
/condition/ can be "position=left" or FuzzyAnd(FuzzyOr("position=left", "position=right"), FuzzyNot("position=middle"))
/result/ should be "move=left"
*/
func AddFuzzyRule(condition, string result)
public func AddFuzzyRule(condition, string result)
{
if (!result)
FatalError("FuzzyLogic::AddFuzzyRule needs result string");
FatalError("FuzzyLogic::AddRule needs result string");
if (GetType(condition) == C4V_String)
condition = FuzzyStateStringToArray(condition);
result = FuzzyStateStringToArray(result, true);
PushBack(FuzzyLogic.rules, [condition, result]);
PushBack(this.rules, [condition, result]);
FuzzyLogic.actions[result[0]] = 0;
this.actions[result[0]] = 0;
return true;
}

View File

@ -1,2 +0,0 @@
Name=FuzzyLogic
Description=Contains functions to evaluate fuzzy sets.

View File

@ -1,2 +0,0 @@
Name=FuzzyLogic
Description=Contains functions to evaluate fuzzy sets.