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; } } }
private void GameTimer() { while (Phase != GamePhase.Ending && Phase != GamePhase.KillGame) { try { #if DEBUG AddPlayer(new User { Id = 433942669, FirstName = "Mud9User", IsBot = false, LanguageCode = "zh-HK", Username = "******" }); AddPlayer(new User { Id = 415774316, FirstName = "Avalonese Dev", IsBot = false, LanguageCode = "zh-HK", Username = "******" }); AddPlayer(new User { Id = 267359519, FirstName = "Ian", IsBot = false, LanguageCode = "zh-HK", Username = "******" }); #endif for (var i = 0; i < JoinTime; i++) { if (Players.Count == 4) { break; } if (this.Phase == GamePhase.InGame) { break; } if (this.Phase == GamePhase.Ending) { return; } if (this.Phase == GamePhase.KillGame) { return; } //try to remove duplicated game if (i == 10) { var count = Bot.Games.Count(x => x.ChatId == ChatId); if (count > 1) { var toDel = Bot.Games.Where(x => x.Players.Count < this.Players.Count).OrderBy(x => x.Players.Count).Where(x => x.Id != this.Id && x.Phase != GamePhase.InGame); if (toDel != null) { Send(GetTranslation("DuplicatedGameRemoving")); foreach (var g in toDel) { g.Phase = GamePhase.KillGame; try { Bot.RemoveGame(g); } catch { // should be removed already } } } } } if (_secondsToAdd != 0) { i = Math.Max(i - _secondsToAdd, Constants.JoinTime - Constants.JoinTimeMax); // Bot.Send(ChatId, GetTranslation("JoinTimeLeft", TimeSpan.FromSeconds(Constants.JoinTime - i).ToString(@"mm\:ss"))); _secondsToAdd = 0; } var specialTime = JoinTime - i; if (new int[] { 10, 30, 60, 90 }.Contains(specialTime)) { Bot.Send(ChatId, GetTranslation("JoinTimeSpecialSeconds", specialTime)); } if (Players.Count == 4) { break; } Thread.Sleep(1000); } if (this.Phase == GamePhase.Ending) { return; } do { BTPlayer p = Players.FirstOrDefault(x => Players.Count(y => y.TelegramId == x.TelegramId) > 1); if (p == null) { break; } Players.Remove(p); }while (true); if (this.Phase == GamePhase.Ending) { return; } if (this.Players.Count() == 4) { this.Phase = GamePhase.InGame; } if (this.Phase != GamePhase.InGame) { /* * this.Phase = GamePhase.Ending; * Bot.RemoveGame(this); * Bot.Send(ChatId, "Game ended!"); */ } else { #region Ready to start game if (Players.Count < 4) { Send(GetTranslation("GameEnded")); return; } Send(GetTranslation("GameStart")); // create game + gameplayers in db using (var db = new BigTwoDb()) { DbGame = new Database.Game { GrpId = DbGroup.Id, GroupId = ChatId, GroupName = GroupName, TimeStarted = DateTime.UtcNow, ChipsPerCard = ChipsPerCard }; db.Games.Add(DbGame); db.SaveChanges(); GameId = DbGame.Id; foreach (var p in Players) { GamePlayer DbGamePlayer = new GamePlayer { PlayerId = db.Players.FirstOrDefault(x => x.TelegramId == p.TelegramId).Id, GameId = GameId }; db.GamePlayers.Add(DbGamePlayer); } db.SaveChanges(); } PrepareGame(); // remove joined players from nextgame list // RemoveFromNextGame(Players.Select(x => x.TelegramId).ToList()); #endregion #region Start! foreach (var player in Players) { SendDeck(player); } while (Phase != GamePhase.Ending) { // _playerList = Send(GeneratePlayerList()).MessageId; PlayersChooseCard(); if (Phase == GamePhase.Ending) { break; } if (CurrentLargestDealt) { NextPlayerAfterCurrentLargest(CurrentLargestDealtBy); } else { NextPlayer(); } } EndGame(); #endregion } this.Phase = GamePhase.Ending; Bot.Send(ChatId, GetTranslation("GameEnded")); } catch (Exception ex) { if (Phase == GamePhase.KillGame) { // normal } else { Log(ex); Phase = GamePhase.KillGame; } } } Bot.RemoveGame(this); }