瀏覽代碼

Adds galaxy generation command

Implements a command to generate new galaxies with a specified number of sectors.
Includes options for setting the galaxy name and sector count.
Adds a factory service to handle galaxy, sector, and system creation
with realistic distributions of star types, planet types, and habitability.
The command provides detailed generation statistics.

Also modifies entities and enums to reflect the generation logic and
improve data modeling for celestial bodies.
Specifically:
- Replaces PlanetTypeEnum for habitability with PlanetHabitabilityEnum
- Modifies cascade options on Galaxy, Sector and System entities
- Adds X and Y coordinates for Sector and System
olinox14 2 月之前
父節點
當前提交
9cd179f5fb

+ 119 - 0
api/src/Commands/GalaxyGenerateCommand.php

@@ -0,0 +1,119 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Commands;
+
+use App\Services\GalaxyFactory;
+use Doctrine\ORM\EntityManagerInterface;
+use Symfony\Component\Console\Attribute\AsCommand;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+#[AsCommand(
+    name: 'astra:galaxy:generate',
+    description: 'Génère une nouvelle galaxie avec le nombre de secteurs spécifié'
+)]
+class GalaxyGenerateCommand extends Command
+{
+    public function __construct(
+        private readonly GalaxyFactory $galaxyFactory,
+        private readonly EntityManagerInterface $entityManager
+    ) {
+        parent::__construct();
+    }
+
+    protected function configure(): void
+    {
+        $this
+            ->addOption(
+                'sectors',
+                's',
+                InputOption::VALUE_OPTIONAL,
+                'Nombre de secteurs à générer pour la galaxie',
+                1000
+            )
+            ->addOption(
+                'name',
+                null,
+                InputOption::VALUE_OPTIONAL,
+                'Nom de la galaxie à générer',
+                'Galaxy'
+            )
+            ->setHelp('Cette commande génère une nouvelle galaxie avec le nombre de secteurs spécifié.');
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $io = new SymfonyStyle($input, $output);
+
+        $sectors = (int) $input->getOption('sectors');
+        $name = $input->getOption('name');
+
+        if (!$name) {
+            $io->error('Le nom de la galaxie est requis. Utilisez l\'option --name');
+            return Command::FAILURE;
+        }
+
+        if ($sectors <= 0) {
+            $io->error('Le nombre de secteurs doit être supérieur à 0');
+            return Command::FAILURE;
+        }
+
+        // Vérifier si une galaxie avec ce nom existe déjà
+        $existingGalaxy = $this->entityManager->getRepository(\App\Entity\Galaxy::class)
+            ->findOneBy(['name' => $name]);
+
+        if ($existingGalaxy) {
+            $io->error(sprintf('Une galaxie avec le nom "%s" existe déjà', $name));
+            return Command::FAILURE;
+        }
+
+        $io->title('Génération de la galaxie');
+        $io->text(sprintf('Nom : %s', $name));
+        $io->text(sprintf('Nombre de secteurs : %d', $sectors));
+
+        // Créer la galaxie
+        $galaxy = $this->galaxyFactory->createGalaxy($sectors);
+        $galaxy->setName($name);
+
+        // Sauvegarder en base de données
+        $io->text('Sauvegarde en base de données...');
+
+        $this->entityManager->persist($galaxy);
+        $this->entityManager->flush();
+
+        $io->success(sprintf(
+            'Galaxie "%s" générée avec succès ! %d secteurs créés avec %d systèmes au total.',
+            $name,
+            $galaxy->getSectors()->count(),
+            $galaxy->getSectors()->count() * GalaxyFactory::SYSTEMS_PER_SECTOR
+        ));
+
+        // Statistiques détaillées
+        $totalSystems = 0;
+        $totalPlanets = 0;
+
+        foreach ($galaxy->getSectors() as $sector) {
+            $totalSystems += $sector->getSystems()->count();
+            foreach ($sector->getSystems() as $system) {
+                $totalPlanets += $system->getPlanets()->count();
+            }
+        }
+
+        $io->section('Statistiques de génération');
+        $io->table(
+            ['Élément', 'Quantité'],
+            [
+                ['Secteurs', $galaxy->getSectors()->count()],
+                ['Systèmes', $totalSystems],
+                ['Planètes', $totalPlanets],
+                ['ID de la galaxie', $galaxy->getId()],
+            ]
+        );
+
+        return Command::SUCCESS;
+    }
+}

+ 2 - 2
api/src/Controller/Admin/PlanetCrudController.php

@@ -4,7 +4,7 @@ namespace App\Controller\Admin;
 
 use App\Entity\Planet;
 use App\Enum\CelestialBodySizeEnum;
