feat: generate hints per row in GameGridGenerator

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
thibaud-leclere
2026-03-30 22:32:51 +02:00
parent c2efdd4eeb
commit 32ae77da53

View File

@@ -9,12 +9,15 @@ use App\Entity\Game;
use App\Entity\GameRow; use App\Entity\GameRow;
use App\Entity\User; use App\Entity\User;
use App\Repository\ActorRepository; use App\Repository\ActorRepository;
use App\Repository\MovieRoleRepository;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
class GameGridGenerator class GameGridGenerator
{ {
public function __construct( public function __construct(
private readonly ActorRepository $actorRepository, private readonly ActorRepository $actorRepository,
private readonly MovieRoleRepository $movieRoleRepository,
private readonly WikidataAwardGateway $wikidataAwardGateway,
private readonly EntityManagerInterface $em, private readonly EntityManagerInterface $em,
) {} ) {}
@@ -28,6 +31,8 @@ class GameGridGenerator
$usedActors = [$mainActor->getId()]; $usedActors = [$mainActor->getId()];
$rowOrder = 0; $rowOrder = 0;
$usedMovieRoleIds = [];
$usedHintKeys = [];
foreach (str_split(strtolower($mainActor->getName())) as $char) { foreach (str_split(strtolower($mainActor->getName())) as $char) {
if (!preg_match('/[a-z]/', $char)) { if (!preg_match('/[a-z]/', $char)) {
@@ -50,6 +55,12 @@ class GameGridGenerator
$row->setPosition(strpos(strtolower($actor->getName()), $char)); $row->setPosition(strpos(strtolower($actor->getName()), $char));
$row->setRowOrder($rowOrder); $row->setRowOrder($rowOrder);
$hint = $this->generateHint($mainActor, $usedMovieRoleIds, $usedHintKeys);
if ($hint !== null) {
$row->setHintType($hint['type']);
$row->setHintData($hint['data']);
}
$game->addRow($row); $game->addRow($row);
++$rowOrder; ++$rowOrder;
} }
@@ -115,4 +126,86 @@ class GameGridGenerator
'middle' => $leftSize, 'middle' => $leftSize,
]; ];
} }
/**
* @param list<int> $usedMovieRoleIds MovieRole IDs already used (for DB exclusion)
* @param list<string> $usedHintKeys Semantic keys like "film:42" to avoid duplicate hints
* @return array{type: string, data: string}|null
*/
private function generateHint(Actor $mainActor, array &$usedMovieRoleIds, array &$usedHintKeys): ?array
{
$types = ['film', 'character', 'award'];
shuffle($types);
foreach ($types as $type) {
$hint = $this->resolveHint($type, $mainActor, $usedMovieRoleIds, $usedHintKeys);
if ($hint !== null) {
return $hint;
}
}
return null;
}
/**
* @param list<int> $usedMovieRoleIds
* @param list<string> $usedHintKeys
* @return array{type: string, data: string}|null
*/
private function resolveHint(string $type, Actor $mainActor, array &$usedMovieRoleIds, array &$usedHintKeys): ?array
{
switch ($type) {
case 'film':
$role = $this->movieRoleRepository->findOneRandomByActor(
$mainActor->getId(),
$usedMovieRoleIds,
);
if ($role === null) {
return null;
}
$movieId = (string) $role->getMovie()->getId();
$key = 'film:' . $movieId;
if (in_array($key, $usedHintKeys)) {
return null;
}
$usedMovieRoleIds[] = $role->getId();
$usedHintKeys[] = $key;
return ['type' => 'film', 'data' => $movieId];
case 'character':
$role = $this->movieRoleRepository->findOneRandomByActor(
$mainActor->getId(),
$usedMovieRoleIds,
);
if ($role === null) {
return null;
}
$roleId = (string) $role->getId();
$key = 'character:' . $roleId;
if (in_array($key, $usedHintKeys)) {
return null;
}
$usedMovieRoleIds[] = $role->getId();
$usedHintKeys[] = $key;
return ['type' => 'character', 'data' => $roleId];
case 'award':
try {
$awards = $this->wikidataAwardGateway->getAwards($mainActor);
} catch (\Throwable) {
return null;
}
foreach ($awards as $award) {
$text = $award['name'] . ' (' . $award['year'] . ')';
$key = 'award:' . $text;
if (!in_array($key, $usedHintKeys)) {
$usedHintKeys[] = $key;
return ['type' => 'award', 'data' => $text];
}
}
return null;
}
return null;
}
} }