Skip to content

Commit

Permalink
Shortest path without color
Browse files Browse the repository at this point in the history
  • Loading branch information
tienvx committed May 5, 2022
1 parent 9690e85 commit e48a99e
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 153 deletions.
12 changes: 3 additions & 9 deletions src/Model/Bug/Step.php
Expand Up @@ -42,15 +42,9 @@ public function __clone()

public function getUniqueNodeId(): string
{
$places = $this->places;
ksort($places);
$colorValues = $this->color->getValues();
ksort($colorValues);

return md5(serialize([
'places' => $places,
'color' => $colorValues,
]));
ksort($this->places);

return md5(serialize($this->places));
}

public function setColor(ColorInterface $color): void
Expand Down
2 changes: 0 additions & 2 deletions src/Reducer/DispatcherTemplate.php
Expand Up @@ -8,8 +8,6 @@

abstract class DispatcherTemplate implements DispatcherInterface
{
protected const MIN_PAIR_LENGTH = 2; // 3 steps

protected MessageBusInterface $messageBus;

public function __construct(MessageBusInterface $messageBus)
Expand Down
2 changes: 1 addition & 1 deletion src/Reducer/Random/RandomDispatcher.php
Expand Up @@ -14,7 +14,7 @@ protected function getPairs(array $steps): array

while (count($pairs) < $maxPairs) {
$pair = array_rand(range(0, $length - 1), 2);
if ($pair[1] - $pair[0] >= static::MIN_PAIR_LENGTH && !in_array($pair, $pairs)) {
if (!in_array($pair, $pairs)) {
$pairs[] = $pair;
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/Reducer/Split/SplitDispatcher.php
Expand Up @@ -17,9 +17,7 @@ protected function getPairs(array $steps): array
$range[] = $length - 1;
}
for ($i = 0; $i < count($range) - 1; ++$i) {
if ($range[$i + 1] - $range[$i] >= static::MIN_PAIR_LENGTH) {
$pairs[] = [$range[$i], $range[$i + 1]];
}
$pairs[] = [$range[$i], $range[$i + 1]];
}

return $pairs;
Expand Down
10 changes: 1 addition & 9 deletions src/Resources/config/services.php
Expand Up @@ -45,8 +45,6 @@
use Tienvx\Bundle\MbtBundle\Repository\BugRepositoryInterface;
use Tienvx\Bundle\MbtBundle\Repository\TaskRepository;
use Tienvx\Bundle\MbtBundle\Repository\TaskRepositoryInterface;
use Tienvx\Bundle\MbtBundle\Service\AStar\PetrinetDomainLogic;
use Tienvx\Bundle\MbtBundle\Service\AStar\PetrinetDomainLogicInterface;
use Tienvx\Bundle\MbtBundle\Service\Bug\BugHelper;
use Tienvx\Bundle\MbtBundle\Service\Bug\BugHelperInterface;
use Tienvx\Bundle\MbtBundle\Service\Bug\BugNotifierInterface;
Expand Down Expand Up @@ -247,16 +245,10 @@
->set(ShortestPathStepsBuilder::class)
->args([
service(PetrinetHelperInterface::class),
service(PetrinetDomainLogicInterface::class),
])
->alias(StepsBuilderInterface::class, ShortestPathStepsBuilder::class)

->set(PetrinetDomainLogic::class)
->args([
service(GuardedTransitionServiceInterface::class),
service(MarkingHelperInterface::class),
])
->alias(PetrinetDomainLogicInterface::class, PetrinetDomainLogic::class)
->alias(StepsBuilderInterface::class, ShortestPathStepsBuilder::class)

->set(StepRunner::class)
->args([
Expand Down
11 changes: 0 additions & 11 deletions src/Service/AStar/PetrinetDomainLogicInterface.php

This file was deleted.

@@ -1,30 +1,28 @@
<?php

namespace Tienvx\Bundle\MbtBundle\Service\AStar;
namespace Tienvx\Bundle\MbtBundle\Service\Step\Builder;

use JMGQ\AStar\DomainLogicInterface;
use Petrinet\Model\TransitionInterface;
use SingleColorPetrinet\Model\PetrinetInterface;
use SingleColorPetrinet\Service\GuardedTransitionServiceInterface;
use Tienvx\Bundle\MbtBundle\Exception\RuntimeException;
use Tienvx\Bundle\MbtBundle\Model\Bug\Step;
use Tienvx\Bundle\MbtBundle\Service\Petrinet\MarkingHelperInterface;

class PetrinetDomainLogic implements PetrinetDomainLogicInterface
class PetrinetDomainLogic implements DomainLogicInterface
{
protected GuardedTransitionServiceInterface $transitionService;
protected MarkingHelperInterface $markingHelper;
protected ?PetrinetInterface $petrinet = null;
protected PetrinetInterface $petrinet;

public function __construct(
GuardedTransitionServiceInterface $transitionService,
MarkingHelperInterface $markingHelper
MarkingHelperInterface $markingHelper,
PetrinetInterface $petrinet
) {
$this->transitionService = $transitionService;
$this->markingHelper = $markingHelper;
}

public function setPetrinet(?PetrinetInterface $petrinet): void
{
$this->petrinet = $petrinet;
}

Expand All @@ -46,8 +44,8 @@ public function calculateEstimatedCost(mixed $fromNode, mixed $toNode): float|in
$tokensDiff += abs($toNode->getPlaces()[$place] - $fromNode->getPlaces()[$place]);
}
}
// Estimate it will took N transitions to move N tokens if color is the same, twice if color is not the same.
return $tokensDiff * (($fromNode->getColor()->getValues() != $toNode->getColor()->getValues()) + 1);
// Estimate it will took N transitions to move N tokens.
return $tokensDiff;
}

public function calculateRealCost(mixed $node, mixed $adjacent): float|int
Expand All @@ -62,10 +60,6 @@ public function getAdjacentNodes(mixed $node): iterable
throw new RuntimeException('The provided node is invalid');
}

if (!$this->petrinet instanceof PetrinetInterface) {
throw new RuntimeException('Petrinet is required');
}

$adjacents = [];
$marking = $this->markingHelper->getMarking($this->petrinet, $node->getPlaces(), $node->getColor());
foreach ($this->transitionService->getEnabledTransitions($this->petrinet, $marking) as $transition) {
Expand Down
58 changes: 44 additions & 14 deletions src/Service/Step/Builder/ShortestPathStepsBuilder.php
Expand Up @@ -4,24 +4,31 @@

use Generator;
use JMGQ\AStar\AStar;
use RuntimeException;
use SingleColorPetrinet\Model\PetrinetInterface;
use SingleColorPetrinet\Service\GuardedTransitionServiceInterface;
use Tienvx\Bundle\MbtBundle\Exception\ExceptionInterface;
use Tienvx\Bundle\MbtBundle\Exception\OutOfRangeException;
use Tienvx\Bundle\MbtBundle\Model\Bug\Step;
use Tienvx\Bundle\MbtBundle\Model\Bug\StepInterface;
use Tienvx\Bundle\MbtBundle\Model\BugInterface;
use Tienvx\Bundle\MbtBundle\Service\AStar\PetrinetDomainLogicInterface;
use Tienvx\Bundle\MbtBundle\Service\Petrinet\MarkingHelperInterface;
use Tienvx\Bundle\MbtBundle\Service\Petrinet\PetrinetHelperInterface;

class ShortestPathStepsBuilder implements StepsBuilderInterface
{
protected PetrinetHelperInterface $petrinetHelper;
protected PetrinetDomainLogicInterface $petrinetDomainLogic;
protected GuardedTransitionServiceInterface $transitionService;
protected MarkingHelperInterface $markingHelper;

public function __construct(
PetrinetHelperInterface $petrinetHelper,
PetrinetDomainLogicInterface $petrinetDomainLogic
GuardedTransitionServiceInterface $transitionService,
MarkingHelperInterface $markingHelper
) {
$this->petrinetHelper = $petrinetHelper;
$this->petrinetDomainLogic = $petrinetDomainLogic;
$this->transitionService = $transitionService;
$this->markingHelper = $markingHelper;
}

/**
Expand All @@ -30,23 +37,46 @@ public function __construct(
public function create(BugInterface $bug, int $from, int $to): Generator
{
yield from array_slice($bug->getSteps(), 0, $from);
yield from $this->getSteps($bug, $from, $to);
yield from array_slice($bug->getSteps(), $to + 1);
$petrinet = $this->petrinetHelper->build($bug->getTask()->getModelRevision());
$shortestSteps = $this->getShortestSteps($bug->getSteps(), $from, $to, $petrinet);
$lastStep = end($shortestSteps);
reset($shortestSteps);
yield from $shortestSteps;
yield from $this->getRemainingSteps(array_slice($bug->getSteps(), $to + 1), $lastStep, $petrinet);
}

protected function getSteps(BugInterface $bug, int $from, int $to): iterable
protected function getShortestSteps(array $steps, int $from, int $to, PetrinetInterface $petrinet): iterable
{
$fromStep = $bug->getSteps()[$from] ?? null;
$toStep = $bug->getSteps()[$to] ?? null;
$fromStep = $steps[$from] ?? null;
$toStep = $steps[$to] ?? null;

if (!$fromStep instanceof StepInterface || !$toStep instanceof StepInterface) {
throw new OutOfRangeException('Can not create new steps using invalid range');
throw new OutOfRangeException('Can not create shortest steps between invalid range');
}

$this->petrinetDomainLogic->setPetrinet($this->petrinetHelper->build($bug->getTask()->getModelRevision()));

yield from (new AStar($this->petrinetDomainLogic))->run($fromStep, $toStep);
return (new AStar(new PetrinetDomainLogic($this->transitionService, $this->markingHelper, $petrinet)))->run(
$fromStep,
$toStep
);
}

$this->petrinetDomainLogic->setPetrinet(null);
protected function getRemainingSteps(array $steps, StepInterface $lastStep, PetrinetInterface $petrinet): iterable
{
$marking = $this->markingHelper->getMarking($petrinet, $lastStep->getPlaces(), $lastStep->getColor());
foreach ($steps as $step) {
if (!$step instanceof StepInterface) {
throw new OutOfRangeException('Remaining steps contains invalid step');
}
$transition = $petrinet->getTransitionById($step->getTransition());
if (!$this->transitionService->isEnabled($transition, $marking)) {
throw new RuntimeException('Can not connect remaining steps');
}
$this->transitionService->fire($transition, $marking);
yield new Step(
$this->markingHelper->getPlaces($marking),
$marking->getColor(),
$step->getTransition()
);
}
}
}
11 changes: 3 additions & 8 deletions tests/Model/Bug/StepTest.php
Expand Up @@ -55,24 +55,19 @@ public function testClone(): void
/**
* @dataProvider nodeIdProvider
*/
public function testGetUniqueNodeId(?array $places, ?ColorInterface $color, string $id): void
public function testGetUniqueNodeId(?array $places, string $id): void
{
if ($places) {
$this->step->setPlaces($places);
}
if ($color) {
$this->step->setColor($color);
}
$this->assertSame($id, $this->step->getUniqueNodeId());
}

public function nodeIdProvider(): array
{
return [
[null, null, 'f179bfa0d0b5b6751e353f049461eda8'],
[null, new Color(['key1' => 'value1']), 'e13d72c92c38781375d3a400df07d43a'],
[[0 => 2, 1 => 1], null, 'e1b90c9311d5bd1d7fc90fd43d9bd49f'],
[[0 => 1, 1 => 1], new Color(['key2' => 'value2']), '61a579e02eb3ae787ef03ad40feb9a7d'],
[null, '89fefb193877ee62e29d1da5975dcc47'],
[[0 => 2, 1 => 1], '02878487ecf2302bf7ba2cc919514889'],
];
}

Expand Down
1 change: 0 additions & 1 deletion tests/Reducer/DispatcherTestCase.php
Expand Up @@ -56,7 +56,6 @@ protected function assertMessage(int $length): Callback
}

return $message->getBugId() === $this->bug->getId() &&
$message->getFrom() + 2 <= $message->getTo() &&
$length === $message->getLength();
});
}
Expand Down
3 changes: 3 additions & 0 deletions tests/Reducer/Split/SplitDispatcherTest.php
Expand Up @@ -37,6 +37,7 @@ public function stepsProvider(): array
[6, [
[0, 2],
[2, 4],
[4, 5],
]],
[7, [
[0, 3],
Expand All @@ -45,6 +46,7 @@ public function stepsProvider(): array
[8, [
[0, 3],
[3, 6],
[6, 7],
]],
[9, [
[0, 3],
Expand All @@ -60,6 +62,7 @@ public function stepsProvider(): array
[0, 3],
[3, 6],
[6, 9],
[9, 10],
]],
[12, [
[0, 3],
Expand Down
@@ -1,6 +1,6 @@
<?php

namespace Tienvx\Bundle\MbtBundle\Tests\Service\AStar;
namespace Tienvx\Bundle\MbtBundle\Tests\Service\Step\Builder;

use Petrinet\Model\MarkingInterface;
use PHPUnit\Framework\TestCase;
Expand All @@ -13,12 +13,11 @@
use Tienvx\Bundle\MbtBundle\Exception\RuntimeException;
use Tienvx\Bundle\MbtBundle\Model\Bug\Step;
use Tienvx\Bundle\MbtBundle\Model\Bug\StepInterface;
use Tienvx\Bundle\MbtBundle\Service\AStar\PetrinetDomainLogic;
use Tienvx\Bundle\MbtBundle\Service\AStar\PetrinetDomainLogicInterface;
use Tienvx\Bundle\MbtBundle\Service\Petrinet\MarkingHelperInterface;
use Tienvx\Bundle\MbtBundle\Service\Step\Builder\PetrinetDomainLogic;

/**
* @covers \Tienvx\Bundle\MbtBundle\Service\AStar\PetrinetDomainLogic
* @covers \Tienvx\Bundle\MbtBundle\Service\Step\Builder\PetrinetDomainLogic
*
* @uses \Tienvx\Bundle\MbtBundle\Model\Bug\Step
*/
Expand All @@ -27,7 +26,7 @@ class PetrinetDomainLogicTest extends TestCase
protected GuardedTransitionServiceInterface $transitionService;
protected MarkingHelperInterface $markingHelper;
protected PetrinetInterface $petrinet;
protected PetrinetDomainLogicInterface $petrinetDomainLogic;
protected PetrinetDomainLogic $petrinetDomainLogic;
protected array $transitions;
protected array $markings;
protected array $places;
Expand All @@ -36,8 +35,12 @@ protected function setUp(): void
{
$this->transitionService = $this->createMock(GuardedTransitionServiceInterface::class);
$this->markingHelper = $this->createMock(MarkingHelperInterface::class);
$this->petrinetDomainLogic = new PetrinetDomainLogic($this->transitionService, $this->markingHelper);
$this->petrinet = $this->createMock(PetrinetInterface::class);
$this->petrinetDomainLogic = new PetrinetDomainLogic(
$this->transitionService,
$this->markingHelper,
$this->petrinet
);
$this->transitions = [
$transition1 = new GuardedTransition(),
$transition2 = new GuardedTransition(),
Expand Down Expand Up @@ -98,19 +101,11 @@ public function testCalculateEstimatedCost(Step $fromNode, Step $toNode, int $co
public function estimatedCostProvider(): array
{
$color = new Color(['key' => 'value']);
$differentColor = new Color(['different key' => 'different value']);

return [
[$this->getStep([0 => 1, 1 => 5, 2 => 3], $color), $this->getStep([], $color), 9],
[$this->getStep([], $color), $this->getStep([0 => 3, 1 => 2, 2 => 2, 3 => 1], $color), 8],
[$this->getStep([0 => 2, 1 => 4], $color), $this->getStep([1 => 5, 2 => 3, 3 => 1], $color), 7],
[$this->getStep([0 => 4, 1 => 1, 2 => 2], $color), $this->getStep([2 => 5], $differentColor), 16],
[$this->getStep([], $color), $this->getStep([0 => 4, 1 => 1], $differentColor), 10],
[
$this->getStep([0 => 3, 1 => 2, 2 => 2], $color),
$this->getStep([1 => 7, 2 => 2, 3 => 3], $differentColor),
22,
],
];
}

Expand All @@ -125,16 +120,9 @@ public function testGetAdjacentNodesOfInvalidNode(): void
$this->petrinetDomainLogic->getAdjacentNodes('invalid');
}

public function testGetAdjacentNodesWithoutPetrinet(): void
{
$this->expectExceptionObject(new RuntimeException('Petrinet is required'));
$this->petrinetDomainLogic->getAdjacentNodes($this->getStep([], new Color()));
}

public function testGetAdjacentNodes(): void
{
$node = $this->getStep([12 => 34], new Color(['key' => 'value']));
$this->petrinetDomainLogic->setPetrinet($this->petrinet);
$this->markingHelper
->expects($this->exactly(count($this->transitions) + 1))
->method('getMarking')
Expand Down

0 comments on commit e48a99e

Please sign in to comment.