所以,我正在制作一款充满乐趣的迷你策略游戏,偶然发现了一个我无法解决的问题。
我认为这很容易,但似乎我错了,或者解决方案可能在我的鼻子底下,但我正在寻找关于我的问题的另一种观点。
我有一个数据库,其中包含在地图上记录的地区列表。这些地区中的每一个都有一份相邻地区的清单。
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;
}
}
}
}
}
}
感谢您的帮助,如果您有其他方法可以获得相同的结果,请随意;数据库结构不是静态的,完全在我的控制之下。
答案 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,......等。