Created
August 20, 2014 15:10
-
-
Save DeCarabas/31cd390ea6eb20d77a4b to your computer and use it in GitHub Desktop.
Fuzzy Rules
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <algorithm> | |
#include <limits> | |
#include <memory> | |
#include <string> | |
#include <utility> | |
#include <random> | |
#include <vector> | |
struct fact | |
{ | |
std::string key; | |
float value; | |
fact(const std::string &key, const float value) | |
: key(key), value(value) { | |
} | |
fact(const fact& other) | |
: key(other.key), value(other.value) { | |
} | |
fact(fact &&other) { | |
swap(*this, other); | |
} | |
friend void swap(fact& first, fact& second) { | |
using std::swap; | |
swap(first.key, second.key); | |
swap(first.value, second.value); | |
} | |
}; | |
inline bool operator==(const fact& lhs, const fact& rhs){ return lhs.key == rhs.key; } | |
inline bool operator!=(const fact& lhs, const fact& rhs){ return !operator==(lhs, rhs); } | |
inline bool operator< (const fact& lhs, const fact& rhs){ return lhs.key < rhs.key; } | |
inline bool operator> (const fact& lhs, const fact& rhs){ return operator< (rhs, lhs); } | |
inline bool operator<=(const fact& lhs, const fact& rhs){ return !operator> (lhs, rhs); } | |
inline bool operator>=(const fact& lhs, const fact& rhs){ return !operator< (lhs, rhs); } | |
struct knowlege | |
{ | |
std::shared_ptr<knowlege> parent; | |
bool sorted; | |
std::vector<fact> facts; | |
std::vector<fact>::iterator cursor; | |
knowlege(std::shared_ptr<knowlege> parent) : parent(parent), sorted(false), facts() { | |
cursor = facts.begin(); | |
} | |
knowlege(const knowlege& other) | |
: parent(other.parent), sorted(other.sorted), facts(other.facts) { | |
cursor = facts.begin() + (other.cursor - other.facts.begin()); | |
} | |
knowlege(knowlege &&other) { | |
swap(*this, other); | |
} | |
friend void swap(knowlege& first, knowlege& second) { | |
using std::swap; | |
swap(first.parent, second.parent); | |
swap(first.sorted, second.sorted); | |
swap(first.facts, second.facts); | |
swap(first.cursor, second.cursor); | |
} | |
void sort() { | |
if (!sorted) { | |
std::sort(facts.begin(), facts.end()); | |
cursor = facts.begin(); | |
sorted = true; | |
} | |
} | |
void reset() { | |
if (parent != nullptr) { parent->reset(); } | |
sort(); | |
cursor = facts.begin(); | |
} | |
float *next(const std::string &key) { | |
while (cursor != facts.end()) { | |
int comparison = cursor->key.compare(key); | |
if (comparison == 0) { return &(cursor->value); } | |
if (comparison > 0) { break; } | |
++cursor; | |
} | |
return (parent != nullptr) | |
? parent->next(key) | |
: nullptr; | |
} | |
}; | |
inline static float add_epsilon(const float value) { | |
// TODO: make this add an ULP instead of a float epsilon; I think it is more accurate maybe? Or maybe the floats | |
// round properly? I don't think so... not for small numbers anyway. | |
return value + std::numeric_limits<float>::epsilon(); | |
} | |
inline static float sub_epsilon(const float value) { | |
// TODO: make this add an ULP instead of a float epsilon; I think it is more accurate maybe? Or maybe the floats | |
// round properly? I don't think they do... not for small numbers anyway. | |
return value - std::numeric_limits<float>::epsilon(); | |
} | |
struct criterion | |
{ | |
std::string key; | |
float min; | |
float max; | |
criterion(const std::string &key, float min, float max) | |
: key(key), min(min), max(max) { | |
} | |
criterion(const criterion& other) | |
: key(other.key), min(other.min), max(other.max) { | |
} | |
criterion(criterion &&other) { | |
swap(*this, other); | |
} | |
inline bool matches(float value) { | |
return value >= min && max >= value; | |
} | |
criterion& operator=(criterion other) { | |
swap(*this, other); | |
return *this; | |
} | |
friend void swap(criterion& first, criterion& second) { | |
using std::swap; | |
swap(first.key, second.key); | |
swap(first.min, second.min); | |
swap(first.max, second.max); | |
} | |
inline static criterion equals(const std::string &key, const float value) { | |
return criterion(key, value, value); | |
} | |
inline static criterion greater_than(const std::string &key, const float value) { | |
return greater_than_equal(key, add_epsilon(value)); | |
} | |
inline static criterion greater_than_equal(const std::string &key, const float value) { | |
return criterion(key, value, std::numeric_limits<float>::infinity()); | |
} | |
static criterion less_than(const std::string &key, const float value) { | |
return less_than_equal(key, sub_epsilon(value)); | |
} | |
static criterion less_than_equal(const std::string &key, const float value) { | |
return criterion(key, -std::numeric_limits<float>::infinity(), value); | |
} | |
}; | |
inline bool operator==(const criterion& lhs, const criterion& rhs){ return lhs.key == rhs.key; } | |
inline bool operator!=(const criterion& lhs, const criterion& rhs){ return !operator==(lhs, rhs); } | |
inline bool operator< (const criterion& lhs, const criterion& rhs){ return lhs.key < rhs.key; } | |
inline bool operator> (const criterion& lhs, const criterion& rhs){ return operator< (rhs, lhs); } | |
inline bool operator<=(const criterion& lhs, const criterion& rhs){ return !operator> (lhs, rhs); } | |
inline bool operator>=(const criterion& lhs, const criterion& rhs){ return !operator< (lhs, rhs); } | |
struct rule | |
{ | |
std::vector<criterion> criteria; | |
bool sorted; | |
rule() | |
: criteria(), sorted(false) { | |
} | |
rule(const rule& other) | |
: criteria(other.criteria), sorted(other.sorted) { | |
} | |
rule(rule &&other) { | |
swap(*this, other); | |
} | |
rule& operator=(rule other) { | |
swap(*this, other); | |
return *this; | |
} | |
friend void swap(rule& first, rule& second) { | |
using std::swap; | |
swap(first.criteria, second.criteria); | |
swap(first.sorted, second.sorted); | |
} | |
int score() const { | |
return (int)criteria.size(); | |
} | |
void sort() { | |
if (!sorted) { | |
std::sort(criteria.begin(), criteria.end()); | |
sorted = true; | |
} | |
} | |
bool matches(std::shared_ptr<knowlege> knowlege) { | |
sort(); | |
knowlege->reset(); | |
for (auto iter = criteria.begin(); iter != criteria.end(); ++iter) { | |
float *value = knowlege->next(iter->key); | |
if (nullptr == value) { return false; } | |
if (!iter->matches(*value)) { return false; } | |
} | |
return true; | |
} | |
virtual void run(std::shared_ptr<knowlege> knowlege) const { | |
// Do something with this when you know what to do. | |
} | |
}; | |
struct rule_set | |
{ | |
std::vector<rule> rules; | |
bool sorted; | |
void sort() { | |
if (!sorted) { | |
// Sort inverse by score-- large scores go before small scores. | |
std::sort(rules.begin(), rules.end(), [](const rule& i, const rule& j) { return i.score() > j.score(); }); | |
sorted = true; | |
} | |
} | |
const rule *find_matching_rule(std::shared_ptr<knowlege> knowlege) { | |
sort(); | |
std::vector<rule*> matches; | |
int best_score = -1; | |
for (auto iter = rules.begin(); iter != rules.end(); ++iter) { | |
int score = iter->score(); | |
if (score < best_score) { break; } | |
if (iter->matches(knowlege)) { | |
matches.push_back(&(*iter)); | |
best_score = score; | |
} | |
} | |
if (matches.size() == 1) { | |
return matches[0]; | |
} | |
else if (matches.size() > 0) { | |
// TODO: This is wrong, but I have no good way of threading a random number generator into here. | |
std::default_random_engine generator; | |
std::uniform_int_distribution<int> distribution(0, matches.size() - 1); | |
return matches[distribution(generator)]; | |
} | |
else { | |
return nullptr; | |
} | |
} | |
bool execute_rule(std::shared_ptr<knowlege> knowlege) { | |
const rule *rule = find_matching_rule(knowlege); | |
if (rule != nullptr) { | |
rule->run(knowlege); | |
return true; | |
} | |
return false; | |
} | |
}; | |
int main(int argc, const char *argv[]) | |
{ | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment