<?php
 
error_reporting(-1);
mb_internal_encoding('utf-8');
 
define('SUBWAY', 'sub');
define('FOOT', 'foot');
define('BUS', 'bus');
 
$transportName = array(
    SUBWAY  =>  'едешь на метро',
    FOOT    =>  'идешь пешком',
    BUS     =>  'едешь на автобусе'
);
 
$startPoint = 'pet'; // Петроградская
$endPoint = 'nov'; // Новая Голландия
 
$pointNames = array(
    'pet'   =>  'ст. м. Петроградская',
    'chk'   =>  'ст. м. Чкаловская',
    'gor'   =>  'ст. м. Горьковская',
    'spo'   =>  'ст. м. Спортивная',
    'vas'   =>  'ст. м. Василеостровская',
    'kre'   =>  'Петропавловская крепость',
    'let'   =>  'Летний сад',
    'dvo'   =>  'Дворцовая площадь',
    'isa'   =>  'Исакиевский собор',
    'nov'   =>  'Новая Голландия',
    'ras'   =>  'Дом Раскольникова',
    'gos'   =>  'Гостиный Двор',
    'sen'   =>  'Сенная Площадь',
    'vla'   =>  'ст. м. Владимирская',
    'vit'   =>  'Витебский вокзал',
    'teh'   =>  'Технологический Институт'
);
 
$paths = array(
    'pet'   =>  array(
        'chk'   =>  canGet(10, BUS),
        'gor'   =>  canGet(3, SUBWAY)
    ),
 
    'chk'   =>  array(
        'pet'   =>  canGet(10, BUS),
        'spo'   =>  canGet(3, SUBWAY)
    ),
 
    'gor'   =>  array(
        'pet'   =>  canGet(3, BUS),
        'kre'   =>  canGet(5, FOOT),
        'gos'   =>  canGet(6, SUBWAY)
    ),
 
    'spo'   =>  array(
        'chk'   =>  canGet(3, SUBWAY),
        'vas'   =>  canGet(10, BUS),
        'sen'   =>  canGet(7, SUBWAY)
    ),
 
    'vas'   =>  array(
        'spo'   =>  canGet(10, BUS),
        'gos'   =>  canGet(7, SUBWAY),
        'nov'   =>  canGet(11, FOOT)
    ),
 
    'kre'   =>  array(
        'gor'   =>  canGet(5, FOOT)
    ),
 
    'let'   =>  array(
        'dvo'   =>  canGet(6, FOOT),
        'gos'   =>  canGet(7, FOOT)
    ),
 
    'dvo'   =>  array(
        'isa'   =>  canGet(6, FOOT),
        'gos'   =>  canGet(6, FOOT),
        'let'   =>  canGet(6, FOOT)
    ),
 
    'isa'   =>  array(
        'dvo'   =>  canGet(6, FOOT),
        'nov'   =>  canGet(5, FOOT)
    ),
 
    'nov'   =>  array(
        'vas'   =>  canGet(11, FOOT),
        'isa'   =>  canGet(5, FOOT),
        'ras'   =>  canGet(7, BUS)
    ),
 
    'ras'   =>  array(
        'nov'   =>  canGet(7, BUS),
        'sen'   =>  canGet(3, FOOT)
    ),
 
    'gos'   =>  array(
        'vas'   =>  canGet(7, SUBWAY),
        'sen'   =>  canGet(3, SUBWAY),
        'dvo'   =>  canGet(6, FOOT),
        'gor'   =>  canGet(6, SUBWAY),
        'let'   =>  canGet(7, FOOT),
        'vla'   =>  canGet(7, FOOT)        
    ),
 
    'sen'   =>  array(
        'ras'   =>  canGet(3, FOOT),
        'spo'   =>  canGet(7, SUBWAY),
        'gos'   =>  canGet(3, SUBWAY),
        'vla'   =>  canGet(4, SUBWAY),
        'vit'   =>  canGet(2, SUBWAY),
        'teh'   =>  canGet(3, SUBWAY)
    ),
 
    'vla'   =>  array(
        'sen'   =>  canGet(4, SUBWAY),
        'gos'   =>  canGet(7, FOOT),
        'vit'   =>  canGet(3, SUBWAY)
    ),
 
    'vit'   =>  array(
        'sen'   =>  canGet(2, SUBWAY),
        'teh'   =>  canGet(2, SUBWAY),
        'vla'   =>  canGet(3, SUBWAY)
    ),
 
    'teh'   =>  array(
        'sen'   =>  canGet(3, SUBWAY),
        'vit'   =>  canGet(2, SUBWAY)        
    )
);

$start = "nov";
$target = "kre";
$pathDone = [$start];
$time = 0;

$bestPath = findBestPath($paths, $pathDone, $time, $start, $target);

echo "Начальная точка: $pointNames[$start]<br>";
for($i = 1; $i < count($bestPath['path']); $i++){
    $cur = $bestPath['path'][$i - 1];
    $curFull = $pointNames[$bestPath['path'][$i - 1]];
    $next = $bestPath['path'][$i];
    $nextFull = $pointNames[$bestPath['path'][$i]];
    $tr = $transportName[$paths[$cur][$next]['by']];
    $ti = $paths[$cur][$next]['time'];
    echo "Из неё $tr до точки $nextFull $ti мин.<br>";
    if($i == count($bestPath['path']) - 1){
        $time = $bestPath['time'];
        echo "В итоге ты попадёшь в точку $nextFull за $time мин. Приятной поездки!";
    }
}



function findBestPath($paths, $pathDone, $time, $start, $target){    
    $bestPath = [
        'path' => [],
        'time' => 999999
    ];
    $result = [];

    $foundPaths = findPaths($paths, $pathDone, $time, $start, $target);

    for($i = 0; $i < count($foundPaths); $i += 2){
        $tempArray = [];
        $tempArray['path'] = $foundPaths[$i];
        $tempArray['time'] = $foundPaths[$i + 1];
        array_push($result, $tempArray);
    }
    
    foreach($result as $path){
        if($path["time"] < $bestPath["time"]){
            $bestPath = $path;
        }
    }

    return $bestPath;
}

function findPaths($paths, $pathDone, $time, $point, $target){
	$result = [];
    $foundPaths = [];

	if(array_key_exists($target, $paths[$point])){
		array_push($pathDone, $target);
		$result['path'] = $pathDone;
		$result['time'] = $time + $paths[$point][$target]['time'];
		return $result;
	}else{
		foreach($paths[$point] as $connectedPoint => $connectedPointData){
            if(in_array($connectedPoint, $pathDone)){
                continue;
            }else{
                $newPathDone = $pathDone;
                array_push($newPathDone, $connectedPoint);

                $result = findPaths($paths, $newPathDone, $time+$connectedPointData['time'], $connectedPoint, $target);

                if(!empty($result)){
                    if(count($result) == 1){
                        array_push($foundPaths, $result);
                    }elseif(count($result) > 1){
                        foreach($result as  $path){
                            array_push($foundPaths, $path);
                        }
                    }else{
                        array_push($foundPaths, $result);
                    }                    
                }                
            }
        }
	}

    return $foundPaths;
}

function canGet($time, $byWhat) {
    return array('time'     =>  $time, 'by' =>  $byWhat);
}