[Raid] Add preliminary roster assigner
This commit is contained in:
@@ -44,6 +44,7 @@ public class RaidFormatter
|
|||||||
var separatorLength = Math.Max(nonSubstitute.Select(p => p.Name.Length).Max(), substitute.Select(p => p.Name.Length).Max());
|
var separatorLength = Math.Max(nonSubstitute.Select(p => p.Name.Length).Max(), substitute.Select(p => p.Name.Length).Max());
|
||||||
separatorLength = (int) ((separatorLength + 13) * 0.49); // Don't ask why, it just works
|
separatorLength = (int) ((separatorLength + 13) * 0.49); // Don't ask why, it just works
|
||||||
|
|
||||||
|
// Todo add Total FC number
|
||||||
return new EmbedFieldBuilder()
|
return new EmbedFieldBuilder()
|
||||||
.WithName($"Roster {rosterNumber}")
|
.WithName($"Roster {rosterNumber}")
|
||||||
.WithValue($"{string.Join("\n", nonSubstitute.Select(FormatRosterPlayer))}\n{new string('━', separatorLength)}\n{string.Join("\n", substitute.Select(FormatRosterPlayer))}")
|
.WithValue($"{string.Join("\n", nonSubstitute.Select(FormatRosterPlayer))}\n{new string('━', separatorLength)}\n{string.Join("\n", substitute.Select(FormatRosterPlayer))}")
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
public class RaidRegisterManager
|
public class RaidRegisterManager
|
||||||
{
|
{
|
||||||
public IDictionary<(ulong raidId, ulong playerId), RosterPlayer> RegisteringPlayers =
|
public readonly IDictionary<(ulong raidId, ulong playerId), RosterPlayer> RegisteringPlayers =
|
||||||
new Dictionary<(ulong raidId, ulong playerId), RosterPlayer>();
|
new Dictionary<(ulong raidId, ulong playerId), RosterPlayer>();
|
||||||
}
|
}
|
||||||
136
Cocotte/Modules/Raids/RosterAssigner.cs
Normal file
136
Cocotte/Modules/Raids/RosterAssigner.cs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
namespace Cocotte.Modules.Raids;
|
||||||
|
|
||||||
|
public class RosterAssigner
|
||||||
|
{
|
||||||
|
public void AssignRosters(IEnumerable<RosterPlayer> players, uint playersPerRoster)
|
||||||
|
{
|
||||||
|
// Start by grouping players
|
||||||
|
var groups = GroupPlayers(players.OrderByDescending(p => p.Fc));
|
||||||
|
|
||||||
|
// Create rosters
|
||||||
|
var neededRosters = (int)Math.Ceiling(players.Count(p => !p.Substitute) / (double)playersPerRoster);
|
||||||
|
var rosters = new List<RosterInfo>(Enumerable.Repeat(new RosterInfo(), neededRosters));
|
||||||
|
|
||||||
|
// Todo Check when there's more than max players per roster
|
||||||
|
|
||||||
|
// First pass: assign healers and tanks
|
||||||
|
// Always assign to the group which have the least amount of healer/tank, biased towards healers
|
||||||
|
// Skip groups without players
|
||||||
|
var dpsGroup = new List<PlayerGroup>();
|
||||||
|
foreach (var group in groups.Where(g => !g.AllSubstitutes))
|
||||||
|
{
|
||||||
|
if (group.Players.AnyHealer())
|
||||||
|
{
|
||||||
|
var nextHealerRoster = rosters.MinBy(r => r.RealHealerCount());
|
||||||
|
|
||||||
|
nextHealerRoster!.AddGroup(group);
|
||||||
|
}
|
||||||
|
else if (group.Players.AnyTank())
|
||||||
|
{
|
||||||
|
var nextTankRoster = rosters.MinBy(r => r.RealTankCount());
|
||||||
|
|
||||||
|
nextTankRoster!.AddGroup(group);
|
||||||
|
}
|
||||||
|
// Those groups will be used to assign dps, they should still be in descending order of FC
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dpsGroup.Add(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Third pass: assign dps
|
||||||
|
foreach (var group in dpsGroup)
|
||||||
|
{
|
||||||
|
var nextDpsRoster = rosters.MinBy(r => r.TotalRealFc);
|
||||||
|
|
||||||
|
nextDpsRoster!.AddGroup(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last pass: fill with substitute
|
||||||
|
|
||||||
|
// Assign rosters
|
||||||
|
for (int i = 0; i < rosters.Count; i++)
|
||||||
|
{
|
||||||
|
var roster = rosters[i];
|
||||||
|
|
||||||
|
roster.AssignRosterNumer(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList<PlayerGroup> GroupPlayers(IEnumerable<RosterPlayer> players)
|
||||||
|
{
|
||||||
|
var groups = new List<PlayerGroup>();
|
||||||
|
|
||||||
|
// Todo create groups from player preferences
|
||||||
|
foreach (var rosterPlayer in players)
|
||||||
|
{
|
||||||
|
groups.Add(new PlayerGroup(rosterPlayer));
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RosterInfo
|
||||||
|
{
|
||||||
|
public long TotalRealFc => _groups.Sum(g => g.RealFc);
|
||||||
|
public long TotalFc => _groups.Sum(g => g.TotalFc);
|
||||||
|
|
||||||
|
public IEnumerable<PlayerGroup> PlayerGroups => _groups.Where(g => !g.AllSubstitutes);
|
||||||
|
public IEnumerable<PlayerGroup> SubstituteGroups => _groups.Where(g => g.AllSubstitutes);
|
||||||
|
|
||||||
|
private readonly IList<PlayerGroup> _groups;
|
||||||
|
|
||||||
|
public RosterInfo()
|
||||||
|
{
|
||||||
|
_groups = new List<PlayerGroup>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddGroup(PlayerGroup group)
|
||||||
|
{
|
||||||
|
_groups.Add(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AssignRosterNumer(int rosterNumber)
|
||||||
|
{
|
||||||
|
foreach (var group in _groups)
|
||||||
|
{
|
||||||
|
group.AssignRosterNumer(rosterNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlayerGroup
|
||||||
|
{
|
||||||
|
public long RealFc => _players.Where(p => !p.Substitute).TotalFc();
|
||||||
|
public long TotalFc => _players.TotalFc();
|
||||||
|
public bool AllSubstitutes => _players.All(p => p.Substitute);
|
||||||
|
|
||||||
|
public IEnumerable<RosterPlayer> Players => _players.Where(p => !p.Substitute);
|
||||||
|
public IEnumerable<RosterPlayer> Substitutes => _players.Where(p => p.Substitute);
|
||||||
|
|
||||||
|
private readonly IList<RosterPlayer> _players;
|
||||||
|
|
||||||
|
public PlayerGroup()
|
||||||
|
{
|
||||||
|
_players = new List<RosterPlayer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerGroup(params RosterPlayer[] players)
|
||||||
|
{
|
||||||
|
_players = players;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddPlayer(RosterPlayer player)
|
||||||
|
{
|
||||||
|
_players.Add(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AssignRosterNumer(int rosterNumber)
|
||||||
|
{
|
||||||
|
foreach (var rosterPlayer in _players)
|
||||||
|
{
|
||||||
|
rosterPlayer.RosterNumber = rosterNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
Cocotte/Modules/Raids/RosterExtensions.cs
Normal file
39
Cocotte/Modules/Raids/RosterExtensions.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
namespace Cocotte.Modules.Raids;
|
||||||
|
|
||||||
|
public static class RosterExtensions
|
||||||
|
{
|
||||||
|
public static bool AnyHealer(this IEnumerable<RosterPlayer> players)
|
||||||
|
{
|
||||||
|
return players.Any(p => p.Role == PlayerRole.Healer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool AnyTank(this IEnumerable<RosterPlayer> players)
|
||||||
|
{
|
||||||
|
return players.Any(p => p.Role == PlayerRole.Tank);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int HealerCount(this IEnumerable<RosterPlayer> players)
|
||||||
|
{
|
||||||
|
return players.Count(p => p.Role == PlayerRole.Healer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int TankCount(this IEnumerable<RosterPlayer> players)
|
||||||
|
{
|
||||||
|
return players.Count(p => p.Role == PlayerRole.Tank);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long TotalFc(this IEnumerable<RosterPlayer> players)
|
||||||
|
{
|
||||||
|
return players.Sum(p => p.Fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int RealHealerCount(this RosterInfo rosterInfo)
|
||||||
|
{
|
||||||
|
return rosterInfo.PlayerGroups.Sum(g => g.Players.HealerCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int RealTankCount(this RosterInfo rosterInfo)
|
||||||
|
{
|
||||||
|
return rosterInfo.PlayerGroups.Sum(g => g.Players.TankCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,8 +38,9 @@ IHost host = Host.CreateDefaultBuilder(args)
|
|||||||
services.AddSingleton<RolesOptions>();
|
services.AddSingleton<RolesOptions>();
|
||||||
|
|
||||||
// Raids
|
// Raids
|
||||||
services.AddSingleton<RaidFormatter>();
|
services.AddTransient<RaidFormatter>();
|
||||||
services.AddSingleton<RaidRegisterManager>();
|
services.AddSingleton<RaidRegisterManager>();
|
||||||
|
services.AddTransient<RosterAssigner>();
|
||||||
|
|
||||||
// Custom
|
// Custom
|
||||||
services.AddSingleton<SharedCounter>();
|
services.AddSingleton<SharedCounter>();
|
||||||
|
|||||||
Reference in New Issue
Block a user