139 lines
4.0 KiB
C#
139 lines
4.0 KiB
C#
using RobotAndDonkey.Game.Execution.Commands;
|
|
using RobotAndDonkey.Game.Execution.Requests;
|
|
using RobotAndDonkey.Game.Execution.Results;
|
|
using RobotAndDonkey.Game.GameState;
|
|
using RobotAndDonkey.Game.Intents;
|
|
using System;
|
|
using System.Collections.Immutable;
|
|
using System.Threading;
|
|
|
|
namespace RobotAndDonkey.Game.Execution;
|
|
|
|
public sealed record GameEvent(long Sequence, DateTimeOffset Time, object Payload);
|
|
|
|
public sealed record NextStepIssued(Request Step);
|
|
|
|
public sealed record StepApplied(ImmutableArray<Result> Results);
|
|
|
|
public sealed record StepRejected(Guid RequestId, string Reason);
|
|
|
|
public sealed record StateChanged;
|
|
|
|
public sealed record GameOver(string Reason);
|
|
|
|
public sealed class GameRuntime
|
|
{
|
|
public event EventHandler<GameEvent>? Published;
|
|
|
|
public void Start(CoreLoop coreLoop)
|
|
{
|
|
lock (m_Gate)
|
|
{
|
|
m_CoreLoop = coreLoop;
|
|
Publish(new StateChanged());
|
|
IssueNextStepLocked();
|
|
}
|
|
}
|
|
|
|
public void Submit(Command command, CancellationToken ct = default)
|
|
{
|
|
lock (m_Gate)
|
|
{
|
|
if (m_CoreLoop == null)
|
|
{
|
|
Publish(new StepRejected(command.RequestId, "No core loop started."));
|
|
return;
|
|
}
|
|
|
|
if (m_ExpectedRequest is null)
|
|
{
|
|
Publish(new StepRejected(command.RequestId, "No step expected (game over or not started)."));
|
|
return;
|
|
}
|
|
|
|
if (m_ExpectedRequest.RequestId != command.RequestId)
|
|
{
|
|
Publish(new StepRejected(command.RequestId, "Command does not match the expected step."));
|
|
return;
|
|
}
|
|
|
|
if (!m_ExpectedRequest.IsCommandCompatible(command))
|
|
{
|
|
Publish(new StepRejected(command.RequestId, "Command not compatible with the current step."));
|
|
return;
|
|
}
|
|
|
|
var results = command.Execute(m_CoreLoop, false, false);
|
|
|
|
Publish(new StepApplied([..results]));
|
|
Publish(new StateChanged());
|
|
|
|
IssueNextStepLocked();
|
|
}
|
|
}
|
|
|
|
private void IssueNextStepLocked()
|
|
{
|
|
m_ExpectedRequest = NextRequest();
|
|
|
|
if (m_ExpectedRequest is null)
|
|
Publish(new GameOver("No more steps."));
|
|
else
|
|
Publish(new NextStepIssued(m_ExpectedRequest));
|
|
}
|
|
|
|
private Request? NextRequest()
|
|
{
|
|
var coreLoop = m_CoreLoop!;
|
|
switch (coreLoop.RunPhase)
|
|
{
|
|
case ERunPhase.Init:
|
|
{
|
|
const ERunPhase firstPhase = ERunPhase.ExecuteProgram;
|
|
coreLoop.ResetShop();
|
|
new EnterPreview().Run(Guid.Empty, coreLoop, [], []);
|
|
// DEBUG
|
|
// const ERunPhase firstPhase = ERunPhase.DrawGlitch;
|
|
coreLoop.RunPhase = firstPhase;
|
|
goto case firstPhase;
|
|
}
|
|
case ERunPhase.DrawGlitch:
|
|
{
|
|
return DrawGlitchRequest.Create(coreLoop);
|
|
}
|
|
case ERunPhase.Improve:
|
|
{
|
|
return ImproveRequest.Create(coreLoop);
|
|
}
|
|
case ERunPhase.Gamble:
|
|
{
|
|
return GambleRequest.Create(coreLoop);
|
|
}
|
|
case ERunPhase.BufferOverflow:
|
|
{
|
|
return BufferOverflowRequest.Create(coreLoop);
|
|
}
|
|
case ERunPhase.ExecuteProgram:
|
|
{
|
|
return ExecuteProgramRequest.Create(coreLoop);
|
|
}
|
|
case ERunPhase.Scoring:
|
|
{
|
|
return ScoringRequest.Create(coreLoop);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void Publish(object payload)
|
|
{
|
|
Published?.Invoke(this, new(Interlocked.Increment(ref m_Sequence), DateTimeOffset.UtcNow, payload));
|
|
}
|
|
|
|
private CoreLoop? m_CoreLoop;
|
|
|
|
private readonly Lock m_Gate = new();
|
|
private Request? m_ExpectedRequest;
|
|
private long m_Sequence;
|
|
} |