namespace Vanguard.Board3D { /// /// Локальные правила раскладки для UX. Сервер остаётся авторитарным: /// эти проверки нужны для подсветки и drag&drop, а не для начисления /// результата. /// public static class BoardPlacementRules { public static bool CanRide(BoardZone zone, int slot) => zone == BoardZone.Vanguard && slot == PlaymatLayout.VanguardSlot; public static bool CanCall(int slot, bool occupied) => slot >= 0 && slot < PlaymatLayout.RearGuardCount; // Occupied circles are valid call targets: the previous unit is // moved to the drop zone. public static bool CanGuard(BoardZone zone) => zone == BoardZone.Guardian; public static bool CanMove(int fromSlot, int toSlot) => fromSlot >= 0 && fromSlot < PlaymatLayout.RearGuardCount && toSlot >= 0 && toSlot < PlaymatLayout.RearGuardCount && fromSlot != toSlot && SameColumn(fromSlot, toSlot); public static bool CanAttack(int attackerSlot, int targetSlot) => attackerSlot >= PlaymatLayout.VanguardSlot && attackerSlot < PlaymatLayout.RearGuardCount && targetSlot >= PlaymatLayout.VanguardSlot && targetSlot < PlaymatLayout.RearGuardCount; /// Колонки Classic: левая 0↔3, правая 2↔4; у центра пары нет. public static bool SameColumn(int fromSlot, int toSlot) => (fromSlot, toSlot) is (0, 3) or (3, 0) or (2, 4) or (4, 2); /// Call и Ride входят стоя и открытыми. public static CardState3D EnterFromHand(CircleAttribute circle) => circle switch { CircleAttribute.Vanguard => CardState3D.FaceUpStand, CircleAttribute.RearGuard => CardState3D.FaceUpStand, CircleAttribute.Guardian => CardState3D.FaceUpStand, _ => CardState3D.FaceUpStand, }; /// Перемещение RC→RC сохраняет Rest и открытость. public static CardState3D PreserveOnMove(CardState3D state) => state; public static BoardZone ZoneOfSlot(int slot) => slot == PlaymatLayout.VanguardSlot ? BoardZone.Vanguard : BoardZone.RearGuard; } }