feat: validate scenario deployment zones and objectives
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BattleForge\Domain;
|
||||
|
||||
final class ScenarioValidator
|
||||
{
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public static function validate(Scenario $scenario): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
$occupied = [];
|
||||
|
||||
foreach ($scenario->deploymentZones as $teamId => $zone) {
|
||||
foreach ($zone->positions as $position) {
|
||||
if (!$scenario->battlefield->contains($position)) {
|
||||
$errors[] = "Deployment zone for {$teamId} includes a position outside the battlefield.";
|
||||
continue;
|
||||
}
|
||||
if ($scenario->battlefield->terrainAt($position)->movementCost() === null) {
|
||||
$errors[] = "Deployment zone for {$teamId} sits on impassable terrain at {$position->key()}.";
|
||||
}
|
||||
if (isset($occupied[$position->key()])) {
|
||||
$errors[] = "Deployment zones overlap at {$position->key()}.";
|
||||
}
|
||||
$occupied[$position->key()] = $teamId;
|
||||
}
|
||||
}
|
||||
|
||||
$reachableFromDeployment = self::reachableFromDeployment($scenario, $occupied);
|
||||
|
||||
foreach ($scenario->objectives as $objective) {
|
||||
if (!$scenario->battlefield->contains($objective->position)) {
|
||||
$errors[] = "Objective {$objective->id} is outside the battlefield.";
|
||||
continue;
|
||||
}
|
||||
if ($scenario->battlefield->terrainAt($objective->position)->movementCost() === null) {
|
||||
$errors[] = "Objective {$objective->id} sits on impassable terrain.";
|
||||
}
|
||||
if (!isset($reachableFromDeployment[$objective->position->key()])) {
|
||||
$errors[] = "Objective {$objective->id} is unreachable from any deployment zone.";
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $occupied
|
||||
* @return array<string, true>
|
||||
*/
|
||||
private static function reachableFromDeployment(Scenario $scenario, array $occupied): array
|
||||
{
|
||||
$merged = $scenario->units;
|
||||
$allReachable = [];
|
||||
|
||||
foreach ($merged as $unit) {
|
||||
$others = [];
|
||||
|
||||
foreach ($merged as $other) {
|
||||
if ($other->id === $unit->id || $other->isDefeated()) {
|
||||
continue;
|
||||
}
|
||||
$others[] = $other->position;
|
||||
}
|
||||
|
||||
$reachable = $scenario->battlefield->reachable($unit->position, $unit->speed, $others);
|
||||
|
||||
foreach ($reachable as $key => $_cost) {
|
||||
if (!isset($occupied[$key])) {
|
||||
$allReachable[$key] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $allReachable;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user