Files
chenmoranzhiandCursor ae13f1d06a 落地无头规则引擎与局内内容,便于命令行复现对战。
规则、卡包与测试放在同一套核心里,避免表现层自行结算。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-09 02:06:23 +08:00

50 lines
1.8 KiB
C#

using CardGame.Core;
using Xunit;
namespace Game.Core.Tests;
public sealed class AiSearchTests
{
[Fact]
public void Prefers_face_damage_when_lethal_or_high_value()
{
var state = new GameState(9) { Catalog = Cards.Catalog };
MatchSetup.PlaceSystemField(state, 0, Cards.BaseField);
MatchSetup.PlaceSystemField(state, 1, Cards.BaseField);
state.Players[1].Life = 1;
var attacker = state.NewEntity(Cards.Wisp, 0, Zone.Board, Factions.Guard);
attacker.EnteredOnTurn = 0;
attacker.AttacksRemaining = 1;
state.Board.Add(attacker);
state.ActivePlayerIndex = 0;
state.TurnNumber = 2;
var legal = RulesEngine.GetLegalActions(state);
var (action, top) = TurnBeamSearch.ChooseWithTrace(state, legal);
Assert.IsType<AttackAction>(action);
Assert.Contains(top, t => t.Chosen && t.Label.Contains("敌方主战者", StringComparison.Ordinal));
}
[Fact]
public void PlayUntil_returns_step_trace()
{
var state = MatchSetup.CreateStartingBattle(11, Cards.Catalog, shuffle: false);
state = RulesEngine.Execute(state, new EndTurnAction(0));
var (_, trace) = TurnBeamSearch.PlayUntil(state, untilPlayerIndex: 0);
Assert.NotEmpty(trace.Steps);
Assert.All(trace.Steps, s => Assert.NotEmpty(s.TopCandidates));
}
[Fact]
public void ChooseWithTrace_works_when_all_scores_are_negative()
{
var state = MatchSetup.CreateStartingBattle(13, Cards.Catalog, shuffle: false);
state.Players[0].Life = 1;
state.Players[1].Life = 20;
var legal = RulesEngine.GetLegalActions(state);
var (action, top) = TurnBeamSearch.ChooseWithTrace(state, legal);
Assert.NotNull(action);
Assert.Contains(top, t => t.Chosen);
}
}