Phill Pafford
Phill Pafford

Reputation: 85308

Matrix Combination Logic

NOTE: **Please read all other related questions:**

Here is my first and second attempts at asking this question:

Here is the problem:

I'm trying to find the best solution to test all the validations and also the validation result. I was looking into a Matrix to hold all possible combinations but that might be an overkill.

Here is an example ( 1 - 20 ):

So when the Player has all these validations as TRUE I can then give a level result

All combinations are ( 15 ):

So unique combinations are ( 7 instead of 15 ):

Now I'm trying to find the best possible solution to find unique combinations for all 20 validations and come up with a level validation from that matrix.

UPDATE:

Also I need to find only TRUE Combinations so you might read the Unique Combinations like this:

Boolean Value Results from Validation Tests

So any of these combinations would be a GREEN level.

Also I need to know the order of the test validations as well as the matrix order to compare for level assignment. So for GREEN level I only need the validation result combination matrix for test 1, 2 and 3. So I could ignore tests 4 - 20

UPDATE #2:

I know this looks like a simple OR condition but I wanted to take out the combination logic to set the level into a matrix. I could use the matrix of combinations to determine the level logic without having to code additional or modify current logic in the code itself. I wanted to just compare the validations results for a given set of tests and assign a level to those results. Different permutations of the validation combinations would result in different level assignments.

I understand that I could add the combination logic in the code itself, but as this logic looks to be very volatile and thought this might offer a more flexible solution. Suggestions?

Upvotes: 5

Views: 1136

Answers (4)

Maxime Pacary
Maxime Pacary

Reputation: 23041

(removed my two previous answers for clarity)

After your last edit, instead of answering directly, I would like first to be sure to 100% understand the "level detection algorithm" you want.

If I understand well, you would like to define/maintain a simple configuration structure telling which tests give which level.

e.g. with an associative array:

array(
  'green' => array('test1', 'test2', 'test3'),
  'orange' => array('test2', 'test3', 'test5')
  ...
  );

With the meaning: if one or more of the tests in the list are satisfied, assign that level (array key) to the player. Such logic could easily cover quite a lot of combinations, and would avoid handling a huge matrix.

Maybe you want to extend the logic to tell, for example, that at least N tests among the test list are satisfied.

array(
  'green' => array(
      'tests' => array('test1', 'test2', 'test3'),
      'nb_required' => 2
    ),
  ...
  );

Is that what you want?

BTW, why don't you use a classic XP/level up system? :-p

Upvotes: 1

Baba
Baba

Reputation: 95101

Introduction

You can easy get combinations like this :

echo "<pre>";
$test = ["test_1","test_2","test_3"];

// Get Combination
$return = uniqueCombination($test);

//Sort
sort($return);

//Pretty Print
print_r(array_map(function($v){ return implode(",", $v); }, $return));

function uniqueCombination($in, $minLength = 1, $max = 10) {
    $count = count($in);
    $members = pow(2, $count);
    $return = array();
    for($i = 0; $i < $members; $i ++) {
        $b = sprintf("%0" . $count . "b", $i);
        $out = array();
        for($j = 0; $j < $count; $j ++)
            $b{$j} == '1' and $out[] = $in[$j];

        count($out) >= $minLength && count($out) <= $max and $return[] = $out;
    }
    return $return;
}

Output

Array
(
    [0] => test_1
    [1] => test_2
    [2] => test_3
    [3] => test_1,test_2
    [4] => test_1,test_3
    [5] => test_2,test_3
    [6] => test_1,test_2,test_3
)

The Problem

They are about 1,048,576 combination and i believe this is not the kind of array you want i would suggest a condition based combination rather than all possible combination

Example

// Game Conditions
$game = new Game();
$game->addCondition(new Condition(new Level(1), new Kill(30)));
$game->addCondition(new Condition(new Level(2), new Map(1), new Kill(10)));
$game->addCondition(new Condition(new Level(3), new Grunt(10)));
$game->addCondition(new Condition(new Level(4), new Knife(1), new Ak47(1)));
$game->addCondition(new Condition(new Level(5), new Grenade(1), new Combo(7)));
$game->addCondition(new Condition(new Level(6), new Kill(100), new Blow(10), new Stab(10)));
$game->addCondition(new Condition(new Level(7), new Herb(10), new Medipack(1), new Map(1), new Artwork(1)));
$game->addCondition(new Condition(new Level(8), new Grenade(20),new Artwork(5)));


// User Starts Game
$user = new User($game);


