actorRepository->findOneRandom(4); $game = new Game(); $game->setMainActor($mainActor); $game->setUser($user); $usedActors = [$mainActor->getId()]; $rowOrder = 0; foreach (str_split(strtolower($mainActor->getName())) as $char) { if (!preg_match('/[a-z]/', $char)) { continue; } $tryFindActor = 0; do { $actor = $this->actorRepository->findOneRandom(4, $char); ++$tryFindActor; } while ( in_array($actor->getId(), $usedActors) || $tryFindActor < 5 ); $usedActors[] = $actor->getId(); $row = new GameRow(); $row->setActor($actor); $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']); } $game->addRow($row); ++$rowOrder; } $this->em->persist($game); $this->em->flush(); return $game; } /** * Compute display data (grid, width, middle) from a Game entity for the React component. * * @return array{grid: list, width: int, middle: int} */ public function computeGridData(Game $game): array { $leftSize = 0; $rightSize = 0; $grid = []; $mainActorChars = str_split($game->getMainActor()->getName()); $rows = $game->getRows()->toArray(); $rowIndex = 0; foreach ($mainActorChars as $char) { if (!preg_match('/[a-zA-Z]/', $char)) { $grid[] = [ 'separator' => $char, ]; continue; } $row = $rows[$rowIndex] ?? null; ++$rowIndex; if ($row === null) { continue; } $actor = $row->getActor(); $pos = $row->getPosition(); if ($leftSize < $pos) { $leftSize = $pos; } $rightSizeActor = strlen($actor->getName()) - $pos - 1; if ($rightSize < $rightSizeActor) { $rightSize = $rightSizeActor; } $hintText = $this->resolveHintText($row); $grid[] = [ 'actorName' => $actor->getName(), 'actorId' => $actor->getId(), 'pos' => $pos, 'hintType' => $row->getHintType(), 'hintText' => $hintText, ]; } return [ 'grid' => $grid, 'width' => $rightSize + $leftSize + 1, 'middle' => $leftSize, ]; } /** * Generate a single hint for a row actor. * * @return array{type: string, data: string}|null */ private function generateHint(Actor $rowActor): ?array { $types = ['film', 'character', 'award']; shuffle($types); foreach ($types as $type) { $hint = $this->resolveHint($type, $rowActor); if ($hint !== null) { return $hint; } } return null; } /** * @return array{type: string, data: string}|null */ private function resolveHint(string $type, Actor $rowActor): ?array { switch ($type) { case 'film': $role = $this->movieRoleRepository->findOneRandomByActor($rowActor->getId()); if ($role === null) { return null; } return ['type' => 'film', 'data' => (string) $role->getMovie()->getId()]; case 'character': $role = $this->movieRoleRepository->findOneRandomByActor($rowActor->getId()); if ($role === null) { return null; } return ['type' => 'character', 'data' => (string) $role->getId()]; case 'award': try { $awards = $this->wikidataAwardGateway->getAwards($rowActor); } catch (\Throwable) { return null; } if (!empty($awards)) { $award = $awards[array_rand($awards)]; return ['type' => 'award', 'data' => $award['name'] . ' (' . $award['year'] . ')']; } return null; } return null; } private function resolveHintText(GameRow $row): ?string { $type = $row->getHintType(); $data = $row->getHintData(); if ($type === null || $data === null) { return null; } return match ($type) { 'film' => $this->movieRepository->find((int) $data)?->getTitle(), 'character' => $this->movieRoleRepository->find((int) $data)?->getCharacter(), 'award' => $data, default => null, }; } }