fix: enforce executable movement state

This commit is contained in:
Keith Solomon
2026-07-04 15:47:29 -05:00
parent 1550e2920b
commit 3eba823e52
4 changed files with 259 additions and 8 deletions
+7 -1
View File
@@ -4,13 +4,19 @@ declare(strict_types=1);
namespace BattleForge\Domain;
use InvalidArgumentException;
final class CombatEngine
{
public function move(MatchState $match, string $unitId, Position $destination): MatchState
{
$this->assertMatchActive($match);
$unit = $match->unit($unitId);
try {
$unit = $match->unit($unitId);
} catch (InvalidArgumentException $exception) {
throw new CombatException("Unknown unit: {$unitId}.", previous: $exception);
}
$this->assertCanAct($match, $unit);
$occupied = [];
+19
View File
@@ -30,6 +30,7 @@ final readonly class MatchState
$unitIds = [];
$teamIds = [];
$occupiedPositions = [];
foreach ($units as $unit) {
if (!self::isUnitState($unit)) {
@@ -40,6 +41,24 @@ final readonly class MatchState
throw new InvalidArgumentException("Duplicate unit id: {$unit->id}.");
}
if (!$battlefield->contains($unit->position)) {
throw new InvalidArgumentException("Unit {$unit->id} is outside the battlefield.");
}
if (!$unit->isDefeated() && $battlefield->terrainAt($unit->position)->movementCost() === null) {
throw new InvalidArgumentException("Living unit {$unit->id} must stand on passable terrain.");
}
$positionKey = $unit->position->key();
if (!$unit->isDefeated() && isset($occupiedPositions[$positionKey])) {
throw new InvalidArgumentException("Living units cannot share position {$positionKey}.");
}
if (!$unit->isDefeated()) {
$occupiedPositions[$positionKey] = true;
}
$unitIds[$unit->id] = true;
$teamIds[$unit->teamId] = true;
}