$user->task(new Map(1));
$user->task(new Herb(5));
$user->task(new Kill(10));
$user->task(new Kill(10));
$user->task(new Herb(10));
$user->task(new Kill(10));
$user->task(new Kill(10));
$user->task(new Ak47(1));
$user->task(new Knife(1));
$user->task(new Map(1));
$user->task(new Grunt(17));
$user->task(new Kill(60));
$user->task(new Combo(1));
$user->task(new Kill(40));
$user->task(new Medipack(1));
$user->task(new Artwork(1));
$user->task(new Grenade(1));
$user->task(new Combo(10));
$user->task(new Blow(10));
$user->task(new Stab(5));
$user->task(new Blow(10));
$user->task(new Stab(5));
$user->task(new Stab(5));

printf("\n<b>Total Point %s",number_format($user->getPoint(),0));

Output

+Task Map   Added  (1)
+Task Herb  Added  (5)
+Task Kill  Added  (10)
^Task Kill  Updated (20)
^Task Herb  Updated (15)
^Task Kill  Updated (30)

*Level 1 Completed* 


*Level 2 Completed* 

^Task Kill  Updated (40)
+Task Ak47  Added  (1)
+Task Knife     Added  (1)
^Task Map   Updated (2)
+Task Grunt     Added  (17)

*Level 3 Completed* 


*Level 4 Completed* 

^Task Kill  Updated (100)
+Task Combo     Added  (1)
^Task Kill  Updated (140)
+Task Medipack  Added  (1)
+Task Artwork   Added  (1)
+Task Grenade   Added  (1)
^Task Combo     Updated (11)

*Level 5 Completed* 

+Task Blow  Added  (10)
+Task Stab  Added  (5)
^Task Blow  Updated (20)
^Task Stab  Updated (10)

*Level 6 Completed* 


*Level 7 Completed* 

^Task Stab  Updated (15)


<b>Total Point 1,280</b>

Classes Used

class Task {
    private $no;

    function __construct($no = 1) {
        $this->no = $no;
    }

    function getNo() {
        return $this->no;
    }

    function getName() {
        return get_called_class();
    }

    function merge(Task $task) {
        $this->no += $task->getNo();
        return $this;
    }
}
class User {
    private $game;
    private $point;
    private $tasks = array();

    function __construct(Game $game) {
        $this->game = $game;
    }

    function getPoint() {
        return $this->point;
    }

    function getTask() {
        return $this->tasks;
    }

    function task(Task $task) {
        if (isset($this->tasks[$task->getName()])) {
            $this->tasks[$task->getName()]->merge($task);
            printf("^Task %s \tUpdated (%s)\n", $this->tasks[$task->getName()]->getName(), $this->tasks[$task->getName()]->getNo());
        } else {
            printf("+Task %s \tAdded  (%s)\n", $task->getName(), $task->getNo());
            $this->tasks[$task->getName()] = $task;
        }

        $this->point += $task->getNo() * $task->d;
        $this->game->notify($this);
    }
}
class Condition {
    private $task = array();
    private $status = false;

    function __construct(Level $level) {
        $this->level = $level;
        $tasks = func_get_args();
        array_shift($tasks);
        $this->task = new SplObjectStorage($tasks);
        foreach ( $tasks as $task )
            $this->task->attach($task);
    }

    function update(Game $game, User $user) {
        if ($this->status)
            return;
        $n = 0;
        foreach ( $this->task as $cTask ) {
            foreach ( $user->getTask() as $task ) {
                if ($cTask->getName() == $task->getName()) {
                    if ($task->getNo() >= $cTask->getNo())
                        $n ++;
                }
            }
        }
        if ($n === count($this->task) && ($game->getLevel()->getNo() + 1) == $this->level->getNo()) {
            $this->status = true;
            $game->setLevel($this->level);
            printf("\n*Level %d Completed* \n\n", $this->level->getNo());
        }
    }

    function getStatus() {
        return $this->status;
    }
}
class Game {
    private $taskCondition;
    private $level;

    public function __construct() {
        $this->taskCondition = new SplObjectStorage();
        $this->level = new Level(0);
    }

    function setLevel(Level $level) {
        $this->level = $level;
    }

    function getLevel() {
        return $this->level;
    }

    function addCondition($condition) {
        $this->taskCondition->attach($condition);
    }

    public function notify($user) {
        foreach ( $this->taskCondition as $conditions ) {
            if ($conditions->getStatus() === true) {
                // detached completed condition
                $this->taskCondition->detach($conditions);
                continue;
            }
            $conditions->update($this, $user);
        }
    }

    public function hasCondition() {
        return count($this->taskCondition);
    }
}

class Level extends Task{}
class Action extends Task{};
class Weporn extends Task{};
class Skill extends Task{};
class Tresure extends Task{};
class Medicine extends Task{};

class Kill extends Action{public $d = 5 ;};
class Blow extends Action{public $d = 7 ;};
class Stab extends Action{public $d = 10 ;};

class Map extends Tresure{public $d = 10 ;};
class Artwork extends Tresure{public $d = 20 ;};

