refactor: simplify entity lookups and clean up state updates
- Replace manual entity iteration with `world.FindEntity<T>()` - Remove redundant `MarkModified<GameState>` calls - Add safety counters to prevent infinite loops in tests and systems - Call `FlushPendingMutations` after card transfers to ensure consistency - Rename `Run` to `RunImpl` in several systems
This commit is contained in:
parent
4cdfe9c957
commit
cbb7edd472
|
|
@ -232,13 +232,7 @@ public class GameFlowTests
|
|||
|
||||
private static Entity FindEntity<T>(World world) where T : struct
|
||||
{
|
||||
using var iter = world.Select<T>();
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
if (iter.CurrentEntity != World.SingletonEntity)
|
||||
return iter.CurrentEntity;
|
||||
}
|
||||
return Entity.Null;
|
||||
return world.FindEntity<T>();
|
||||
}
|
||||
|
||||
private static int CountCardsInHand(World world, Entity handEntity)
|
||||
|
|
@ -251,11 +245,14 @@ public class GameFlowTests
|
|||
private static List<(Suit, Rank)> GetAllCards(World world)
|
||||
{
|
||||
var cards = new List<(Suit, Rank)>();
|
||||
var query = world.Query().With<Card>().Build();
|
||||
world.ForEach(query, (Entity e, ref Card card) =>
|
||||
using (var iter = world.Select<Card>())
|
||||
{
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
var card = iter.Val1;
|
||||
cards.Add((card.Suit, card.Rank));
|
||||
});
|
||||
}
|
||||
}
|
||||
cards.Sort((a, b) =>
|
||||
{
|
||||
int cmp = a.Item1.CompareTo(b.Item1);
|
||||
|
|
|
|||
|
|
@ -199,8 +199,10 @@ public class PlayTests
|
|||
|
||||
// Player turn: agent decides hit/stand.
|
||||
int roundHits = 0;
|
||||
while (world.ReadSingleton<GameState>().Phase == GamePhase.PlayerTurn)
|
||||
int playerTurnSafety = 0;
|
||||
while (world.ReadSingleton<GameState>().Phase == GamePhase.PlayerTurn && playerTurnSafety < 52)
|
||||
{
|
||||
playerTurnSafety++;
|
||||
var total = HandUtil.CalculateHand(world, new PlayerHand());
|
||||
var decision = agent.Decide(world);
|
||||
log.Decisions.Add($"R{totalRounds + 1} Hand={total}, Decision={decision}");
|
||||
|
|
@ -282,11 +284,7 @@ public class PlayTests
|
|||
|
||||
private static Entity FindEntity<T>(World world) where T : struct
|
||||
{
|
||||
using var iter = world.Select<T>();
|
||||
while (iter.MoveNext())
|
||||
if (iter.CurrentEntity != World.SingletonEntity)
|
||||
return iter.CurrentEntity;
|
||||
return Entity.Null;
|
||||
return world.FindEntity<T>();
|
||||
}
|
||||
|
||||
private static List<string> GetHandCards(World world, Entity hand)
|
||||
|
|
|
|||
|
|
@ -248,12 +248,6 @@ public class SnapshotTests
|
|||
|
||||
private static Entity FindEntity<T>(World world) where T : struct
|
||||
{
|
||||
using var iter = world.Select<T>();
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
if (iter.CurrentEntity != World.SingletonEntity)
|
||||
return iter.CurrentEntity;
|
||||
}
|
||||
return Entity.Null;
|
||||
return world.FindEntity<T>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ public struct HitCommand : ICommand
|
|||
{
|
||||
public void Execute(World world)
|
||||
{
|
||||
ref var state = ref world.GetSingleton<GameState>();
|
||||
var state = world.ReadSingleton<GameState>();
|
||||
|
||||
if (state.Phase != GamePhase.PlayerTurn)
|
||||
return;
|
||||
|
|
@ -25,9 +25,8 @@ public struct HitCommand : ICommand
|
|||
internal static void DrawCard<THand>(World world)
|
||||
where THand : struct
|
||||
{
|
||||
var singletonEntity = World.SingletonEntity;
|
||||
var deckEntity = FindEntity<Deck>(world, singletonEntity);
|
||||
var handEntity = FindEntity<THand>(world, singletonEntity);
|
||||
var deckEntity = world.FindEntity<Deck>();
|
||||
var handEntity = world.FindEntity<THand>();
|
||||
|
||||
if (deckEntity == Entity.Null || handEntity == Entity.Null)
|
||||
return;
|
||||
|
|
@ -42,17 +41,14 @@ public struct HitCommand : ICommand
|
|||
// Remove from deck, add to hand.
|
||||
world.RemoveComponent<InDeck>(cardEntity);
|
||||
world.AddComponent(cardEntity, new Holds { Target = handEntity });
|
||||
|
||||
// Flush so subsequent DrawCard calls see the updated state.
|
||||
world.FlushPendingMutations();
|
||||
}
|
||||
|
||||
internal static Entity FindEntity<T>(World world, Entity singletonEntity)
|
||||
internal static Entity FindEntity<T>(World world)
|
||||
where T : struct
|
||||
{
|
||||
using var iter = world.Select<T>();
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
if (iter.CurrentEntity != singletonEntity)
|
||||
return iter.CurrentEntity;
|
||||
}
|
||||
return Entity.Null;
|
||||
return world.FindEntity<T>();
|
||||
}
|
||||
}
|
||||
|
|
@ -17,28 +17,25 @@ public struct NewRoundCommand : ICommand
|
|||
return;
|
||||
|
||||
// Clear hands from previous round.
|
||||
var singletonEntity = World.SingletonEntity;
|
||||
var handEntities = new List<Entity>();
|
||||
using (var iter = world.Select<PlayerHand>())
|
||||
{
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
if (iter.CurrentEntity != singletonEntity)
|
||||
handEntities.Add(iter.CurrentEntity);
|
||||
handEntities.Add(iter.Entity);
|
||||
}
|
||||
}
|
||||
using (var iter = world.Select<DealerHand>())
|
||||
{
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
if (iter.CurrentEntity != singletonEntity)
|
||||
handEntities.Add(iter.CurrentEntity);
|
||||
handEntities.Add(iter.Entity);
|
||||
}
|
||||
}
|
||||
foreach (var hand in handEntities)
|
||||
{
|
||||
var cards = world.GetSources<Holds>(hand);
|
||||
var deckEntity = HitCommand.FindEntity<Deck>(world, singletonEntity);
|
||||
var deckEntity = world.FindEntity<Deck>();
|
||||
foreach (var card in cards)
|
||||
{
|
||||
world.RemoveComponent<Holds>(card);
|
||||
|
|
@ -49,6 +46,5 @@ public struct NewRoundCommand : ICommand
|
|||
state.RoundNumber++;
|
||||
state.Phase = GamePhase.Betting;
|
||||
state.Result = RoundResult.None;
|
||||
world.MarkModified<GameState>(World.SingletonEntity);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,5 @@ public struct PlaceBetCommand : ICommand
|
|||
state.CurrentBet = Amount;
|
||||
state.Chips -= Amount;
|
||||
state.Phase = GamePhase.Dealing;
|
||||
world.MarkModified<GameState>(World.SingletonEntity);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,5 @@ public struct StandCommand : ICommand
|
|||
return;
|
||||
|
||||
state.Phase = GamePhase.DealerTurn;
|
||||
world.MarkModified<GameState>(World.SingletonEntity);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ namespace Game.Blackjack;
|
|||
/// </summary>
|
||||
public class DealSystem : ISystem
|
||||
{
|
||||
public void Run(World world)
|
||||
public void RunImpl(World world)
|
||||
{
|
||||
var state = world.ReadSingleton<GameState>();
|
||||
if (state.Phase != GamePhase.Dealing)
|
||||
|
|
@ -23,7 +23,6 @@ public class DealSystem : ISystem
|
|||
HitCommand.DrawCard<DealerHand>(world);
|
||||
|
||||
mutableState.Phase = GamePhase.PlayerTurn;
|
||||
world.MarkModified<GameState>(World.SingletonEntity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Game.Blackjack;
|
|||
/// </summary>
|
||||
public class DealerSystem : ISystem
|
||||
{
|
||||
public void Run(World world)
|
||||
public void RunImpl(World world)
|
||||
{
|
||||
var state = world.ReadSingleton<GameState>();
|
||||
if (state.Phase != GamePhase.DealerTurn)
|
||||
|
|
@ -18,10 +18,12 @@ public class DealerSystem : ISystem
|
|||
|
||||
// Dealer must hit on 16 and below, stand on 17+.
|
||||
int dealerTotal = HandUtil.CalculateHand(world, DealerHandTag.Instance);
|
||||
while (dealerTotal < 17)
|
||||
int safety = 0;
|
||||
while (dealerTotal < 17 && safety < 52)
|
||||
{
|
||||
HitCommand.DrawCard<DealerHand>(world);
|
||||
dealerTotal = HandUtil.CalculateHand(world, DealerHandTag.Instance);
|
||||
safety++;
|
||||
}
|
||||
|
||||
int playerTotal = HandUtil.CalculateHand(world, PlayerHandTag.Instance);
|
||||
|
|
@ -49,6 +51,5 @@ public class DealerSystem : ISystem
|
|||
mutableState.Chips += mutableState.CurrentBet;
|
||||
}
|
||||
|
||||
world.MarkModified<GameState>(World.SingletonEntity);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,18 +9,17 @@ namespace Game.Blackjack;
|
|||
/// </summary>
|
||||
public class DeckSetupSystem : ISystem
|
||||
{
|
||||
public void Run(World world)
|
||||
public void RunImpl(World world)
|
||||
{
|
||||
var state = world.ReadSingleton<GameState>();
|
||||
if (state.Phase != GamePhase.Dealing)
|
||||
return;
|
||||
|
||||
// Only create deck entities if they don't exist yet.
|
||||
var singletonEntity = World.SingletonEntity;
|
||||
bool hasDeck = false;
|
||||
using (var iter = world.Select<Deck>())
|
||||
{
|
||||
hasDeck = iter.MoveNext() && iter.CurrentEntity != singletonEntity;
|
||||
hasDeck = iter.MoveNext();
|
||||
}
|
||||
|
||||
if (!hasDeck)
|
||||
|
|
@ -50,7 +49,7 @@ public class DeckSetupSystem : ISystem
|
|||
|
||||
// Shuffle the deck using mulberry32 with the current seed.
|
||||
ref var mutableState = ref world.GetSingleton<GameState>();
|
||||
var deckEntity2 = HitCommand.FindEntity<Deck>(world, singletonEntity);
|
||||
var deckEntity2 = world.FindEntity<Deck>();
|
||||
var cards = world.GetSources<InDeck>(deckEntity2).ToArray();
|
||||
Shuffle(world, deckEntity2, cards, ref mutableState.Seed);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,18 +22,7 @@ public static class HandUtil
|
|||
where T : struct
|
||||
{
|
||||
// Find the hand entity.
|
||||
Entity handEntity = Entity.Null;
|
||||
using (var iter = world.Select<T>())
|
||||
{
|
||||
while (iter.MoveNext())
|
||||
{
|
||||
if (iter.CurrentEntity != World.SingletonEntity)
|
||||
{
|
||||
handEntity = iter.CurrentEntity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var handEntity = world.FindEntity<T>();
|
||||
|
||||
if (handEntity == Entity.Null)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Game.Blackjack;
|
|||
/// </summary>
|
||||
public class PlayerBustCheckSystem : ISystem
|
||||
{
|
||||
public void Run(World world)
|
||||
public void RunImpl(World world)
|
||||
{
|
||||
var state = world.ReadSingleton<GameState>();
|
||||
if (state.Phase != GamePhase.PlayerTurn)
|
||||
|
|
@ -21,6 +21,5 @@ public class PlayerBustCheckSystem : ISystem
|
|||
ref var mutableState = ref world.GetSingleton<GameState>();
|
||||
mutableState.Phase = GamePhase.RoundOver;
|
||||
mutableState.Result = RoundResult.PlayerBust;
|
||||
world.MarkModified<GameState>(World.SingletonEntity);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue