5

注意:**请阅读所有其他相关问题:**

这是我提出这个问题的第一次和第二次尝试:

这是问题所在:

  • 我有几个(比如 20 个)布尔验证(真/假)
  • 所有布尔验证作为一个整体也有一个验证结果

我正在尝试找到测试所有验证以及验证结果的最佳解决方案。我正在研究一个矩阵来保存所有可能的组合,但这可能是一种矫枉过正。

这是一个示例( 1 - 20 ):

  • test_1 = 有 30 次击杀
  • test_2 = 已找到地图 1
  • test_3 = 已掌握 1 级
  • test_4 = 已达到 Grunt 状态
  • test_5 = 拥有突击武器
  • test_6 = 有刀
  • test_7 = 有手榴弹
  • test_x = 等等...

因此,当 Player 将所有这些验证都设置为 TRUE 时,我可以给出关卡结果

  • if test_1, test_2, test_3 (三者的任意组合): level = green

所有组合为 ( 15 ):

  • 测试_1
  • 测试_2
  • 测试_3
  • 测试_1,测试_2
  • 测试_1,测试_3
  • test_2, test_1 (重复的可以跳过)
  • 测试_2,测试_3
  • test_3, test_1 (重复可以跳过)
  • test_3, test_2 (重复的可以跳过)
  • 测试_1、测试_2、测试_3
  • test_1、test_3、test_2(重复的可以跳过)
  • test_2, test_1, test_3 (重复可以跳过)
  • test_2, test_3, test_1 (重复可以跳过)
  • test_3, test_1, test_2 (重复的可以跳过)
  • test_3, test_2, test_1 (重复的可以跳过)

所以独特的组合是( 7 而不是 15 ):

  • 测试_1
  • 测试_2
  • 测试_3
  • 测试_1,测试_2
  • 测试_1,测试_3
  • 测试_2,测试_3
  • 测试_1、测试_2、测试_3

现在,我正在尝试找到最佳解决方案,为所有 20 个验证找到唯一组合,并从该矩阵中得出一个级别验证。

更新:

另外我只需要找到真正的组合,这样你就可以像这样阅读独特的组合:

  • 测试_1
  • 测试_2
  • 测试_3
  • 测试_1,测试_2
  • 测试_1,测试_3
  • 测试_2,测试_3
  • 测试_1、测试_2、测试_3

验证测试的布尔值结果

  • 对,错,错
  • 假,真,假
  • 假,假,真
  • 对,对,错
  • 对,错,对
  • 错误,正确,正确
  • 真的,真的,真的

所以这些组合中的任何一个都是绿色级别。

我还需要知道测试验证的顺序以及矩阵顺序以比较级别分配。所以对于 GREEN 级别,我只需要测试 1、2 和 3 的验证结果组合矩阵。所以我可以忽略测试 4 - 20

更新#2:

我知道这看起来像一个简单的 OR 条件,但我想取出组合逻辑将级别设置为矩阵。我可以使用组合矩阵来确定级别逻辑,而无需编写额外的代码或修改代码本身中的当前逻辑。我只想比较一组给定测试的验证结果,并为这些结果分配一个级别。验证组合的不同排列将导致不同的级别分配。

我知道我可以在代码本身中添加组合逻辑,但由于这个逻辑看起来非常不稳定,我认为这可能会提供更灵活的解决方案。建议?

4

4 回答 4

1

(为了清楚起见,删除了我之前的两个答案)

在您最后一次编辑之后,我想首先确保 100% 了解您想要的“级别检测算法”,而不是直接回答。

如果我理解得很好,您想定义/维护一个简单的配置结构,告诉哪些测试给出了哪个级别。

例如,使用关联数组:

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

含义:如果满足列表中的一个或多个测试,则将该级别(数组键)分配给玩家。这样的逻辑可以很容易地涵盖相当多的组合,并且可以避免处理一个巨大的矩阵。

例如,您可能想扩展逻辑来告诉测试列表中至少有 N 个测试得到满足。

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

那是你要的吗?

顺便说一句,你为什么不使用经典的 XP/升级系统?:-p

于 2012-11-23T15:51:48.373 回答
1

介绍

您可以轻松获得这样的组合:

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;
}

输出

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
)

问题

它们是关于1,048,576组合的,我相信这不是你想要的那种数组我会建议基于条件的组合而不是所有可能的组合

例子