class Knife extends Weporn{public $d = 5 ;};
class Grenade extends Weporn{public $d = 10 ;};
class Ak47 extends Weporn{public $d = 10 ;};

class Jump extends Skill{public $d = 2 ;};
class Grunt  extends Skill{public $d = 4 ;};
class Combo extends Skill{public $d = 7 ;};

class Medipack extends Medicine{public $d = 5 ;};
class Herb extends Medicine{public $d = 5 ;};

Simple Online Demo

Upvotes: 1

JayC
JayC

Reputation: 7141

Not quite to answer your question, but it seems you're missing something.

Lets say you have twenty tests, labeled 1 though n. Decide for test n whether you want to validate for it, or not. Either you include the test for validation, or you don't. That's two choices for every test. Proceed through (n-1) until you have no more tests. For n=20, that's 2^20 = 1048576 possible combinations of tests (including one combination where you don't select any tests), which means 1048576 results. Now I still don't understand what you mean by "level of validation", but I have to wonder why you'd need that many combinations of tests in the first place.

Edit: Well, a test matrix (so to speak) could work.... but you'd still have to generate the matrix. You're likely not going to hand code all 1048576 combinations. But supposing you've already created the mapping, you just have a zero indexed array as a look up table with 1048576 values that you desire and you're done! All you would need to map the test results to the matrix would be assigning each test a binary digit (test1 would be the one's place, test2 would be the 2's place, etc.)

What I suspect you really want is a quick way to generate the mapping given some broader rules you might encode in php... but here's funny part about that. If you had some code that generated the matrix for any and every set of tests, the matrix is then superfluous; Your code is essentially a compressed representation for the matrix. The only point in using one over the other is whether or not one would be faster than the other.

It also seems you really don't care about all 1048576 combinations. I suspect it might benefit you to partition your tests into their own set of tests (one for red, one for blue, etc). For instance, if you partition tests into groups of 5, and there is, oh, 3 different possibilities (instead of 16) for each group, you're only working with (3 different results per group)^(4 groups) = 81 unique results. Eighty one unique results is much more manageable than over a million.

It seems you may also need different partitions for different independent things. It also may not matter even which tests result in true as long as a certain number of them are true, that is "goal: 3 out of 5 blue objectives met" "goal: 7 out of 10 red and blue objectives met" or whatever. Those tests would have to be accessed independently and they wouldn't necessarily be multiplicative with the results of the other tests-- there will never be 7 out of 10 red and blue objectives met if none of your blue objectives are met (I'll admit, this is hard to explain further without an example).

So, really, there's no quick answer. Either you deal with all and every one of the 1048576 combinations individually, or you create and encode some sort of generic grouping scheme for your tests to drastically cut down on combinations. You can certainly create your full matrix scheme by said scheme, and maybe even dynamically generate your full 1048576 element matrix for a given set of combinations. But you cannot burn a whole candle by only burning half of it.

Edit II (and III):

I'm going to make one more guess. The levels you've proposed seem to be related to the number of objectives completed per each (and previous batch). So encode your results in to a string of ("T", and "F") and count the "F"s for each batch and determine from there. If the count of Fs is the number of characters in the string, no objective of the batch is completed, if the number of Fs is zero, all objectives of the batch are completed.

Lets say the batch after blue is purple. Could somebody achieve a goal in purple without completing all the green batch? If so, you have another color to assign for that possibility; if not, then, I would assume that these levels are ordered (orange between green and blue, maybe "brown" or something between blue and purple--you can tell I pulled this out of thin air-- and it should be fairly simple to cascade through these counts to determine the current "level".

If there is no ordering, then it's quite analogous to the situation I mentioned above: Each group has a result { "no objectives completed", "some objectives completed", "all objectives completed"}. There are x groups which means 3^x possible results. You should be basing your level matrix on these results, not the results of the tests previously mentioned.

Upvotes: 1

Mohamed Navas
Mohamed Navas

Reputation: 612

I have found the graphic display of your question as you mentioned.

|0|1|2|3|4|5|6|7|8|9|
|1|T|T|T|T|T|T|T|T|T|
|2|F|T|T|T|T|T|T|T|T|
|3|F|F|T|T|T|T|T|T|T|
|4|F|F|F|T|T|T|T|T|T|
|5|F|F|F|F|T|T|T|T|T|
|6|F|F|F|F|F|T|T|T|T|
|7|F|F|F|F|F|F|T|T|T|=>[7,9] if this is the answer
|8|F|F|F|F|F|F|F|T|T|
|9|F|F|F|F|F|F|F|F|T|

In my opinion you should check the conditions in reverse diagonal order, then left to right. That means

first check [9,9]

if it fails check [8,8]

if this fails check [7,7]

if it gives true check [7,8]

if this gives false check [7,9] this must be the answer and the simple shortcut,

This method will reduce over all process time.

Upvotes: 0

Related Questions