-use App\Enum\PlanetTypeEnum;
+use App\Enum\PlanetHabitabilityEnum;
 use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
 use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
 use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
@@ -23,7 +23,7 @@ class PlanetCrudController extends AbstractCrudController
         return [
             IdField::new('id')->onlyOnIndex(),
             TextField::new('name'),
-            ChoiceField::new('type')->setChoices($this->enumChoices(PlanetTypeEnum::class))->renderAsBadges(),
+            ChoiceField::new('type')->setChoices($this->enumChoices(PlanetHabitabilityEnum::class))->renderAsBadges(),
             IntegerField::new('position'),
             ChoiceField::new('size')->setChoices($this->enumChoices(CelestialBodySizeEnum::class))->renderAsBadges(),
         ];

+ 1 - 1
api/src/Entity/Galaxy.php

@@ -21,7 +21,7 @@ class Galaxy
     private string $name;
 
     /** @var Collection<int, Sector> */
-    #[ORM\OneToMany(targetEntity: Sector::class, mappedBy: 'galaxy', orphanRemoval: true)]
+    #[ORM\OneToMany(targetEntity: Sector::class, mappedBy: 'galaxy', orphanRemoval: true, cascade: ['persist', 'remove'])]
     private Collection $sectors;
 
     #[ORM\OneToOne(inversedBy: 'galaxy', targetEntity: Game::class)]

+ 17 - 2
api/src/Entity/Planet.php

@@ -5,6 +5,7 @@ namespace App\Entity;
 
 use ApiPlatform\Metadata\ApiResource;
 use App\Enum\CelestialBodySizeEnum;
+use App\Enum\PlanetHabitabilityEnum;
 use App\Enum\PlanetTypeEnum;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
@@ -25,10 +26,13 @@ class Planet
     private string $name;
 
     #[ORM\ManyToOne(targetEntity: System::class, inversedBy: 'planets')]
-    #[ORM\JoinColumn(nullable: true)]
+    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
     private ?System $system = null;
 
-    #[ORM\Column(length: 24, enumType: PlanetTypeEnum::class, options: ['default' => PlanetTypeEnum::UNINHABITABLE])]
+    #[ORM\Column(length: 24, enumType: PlanetHabitabilityEnum::class, options: ['default' => PlanetHabitabilityEnum::UNINHABITABLE])]
+    private PlanetHabitabilityEnum $habitability;
+
+    #[ORM\Column(length: 24, enumType: PlanetTypeEnum::class, options: ['default' => PlanetTypeEnum::TERRESTRIAL])]
     private PlanetTypeEnum $type;
 
     #[ORM\Column(nullable: true)]
@@ -74,6 +78,17 @@ class Planet
         return $this;
     }
 
+    public function getHabitability(): PlanetHabitabilityEnum
+    {
+        return $this->habitability;
+    }
+
+    public function setHabitability(PlanetHabitabilityEnum $habitability): self
+    {
+        $this->habitability = $habitability;
+        return $this;
+    }
+
     public function getType(): PlanetTypeEnum
     {
         return $this->type;

+ 44 - 2
api/src/Entity/Sector.php

@@ -20,20 +20,29 @@ class Sector
     #[ORM\Column]
     private ?int $id = null;
 
+    #[ORM\Column(nullable: true)]
+    private ?int $number = null;
+
     #[ORM\Column(length: 100, nullable: false)]
     private string $name;
 
     /** @var Collection<int, System> */
-    #[ORM\OneToMany(targetEntity: System::class, mappedBy: 'sector', orphanRemoval: true)]
+    #[ORM\OneToMany(targetEntity: System::class, mappedBy: 'sector', orphanRemoval: true, cascade: ['persist', 'remove'])]
     private Collection $systems;
 
     #[ORM\ManyToOne(targetEntity: Galaxy::class, inversedBy: 'sectors')]
-    #[ORM\JoinColumn(nullable: true)]
+    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
     private ?Galaxy $galaxy = null;
 
     #[ORM\Column(length: 24, enumType: SectorStatusEnum::class, options: ['default' => SectorStatusEnum::UNEXPLORED])]
     private SectorStatusEnum $status = SectorStatusEnum::UNEXPLORED;
 
+    #[ORM\Column(nullable: true)]
+    private ?int $x = null;
+
+    #[ORM\Column(nullable: true)]
+    private ?int $y = null;
+
     public function __construct()
     {
         $this->systems = new ArrayCollection();
@@ -50,6 +59,17 @@ class Sector
         return $this;
     }
 
+    public function getNumber(): ?int
+    {
+        return $this->number;
+    }
+
+    public function setNumber(?int $number): self
+    {
+        $this->number = $number;
+        return $this;
+    }
+
     public function getName(): string
     {
         return $this->name;
@@ -115,4 +135,26 @@ class Sector
         $this->status = $status;
         return $this;
     }
+
+    public function getX(): ?int
+    {
+        return $this->x;
+    }
+
+    public function setX(?int $x): self
+    {
+        $this->x = $x;
+        return $this;
+    }
+
+    public function getY(): ?int
+    {
+        return $this->y;
+    }
+
+    public function setY(?int $y): self
+    {
+        $this->y = $y;
+        return $this;
+    }
 }

