feat(gamecli): wire stdio read-line loop to physics + reward + observation
This commit is contained in:
@@ -1,2 +1,98 @@
|
|||||||
// Placeholder. Rewritten in Task 8.
|
using System.Text.Json;
|
||||||
System.Console.Error.WriteLine("GameCli placeholder");
|
using GameCli;
|
||||||
|
|
||||||
|
// Deterministic PRNG for start-pose randomization on reset.
|
||||||
|
// Seeded fresh on each reset if the caller supplies a seed.
|
||||||
|
var rng = new Random();
|
||||||
|
|
||||||
|
// Current world state and step counter. Reset on {"cmd":"reset"}.
|
||||||
|
var ship = ShipState.AtCenter();
|
||||||
|
int stepCount = 0;
|
||||||
|
int lastAction = 0;
|
||||||
|
|
||||||
|
// --- Startup handshake ---------------------------------------------------
|
||||||
|
var init = new InitMessage
|
||||||
|
{
|
||||||
|
Init = new InitPayload
|
||||||
|
{
|
||||||
|
World = new[] { 1f, 1f },
|
||||||
|
Dt = Physics.Dt,
|
||||||
|
ObsDim = Observation.Dim,
|
||||||
|
NActions = 4,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Console.WriteLine(JsonSerializer.Serialize(init, ProtocolJsonContext.Default.InitMessage));
|
||||||
|
Console.Out.Flush();
|
||||||
|
|
||||||
|
// --- Read-line loop ------------------------------------------------------
|
||||||
|
string? line;
|
||||||
|
while ((line = Console.In.ReadLine()) is not null)
|
||||||
|
{
|
||||||
|
if (line.Length == 0) continue;
|
||||||
|
|
||||||
|
StepInput? input;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
input = JsonSerializer.Deserialize(line, ProtocolJsonContext.Default.StepInput);
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"invalid json: {ex.Message}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (input is null) continue;
|
||||||
|
|
||||||
|
// Reset command.
|
||||||
|
if (input.Cmd == "reset")
|
||||||
|
{
|
||||||
|
if (input.Seed is int seed) rng = new Random(seed);
|
||||||
|
ship = RandomStartPose(rng);
|
||||||
|
stepCount = 0;
|
||||||
|
lastAction = 0;
|
||||||
|
WriteObservation((0.5f, 0.5f), reward: 0f, done: false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step command.
|
||||||
|
int action = input.Action ?? 0;
|
||||||
|
var target = (input.Target is { Length: >= 2 })
|
||||||
|
? (input.Target[0], input.Target[1])
|
||||||
|
: (0.5f, 0.5f);
|
||||||
|
|
||||||
|
ship = Physics.Step(ship, action);
|
||||||
|
lastAction = action;
|
||||||
|
stepCount++;
|
||||||
|
|
||||||
|
float reward = Reward.Compute(ship, target, action);
|
||||||
|
WriteObservation(target, reward, done: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Helpers -------------------------------------------------------------
|
||||||
|
void WriteObservation((float X, float Y) target, float reward, bool done)
|
||||||
|
{
|
||||||
|
var output = new StepOutput
|
||||||
|
{
|
||||||
|
Obs = Observation.Build(ship, target),
|
||||||
|
State = new ShipStateDto
|
||||||
|
{
|
||||||
|
X = ship.X,
|
||||||
|
Y = ship.Y,
|
||||||
|
Angle = ship.Angle,
|
||||||
|
Engine = lastAction,
|
||||||
|
},
|
||||||
|
Reward = reward,
|
||||||
|
Done = done,
|
||||||
|
Step = stepCount,
|
||||||
|
};
|
||||||
|
Console.WriteLine(JsonSerializer.Serialize(output, ProtocolJsonContext.Default.StepOutput));
|
||||||
|
Console.Out.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
static ShipState RandomStartPose(Random rng)
|
||||||
|
{
|
||||||
|
// Random position in a comfortable box; zero velocity, near-upright.
|
||||||
|
float x = 0.2f + (float)rng.NextDouble() * 0.6f;
|
||||||
|
float y = 0.2f + (float)rng.NextDouble() * 0.4f;
|
||||||
|
float angle = ((float)rng.NextDouble() - 0.5f) * 0.4f; // ±0.2 rad
|
||||||
|
return new ShipState(x, y, 0f, 0f, angle, 0f);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user