diff --git a/GameCli/Program.cs b/GameCli/Program.cs index 8e5680c..9ddd8b5 100644 --- a/GameCli/Program.cs +++ b/GameCli/Program.cs @@ -1,2 +1,98 @@ -// Placeholder. Rewritten in Task 8. -System.Console.Error.WriteLine("GameCli placeholder"); +using System.Text.Json; +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); +}