/**
  * @param RideNodeList $rideNodeList
  */
 public function addRideNodeList(RideNodeList $rideNodeList)
 {
     $this->totalDistance += $rideNodeList->getTotalDistance();
     $this->totalEmptyRideTime += $rideNodeList->getTotalEmptyRideTime();
     $this->totalEmptyRideDistance += $rideNodeList->getTotalEmptyRideDistance();
     $this->rideNodeLists[] = $rideNodeList;
 }
 /**
  * Enforce operational constraints for ride nodes and node lists:
  *
  * constraint 1: vehicle capacity (seats, wheelchairs) not exceeded
  * constraint 2: compatibility passenger(s) and vehicle (true)
  * constraint 3: compatibility passenger(s) and driver (true)
  *
  * return true if anything is wrong (taboo, sometimes also called tabu)
  *
  * @param RideNodeList $rideNodeList
  * @param RideNode $rideNode
  * @return bool
  */
 public function isTaboo(RideNodeList $rideNodeList, RideNode $rideNode)
 {
     $vehicle = $rideNodeList->getVehicle();
     if (null !== $vehicle) {
         // constraint 1: vehicle capacity (resources)
         $vehicleCat = $vehicle->getCategory();
         if ($rideNode->passengers > $vehicleCat->getAmountOfSeats() or $rideNode->wheelChairs > $vehicleCat->getAmountOfWheelChairs()) {
             return true;
         }
         // constraint 2: compatibility of vehicle category and passenger(s)
         $contradicting = $rideNode->contradictingVehicleCategories;
         if (null !== $contradicting) {
             if (array_key_exists($vehicleCat->getId(), $contradicting)) {
                 return true;
             }
         }
     }
     // todo constraint 3: compatibility passenger(s) and driver tested here...
     return false;
 }
 /**
  * @param RideNode $feasibleNode
  * @param RideNodeList $rideNodeList
  * @throws \Exception
  * @return mixed
  */
 private function checkIfNodeIsFeasibleForNodeList(RideNode $feasibleNode, RideNodeList $rideNodeList)
 {
     /** @var RideNode $listNode */
     $listNode = $rideNodeList->getFirstNode();
     $counter = 0;
     while (null !== $listNode) {
         if ($this->checkOverlappingNodes($feasibleNode, $listNode)) {
             // overlapping nodes
             return RideDTO::MAYBE;
         } elseif ($feasibleNode->endMinute < $listNode->startMinute) {
             // feasible node is left
             $rideNodeList->addRideNodeBeforeRideNode($feasibleNode, $listNode);
             return $this->checkEmptyRidesInNodeList($rideNodeList, $feasibleNode);
         } elseif ($listNode === $rideNodeList->getLastNode()) {
             // feasible node is right of last node
             $rideNodeList->addRideNodeAfterRideNode($feasibleNode, $listNode);
             return $this->checkEmptyRidesInNodeList($rideNodeList, $feasibleNode);
         } else {
             // feasible node is in between two nodes, check next node
             $listNode = $listNode->nextNode;
             $counter = $this->incrementAndTestLoopCounter($counter);
         }
     }
     return RideDTO::YES;
     // vehicle driving pool is empty
 }
 /**
  * @param $rideNodes RideNode[]
  * @return RideConfiguration
  */
 private function buildGenericLeastDurationConfiguration($rideNodes)
 {
     $rideConfiguration = new RideConfiguration($this->drivingPools);
     $workRideNodes = $rideNodes;
     //always loop all available pools - but stop when no missions are left so it is
     //possible to have empty rideNodeLists (no driver/vehicle needed for these)
     //the amount of created rideNodeLists will give the amount of used vehicles
     foreach ($this->drivingPools as $drivingPool) {
         if (count($workRideNodes) < 1) {
             break;
         }
         $rideNodeList = new RideNodeList($drivingPool);
         //set first node on start
         $rideNodeList->addRideNode(array_shift($workRideNodes));
         $poolExtraMinutesPerRide = 0;
         if ($drivingPool->hasAssociatedDriver()) {
             $poolExtraMinutesPerRide += $drivingPool->getDriver()->getExtraMinutes();
         }
         $stillFeasible = true;
         while ($stillFeasible) {
             $actualNode = $rideNodeList->getActualRideNode();
             $bestNode = null;
             $bestNodeKey = null;
             $bestEmptyRide = null;
             $actualDuration = -1;
             //check all nodes in workSet for feasible and best distance
             foreach ($workRideNodes as $compareNodeKey => $compareNode) {
                 $emptyRide = $this->adjacenceMatrix[$actualNode->getRideNodeHashId()][$compareNode->getRideNodeHashId()];
                 //not feasible, get next node
                 if ($emptyRide === -1) {
                     continue;
                 }
                 if ($this->isTaboo($rideNodeList, $compareNode)) {
                     continue;
                 }
                 // feasible
                 $feasibleTimeForNextNode = $actualNode->endMinute + $emptyRide->duration + DispositionVariables::ARRIVAL_BEFORE_PICKUP + $poolExtraMinutesPerRide;
                 //feasible time for node + emptyRide -> to next node
                 if ($feasibleTimeForNextNode <= $compareNode->startMinute) {
                     if ($actualDuration === -1) {
                         $bestNode = $compareNode;
                         $bestNodeKey = $compareNodeKey;
                         $bestEmptyRide = $emptyRide;
                         $actualDuration = $emptyRide->duration;
                     }
                     if ($emptyRide->duration < $actualDuration) {
                         $bestNode = $compareNode;
                         $bestNodeKey = $compareNodeKey;
                         $bestEmptyRide = $emptyRide;
                         $actualDuration = $emptyRide->duration;
                     }
                 }
             }
             if ($bestNode && $bestEmptyRide) {
                 $rideNodeList->addRideNode($bestNode);
                 $rideNodeList->addRideNode($bestEmptyRide);
                 unset($workRideNodes[$bestNodeKey]);
             } else {
                 $stillFeasible = false;
             }
         }
         $rideConfiguration->addRideNodeList($rideNodeList);
     }
     $rideConfiguration->setNotFeasibleNodes($workRideNodes);
     $rideConfiguration->removeEmptyRideNodeLists();
     return $rideConfiguration;
 }
 /**
  * build rideConfiguration in this builder and returns it
  *
  * @return RideConfiguration
  */
 public function createConfigurationFromExistingMissions()
 {
     foreach ($this->drivingPools as $pool) {
         $rideNodeList = new RideNodeList($pool);
         if ($pool->hasAssociatedDrivingMissions()) {
             $nodes = $this->createRideNodesFromDrivingMissions($pool->getDrivingMissions());
             $this->sortNodesByStartMinute($nodes);
             foreach ($nodes as $node) {
                 $rideNodeList->addRideNode($node);
             }
         }
         $this->rideConfiguration->addRideNodeList($rideNodeList);
     }
     return $this->rideConfiguration;
 }