+ 30 - 2
api/src/Entity/System.php

@@ -25,11 +25,11 @@ class System
     private string $name;
 
     /** @var Collection<int, Planet> */
-    #[ORM\OneToMany(targetEntity: Planet::class, mappedBy: 'system', orphanRemoval: true)]
+    #[ORM\OneToMany(targetEntity: Planet::class, mappedBy: 'system', cascade: ['persist', 'remove'], orphanRemoval: true)]
     private Collection $planets;
 
     #[ORM\ManyToOne(targetEntity: Sector::class, inversedBy: 'systems')]
-    #[ORM\JoinColumn(nullable: true)]
+    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
     private ?Sector $sector = null;
 
     #[ORM\Column(length: 20, enumType: StarTypeEnum::class, options: ['default' => StarTypeEnum::RED_DWARF])]
@@ -42,6 +42,12 @@ class System
     #[Assert\Range(notInRangeMessage: 'between_{{ min }}_and_{{ max }}', min: 0, max: 7)]
     private int $asteroidBeltPosition = 0;
 
+    #[ORM\Column(nullable: true)]
+    private ?int $x = null;
+
+    #[ORM\Column(nullable: true)]
+    private ?int $y = null;
+
     public function __construct()
     {
         $this->planets = new ArrayCollection();
@@ -145,4 +151,26 @@ class System
         $this->asteroidBeltPosition = $asteroidBeltPosition;
         return $this;
     }
+
+    public function getX(): ?int
+    {
+        return $this->x;
+    }
+
+    public function setX(?int $x): self
+    {
+        $this->x = $x;
+        return $this;
+    }
+
+    public function getY(): ?int
+    {
+        return $this->y;
+    }
+
+    public function setY(?int $y): self
+    {
+        $this->y = $y;
+        return $this;
+    }
 }

+ 11 - 0
api/src/Enum/PlanetHabitabilityEnum.php

@@ -0,0 +1,11 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Enum;
+
+enum PlanetHabitabilityEnum: string
+{
+    case INHABITABLE = 'INHABITABLE';
+    case TERRAFORMABLE = 'TERRAFORMABLE';
+    case UNINHABITABLE = 'UNINHABITABLE';
+}

+ 4 - 3
api/src/Enum/PlanetTypeEnum.php

@@ -5,7 +5,8 @@ namespace App\Enum;
 
 enum PlanetTypeEnum: string
 {
-    case INHABITABLE = 'INHABITABLE';
-    case TERRAFORMABLE = 'TERRAFORMABLE';
-    case UNINHABITABLE = 'UNINHABITABLE';
+    case TERRESTRIAL = 'TERRESTRIAL';
+    case GAS = 'GAS';
+    case ICE = 'ICE';
+    case DWARF = 'DWARF';
 }

+ 2 - 2
api/src/Enum/StarTypeEnum.php

@@ -10,6 +10,6 @@ enum StarTypeEnum: string
     case YELLOW_DWARF = 'YELLOW_DWARF';
     case SUB_GIANT = 'SUB_GIANT';
     case F_STAR = 'F_STAR';
-    case A_STAR = 'G_STAR';
-    case T_STAR = 'K_STAR';
+    case A_STAR = 'A_STAR';
+    case T_STAR = 'T_STAR';
 }

+ 281 - 3
api/src/Services/GalaxyFactory.php

@@ -4,25 +4,303 @@ declare(strict_types=1);
 namespace App\Services;
 
 use App\Entity\Galaxy;
+use App\Entity\Planet;
 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
 {
-    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->setName($name);
 
         for ($i = 0; $i < $nbSectors; $i++) {
-            $sector = $this->createSector();
+            $sector = $this->createSector($i + 1);
             $galaxy->addSector($sector);
         }
 
         return $galaxy;
     }
 
-    protected function createSector(): Sector {
+    protected function createSector(int $number): 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;
     }
+
+    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);
+    }
 }