How to implement a set of rules...

Started by
8 comments, last by Squirm 18 years, 8 months ago
Let's say I have an array:
a[5];
Each element in the array will hold a random integer, ranging from 1-8. I want to implement a set of rules to check against the elements of the array. For instance... The first rule is this: Out of the 5 elements, 2 of them MUST be 7 or 8 and the others can be any other value (1-6). What's the best way to check whether my array will meet this rule? There are 16 more rules I'll need to check for too, so the implementation needs to be flexible. I've been racking my brain all night and I have yet to figure out a solution. (If it matters the values 1-6 will represent colors, 7 and 8 will represent a side.) Edit: I'm coding this in C++/OOP.
Advertisement
You can always create a function that you pass a value through a parameter to be put in the posiiton of the array and you can do the checking there.
Elegantly parameterizing a ruleset is one of the trickiest tasks in computer science. The first step is to abstract all the rules into one form. In your case, it sounds like the form will be something like "at least X elements must be in the set Y". You could then parameterize the rule you gave as [X=2, Y={7,8}]. Of course, it's possible that your other rules won't fall into that pattern. You can break them up into groups, each of which has its own form, or try to find a form which encapsulates all possible rules.

Can you give 3-4 other example rules?
I'll explain a bit more. The game revolves around nuts that act as dice. When you "roll" a nut, it can either fall down (hole facing up) or land on one of it's 6 sides. There are 2 colors per 6 sides for a total of 3 colors. (In other words, 2 sides of the nut contain blue, 2 contain red, and 2 contain yellow.) I'm trying to simulate the 5 nuts being rolled. Here are the first few rules:

1. TWO holes / THREE standing (any color)
2. THREE holes / TWO standing (any color)
3. FOUR standing (any color)
4. At least THREE standing, ONE OF EACH COLOR (1 blue, 1 red, 1 yellow)
5. THREE holes / A pair of equal colors standing

I hope this helps...
Heh... you've just described, in remarkably few rules, a system that really should have a full first-order logic checker for full parameterization. Instead, I suggest you take the other tack, splitting them into groups. Further, you can split them into clauses which are ANDed together.

Now, I have to ask: do you want these rules to be data driven, or can they be code-driven? Data-driven will be much more difficult.
Can you give a C++ example of splitting them into groups? :)

Code-driven is fine. There are 16 total rules. They'll be hard coded and never change.
I guess you could have something like this:

CheckRule(int min_amount, std::vector<int> match_set, std::vector<int> set);


Pass it these values:

min_amount: The number of values in the set that must match something from match_set.

match_set: The set of numbers to be checked against (like 7, 8 or 1, 2, 3, 4, 5, 6)

set: The array of numbers to check against the ruleset.

This sounds like homework, so I'll let you sort out the implementation :)
my siteGenius is 1% inspiration and 99% perspiration
If they're hard-coded it's not too difficult to write a set of functions that test the basic elements of your rules and can be combined to test the rules:

#include <algorithm>  // for std::count_ifbool is_blue(int nut)     { return nut == 1 || nut == 2; }bool is_red(int nut)      { return nut == 3 || nut == 4; }bool is_yellow(int nut)   { return nut == 5 || nut == 6; }const int num_nuts = 5;int nuts[num_nuts];int num_blue     = std::count_if(nuts, nuts+num_nuts, is_blue  );int num_red      = std::count_if(nuts, nuts+num_nuts, is_red   );int num_yellow   = std::count_if(nuts, nuts+num_nuts, is_yellow);int num_standing = num_blue + num_red + num_yellow;int num_holes    = num_nuts - num_standing;int num_colors   = (num_blue>0) + (num_red>0) + (num_yellow>0);int num_pairs    = (num_blue>1) + (num_red>1) + (num_yellow>1);int num_triples  = (num_blue>2) + (num_red>2) + (num_yellow>2);bool rule1 = num_standing == 3;  // Implies 2 holes with 5 nutsbool rule2 = num_standing == 2;  // Implies 3 holes with 5 nutsbool rule3 = num_standing == 4;  // Implies 1 hole with 5 nutsbool rule4 = num_colors == 3;    // Implies at least 3 standingbool rule5 = num_holes == 2 && num_pairs == 1;


Any more rules you need implemented?
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Fruny's example is how I would do it in C++. std::count and std::count_if can save you a fair amount of code here. Out of interest - because a lot of people do this - are you randomly generating an array, testing for compliance, and if it fails, regenerating the array until it complies? If so, a better way of doing it is usually to generate a compliant array in a predetermined order and then shuffle it using std::random_shuffle.

As an aside, Fruny's code also shows why I would not do this sort of thing in C++ if I could avoid it. :)

Python example:
arrayIsOk = (a.count(7) + a.count(8) = 2) and (len(a) = 5)

You probably wouldn't even need the last check.
I'd have started in a completely different place. "I need a set of 16 rules but I'm not sure how to make them" means to me "I need to make a base class called 'rule' and derive 16 concrete classes from it"
class rule{    bool matches(int a[5]) = 0;};class my_rule : public rule{    bool matches(int a[5]) { hard coded test; }};

It ends up more flexible but also larger than the parameterized version people have suggested. It can cope, with remarkably little change, with a new rule involving "3 yellow if there are 2 red, or 1 blue and 4 yellow if there is only 1 red" which doesn't fit in the original pattern of rules. The best thing about it is that when faced with the original problem, it's obvious how to work out a solution this way. The worst thing about it is that it is more complicated than it needs to be. It probably isn't the best solution in your case, but it isn't _bad_.

This topic is closed to new replies.

Advertisement