GameRoom.GameProcess.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using MafiaTelegramBot.Controllers;
  7. using MafiaTelegramBot.CustomCollections.Extensions;
  8. using MafiaTelegramBot.Models;
  9. using MafiaTelegramBot.Resources;
  10. using Telegram.Bot.Types;
  11. using Timer = System.Timers.Timer;
  12. namespace MafiaTelegramBot.Game.GameRooms
  13. {
  14. public partial class GameRoom
  15. {
  16. public void Start()
  17. {
  18. new Task(async () =>
  19. {
  20. IsRunning = true;
  21. await FirstNight();
  22. await FirstDay();
  23. await GameCycle();
  24. await EndOfGame();
  25. }).Start();
  26. }
  27. private async Task FirstDay()
  28. {
  29. IsDay = true;
  30. await PlayersCh.SendSticker(Stickers.Sticker["Day"]);
  31. await PlayersCh.Send(strings.first_day_message);
  32. await Task.Run(async() =>
  33. {
  34. var turnsCount = _turnOrder.Count;
  35. Player firstPlayer = null;
  36. for (var i = 0; i < turnsCount; ++i)
  37. {
  38. var player = _turnOrder.Dequeue();
  39. if(!player.IsPlaying) continue;
  40. await PlayersCh.Send($"{strings.now_turn} ({player.TurnOrder}) {player.NickName}");
  41. if (TimerEnabled) await Bot.SendWithMarkdown2(player.ChatId, strings.you_turn_say);
  42. await player.CurrentRole.SpeakAction();
  43. if (firstPlayer != null && player.IsPlaying) _turnOrder.Enqueue(player);
  44. else firstPlayer = player;
  45. }
  46. _turnOrder.Enqueue(firstPlayer);
  47. });
  48. }
  49. private async Task FirstNight()
  50. {
  51. IsDay = false;
  52. await Task.Run(async() =>
  53. {
  54. await PlayersCh.SendSticker(Stickers.Sticker["Night"]);
  55. await PlayersCh.Send(strings.city_falls_asleep);
  56. var mafia = Players.Values.Where(player => player.GetRole() is Roles.Mafia).ToArray();
  57. var don = Players.Values.FirstOrDefault(player => player.GetRole() is Roles.Don);
  58. var message = strings.your_teammates;
  59. if (don != null) message += $"\n({don.TurnOrder}) {don.NickName} - {roles.Don}";
  60. message = mafia.Aggregate(message, (current, player) => current + $"\n({player.TurnOrder}) {player.NickName}");
  61. await _mafiaCh.Send(message);
  62. var resetEvent = new ManualResetEvent(false);
  63. var timer = new Timer
  64. {
  65. AutoReset = false,
  66. Interval = 10 * 1000
  67. };
  68. timer.Elapsed += (_, _) => resetEvent.Set();
  69. timer.Start();
  70. foreach (var player in Players.Values)
  71. {
  72. player.CurrentRole.KnownRoles.Add(player);
  73. if(player.GetRole() is Roles.Don or Roles.Mafia) player.CurrentRole.KnownRoles.AddRange(
  74. Players.Values.Where(p=> player.Id != p.Id && p.GetRole() is Roles.Mafia or Roles.Don)
  75. );
  76. }
  77. resetEvent.WaitOne();
  78. });
  79. }
  80. private async Task GameCycle()
  81. {
  82. await Task.Run(async () =>
  83. {
  84. var alivePlayers = Players.Values.Where(p => p.IsAlive).ToList();
  85. var aliveMafia = alivePlayers.Where(p => p.GetRole() is Roles.Don or Roles.Mafia).ToList();
  86. var gameNotEnded = alivePlayers.Count > 2 * aliveMafia.Count && aliveMafia.Count > 0;
  87. while (gameNotEnded)
  88. {
  89. await NightPhase();
  90. await SummingUpPhase();
  91. alivePlayers = Players.Values.Where(p => p.IsAlive).ToList();
  92. aliveMafia = alivePlayers.Where(p => p.GetRole() is Roles.Don or Roles.Mafia).ToList();
  93. gameNotEnded = alivePlayers.Count > 2 * aliveMafia.Count && aliveMafia.Count > 0;
  94. if (!gameNotEnded) break;
  95. await DayPhase();
  96. await DispatchPhase();
  97. alivePlayers = Players.Values.Where(p => p.IsAlive).ToList();
  98. aliveMafia = alivePlayers.Where(p => p.GetRole() is Roles.Don or Roles.Mafia).ToList();
  99. gameNotEnded = alivePlayers.Count > 2 * aliveMafia.Count && aliveMafia.Count > 0;
  100. if (!gameNotEnded) break;
  101. }
  102. });
  103. }
  104. private async Task DayPhase()
  105. {
  106. IsDay = true;
  107. var discussionTimer = new Timer {AutoReset = false, Interval = 60 * 1000};
  108. var discussionEnded = new ManualResetEvent(false);
  109. foreach (var player in Players.Values) player.IsSpeaker = true;
  110. discussionTimer.Elapsed += async (_, _) =>
  111. {
  112. foreach (var player in Players.Values) player.IsSpeaker = false;
  113. await PlayersCh.Send(strings.day_discussion_ended);
  114. discussionEnded.Set();
  115. };
  116. await PlayersCh.Send(strings.disscution_time);
  117. discussionTimer.Start();
  118. discussionEnded.WaitOne();
  119. var turnsCount = _turnOrder.Count;
  120. Player firstPlayer = null;
  121. for (var i = 0; i < turnsCount; ++i)
  122. {
  123. var player = _turnOrder.Dequeue();
  124. if(!player.IsPlaying || !player.IsAlive) continue;
  125. if (firstPlayer == null) player.IsFirst = true;
  126. await PlayersCh.Send($"{strings.now_turn} ({player.TurnOrder}) {player.NickName}");
  127. if (TimerEnabled) await Bot.SendWithMarkdown2(player.ChatId, strings.you_turn_say);
  128. await player.CurrentRole.SpeakAction();
  129. await player.CurrentRole.VotingAction();
  130. if (player.IsFirst) firstPlayer = player;
  131. else _turnOrder.Enqueue(player);
  132. }
  133. firstPlayer!.IsFirst = false;
  134. _turnOrder.Enqueue(firstPlayer);
  135. }
  136. private async Task NightPhase()
  137. {
  138. IsDay = false;
  139. await PlayersCh.SendSticker(Stickers.Sticker["Night"]);
  140. await PlayersCh.Send(strings.city_falls_asleep);
  141. await Task.Run(async () =>
  142. {
  143. var resetEvent = new ManualResetEvent(false);
  144. var timer = new Timer{ Interval = 60*1000, AutoReset = false };
  145. foreach (var player in Players.Values.Where(p=>p.IsAlive))
  146. {
  147. await player.CurrentRole.NightAction();
  148. }
  149. timer.Elapsed += (_, _) => resetEvent.Set();
  150. timer.Start();
  151. resetEvent.WaitOne();
  152. });
  153. }
  154. public async Task SummingUpPhase()
  155. {
  156. await Task.Run(async () =>
  157. {
  158. var beforeKill = Players.Values.ToDictionary(p=>p.Id,p=>p.IsAlive);
  159. var mafiaNotAgree = "";
  160. var votes = Players.Values.Where(p=> p.IsAlive && p.CurrentRole.MafiaTargetId != -1 && p.CurrentRole.MafiaTargetId != -2)
  161. .GroupBy(p => p.CurrentRole.MafiaTargetId)
  162. .Select(item => new {id = item.Key, count = item.Count()})
  163. .ToList();
  164. if (votes.Count == 0) mafiaNotAgree = strings.mafia_not_kill_message;
  165. else {
  166. var max = votes.Max(item => item.count);
  167. var maxObjects = votes.Where(item => item.count == max).ToList();
  168. if(maxObjects.Count == 1) await Players[maxObjects[0].id].CurrentRole.Kill();
  169. }
  170. foreach (var (role, players) in PlayersRole)
  171. {
  172. if (role is Roles.Mafia)
  173. foreach (var mafia in players) await mafia.CurrentRole.ApplyNightActionResult();
  174. else if (players.Count == 1)
  175. {
  176. var player = players[0];
  177. if (role != Roles.Doctor) player.CanBeHealed = true;
  178. await player.CurrentRole.ApplyNightActionResult();
  179. }
  180. }
  181. var afterKill = Players.Values.ToDictionary(p=>p.Id,p=>p.IsAlive);
  182. var message = strings.city_wakes_up;
  183. if(afterKill.IsEquals(beforeKill)) message += strings.everyone_survived;
  184. else foreach (var (id, alive) in afterKill)
  185. if(beforeKill[id] != alive) {
  186. message += alive
  187. ? $"\n{Players[id].NickName} {strings.will_be_ressurected}"
  188. : $"\n{Players[id].NickName} {strings.will_be_killed}";
  189. await PlayersCh.SendTo(id, alive ? strings.you_will_be_ressurected : strings.you_will_be_killed);
  190. }
  191. message += '\n' + mafiaNotAgree;
  192. await PlayersCh.SendSticker(Stickers.Sticker["Day"]);
  193. await PlayersCh.Send(message);
  194. });
  195. }
  196. private async Task DefencePhase(List<Player> contenders)
  197. {
  198. await Task.Run( async () =>
  199. {
  200. foreach (var contender in contenders) await contender.CurrentRole.DefenceAction();
  201. var votersPlayers = Players.Values.Where(p => p.IsAlive).ToArray();
  202. Timer votingTimer = new() {Interval = 10 * 1000, AutoReset = false};
  203. var resetEvent = new ManualResetEvent(false);
  204. votingTimer.Elapsed += (_, _) => resetEvent.Set();
  205. List<Message> messagesToDelete = new();
  206. foreach (var player in votersPlayers)
  207. messagesToDelete.Add(await PlayersCh.SendTo(player.ChatId, strings.you_have_ten_seconds_to_vote,
  208. Keyboard.VoteKeyboard(contenders, player.Id, vote: Callback.VoteToKill)));
  209. VoteUpList.Clear();
  210. VoteKillList.Clear();
  211. votingTimer.Start();
  212. resetEvent.WaitOne();
  213. votingTimer.Stop();
  214. foreach (var message in messagesToDelete)
  215. await PlayersCh.EditTo(message.Chat.Id, message.MessageId, strings.time_out);
  216. });
  217. }
  218. private async Task DispatchPhase()
  219. {
  220. await Task.Run(async () =>
  221. {
  222. if (VoteUpList.Count == 1) await VoteUpList[0].CurrentRole.Dispatch();
  223. else
  224. {
  225. await DefencePhase(VoteUpList);
  226. var contenders = await CalculateCandidates();
  227. switch (contenders.Count)
  228. {
  229. case 1:
  230. await contenders[0].CurrentRole.Dispatch();
  231. break;
  232. case > 1:
  233. await DefencePhase(contenders);
  234. contenders = await CalculateCandidates();
  235. if (contenders.Count == 1) await contenders[0].CurrentRole.Dispatch();
  236. else await PlayersCh.Send(strings.villagers_could_not_decide);
  237. break;
  238. default:
  239. await PlayersCh.Send(strings.villagers_could_not_decide);
  240. break;
  241. }
  242. }
  243. VoteUpList.Clear();
  244. VoteKillList.Clear();
  245. });
  246. }
  247. private async Task<List<Player>> CalculateCandidates()
  248. {
  249. var message = strings.results_of_voting;
  250. foreach (var (playerId, targetId) in VoteKillList)
  251. message += $"\n({Players[playerId].TurnOrder}) {Players[playerId].NickName} {strings.vote_to} " +
  252. $"({Players[targetId].TurnOrder}) {Players[targetId].NickName}";
  253. if (message == strings.results_of_voting) message = strings.no_one_voted;
  254. await PlayersCh.Send(message);
  255. var votes = VoteUpList.Where(p=> p.IsAlive && p.IsPlaying)
  256. .GroupBy(p => p.Id)
  257. .Select(item => new {id = item.Key, count = item.Count()})
  258. .ToList();
  259. List<Player> result = new();
  260. if (votes.Count == 0) return result;
  261. var max = votes.Max(item => item.count);
  262. var maxObjects = votes.Where(item => item.count == max);
  263. result.AddRange(maxObjects.Select(obj => Players[obj.id]));
  264. VoteUpList.Clear();
  265. VoteKillList.Clear();
  266. return result.ToList();
  267. }
  268. private async Task PutUpVote(long playerId, long targetId, int messageId, bool toKill = false)
  269. {
  270. var player = Players[playerId];
  271. if (targetId == 0)
  272. {
  273. if (!toKill) await PlayersCh.SendExcept(player.ChatId, $"{player.NickName} {strings.skip_vote}");
  274. await PlayersCh.EditTo(player.Id, messageId, strings.you_skip_vote);
  275. }
  276. else
  277. {
  278. var target = Players[targetId];
  279. VoteUpList.Add(target);
  280. if (toKill) VoteKillList.Add(playerId, targetId);
  281. if (playerId != targetId)
  282. {
  283. if (!toKill) await PlayersCh.SendExcept(player.ChatId, $"{player.NickName} {strings.put_up_vote_to} {target.NickName}");
  284. await PlayersCh.EditTo(player.Id, messageId, $"{strings.you_vote_player} {target.NickName}");
  285. }
  286. else
  287. {
  288. if (!toKill) await PlayersCh.SendExcept(player.ChatId, $"{player.NickName} {strings.vote_to_self}");
  289. await PlayersCh.EditTo(player.Id, messageId, strings.you_vote_to_self);
  290. }
  291. }
  292. }
  293. private async Task EndOfGame()
  294. {
  295. await Task.Run(async () =>
  296. {
  297. var aliveMafia = Players.Values.Where(p => p.GetRole() is Roles.Don or Roles.Mafia && p.IsAlive).ToList();
  298. string additionalResult = "";
  299. foreach (var role in PlayersRole)
  300. {
  301. if (role.Value.Count() != 1) continue;
  302. string yellowResult = await role.Value[0].CurrentRole.IsWon();
  303. if (yellowResult != "") additionalResult += "\n" + yellowResult;
  304. }
  305. if (aliveMafia.Count == 0)
  306. {
  307. await PlayersCh.Send(strings.villagers_won + additionalResult, exceptDied: false);
  308. await PlayersCh.SendSticker(Stickers.Sticker["VillagerWins"]);
  309. }
  310. else
  311. {
  312. await PlayersCh.Send(strings.mafia_won + additionalResult, exceptDied: false);
  313. await PlayersCh.SendSticker(Stickers.Sticker["MafiaWins"]);
  314. }
  315. var rolesMessage = strings.in_this_game_roles;
  316. var sortedPLayers = Players.Values.ToList();
  317. sortedPLayers.Sort((x, y) => x.TurnOrder - y.TurnOrder);
  318. foreach (var player in sortedPLayers)
  319. {
  320. rolesMessage += $"\n({player.TurnOrder}) {player.NickName} - {player.GetRoleName()}";
  321. player.ResetState();
  322. }
  323. //var dblist = UserDao.DataBase.Statistics.Where(s => sortedPLayers.Contains(s.UserId, s.Role.ToString())).ToList();
  324. await PlayersCh.Send(rolesMessage);
  325. IsRunning = false;
  326. IsDay = false;
  327. _turnOrder.Clear();
  328. Settings.Clear();
  329. foreach (var (_, list) in PlayersRole) list.Clear();
  330. if (!Players.ContainsKey(Owner.Id) || Players.Count == 0) new Task(async() =>
  331. await RoomController.DissolveRoom(RoomEncrypter.GetCode(RoomName))).Start();
  332. else
  333. {
  334. await Bot.SendWithMarkdown2(Owner.ChatId, strings.thanks_for_game, Keyboard.OwnerGameMenu);
  335. await PlayersCh.SendExcept(Owner.Id, strings.thanks_for_game, Keyboard.PlayerGameMenu);
  336. }
  337. });
  338. }
  339. }
  340. }