我正在制作一个2D字符串,最后用\ n \ n新行。
迷宫是1000 x 1000,但为了便于阅读,我将其缩小为10 x 5。
0表示空格。
S意味着起点。
T表示目标点。
我创建了一个名为cal_path的函数来计算S和T之间的路径 结果不正确。我已经对估计答案进行了硬编码。任何建议/帮助将不胜感激。
<?php
$maze='S000000000
0000000000
0000000000
00000000T0
0000000000';
$maze_y=explode(PHP_EOL,$maze);//convert line to array
function show_maze($maze_y){
for ($y=0;$y<count($maze_y);$y++){
for ($x=0;$x<=strlen($maze_y[$y]);$x++){
echo $maze_y[$y][$x];
}
echo "\n";
}
}
function cal_path($maze_y){
// I hardcoded it right now, I am stuck here
return array(
array(1,1),
array(1,2),
array(1,3),
array(2,4),
array(2,5),
array(2,6),
array(3,7),
array(3,8),
);
}
show_maze($maze_y); //original maze
$path=cal_path($maze_y);
foreach ($path as $point){
$maze_y[$point[0]][$point[1]]='P';
}
show_maze($maze_y);
输出:
S000000000
0000000000
0000000000
00000000T0
0000000000
估算输出:
S000000000
0PPP000000
0000PPP000
0000000PP0
0000000000
这是我的尝试,但未能找到最短路径。
<?php
$maze = '0000000000
0000S00000
0000000000
00000000T0
0000000000';
$maze_y = explode(PHP_EOL, $maze); //convert line to array
function show_maze($maze_y)
{
for ($y = 0; $y < count($maze_y); $y++) {
for ($x = 0; $x < strlen($maze_y[$y]); $x++) {
echo $maze_y[$y][$x];
}
echo "\n";
}
}
function cal_path($maze_y)
{
$found_start = -1;
$found_end = -1;
$row = 0;
foreach ($maze_y as $current) {
for ($x = 0; $x <= strlen($current); $x++) {
if ($found_start == -1) {
$found_start = (strpos($current, 'S') === false) ? '-1' : strpos($current, 'S');
if ($found_start != -1) {
$found_start_y = $row;
}
}
if ($found_end == -1) {
$found_end = (strpos($current, 'T') === false) ? '-1' : strpos($current, 'T');
if ($found_end != -1) {
$found_end_y = $row;
}
}
}
$row++;
}
echo 'start' . $found_start . ' - ' . $found_start_y;
echo "\n";
echo 'end' . $found_end . ' - ' . $found_end_y;
echo "\n";
$step_size_y = $found_end_y - $found_start_y;
$step_size_x = $found_end - $found_start;
echo "step size X $step_size_x Y $step_size_y";
echo "\n";
$start_pointer = array($found_start, $found_start_y);
$maxtry = 100;
$cal_result = array();
while ($maxtry > 0 && ($start_pointer[0] != $found_end || $start_pointer[1] != $found_end_y)) {
$maxtry--;
if ($step_size_x > 1 && $start_pointer[0] != $found_end) {
$start_pointer[0]++;
} else if ($step_size_x < 1 && $start_pointer[0] != $found_end) {
$start_pointer[0]--;
}
if ($step_size_y > 1 && $start_pointer[1] != $found_end_y) {
$start_pointer[1]++;
} else if ($step_size_y < 1 && $start_pointer[1] != $found_end_y) {
$start_pointer[1]--;
}
array_push($cal_result, array($start_pointer[1] , $start_pointer[0] ));
echo 'Path: ' . $start_pointer[0] . ' - ' . $start_pointer[1] . "\n";
}
return $cal_result;
}
show_maze($maze_y); //original maze
$path = cal_path($maze_y);
foreach ($path as $point) {
$maze_y[$point[0]][$point[1]] = 'P';
}
show_maze($maze_y);
答案 0 :(得分:2)
您可以使用以下内容作为起点。
这是一个简单的伪代码实现:https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Using_a_priority_queue
<?php
require_once 'src/MinQueue.php';
require_once 'src/Dijkstra.php';
require_once 'src/Maze.php';
$maze = Maze::fromString(file_get_contents('maze.txt')); // < a simple text file
$start = $maze->find('S');
$goal = $maze->find('T');
$helper = new Dijkstra(
// return neighbors
function ($a) use ($maze) {
return $maze->getNeighbors($a, ['W']);
},
// calculate the distance
function ($a, $b) use ($maze) {
return $maze->getDistance($a, $b);
}
);
$tStart = microtime(true);
$path = $helper->findPath($start, $goal);
$tEnd = microtime(true);
// export the maze with the path marked with '.'
$mazeStrWithPath = $maze->toString(function ($tile) use ($path) {
return in_array($tile, $path, true) && !in_array($tile->value, ['S', 'T'])
? '.'
: $tile->value
;
});
printf("%s\nin: %.5fs\n\n", $mazeStrWithPath, $tEnd - $tStart);
_____________________________________________________W_________________
_____________________________________________________W_________________
_____________________________________________________W_________________
_________________W___________________________________W_________________
_________________W___________________________________W_____________T___
_________________W___________________________________W____________.____
_________________W___________________________________W___________._____
_________________W___________________________________W__________.______
_________________W___________________________________W_________._______
_________________W____________________.........______W________.________
_________________W___________________.WWWWWWWWW._____W_______._________
_________________W__________________._W_____...______W______.__________
_________________W_________________.__W____.WWWWWWWWWW_____.___________
_________________W________________.___W_____...............____________
_________________W_____________...____W________________________________
_________________W____________.WWWWWWWW________________________________
_________________W_____________.______W________________________________
_________W_______W______________._____W________________________________
_________W_______W______________._____W________________________________
_________W_______WWWWWWWWWWWWWWW._____W________________________________
_________W_____________________.______W________________________________
_________W____________________._______W________________________________
_________W___________________.________W________________________________
__S...___W__________________._________W________________________________
______.__W_________________.__________W________________________________
_______._W________________.___________W________________________________
________.W_______________.____________W________________________________
_________................_____________W________________________________
使用以下类:
<?php
declare(strict_types=1);
class MinQueue implements \Countable
{
/**
* @var \SplPriorityQueue
*/
private $queue;
/**
* @var \SplObjectStorage
*/
private $register;
/**
* MinQueue constructor.
*/
public function __construct()
{
$this->queue = new class extends \SplPriorityQueue
{
/** @inheritdoc */
public function compare($p, $q)
{
return $q <=> $p;
}
};
$this->register = new \SplObjectStorage();
}
/**
* @param object $value
* @param mixed $priority
*/
public function insert($value, $priority)
{
$this->queue->insert($value, $priority);
$this->register->attach($value);
}
/**
* @return object
*/
public function extract()
{
$value = $this->queue->extract();
$this->register->detach($value);
return $value;
}
/**
* @inheritdoc
*/
public function contains($value)
{
return $this->register->contains($value);
}
/**
* @inheritdoc
*/
public function count()
{
return count($this->queue);
}
}
<?php
declare(strict_types=1);
class Dijkstra
{
/**
* @var callable
*/
private $neighbors;
/**
* @var callable
*/
private $length;
/**
* Dijkstra constructor.
*
* @param callable $neighbors
* @param callable $length
*/
public function __construct(callable $neighbors, callable $length)
{
$this->neighbors = $neighbors;
$this->length = $length;
}
/**
* see: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Using_a_priority_queue
*
* @param object $src
* @param object $dst
*
* @return array
*/
public function findPath($src, $dst): array
{
// setup
$queue = new MinQueue();
$distance = new \SplObjectStorage();
$path = new \SplObjectStorage();
// init
$queue->insert($src, 0);
$distance[$src] = 0;
while (count($queue) > 0) {
$u = $queue->extract();
if ($u === $dst) {
return $this->buildPath($dst, $path);
}
foreach (call_user_func($this->neighbors, $u) as $v) {
$alt = $distance[$u] + call_user_func($this->length, $u, $v);
$best = isset($distance[$v]) ? $distance[$v] : INF;
if ($alt < $best) {
$distance[$v] = $alt;
$path[$v] = $u;
if (!$queue->contains($v)) {
$queue->insert($v, $alt);
}
}
}
}
throw new \LogicException('No path found.');
}
/**
* @param object $dst
* @param \SplObjectStorage $path
*
* @return array
*/
private function buildPath($dst, \SplObjectStorage $path): array
{
$result = [$dst];
while (isset($path[$dst]) && null !== $path[$dst]) {
$src = $path[$dst];
$result[] = $src;
$dst = $src;
}
return array_reverse($result);
}
}
<?php
declare(strict_types=1);
class Maze
{
/**
* @var array
*/
private $tiles = [];
/**
* Maze constructor.
*
* @param array $tiles
*/
private function __construct(array $tiles = [])
{
$this->tiles = $tiles;
}
/**
* @param string $maze
* @param string $rowDelimiter
*
* @return Maze
*/
public static function fromString(string $maze, string $rowDelimiter = "\n"): Maze
{
$tiles = [];
foreach (explode($rowDelimiter, $maze) as $r => $row) {
$rowTiles = [];
foreach (str_split(trim($row)) as $c => $value) {
$rowTiles[] = (object)[
'row' => $r,
'col' => $c,
'value' => $value
];
}
$tiles[] = $rowTiles;
}
return new self($tiles);
}
/**
* @param callable $renderer
* @param string $rowDelimiter
*
* @return string
*/
public function toString(callable $renderer = null, string $rowDelimiter = "\n"): string
{
$renderer = $renderer ?: function ($tile) { return $tile->value; };
$result = [];
foreach ($this->tiles as $r => $row) {
if (!isset($result[$r])) {
$result[$r] = [];
}
foreach ($row as $c => $tile) {
$result[$r][$c] = $renderer($tile);
}
}
return implode($rowDelimiter, array_map('implode', $result));
}
/**
* @param string $value
*
* @return object
*/
public function find(string $value)
{
foreach ($this->tiles as $row) {
foreach ($row as $tile) {
if ($tile->value === $value) {
return $tile;
}
}
}
return null;
}
/**
* @param object $tile
* @param array $filter
*
* @return array
*/
public function getNeighbors($tile, array $filter = []): array
{
$neighbors = [];
foreach ([
[-1, -1], [-1, 0], [-1, 1],
[ 0, -1], [ 0, 1],
[ 1, -1], [ 1, 0], [ 1, 1],
] as $transformation) {
$r = $tile->row + $transformation[0];
$c = $tile->col + $transformation[1];
if (isset($this->tiles[$r][$c]) && !in_array($this->tiles[$r][$c]->value, $filter, true)) {
$neighbors[] = $this->tiles[$r][$c];
}
}
return $neighbors;
}
/**
* @param object $a
* @param object $b
*
* @return float
*/
public function getDistance($a, $b): float
{
$p = $b->row - $a->row;
$q = $b->col - $a->col;
return sqrt($p * $p + $q * $q);
}
}