Reputation: 85308
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
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
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 ;};
Upvotes: 1
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
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