|
@@ -4,25 +4,303 @@ declare(strict_types=1);
|
|
|
namespace App\Services;
|
|
namespace App\Services;
|
|
|
|
|
|
|
|
use App\Entity\Galaxy;
|
|
use App\Entity\Galaxy;
|
|
|
|
|
+use App\Entity\Planet;
|
|
|
use App\Entity\Sector;
|
|
use App\Entity\Sector;
|
|
|
|
|
+use App\Entity\System;
|
|
|
|
|
+use App\Enum\CelestialBodySizeEnum;
|
|
|
|
|
+use App\Enum\PlanetHabitabilityEnum;
|
|
|
|
|
+use App\Enum\PlanetTypeEnum;
|
|
|
|
|
+use App\Enum\StarTypeEnum;
|
|
|
|
|
|
|
|
class GalaxyFactory
|
|
class GalaxyFactory
|
|
|
{
|
|
{
|
|
|
- public function createGalaxy(int $nbSectors = 1000): Galaxy
|
|
|
|
|
|
|
+ const SYSTEMS_PER_SECTOR = 12;
|
|
|
|
|
+
|
|
|
|
|
+ public function createGalaxy(int $nbSectors = 1000, string $name = 'Galaxy'): Galaxy
|
|
|
{
|
|
{
|
|
|
$galaxy = new Galaxy();
|
|
$galaxy = new Galaxy();
|
|
|
|
|
+ $galaxy->setName($name);
|
|
|
|
|
|
|
|
for ($i = 0; $i < $nbSectors; $i++) {
|
|
for ($i = 0; $i < $nbSectors; $i++) {
|
|
|
- $sector = $this->createSector();
|
|
|
|
|
|
|
+ $sector = $this->createSector($i + 1);
|
|
|
$galaxy->addSector($sector);
|
|
$galaxy->addSector($sector);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return $galaxy;
|
|
return $galaxy;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- protected function createSector(): Sector {
|
|
|
|
|
|
|
+ protected function createSector(int $number): Sector {
|
|
|
$sector = new Sector();
|
|
$sector = new Sector();
|
|
|
|
|
+ $sector->setNumber($number);
|
|
|
|
|
+ $sector->setName("Sector $number");
|
|
|
|
|
+
|
|
|
|
|
+ for ($i = 0; $i < self::SYSTEMS_PER_SECTOR; $i++) {
|
|
|
|
|
+ $system = $this->createSystem();
|
|
|
|
|
+ $sector->addSystem($system);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return $sector;
|
|
return $sector;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ protected function createSystem(): System {
|
|
|
|
|
+ $system = new System();
|
|
|
|
|
+
|
|
|
|
|
+ // 1. Choisir aléatoirement un type d'étoile avec des proportions réalistes
|
|
|
|
|
+ $starType = $this->generateRandomStarType();
|
|
|
|
|
+ $system->setStarType($starType);
|
|
|
|
|
+
|
|
|
|
|
+ // Générer une taille d'étoile appropriée
|
|
|
|
|
+ $starSize = $this->generateStarSize($starType);
|
|
|
|
|
+ $system->setStarSize($starSize);
|
|
|
|
|
+
|
|
|
|
|
+ // Générer un nom de système (temporaire)
|
|
|
|
|
+ $system->setName($this->generateSystemName());
|
|
|
|
|
+
|
|
|
|
|
+ // 2. Selon le type d'étoile, déterminer s'il y a des planètes
|
|
|
|
|
+ if ($this->shouldHavePlanets($starType)) {
|
|
|
|
|
+ // 3. Ajouter des planètes de manière réaliste
|
|
|
|
|
+ $this->generatePlanets($system, $starType);
|
|
|
|
|
+
|
|
|
|
|
+ // 5. Ajouter une ceinture d'astéroïdes si approprié
|
|
|
|
|
+ if ($this->shouldHaveAsteroidBelt($system->getPlanets()->count())) {
|
|
|
|
|
+ $system->setAsteroidBeltPosition($this->generateAsteroidBeltPosition($system->getPlanets()->count()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $system;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function generateRandomStarType(): StarTypeEnum
|
|
|
|
|
+ {
|
|
|
|
|
+ $rand = mt_rand(1, 1000);
|
|
|
|
|
+
|
|
|
|
|
+ // Proportions approximatives observées dans la galaxie
|
|
|
|
|
+ if ($rand <= 750) return StarTypeEnum::RED_DWARF; // ~75%
|
|
|
|
|
+ if ($rand <= 870) return StarTypeEnum::ORANGE_DWARF; // ~12%
|
|
|
|
|
+ if ($rand <= 940) return StarTypeEnum::YELLOW_DWARF; // ~7%
|
|
|
|
|
+ if ($rand <= 970) return StarTypeEnum::F_STAR; // ~3%
|
|
|
|
|
+ if ($rand <= 990) return StarTypeEnum::A_STAR; // ~2%
|
|
|
|
|
+ if ($rand <= 998) return StarTypeEnum::SUB_GIANT; // ~0.8%
|
|
|
|
|
+ return StarTypeEnum::T_STAR; // ~0.2%
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function generateStarSize(StarTypeEnum $starType): CelestialBodySizeEnum
|
|
|
|
|
+ {
|
|
|
|
|
+ // Distribution réaliste des tailles selon le type d'étoile
|
|
|
|
|
+ $rand = mt_rand(1, 100);
|
|
|
|
|
+
|
|
|
|
|
+ return match($starType) {
|
|
|
|
|
+ StarTypeEnum::RED_DWARF => $rand <= 80 ? CelestialBodySizeEnum::S : CelestialBodySizeEnum::M,
|
|
|
|
|
+ StarTypeEnum::ORANGE_DWARF => $rand <= 60 ? CelestialBodySizeEnum::M : CelestialBodySizeEnum::L,
|
|
|
|
|
+ StarTypeEnum::YELLOW_DWARF => $rand <= 70 ? CelestialBodySizeEnum::M : CelestialBodySizeEnum::L,
|
|
|
|
|
+ StarTypeEnum::F_STAR => $rand <= 40 ? CelestialBodySizeEnum::M : CelestialBodySizeEnum::L,
|
|
|
|
|
+ StarTypeEnum::A_STAR => $rand <= 20 ? CelestialBodySizeEnum::L : CelestialBodySizeEnum::XL,
|
|
|
|
|
+ StarTypeEnum::SUB_GIANT => $rand <= 30 ? CelestialBodySizeEnum::L : CelestialBodySizeEnum::XL,
|
|
|
|
|
+ StarTypeEnum::T_STAR => $rand <= 10 ? CelestialBodySizeEnum::XL : CelestialBodySizeEnum::XXL,
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function shouldHavePlanets(StarTypeEnum $starType): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ $rand = mt_rand(1, 100);
|
|
|
|
|
+
|
|
|
|
|
+ // Probabilité d'avoir des planètes selon le type d'étoile
|
|
|
|
|
+ return match($starType) {
|
|
|
|
|
+ StarTypeEnum::RED_DWARF => $rand <= 85, // 85% de chance
|
|
|
|
|
+ StarTypeEnum::ORANGE_DWARF => $rand <= 90, // 90% de chance
|
|
|
|
|
+ StarTypeEnum::YELLOW_DWARF => $rand <= 95, // 95% de chance
|
|
|
|
|
+ StarTypeEnum::F_STAR => $rand <= 80, // 80% de chance
|
|
|
|
|
+ StarTypeEnum::A_STAR => $rand <= 60, // 60% de chance
|
|
|
|
|
+ StarTypeEnum::SUB_GIANT => $rand <= 40, // 40% de chance
|
|
|
|
|
+ StarTypeEnum::T_STAR => $rand <= 20, // 20% de chance
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function generatePlanets(System $system, StarTypeEnum $starType): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $maxPlanets = $this->getMaxPlanets($starType);
|
|
|
|
|
+ $numPlanets = mt_rand(1, $maxPlanets);
|
|
|
|
|
+
|
|
|
|
|
+ for ($position = 1; $position <= $numPlanets; $position++) {
|
|
|
|
|
+ $planet = $this->createPlanet($position, $starType, $numPlanets);
|
|
|
|
|
+ $system->addPlanet($planet);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4. Rendre certaines planètes habitables/terraformables
|
|
|
|
|
+ $this->assignHabitability($system, $starType);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function getMaxPlanets(StarTypeEnum $starType): int
|
|
|
|
|
+ {
|
|
|
|
|
+ return match($starType) {
|
|
|
|
|
+ StarTypeEnum::RED_DWARF => mt_rand(1, 4), // 1-4 planètes
|
|
|
|
|
+ StarTypeEnum::ORANGE_DWARF => mt_rand(2, 6), // 2-6 planètes
|
|
|
|
|
+ StarTypeEnum::YELLOW_DWARF => mt_rand(3, 8), // 3-8 planètes
|
|
|
|
|
+ StarTypeEnum::F_STAR => mt_rand(2, 7), // 2-7 planètes
|
|
|
|
|
+ StarTypeEnum::A_STAR => mt_rand(1, 5), // 1-5 planètes
|
|
|
|
|
+ StarTypeEnum::SUB_GIANT => mt_rand(1, 3), // 1-3 planètes
|
|
|
|
|
+ StarTypeEnum::T_STAR => mt_rand(1, 2), // 1-2 planètes
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function createPlanet(int $position, StarTypeEnum $starType, int $totalPlanets): Planet
|
|
|
|
|
+ {
|
|
|
|
|
+ $planet = new Planet();
|
|
|
|
|
+ $planet->setPosition($position);
|
|
|
|
|
+
|
|
|
|
|
+ // Déterminer le type de planète selon la position
|
|
|
|
|
+ $planetType = $this->getPlanetType($position, $totalPlanets, $starType);
|
|
|
|
|
+ $planet->setType($planetType);
|
|
|
|
|
+
|
|
|
|
|
+ // Déterminer la taille de la planète
|
|
|
|
|
+ $planetSize = $this->getPlanetSize($planetType);
|
|
|
|
|
+ $planet->setSize($planetSize);
|
|
|
|
|
+
|
|
|
|
|
+ // Nom temporaire
|
|
|
|
|
+ $planet->setName("Planet $position");
|
|
|
|
|
+
|
|
|
|
|
+ // Habitabilité par défaut (sera modifiée par assignHabitability)
|
|
|
|
|
+ $planet->setHabitability(PlanetHabitabilityEnum::UNINHABITABLE);
|
|
|
|
|
+
|
|
|
|
|
+ return $planet;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function getPlanetType(int $position, int $totalPlanets, StarTypeEnum $starType): PlanetTypeEnum
|
|
|
|
|
+ {
|
|
|
|
|
+ $innerZone = ceil($totalPlanets * 0.4); // 40% des positions sont la zone interne
|
|
|
|
|
+ $middleZone = ceil($totalPlanets * 0.7); // 70% des positions sont zone interne + moyenne
|
|
|
|
|
+
|
|
|
|
|
+ if ($position <= $innerZone) {
|
|
|
|
|
+ // Zone interne : principalement des planètes telluriques
|
|
|
|
|
+ return mt_rand(1, 100) <= 85 ? PlanetTypeEnum::TERRESTRIAL : PlanetTypeEnum::DWARF;
|
|
|
|
|
+ } elseif ($position <= $middleZone) {
|
|
|
|
|
+ // Zone moyenne : mélange de types
|
|
|
|
|
+ $rand = mt_rand(1, 100);
|
|
|
|
|
+ if ($rand <= 40) return PlanetTypeEnum::TERRESTRIAL;
|
|
|
|
|
+ if ($rand <= 70) return PlanetTypeEnum::GAS;
|
|
|
|
|
+ return PlanetTypeEnum::ICE;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Zone externe : principalement des géantes gazeuses et planètes glacées/naines
|
|
|
|
|
+ $rand = mt_rand(1, 100);
|
|
|
|
|
+ if ($rand <= 45) return PlanetTypeEnum::GAS;
|
|
|
|
|
+ if ($rand <= 75) return PlanetTypeEnum::ICE;
|
|
|
|
|
+ return PlanetTypeEnum::DWARF;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function getPlanetSize(PlanetTypeEnum $type): CelestialBodySizeEnum
|
|
|
|
|
+ {
|
|
|
|
|
+ $rand = mt_rand(1, 100);
|
|
|
|
|
+
|
|
|
|
|
+ return match($type) {
|
|
|
|
|
+ PlanetTypeEnum::TERRESTRIAL => match(true) {
|
|
|
|
|
+ $rand <= 30 => CelestialBodySizeEnum::S,
|
|
|
|
|
+ $rand <= 70 => CelestialBodySizeEnum::M,
|
|
|
|
|
+ default => CelestialBodySizeEnum::L
|
|
|
|
|
+ },
|
|
|
|
|
+ PlanetTypeEnum::GAS => match(true) {
|
|
|
|
|
+ $rand <= 20 => CelestialBodySizeEnum::L,
|
|
|
|
|
+ $rand <= 60 => CelestialBodySizeEnum::XL,
|
|
|
|
|
+ default => CelestialBodySizeEnum::XXL
|
|
|
|
|
+ },
|
|
|
|
|
+ PlanetTypeEnum::ICE => match(true) {
|
|
|
|
|
+ $rand <= 50 => CelestialBodySizeEnum::S,
|
|
|
|
|
+ $rand <= 80 => CelestialBodySizeEnum::M,
|
|
|
|
|
+ default => CelestialBodySizeEnum::L
|
|
|
|
|
+ },
|
|
|
|
|
+ PlanetTypeEnum::DWARF => match(true) {
|
|
|
|
|
+ $rand <= 70 => CelestialBodySizeEnum::XS,
|
|
|
|
|
+ default => CelestialBodySizeEnum::S
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function assignHabitability(System $system, StarTypeEnum $starType): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $planets = $system->getPlanets()->toArray();
|
|
|
|
|
+ $totalPlanets = count($planets);
|
|
|
|
|
+
|
|
|
|
|
+ if ($totalPlanets === 0) return;
|
|
|
|
|
+
|
|
|
|
|
+ // Déterminer la zone habitable selon le type d'étoile
|
|
|
|
|
+ $habitableZone = $this->getHabitableZone($starType, $totalPlanets);
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($planets as $planet) {
|
|
|
|
|
+ if ($planet->getType() === PlanetTypeEnum::TERRESTRIAL) {
|
|
|
|
|
+ $habitability = $this->calculateHabitability($planet->getPosition(), $habitableZone, $starType);
|
|
|
|
|
+ $planet->setHabitability($habitability);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function getHabitableZone(StarTypeEnum $starType, int $totalPlanets): array
|
|
|
|
|
+ {
|
|
|
|
|
+ // Retourne [min, max] des positions dans la zone habitable
|
|
|
|
|
+ return match($starType) {
|
|
|
|
|
+ StarTypeEnum::RED_DWARF => [1, min(2, $totalPlanets)], // Zone très proche
|
|
|
|
|
+ StarTypeEnum::ORANGE_DWARF => [2, min(4, $totalPlanets)], // Zone proche-moyenne
|
|
|
|
|
+ StarTypeEnum::YELLOW_DWARF => [2, min(5, $totalPlanets)], // Zone étendue
|
|
|
|
|
+ StarTypeEnum::F_STAR => [3, min(6, $totalPlanets)], // Zone plus lointaine
|
|
|
|
|
+ StarTypeEnum::A_STAR => [4, min(7, $totalPlanets)], // Zone très lointaine
|
|
|
|
|
+ StarTypeEnum::SUB_GIANT => [3, min(5, $totalPlanets)], // Zone variable
|
|
|
|
|
+ StarTypeEnum::T_STAR => [5, min(8, $totalPlanets)], // Zone très éloignée
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function calculateHabitability(int $position, array $habitableZone, StarTypeEnum $starType): PlanetHabitabilityEnum
|
|
|
|
|
+ {
|
|
|
|
|
+ [$minHabitable, $maxHabitable] = $habitableZone;
|
|
|
|
|
+ $rand = mt_rand(1, 100);
|
|
|
|
|
+
|
|
|
|
|
+ if ($position >= $minHabitable && $position <= $maxHabitable) {
|
|
|
|
|
+ // Dans la zone habitable
|
|
|
|
|
+ $habitabilityChance = match($starType) {
|
|
|
|
|
+ StarTypeEnum::RED_DWARF => 30, // 30% de chance d'être habitable
|
|
|
|
|
+ StarTypeEnum::ORANGE_DWARF => 40, // 40% de chance
|
|
|
|
|
+ StarTypeEnum::YELLOW_DWARF => 50, // 50% de chance
|
|
|
|
|
+ StarTypeEnum::F_STAR => 35, // 35% de chance
|
|
|
|
|
+ StarTypeEnum::A_STAR => 20, // 20% de chance
|
|
|
|
|
+ StarTypeEnum::SUB_GIANT => 15, // 15% de chance
|
|
|
|
|
+ StarTypeEnum::T_STAR => 10, // 10% de chance
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if ($rand <= $habitabilityChance) {
|
|
|
|
|
+ return PlanetHabitabilityEnum::INHABITABLE;
|
|
|
|
|
+ } elseif ($rand <= $habitabilityChance + 25) {
|
|
|
|
|
+ return PlanetHabitabilityEnum::TERRAFORMABLE;
|
|
|
|
|
+ }
|
|
|
|
|
+ } elseif (abs($position - ($minHabitable + $maxHabitable) / 2) <= 1) {
|
|
|
|
|
+ // Proche de la zone habitable
|
|
|
|
|
+ if ($rand <= 15) {
|
|
|
|
|
+ return PlanetHabitabilityEnum::TERRAFORMABLE;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return PlanetHabitabilityEnum::UNINHABITABLE;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function shouldHaveAsteroidBelt(int $numPlanets): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ // Plus il y a de planètes, plus il y a de chances d'avoir une ceinture d'astéroïdes
|
|
|
|
|
+ $chance = min(30 + ($numPlanets * 10), 70); // Entre 30% et 70% selon le nombre de planètes
|
|
|
|
|
+ return mt_rand(1, 100) <= $chance;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function generateAsteroidBeltPosition(int $numPlanets): int
|
|
|
|
|
+ {
|
|
|
|
|
+ // La ceinture d'astéroïdes se trouve généralement entre les planètes internes et externes
|
|
|
|
|
+ $minPosition = max(2, (int)ceil($numPlanets * 0.3));
|
|
|
|
|
+ $maxPosition = max($minPosition, min($numPlanets + 1, (int)ceil($numPlanets * 0.7)));
|
|
|
|
|
+
|
|
|
|
|
+ return mt_rand($minPosition, $maxPosition);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function generateSystemName(): string
|
|
|
|
|
+ {
|
|
|
|
|
+ // Générateur de noms temporaire - pourrait être amélioré avec des noms plus réalistes
|
|
|
|
|
+ $prefixes = ['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta', 'Eta', 'Theta'];
|
|
|
|
|
+ $suffixes = ['Centauri', 'Draconis', 'Aquilae', 'Orionis', 'Virginis', 'Leonis', 'Ursae', 'Cygni'];
|
|
|
|
|
+
|
|
|
|
|
+ return $prefixes[array_rand($prefixes)] . ' ' . $suffixes[array_rand($suffixes)] . ' ' . mt_rand(1, 999);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|