如何使用PHP中的MySQL数据设置递归步骤计数器

时间:2017-06-03 18:47:55

标签: php mysql sql recursion

所以,我正在制作一款充满乐趣的迷你策略游戏,偶然发现了一个我无法解决的问题。

我认为这很容易,但似乎我错了,或者解决方案可能在我的鼻子底下,但我正在寻找关于我的问题的另一种观点。

我有一个数据库,其中包含在地图上记录的地区列表。这些地区中的每一个都有一份相邻地区的清单。

ID | name  | adjacent     | faction

1  | Name1 | 2;7;10;24    | 5
2  | Name2 | 1;3;7;8      | 4
3  | Name3 | 2;4;5;8      | 8

相邻是由分号分隔的ID列表

我的目标是找到某个派系所持领土的最短路线。距离的计算方法是到达领土所需的步数。

这样,我可以快速获得距离并在这方面应用修饰符。

例如,在上面的数据代码中:

  • 领土1与2的距离为0,因为它们相邻。

  • 地域1与3相距1,因为3与2相邻(2与1相邻)。

  • 地域1与4相距2,因为4与3相邻,与2相邻。

目前,我的代码看起来像这样;

function getProximity($connexion, $faction, $terrain)
{
    $query = "SELECT id, adjacent FROM worldmap WHERE faction = ".$faction;
    $result = mysqli_query($connexion, $query);

    $proximity = 10;
    while ($row = mysqli_fetch_row($result))
    {
        $adjacent = explode(";", $row[1]);
        if (in_array($terrain, $adjacent))
        {
            //territory is adjacent, stop looking
            return 0;
        }
        else
        {
            //if not directly adjacent, seek through the list
            foreach($adjacent as $adj)
            {
                $prox = getSubProximity($connexion, array($row[0]), $adj, $terrain, 1);
                if ($prox < $proximity)
                {
                    $proximity = $prox;
                }
            }
        }
    }

    return $proximity;
}

function getSubProximity($connexion, $from, $terrain, $terrain2, $proximity)
{
    //Attempt to break weird loops... not succesful...
    if ($proximity > 10)
    {
        return $proximity;
    }
    $query = "SELECT id, adjacent FROM worldmap WHERE id = ".$terrain;
    $result = mysqli_query($connexion, $query);

    while ($row = mysqli_fetch_row($result))
    {
        $adjacent = explode(";", $row[1]);
        if (in_array($terrain2, $adjacent))
        {
            //territory is adjacent, stop looping
            return $proximity;
        }
        else
        {
            foreach($adjacent as $adj)
            {
                //check if we've been through there
                if (!in_array($adj, $from))
                {
                    array_push($from, $terrain);
                    $prox = getSubProximity($connexion, $from, $adj, $terrain2, $proximity+1);
                    if ($prox < $proximity)
                    {
                        $proximity = $prox;
                    }
                }
            }
        }
    }
}

感谢您的帮助,如果您有其他方法可以获得相同的结果,请随意;数据库结构不是静态的,完全在我的控制之下。

1 个答案:

答案 0 :(得分:2)

我建议的一些事情:

  • MySql在处理自引用/分层数据方面并不是那么强大,所以对于通常的答案(“在SQL中执行”),在PHP中执行逻辑。

  • 由于总数据集相对较小,因此请使用一个查询读取内存中的所有地区数据,然后将数据库保留在其中。

  • 执行非递归,广度优先搜索。这样,您最多只需访问一次世界领域。

  • 保留您已访问过的区域列表,以避免在圈子中无限循环,并为此使用关联数组,以便快速查找密钥(而不是低效in_array)。仅记下$from领域是不够的。

以下是此类代码的大纲:

// This function returns the whole data set in a nice data structure 
function getProximityData($connexion) {
    $query = "SELECT id, adjacent, faction FROM worldmap";
    $result = mysqli_query($connexion, $query);
    while ($row = mysqli_fetch_row($result)) {
        $territories[$row[0]] = [
            "adjacent" => explode(";", $row[1]),
            "faction" => $row[2]
        ];
    }
    return $territories;
}

function getProximity($territories, $faction, $terrain) {
    // mark this terrain as visited
    $visited[$terrain] = 1;
    // Create queue for breadth first search
    $queue = [$terrain];
    $steps = 0;
    while (count($queue)) {
        $next = [];
        foreach($queue as $terrain) {
            if ($territories[$terrain]["faction"] === $faction) {
                // Territory belongs to the faction, stop looking
                return $steps;
            }
            // collect list of unvisited neighbours of this terrain
            foreach($territories[$terrain]["adjacent"] as $adj) {
                if (!isset($visited[$adj])) { // not yet visited
                    $next[] = $adj;
                    $visited[$adj] = 1;
                }
            }
        }
        $queue = $next;
        $steps++;
    }
    // Should never get here: it would mean territories are disconnected
}

// Load the world map: 
$territories = getProximityData($connexion);

// Example use:
$steps = getProximity($territories, 5, 4); // distance of faction 5 to territory 4.

请注意,对于由给定阵营(第二个参数)拥有的区域(最后一个参数),将返回0。对于与该派系相邻的领土,你将得到1,......等。