VanguardReMaster/server/tests/Vanguard.Rules.Tests/DeckValidatorTests.cs

174 lines
5.4 KiB
C#

using Vanguard.Rules.Cards;
using Vanguard.Rules.Decks;
using Xunit;
namespace Vanguard.Rules.Tests;
public class DeckValidatorTests
{
private readonly CardCatalog _catalog = TestCatalog.Instance;
[Fact]
public void Legal_Deck_Has_No_Violations()
{
var deck = BuildLegalDeck();
Assert.Empty(DeckValidator.Validate(_catalog, deck));
}
[Fact]
public void Deck_Must_Have_Fifty_Cards()
{
var deck = BuildLegalDeck();
deck.RemoveAt(0);
var violations = DeckValidator.Validate(_catalog, deck);
Assert.Contains(violations, v => v.Code == "deck.size");
}
[Fact]
public void Copy_Limit_Counts_By_Name_Across_Printings()
{
var deck = DeckWith(("BT01/002EN", 4), ("TD01/005EN", 4));
var violations = DeckValidator.Validate(_catalog, deck);
var copies = Assert.Single(violations, v => v.Code == "deck.copies");
Assert.Contains("Blaster Blade", copies.Message);
}
[Fact]
public void Copy_Limit_Counts_Parallel_Art_As_Same_Card()
{
var deck = DeckWith(("BT01/002EN", 4), ("BT01/S02EN", 4));
var violations = DeckValidator.Validate(_catalog, deck);
Assert.Contains(violations, v => v.Code == "deck.copies");
}
[Fact]
public void Sentinel_Limit_Is_Four()
{
var deck = DeckWith(("BT01/011EN", 4), ("BT01/015EN", 1));
var violations = DeckValidator.Validate(_catalog, deck);
Assert.Contains(violations, v => v.Code == "deck.sentinel");
}
[Fact]
public void Heal_Limit_Is_Four()
{
var deck = DeckWith(("BT01/047EN", 4), ("BT01/053EN", 1));
var violations = DeckValidator.Validate(_catalog, deck);
Assert.Contains(violations, v => v.Code == "deck.heal");
}
[Fact]
public void Exactly_Sixteen_Triggers_Are_Required()
{
var deck = BuildLegalDeck();
var triggerIndex = deck.FindIndex(id =>
_catalog.CardOf(id).Trigger != TriggerType.None);
Assert.True(triggerIndex >= 0);
deck[triggerIndex] = "BT01/042EN"; // Marron, no trigger
var violations = DeckValidator.Validate(_catalog, deck);
Assert.Contains(violations, v => v.Code == "deck.triggers");
}
[Fact]
public void Unknown_Printing_Is_Reported()
{
var deck = BuildLegalDeck();
deck[0] = "BT99/999EN";
var violations = DeckValidator.Validate(_catalog, deck);
Assert.Contains(violations, v => v.Code == "deck.unknown-printing");
}
private List<string> BuildLegalDeck() => DeckWith();
/// <summary>
/// Собирает колоду на 50 карт: сначала заданные принты, затем по 4 копии
/// каждого имени, кроме Sentinel и Heal (чтобы не упираться в их лимиты).
/// </summary>
private List<string> DeckWith(params (string PrintingId, int Count)[] seed)
{
var deck = new List<string>();
var usedNames = new HashSet<string>(StringComparer.Ordinal);
foreach (var (printingId, count) in seed)
{
deck.AddRange(Enumerable.Repeat(printingId, count));
usedNames.Add(_catalog.Printing(printingId).NameId);
}
var triggers = deck.Count(id => _catalog.CardOf(id).Trigger != TriggerType.None);
var heals = deck.Count(id => _catalog.CardOf(id).Trigger == TriggerType.Heal);
foreach (var group in _catalog.Printings
.Where(p => !p.IsParallel)
.GroupBy(p => p.NameId)
.OrderBy(g => g.Key, StringComparer.Ordinal))
{
if (triggers >= DeckValidator.RequiredTriggers || deck.Count >= DeckValidator.RequiredSize)
break;
if (usedNames.Contains(group.Key))
continue;
var card = _catalog.Card(group.Key);
if (card.IsSentinel || card.Trigger == TriggerType.None)
continue;
if (card.Trigger == TriggerType.Heal && heals >= DeckValidator.MaxHeal)
continue;
var printingId = group.First().PrintingId;
usedNames.Add(group.Key);
var copies = Math.Min(
DeckValidator.MaxCopiesPerName,
Math.Min(DeckValidator.RequiredTriggers - triggers,
DeckValidator.RequiredSize - deck.Count));
for (var i = 0; i < copies; i++)
{
deck.Add(printingId);
triggers++;
if (card.Trigger == TriggerType.Heal)
heals++;
}
}
foreach (var group in _catalog.Printings
.Where(p => !p.IsParallel)
.GroupBy(p => p.NameId)
.OrderBy(g => g.Key, StringComparer.Ordinal))
{
if (deck.Count >= DeckValidator.RequiredSize)
break;
if (usedNames.Contains(group.Key))
continue;
var card = _catalog.Card(group.Key);
if (card.IsSentinel || card.Trigger != TriggerType.None)
continue;
var printingId = group.First().PrintingId;
usedNames.Add(group.Key);
for (var i = 0; i < DeckValidator.MaxCopiesPerName && deck.Count < DeckValidator.RequiredSize; i++)
deck.Add(printingId);
}
return deck;
}
}