GameRoom.GameProcess.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  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.DataBase.EntityDao;
  9. using MafiaTelegramBot.Game.GameRoles;
  10. using MafiaTelegramBot.Models;
  11. using MafiaTelegramBot.Resources;
  12. using Microsoft.EntityFrameworkCore;
  13. using Telegram.Bot.Types;
  14. using Timer = System.Timers.Timer;
  15. namespace MafiaTelegramBot.Game.GameRooms
  16. {
  17. public partial class GameRoom
  18. {
  19. public void Start()
  20. {
  21. new Task(async () =>
  22. {
  23. IsRunning = true;
  24. await FirstNight();
  25. await FirstDay();
  26. await GameCycle();
  27. await EndOfGame();
  28. }).Start();
  29. }
  30. private async Task FirstDay()
  31. {
  32. IsDay = true;
  33. await PlayersCh.SendSticker(Stickers.Sticker["Day"]);
  34. await PlayersCh.Send(strings.first_day_message);
  35. await Task.Run(async() =>
  36. {
  37. var turnsCount = _turnOrder.Count;
  38. Player firstPlayer = null;
  39. for (var i = 0; i < turnsCount; ++i)
  40. {
  41. var player = _turnOrder.Dequeue();
  42. if(!player.IsPlaying) continue;
  43. await PlayersCh.Send($"{strings.now_turn} ({player.TurnOrder}) {player.NickName}");
  44. if (TimerEnabled) await Bot.SendWithMarkdown2(player.ChatId, strings.you_turn_say);
  45. await player.CurrentRole.SpeakAction();
  46. if (firstPlayer != null && player.IsPlaying) _turnOrder.Enqueue(player);
  47. else firstPlayer = player;
  48. }
  49. _turnOrder.Enqueue(firstPlayer);
  50. });
  51. }
  52. private async Task FirstNight()
  53. {
  54. IsDay = false;
  55. await Task.Run(async() =>
  56. {
  57. await PlayersCh.SendSticker(Stickers.Sticker["Night"]);
  58. await PlayersCh.Send(strings.city_falls_asleep);
  59. var mafia = Players.Values.Where(player => player.GetRole() is Roles.Mafia).ToArray();
  60. var don = Players.Values.FirstOrDefault(player => player.GetRole() is Roles.Don);
  61. var message = strings.your_teammates;
  62. if (don != null) message += $"\n({don.TurnOrder}) {don.NickName} - {roles.Don}";
  63. message = mafia.Aggregate(message, (current, player) => current + $"\n({player.TurnOrder}) {player.NickName}");
  64. await _mafiaCh.Send(message);
  65. var resetEvent = new ManualResetEvent(false);
  66. var timer = new Timer
  67. {
  68. AutoReset = false,
  69. Interval = 10 * 1000
  70. };
  71. timer.Elapsed += (_, _) => resetEvent.Set();
  72. timer.Start();
  73. foreach (var player in Players.Values)
  74. {
  75. if (player.GetRole() is not (Roles.Don or Roles.Mafia or Roles.Dame))
  76. player.CurrentRole.KnownRoles.Add(player.Id, player.GetRoleName());
  77. else
  78. foreach (var p in Players.Values
  79. .Where(p => p.GetRole() is Roles.Don or Roles.Mafia or Roles.Dame))
  80. player.CurrentRole.KnownRoles.Add(p.Id, p.GetRoleName());
  81. }
  82. resetEvent.WaitOne();
  83. });
  84. }
  85. private async Task GameCycle()
  86. {
  87. await Task.Run(async () =>
  88. {
  89. while (GameNotEnded())
  90. {
  91. await NightPhase();
  92. await SummingUpPhase();
  93. if (!GameNotEnded()) break;
  94. await DayPhase();
  95. await DispatchPhase();
  96. if (!GameNotEnded()) break;
  97. }
  98. });
  99. }
  100. private bool GameNotEnded()
  101. {
  102. var playersCount = Players.Values.Count(p => p.IsAlive);
  103. var mafiaCount = Players.Values.Count(p => p.IsAlive && p.GetRole() is Roles.Don or Roles.Mafia or Roles.Dame);
  104. if (PlayersRole.ContainsKey(Roles.Werewolf)
  105. && PlayersRole[Roles.Werewolf].Count == 1 && PlayersRole[Roles.Werewolf][0].IsAlive
  106. && ((WerewolfRole) PlayersRole[Roles.Werewolf][0].CurrentRole).IsMafia) mafiaCount++;
  107. var villagersCount = playersCount - mafiaCount;
  108. return villagersCount > mafiaCount && mafiaCount > 0;
  109. }
  110. private async Task DayPhase()
  111. {
  112. IsDay = true;
  113. var discussionTimer = new Timer {AutoReset = false, Interval = 60 * 1000};
  114. var discussionEnded = new ManualResetEvent(false);
  115. foreach (var player in Players.Values.Where(player => !player.IsBlocked)) player.IsSpeaker = true;
  116. discussionTimer.Elapsed += async (_, _) =>
  117. {
  118. foreach (var player in Players.Values) player.IsSpeaker = false;
  119. await PlayersCh.Send(strings.day_discussion_ended);
  120. discussionEnded.Set();
  121. };
  122. await PlayersCh.Send(strings.disscution_time);
  123. discussionTimer.Start();
  124. discussionEnded.WaitOne();
  125. var turnsCount = _turnOrder.Count;
  126. Player toEndQueue = null;
  127. for (var i = 0; i < turnsCount; ++i)
  128. {
  129. var player = _turnOrder.Dequeue();
  130. if(!Players.ContainsKey(player.Id) || !player.IsAlive) continue;
  131. if (!player.IsBlocked)
  132. {
  133. await PlayersCh.Send($"{strings.now_turn} ({player.TurnOrder}) {player.NickName}");
  134. if (TimerEnabled) await Bot.SendWithMarkdown2(player.ChatId, strings.you_turn_say);
  135. await player.CurrentRole.SpeakAction();
  136. if (Players.ContainsKey(player.Id)) await player.CurrentRole.VotingAction(VoteUpList.Count == 0);
  137. }
  138. if (toEndQueue != null) _turnOrder.Enqueue(player);
  139. else toEndQueue = player;
  140. }
  141. _turnOrder.Enqueue(toEndQueue);
  142. }
  143. private async Task NightPhase()
  144. {
  145. IsDay = false;
  146. await PlayersCh.SendSticker(Stickers.Sticker["Night"]);
  147. await PlayersCh.Send(strings.city_falls_asleep);
  148. await Task.Run(async () =>
  149. {
  150. var resetEvent = new ManualResetEvent(false);
  151. var timer = new Timer{ Interval = 60*1000, AutoReset = false };
  152. foreach (var player in Players.Values.Where(p=>p.IsAlive))
  153. {
  154. await player.CurrentRole.NightAction();
  155. }
  156. timer.Elapsed += (_, _) => resetEvent.Set();
  157. var doctorId = PlayersRole[Roles.Doctor].Count == 1 ? PlayersRole[Roles.Doctor][0].Id : -1;
  158. foreach (var player in Players.Values)
  159. {
  160. if(doctorId != player.Id) player.CanBeHealed = true;
  161. player.CanBeBlockedNight = true;
  162. player.CanBeBlockedDay = true;
  163. player.IsBlocked = false;
  164. }
  165. timer.Start();
  166. resetEvent.WaitOne();
  167. });
  168. }
  169. public async Task SummingUpPhase()
  170. {
  171. await Task.Run(async () =>
  172. {
  173. var beforeKill = Players.Values.ToDictionary(p=>p.Id,p=>p.IsAlive);
  174. var mafiaNotAgree = "";
  175. var votes = Players.Values.Where(p=> p.IsAlive && p.CurrentRole.MafiaTargetId != -1 && p.CurrentRole.MafiaTargetId != -2)
  176. .GroupBy(p => p.CurrentRole.MafiaTargetId)
  177. .Select(item => new {id = item.Key, count = item.Count()})
  178. .ToList();
  179. if (votes.Count == 0) mafiaNotAgree = strings.mafia_not_kill_message;
  180. else {
  181. var max = votes.Max(item => item.count);
  182. var maxObjects = votes.Where(item => item.count == max).ToList();
  183. if(maxObjects.Count == 1)
  184. {
  185. if (Players.ContainsKey(maxObjects[0].id))
  186. await Players[maxObjects[0].id].CurrentRole.Kill();
  187. }
  188. }
  189. foreach (var (role, players) in PlayersRole)
  190. {
  191. if (role is Roles.Mafia)
  192. foreach (var mafia in players.Where(p=>p.IsAlive)) await mafia.CurrentRole.ApplyNightActionResult();
  193. else if (players.Count == 1 && players[0].IsAlive) await players[0].CurrentRole.ApplyNightActionResult();
  194. }
  195. var afterKill = Players.Values.ToDictionary(p=>p.Id,p=>p.IsAlive);
  196. var message = strings.city_wakes_up;
  197. if(afterKill.IsEquals(beforeKill)) message += strings.everyone_survived;
  198. else foreach (var (id, alive) in afterKill)
  199. if(beforeKill[id] != alive && Players.ContainsKey(id))
  200. {
  201. message += alive
  202. ? $"\n{Players[id].NickName} {strings.will_be_ressurected}"
  203. : $"\n{Players[id].NickName} {strings.will_be_killed}";
  204. await PlayersCh.SendTo(id, alive ? strings.you_will_be_ressurected : strings.you_will_be_killed);
  205. }
  206. message += '\n' + mafiaNotAgree;
  207. await PlayersCh.SendSticker(Stickers.Sticker["Day"]);
  208. await PlayersCh.Send(message);
  209. });
  210. }
  211. private async Task DefencePhase(List<Player> contenders)
  212. {
  213. await Task.Run( async () =>
  214. {
  215. var contendersCopy = contenders.ToList();
  216. VoteUpList.Clear();
  217. VoteKillList.Clear();
  218. foreach (var contender in contendersCopy.Where(p => !p.IsBlocked)) await contender.CurrentRole.DefenceAction();
  219. var votersPlayers = Players.Values.Where(p => p.IsAlive && !p.IsBlocked).ToArray();
  220. Timer votingTimer = new() {Interval = 20 * 1000, AutoReset = false};
  221. var resetEvent = new ManualResetEvent(false);
  222. votingTimer.Elapsed += (_, _) => resetEvent.Set();
  223. List<Message> messagesToDelete = new();
  224. foreach (var player in votersPlayers)
  225. messagesToDelete.Add(await PlayersCh.SendTo(player.ChatId, strings.you_have_ten_seconds_to_vote,
  226. Keyboard.VoteKeyboard(contendersCopy.Where(p=>Players.ContainsKey(p.Id)).ToList(), player.Id, false, Callback.VoteToKill)));
  227. votingTimer.Start();
  228. resetEvent.WaitOne();
  229. votingTimer.Stop();
  230. foreach (var message in messagesToDelete)
  231. await PlayersCh.EditTo(message.Chat.Id, message.MessageId, strings.time_out);
  232. });
  233. }
  234. private async Task DispatchPhase()
  235. {
  236. await Task.Run(async () =>
  237. {
  238. switch (VoteUpList.Count)
  239. {
  240. case 0:
  241. await PlayersCh.Send(strings.nothing_up_to_vote);
  242. break;
  243. case 1:
  244. {
  245. await VoteUpList[0].CurrentRole.Dispatch();
  246. break;
  247. }
  248. default:
  249. {
  250. await DefencePhase(VoteUpList);
  251. VoteUpList = await CalculateCandidates();
  252. switch (VoteUpList.Count)
  253. {
  254. case 0:
  255. await PlayersCh.Send(strings.nothing_up_to_dispatch);
  256. break;
  257. case 1:
  258. await VoteUpList[0].CurrentRole.Dispatch();
  259. break;
  260. case > 1:
  261. await DefencePhase(VoteUpList);
  262. VoteUpList = await CalculateCandidates();
  263. switch (VoteUpList.Count)
  264. {
  265. case 0:
  266. await PlayersCh.Send(strings.nothing_up_to_dispatch);
  267. break;
  268. case 1:
  269. await VoteUpList[0].CurrentRole.Dispatch();
  270. break;
  271. default:
  272. await PlayersCh.Send(strings.villagers_could_not_decide);
  273. break;
  274. }
  275. break;
  276. }
  277. break;
  278. }
  279. }
  280. VoteUpList.Clear();
  281. VoteKillList.Clear();
  282. });
  283. }
  284. private async Task<List<Player>> CalculateCandidates()
  285. {
  286. var message = strings.results_of_voting;
  287. var voteKillCopy = VoteKillList.ToDictionary(e => e.Key, e => e.Value);
  288. foreach (var (playerId, targetId) in voteKillCopy.Where(item => item.Value != 0))
  289. {
  290. message += Players.ContainsKey(playerId) && Players.ContainsKey(targetId)
  291. ? $"\n({Players[playerId].TurnOrder}) {Players[playerId].NickName} {strings.vote_to} " +
  292. $"({Players[targetId].TurnOrder}) {Players[targetId].NickName}" : "";
  293. }
  294. if (message == strings.results_of_voting) message = strings.no_one_voted;
  295. await PlayersCh.Send(message);
  296. var votes = VoteUpList.Where(p=>Players.ContainsKey(p.Id))
  297. .GroupBy(p => p.Id)
  298. .Select(item => new {id = item.Key, count = item.Count()})
  299. .ToList();
  300. List<Player> result = new();
  301. if (votes.Count == 0) return result;
  302. var max = votes.Max(item => item.count);
  303. var maxObjects = votes.Where(item => item.count == max);
  304. result.AddRange(from obj in maxObjects select Players[obj.id]);
  305. return result;
  306. }
  307. private async Task PutUpVote(long playerId, long targetId, int messageId, bool toKill = false)
  308. {
  309. if (Players.ContainsKey(playerId))
  310. {
  311. if (targetId == 0 && VoteKillList.AddUniqueByKey(playerId, targetId))
  312. {
  313. if (!toKill) await PlayersCh.SendExcept(playerId, $"{Players[playerId].NickName} {strings.skip_vote}");
  314. await PlayersCh.EditTo(playerId, messageId, strings.you_skip_vote);
  315. }
  316. else
  317. {
  318. if (Players.ContainsKey(targetId))
  319. {
  320. if (!toKill)
  321. {
  322. if (VoteUpList.AddUnique(Players[targetId]))
  323. {
  324. if (playerId != targetId)
  325. {
  326. await PlayersCh.SendExcept(playerId, $"{Players[playerId].NickName} {strings.put_up_vote_to} {Players[targetId].NickName}");
  327. await PlayersCh.EditTo(playerId, messageId, $"{strings.you_vote_player} {Players[targetId].NickName}");
  328. }
  329. else
  330. {
  331. await PlayersCh.SendExcept(playerId, $"{Players[playerId].NickName} {strings.vote_to_self}");
  332. await PlayersCh.EditTo(playerId, messageId, strings.you_vote_to_self);
  333. }
  334. }
  335. }
  336. else if (VoteKillList.AddUniqueByKey(playerId, targetId))
  337. {
  338. VoteUpList.Add(Players[targetId]);
  339. if (playerId != targetId)
  340. await PlayersCh.EditTo(playerId, messageId, $"{strings.you_vote_to_kill} {Players[targetId].NickName}");
  341. else
  342. await PlayersCh.EditTo(playerId, messageId, strings.you_vote_to_kill_self);
  343. }
  344. }
  345. }
  346. }
  347. else await PlayersCh.SendTo(playerId, strings.this_player_left_from_game);
  348. }
  349. private async Task EndOfGame()
  350. {
  351. await Task.Run(async () =>
  352. {
  353. var aliveMafia = Players.Values.Where(p => p.GetRole() is Roles.Don or Roles.Mafia && p.IsAlive).ToList();
  354. var additionalResult = "";
  355. foreach (var (_, value) in PlayersRole)
  356. {
  357. if (value.Count != 1) continue;
  358. var yellowResult = await value[0].CurrentRole.IsWon();
  359. if (yellowResult != "") additionalResult += "\n" + yellowResult;
  360. }
  361. if (aliveMafia.Count == 0)
  362. {
  363. await PlayersCh.Send(strings.villagers_won + additionalResult, exceptDied: false);
  364. await PlayersCh.SendSticker(Stickers.Sticker["VillagerWins"]);
  365. }
  366. else
  367. {
  368. await PlayersCh.Send(strings.mafia_won + additionalResult, exceptDied: false);
  369. await PlayersCh.SendSticker(Stickers.Sticker["MafiaWins"]);
  370. }
  371. if (PlayersRole.ContainsKey(Roles.Fool) && PlayersRole[Roles.Fool].Count == 1)
  372. await PlayersRole[Roles.Fool][0].CurrentRole.IsWon();
  373. var rolesMessage = strings.in_this_game_roles;
  374. var players = Players.Values.ToList();
  375. var statsQuery = "SELECT * FROM mafia.statistics WHERE";
  376. foreach (var player in players)
  377. {
  378. rolesMessage += $"\n({player.TurnOrder}) {player.NickName} - {player.GetRoleName()}";
  379. statsQuery += $" id = {player.Id} AND role = '{player.GetRole().ToString()}' OR role = 'All' OR";
  380. player.ResetState();
  381. }
  382. statsQuery = statsQuery.Substring(0, statsQuery.Length - 2);
  383. try
  384. {
  385. var statsList = UserDao.DataBase.Statistics.FromSqlRaw(statsQuery).ToArrayAsync().Result;
  386. foreach (var player in statsList)
  387. {
  388. player.Games++;
  389. //Yellow get Wins++
  390. if (aliveMafia.Count() == 0)
  391. {
  392. //then civil roles get Wins++;
  393. }
  394. else
  395. {
  396. //then civil rols get Wins++;
  397. }
  398. //Check Fool Wins++
  399. }
  400. UserDao.DataBase.Statistics.UpdateRange(statsList);
  401. await UserDao.DataBase.SaveChangesAsync();
  402. }
  403. catch (Exception e)
  404. {
  405. await Console.Out.WriteLineAsync(e.Message);
  406. }
  407. await PlayersCh.Send(rolesMessage);
  408. IsRunning = false;
  409. IsDay = false;
  410. _turnOrder.Clear();
  411. Settings.Clear();
  412. foreach (var (_, list) in PlayersRole) list.Clear();
  413. if (!Players.ContainsKey(Owner.Id) || Players.Count == 0) new Task(async() =>
  414. await RoomController.DissolveRoom(RoomEncrypter.GetCode(RoomName))).Start();
  415. else
  416. {
  417. await Bot.SendWithMarkdown2(Owner.ChatId, strings.thanks_for_game, Keyboard.OwnerGameMenu);
  418. await PlayersCh.SendExcept(Owner.Id, strings.thanks_for_game, Keyboard.PlayerGameMenu);
  419. }
  420. if (Players.Count <= Constants.PLAYER_DISABLE_TIMER) StartTimer();
  421. });
  422. }
  423. }
  424. }