PHP A* Pathfinding can't work for complex maze HackerRank

254 views Asked by At

I'm trying to A* pathfinding with Pacman problem using PHP.

<?php
$_fp = fopen("php://stdin", "r");

// Node
class Node {

    var $x;
    var $y;
    var $fCost;
    var $hCost;
    var $gCost = 0;
    var $parent;

    function __construct($x=0,$y=0){
        $this->x = $x;
        $this->y = $y;
    }

    function getNeighbours($depth = 1) {
        $neighbours = array();

        $operand = array(
            array ('x' => -1, 'y' => 0),
            array ('x' => 0, 'y' => -1),
            array ('x' => 0, 'y' => 1),
            array ('x' => 1, 'y' => 0)
            );

        foreach ($operand as $key => $value) {
            $checkX = $this->x + $value['x'];
            $checkY = $this->y + $value['y'];

            if( $checkX >= 0 && $checkY >= 0 )
                array_push( $neighbours, $node = new Node( $checkX, $checkY ) );
        }

        return $neighbours;
    }   

    function fCost(){
        global $food;
        return $this->gCost() + $this->hCost($food);
    }

    function gCost(){
        global $pacman;
        return abs($pacman->x - $this->x) + abs($pacman->y - $this->y);
    }

    function hCost($destination){
        return abs($destination->x - $this->x) + abs($destination->y - $this->y);
    }
}

function retracePath($start,$end) {
    $current = $end;

    while ( $current != $start ) {
        echo $current->x . " " . $current->y."<br>";
        $current = $current->parent;
    }
}

$pacman = new Node();
$food = new Node();

// Input data
fscanf($_fp, "%d %d", $pacman->x, $pacman->y); // Pacman's position
fscanf($_fp, "%d %d", $food->x, $food->y); // Food's position
fscanf($_fp, "%d %d", $row_size, $col_size); // For map size row and col


// Input for map by row
for($row=0; $row<$row_size; $row++) {
    $map[$row] = trim(fgets(STDIN));
}

// Astar
$arr_open = array(); // set of nodes to be evaluated
$arr_close = array(); // set of nodes already evaluated

array_push($arr_open, $pacman); // add the start node to $arr_open

$key_arr_open = 0;

while( count($arr_open) > 0 ) { // loop

    $current = new Node();
    $current = $arr_open[$key_arr_open];
    unset($arr_open[$key_arr_open]);
    array_push($arr_close, $current);

    if($current->x == $food->x && $current->y == $food->y) {
        retracePath($pacman,$current);
        echo "sukses<br>"
        break;
    }

    $neighbours = $current->getNeighbours();

    foreach ($neighbours as $key => $data) {

        if($map[$data->x][$data->y] == "%" or in_array($data, $arr_close))
        {
            //echo "not traversable<br>";
            continue;
        }

        $new_cost_to_neighbour = $current->gCost() + $current->hCost($data);

        if( $new_cost_to_neighbour < $data->gCost() or !in_array( $data, $arr_open ) ) {
            $data->gCost = $new_cost_to_neighbour;
            $data->hCost = $data->hCost($food);
            $data->fCost = $new_cost_to_neighbour + $data->hCost($food);
            $data->parent = $current;

            if( !in_array($data, $arr_open)  )
            {
                array_push($arr_open, $data);
            }
        }
    }

    $key_arr_open ++;
}

?>

Input format : Position x and y pacman, position x and y food, count row and column of tile, then the grid. Grid format is "P" for pacman, "." for food, "-" for traversable path and "%" for wall.

The problem is when I give input with 6x6 tile like this :

%%%%%%
%-%%-%
%-%%-%
%-%%-%
%.--P%
%%%%%%

The code is work. But, if I give input with 37x37 tile with complex maze, the node in the array of open always looped. (From HackerRank)

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %-------%-%-%-----------%---%-----%-%
  %-%%%%%%%-%-%%%-%-%%%-%%%-%%%%%%%-%-%
  %-------%-------%-%-----%-----%-%---%
  %%%%%-%%%%%-%%%-%-%-%-%%%-%%%%%-%-%%%
  %---%-%-%-%---%-%-%-%---%-%---%-%---%
  %-%%%-%-%-%-%%%-%%%%%-%%%-%-%%%-%%%-%
  %-------%-----%---%---%-----%-%-%---%
  %%%-%%%%%%%%%-%%%%%%%-%%%-%%%-%-%-%-%
  %-------------%-------%-%---%-----%-%
  %-%-%%%%%-%-%%%-%-%-%%%-%-%%%-%%%-%-%
  %-%-%-----%-%-%-%-%-----%---%-%-%-%-%
  %-%-%-%%%%%%%-%-%%%%%%%%%-%%%-%-%%%-%
  %-%-%-%-----%---%-----%-----%---%---%
  %%%-%%%-%-%%%%%-%%%%%-%%%-%%%-%%%%%-%
  %-----%-%-%-----%-%-----%-%---%-%-%-%
  %-%-%-%-%-%%%-%%%-%%%-%%%-%-%-%-%-%-%
  %-%-%-%-%-----------------%-%-%-----%
  %%%-%%%%%%%-%-%-%%%%%-%%%-%-%%%-%%%%%
  %-------%-%-%-%-----%---%-----%-%---%
  %%%%%-%-%-%%%%%%%%%-%%%%%%%%%%%-%-%%%
  %---%-%-----------%-%-----%---%-%---%
  %-%%%-%%%%%-%%%%%%%%%-%%%%%-%-%-%%%-%
  %-%---%------%--------%-----%-------%
  %-%-%-%%%%%-%%%-%-%-%-%-%%%%%%%%%%%%%
  %-%-%---%-----%-%-%-%-------%---%-%-%
  %-%-%%%-%%%-%-%-%-%%%%%%%%%-%%%-%-%-%
  %-%---%-%---%-%-%---%-%---%-%-%-----%
  %-%%%-%%%-%%%%%-%%%-%-%-%%%%%-%-%%%%%
  %-------%---%-----%-%-----%---%-%---%
  %%%-%-%%%%%-%%%%%-%%%-%%%-%-%%%-%-%%%
  %-%-%-%-%-%-%-%-----%-%---%-%---%-%-%
  %-%-%%%-%-%-%-%-%%%%%%%%%-%-%-%-%-%-%
  %---%---%---%-----------------%-----%
  %-%-%-%-%%%-%%%-%%%%%%%-%%%-%%%-%%%-%
  %.%-%-%-------%---%-------%---%-%--P%
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1

There are 1 answers

0
Armali On

Your program is almost correct. The problem arises from using in_array() for looking up a node in $arr_close or $arr_open, because in_array() compares not only the position $x, $y, but also the other Node members $fCost, $hCost, $gCost ...; thus it doesn't recognize that a node is already in the closed set of nodes already evaluated if those other members differ, and gets to evaluate it repeatedly.

A quick fix is to use instead of in_array() a self-defined function that as needed only compares the $x, $y members:

function in($node, $arr)
{
    foreach ($arr as &$member)
        if ($member->x == $node->x && $member->y == $node->y) return TRUE;
    return FALSE;
}