Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> [class] Znajdź najkrótszą drogę, algorytm A*
bim2
post
Post #1





Grupa: Zarejestrowani
Postów: 1 873
Pomógł: 152
Dołączył: 9.04.2006
Skąd: Berlin

Ostrzeżenie: (0%)
-----


  1. <?php
  2. /**
  3.  * License
  4.  *
  5.  * This library is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU Lesser General Public
  7.  * License as published by the Free Software Foundation; either
  8.  * version 2.1 of the License, or (at your option) any later version.
  9.  *
  10.  * This library is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  * Lesser General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Lesser General Public
  16.  * License along with this library; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  **/
  19. /**
  20.  * @author Hernas Service <kontakt@hernass.pl>
  21.  * @copyright 2008 HernasS.pl.
  22.  * @version 2.0
  23.  */
  24. class Way {
  25.    /* Startowy punkt na osi X */
  26.    private $start_x;
  27.    
  28.    /* Startowy punkt na osi Y */
  29.    private $start_y;
  30.    
  31.    /* Mapa podana przez użytkownika */
  32.    private $aMap;
  33.    
  34.    /* Mapa wygenerowana na potrzeby skryptu */
  35.    private $aMap_;
  36.    
  37.    /* Punkt przeznaczenia na osi X */
  38.    private $destination_x;
  39.    
  40.    /* Punkt przeznaczenia na osi Y */
  41.    private $destination_y;
  42.    
  43.    /* Tablica przeszukiwania dróg */
  44.    private $aWay = array();
  45.    
  46.    /* Tablica wyjściowa z wybraną drogą */
  47.    private $aWays = array();
  48.    
  49.    /* Numer kolejnego kroku przeszukiwania */
  50.    private $fillNumber = 0;
  51.    
  52.    /* Konstruktor klasy
  53.      * @param integer $x - Startowy punkt na osi X
  54.      * @param integer $y - Startowy punkt na osi Y
  55.      * @param array $aMap - Mapa podana przez użytkownika
  56.     */
  57.    public function __construct($x, $y, $aMap)
  58.    {
  59.        $this->aMap = $aMap;
  60.        
  61.        $this->aWays[] = array($x, $y);
  62.        $this->start_x = $x;
  63.        $this->start_y = $y;
  64.        
  65.        $this->regenerateMap();
  66.        
  67.    }
  68.    /* Szuka drogi
  69.      * @param integer $x - Punkt przeznaczenia na osi X
  70.      * @param integer $y - Punkt przeznaczenia na osi Y
  71.     */
  72.    public function findWay($x, $y)
  73.    {    
  74.        $this->destination_x = $x;
  75.        $this->destination_y = $y;    
  76.        
  77.        $this->startFinding();
  78.        $this->mirrorFinding($x, $y);
  79.        return $this->aWay;
  80.    }
  81.    /* Znaczy kolejne kroki na mapie */
  82.    private function startFinding()
  83.    {
  84.        foreach($this->aWays AS $v)
  85.        {
  86.            list($x, $y) = $v;
  87.            $this->aMap_[$y][$x] = $this->fillNumber;
  88.            $new_x[] = $x+1;
  89.            $new_y[] = $y;
  90.            
  91.            $new_x[] = $x+1;
  92.            $new_y[] = $y+1;
  93.            
  94.            $new_x[] = $x+1;
  95.            $new_y[] = $y-1;
  96.            
  97.            $new_x[] = $x-1;
  98.            $new_y[] = $y;
  99.            
  100.            $new_x[] = $x-1;
  101.            $new_y[] = $y+1;
  102.            
  103.            $new_x[] = $x-1;
  104.            $new_y[] = $y-1;
  105.                    
  106.            $new_x[] = $x;
  107.            $new_y[] = $y+1;
  108.            
  109.            $new_x[] = $x;
  110.            $new_y[] = $y-1;
  111.            $paths = array();
  112.            foreach($new_x AS $i => $v)
  113.            {
  114.                if(isset($this->aMap_[$new_y[$i]][$v]) AND $this->aMap_[$new_y[$i]][$v]==-1)
  115.                {
  116.                    if($v==$this->destination_y AND $new_y[$i]==$this->destination_x)
  117.                    {
  118.                        $paths[] = array($v, $new_y[$i]);
  119.                        break;
  120.                    }
  121.                    $paths[] = array($v, $new_y[$i]);
  122.                }
  123.            }
  124.        }
  125.        
  126.        $this->fillNumber++;
  127.    if(isset($paths))
  128.    {
  129.        $this->aWays = $paths;
  130.        $this->startFinding();
  131.    }
  132.        
  133.    }
  134.    /* Szuka drogi od końca po kolejnych krokach
  135.      * @param integer $x - Punkt kolejnego kroku na osi X
  136.      * @param integer $y - Punkt kolejnego kroku na osi Y
  137.     */
  138.    private function mirrorFinding($x, $y)
  139.    {
  140.        $new_x[] = $x+1;
  141.        $new_y[] = $y;
  142.        
  143.        $new_x[] = $x+1;
  144.        $new_y[] = $y+1;
  145.        
  146.        $new_x[] = $x+1;
  147.        $new_y[] = $y-1;
  148.        
  149.        $new_x[] = $x-1;
  150.        $new_y[] = $y;
  151.        
  152.        $new_x[] = $x-1;
  153.        $new_y[] = $y+1;
  154.        
  155.        $new_x[] = $x-1;
  156.        $new_y[] = $y-1;
  157.                
  158.        $new_x[] = $x;
  159.        $new_y[] = $y+1;
  160.        
  161.        $new_x[] = $x;
  162.        $new_y[] = $y-1;
  163.        foreach($new_x AS $i => $v)
  164.        {
  165.            if(isset($this->aMap_[$new_y[$i]][$v]) AND $this->aMap_[$new_y[$i]][$v]==($this->aMap_[$y][$x]-1))
  166.            {
  167.                $this->aWay[] = array($new_y[$i], $v);
  168.                $this->mirrorFinding($v, $new_y[$i]);
  169.                break;
  170.            }
  171.        }
  172.    }
  173.    /* Generuję mapę na potrzeby skryptu    */
  174.    private function regenerateMap()
  175.    {
  176.        foreach($this->aMap AS $y => $v_)
  177.        {
  178.            foreach($v_ AS $x => $v)
  179.            {
  180.                if($this->start_x == $x AND $this->start_y == $y)
  181.                {
  182.                    $this->aMap_[$y][$x] = 0;
  183.                } elseif($v==1) {
  184.                    $this->aMap_[$y][$x] = -2;
  185.                } elseif($v==0) {
  186.                    $this->aMap_[$y][$x] = -1;
  187.                } else {
  188.                    $this->aMap_[$y][$x] = -2;
  189.                }
  190.            }            
  191.        }
  192.    }
  193.    
  194.    
  195. }
  196. ?>


