using System; using System.Collections.Generic; using System.Linq; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.Random; namespace HeuristicLab.SimulationCore.Samples { #region Model public enum Suits { Hearts, Bells, Leaves, Acorns } public enum Faces { Numeric, Under, Over, King, Sow } [StorableClass] public sealed class Card : IDeepCloneable { [Storable] public Suits Suit { get; private set; } [Storable] public Faces Face { get; private set; } [Storable] public int Value { get; private set; } [StorableConstructor] private Card(bool deserializing) { } private Card(Card original, Cloner cloner) { cloner.RegisterClonedObject(original, this); Suit = original.Suit; Face = original.Face; Value = original.Value; } public Card(Suits s, Faces f, int v) { Suit = s; Face = f; Value = v; } public object Clone() { return Clone(new Cloner()); } public IDeepCloneable Clone(Cloner cloner) { return new Card(this, cloner); } public override bool Equals(object obj) { var other = (obj as Card); if (other == null) return false; return Suit == other.Suit && Face == other.Face && Value == other.Value; } public override int GetHashCode() { return Suit.GetHashCode() ^ Face.GetHashCode() ^ Value.GetHashCode(); } } [StorableClass] public sealed class Deck : IDeepCloneable { private List cards; public IEnumerable Cards { get { return cards.Select(x => x); } } public int Count { get { return cards.Count; } } [StorableConstructor] private Deck(bool deserializing) { } private Deck(Deck original, Cloner cloner) { cloner.RegisterClonedObject(original, this); cards = original.Cards.Select(cloner.Clone).ToList(); } public Deck() { cards = new List() { new Card(Suits.Acorns, Faces.Numeric, 7), new Card(Suits.Acorns, Faces.Numeric, 8), new Card(Suits.Acorns, Faces.Numeric, 9), new Card(Suits.Acorns, Faces.Numeric, 10), new Card(Suits.Acorns, Faces.Under, 10), new Card(Suits.Acorns, Faces.Over, 10), new Card(Suits.Acorns, Faces.King, 10), new Card(Suits.Acorns, Faces.Sow, 11), new Card(Suits.Bells, Faces.Numeric, 7), new Card(Suits.Bells, Faces.Numeric, 8), new Card(Suits.Bells, Faces.Numeric, 9), new Card(Suits.Bells, Faces.Numeric, 10), new Card(Suits.Bells, Faces.Under, 10), new Card(Suits.Bells, Faces.Over, 10), new Card(Suits.Bells, Faces.King, 10), new Card(Suits.Bells, Faces.Sow, 11), new Card(Suits.Hearts, Faces.Numeric, 7), new Card(Suits.Hearts, Faces.Numeric, 8), new Card(Suits.Hearts, Faces.Numeric, 9), new Card(Suits.Hearts, Faces.Numeric, 10), new Card(Suits.Hearts, Faces.Under, 10), new Card(Suits.Hearts, Faces.Over, 10), new Card(Suits.Hearts, Faces.King, 10), new Card(Suits.Hearts, Faces.Sow, 11), new Card(Suits.Leaves, Faces.Numeric, 7), new Card(Suits.Leaves, Faces.Numeric, 8), new Card(Suits.Leaves, Faces.Numeric, 9), new Card(Suits.Leaves, Faces.Numeric, 10), new Card(Suits.Leaves, Faces.Under, 10), new Card(Suits.Leaves, Faces.Over, 10), new Card(Suits.Leaves, Faces.King, 10), new Card(Suits.Leaves, Faces.Sow, 11), }; } public object Clone() { return Clone(new Cloner()); } public IDeepCloneable Clone(Cloner cloner) { return new Deck(this, cloner); } public void Shuffle() { cards = cards.Shuffle(new FastRandom()).ToList(); } public Card Deal1() { var deal = cards[cards.Count - 1]; cards.RemoveAt(cards.Count - 1); return deal; } public Card[] Deal3() { var deal = new Card[3]; deal[0] = cards[cards.Count - 3]; deal[1] = cards[cards.Count - 2]; deal[2] = cards[cards.Count - 1]; cards.RemoveRange(cards.Count - 3, 3); return deal; } } [StorableClass] public sealed class Hand : IDeepCloneable { [Storable] public Card First { get; set; } [Storable] public Card Second { get; set; } [Storable] public Card Third { get; set; } public IEnumerable Cards { get { return new[] { First, Second, Third }; } set { var v = value.ToArray(); First = v[0]; Second = v[1]; Third = v[2]; if (v.Length != 3) throw new ArgumentException("A hand must always consist of exactly three cards."); } } [StorableConstructor] private Hand(bool deserializing) { } private Hand(Hand original, Cloner cloner) { cloner.RegisterClonedObject(original, this); Cards = original.Cards.Select(cloner.Clone); } public Hand(IEnumerable cards) { Cards = cards; } public object Clone() { return Clone(new Cloner()); } public IDeepCloneable Clone(Cloner cloner) { return new Hand(this, cloner); } public int Rating() { if (First.Suit == Second.Suit && First.Suit == Third.Suit) { var value = First.Value + Second.Value + Third.Value; if (value == 31) return 36; return value; } if (First.Suit == Second.Suit) return First.Value + Second.Value; if (First.Suit == Third.Suit) return First.Value + Third.Value; if (Second.Suit == Third.Suit) return Second.Value + Third.Value; if (Cards.Select(x => x.Face).Distinct().Count() == 1) return 33; return 0; } } [StorableClass] public sealed class Player : IDeepCloneable { [Storable] public string Name { get; private set; } [Storable] public Hand Hand { get; set; } [Storable] public int Coins { get; set; } [Storable] public bool Blocks { get; set; } [StorableConstructor] private Player(bool deserializing) { } private Player(Player original, Cloner cloner) { cloner.RegisterClonedObject(original, this); Name = original.Name; Hand = cloner.Clone(original.Hand); Coins = original.Coins; Blocks = original.Blocks; } public Player(string name, int coins) { Name = name; Coins = coins; Blocks = false; } public object Clone() { return Clone(new Cloner()); } public IDeepCloneable Clone(Cloner cloner) { return new Player(this, cloner); } } [StorableClass] public sealed class CardGameModel : Model { [Storable] public Hand Table { get; set; } [Storable] public Hand Talon { get; set; } [Storable] public List Players { get; set; } [Storable] public Deck Deck { get; set; } [Storable] public int DealerIndex { get; set; } [Storable] public int PlayerIndex { get; set; } [Storable] public IRandom Random { get; set; } [StorableConstructor] private CardGameModel(bool deserializing) : base(deserializing) { } private CardGameModel(CardGameModel original, Cloner cloner) : base(original, cloner) { Table = cloner.Clone(original.Table); Talon = cloner.Clone(original.Talon); if (original.Players != null) Players = original.Players.Select(cloner.Clone).ToList(); Deck = cloner.Clone(original.Deck); DealerIndex = original.DealerIndex; PlayerIndex = original.PlayerIndex; Random = cloner.Clone(original.Random); } public CardGameModel() { } public override IDeepCloneable Clone(Cloner cloner) { return new CardGameModel(this, cloner); } } #endregion #region Simulation [StorableClass] [Creatable("Simulations")] public sealed class CardGameSimulation : DiscreteEventSimulation { public override Type ProblemType { get { return typeof(CardGameScenario); } } private CardGameScenario Scenario { get { return (CardGameScenario)base.Problem; } set { base.Problem = value; } } [StorableConstructor] private CardGameSimulation(bool deserializing) : base(deserializing) { } private CardGameSimulation(CardGameSimulation original, Cloner cloner) : base(original, cloner) { RegisterScenarioEventHandlers(); } public CardGameSimulation() { PrepareSimulation(); Scenario = new CardGameScenario(); ParameterizeInitialAction(); RegisterScenarioEventHandlers(); } public override IDeepCloneable Clone(Cloner cloner) { return new CardGameSimulation(this, cloner); } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { RegisterScenarioEventHandlers(); } private void PrepareSimulation() { Activities = new Activity[] { new DealerActivity(), new PlayerActivity() }; Reporters = new Reporter[0]; InitialAction = new InitializeModelAction(); Model = new CardGameModel(); } private void RegisterScenarioEventHandlers() { Scenario.NumberOfPlayersParameter.Value.ValueChanged += ParametersChanged; Scenario.InitialCoinsParameter.Value.ValueChanged += ParametersChanged; } protected override void OnProblemChanged() { base.OnProblemChanged(); RegisterScenarioEventHandlers(); ParameterizeInitialAction(); } protected override void OnPrepared() { PrepareSimulation(); ParameterizeInitialAction(); base.OnPrepared(); } private void ParametersChanged(object sender, EventArgs e) { ParameterizeInitialAction(); } private void ParameterizeInitialAction() { ((InitializeModelAction)InitialAction).NumberOfPlayers = Scenario.NumberOfPlayersParameter.Value.Value; ((InitializeModelAction)InitialAction).InitialCoins = Scenario.InitialCoinsParameter.Value.Value; } } [Item("Card Game Scenario", "A card game scenario.")] [StorableClass] public sealed class CardGameScenario : Scenario { public IFixedValueParameter NumberOfPlayersParameter { get { return (IFixedValueParameter)Parameters["NumberOfPlayers"]; } } public IFixedValueParameter InitialCoinsParameter { get { return (IFixedValueParameter)Parameters["InitialCoins"]; } } public CardGameScenario() { Parameters.Add(new FixedValueParameter("NumberOfPlayers", "The number of players.", new IntValue(5))); Parameters.Add(new FixedValueParameter("InitialCoins", "The number of initial coins.", new IntValue(3))); } [StorableConstructor] private CardGameScenario(bool deserializing) : base(deserializing) { } private CardGameScenario(CardGameScenario original, Cloner cloner) : base(original, cloner) { } public override IDeepCloneable Clone(Cloner cloner) { return new CardGameScenario(this, cloner); } } #endregion #region Activities [StorableClass] public sealed class DealerActivity : Activity { public override IEnumerable MonitoredActions { get { return new[] { typeof(InitializeModelAction), typeof(FinalizeRoundAction), typeof(FoldCardsAction), typeof(AcceptCardsAction) }; } } [StorableConstructor] private DealerActivity(bool deserializing) : base(deserializing) { } private DealerActivity(DealerActivity original, Cloner cloner) : base(original, cloner) { } public DealerActivity() : base(new SortedListEventQueue()) { } public override IDeepCloneable Clone(Cloner cloner) { return new DealerActivity(this, cloner); } public override void ManageEvents(CardGameModel model, IAction lastAction) { if (lastAction is InitializeModelAction || lastAction is FinalizeRoundAction) { if (model.Players.Count <= 1) return; var nextPlayer = model.Players[(model.DealerIndex + 1) % model.Players.Count]; EventQueue.Push(model.CurrentTime + 1, new DealCardsAction(nextPlayer, doYouKeep: true)); } else if (lastAction is AcceptCardsAction || lastAction is FoldCardsAction) { EventQueue.Push(model.CurrentTime + 1, new DealCardsAction(model.Players.Where(x => x.Hand == null))); EventQueue.Push(model.CurrentTime + 2, new DealTableCardsAction()); } } } [StorableClass] public sealed class PlayerActivity : Activity { public override IEnumerable MonitoredActions { get { return new[] { typeof(DealCardsAction), typeof(DealTableCardsAction), typeof(NextPlayerAction) }; } } [StorableConstructor] private PlayerActivity(bool deserializing) : base(deserializing) { } private PlayerActivity(PlayerActivity original, Cloner cloner) : base(original, cloner) { } public PlayerActivity() : base(new SortedListEventQueue()) { } public override IDeepCloneable Clone(Cloner cloner) { return new PlayerActivity(this, cloner); } public override void ManageEvents(CardGameModel model, IAction lastAction) { if (lastAction is DealCardsAction) ManageDealCards(model, (DealCardsAction)lastAction); else if (lastAction is DealTableCardsAction) ManageTableCards(model); else if (lastAction is NextPlayerAction) ManageNextPlayer(model); } private void ManageDealCards(CardGameModel model, DealCardsAction dealCardsAction) { if (dealCardsAction.DoYouKeep) { var player = dealCardsAction.Players[0]; if (player.Hand.Rating() < 16) EventQueue.Push(model.CurrentTime + 1, new FoldCardsAction(player)); else EventQueue.Push(model.CurrentTime + 1, new AcceptCardsAction()); } } private void ManageTableCards(CardGameModel model) { var player = model.Players[model.PlayerIndex]; if (player.Hand.Rating() > 20) EventQueue.Push(model.CurrentTime + 1, new PlayerBlocksAction()); else EventQueue.Push(model.CurrentTime + 1, new SwapCardsAction(player.Hand, model.Table, model.Random.Next(1, 4), model.Random.Next(1, 4))); EventQueue.Push(model.CurrentTime + 2, new NextPlayerAction()); } private void ManageNextPlayer(CardGameModel model) { var player = model.Players[model.PlayerIndex]; if (player.Blocks || player.Hand.Rating() == 36) { EventQueue.Push(model.CurrentTime + 1, new FinalizeRoundAction()); return; } if (player.Hand.Rating() > 20) EventQueue.Push(model.CurrentTime + 1, new PlayerBlocksAction()); else EventQueue.Push(model.CurrentTime + 1, new SwapCardsAction(player.Hand, model.Table, model.Random.Next(1, 4), model.Random.Next(1, 4))); EventQueue.Push(model.CurrentTime + 2, new NextPlayerAction()); } } #endregion #region Actions [StorableClass] public sealed class InitializeModelAction : Action { [Storable] public int NumberOfPlayers { get; set; } [Storable] public int InitialCoins { get; set; } private InitializeModelAction(InitializeModelAction original, Cloner cloner) : base(original, cloner) { NumberOfPlayers = original.NumberOfPlayers; InitialCoins = original.InitialCoins; } public InitializeModelAction() { NumberOfPlayers = 5; InitialCoins = 3; } public override void Execute(CardGameModel model) { model.CurrentTime = 0; model.Random = new FastRandom(); model.Players = new List(); for (int i = 0; i < NumberOfPlayers; i++) { model.Players.Add(new Player("Player " + i, InitialCoins)); } model.DealerIndex = model.Random.Next(model.Players.Count); model.PlayerIndex = (model.DealerIndex + 1) % model.Players.Count; model.Deck = new Deck(); model.Deck.Shuffle(); model.Table = null; model.Talon = null; } public override IDeepCloneable Clone(Cloner cloner) { return new InitializeModelAction(this, cloner); } } [StorableClass] public sealed class FinalizeRoundAction : Action { private FinalizeRoundAction(FinalizeRoundAction original, Cloner cloner) : base(original, cloner) { } public FinalizeRoundAction() { } public override void Execute(CardGameModel model) { var minScore = model.Players.Min(x => x.Hand.Rating()); foreach (var p in model.Players.Where(x => x.Hand.Rating() == minScore)) p.Coins--; model.DealerIndex -= model.Players.Select((v, i) => new { Idx = i, Player = v }).Count(x => x.Idx <= model.DealerIndex && x.Player.Coins < 0); model.Players = model.Players.Where(x => x.Coins >= 0).ToList(); foreach (var p in model.Players) { p.Blocks = false; p.Hand = null; } if (model.Players.Count > 0) { model.DealerIndex = Math.Min(model.DealerIndex, model.Players.Count); model.DealerIndex = (model.DealerIndex + 1) % model.Players.Count; model.PlayerIndex = (model.DealerIndex + 1) % model.Players.Count; } model.Deck = new Deck(); model.Deck.Shuffle(); model.Table = null; model.Talon = null; } public override IDeepCloneable Clone(Cloner cloner) { return new FinalizeRoundAction(this, cloner); } } [StorableClass] public sealed class DealCardsAction : Action { [Storable] public Player[] Players { get; private set; } [Storable] public bool DoYouKeep { get; private set; } private DealCardsAction(DealCardsAction original, Cloner cloner) : base(original, cloner) { Players = original.Players.Select(cloner.Clone).ToArray(); DoYouKeep = original.DoYouKeep; } private DealCardsAction() { } public DealCardsAction(Player player, bool doYouKeep) { Players = new[] { player }; DoYouKeep = doYouKeep; } public DealCardsAction(IEnumerable players, bool doYouKeep = false) { Players = players.ToArray(); DoYouKeep = doYouKeep; } public override void Execute(CardGameModel model) { foreach (var player in Players) player.Hand = new Hand(model.Deck.Deal3()); } public override IDeepCloneable Clone(Cloner cloner) { return new DealCardsAction(this, cloner); } } [StorableClass] public sealed class FoldCardsAction : Action { [Storable] public Player Player { get; private set; } private FoldCardsAction(FoldCardsAction original, Cloner cloner) : base(original, cloner) { Player = cloner.Clone(original.Player); } public FoldCardsAction(Player player) { Player = player; } public override void Execute(CardGameModel model) { model.Talon = Player.Hand; Player.Hand = null; } public override IDeepCloneable Clone(Cloner cloner) { return new FoldCardsAction(this, cloner); } } [StorableClass] public sealed class AcceptCardsAction : Action { [StorableConstructor] private AcceptCardsAction(AcceptCardsAction original, Cloner cloner) : base(original, cloner) { } public AcceptCardsAction() { } public override void Execute(CardGameModel model) { } public override IDeepCloneable Clone(Cloner cloner) { return new AcceptCardsAction(this, cloner); } } [StorableClass] public sealed class DealTableCardsAction : Action { private DealTableCardsAction(DealTableCardsAction original, Cloner cloner) : base(original, cloner) { } public DealTableCardsAction() { } public override void Execute(CardGameModel model) { model.Table = new Hand(model.Deck.Deal3()); } public override IDeepCloneable Clone(Cloner cloner) { return new DealTableCardsAction(this, cloner); } } [StorableClass] public sealed class SwapCardsAction : Action { [Storable] public Hand Source { get; private set; } [Storable] public Hand Target { get; private set; } [Storable] public int SourceIndex { get; private set; } [Storable] public int TargetIndex { get; private set; } private SwapCardsAction(SwapCardsAction original, Cloner cloner) : base(original, cloner) { Source = cloner.Clone(original.Source); Target = cloner.Clone(original.Target); SourceIndex = original.SourceIndex; TargetIndex = original.TargetIndex; } public SwapCardsAction(Hand source, Hand target, int sourceIndex, int targetIndex) { Source = source; Target = target; SourceIndex = sourceIndex; TargetIndex = targetIndex; } public override void Execute(CardGameModel model) { if (SourceIndex == -1) { var source = Source.Cards.ToArray(); Source.Cards = Target.Cards; Target.Cards = source; } else { var sourceCards = Source.Cards.ToArray(); var targetCards = Target.Cards.ToArray(); var h = sourceCards[SourceIndex - 1]; sourceCards[SourceIndex - 1] = targetCards[TargetIndex - 1]; targetCards[TargetIndex - 1] = h; Source.Cards = sourceCards; Target.Cards = targetCards; } } public override IDeepCloneable Clone(Cloner cloner) { return new SwapCardsAction(this, cloner); } } [StorableClass] public sealed class PlayerBlocksAction : Action { private PlayerBlocksAction(PlayerBlocksAction original, Cloner cloner) : base(original, cloner) { } public PlayerBlocksAction() { } public override void Execute(CardGameModel model) { model.Players[model.PlayerIndex].Blocks = true; } public override IDeepCloneable Clone(Cloner cloner) { return new PlayerBlocksAction(this, cloner); } } [StorableClass] public sealed class NextPlayerAction : Action { private NextPlayerAction(NextPlayerAction original, Cloner cloner) : base(original, cloner) { } public NextPlayerAction() { } public override void Execute(CardGameModel model) { model.PlayerIndex = (model.PlayerIndex + 1) % model.Players.Count; } public override IDeepCloneable Clone(Cloner cloner) { return new NextPlayerAction(this, cloner); } } #endregion }