From cb58bed40ed0831d1807102a6928192fd1038299 Mon Sep 17 00:00:00 2001 From: David Dormagen Date: Sun, 4 Oct 2015 18:38:05 +0200 Subject: [PATCH] fuzzy logic library: changed interface & added documentation The interface now follows the Shape library's interface. --- docs/sdk/script/FuzzyLogic.xml | 87 ++++++++++++++ .../Objects.ocd/Animals.ocd/Fish.ocd/Script.c | 66 +++++----- .../Animals.ocd/Piranha.ocd/Script.c | 49 ++++---- .../Libraries.ocd/FuzzyLogic.ocd/DefCore.txt | 4 +- .../Libraries.ocd/FuzzyLogic.ocd/Script.c | 113 ++++++++++-------- .../FuzzyLogic.ocd/StringTblDE.txt | 2 - .../FuzzyLogic.ocd/StringTblUS.txt | 2 - 7 files changed, 206 insertions(+), 117 deletions(-) create mode 100644 docs/sdk/script/FuzzyLogic.xml delete mode 100644 planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/StringTblDE.txt delete mode 100644 planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/StringTblUS.txt diff --git a/docs/sdk/script/FuzzyLogic.xml b/docs/sdk/script/FuzzyLogic.xml new file mode 100644 index 000000000..0dd6160f1 --- /dev/null +++ b/docs/sdk/script/FuzzyLogic.xml @@ -0,0 +1,87 @@ + + + + + Fuzzy Logic library + Fuzzy Logic library + + 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. + Creating a new fuzzy logic instance + 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. + var fuzzy_logic = FuzzyLogic->Init(); + + + Set definitions + 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]]); + First you have to define on which sets (imagine them like categories) your logic should work. One set could e.g. be temperature and the values in the set could be {low, medium, high}. + 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 low and then slowly becomes less low until 20. The definition would look like this: + fuzzy_logic->AddSet("temperature", "low", [[-20, 1], [0, 1], [20, 0]]); + The assignment parameter specifies how strongly the value belongs to the set (0 or 1). Values that lie between the given three are interpolated. + Similarly, we can also add definitions for medium and high. + +fuzzy_logic->AddSet("temperature", "medium", [[0, 0], [20, 1], [40, 0]]); +fuzzy_logic->AddSet("temperature", "high", [[10, 0], [40, 1], [100, 1]]); + + In a similar way, you also have to add sets for your actions (as opposed to your observations, like the temperature 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: + +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]]); + + + Rule definition + fuzzy_logic->AddRule(string/array condition, string result); + 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 "<set_name>=<textual_value>" (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: + +fuzzy_logic->AddRule("temperature=low", "heater=full_power"); +fuzzy_logic->AddRule("temperature=medium", "heater=a_little"); +fuzzy_logic->AddRule("temperature=high", "heater=off"); + + + Rule operators + +fuzzy_logic->And(condition1, condition2); +fuzzy_logic->Or(condition1, condition2); +fuzzy_logic->Not(condition); + + 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 window with the textual value of open. Then we could adjust our rules as follows: + fuzzy_logic->AddRule(fuzzy_logic->And("temperature=medium", fuzzy_logic->Not("window=open")), "heater=a_little"); + + Setting Values +fuzzy_logic->Fuzzify(string set, int value); + After we have defined all sets and rules, we need to provide values for our sets. The function Fuzzify 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 temperature value low). The Fuzzify function can be called an arbitrary amount of times before the next Execute. + +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", GetTemperature()); +} + + + Getting actions + proplist fuzzy_logic->Execute(); + After having set all necessary values, the resulting actions according to the rules are calculated when calling Execute. 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. + +// 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"]); +} + + + Zapper2015-09 + diff --git a/planet/Objects.ocd/Animals.ocd/Fish.ocd/Script.c b/planet/Objects.ocd/Animals.ocd/Fish.ocd/Script.c index afd5c2436..f674c6718 100644 --- a/planet/Objects.ocd/Animals.ocd/Fish.ocd/Script.c +++ b/planet/Objects.ocd/Animals.ocd/Fish.ocd/Script.c @@ -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) diff --git a/planet/Objects.ocd/Animals.ocd/Piranha.ocd/Script.c b/planet/Objects.ocd/Animals.ocd/Piranha.ocd/Script.c index bedab943b..ab2d6dce4 100644 --- a/planet/Objects.ocd/Animals.ocd/Piranha.ocd/Script.c +++ b/planet/Objects.ocd/Animals.ocd/Piranha.ocd/Script.c @@ -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(); } diff --git a/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/DefCore.txt b/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/DefCore.txt index a8eeb4b84..176bf0a1b 100644 --- a/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/DefCore.txt +++ b/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/DefCore.txt @@ -1,4 +1,4 @@ [DefCore] -id=Library_FuzzyLogic -Version=6,0 +id=FuzzyLogic +Version=7,0 Category=C4D_StaticBack diff --git a/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/Script.c b/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/Script.c index 721b65654..c732423da 100644 --- a/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/Script.c +++ b/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/Script.c @@ -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; } diff --git a/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/StringTblDE.txt b/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/StringTblDE.txt deleted file mode 100644 index 386fbb2ed..000000000 --- a/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/StringTblDE.txt +++ /dev/null @@ -1,2 +0,0 @@ -Name=FuzzyLogic -Description=Contains functions to evaluate fuzzy sets. \ No newline at end of file diff --git a/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/StringTblUS.txt b/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/StringTblUS.txt deleted file mode 100644 index 386fbb2ed..000000000 --- a/planet/Objects.ocd/Libraries.ocd/FuzzyLogic.ocd/StringTblUS.txt +++ /dev/null @@ -1,2 +0,0 @@ -Name=FuzzyLogic -Description=Contains functions to evaluate fuzzy sets. \ No newline at end of file