diff --git a/src/Provider/GameGridProvider.php b/src/Provider/GameGridProvider.php index 1245a9f..fad071c 100644 --- a/src/Provider/GameGridProvider.php +++ b/src/Provider/GameGridProvider.php @@ -24,9 +24,40 @@ class GameGridProvider private readonly EntityManagerInterface $em, ) {} - public function generate(?User $user = null): Game + /** + * @param array{watchedOnly?: bool, hintTypes?: list, awardTypeIds?: list|null} $config + */ + public function generate(?User $user = null, array $config = []): ?Game { - $mainActor = $this->actorRepository->findOneRandom(4); + $watchedOnly = $config['watchedOnly'] ?? false; + $hintTypes = $config['hintTypes'] ?? ['film', 'character', 'award']; + $awardTypeIds = $config['awardTypeIds'] ?? null; + + for ($attempt = 0; $attempt < 5; $attempt++) { + $game = $this->tryGenerate($user, $watchedOnly, $hintTypes, $awardTypeIds); + if ($game !== null) { + return $game; + } + } + + return null; + } + + /** + * @param list $hintTypes + * @param list|null $awardTypeIds + */ + private function tryGenerate(?User $user, bool $watchedOnly, array $hintTypes, ?array $awardTypeIds): ?Game + { + if ($watchedOnly && $user !== null) { + $mainActor = $this->actorRepository->findOneRandomInWatchedFilms($user, 4); + } else { + $mainActor = $this->actorRepository->findOneRandom(4); + } + + if ($mainActor === null) { + return null; + } $game = new Game(); $game->setMainActor($mainActor); @@ -40,14 +71,23 @@ class GameGridProvider continue; } - $tryFindActor = 0; - do { - $actor = $this->actorRepository->findOneRandom(4, $char); - ++$tryFindActor; - } while ( - in_array($actor->getId(), $usedActors) - || $tryFindActor < 5 - ); + $actor = null; + for ($try = 0; $try < 5; $try++) { + if ($watchedOnly && $user !== null) { + $candidate = $this->actorRepository->findOneRandomInWatchedFilms($user, 4, $char); + } else { + $candidate = $this->actorRepository->findOneRandom(4, $char); + } + + if ($candidate !== null && !in_array($candidate->getId(), $usedActors)) { + $actor = $candidate; + break; + } + } + + if ($actor === null) { + return null; + } $usedActors[] = $actor->getId(); @@ -56,11 +96,12 @@ class GameGridProvider $row->setPosition(strpos(strtolower($actor->getName()), $char)); $row->setRowOrder($rowOrder); - $hint = $this->generateHint($actor); - if ($hint !== null) { - $row->setHintType($hint['type']); - $row->setHintData($hint['data']); + $hint = $this->generateHint($actor, $hintTypes, $awardTypeIds); + if ($hint === null) { + return null; // Every row must have a hint } + $row->setHintType($hint['type']); + $row->setHintData($hint['data']); $game->addRow($row); ++$rowOrder; @@ -133,17 +174,17 @@ class GameGridProvider } /** - * Generate a single hint for a row actor. - * + * @param list $allowedTypes + * @param list|null $awardTypeIds * @return array{type: string, data: string}|null */ - private function generateHint(Actor $rowActor): ?array + private function generateHint(Actor $rowActor, array $allowedTypes = ['film', 'character', 'award'], ?array $awardTypeIds = null): ?array { - $types = ['film', 'character', 'award']; + $types = $allowedTypes; shuffle($types); foreach ($types as $type) { - $hint = $this->resolveHint($type, $rowActor); + $hint = $this->resolveHint($type, $rowActor, $awardTypeIds); if ($hint !== null) { return $hint; } @@ -153,27 +194,33 @@ class GameGridProvider } /** + * @param list|null $awardTypeIds * @return array{type: string, data: string}|null */ - private function resolveHint(string $type, Actor $rowActor): ?array + private function resolveHint(string $type, Actor $rowActor, ?array $awardTypeIds = null): ?array { + $actorId = $rowActor->getId(); + if ($actorId === null) { + return null; + } + switch ($type) { case 'film': - $role = $this->movieRoleRepository->findOneRandomByActor($rowActor->getId()); + $role = $this->movieRoleRepository->findOneRandomByActor($actorId); if ($role === null) { return null; } return ['type' => 'film', 'data' => (string) $role->getMovie()->getId()]; case 'character': - $role = $this->movieRoleRepository->findOneRandomByActor($rowActor->getId()); + $role = $this->movieRoleRepository->findOneRandomByActor($actorId); if ($role === null) { return null; } return ['type' => 'character', 'data' => (string) $role->getId()]; case 'award': - $award = $this->awardRepository->findOneRandomByActor($rowActor->getId()); + $award = $this->awardRepository->findOneRandomByActorAndTypes($actorId, $awardTypeIds); if ($award === null) { return null; } diff --git a/tests/Provider/GameGridProviderTest.php b/tests/Provider/GameGridProviderTest.php index 339b355..b36d762 100644 --- a/tests/Provider/GameGridProviderTest.php +++ b/tests/Provider/GameGridProviderTest.php @@ -53,4 +53,31 @@ class GameGridProviderTest extends TestCase $this->assertSame('Academy Award for Best Actor (2020)', $result); } + + public function testGenerateHintRespectsAllowedTypes(): void + { + $movieRoleRepo = $this->createMock(MovieRoleRepository::class); + $movieRoleRepo->method('findOneRandomByActor')->willReturn(null); + + $awardRepo = $this->createMock(AwardRepository::class); + $awardRepo->method('findOneRandomByActor')->willReturn(null); + $awardRepo->method('findOneRandomByActorAndTypes')->willReturn(null); + + $generator = new GameGridProvider( + $this->createMock(ActorRepository::class), + $movieRoleRepo, + $this->createMock(MovieRepository::class), + $awardRepo, + $this->createMock(EntityManagerInterface::class), + ); + + $actor = new Actor(); + $actor->setName('Test'); + + // Only allow 'award' type, but no awards exist → should return null + $method = new \ReflectionMethod($generator, 'generateHint'); + $result = $method->invoke($generator, $actor, ['award'], null); + + $this->assertNull($result); + } }