using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using DominionBase.Piles; using DominionBase.Players; namespace DominionBase { public abstract class GameMessage { public virtual Boolean CheckEndGame { get { return false; } } public String Message { get; set; } public WaitCallback WaitCallback { get; set; } public GameMessage() { } public GameMessage(String message) : this(null, message) { } public GameMessage(WaitCallback waitCallback) { WaitCallback = waitCallback; } public GameMessage(WaitCallback waitCallback, String message) : this(waitCallback) { Message = message; } public virtual void ActBefore(Game game) { } public virtual Thread ActAfter(Game game) { return null; } } public class GameResponseMessage : GameMessage { } public class GameBuyMessage : GameMessage { public Player Player { get; set; } public Supply Supply { get; set; } public GameBuyMessage() { } public GameBuyMessage(Player player, Supply supply) : this(null, player, supply) { } public GameBuyMessage(WaitCallback waitCallback, Player player, Supply supply) : base(waitCallback) { Player = player; Supply = supply; } public override void ActBefore(Game game) { Player.Buy(this.Supply); } } public class GamePlayMessage : GameMessage { public Player Player { get; set; } public Cards.Card Card { get; set; } public GamePlayMessage() { } public GamePlayMessage(Player player, Cards.Card card) : this(null, player, card) { } public GamePlayMessage(WaitCallback waitCallback, Player player, Cards.Card card) : base(waitCallback) { Player = player; Card = card; } public override void ActBefore(Game game) { Player.PlayCard(Card); } } public class GameEndTurnMessage : GameMessage { public override Boolean CheckEndGame { get { return true; } } public Player Player { get; set; } public GameEndTurnMessage() { } public GameEndTurnMessage(Player player) : this(null, player) { } public GameEndTurnMessage(WaitCallback waitCallback, Player player) : base(waitCallback) { Player = player; } public override void ActBefore(Game game) { if (Player.Phase != PhaseEnum.Waiting) Player.Cleanup(); //game.SetNextPlayer(); } public override Thread ActAfter(Game game) { return game.SetNextPlayer(); } } public class GamePlayTreasuresMessage : GameMessage { public Player Player { get; set; } public GamePlayTreasuresMessage() { } public GamePlayTreasuresMessage(Player player) : this(null, player) { } public GamePlayTreasuresMessage(WaitCallback waitCallback, Player player) : base(waitCallback) { Player = player; } public override void ActBefore(Game game) { Player.PlayTreasures(game); } } public class GameGoToBuyPhaseMessage : GameMessage { public Player Player { get; set; } public GameGoToBuyPhaseMessage() { } public GameGoToBuyPhaseMessage(Player player) : this(null, player) { } public GameGoToBuyPhaseMessage(WaitCallback waitCallback, Player player) : base(waitCallback) { Player = player; } public override void ActBefore(Game game) { Player.GoToBuyPhase(); } } public class GameEndMessage : GameMessage { public Player Player { get; set; } public GameEndMessage() { } public GameEndMessage(Player player) : this(null, player) { } public GameEndMessage(WaitCallback waitCallback, Player player) : base(waitCallback) { Player = player; } public override void ActBefore(Game game) { game.Abort(); } } public class GameEndedEventArgs : EventArgs { public GameEndedEventArgs() { } } public class GameMessageEventArgs : EventArgs { public Player Player; public Player AffectedPlayer = null; public Cards.Card SourceCard; public ICard Card1 = null; public ICard Card2 = null; public int Count = 1; public GameMessageEventArgs(Player player, Cards.Card sourceCard) { this.Player = player; this.SourceCard = sourceCard; } public GameMessageEventArgs(Player player, Cards.Card sourceCard, ICard card) : this(player, sourceCard) { this.Card1 = card; } public GameMessageEventArgs(Player player, Cards.Card sourceCard, ICard card1, ICard card2) : this(player, sourceCard) { this.Card1 = card1; this.Card2 = card2; } public GameMessageEventArgs(Player player, Cards.Card sourceCard, ICard card, int count) : this(player, sourceCard, card) { this.Count = count; } public GameMessageEventArgs(Player player, Player playerAffected, Cards.Card sourceCard, ICard card) : this(player, sourceCard, card) { this.AffectedPlayer = playerAffected; } } public class CostComputeEventArgs : EventArgs { public ICard Card; public Cards.Cost Cost; public CostComputeEventArgs(ICard card, Cards.Cost cost) { if (card == null || cost == (Cards.Cost)null) return; this.Card = card; this.Cost = cost.Clone(); } } public class GameCreationException : Exception { public GameCreationException() { } public GameCreationException(string message) : base(message) { } public GameCreationException(string message, Exception innerException) : base(message, innerException) { } internal GameCreationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } public enum GameState { Unknown, Setup, NotStarted, Running, Ended, Aborted } public class Game : IDisposable { private Guid _Id = Guid.NewGuid(); private GameState _GameState = GameState.Unknown; private Random _RNG = new Random(); public delegate void GameMessageEventHandler(object sender, GameMessageEventArgs e); public event GameMessageEventHandler GameMessage = null; public delegate void GameEndedEventHandler(object sender, GameEndedEventArgs e); public event GameEndedEventHandler GameEndedEvent = null; public delegate void CostComputeEventHandler(object sender, CostComputeEventArgs e); public event CostComputeEventHandler CostCompute = null; private Table _Table = null; private PlayerCollection _Players = null; private Player _ActivePlayer = null; private TurnList _TurnsTaken = new TurnList(); private int _EndgameSupplies = 3; private Queue _MessageRequestQueue = new Queue(); private Queue _MessageResponseQueue = new Queue(); private Boolean _ShouldStop = false; private GameSettings _Settings = new GameSettings(); private List _CardsAvailable = new List(); public Game(int numHumanPlayers, IEnumerable playerNames, IEnumerable aiTypes, GameSettings settings) { this.State = GameState.Setup; this.Settings = settings; _Table = new Table(this, playerNames.Count(), false); _Players = new PlayerCollection(); // Add human players for (int i = 0; i < numHumanPlayers; i++) _Players.Add(new Players.Human(this, playerNames.ElementAt(i))); // Add AI players for (int i = numHumanPlayers; i < playerNames.Count(); i++) _Players.Add((Player)aiTypes.ElementAt(i).GetConstructor(new Type[] { typeof(Game), typeof(String)}).Invoke(new Object[] { this, playerNames.ElementAt(i) })); Utilities.Shuffler.Shuffle(_Players); if (settings.IdenticalStartingHands) foreach (Player player in _Players.Skip(1)) player.SetupDeckAs(_Players.ElementAt(0)); if (playerNames.Count() > 4) _EndgameSupplies = 4; this.CardsAvailable = Cards.CardCollection.GetAllCards(c => IsCardAllowed(c)); List _CardsChosen = new List(); if (settings.Preset != null) { _CardsChosen = settings.Preset.Cards; } else if (settings.Constraints != null) { _CardsChosen.AddRange(settings.Constraints.SelectCards(this.CardsAvailable, 10)); } this.CardsAvailable.RemoveAll(card => _CardsChosen.Contains(card)); _CardsChosen.ForEach(c => _Table.AddKingdomSupply(_Players, c.CardType)); // should now have a list of cards that can be drawn from for things like Bane supplies _Table.FinalizeSupplies(this); _Players.InitializeDecks(); _Players.FinalizeDecks(); this.State = GameState.NotStarted; } public void Clear() { this.Players.TearDown(); this.Table.TearDown(); foreach (Cards.Card card in this.CardsAvailable) card.TearDown(); Cards.CardCollection cardsAvailable = new Cards.CardCollection(this.CardsAvailable); List players = new List(this.Players); this.Table.Clear(); this.Players.Clear(); this.Winners.Clear(); this.TurnsTaken.Clear(); this.MessageRequestQueue.Clear(); this.MessageResponseQueue.Clear(); this.CardsAvailable.Clear(); #if DEBUG foreach (Cards.Card card in cardsAvailable) card.TestFireAllEvents(); foreach (Player p in players) p.TestFireAllEvents(); TestFireAllEvents(); #endif } public void TestFireAllEvents() { if (GameMessage != null) GameMessage(this, new GameMessageEventArgs(null, null)); if (GameEndedEvent != null) GameEndedEvent(this, new GameEndedEventArgs()); if (CostCompute != null) CostCompute(this, new CostComputeEventArgs(null, null)); } #region IDisposable variables, properties, & methods // Track whether Dispose has been called. private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. this._ActivePlayer = null; this._CardsAvailable = null; this._MessageRequestQueue = null; this._MessageResponseQueue = null; this._Players = null; this._RNG = null; this._Table = null; this._TurnsTaken = null; } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. // Note disposing has been done. disposed = true; } } ~Game() { Dispose(false); } #endregion public void SendMessage(Player player, Cards.Card sourceCard) { if (GameMessage != null) { GameMessageEventArgs gmea = new GameMessageEventArgs(player, sourceCard); GameMessage(this, gmea); } } public void SendMessage(Player player, Cards.Card sourceCard, ICard card) { if (GameMessage != null) { GameMessageEventArgs gmea = new GameMessageEventArgs(player, sourceCard, card); GameMessage(this, gmea); } } public void SendMessage(Player player, Cards.Card sourceCard, ICard card1, ICard card2) { if (GameMessage != null) { GameMessageEventArgs gmea = new GameMessageEventArgs(player, sourceCard, card1, card2); GameMessage(this, gmea); } } public void SendMessage(Player player, Cards.Card sourceCard, ICard card, int count) { if (GameMessage != null) { GameMessageEventArgs gmea = new GameMessageEventArgs(player, sourceCard, card, count); GameMessage(this, gmea); } } public void SendMessage(Player player, Player playerAffected, Cards.Card sourceCard, ICard card) { if (GameMessage != null) { GameMessageEventArgs gmea = new GameMessageEventArgs(player, playerAffected, sourceCard, card); GameMessage(this, gmea); } } /// /// Returns True if the card is allowed to be in the game /// /// /// private bool IsCardAllowed(Cards.Card card) { if (card.Location == Cards.Location.Kingdom) return true; return false; } internal Cards.Card GetNewCardPile(Func predicate) { IEnumerable matchingCards = this.CardsAvailable.Where(predicate); if (matchingCards.Count() == 0) throw new GameCreationException("Cannot satisfy specified constraints! Please double-check and make sure it's possible to construct a Kingdom card setup with the constraints specified"); int index; // For some reason, Random.Next() actually sometimes returns the end point as an option -- Why? That seems really wrong. do index = _RNG.Next(matchingCards.Count()); while (index == matchingCards.Count()); Cards.Card toReturn = matchingCards.ElementAt(index); this.CardsAvailable.Remove(toReturn); return toReturn; } public GameState State { get { return _GameState; } private set { _GameState = value; } } internal List CardsAvailable { get { return _CardsAvailable; } private set { _CardsAvailable = value; } } public Table Table { get { return _Table; } } public PlayerCollection Players { get { return _Players; } } public Player ActivePlayer { get { return _ActivePlayer; } private set { _ActivePlayer = value; } } public Player GetActivePlayer() { return ActivePlayer; } public Queue MessageRequestQueue { get { return _MessageRequestQueue; } } public Queue MessageResponseQueue { get { return _MessageResponseQueue; } } public IEnumerator GetPlayersStartingWithActiveEnumerator() { IEnumerator p = this.Players.GetPlayersStartingWithEnumerator(this.ActivePlayer); return p; } public IEnumerator GetPlayersStartingWithEnumerator(Player player) { IEnumerator p = this.Players.GetPlayersStartingWithEnumerator(player); return p; } public TurnList TurnsTaken { get { return _TurnsTaken; } } public Turn CurrentTurn { get { if (_TurnsTaken == null || _TurnsTaken.Count == 0) return null; return _TurnsTaken[_TurnsTaken.Count - 1]; } } public GameSettings Settings { get { return _Settings; } private set { _Settings = value; } } public void Reset() { _Table.Reset(); foreach (Player player in _Players) player.Reset(); } public Player GetPlayerFromIndex(Player currentPlayer, int index) { while (index < 0) index += _Players.Count; return _Players[(_Players.IndexOf(currentPlayer) + index) % _Players.Count]; } public Thread SetNextPlayer() { Boolean modifiedTurn = false; if (this.TurnsTaken.Count > 0 && this.CurrentTurn.IsTurnFinished && this.CurrentTurn.NextPlayer != null) { this.ActivePlayer = this.CurrentTurn.NextPlayer; modifiedTurn = true; } else if (this.ActivePlayer != null) this.ActivePlayer = this.Players.Next(this.ActivePlayer); else this.ActivePlayer = this.Players[0]; Reset(); Turn turn = new Turn(this.ActivePlayer) { ModifiedTurn = modifiedTurn }; if (this.CurrentTurn != null) turn.GrantedBy = this.CurrentTurn.NextGrantedBy; this.TurnsTaken.Add(turn); Thread startThread = new Thread(() => this.ActivePlayer.Start(turn)); return startThread; } private void End() { if (this.State != GameState.Aborted) this.State = GameState.Ended; Reset(); _ActivePlayer = null; foreach (Player player in _Players) { lock(player) player.End(); } if (GameEndedEvent != null) { GameEndedEventArgs geea = new GameEndedEventArgs(); GameEndedEvent(this, geea); } } public AutoResetEvent WaitEvent = new AutoResetEvent(false); public void StartAsync() { try { this.State = GameState.Running; _ShouldStop = false; Thread startingThread = SetNextPlayer(); while (!_ShouldStop) { if (startingThread != null) startingThread.Start(); WaitEvent.WaitOne(); startingThread = null; while (this.MessageRequestQueue.Count > 0) { lock (this.MessageRequestQueue) { GameMessage message = this.MessageRequestQueue.Dequeue(); //System.Diagnostics.Trace.WriteLine(String.Format("Message: {0}", message.Message)); GameMessage response = new GameResponseMessage(); response.Message = "ACK"; try { message.ActBefore(this); } catch (NullReferenceException nre) { if (!_ShouldStop) throw nre; } lock (_MessageResponseQueue) { _MessageResponseQueue.Enqueue(response); } if (message.WaitCallback != null) message.WaitCallback(null); Thread t = null; if (message.CheckEndGame && IsEndgameTriggered) _ShouldStop = true; else t = message.ActAfter(this); if (t != null) startingThread = t; } } } End(); } catch (Exception ex) { Utilities.Logging.LogError(ex); throw; } } public Boolean IsEndgameTriggered { get { return (_Table.Supplies.Values.Any(s => s.IsEndgameTriggered) || _Table.Supplies.EmptySupplyPiles >= _EndgameSupplies); } } public PlayerCollection Winners { get { PlayerCollection playersWhoWon = new PlayerCollection(); if (!this.IsEndgameTriggered) return playersWhoWon; foreach (Player player in this.Players.OrderByDescending(p => p.VictoryPoints).ThenBy(p => this.TurnsTaken.Count(t => t.IsTurnFinished && t.Player == p && !t.ModifiedTurn))) { if (playersWhoWon.Count == 0 || (playersWhoWon[0].VictoryPoints == player.VictoryPoints && this.TurnsTaken.Count(t => t.IsTurnFinished && t.Player == playersWhoWon[0] && !t.ModifiedTurn) == this.TurnsTaken.Count(t => t.IsTurnFinished && t.Player == player && !t.ModifiedTurn))) playersWhoWon.Add(player); } return playersWhoWon; } } public void Abort() { this.State = GameState.Aborted; _ShouldStop = true; } public Cards.Cost Cost(ICard card) { if (CostCompute != null) { CostComputeEventArgs ccea = new CostComputeEventArgs(card, card.BaseCost); CostCompute(this, ccea); return ccea.Cost; } else return card.BaseCost; } } }