Location: PHPKode > scripts > Path finder > class.pathfinder.php
<?php

//EXAMPLE OF USE
/*
$map=getMap(); //You can use whatever function you need for this purpose
require('class.pathfinder.php');
$path=new PathFinder(); //Create new pathfinder object
$path->setOrigin($valuestring['ox'],$valuestring['oy']); //Set the start location
$path->setDestination($valuestring['dx'],$valuestring['dy']); //Set the destination
$path->setMap($map); //Assign the map to use for calculations
$result=$path->returnPath(); //Returns an array of all the walked steps
*/

//Example of the map-array generating function
//This pathfinder example assumes that a square map is used. To use rectangular map you need to tweak the pathfinder a bit
//Here we assign one tile in four as 'unpassable'
//Map is an associative array which has a key of the tile's coordinates as the base. Only required array value is 'weight' but
//you can store additional information in the array for, say, public purposes, such as tile graphic data.
/*
function getMap(){
	$map=array();
	for($x=1;$x<=20;$x++){
		for($y=1;$y<=20;$y++){
			$rand=rand(1,4);
			if($rand==1){
				$map[$x.'x'.$y]=array('weight'=>'10.0');
			} else {
				$map[$x.'x'.$y]=array('weight'=>'1.0');
			}
		}
	}
	return $map;
}
*/
 
class PathFinder{

	//stores the map data array
	private $map=array();
	
	//stores origin and destination locations
	private $origX=0;
	private $origY=0;
	private $destX=0;
	private $destY=0;
	
	//this is used to store the returned path
	private $path=array();
	
	//this stores locations already looked at, this is temporary and is cleared if pathfinding gets stuck
	private $walked=array();
	
	//internal storage of squares count and map width
	private $mapSquares=0;
	private $mapWidth=0;
	
	//flag used to determine if path has been found
	private $found=0;
	
	//this is a failsafe if the calculation takes too long. It is limited to 100 solves by default
	//Should failsafe be unsuccessful then only partial path will be returned
	private $failSafe=0;
	public $failSafeLimit=100;
	
	//function to set the map, it needs to be an array
	public function setMap($map){
		$this->map=$map;
		$this->mapSquares=count($this->map);
		$this->mapWidth=sqrt($this->mapSquares);
	}
	
	//set origin location
	public function setOrigin($origX,$origY){
		$this->origX=$origX;
		$this->origY=$origY;
		
	}
	
	//set destination location
	public function setDestination($destX,$destY){
		$this->destX=$destX;
		$this->destY=$destY;		
	}
	
	//calculates and returns path
	public function returnPath(){
		if($this->origX!=0 && $this->origY!=0 && $this->destX!=0 && $this->destY!=0 && !empty($this->map)){
			//this function runs recursively within itself until path is found or failSafe is reached
			$this->findPath($this->origX,$this->origY);
			return $this->path;
		} else {
			echo 'Insufficient pathfinder data, please define origin and destination points and a map';
			die();
		}
	}

	//main function for path calculation
	private function findPath($curX,$curY){
		//failsafe check here
		$this->failSafe++;
		if($this->failSafe>=$this->failSafeLimit){
			$this->found=1;
		}
		if($this->found!=1){
			//resetting the default values for this current square
			$lowestEstimatedCost=0;
			$shortestX=0;
			$shortestY=0;
			//looping through all the nearby squares
			for($x=-1;$x<=1;$x++){
				if($this->found==0){
					for($y=-1;$y<=1;$y++){
						if($this->found==0){
							$checkX=$x+$curX;
							$checkY=$y+$curY;
							//making sure the checked square is within map boundries
							if($checkX>=1 && $checkY>=1 && $checkX<=$this->mapWidth && $checkY<=$this->mapWidth){
								if($checkX==$curX && $checkY==$curY){
									//this is the current square we are on, so ignore for now
								} else if($checkX==$this->destX && $checkY==$this->destY){
									//we have found the destination!
									$this->found=1;
									$this->path[]=$this->destX.'x'.$this->destY;
								} else {
									//if this square has not been 'walked' yet, then consider it as one of the next step
									if(!in_array($checkX.'x'.$checkY,$this->walked)){
										$weight=$this->map[$checkX.'x'.$checkY]['weight'];
										$weight=(($x==-1 && $y==-1) || ($x==-1 && $y==1) || ($x==1 && $y==-1) || ($x==1 && $y==1))?$weight*1.4:$weight;
										$estimatedCost=$this->estimatedCost($checkX,$checkY,$weight);
										//this makes sure the lowest cost square is used for next step
										if($estimatedCost<$lowestEstimatedCost || $lowestEstimatedCost==0){
											$lowestEstimatedCost=$estimatedCost;
											$shortestX=$checkX;
											$shortestY=$checkY;
										}
									}
								}
							}
						}
					}
				}
			}
		}
		if($this->found!=1){
			if($shortestX==0 || $shortestY==0){
				//if the shortest locations are 0 then calculation must have gotten stuck and we clear the walked path
				//and try to find a new path from where we got stuck, essentially a re-solve
				$this->walked=array();
				$this->walked[]=$curX.'x'.$curY;
				$this->findPath($curX,$curY,$this->destX,$this->destY);
			} else {
				//we store the next step into walked array (so it is not checked again during this path)
				$this->walked[]=$shortestX.'x'.$shortestY;
				//storing the location into path array
				$this->path[]=$shortestX.'x'.$shortestY;
				$this->findPath($shortestX,$shortestY,$this->destX,$this->destY);
			}
		}
	}
	
	//this is used to calculate estimated distance between two points
	private function estimatedCost($curX,$curY,$weight){
		$dx=$this->destX-$curX;
		$dy=$this->destY-$curY;
		$value=sqrt(($dx*$dx)+($dy*$dy));
		return $value*$weight;
	}

}

?>
Return current item: Path finder