oecs-sharp/examples/TicTacToe/Program.cs

120 lines
4.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using OECS;
using R3;
namespace TicTacToe;
public static class Program
{
public static void Main()
{
var world = new World();
// ── Setup reactivity logging ──────────────────────────────────
//
// Each subscription appends to a shared log buffer. The log is
// printed after the board render so Console.Clear() doesn't wipe it.
var reactivityLog = new List<string>();
// 1. Log every entity-level change (creates, destroys).
world.ObserveEntityChanges().Subscribe(change =>
{
if (change.ComponentType != null) return;
reactivityLog.Add($"[react] {change}");
});
// 2. Log every Mark component change (placed on a cell).
world.ObserveComponentChanges<Mark>().Subscribe(change =>
{
reactivityLog.Add($"[react] {change}");
});
// 3. Log every GameState singleton change.
world.ObserveComponentChanges<GameState>().Subscribe(change =>
{
reactivityLog.Add($"[react] {change}");
});
// 4. Log changes matching a query: cells that have a Mark.
var markedQuery = world.Query().With<Cell>().With<Mark>().Build();
world.ObserveQuery(markedQuery).Subscribe(change =>
{
reactivityLog.Add($"[react:query] {change}");
});
// ── Load initial state from CSV ───────────────────────────────
var csvPath = Path.Combine(AppContext.BaseDirectory, "Data", "board.csv");
CsvLoader.LoadCells(world, csvPath);
world.PostChanges(); // flush entity-created notifications
// ── Initialize singleton ──────────────────────────────────────
world.SetSingleton(new GameState
{
CurrentPlayer = Player.X,
Status = GameStatus.Playing,
MoveCount = 0
});
world.PostChanges();
// ── Wire up systems ───────────────────────────────────────────
var group = new SystemGroup(world);
group.Add(new WinCheckSystem(world));
group.Add(new RenderSystem(world));
// ── Game loop ─────────────────────────────────────────────────
// Initial render.
group.RunLogical();
PrintReactivityLog(reactivityLog);
while (true)
{
ref var state = ref world.GetSingleton<GameState>();
if (state.Status != GameStatus.Playing)
break;
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
continue;
var parts = input.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2 ||
!int.TryParse(parts[0], out var row) ||
!int.TryParse(parts[1], out var col))
{
Console.WriteLine(" Invalid input. Try \"1 2\".");
continue;
}
if (row < 0 || row > 2 || col < 0 || col > 2)
{
Console.WriteLine(" Row and col must be 02.");
continue;
}
world.Commands.Enqueue(new PlaceMarkCommand { Row = row, Col = col });
group.RunLogical();
PrintReactivityLog(reactivityLog);
}
// ── Final state ───────────────────────────────────────────────
Console.WriteLine();
Console.WriteLine("=== Game over ===");
PrintReactivityLog(reactivityLog);
}
private static void PrintReactivityLog(List<string> log)
{
if (log.Count == 0) return;
Console.WriteLine("── reactivity log ──");
foreach (var entry in log)
Console.WriteLine(entry);
Console.WriteLine();
log.Clear();
}
}