// 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));

输出

+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>

使用的类

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 ;};

简单的在线演示

于 2012-11-23T15:59:38.323 回答
1

不完全回答你的问题,但似乎你错过了一些东西。

假设您有 20 个测试,标记为 1 虽然n。决定 test n是否要对其进行验证。要么包含验证测试,要么不包含。这是每次测试的两个选择。继续 ( n -1) 直到您没有更多的测试。对于n =20,即 2^20 = 1048576 种可能的测试组合(包括一种您不选择任何测试的组合),这意味着 1048576 个结果。现在我仍然不明白你所说的“验证级别”是什么意思,但我不得不想知道为什么你首先需要这么多的测试组合。

编辑:嗯,一个测试矩阵(可以这么说)可以工作....但你仍然必须生成矩阵。您可能不会手动编码所有 1048576 组合。但是假设您已经创建了映射,那么您只有一个零索引数组作为查找表,其中包含您想要的 1048576 个值,您就完成了!将测试结果映射到矩阵所需要做的就是为每个测试分配一个二进制数字(test1 是一个位置,test2 是 2 的位置,等等)

我怀疑您真正想要的是一种快速生成映射的方法,因为您可能会在 php 中编码一些更广泛的规则......但这里有一些有趣的部分。如果您有一些代码为任何一组测试生成矩阵,那么矩阵就是多余的;您的代码本质上是矩阵的压缩表示。在另一个上使用一个的唯一要点是一个是否会比另一个更快。

看来您真的并不关心所有 1048576 组合。我怀疑将测试划分为他们自己的一组测试(一个用于红色,一个用于蓝色等)可能会对您有所帮助。例如,如果您将测试分成 5 个组,并且每个组有 3 种不同的可能性(而不是 16 种),那么您只使用(每组 3 个不同的结果)^(4 组)= 81独特的结果。81 个独特的结果比超过 100 万个更易于管理。

看来您可能还需要为不同的独立事物使用不同的分区。只要其中一定数量的测试是真的,即使哪些测试结果为真也可能无关紧要,即“目标:满足 5 个蓝色目标中的 3 个”“目标:满足 10 个红色和蓝色目标中的 7 个”或其他. 这些测试必须独立访问,并且它们不一定与其他测试的结果相乘——如果你的蓝色目标都没有得到满足,那么 10 个红色和蓝色目标中永远不会有 7 个得到满足(我会承认,如果没有例子,这很难进一步解释)。

所以,真的,没有快速的答案。要么单独处理所有 1048576 个组合,要么为测试创建和编码某种通用分组方案,以大幅减少组合。您当然可以通过所述方案创建完整的矩阵方案,甚至可以为给定的组合动态生成完整的 1048576 元素矩阵。但是你不能只烧一半就烧掉整根蜡烛。

编辑二(和三):

我再做一个猜测。您提出的级别似乎与每个(和前一批)完成的目标数量有关。因此,将您的结果编码为一串(“T”和“F”)并计算每个批次的“F”并从那里确定。如果 Fs 的计数是字符串中的字符数,则该批次的目标没有完成,如果 Fs 的数量为零,则该批次的所有目标都已完成。

可以说蓝色之后的批次是紫色的。有人可以在不完成所有绿色批次的情况下实现紫色目标吗?如果是这样,您可以为这种可能性分配另一种颜色;如果不是,那么,我会假设这些级别是有序的(绿色和蓝色之间的橙色,也许是“棕色”或蓝色和紫色之间的东西——你可以说我是凭空捏造出来的——它应该相当简单通过这些计数级联来确定当前的“级别”。

如果没有排序,那么和我上面提到的情况很相似:每个组都有一个结果{“没有完成目标”,“完成一些目标”,“完成所有目标”}。有些x组意味着3^x可能的结果。您应该将水平矩阵基于这些结果,而不是前面提到的测试结果。

于 2012-11-23T17:25:03.870 回答
0

正如您所提到的,我已经找到了您问题的图形显示。

|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|

在我看来,您应该以反向对角线顺序检查条件,然后从左到右。这意味着

首先检查 [9,9]

如果失败检查 [8,8]

如果失败,请检查 [7,7]

如果它给出真正的检查 [7,8]

如果这给出了错误的检查 [7,9] 这一定是答案和简单的快捷方式,

这种方法将减少所有处理时间。

于 2012-11-23T16:01:56.670 回答