using System;
using Luneraria.Core;
namespace Luneraria.View
{
///
/// Runs a bot match on the host (U2C/U2D) against wall-clock time: planning (the six bots plan
/// through the command interface), combat in fixed steps, a pause after the settlement, the
/// next round, until an Ancient falls. Only the core decides; time only paces the calls, so
/// the sequence of core calls is the one of LuneTrace.RunCore at every frame rate.
///
public sealed class LuneMatchRunner
{
public enum Stage { Planning, Combat, Settled, Over }
public readonly LuneMatch Match;
public readonly LuneClock Clock;
public readonly string Profile;
public LuneTrace Trace;
public double PlanningSeconds = 1.0;
public double SettledSeconds = 2.0;
public int MaxRound = 60;
public Stage Current { get; private set; }
public int StepsThisFrame { get; private set; }
public event Action CombatStarted;
public event Action Settled;
public event Action Finished;
double _stageTime;
public LuneMatchRunner(MatchOptions options, int startRound = 1, int extraGold = 0, string profile = "")
{
Match = NewMatch(options, startRound, extraGold);
Clock = new LuneClock(Match.Balance.SimStep);
Profile = profile;
EnterPlanning();
}
/// A match with the public fixtures the golden scenarios use (start round, gold).
public static LuneMatch NewMatch(MatchOptions options, int startRound, int extraGold)
{
var match = new LuneMatch();
match.Setup(options);
if (startRound > 1)
match.RoundNumber = startRound;
if (extraGold != 0)
foreach (Player player in match.Players)
player.Draft.Gold += extraGold;
return match;
}
void EnterPlanning()
{
Current = Stage.Planning;
_stageTime = 0.0;
for (int slot = 0; slot < 6; slot++)
LuneBot.Plan(Match, slot, Profile);
}
/// Advances by simulated seconds (frame time times match speed).
public void Advance(double seconds)
{
StepsThisFrame = 0;
switch (Current)
{
case Stage.Planning:
_stageTime += seconds;
if (_stageTime >= PlanningSeconds)
{
Match.BeginCombat();
Clock.Reset();
Current = Stage.Combat;
CombatStarted?.Invoke(Match.Combat);
}
break;
case Stage.Combat:
StepsThisFrame = Clock.Advance(seconds, StepOnce);
if (Match.Phase != Phase.Combat)
EndCombat();
break;
case Stage.Settled:
_stageTime += seconds;
if (_stageTime >= SettledSeconds)
EnterPlanning();
break;
}
}
bool StepOnce()
{
Match.StepCombat();
Trace?.RecordStep(Match.Combat);
return Match.Phase == Phase.Combat;
}
void EndCombat()
{
Trace?.RecordSettlement(Match);
Settled?.Invoke(Match.LastSettlement);
_stageTime = 0.0;
if (Match.Phase == Phase.Over || Match.RoundNumber > MaxRound)
{
Current = Stage.Over;
Finished?.Invoke(Match.Winner);
}
else
{
Current = Stage.Settled;
}
}
}
}