refactor: simplify iterator usage and improve command execution
- Replace manual `while (iter.MoveNext())` loops with `foreach` loops in tests - Implement batching and mutation flushing in `CommandQueue.ExecuteAll` to ensure chained commands see each other's changes
This commit is contained in:
parent
d9cd943c52
commit
5d7eb14911
|
|
@ -12,10 +12,9 @@ public class GameFlowTests
|
||||||
{
|
{
|
||||||
var (world, _) = SetupGame();
|
var (world, _) = SetupGame();
|
||||||
|
|
||||||
var query = new Query<Cell>().Without<Mark>();
|
|
||||||
var emptyCount = 0;
|
var emptyCount = 0;
|
||||||
using var iter = world.Select(query);
|
foreach (var _ in world.Select(new Query<Cell>().Without<Mark>()))
|
||||||
while (iter.MoveNext()) emptyCount++;
|
emptyCount++;
|
||||||
|
|
||||||
emptyCount.Should().Be(9);
|
emptyCount.Should().Be(9);
|
||||||
}
|
}
|
||||||
|
|
@ -29,13 +28,12 @@ public class GameFlowTests
|
||||||
group.RunLogical();
|
group.RunLogical();
|
||||||
|
|
||||||
var markedCount = 0;
|
var markedCount = 0;
|
||||||
using var iter = world.Select<Cell, Mark>();
|
foreach (var it in world.Select<Cell, Mark>())
|
||||||
while (iter.MoveNext())
|
|
||||||
{
|
{
|
||||||
markedCount++;
|
markedCount++;
|
||||||
iter.Item1.Row.Should().Be(0);
|
it.Item1.Row.Should().Be(0);
|
||||||
iter.Item1.Col.Should().Be(0);
|
it.Item1.Col.Should().Be(0);
|
||||||
iter.Item2.Player.Should().Be(Player.X);
|
it.Item2.Player.Should().Be(Player.X);
|
||||||
}
|
}
|
||||||
|
|
||||||
markedCount.Should().Be(1);
|
markedCount.Should().Be(1);
|
||||||
|
|
@ -67,11 +65,8 @@ public class GameFlowTests
|
||||||
|
|
||||||
// Only one mark should exist at (0,0), and it should still be X.
|
// Only one mark should exist at (0,0), and it should still be X.
|
||||||
var marks = new List<(int Row, int Col, Player Player)>();
|
var marks = new List<(int Row, int Col, Player Player)>();
|
||||||
using var iter3 = world.Select<Cell, Mark>();
|
foreach (var it in world.Select<Cell, Mark>())
|
||||||
while (iter3.MoveNext())
|
marks.Add((it.Item1.Row, it.Item1.Col, it.Item2.Player));
|
||||||
{
|
|
||||||
marks.Add((iter3.Item1.Row, iter3.Item1.Col, iter3.Item2.Player));
|
|
||||||
}
|
|
||||||
|
|
||||||
marks.Should().ContainSingle()
|
marks.Should().ContainSingle()
|
||||||
.Which.Should().Be((0, 0, Player.X));
|
.Which.Should().Be((0, 0, Player.X));
|
||||||
|
|
@ -170,11 +165,8 @@ public class GameFlowTests
|
||||||
state.MoveCount.Should().Be(2);
|
state.MoveCount.Should().Be(2);
|
||||||
|
|
||||||
var marks = new List<(int Row, int Col, Player Player)>();
|
var marks = new List<(int Row, int Col, Player Player)>();
|
||||||
using var iter2 = world2.Select<Cell, Mark>();
|
foreach (var it in world2.Select<Cell, Mark>())
|
||||||
while (iter2.MoveNext())
|
marks.Add((it.Item1.Row, it.Item1.Col, it.Item2.Player));
|
||||||
{
|
|
||||||
marks.Add((iter2.Item1.Row, iter2.Item1.Col, iter2.Item2.Player));
|
|
||||||
}
|
|
||||||
|
|
||||||
marks.Should().BeEquivalentTo([(0, 0, Player.X), (1, 0, Player.O)]);
|
marks.Should().BeEquivalentTo([(0, 0, Player.X), (1, 0, Player.O)]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,12 +160,9 @@ public class PlayTests
|
||||||
{
|
{
|
||||||
var sb = new System.Text.StringBuilder();
|
var sb = new System.Text.StringBuilder();
|
||||||
var grid = new char?[3, 3];
|
var grid = new char?[3, 3];
|
||||||
using (var iter = world.Select<Cell, Mark>())
|
foreach (var it in world.Select<Cell, Mark>())
|
||||||
{
|
grid[it.Item1.Row, it.Item1.Col] =
|
||||||
while (iter.MoveNext())
|
it.Item2.Player == Player.X ? 'X' : 'O';
|
||||||
grid[iter.Item1.Row, iter.Item1.Col] =
|
|
||||||
iter.Item2.Player == Player.X ? 'X' : 'O';
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int r = 0; r < 3; r++)
|
for (int r = 0; r < 3; r++)
|
||||||
{
|
{
|
||||||
|
|
@ -253,19 +250,16 @@ public class PlayTests
|
||||||
private static List<(int Row, int Col)> GetEmptyCells(World world)
|
private static List<(int Row, int Col)> GetEmptyCells(World world)
|
||||||
{
|
{
|
||||||
var empty = new List<(int, int)>();
|
var empty = new List<(int, int)>();
|
||||||
var query = new Query<Cell>().Without<Mark>();
|
foreach (var it in world.Select(new Query<Cell>().Without<Mark>()))
|
||||||
using var iter = world.Select(query);
|
empty.Add((it.Item1.Row, it.Item1.Col));
|
||||||
while (iter.MoveNext())
|
|
||||||
empty.Add((iter.Item1.Row, iter.Item1.Col));
|
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool WouldWin(World world, int row, int col, Player player)
|
private static bool WouldWin(World world, int row, int col, Player player)
|
||||||
{
|
{
|
||||||
var grid = new Player[3, 3];
|
var grid = new Player[3, 3];
|
||||||
using (var iter = world.Select<Cell, Mark>())
|
foreach (var it in world.Select<Cell, Mark>())
|
||||||
while (iter.MoveNext())
|
grid[it.Item1.Row, it.Item1.Col] = it.Item2.Player;
|
||||||
grid[iter.Item1.Row, iter.Item1.Col] = iter.Item2.Player;
|
|
||||||
grid[row, col] = player;
|
grid[row, col] = player;
|
||||||
|
|
||||||
for (int r = 0; r < 3; r++)
|
for (int r = 0; r < 3; r++)
|
||||||
|
|
|
||||||
|
|
@ -208,14 +208,9 @@ public class SnapshotTests
|
||||||
|
|
||||||
// Board: build a 3x3 grid.
|
// Board: build a 3x3 grid.
|
||||||
var grid = new char?[3, 3];
|
var grid = new char?[3, 3];
|
||||||
using (var iter = world.Select<Cell, Mark>())
|
foreach (var it in world.Select<Cell, Mark>())
|
||||||
{
|
grid[it.Item1.Row, it.Item1.Col] =
|
||||||
while (iter.MoveNext())
|
it.Item2.Player == Player.X ? 'X' : 'O';
|
||||||
{
|
|
||||||
grid[iter.Item1.Row, iter.Item1.Col] =
|
|
||||||
iter.Item2.Player == Player.X ? 'X' : 'O';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int totalCells = 0;
|
int totalCells = 0;
|
||||||
int markedCells = 0;
|
int markedCells = 0;
|
||||||
|
|
|
||||||
|
|
@ -41,11 +41,18 @@ public class CommandQueue
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Executes all queued commands in FIFO order against the given world.
|
/// Executes all queued commands in FIFO order against the given world.
|
||||||
///
|
///
|
||||||
|
/// Commands run inside a batching scope — structural mutations are
|
||||||
|
/// deferred and <see cref="World.GetComponent{T}"/> calls are
|
||||||
|
/// auto-tracked for modification until the drain completes.
|
||||||
|
/// Pending mutations are flushed after each command, so chained
|
||||||
|
/// commands see each other's changes within the same drain cycle.
|
||||||
|
///
|
||||||
/// The queue is fully drained — commands enqueued by other commands during
|
/// The queue is fully drained — commands enqueued by other commands during
|
||||||
/// this call are also executed before the method returns.
|
/// this call are also executed before the method returns.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ExecuteAll(World world)
|
public void ExecuteAll(World world)
|
||||||
{
|
{
|
||||||
|
world.BeginBatching();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (index < _commands.Count)
|
while (index < _commands.Count)
|
||||||
{
|
{
|
||||||
|
|
@ -60,9 +67,13 @@ public class CommandQueue
|
||||||
{
|
{
|
||||||
_errors.Add(ex);
|
_errors.Add(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush after each command so chained commands see mutations.
|
||||||
|
world.FlushPendingMutations();
|
||||||
}
|
}
|
||||||
|
|
||||||
_commands.Clear();
|
_commands.Clear();
|
||||||
|
world.EndBatching();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue