Rule Engine

So I am basically at the point now where I have a functioning rule engine for the logic games project. It's all out on github. Here's the gist of it:

The class structure is described here, from a few posts ago. The rule evaluation is the new part. The RuleBase stores a list of Facts, which are in a hash keyed by the Entity and the Property to which they apply.

A Clause can be evaluated by determining if there is a Fact with the same information. So the Clause's truth is evaluated by looking at the RuleBase's facts hash with the Clause's Entity and Property. If a Fact is found for that Entity and Property, and the Fact's property_value and comparator match those of the Clause, then the Clause evaluates as TRUE. If the Fact's property_value matches that of the Clause, but the comparator is NOT equal to the Clause's, then the Clause evaluates as FALSE. In all other situations, the Clause evaluates as UNKNOWN.

A ClauseCluster is evaluated by looking at the rhs and lhs (right hand side and left hand side of the clause) and using the operator to perform Boolean logic. So if the operator is AND, then the lhs and rhs must both be TRUE for the ClauseCluster to evaluate to TRUE, and if the operator is OR, then the lhs and the rhs must both be FALSE for the ClauseCluster to evaluate to FALSE. If the truth of the ClauseCluster can't be determined (for example, if the operator is AND and the lhs is TRUE but the rhs is UNKNOWN, then the ClauseCluster evaluates to UNKNOWN.

Rule's are evaluated by evaluating the Clause or ClauseCluster that is the antecedent. All the work is delegated to the antecedent's evaluation method. The interesting part of the Rule's evaluation is what happens after the truth of the antecedent is determined. If the antecedent is TRUE, then a new Fact gets added to the RuleBase's facts hash based on the consequent. If the antecedent is TRUE or FALSE (i.e. not UNKNOWN), then a flag gets set marking the Rule as having been fired.

The RuleEngine gets evaluated by evaluating all of its Rules. It iterates over all of its Rules, evaluating any Rule that has not already been fired (i.e. who's truth is still UNKNOWN) until either a) all Rules have been fired, or b) the remaining unfired Rules cannot be evaluated because we don't have enough information. In the case of the latter, this is determined by completing a full iteration without inferring any new knowledge. So if all Rules get evaluated, and no new Facts get generated, then the no new Facts will be generated in the next iteration either, so we can stop trying.

My next step is to start putting together a suite of tests that represent real logic games data. For right now, I am going to manually convert a set of logic games into sets of Rules and Facts just to test out the rule engine. After that, the really hard work begins: parsing a logic game, turning natural language into a set of Rules.