За последние 24 часа нас посетили 22402 программиста и 1637 роботов. Сейчас ищет 971 программист ...

Кратчайший путь

Тема в разделе "Прочие вопросы по PHP", создана пользователем Fusix, 24 апр 2011.

  1. Fusix

    Fusix Активный пользователь

    С нами с:
    22 фев 2010
    Сообщения:
    67
    Симпатии:
    0
    Как реализовать Алгоритм A* или Алгоритм Дейкстры на PHP?
    Нужно найти кратчайший путь от старта (зеленым) по цели например тут:

    [​IMG]

    Уже полгода мучаюсь... помогите. Зарание спасибо
     
  2. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
  3. Fusix

    Fusix Активный пользователь

    С нами с:
    22 фев 2010
    Сообщения:
    67
    Симпатии:
    0
    Да я и с тем не разобрался... Можно пример кода?
    Я делал цикл с условиями - работает но очень плохо обходит преграды... нужно что-то более эффективное... а как?
     
  4. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    если делать, то по порядку.
    1. как хранится матрица поля? или как бы ты хотел, чтобы она хранилась
    2. нужно словесное описание алгоритма дейкстры
    3. есть ли под руками реализация алгоритма дейкстры на другом языке программирования?
    а дальше будем думать, как из этого что-то слепить
     
  5. Fusix

    Fusix Активный пользователь

    С нами с:
    22 фев 2010
    Сообщения:
    67
    Симпатии:
    0
    Пусть будет A*.
    1. Я думаю не хранить, т.к. я могу обратиться к базе и проверить проходимость.
    2. http://www.policyalmanac.org/games/aSta ... al_rus.htm
    3. Есть А*, на C++ (Visual Studio)
    Исходник есть в этой статье
     
  6. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    т.е. хранить всё равно надо. и портировать из базы в php всё равно придётся (хотя бы прямоугольную область, которая содержит старт и финиш).
    зы: алгоритм посмотрел поверхностно. реализацию на сях посмотрел подробно. буду смотреть, что можно сделать
     
  7. Fusix

    Fusix Активный пользователь

    С нами с:
    22 фев 2010
    Сообщения:
    67
    Симпатии:
    0
    Спасибо
     
  8. Fusix

    Fusix Активный пользователь

    С нами с:
    22 фев 2010
    Сообщения:
    67
    Симпатии:
    0
    titch,
    Вот как я перевел на PHP код с С++;
    PHP:
    1. <?
    2. function FindPath($pathfinderID, $startX, $startY, $targetX, $targetY, $stepByStep = false)
    3. {
    4.     global $pathBank;
    5.  
    6.     $onOpenList=0;
    7.     $parentXval=0;
    8.     $parentYval=0;
    9.     $a=0;
    10.     $b=0;
    11.     $m=0;
    12.     $u=0;
    13.     $v=0;
    14.     $temp=0;
    15.     $corner=0;
    16.     $numberOfOpenListItems=0;
    17.     $addedGCost=0;
    18.     $tempGcost=0;
    19.     $path=0;
    20.     $x=0;
    21.     $y=0;
    22.     $newOpenListItemID=0;
    23.  
    24.     $mapWidth = 5;
    25.     $mapHeight = 5;
    26.  
    27.     //tempx, pathX, pathY, cellPosition != 0 ò.å. = null
    28.  
    29. //2.Quick Path Checks: Under the some circumstances no path needs to
    30. //  be generated ...
    31.  
    32. //  If starting location and target are in the same location...
    33.     if ($startX == $targetX && $startY == $targetY && $pathLocation[$pathfinderID] > 0)
    34.         return 'found';
    35.     if ($startX == $targetX && $startY == $targetY && $pathLocation[$pathfinderID] == 0)
    36.         return 'nonexistent';
    37.  
    38.  
    39.         echo $pathfinderID;
    40. //3.Reset some variables that need to be cleared
    41.     for ($x = 0; $x < $mapWidth; $x++)
    42.     {
    43.         for ($y = 0; $y < $mapHeight; $y++)
    44.             $whichList[$x][$y] = 0;
    45.     }
    46.  
    47.     $onClosedList = 2; //changing the values of onOpenList and onClosed list is faster than redimming whichList() array
    48.     $onOpenList = 1;
    49.     $pathLength[$pathfinderID] = 0;
    50.     $pathLocation [$pathfinderID] = 0;
    51.     $Gcost[$startX][$startY] = 0; //reset starting square's G value to 0
    52.  
    53. //4.Add the starting location to the open list of squares to be checked.
    54.     $numberOfOpenListItems = 1;
    55.     $openList[1] = 1;//assign it as the top (and currently only) item in the open list, which is maintained as a binary heap (explained below)
    56.     $openX[1] = $startX;
    57.     $openY[1] = $startY;
    58.  
    59. //5.Do the following until a path is found or deemed nonexistent.
    60.     do
    61.     {
    62.  
    63.     //6.If the open list is not empty, take the first cell off of the list.
    64.     //  This is the lowest F cost cell on the open list.
    65.         if ($numberOfOpenListItems != 0)
    66.         {
    67.  
    68.         //7. Pop the first item off the open list.
    69.             $parentXval = $openX[$openList[1]];
    70.             $parentYval = $openY[$openList[1]]; //record cell coordinates of the item
    71.             $whichList[$parentXval][$parentYval] = $onClosedList;//add the item to the closed list
    72.  
    73.         //  Open List = Binary Heap: Delete this item from the open list, which
    74.         //  is maintained as a binary heap. For more information on binary heaps, see:
    75.         //  [url=http://www.policyalmanac.org/games/binaryHeaps.htm]http://www.policyalmanac.org/games/binaryHeaps.htm[/url]
    76.             $numberOfOpenListItems = $numberOfOpenListItems - 1;//reduce number of open list items by 1
    77.  
    78.         //  Delete the top item in binary heap and reorder the heap, with the lowest F cost item rising to the top.
    79.             $openList[1] = $openList[$numberOfOpenListItems+1];//move the last item in the heap up to slot #1
    80.             $v = 1;
    81.  
    82.     //  Repeat the following until the new item in slot #1 sinks to its proper spot in the heap.
    83.             do
    84.             {
    85.                 $u = $v;
    86.                 if (2*$u+1 <= $numberOfOpenListItems) //if both children exist
    87.                 {
    88.                     //Check if the F cost of the parent is greater than each child.
    89.                     //Select the lowest of the two children.
    90.                     if ($Fcost[$openList[$u]] >= $Fcost[$openList[2*$u]])
    91.                         $v = 2*$u;
    92.                     if ($Fcost[$openList[$v]] >= $Fcost[$openList[2*$u+1]])
    93.                         $v = 2*$u+1;
    94.                 }
    95.                 else
    96.                 {
    97.                     if (2*$u <= $numberOfOpenListItems) //if only child #1 exists
    98.                     {
    99.                     //Check if the F cost of the parent is greater than child #1
    100.                         if ($Fcost[$openList[$u]] >= $Fcost[$openList[2*$u]])
    101.                             $v = 2*$u;
    102.                     }
    103.                 }
    104.  
    105.                 if ($u != $v) //if parent's F is > one of its children, swap them
    106.                 {
    107.                     $temp = $openList[$u];
    108.                     $openList[$u] = $openList[$v];
    109.                     $openList[$v] = $temp;
    110.                 }
    111.                 else
    112.                 break; //otherwise, exit loop
    113.  
    114.         }
    115.         while (1);//reorder the binary heap // while (!KeyDown(27));
    116.  
    117.  
    118.     //7.Check the adjacent squares. (Its "children" -- these path children
    119.     //  are similar, conceptually, to the binary heap children mentioned
    120.     //  above, but don't confuse them. They are different. Path children
    121.     //  are portrayed in Demo 1 with grey pointers pointing toward
    122.     //  their parents.) Add these adjacent child squares to the open list
    123.     //  for later consideration if appropriate (see various if statements
    124.     //  below).
    125.         for ($b = $parentYval-1; $b <= $parentYval+1; $b++){
    126.         for ($a = $parentXval-1; $a <= $parentXval+1; $a++){
    127.  
    128.     //  If not off the map (do this first to avoid array out-of-bounds errors)
    129.         if ($a != -1 && $b != -1 && $a != $mapWidth && $b != $mapHeight){
    130.  
    131.     //  If not already on the closed list (items on the closed list have
    132.     //  already been considered and can now be ignored).
    133.         if ($whichList[$a][$b] != $onClosedList) {
    134.  
    135.     //  If not a wall/obstacle square.
    136.         if ($walkability [$a][$b] != $unwalkable) {
    137.  
    138.     //  Don't cut across corners
    139.         $corner = 'walkable';
    140.         if ($a == $parentXval-1)
    141.         {
    142.             if ($b == $parentYval-1)
    143.             {
    144.                 if ($walkability[$parentXval-1][$parentYval] == 'unwalkable' || $walkability[$parentXval][$parentYval-1] == 'unwalkable') $corner = 'unwalkable';
    145.             }
    146.             elseif ($b == $parentYval+1)
    147.             {
    148.                 if ($walkability[$parentXval][$parentYval+1] == 'unwalkable' || $walkability[$parentXval-1][$parentYval] == 'unwalkable') $corner = 'unwalkable';
    149.             }
    150.         }
    151.         elseif ($a == $parentXval+1)
    152.         {
    153.             if ($b == $parentYval-1)
    154.             {
    155.                 if ($walkability[$parentXval][$parentYval-1] == 'unwalkable' || $walkability[$parentXval+1][$parentYval] == 'unwalkable') $corner = 'unwalkable';
    156.             }
    157.             else if ($b == $parentYval+1)
    158.             {
    159.                 if ($walkability[$parentXval+1][$parentYval] == 'unwalkable' || $walkability[$parentXval][$parentYval+1] == 'unwalkable') $corner = 'unwalkable';
    160.             }
    161.         }
    162.  
    163.         if ($corner == 'walkable') {
    164.  
    165.     //  If not already on the open list, add it to the open list.
    166.         if ($whichList[$a][$b] != $onOpenList)
    167.         {
    168.  
    169.             //Create a new open list item in the binary heap.
    170.             $newOpenListItemID = $newOpenListItemID+1; //each new item has a unique ID #
    171.             $m = $numberOfOpenListItems+1;
    172.             $openList[$m] = $newOpenListItemID;//place the new open list item (actually, its ID#) at the bottom of the heap
    173.             $openX[$newOpenListItemID] = $a;
    174.             $openY[$newOpenListItemID] = $b;//record the x and y coordinates of the new item
    175.  
    176.             //Figure out its G cost
    177.             if (abs($a-  $parentXval) == 1 && abs($b - $parentYval) == 1) $addedGCost = 14;//cost of going to diagonal squares
    178.             else $addedGCost = 10;//cost of going to non-diagonal squares
    179.             $Gcost[$a][$b] = $Gcost[$parentXval][$parentYval] + $addedGCost;
    180.  
    181.             //Figure out its H and F costs and parent
    182.             $Hcost[$openList[$m]] = 10*(abs($a - $targetX) + abs($b - $targetY));
    183.             $Fcost[$openList[$m]] = $Gcost[$a][$b] + $Hcost[$openList[$m]];
    184.             $parentX[$a][$b] = $parentXval ; $parentY[$a][$b] = $parentYval;
    185.  
    186.             while ($m != 1) //While item hasn't bubbled to the top (m=1)
    187.             {
    188.                 //Check if child's F cost is < parent's F cost. If so, swap them.
    189.                 if ($Fcost[$openList[$m]] <= $Fcost[$openList[$m/2]])
    190.                 {
    191.                     $temp = $openList[$m/2];
    192.                     $openList[$m/2] = $openList[$m];
    193.                     $openList[$m] = $temp;
    194.                     $m = $m/2;
    195.                 }
    196.                 else
    197.                     break;
    198.             }
    199.             $numberOfOpenListItems = $numberOfOpenListItems+1;//add one to the number of items in the heap
    200.  
    201.             //Change whichList to show that the new item is on the open list.
    202.             $whichList[$a][$b] = $onOpenList;
    203.         }
    204.  
    205.     //8.If adjacent cell is already on the open list, check to see if this
    206.     //  path to that cell from the starting location is a better one.
    207.     //  If so, change the parent of the cell and its G and F costs.
    208.         else //If whichList(a,b) = onOpenList
    209.         {
    210.  
    211.             //Figure out the G cost of this possible new path
    212.             if (abs($a - $parentXval) == 1 && abs($b - $parentYval) == 1) $addedGCost = 14;//cost of going to diagonal tiles
    213.             else $addedGCost = 10;//cost of going to non-diagonal tiles
    214.             $tempGcost = $Gcost[$parentXval][$parentYval] + $addedGCost;
    215.  
    216.             //If this path is shorter (G cost is lower) then change
    217.             //the parent cell, G cost and F cost.
    218.             if ($tempGcost < $Gcost[$a][$b]) //if G cost is less,
    219.             {
    220.                 $parentX[$a][$b] = $parentXval; //change the square's parent
    221.                 $parentY[$a][$b] = $parentYval;
    222.                 $Gcost[$a][$b] = $tempGcost;//change the G cost
    223.  
    224.                 //Because changing the G cost also changes the F cost, if
    225.                 //the item is on the open list we need to change the item's
    226.                 //recorded F cost and its position on the open list to make
    227.                 //sure that we maintain a properly ordered open list.
    228.                 for($x = 1; $x <= $numberOfOpenListItems; $x++) //look for the item in the heap
    229.                 {
    230.                 if ($openX[$openList[$x]] == $a && $openY[$openList[$x]] == $b) //item found
    231.                 {
    232.                     $Fcost[$openList[$x]] = $Gcost[$a][$b] + $Hcost[$openList[$x]];//change the F cost
    233.  
    234.                     //See if changing the F score bubbles the item up from it's current location in the heap
    235.                     $m = $x;
    236.                     while($m != 1) //While item hasn't bubbled to the top (m=1)
    237.                     {
    238.                         //Check if child is < parent. If so, swap them.
    239.                         if ($Fcost[$openList[$m]] < $Fcost[$openList[$m/2]])
    240.                         {
    241.                             $temp = $openList[$m/2];
    242.                             $openList[$m/2] = $openList[$m];
    243.                             $openList[$m] = $temp;
    244.                             $m = $m/2;
    245.                         }
    246.                         else break;
    247.                     }
    248.                     break; //exit for x = loop
    249.                 } //If openX(openList(x)) = a
    250.                 } //For x = 1 To numberOfOpenListItems
    251.             }//If tempGcost < Gcost(a,b)
    252.  
    253.         }//else If whichList(a,b) = onOpenList
    254.         }//If not cutting a corner
    255.         }//If not a wall/obstacle square.
    256.         }//If not already on the closed list
    257.         }//If not off the map
    258.         }//for (a = parentXval-1; a <= parentXval+1; a++){
    259.         }//for (b = parentYval-1; b <= parentYval+1; b++){
    260.  
    261.         }//if (numberOfOpenListItems != 0)
    262.  
    263.     //9.If open list is empty then there is no path.
    264.         else
    265.         {
    266.             $path = 'nonexistent'; break;
    267.         }
    268.  
    269.         //If target is added to open list then path has been found.
    270.         if ($whichList[$targetX][$targetY] == $onOpenList)
    271.         {
    272.             $path = 'found';
    273.             break;
    274.         }
    275.  
    276.         }
    277.     while (1);//Do until path is found or deemed nonexistent  // while (!(KeyDown(27)));
    278.  
    279. //10.Save the path if it exists.
    280.     if ($path == 'found')
    281.     {
    282.  
    283. //a.Working backwards from the target to the starting location by checking
    284. //  each cell's parent, figure out the length of the path.
    285.     $pathX = $targetX;
    286.     $pathY = $targetY;
    287.     do
    288.     {
    289.         //Look up the parent of the current cell.
    290.         $tempx = $parentX[$pathX][$pathY];
    291.         $pathY = $parentY[$pathX][$pathY];
    292.         $pathX = $tempx;
    293.  
    294.         //Figure out the path length
    295.         $pathLength[$pathfinderID] = $pathLength[$pathfinderID] + 1;
    296.     }
    297.     while ($pathX != $startX || $pathY != $startY);
    298.  
    299. //b.Resize the data bank to the right size in bytes
    300.  
    301.     /*
    302.     pathBank[$pathfinderID] = (int*) realloc (pathBank[$pathfinderID],
    303.         pathLength[$pathfinderID]*8);
    304.     */
    305.  
    306. //c. Now copy the path information over to the databank. Since we are
    307. //  working backwards from the target to the start location, we copy
    308. //  the information to the data bank in reverse order. The result is
    309. //  a properly ordered set of path data, from the first step to the
    310. //  last.
    311.     $pathX = $targetX;
    312.     $pathY = $targetY;
    313.     $cellPosition = $pathLength[$pathfinderID]*2;//start at the end
    314.     do
    315.     {
    316.         $cellPosition = $cellPosition - 2;//work backwards 2 integers
    317.         $pathBank[$pathfinderID][$cellPosition] = $pathX;
    318.         $pathBank[$pathfinderID][$cellPosition+1] = $pathY;
    319.  
    320.     //d.Look up the parent of the current cell.
    321.         $tempx = $parentX[$pathX][$pathY];
    322.         $pathY = $parentY[$pathX][$pathY];
    323.         $pathX = $tempx;
    324.  
    325.     //e.If we have reached the starting square, exit the loop.
    326.     }
    327.     while ($pathX != $startX || $pathY != $startY);
    328.  
    329.     }
    330.     return $path;
    331. }
    332.  
    333. $test = FindPath(1, 1, 0, 4, 5);
    334. echo $test;
    335.  
    336. ?>
     
  9. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    отредактируй так, чтобы был тег не code, а php, тогда синтаксис подсветится
     
  10. Fusix

    Fusix Активный пользователь

    С нами с:
    22 фев 2010
    Сообщения:
    67
    Симпатии:
    0
    Отредактировал.

    Теперь нужно получить путь...

    На СИ она так:

    Код (Text):
    1. void ReadPath(int pathfinderID) //If a path exists, read the path data
    2. //  from the pathbank.
    3. {
    4.     pathLocation[pathfinderID] = 1; //set pathLocation to 1st step
    5.     while (pathLocation[pathfinderID] < pathLength[pathfinderID])
    6.     {
    7.         int a = pathBank[pathfinderID] [pathLocation[pathfinderID]*2-2];
    8.         int b = pathBank[pathfinderID] [pathLocation[pathfinderID]*2-1];
    9.         pathLocation[pathfinderID] = pathLocation[pathfinderID] + 1;
    10.         whichList[a][b] = 3;//draw dotted path
    11.     }
    12. }
    Только не пойму, как она получает pathBank, если он не глобальный
     
  11. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    php это не c++
    верни путь сразу из функции. у тебя же не типизированный язык. ты можешь вернуть результат сразу хоть false, хоть массив
     
  12. Fusix

    Fusix Активный пользователь

    С нами с:
    22 фев 2010
    Сообщения:
    67
    Симпатии:
    0
    Щас при запуске выдает nonexistent
     
  13. titch

    titch Активный пользователь

    С нами с:
    18 дек 2010
    Сообщения:
    847
    Симпатии:
    0
    смотри, что делает функция. она восстанавливает путь. в контексте php это не выгодно. лучше сразу вернуть путь вида array(array(x1,y1),array(x2,y2),array(x3,y3),...,array(xN,yN))
     
  14. Fusix

    Fusix Активный пользователь

    С нами с:
    22 фев 2010
    Сообщения:
    67
    Симпатии:
    0
    Ну мне сейчас нужно чтобы он вовще хоть путь нашел
     
  15. Gromo

    Gromo Активный пользователь

    С нами с:
    24 май 2010
    Сообщения:
    2.786
    Симпатии:
    2
    Адрес:
    Ташкент
    ты бы хоть про рекурсии почитал - походу именно это тебе и нужно при поиске
     
  16. Fusix

    Fusix Активный пользователь

    С нами с:
    22 фев 2010
    Сообщения:
    67
    Симпатии:
    0
    Уже пробывал. Не то... нужно именно это... т.е. A*