Fun Decision Tree Question

Yesterday I solved an interesting question that went as follows: given two players with a list of integers evaluate whether or not a player has won based on 3 rules. Note that the question was not EXACTLY worded like this but this is the gist of it.

The first rule is if player 1 has a fibonacci sequence player 1 wins, otherwise player 2 wins and if there’s a tie we evalute the second rule. If need be, we evaluate the third rule if the second rule results in a tie also.

The second rule is the player with the highest # wins.

And the third rule is the player with a higher count of prime numbers wins.

The solution

Initially my solution was to simply write a function that evaluated each of the 3 rules one by one, resolving ties if needed. This solution basically breaks down into a tree and a sort of messy if/else branch. We can think of evaluating a rule as returning 3 values PLAYER1_WON, PLAYER2_WON, or TIE. We can use separate functions like count_primes(list), isFibonacci(list) and max_card(list) to avoid redundant code and simplify everything. I coded it and then my interviewer asked me to do better for arbitrary N rules.

As I was coding the worse solution that worked, I realized we could just simplify each rule into a list of them and evaluate them one by one top down much like you would in a directed-acylic graph of dependencies or a decision tree. This approach is nice because in essence we only have to write code for each rule, then evaluate them sequentially instead of writing very branchy/bug-prone and difficult to read code. With this approach we think of our rules as a pipeline and just evaluate the next rule if the current rule evaluates to a tie.

class Rule {
    virtual RULE_RESULT evaluate(vector<int>& list1, vector<int>& list2);
}

and then have each rule override accordingly using virtual inheritance

class FibonacciRule : public Rule {
    virtual RULE_RESULT evaluate(vector<int>& list1, vector<int>& list2) {
        // implement logic for determining who won the fib sequence rule
    }
}

class MaxHandrule : public Rule {
    virtual RULE_RESULT evaluate(vector<int>& list1, vector<int>& list2) {
        // implement this
    }
}

class PrimeRule : public Rule {
    virtual RULE_RESULT evaluate(vector<int>& list1, vector<int>& list2) {
        // implement this
    }
}

When we want to evaluate any number of N rules we can just pass in a list of rules and go through to evaluate a hand like so, continuing if any ties occur

RULE_RESULT evaluateLists(vector<int>& list1, vector<int>& list2, vector<Rule>& rules) {
    for (Rule& rule: rules) {
        RULE_RESULT result = rule.evaluate(list1, list2);
        if (result != TIE)
            return result;
    }
    // All the rules were ties
    return TIE;
}

So we simplified the problem, reduced the amount of work to do and improved the maintainability of our code by taking this approach to solve this problem.

Back