Ejemplo n.º 1
0
 private bool PlayerCanDoAction(Action a, CPlayer p)
 {
     return(p.Cards.Any(x => x.ActionsAllowed.Contains(a)));
 }
Ejemplo n.º 2
0
 internal void Concede()
 {
     ChoiceMade = Action.Concede;
 }
Ejemplo n.º 3
0
        private bool PlayerMadeBlockableAction(CPlayer p, Action a, CPlayer t = null, string cardUsed = "")
        {
            //TODO finish this up.
            var     aMsg = "";
            CPlayer bluffer = null, blocker = null, bluffBlock = null;
            bool    canBlock = false;
            bool    canBluff = true;

            switch (a)
            {
            case Action.ForeignAid:
                aMsg     = $"{p.Name.ToBold()} has chosen to take foreign aid (2 coins). Does anyone want to block?";
                canBluff = false;
                canBlock = true;
                break;

            case Action.Assassinate:
                cardUsed = "💀 Assassin";
                aMsg     = $"{p.Name.ToBold()} has chosen to assassinate {t.Name.ToBold()} [{cardUsed}]. Does anyone want to block / call bluff?";
                canBlock = true;
                break;

            case Action.Tax:
                cardUsed = "💰 Duke";
                aMsg     = $"{p.Name.ToBold()} has chosen to take tax (3 coins) [{cardUsed}]. Does anyone want to call bluff?";
                break;

            case Action.Exchange:
                cardUsed = "👳 Ambassador";
                aMsg     = $"{p.Name.ToBold()} has chosen to exchange cards with the deck [{cardUsed}]. Does anyone want to call bluff?";

                break;

            case Action.Steal:
                cardUsed = "🛡 Captain";
                aMsg     = $"{p.Name.ToBold()} has chosen to steal from {t.Name.ToBold()} [{cardUsed}]. Does anyone want to block / call bluff?";
                canBlock = true;
                break;

            case Action.BlockSteal:
                aMsg = $"{p.Name.ToBold()} has chosen to block {t.Name.ToBold()} from stealing [{cardUsed}]. Does anyone want to call bluff?";
                break;

            case Action.BlockAssassinate:
                cardUsed = "👠 Contessa";
                aMsg     = $"{p.Name.ToBold()} has chosen to block {t.Name.ToBold()} from assassinating [{cardUsed}]. Does anyone want to call bluff?";
                break;

            case Action.BlockForeignAid:
                cardUsed = "💰 Duke";
                aMsg     = $"{p.Name.ToBold()} has chosen to block {t.Name.ToBold()} from taking foreign aid [{cardUsed}]. Does anyone want to call bluff?";
                break;
            }
            Send(aMsg, menu: CreateBlockMenu(p.Id, canBlock, canBluff), menuNot: p.Id);

            foreach (var pl in Players)
            {
                if (canBlock && (pl.Id == t?.Id || a == Action.ForeignAid))
                {
                    pl.Block = null;
                }
                else
                {
                    pl.Block = false;
                }

                if (canBluff)
                {
                    pl.CallBluff = null;
                }
                else
                {
                    pl.CallBluff = false;
                }

                if (Turn == pl.Id)
                {
                    pl.CallBluff = pl.Block = false;
                }
            }

            WaitForChoice(ChoiceType.Block, p.Id, canBlock, canBluff);
            Turn = ActualTurn;
            //check to see if anyone called block or bluff
            bluffer = Players.FirstOrDefault(x => x.CallBluff == true);
            if (canBlock)
            {
                blocker = Players.FirstOrDefault(x => x.Block == true);
            }

            var isBluff = false;

            if (a.Equals(Action.BlockSteal) || a.Equals(Action.Steal))
            {
                //check the cardUsed
                isBluff = (p.Cards.All(x => x.Name != cardUsed));
            }
            else
            {
                isBluff = !PlayerCanDoAction(a, p);
            }

            if (blocker != null)
            {
                foreach (var pl in Players)
                {
                    pl.Block = null;
                }
                if (a == Action.Steal)
                {
                    //more than one card can block stealing
                    var menu = new InlineKeyboardMarkup(new[] { "🛡 Captain", "👳 Ambassador" }.Select(x => new InlineKeyboardButton(x, $"card|{GameId}|{x}")).ToArray());
                    ActualTurn = Turn;
                    Turn       = blocker.Id;
                    Send($"{blocker.Name.ToBold()} has chosen to block.  Please choose which card you are blocking with", menu: menu);
                    WaitForChoice(ChoiceType.Card);
                    Turn       = ActualTurn;
                    cardUsed   = CardToLose;
                    CardToLose = null;
                    if (cardUsed == null)
                    {
                        return(true);
                    }
                }
                var blocked = PlayerMadeBlockableAction(blocker, (Action)Enum.Parse(typeof(Action), "Block" + a.ToString(), true), p, cardUsed);
                if (blocked)
                {
                    DBAddValue(blocker, Models.ValueType.ActionsBlocked);
                    LastMenu = null;
                    Send($"{blocker.Name.ToBold()} has blocked {p.Name.ToBold()}");
                }
                return(!blocked);
            }
            else if (bluffer != null)
            {
                LastMenu = null;
                //fun time
                var msg = $"{bluffer.Name} has chosen to call a bluff.\n";

                //if stealing, simply checking is not good enough...
                if (!isBluff)
                {
                    //player has a card!
                    if (bluffer.Cards.Count() == 1)
                    {
                        Send($"{bluffer.Name.ToBold()}, {p.Name.ToBold()} had {cardUsed.ToBold()}.  You are out of cards, and therefore out of the game!");
                        PlayerLoseCard(bluffer, bluffer.Cards.First());
                        //Graveyard.Add(bluffer.Cards.First());
                        bluffer.Cards.Clear();
                    }
                    else
                    {
                        Send(msg + $"{bluffer.Name.ToBold()}, {p.Name.ToBold()} had {cardUsed.ToBold()}.  You must pick a card to lose!");
                        //TODO pick card to lose
                        PlayerLoseCard(bluffer);
                    }
                    //players card goes back in deck, given new card
                    try
                    {
                        var card = p.Cards.First(x => x.Name == cardUsed);
                        Cards.Add(card);
                        p.Cards.Remove(card);
                        Cards.Shuffle();
                        Cards.Shuffle();
                        card = Cards.First();
                        Cards.Remove(card);
                        p.Cards.Add(card);
                        Send($"You have lost your {cardUsed}.", p.Id, newMsg: true);
                    }
                    catch (Exception e)
                    {
                        Bot.Api.SendTextMessageAsync(Bot.Para, $"Error in blocking.\n{LastMessageSent}\n\n{e.Message}\n{p.Name}\nCard Used: {cardUsed}\nPlayers cards: {p.Cards.Aggregate("", (current, b) => current + ", " + b.Name)}");
                    }
                    TellCards(p);

                    return(true);
                }
                else
                {
                    if (p.Cards.Count() == 1)
                    {
                        Send(msg + $"{p.Name.ToBold()}, your bluff was called.  You are out of cards, and therefore out of the game!");
                        PlayerLoseCard(p, p.Cards.First());
                        //Graveyard.Add(p.Cards.First());
                        p.Cards.Clear();
                    }
                    else
                    {
                        Send(msg + $"{p.Name.ToBold()}, you did not have {cardUsed.ToBold()}! You must pick a card to lose.");
                        PlayerLoseCard(p);
                    }
                    //successful bluff called
                    DBAddBluff(p, cardUsed, true, bluffer);
                    return(false);
                }
            }
            else
            {
                if (isBluff)
                {
                    //was a successful bluff
                    DBAddBluff(p, cardUsed);
                }
                return(true);
            }
        }
