feat: GameGridProvider accepts config for hint types, watched-only, and retry logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
thibaud-leclere
2026-04-01 23:06:04 +02:00
parent 9c095a76eb
commit 94ff0ced63
2 changed files with 97 additions and 23 deletions

View File

@@ -24,9 +24,40 @@ class GameGridProvider
private readonly EntityManagerInterface $em, private readonly EntityManagerInterface $em,
) {} ) {}
public function generate(?User $user = null): Game /**
* @param array{watchedOnly?: bool, hintTypes?: list<string>, awardTypeIds?: list<int>|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<string> $hintTypes
* @param list<int>|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 = new Game();
$game->setMainActor($mainActor); $game->setMainActor($mainActor);
@@ -40,14 +71,23 @@ class GameGridProvider
continue; continue;
} }
$tryFindActor = 0; $actor = null;
do { for ($try = 0; $try < 5; $try++) {
$actor = $this->actorRepository->findOneRandom(4, $char); if ($watchedOnly && $user !== null) {
++$tryFindActor; $candidate = $this->actorRepository->findOneRandomInWatchedFilms($user, 4, $char);
} while ( } else {
in_array($actor->getId(), $usedActors) $candidate = $this->actorRepository->findOneRandom(4, $char);
|| $tryFindActor < 5 }
);
if ($candidate !== null && !in_array($candidate->getId(), $usedActors)) {
$actor = $candidate;
break;
}
}
if ($actor === null) {
return null;
}
$usedActors[] = $actor->getId(); $usedActors[] = $actor->getId();
@@ -56,11 +96,12 @@ class GameGridProvider
$row->setPosition(strpos(strtolower($actor->getName()), $char)); $row->setPosition(strpos(strtolower($actor->getName()), $char));
$row->setRowOrder($rowOrder); $row->setRowOrder($rowOrder);
$hint = $this->generateHint($actor); $hint = $this->generateHint($actor, $hintTypes, $awardTypeIds);
if ($hint !== null) { if ($hint === null) {
$row->setHintType($hint['type']); return null; // Every row must have a hint
$row->setHintData($hint['data']);
} }
$row->setHintType($hint['type']);
$row->setHintData($hint['data']);
$game->addRow($row); $game->addRow($row);
++$rowOrder; ++$rowOrder;
@@ -133,17 +174,17 @@ class GameGridProvider
} }
/** /**
* Generate a single hint for a row actor. * @param list<string> $allowedTypes
* * @param list<int>|null $awardTypeIds
* @return array{type: string, data: string}|null * @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); shuffle($types);
foreach ($types as $type) { foreach ($types as $type) {
$hint = $this->resolveHint($type, $rowActor); $hint = $this->resolveHint($type, $rowActor, $awardTypeIds);
if ($hint !== null) { if ($hint !== null) {
return $hint; return $hint;
} }
@@ -153,27 +194,33 @@ class GameGridProvider
} }
/** /**
* @param list<int>|null $awardTypeIds
* @return array{type: string, data: string}|null * @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) { switch ($type) {
case 'film': case 'film':
$role = $this->movieRoleRepository->findOneRandomByActor($rowActor->getId()); $role = $this->movieRoleRepository->findOneRandomByActor($actorId);
if ($role === null) { if ($role === null) {
return null; return null;
} }
return ['type' => 'film', 'data' => (string) $role->getMovie()->getId()]; return ['type' => 'film', 'data' => (string) $role->getMovie()->getId()];
case 'character': case 'character':
$role = $this->movieRoleRepository->findOneRandomByActor($rowActor->getId()); $role = $this->movieRoleRepository->findOneRandomByActor($actorId);
if ($role === null) { if ($role === null) {
return null; return null;
} }
return ['type' => 'character', 'data' => (string) $role->getId()]; return ['type' => 'character', 'data' => (string) $role->getId()];
case 'award': case 'award':
$award = $this->awardRepository->findOneRandomByActor($rowActor->getId()); $award = $this->awardRepository->findOneRandomByActorAndTypes($actorId, $awardTypeIds);
if ($award === null) { if ($award === null) {
return null; return null;
} }

View File

@@ -53,4 +53,31 @@ class GameGridProviderTest extends TestCase
$this->assertSame('Academy Award for Best Actor (2020)', $result); $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);
}
} }