Map.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. <?php
  2. /**
  3. * Classe Tile
  4. *
  5. * Représente une tuile avec des propriétés arbitraires.
  6. */
  7. if (!class_exists('Tile')) {
  8. class Tile
  9. {
  10. /**
  11. * @var array Les propriétés de la tuile
  12. */
  13. private array $properties;
  14. /**
  15. * Constructeur d'une tuile
  16. *
  17. * @param array $properties Les propriétés initiales de la tuile
  18. */
  19. public function __construct(array $properties = [])
  20. {
  21. $this->properties = $properties;
  22. }
  23. /**
  24. * Définit une propriété
  25. *
  26. * @param string $key La clé de la propriété
  27. * @param mixed $value La valeur de la propriété
  28. */
  29. public function setProperty(string $key, $value): void
  30. {
  31. $this->properties[$key] = $value;
  32. }
  33. /**
  34. * Récupère une propriété
  35. *
  36. * @param string $key La clé de la propriété
  37. * @param mixed $default La valeur par défaut si la propriété n'existe pas
  38. * @return mixed La valeur de la propriété
  39. */
  40. public function getProperty(string $key, $default = null)
  41. {
  42. return $this->properties[$key] ?? $default;
  43. }
  44. /**
  45. * Vérifie si une propriété existe
  46. *
  47. * @param string $key La clé de la propriété
  48. * @return bool True si la propriété existe
  49. */
  50. public function hasProperty(string $key): bool
  51. {
  52. return array_key_exists($key, $this->properties);
  53. }
  54. /**
  55. * Supprime une propriété
  56. *
  57. * @param string $key La clé de la propriété
  58. */
  59. public function removeProperty(string $key): void
  60. {
  61. unset($this->properties[$key]);
  62. }
  63. /**
  64. * Récupère toutes les propriétés
  65. *
  66. * @return array Toutes les propriétés
  67. */
  68. public function getAllProperties(): array
  69. {
  70. return $this->properties;
  71. }
  72. /**
  73. * Définit toutes les propriétés
  74. *
  75. * @param array $properties Les nouvelles propriétés
  76. */
  77. public function setAllProperties(array $properties): void
  78. {
  79. $this->properties = $properties;
  80. }
  81. /**
  82. * Vérifie si la tuile est vide (aucune propriété)
  83. *
  84. * @return bool True si la tuile est vide
  85. */
  86. public function isEmpty(): bool
  87. {
  88. return empty($this->properties);
  89. }
  90. /**
  91. * Convertit la tuile en tableau pour la sérialisation
  92. *
  93. * @return array Les données de la tuile
  94. */
  95. public function toArray(): array
  96. {
  97. return $this->properties;
  98. }
  99. /**
  100. * Crée une tuile depuis un tableau
  101. *
  102. * @param array $data Les données de la tuile
  103. * @return Tile La tuile créée
  104. */
  105. public static function fromArray(array $data): Tile
  106. {
  107. return new self($data);
  108. }
  109. /**
  110. * Méthodes d'accès rapide pour les propriétés communes
  111. */
  112. /**
  113. * Récupère le type de la tuile
  114. *
  115. * @return string|null Le type ou null
  116. */
  117. public function getType(): ?string
  118. {
  119. return $this->getProperty('type');
  120. }
  121. /**
  122. * Définit le type de la tuile
  123. *
  124. * @param string $type Le type
  125. */
  126. public function setType(string $type): void
  127. {
  128. $this->setProperty('type', $type);
  129. }
  130. /**
  131. * Récupère les ressources de la tuile
  132. *
  133. * @return array Les ressources
  134. */
  135. public function getResources(): array
  136. {
  137. return $this->getProperty('resources', []);
  138. }
  139. /**
  140. * Définit les ressources de la tuile
  141. *
  142. * @param array $resources Les ressources
  143. */
  144. public function setResources(array $resources): void
  145. {
  146. $this->setProperty('resources', $resources);
  147. }
  148. /**
  149. * Récupère la température de la tuile
  150. *
  151. * @return float|null La température ou null
  152. */
  153. public function getTemperature(): ?float
  154. {
  155. return $this->getProperty('temperature');
  156. }
  157. /**
  158. * Définit la température de la tuile
  159. *
  160. * @param float $temperature La température
  161. */
  162. public function setTemperature(float $temperature): void
  163. {
  164. $this->setProperty('temperature', $temperature);
  165. }
  166. /**
  167. * Récupère l'élévation de la tuile
  168. *
  169. * @return int|null L'élévation ou null
  170. */
  171. public function getElevation(): ?int
  172. {
  173. return $this->getProperty('elevation');
  174. }
  175. /**
  176. * Définit l'élévation de la tuile
  177. *
  178. * @param int $elevation L'élévation
  179. */
  180. public function setElevation(int $elevation): void
  181. {
  182. $this->setProperty('elevation', $elevation);
  183. }
  184. }
  185. }
  186. /**
  187. * Classe Map
  188. *
  189. * Représente une carte composée d'hexagones (damier hexagonal).
  190. * Chaque hexagone est une parcelle pouvant contenir une tuile avec des propriétés arbitraires.
  191. */
  192. if (!class_exists('Map')) {
  193. class Map
  194. {
  195. /**
  196. * @var array Les hexagones de la carte indexés par coordonnées [q][r]
  197. */
  198. private array $hexagons = [];
  199. /**
  200. * @var string Le nom de la carte
  201. */
  202. private string $name;
  203. /**
  204. * @var int La largeur de la carte (nombre d'hexagones en largeur)
  205. */
  206. private int $width;
  207. /**
  208. * @var int La hauteur de la carte (nombre d'hexagones en hauteur)
  209. */
  210. private int $height;
  211. /**
  212. * @var string La date de création de la carte
  213. */
  214. private string $createdAt;
  215. /**
  216. * @var string La date de dernière modification
  217. */
  218. private string $updatedAt;
  219. /**
  220. * Constructeur de la carte
  221. *
  222. * @param string $name Le nom de la carte
  223. * @param int $width La largeur de la carte (en nombre d'hexagones)
  224. * @param int $height La hauteur de la carte (en nombre d'hexagones)
  225. */
  226. public function __construct(string $name, int $width = 10, int $height = 10)
  227. {
  228. $this->name = $name;
  229. $this->width = $width;
  230. $this->height = $height;
  231. $this->createdAt = date('Y-m-d H:i:s');
  232. $this->updatedAt = date('Y-m-d H:i:s');
  233. // Initialiser la carte avec des hexagones vides
  234. $this->initializeMap();
  235. }
  236. /**
  237. * Initialise la carte avec des hexagones vides
  238. */
  239. private function initializeMap(): void
  240. {
  241. for ($q = 0; $q < $this->width; $q++) {
  242. for ($r = 0; $r < $this->height; $r++) {
  243. $this->hexagons[$q][$r] = new Hexagon($q, $r, new Tile(['type' => 'empty']));
  244. }
  245. }
  246. }
  247. /**
  248. * Définit la tuile d'un hexagone
  249. *
  250. * @param int $q Coordonnée Q de l'hexagone
  251. * @param int $r Coordonnée R de l'hexagone
  252. * @param Tile $tile La tuile à placer
  253. * @return bool True si l'hexagone a été modifié, false sinon
  254. */
  255. public function setTile(int $q, int $r, Tile $tile): bool
  256. {
  257. if (!$this->isValidCoordinate($q, $r)) {
  258. return false;
  259. }
  260. $this->hexagons[$q][$r]->setTile($tile);
  261. $this->updatedAt = date('Y-m-d H:i:s');
  262. return true;
  263. }
  264. /**
  265. * Récupère la tuile d'un hexagone
  266. *
  267. * @param int $q Coordonnée Q de l'hexagone
  268. * @param int $r Coordonnée R de l'hexagone
  269. * @return Tile|null La tuile ou null si les coordonnées sont invalides
  270. */
  271. public function getTile(int $q, int $r): ?Tile
  272. {
  273. if (!$this->isValidCoordinate($q, $r)) {
  274. return null;
  275. }
  276. return $this->hexagons[$q][$r]->getTile();
  277. }
  278. /**
  279. * Récupère un hexagone
  280. *
  281. * @param int $q Coordonnée Q de l'hexagone
  282. * @param int $r Coordonnée R de l'hexagone
  283. * @return Hexagon|null L'hexagone ou null si les coordonnées sont invalides
  284. */
  285. public function getHexagon(int $q, int $r): ?Hexagon
  286. {
  287. if (!$this->isValidCoordinate($q, $r)) {
  288. return null;
  289. }
  290. return $this->hexagons[$q][$r];
  291. }
  292. /**
  293. * Vérifie si les coordonnées sont valides
  294. *
  295. * @param int $q Coordonnée Q
  296. * @param int $r Coordonnée R
  297. * @return bool True si les coordonnées sont valides
  298. */
  299. public function isValidCoordinate(int $q, int $r): bool
  300. {
  301. return isset($this->hexagons[$q][$r]);
  302. }
  303. /**
  304. * Récupère tous les hexagones de la carte
  305. *
  306. * @return array Les hexagones indexés par [q][r]
  307. */
  308. public function getAllHexagons(): array
  309. {
  310. return $this->hexagons;
  311. }
  312. /**
  313. * Récupère les hexagones d'une ligne spécifique
  314. *
  315. * @param int $q La coordonnée Q de la ligne
  316. * @return array|null Les hexagones de la ligne ou null si la ligne n'existe pas
  317. */
  318. public function getHexagonsRow(int $q): ?array
  319. {
  320. return $this->hexagons[$q] ?? null;
  321. }
  322. /**
  323. * Compte le nombre d'hexagones d'un certain type
  324. *
  325. * @param string $tileType Le type de tuile à compter
  326. * @return int Le nombre d'hexagones de ce type
  327. */
  328. public function countTiles(string $tileType): int
  329. {
  330. $count = 0;
  331. foreach ($this->hexagons as $row) {
  332. foreach ($row as $hexagon) {
  333. $tile = $hexagon->getTile();
  334. if ($tile && $tile->getType() === $tileType) {
  335. $count++;
  336. }
  337. }
  338. }
  339. return $count;
  340. }
  341. /**
  342. * Compte le nombre d'hexagones vides
  343. *
  344. * @return int Le nombre d'hexagones vides
  345. */
  346. public function countEmptyTiles(): int
  347. {
  348. $count = 0;
  349. foreach ($this->hexagons as $row) {
  350. foreach ($row as $hexagon) {
  351. if ($hexagon->isEmpty()) {
  352. $count++;
  353. }
  354. }
  355. }
  356. return $count;
  357. }
  358. /**
  359. * Récupère les statistiques de la carte
  360. *
  361. * @return array Les statistiques (nombre total d'hexagones, répartition par type)
  362. */
  363. public function getStatistics(): array
  364. {
  365. $totalHexagons = $this->width * $this->height;
  366. $tileTypeCounts = [];
  367. $emptyCount = $this->countEmptyTiles();
  368. // Compter les types de tuiles présents
  369. foreach ($this->hexagons as $row) {
  370. foreach ($row as $hexagon) {
  371. $tile = $hexagon->getTile();
  372. if ($tile && !$tile->isEmpty()) {
  373. $type = $tile->getType();
  374. if ($type !== null) {
  375. $tileTypeCounts[$type] = ($tileTypeCounts[$type] ?? 0) + 1;
  376. }
  377. }
  378. }
  379. }
  380. return [
  381. 'total_hexagons' => $totalHexagons,
  382. 'width' => $this->width,
  383. 'height' => $this->height,
  384. 'tile_type_counts' => $tileTypeCounts,
  385. 'empty_count' => $emptyCount,
  386. 'empty_percentage' => round(($emptyCount / $totalHexagons) * 100, 2),
  387. 'filled_count' => $totalHexagons - $emptyCount,
  388. 'filled_percentage' => round((($totalHexagons - $emptyCount) / $totalHexagons) * 100, 2)
  389. ];
  390. }
  391. /**
  392. * Convertit la carte en tableau pour la sérialisation JSON
  393. *
  394. * @return array Les données de la carte
  395. */
  396. public function toArray(): array
  397. {
  398. $hexagonsData = [];
  399. foreach ($this->hexagons as $q => $row) {
  400. foreach ($row as $r => $hexagon) {
  401. $hexagonsData[] = $hexagon->toArray();
  402. }
  403. }
  404. return [
  405. 'name' => $this->name,
  406. 'width' => $this->width,
  407. 'height' => $this->height,
  408. 'created_at' => $this->createdAt,
  409. 'updated_at' => $this->updatedAt,
  410. 'hexagons' => $hexagonsData
  411. ];
  412. }
  413. /**
  414. * Convertit la carte en JSON
  415. *
  416. * @param bool $prettyPrint True pour un JSON formaté
  417. * @return string Le JSON de la carte
  418. */
  419. public function toJson(bool $prettyPrint = false): string
  420. {
  421. return json_encode(
  422. $this->toArray(),
  423. $prettyPrint ? JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE : 0
  424. );
  425. }
  426. /**
  427. * Sauvegarde la carte dans un fichier JSON
  428. *
  429. * @param string $filePath Le chemin du fichier
  430. * @return bool True si la sauvegarde a réussi
  431. */
  432. public function saveToFile(string $filePath): bool
  433. {
  434. try {
  435. $json = $this->toJson(true);
  436. $result = file_put_contents($filePath, $json);
  437. return $result !== false;
  438. } catch (Exception $e) {
  439. AppDebugger::log('Erreur lors de la sauvegarde de la carte: ' . $e->getMessage(), 'ERROR');
  440. return false;
  441. }
  442. }
  443. /**
  444. * Charge une carte depuis un tableau
  445. *
  446. * @param array $data Les données de la carte
  447. * @return Map|null La carte chargée ou null en cas d'erreur
  448. */
  449. public static function fromArray(array $data): ?Map
  450. {
  451. try {
  452. $map = new self($data['name'], $data['width'], $data['height']);
  453. $map->createdAt = $data['created_at'] ?? date('Y-m-d H:i:s');
  454. $map->updatedAt = $data['updated_at'] ?? date('Y-m-d H:i:s');
  455. // Charger les hexagones
  456. foreach ($data['hexagons'] as $hexagonData) {
  457. $q = $hexagonData['q'];
  458. $r = $hexagonData['r'];
  459. if ($map->isValidCoordinate($q, $r)) {
  460. $map->hexagons[$q][$r] = Hexagon::fromArray($hexagonData);
  461. }
  462. }
  463. return $map;
  464. } catch (Exception $e) {
  465. AppDebugger::log('Erreur lors du chargement de la carte depuis un tableau: ' . $e->getMessage(), 'ERROR');
  466. return null;
  467. }
  468. }
  469. /**
  470. * Charge une carte depuis un fichier JSON
  471. *
  472. * @param string $filePath Le chemin du fichier
  473. * @return Map|null La carte chargée ou null en cas d'erreur
  474. */
  475. public static function fromFile(string $filePath): ?Map
  476. {
  477. try {
  478. if (!file_exists($filePath)) {
  479. return null;
  480. }
  481. $json = file_get_contents($filePath);
  482. $data = json_decode($json, true);
  483. if (json_last_error() !== JSON_ERROR_NONE) {
  484. AppDebugger::log('Erreur JSON lors du chargement du fichier: ' . json_last_error_msg(), 'ERROR');
  485. return null;
  486. }
  487. return self::fromArray($data);
  488. } catch (Exception $e) {
  489. AppDebugger::log('Erreur lors du chargement de la carte depuis un fichier: ' . $e->getMessage(), 'ERROR');
  490. return null;
  491. }
  492. }
  493. // Getters
  494. public function getName(): string { return $this->name; }
  495. public function getWidth(): int { return $this->width; }
  496. public function getHeight(): int { return $this->height; }
  497. public function getCreatedAt(): string { return $this->createdAt; }
  498. public function getUpdatedAt(): string { return $this->updatedAt; }
  499. // Setters
  500. public function setName(string $name): void
  501. {
  502. $this->name = $name;
  503. $this->updatedAt = date('Y-m-d H:i:s');
  504. }
  505. }
  506. }
  507. /**
  508. * Classe Hexagon
  509. *
  510. * Représente un hexagone individuel dans la carte.
  511. */
  512. if (!class_exists('Hexagon')) {
  513. class Hexagon
  514. {
  515. /**
  516. * @var int Coordonnée Q (axe horizontal)
  517. */
  518. private int $q;
  519. /**
  520. * @var int Coordonnée R (axe diagonal)
  521. */
  522. private int $r;
  523. /**
  524. * @var Tile|null La tuile de l'hexagone
  525. */
  526. private ?Tile $tile;
  527. /**
  528. * Constructeur d'un hexagone
  529. *
  530. * @param int $q Coordonnée Q
  531. * @param int $r Coordonnée R
  532. * @param Tile|null $tile La tuile initiale (null pour vide)
  533. */
  534. public function __construct(int $q, int $r, ?Tile $tile = null)
  535. {
  536. $this->q = $q;
  537. $this->r = $r;
  538. $this->tile = $tile;
  539. }
  540. /**
  541. * Définit la tuile
  542. *
  543. * @param Tile|null $tile La nouvelle tuile (null pour vider)
  544. */
  545. public function setTile(?Tile $tile): void
  546. {
  547. $this->tile = $tile;
  548. }
  549. /**
  550. * Récupère la tuile
  551. *
  552. * @return Tile|null La tuile ou null si vide
  553. */
  554. public function getTile(): ?Tile
  555. {
  556. return $this->tile;
  557. }
  558. /**
  559. * Vérifie si l'hexagone est vide
  560. *
  561. * @return bool True si l'hexagone est vide
  562. */
  563. public function isEmpty(): bool
  564. {
  565. return $this->tile === null || $this->tile->isEmpty() || $this->tile->getType() === 'empty';
  566. }
  567. /**
  568. * Récupère le type de tuile (méthode de compatibilité)
  569. *
  570. * @return string|null Le type de tuile ou null
  571. */
  572. public function getTileType(): ?string
  573. {
  574. return $this->tile ? $this->tile->getType() : null;
  575. }
  576. /**
  577. * Définit le type de tuile (méthode de compatibilité)
  578. *
  579. * @param string $tileType Le type de tuile
  580. */
  581. public function setTileType(string $tileType): void
  582. {
  583. if ($this->tile === null) {
  584. $this->tile = new Tile();
  585. }
  586. $this->tile->setType($tileType);
  587. }
  588. /**
  589. * Calcule la coordonnée S (pour les coordonnées cubiques)
  590. *
  591. * @return int La coordonnée S
  592. */
  593. public function getS(): int
  594. {
  595. return -$this->q - $this->r;
  596. }
  597. /**
  598. * Calcule la distance à un autre hexagone
  599. *
  600. * @param Hexagon $other L'autre hexagone
  601. * @return int La distance
  602. */
  603. public function distanceTo(Hexagon $other): int
  604. {
  605. return (abs($this->q - $other->q) + abs($this->r - $other->r) + abs($this->getS() - $other->getS())) / 2;
  606. }
  607. /**
  608. * Récupère les hexagones voisins
  609. *
  610. * @return array Les directions vers les voisins [q, r]
  611. */
  612. public static function getNeighbors(): array
  613. {
  614. return [
  615. [1, 0], // Est
  616. [1, -1], // Nord-Est
  617. [0, -1], // Nord-Ouest
  618. [-1, 0], // Ouest
  619. [-1, 1], // Sud-Ouest
  620. [0, 1] // Sud-Est
  621. ];
  622. }
  623. /**
  624. * Convertit l'hexagone en tableau pour la sérialisation
  625. *
  626. * @return array Les données de l'hexagone
  627. */
  628. public function toArray(): array
  629. {
  630. return [
  631. 'q' => $this->q,
  632. 'r' => $this->r,
  633. 'tile' => $this->tile ? $this->tile->toArray() : null
  634. ];
  635. }
  636. /**
  637. * Crée un hexagone depuis un tableau
  638. *
  639. * @param array $data Les données de l'hexagone
  640. * @return Hexagon L'hexagone créé
  641. */
  642. public static function fromArray(array $data): Hexagon
  643. {
  644. $tile = null;
  645. if (isset($data['tile']) && $data['tile'] !== null) {
  646. $tile = Tile::fromArray($data['tile']);
  647. } elseif (isset($data['tile_type'])) {
  648. // Compatibilité avec l'ancien format
  649. $tile = new Tile(['type' => $data['tile_type']]);
  650. }
  651. return new self($data['q'], $data['r'], $tile);
  652. }
  653. // Getters
  654. public function getQ(): int { return $this->q; }
  655. public function getR(): int { return $this->r; }
  656. }
  657. }