private DiscordEmbedBuilder BuildGameFoundMessage(InhouseGame inhouseGame, InhouseQueue inhouseQueue) { var blueWinrate = CalculateBlueSideWinrate(inhouseGame); var embedBuilder = new DiscordEmbedBuilder(); embedBuilder.Title = "📢Game found📢"; embedBuilder.Description = $"Blue side expected winrate is {blueWinrate.ToString("0.0")}%\n" + "If you are ready to play, press ✅\n" + "If you cannot play, press ❌\n" + "Recheck if it didn't work the first time"; var bluePlayers = inhouseGame.InhousePlayers.Where(x => x.PlayerSide == PlayerSide.Blue).OrderBy(x => x.PlayerRole); var bluePlayersString = ""; foreach (var bluePlayer in bluePlayers) { bluePlayersString += $"{inhouseQueue.Emojis[bluePlayer.PlayerRole]}{_statusEmojis[bluePlayer.PlayerStatus]}{bluePlayer.Player.DisplayName}\n"; } var redPlayers = inhouseGame.InhousePlayers.Where(x => x.PlayerSide == PlayerSide.Red).OrderBy(x => x.PlayerRole); var redPlayersString = ""; foreach (var redPlayer in redPlayers) { redPlayersString += $"{inhouseQueue.Emojis[redPlayer.PlayerRole]}{_statusEmojis[redPlayer.PlayerStatus]}{redPlayer.Player.DisplayName}\n"; } embedBuilder.AddField("BLUE", bluePlayersString, true); embedBuilder.AddField("RED", redPlayersString, true); return(embedBuilder); }
private double CalculateBlueSideWinrate(InhouseGame inhouseGame) { var overallBlueWinrate = 0.0; var overallRedWinrate = 0.0; var uow = _db.UnitOfWork(); foreach (var player in inhouseGame.InhousePlayers) { var playerStat = uow.InhousePlayerStats.GetByPlayerId(player.Player.Id); if (playerStat == null) { if (player.PlayerSide == PlayerSide.Blue) { overallBlueWinrate += 50.0; } else { overallRedWinrate += 50.0; } continue; } var totalGames = playerStat.Wins + playerStat.Loses; var wins = playerStat.Wins + 10 - Math.Min(10, totalGames); var loses = playerStat.Loses + 10 - Math.Min(10, totalGames); var winrate = 100.0 * wins / (wins + loses); if (player.PlayerSide == PlayerSide.Blue) { overallBlueWinrate += winrate; } else { overallRedWinrate += winrate; } } if (overallBlueWinrate == 0.0 && overallRedWinrate == 0.0) { return(50.0); } return(100 * overallBlueWinrate / (overallBlueWinrate + overallRedWinrate)); }
public void AddByInhouseGame(InhouseGame inhouseGame, PlayerSide winningSide) { foreach (var player in inhouseGame.InhousePlayers) { var playerDb = GetByPlayerId(player.Player.Id); if (playerDb == null) { playerDb = new InhousePlayerStatDb(player.Player.Id); } if (player.PlayerSide == winningSide) { playerDb.Wins += 1; } else { playerDb.Loses += 1; } _dbset.Update(playerDb); } }
private async Task ReadyCheck(DiscordChannel channel, InhouseGame inhouseGame) { var inhouseQueue = InhouseQueues.FirstOrDefault(x => x.ChannelId == channel.Id); var mentionMsg = ""; foreach (var player in inhouseGame.InhousePlayers) { mentionMsg += $"{player.Player.Mention} "; } var embedBuilder = BuildGameFoundMessage(inhouseGame, inhouseQueue); var channelMessage = await channel.SendMessageAsync(mentionMsg, embed : embedBuilder.Build()).ConfigureAwait(false); var checkEmoji = DiscordEmoji.FromName(_client, ":white_check_mark:"); var xEmoji = DiscordEmoji.FromName(_client, ":x:"); await channelMessage.CreateReactionAsync(checkEmoji).ConfigureAwait(false); await channelMessage.CreateReactionAsync(xEmoji).ConfigureAwait(false); await Task.Run(async() => { while (true) { var interactivity = _client.GetInteractivity(); var response = await interactivity.WaitForReactionAsync(x => x.Message.Id == channelMessage.Id && x.User.Id != _client.CurrentUser.Id).ConfigureAwait(false); if (response.TimedOut) { // put everyone who was ready back to queue foreach (var queueGroup in inhouseGame.QueueGroups) { var removedPlayers = new List <InhousePlayer>(); foreach (var inhousePlayer in queueGroup.Players) { if (inhousePlayer.PlayerStatus == PlayerStatus.Ready) { inhousePlayer.PlayerStatus = PlayerStatus.None; } else { removedPlayers.Add(inhousePlayer); } } queueGroup.Players.RemoveAll(x => removedPlayers.Any(y => x == y)); inhouseQueue.PlayersInQueue.Insert(0, queueGroup); } await ShowQueue(channel, "Game timed out, readied players will now be put back in queue.").ConfigureAwait(false); InhouseGames.Remove(inhouseGame); return; } if (response.Result.Emoji == checkEmoji) { // check if it is one of the players var player = inhouseGame.InhousePlayers.FirstOrDefault(x => x.Player.Id == response.Result.User.Id); if (player == null) { continue; } var readyPlayer = inhouseGame.InhousePlayers.FirstOrDefault(x => x.Player.Id == response.Result.User.Id); readyPlayer.PlayerStatus = PlayerStatus.Ready; var embedBuilder = BuildGameFoundMessage(inhouseGame, inhouseQueue); await channelMessage.ModifyAsync(embed: embedBuilder.Build()).ConfigureAwait(false); if (inhouseGame.InhousePlayers.All(x => x.PlayerStatus == PlayerStatus.Ready)) { await channel.SendMessageAsync($"Everybody has readied, game will now start\nUse {_prefix}won to score the game."); return; } } else if (response.Result.Emoji == xEmoji) { // check if it is one of the players var player = inhouseGame.InhousePlayers.FirstOrDefault(x => x.Player.Id == response.Result.User.Id); if (player == null) { continue; } // put everyone back to queue except the author foreach (var queueGroup in inhouseGame.QueueGroups) { var removedPlayers = new List <InhousePlayer>(); foreach (var inhousePlayer in queueGroup.Players) { if (inhousePlayer.Player.Id == response.Result.User.Id) { removedPlayers.Add(inhousePlayer); } else { inhousePlayer.PlayerStatus = PlayerStatus.None; } } queueGroup.Players.RemoveAll(x => removedPlayers.Any(y => x == y)); inhouseQueue.PlayersInQueue.Insert(0, queueGroup); } InhouseGames.Remove(inhouseGame); await ShowQueue(channel, "A player cancelled the game and was removed from the queue\nAll other players have been put back into the queue").ConfigureAwait(false); return; } } }); }
private async Task AttemptMatchmake(DiscordChannel channel) { var inhouseQueue = InhouseQueues.FirstOrDefault(x => x.ChannelId == channel.Id); // if less than 10 players in queue, no game can be made if (inhouseQueue.PlayersInQueue.Sum(x => x.Players.Count()) < 10) { return; } // brute force check for matchmake with buckets var buckets = new List <MatchmakeBucket>(); buckets.Add(new MatchmakeBucket()); // we reserve SOLO fill players for last MatchmakeBucket filledBucket = null; var fillPlayers = new List <QueueGroup>(); foreach (var queueGroup in inhouseQueue.PlayersInQueue) { var tempBuckets = new List <MatchmakeBucket>(); // check for SOLO fill players if (queueGroup.Players.Count() == 1 && queueGroup.Players[0].QueuedRoles[PlayerRole.Fill]) { fillPlayers.Add(queueGroup); var maxBucket = buckets.FirstOrDefault(x => x.PlayerCount + fillPlayers.Count() >= 10); if (maxBucket != null) { filledBucket = maxBucket; break; } continue; } // get each role combination from each queueGroup foreach (var bucket in buckets) { // check to see if a copy of bucket without roleCombination is already in var hasOldBucket = false; var roleCombinations = queueGroup.PossibleRoleCombinations(); while (roleCombinations.Count() > 0) { var scramble = AegisRandom.RandomNumber(0, roleCombinations.Count()); var roleCombination = roleCombinations[scramble]; roleCombinations.RemoveAt(scramble); var playerSides = AegisRandom.RandomBool() ? new List <PlayerSide>() { PlayerSide.Blue, PlayerSide.Red } : new List <PlayerSide>() { PlayerSide.Red, PlayerSide.Blue }; foreach (var i in playerSides) { var tempBucket = new MatchmakeBucket(bucket); var canPutIntoBucket = tempBucket.TryAddRoleCombination(roleCombination, i); if (canPutIntoBucket) { tempBucket.QueueGroups.Add(queueGroup); tempBuckets.Add(tempBucket); } else if (!hasOldBucket) { tempBuckets.Add(tempBucket); hasOldBucket = true; } if (tempBucket.PlayerCount + fillPlayers.Count() >= 10) { filledBucket = tempBucket; break; } } if (filledBucket != null) { break; } } if (filledBucket != null) { break; } } buckets = tempBuckets; if (filledBucket != null) { break; } } // if no bucket is filled, then no game is found if (filledBucket == null) { return; } // now we put all fill players in var missingBlueRoles = new List <PlayerRole>(); var missingRedRoles = new List <PlayerRole>(); foreach (PlayerRole role in Enum.GetValues(typeof(PlayerRole))) { if (role == PlayerRole.Fill) { continue; } missingBlueRoles.Add(role); missingRedRoles.Add(role); } foreach (var role in filledBucket.BluePlayers) { missingBlueRoles.Remove(role.Key); } foreach (var role in filledBucket.RedPlayers) { missingRedRoles.Remove(role.Key); } foreach (var fillPlayer in fillPlayers) { var scramble = AegisRandom.RandomNumber(0, missingBlueRoles.Count() + missingRedRoles.Count()); if (scramble < missingBlueRoles.Count()) { var role = missingBlueRoles[scramble]; missingBlueRoles.RemoveAt(scramble); filledBucket.BluePlayers.Add(role, fillPlayer.Players[0]); filledBucket.QueueGroups.Add(fillPlayer); filledBucket.PlayerCount += 1; } else { var role = missingRedRoles[scramble - missingBlueRoles.Count()]; missingRedRoles.RemoveAt(scramble - missingBlueRoles.Count()); filledBucket.RedPlayers.Add(role, fillPlayer.Players[0]); filledBucket.QueueGroups.Add(fillPlayer); filledBucket.PlayerCount += 1; } } // insert players into game and randomize side var inhouseGame = new InhouseGame(channel.Id); foreach (var player in filledBucket.BluePlayers) { player.Value.PlayerSide = PlayerSide.Blue; player.Value.PlayerRole = player.Key; inhouseGame.InhousePlayers.Add(player.Value); } foreach (var player in filledBucket.RedPlayers) { player.Value.PlayerSide = PlayerSide.Red; player.Value.PlayerRole = player.Key; inhouseGame.InhousePlayers.Add(player.Value); } InhouseGames.Add(inhouseGame); inhouseGame.QueueGroups = filledBucket.QueueGroups; // remove everybody in the game from queue var inhousePlayers = inhouseGame.InhousePlayers.Select(x => x.Player.Id); inhouseQueue.PlayersInQueue.RemoveAll(x => filledBucket.QueueGroups.Any(y => y == x)); await ReadyCheck(channel, inhouseGame).ConfigureAwait(false); }