private bool PlayerCanDoAction(Action a, CPlayer p) { return(p.Cards.Any(x => x.ActionsAllowed.Contains(a))); }
internal void Concede() { ChoiceMade = Action.Concede; }
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); } }
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; } } }