Ejemplo n.º 4
0
        public void StartGame()
        {
            if (State != GameState.Joining)
            {
                return;
            }
            LastMenu = null;
            Send("Game is starting!");
            if (Players.Count < 2)
            {
                Send("Not enough players to start the game..");
                State = GameState.Ended;
                return;
            }
            if (State != GameState.Joining)
            {
                return;
            }
            Program.GamesPlayed++;
            State = GameState.Initializing;
            Players.Shuffle();
            Players.Shuffle();
            //create db entry for game
            using (var db = new CoupContext())
            {
                var grp = db.ChatGroups.FirstOrDefault(x => x.TelegramId == ChatId);
                var g   = new Database.Game {
                    GroupId = grp?.Id, GameType = IsGroup ? "Group" : IsRandom ? "Stranger" : "Friend", TimeStarted = DateTime.Now
                };
                db.Games.Add(g);
                db.SaveChanges();
                DBGameId = g.Id;
            }

            //hand out cards
            foreach (var p in Players)
            {
                var card = Cards.First();
                Cards.Remove(card);
                p.Cards.Add(card);
            }
            //round robin :P
            foreach (var p in Players)
            {
                var card = Cards.First();
                Cards.Remove(card);
                p.Cards.Add(card);
                using (var db = new CoupContext())
                {
                    //create db entry
                    var dbp = db.Players.FirstOrDefault(x => x.TelegramId == p.Id);
                    if (dbp == null)
                    {
                        dbp = new Player {
                            TelegramId = p.Id, Created = DateTime.UtcNow, Language = "English", Name = p.Name, Username = p.TeleUser.Username
                        };
                        db.Players.Add(dbp);
                        db.SaveChanges();
                    }
                    dbp.Name     = p.Name;
                    dbp.Username = p.TeleUser.Username;

                    p.Language = dbp.Language;

                    var gp = new GamePlayer()
                    {
                        GameId        = DBGameId,
                        StartingCards = p.Cards.Aggregate("", (a, b) => a + "," + b.Name)
                    };
                    dbp.GamePlayers.Add(gp);
                    db.SaveChanges();
                }
                //tell player their cards
                SendMenu(p);
                //TellCards(p);
            }
            //DEBUG OUT
#if DEBUG
            //for (int i = 0; i < Players.Count(); i++)
            //{
            //    Console.WriteLine($"Player {i}: {Players[i].Cards[0].Name}, {Players[i].Cards[1].Name}");
            //}
            //Console.WriteLine($"Deck:\n{Cards.Aggregate("", (a, b) => a + "\n" + b.Name)}");
#endif
            State = GameState.Running;

            while (true)
            {
                try
                {
                    Round++;
                    //will use break to end the game
                    foreach (var p in Players)
                    {
                        Cards.Shuffle();
                        if (p.Cards.Count() == 0)
                        {
                            continue;
                        }
                        Turn = p.Id;
                        Send($"{p.GetName()}'s turn. {p.Name.ToBold()} has {p.Coins} coins.", newMsg: true).ToList();
                        p.CallBluff = false;
                        //it is the players turn.
                        //first off, do they have to Coup?
                        if (p.Coins >= 10)
                        {
                            //Coup time!
                            Send($"{p.GetName()} has 10 or more coins, and must Coup someone.");
                            ChoiceMade = Action.Coup;
                        }
                        else
                        {
                            //ok, no, so get actions that they can make
                            Send($"{p.Name.ToBold()} please choose an action.", p.Id, menu: CreateActionMenu(p), menuTo: p.Id, newMsg: true);
                            LastMenu = null;
                            if (IsGroup)
                            {
                                Send($"{p.Name.ToBold()} please choose an action.");
                            }
                        }
                        var choice = WaitForChoice(ChoiceType.Action);
                        Console.WriteLine($"{p.Name} has chosen to {ChoiceMade}");
                        CPlayer target;
                        CPlayer blocker;
                        CPlayer bluffer;
                        IEnumerable <CPlayer> choices;
                        switch (choice)
                        {
                        //DONE
                        case Action.Income:
                            LastMenu = null;
                            Send($"{p.Name.ToBold()} has chosen to take income (1 coin).");
                            DBAddValue(p, Models.ValueType.CoinsCollected);
                            p.Coins++;
                            break;

                        case Action.ForeignAid:
                            if (PlayerMadeBlockableAction(p, choice, cardUsed: "Ambassador"))
                            {
                                LastMenu = null;
                                Send($"{p.Name.ToBold()} was not blocked, and has gained two coins.");
                                DBAddValue(p, Models.ValueType.CoinsCollected, 2);
                                p.Coins += 2;
                            }
                            break;

                        case Action.Coup:
                            p.Coins -= 7;
                            Send($"{p.Name.ToBold()} has chosen to Coup.");
                            choices = Players.Where(x => x.Id != p.Id && x.Cards.Count() > 0);
                            if (choices.Count() == 1)
                            {
                                target = choices.First();
                            }
                            else
                            {
                                Send($"Please choose who to Coup.", p.Id, menu: CreateTargetMenu(p), menuTo: p.Id);
                                LastMenu = null;
                                WaitForChoice(ChoiceType.Target);
                                target = Players.FirstOrDefault(x => x.Id == ChoiceTarget);
                            }
                            DBAddValue(p, Models.ValueType.PlayersCouped);
                            if (target == null)
                            {
                                LastMenu = null;
                                Send($"{p.Name.ToBold()} did not choose in time, and has lost 7 coins");
                                Send("Times up!", p.Id, menuTo: p.Id);
                                break;
                            }

                            if (target.Cards.Count() == 1)
                            {
                                PlayerLoseCard(target, target.Cards.First());
                                //Graveyard.Add(target.Cards.First());
                                target.Cards.Clear();
                                LastMenu = null;
                                Send($"{target.Name.ToBold()}, you have been couped.  You are out of cards, and therefore out of the game!");
                            }
                            else
                            {
                                Send($"{p.Name.ToBold()} has chosen to Coup {target.Name.ToBold()}!  {target.Name.ToBold()} please choose a card to lose.");
                                PlayerLoseCard(target);
                            }
                            break;

                        case Action.Tax:
                            if (PlayerMadeBlockableAction(p, Action.Tax))
                            {
                                LastMenu = null;
                                Send($"{p.Name.ToBold()} was not blocked, and has gained three coins.");
                                DBAddValue(p, Models.ValueType.CoinsCollected, 3);
                                p.Coins += 3;
                            }
                            break;

                        case Action.Assassinate:
                            //OH BOY
                            p.Coins -= 3;
                            Send($"{p.Name.ToBold()} has paid 3 coins to assassinate.");
                            choices = Players.Where(x => x.Id != p.Id && x.Cards.Count() > 0);
                            if (choices.Count() == 1)
                            {
                                target = choices.First();
                            }
                            else
                            {
                                Send($"Please choose who to assassinate.", p.Id, menu: CreateTargetMenu(p), menuTo: p.Id);
                                LastMenu = null;
                                WaitForChoice(ChoiceType.Target);
                                target = Players.FirstOrDefault(x => x.Id == ChoiceTarget);
                            }

                            if (target == null)
                            {
                                LastMenu = null;
                                Send("Times up!", p.Id, menuTo: p.Id);
                                Send($"{p.Name.ToBold()} did not choose in time, and has lost 3 coins!");
                                break;
                            }
                            if (PlayerMadeBlockableAction(p, choice, target, "Assassin"))
                            {
                                DBAddValue(p, Models.ValueType.PlayersAssassinated);
                                //unblocked!
                                if (target.Cards.Count() == 1)
                                {
                                    Send($"{target.Name.ToBold()}, you have been assassinated.  You are out of cards, and therefore out of the game!");
                                    PlayerLoseCard(target, target.Cards.First());
                                    //Graveyard.Add(target.Cards.First());
                                    target.Cards.Clear();
                                }
                                else
                                {
                                    Send($"{p.Name.ToBold()} was not blocked!  {target.Name.ToBold()} please choose a card to lose.");
                                    PlayerLoseCard(target);
                                }
                            }
                            break;

                        case Action.Exchange:
                            if (PlayerMadeBlockableAction(p, choice))
                            {
                                LastMenu = null;
                                Cards.Shuffle();
                                var count    = p.Cards.Count();
                                var newCards = Cards.Take(count).ToList();
                                Cards.AddRange(p.Cards);
                                newCards.AddRange(p.Cards.ToList());
                                p.Cards.Clear();
                                var menu = new InlineKeyboardMarkup(new[]
                                {
                                    newCards.Select(x => new InlineKeyboardButton(x.Name, $"card|{GameId}|{x.Name}")).ToArray()
                                });
                                LastMenu = null;
                                Turn     = p.Id; //just to be sure
                                Send($"{p.Name.ToBold()} was not blocked. Please choose your new cards in PM @{Bot.Me.Username}.");
                                Send($"{p.Name}, choose your first card", p.Id, menu: menu, newMsg: true);
                                WaitForChoice(ChoiceType.Card);
                                if (String.IsNullOrEmpty(CardToLose))
                                {
                                    LastMenu = null;
                                    Send($"Time ran out, moving on");
                                    p.Cards.AddRange(newCards.Take(count));
                                    TellCards(p);
                                    break;
                                }
                                var card2 = "";

                                newCards.Remove(newCards.First(x => x.Name == CardToLose));
                                var card1 = CardToLose;
                                CardToLose = null;
                                if (count == 2)
                                {
                                    menu = new InlineKeyboardMarkup(new[]
                                    {
                                        newCards.Select(x => new InlineKeyboardButton(x.Name, $"card|{GameId}|{x.Name}")).ToArray()
                                    });
                                    Send($"{p.Name}, choose your second card", p.Id, menu: menu, menuTo: p.Id);
                                    WaitForChoice(ChoiceType.Card);
                                    if (String.IsNullOrEmpty(CardToLose))
                                    {
                                        LastMenu = null;
                                        Send($"Time ran out, moving on");
                                        p.Cards.Add(newCards.First());
                                        TellCards(p);
                                        break;
                                    }
                                    card2      = CardToLose;
                                    CardToLose = null;
                                    newCards.Remove(newCards.First(x => x.Name == card2));
                                }
                                var newCard = Cards.First(x => x.Name == card1);
                                Cards.Remove(newCard);
                                p.Cards.Add(newCard);
                                if (count == 2)
                                {
                                    newCard = Cards.First(x => x.Name == card2);
                                    Cards.Remove(newCard);
                                    p.Cards.Add(newCard);
                                }
                                Cards.Shuffle();
                                TellCards(p);
                            }
                            break;

                        case Action.Steal:
                            Send($"{p.Name.ToBold()} has chosen to steal.");
                            choices = Players.Where(x => x.Id != p.Id && x.Cards.Count() > 0);
                            if (choices.Count() == 1)
                            {
                                target = choices.First();
                            }
                            else
                            {
                                Send($"{p.Name.ToBold()} please choose who to steal from.", p.Id, menu: CreateTargetMenu(p), menuTo: p.Id);
                                LastMenu = null;
                                WaitForChoice(ChoiceType.Target);
                                target = Players.FirstOrDefault(x => x.Id == ChoiceTarget);
                            }
                            if (target == null)
                            {
                                LastMenu = null;
                                Send("Times up!", p.Id, menuTo: p.Id);
                                Send($"{p.Name.ToBold()} did not choose in time");
                                break;
                            }
                            if (PlayerMadeBlockableAction(p, choice, target, "Captain"))
                            {
                                LastMenu = null;
                                var coinsTaken = Math.Min(target.Coins, 2);
                                DBAddValue(p, Models.ValueType.CoinsStolen, coinsTaken);
                                p.Coins      += coinsTaken;
                                target.Coins -= coinsTaken;
                                Send($"{p.Name.ToBold()} was not blocked, and has taken {coinsTaken} coins from {target.Name.ToBold()}.");
                            }
                            break;

                        case Action.Concede:
                            Graveyard.AddRange(p.Cards);
                            p.Cards.Clear();
                            LastMenu = null;
                            Send($"{p.Name} has chosen to concede the game, and is out!");
                            break;

                        default:
                            LastMenu = null;
                            Send($"{p.Name.ToBold()} did not do anything, moving on...");
                            p.AfkCount++;
                            if (p.AfkCount >= 2)
                            {
                                //out!
                                Graveyard.AddRange(p.Cards);
                                p.Cards.Clear();
                                Send($"{p.Name} is AFK, and is out!");
                            }
                            break;
                        }

                        //reset all the things
                        ChoiceMade   = Action.None;
                        ChoiceTarget = 0;
                        CardToLose   = "";
                        foreach (var pl in Players)
                        {
                            pl.CallBluff = null;
                            pl.Block     = null;
                        }
                        if (Players.Count(x => x.Cards.Count() > 0) == 1)
                        {
                            //game is over
                            var winner = Players.FirstOrDefault(x => x.Cards.Count() > 0);
                            //set the winner in the database
                            using (var db = new CoupContext())
                            {
                                var gp = GetDBGamePlayer(winner, db);
                                gp.Won         = true;
                                gp.EndingCards = winner.Cards.Count() > 1 ? winner.Cards.Aggregate("", (a, b) => a + "," + b.Name) : winner.Cards.First().Name;

                                var g = db.Games.Find(DBGameId);
                                g.TimeEnded = DateTime.UtcNow;
                                db.SaveChanges();
                            }
                            LastMenu = null;
                            Send($"{winner.GetName()} has won the game!", newMsg: true);
                            State = GameState.Ended;
                            return;
                        }
                    }
                }
                catch (Exception e)
                {
                    Send($"Game error: {e.Message}\n{e.StackTrace}");
                    State = GameState.Ended;
                    return;
                }
            }
        }