Oraz przykład użycia (IMG:http://forum.php.pl/style_emoticons/default/smile.gif)

  1. <?php
  2. $aMap = array(
  3. array(1,0,0,0,1,0,0,0),
  4. array(1,0,1,1,1,1,0,0),
  5. array(1,0,1,0,0,1,0,1),
  6. array(0,0,0,0,0,0,0,1),
  7. array(0,0,0,1,1,1,1,1),
  8. array(0,1,0,0,0,0,0,0),
  9. array(1,1,0,0,0,1,0,0),
  10. array(1,1,0,1,1,1,0,0),
  11. ); // 0 -można chodzić
  12. $oWay = new Way(2, 7, $aMap); //X i Y liczone od 0 (jak klucz arraya)
  13. print_r($oWay->findWay(7, 7));
  14. ?>


Oraz przykład graficzny:
http://hernass.pl/searchWay/

PS. Dziękuję wszystkim, którzy pomogli przy skrypcie (IMG:http://forum.php.pl/style_emoticons/default/snitch.gif)
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi
-=Peter=-
post
Post #2





Grupa: Zarejestrowani
Postów: 304
Pomógł: 51
Dołączył: 4.02.2005
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Zainspirowany tym tematem, nie patrząc na kod który bim2 podał, stworzyłem też swoją klasę szukającą najkrótszą drogę (też żadnego książkowego algortmu nie znałem na ten problem). Oto kod, jeśli ktoś by chciał go wykorzystać to brać śmiało (IMG:http://forum.php.pl/style_emoticons/default/tongue.gif) Nie sprawdzałem go dokładnie, ale faktycznie zwraca najkrótszą drogę.

  1. <?php
  2.  
  3. class Path{
  4.    /**
  5.      * Map
  6.      *
  7.      * example:
  8.      *  $map = array(
  9.      *         array(0, 1, 0, 0, 0, 0),
  10.      *         array(0, 1, 0, 1, 0, 1),
  11.      *         array(1, 1, 1, 1, 0, 1),
  12.      *         array(1, 1, 1, 1, 1, 1),
  13.      *         array(1, 0, 1, 0, 1, 1),
  14.      *         array(0, 1, 1, 0, 0, 0)
  15.      * );
  16.      *
  17.      *
  18.      * @var array
  19.      */
  20.    private $map = array();
  21.    
  22.    /**
  23.      * Size of map
  24.      *
  25.      * @var array
  26.      */
  27.    private $mapSize = array();
  28.    
  29.    /**
  30.      * Coordinates of start field ($sx, $sy) and
  31.      * destination field ($dx, $dy)
  32.      *
  33.      * @var integer
  34.      */
  35.    private $sx, $sy, $dx, $dy;
  36.    
  37.    /**
  38.      * Generated shortest way
  39.      *
  40.      * @var array
  41.      */
  42.    private $path = array();
  43.    
  44.    /**
  45.      * Number of footsteps
  46.      *
  47.      * @var int
  48.      */
  49.    private $minFootsteps = null;
  50.    
  51.    /**
  52.      * Constructor
  53.      *
  54.      * @param array Map
  55.      */
  56.    public function __construct(array $map){
  57.        $this->setMap($map);
  58.    }
  59.    
  60.    /**
  61.      * Setter
  62.      *
  63.      * @param array Map
  64.      * @return boolean
  65.      */
  66.    private function setMap(array $map){
  67.        
  68.        //validation
  69.        if(count($map) > 0){
  70.            $size = null;
  71.            
  72.            foreach($map as $line){
  73.                $size = ($size === null ? count($line) : $size);
  74.                
  75.                if(count($line) != $size){
  76.                    throw new Exception('Wymiary kazdego wiersza maja byc takie same!');
  77.                }
  78.                
  79.                foreach($line as $element){
  80.                    if($element != 0 && $element != 1){
  81.                        throw new Exception('Wartosci pojedynczego pola musza miec wartosc 0 lub 1');
  82.                    }
  83.                }
  84.            }
  85.            
  86.            $this->map = $map;
  87.            $this->mapSize = array($size, count($map));
  88.            return true;
  89.        }
  90.        throw new Exception('Podano pusta mape');
  91.    }
  92.    
  93.    /**
  94.      * Getter
  95.      *
  96.      * @return array
  97.      */
  98.    public function getMapSize(){
  99.        return $this->mapSize;
  100.    }
  101.    
  102.    /**
  103.      * Set start field
  104.      *
  105.      * @param integer $x
  106.      * @param integer $y
  107.      * @return boolean
  108.      */
  109.    public function setStart($x, $y){
  110.        if($this->validatePoint($x, $y)){
  111.            $this->sx = $x;
  112.            $this->sy = $y;
  113.            
  114.            return true;
  115.        }
  116.        
  117.        return false;
  118.    }
  119.    
  120.    /**
  121.      * Set destination field
  122.      *
  123.      * @param integer $x
  124.      * @param integer $y
  125.      * @return boolean
  126.      */
  127.    public function setDestination($x, $y){
  128.        if($this->validatePoint($x, $y)){
  129.            $this->dx = $x;
  130.            $this->dy = $y;
  131.            
  132.            return true;
  133.        }
  134.        
  135.        return false;
  136.    }
  137.    
  138.    /**
  139.      * Get generated the shortest way
  140.      *
  141.      * @return array
  142.      */
  143.    public function getPath(){
  144.        return $this->path;
  145.    }
  146.    
  147.    /**
  148.      * Start searching
  149.      */
  150.    public function search(){
  151.        $this->walk($this->sx, $this->sy);            
  152.    }
  153.    
  154.    public function isWay(){
  155.        return (count($this->path) > 0 ? true : false);
  156.    }
  157.    
  158.    /**
  159.      * Main method
  160.      *
  161.      * @param integer x coordinate of current field
  162.      * @param integer y coordinate of current field
  163.      * @param integer number of footsteps
  164.      * @param array path
  165.      * @param array visited fields
  166.      * @return boolean
  167.      */
  168.    private function walk($x, $y, $footsteps = 0, $path = array(), $visited = array()){
  169.        $path[] = array($x, $y);
  170.        
  171.        if($x == $this->dx && $y == $this->dy){
  172.            if(($this->minFootsteps == null) || ($this->minFootsteps > $footsteps)){
  173.                $this->minFootsteps = $footsteps;
  174.                $this->path = $path;            
  175.            }
  176.            
  177.            return true;
  178.        }
  179.        
  180.        $visited[$x.'x'.$y] = 1;
  181.        $footsteps += 1;
  182.        
  183.        //Próbuj wejść na każde sąsiednie pole (nie na skos)
  184.        //i kontynuuj podróżowanie :]
  185.                
  186.        if(isset($this->map[$y][$x-1])){
  187.            if(($this->map[$y][$x-1] == 1) && !array_key_exists(($x-1).'x'.$y, $visited)){
  188.                $this->walk($x-1, $y, $footsteps, $path, $visited);
  189.            }
  190.        }
  191.        
  192.        if(isset($this->map[$y][$x+1])){
  193.            if(($this->map[$y][$x+1] == 1) && !array_key_exists(($x+1).'x'.$y, $visited)){
  194.                $this->walk($x+1, $y, $footsteps, $path, $visited);
  195.            }
  196.        }
  197.        
  198.        if(isset($this->map[$y-1][$x])){
  199.            if(($this->map[$y-1][$x] == 1) &&  !array_key_exists($x.'x'.($y-1), $visited)){
  200.                $this->walk($x, $y-1, $footsteps, $path, $visited);
  201.            }
  202.        }
  203.        
  204.        if(isset($this->map[$y+1][$x])){
  205.            if(($this->map[$y+1][$x] == 1)  && !array_key_exists($x.'x'.($y+1), $visited)){
  206.                $this->walk($x, $y+1, $footsteps, $path, $visited);
  207.            }
  208.        }
  209.        
  210.        return false;                
  211.    }
  212.    
  213.    private function validatePoint($x, $y){
  214.        if($x > ($this->mapSize[0] - 1) || $y > ($this->mapSize[1] - 1)){
  215.            return false;
  216.        }
  217.        
  218.        
  219.        if($this->map[$y][$x] == 0){
  220.            return false;
  221.        }
  222.        
  223.        return true;
  224.    }    
  225. }
  226.  
  227. //użycie
  228. $map = array(
  229.        array(0, 1, 0),
  230.        array(0,1,0),
  231.        array(0, 1, 1),
  232. );//1 - da się przejść, 0 - nie da się przejść
  233. $o = new Path($map);
  234. $o->setStart(1,1);
  235. $o->setDestination(2,2);
  236. $o->search();
  237.  
  238. $path = $o->getPath();
  239. ?>


Przeprowadziłem też testy porównawcze mojej klasy i klasy bim2. Aby był sens tego porównania (czyli porównanie samego algorytmu porównania) z klasy bim2 wywaliłem możliwość chodzenia na skos (w mojej klasie to nie jest uwzględnione), a z mojej wywaliłem walidację mapy (bo u bim2 nie ma tego, a jest cholernie zasobożerne). Wynik testów wskazał, że moja klasa jest 40 razy wydajniejsza. Pewnie to z tego względu, że (wnioskuję z powieszchownego rzucenia okiem) klasa bim2 ustala ilość koniecznych kroków, które trzeba wykonać dla każdego pola z podanej mapki, a algorytm mojej klasy polega na tym aby wygenerować wszystkie możliwe drogi z punktu do punktu i wybrać najkrótszą.

Może trochę chamsko (wg niektórych?) się podpiąłem pod ten temat, no ale równie dobrze mógłbym nic nie napisać i zostawić to dla siebie, a tak to przynajmniej są podane dwa trochę różne sposoby rozwiązania podobnego/tego samego problemu (IMG:http://forum.php.pl/style_emoticons/default/tongue.gif) (IMG:http://forum.php.pl/style_emoticons/default/tongue.gif) Może komuś się to kiedyś sprzyda.

Ten post edytował -=Peter=- 2.01.2009, 18:51:18
Go to the top of the page
+Quote Post

Posty w temacie
- bim2   [class] Znajdź najkrótszą drogę   16.11.2008, 22:28:55
- - sticker   oho widzę że ktoś sie tu bawi w pisanie AI w PHP   17.11.2008, 14:47:59
- - bim2   ~sticker Nie Po prostu potrzebuję tego do gry. :...   17.11.2008, 15:15:00
- - Babcia@Stefa   Nieźle, klasa bardzo "huda" i zapewne wy...   17.11.2008, 20:36:45
- - bim2   Jeśli znałeś algorytm to nie A jak go nie znałeś,...   18.11.2008, 07:44:39
- - Wicko   Hej, Bartku/Michale! Ciekawa rzecz, ale.. ni...   19.11.2008, 18:26:33
|- - phpion   Cytat(Wicko @ 19.11.2008, 20:26:33 ) ...   19.11.2008, 18:53:50
|- - djstrong   Cytat(Wicko @ 19.11.2008, 18:26:33 ) ...   1.01.2009, 12:55:32
- - AxZx   ale chyba nie powinna iść po skosie. to zależy pew...   19.11.2008, 19:01:19
|- - phpion   Cytat(AxZx @ 19.11.2008, 21:01:19 ) a...   19.11.2008, 19:05:58
- - bim2   Jest to najkrótsza droga. Można przydzielić rangę,...   19.11.2008, 20:48:16
- - wookieb   Sry za offtop ale... Dla startowego elementu 2,1 i...   3.12.2008, 16:43:53
- - ShadowD   Trochę pokombinowałem i znalazłem błąd a bynajmnie...   3.12.2008, 20:17:04
- - bim2   @wookieb Błąd w rysowaniu mapki, spójrz na kolejne...   8.12.2008, 17:15:47
- - bim2   Jest tak zrobione, ale musi numerować wszystkie kr...   1.01.2009, 17:54:19
|- - djstrong   Cytat(bim2 @ 1.01.2009, 17:54:19 ) Je...   10.01.2009, 15:21:14
- - -=Peter=-   Zainspirowany tym tematem, nie patrząc na kod któr...   2.01.2009, 18:50:13
- - bim2   Miło, pewnie sam wykorzystam twoją klasę Jeśli je...   2.01.2009, 20:22:07
- - -=Peter=-   A jednak chyba moja klasa jest z dupy Przy mapce ...   3.01.2009, 00:09:02
- - bim2   Hmm. I by się zgadzało Moja tylko ponumeruje sobi...   3.01.2009, 00:33:04
- - bartg   Imho sprawdzanie każdej trasy przy dobrym algorytm...   7.01.2009, 15:16:28
- - bim2   http://hernass.pl/searchWay/ Screen ze starej wers...   4.02.2009, 13:40:42
- - rzymek01   nie łatwiej przerobić z c++ na php Algorytm Dijkst...   4.02.2009, 16:21:53
|- - djstrong   Cytat(rzymek01 @ 4.02.2009, 16:21:53 ...   5.02.2009, 15:13:14
- - bim2   Dla mnie nie jeśli jest Ci prościej to proszę nap...   4.02.2009, 20:03:13
- - ucho   Przydało by się do tego ( jeśli istnieje to niech ...   4.02.2009, 20:31:26
- - rzymek01   @bim2, napisałem o algorytmie Dijkstry, bo jest pr...   5.02.2009, 18:49:56
- - bim2   No nie wiem. http://pl.wikipedia.org/wiki/Przeszuk...   5.02.2009, 19:27:15
- - ShadowD   Nadal chyba jest coś nie tak. Podałem: 88 21 I d...   5.02.2009, 21:36:00
- - bim2   Mówisz o js czy php?   5.02.2009, 23:49:12
- - ShadowD   Php js nie sprawdzałem... ;]   6.02.2009, 02:08:21
- - krriv   Witajcie, planuję napisać aplikacj...   20.06.2009, 09:22:58
- - bim2   Może połącz na początek te 64 pola tak żeby stanow...   21.06.2009, 19:28:18
- - krriv   Jeśli chodzi o połączenia to są nimi wspomniane wc...   22.06.2009, 11:47:42
- - rzymek01   zbudować graf, który będzie odzwierciedlał połaczn...   22.06.2009, 15:17:43


Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 12.10.2025 - 00:49