diff --git a/PartyModes/PartyModeChallenge/CChallengeRounds.cs b/PartyModes/PartyModeChallenge/CChallengeRounds.cs index 5ec9f331..d09a3b3b 100644 --- a/PartyModes/PartyModeChallenge/CChallengeRounds.cs +++ b/PartyModes/PartyModeChallenge/CChallengeRounds.cs @@ -17,218 +17,110 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using VocaluxeLib.Utils.Combinatorics; namespace VocaluxeLib.PartyModes.Challenge { public class CChallengeRounds { - private readonly Random _Rand; - private readonly int _NumPlayer; - - public readonly List Rounds; + private readonly List _Rounds = new List(); public int Count { - get { return Rounds.Count; } + get { return _Rounds.Count; } } - public CCombination this[int index] + public CRound this[int index] { - get { return Rounds[index]; } + get { return _Rounds[index]; } } - public CChallengeRounds(int numRounds, int numPlayer, int numPlayerAtOnce) + public CChallengeRounds(int numRounds, int numPlayer, int playersPerRound) { - Rounds = new List(); - _Rand = new Random(DateTime.Now.Millisecond); + if (numRounds < 1 || numPlayer < 1 || playersPerRound < 1) + throw new ArgumentException("Invalid paramters for the rounds"); - if (numPlayerAtOnce < 1 || numRounds < 1 || numPlayer == 0) - _NumPlayer = 0; - else - { - _NumPlayer = numPlayer; - _BuildRounds(numRounds, numPlayerAtOnce, _Rand); - } + if (playersPerRound > numPlayer) + playersPerRound = numPlayer; + + _BuildRounds(numRounds, numPlayer, playersPerRound); } - public CCombination GetRound(int roundNr) + /// + /// Divides players in 2 categories: Those how sung the most and the others + /// + /// List matching the playerIndex to the number of songs sung + private static List _GetPlayersForRound(IList numSung) { - if (roundNr >= Rounds.Count || _NumPlayer == 0 || Rounds.Count == 0) - return null; - - return Rounds[roundNr]; + List mustSing = new List(); + int max = numSung.Max(); + for (int i = 0; i < numSung.Count; i++) + { + if (numSung[i] < max) + mustSing.Add(i); + } + return mustSing; } - private void _BuildRounds(int numRounds, int numPlayerAtOnce, Random rand) + private static void _AddCombinations(int numPlayer, int playersPerRound, List combinations) { - CCombinations combinations = new CCombinations(_NumPlayer, numPlayerAtOnce, rand); - Rounds.Add(combinations.GetNextCombination(null)); - - for (int i = 1; i < numRounds; i++) - Rounds.Add(combinations.GetNextCombination(_GetPlayerDemand(i))); + List input = new List(numPlayer); + for (int i = 0; i < numPlayer; i++) + input.Add(i); + CCombinations combs = new CCombinations(input, playersPerRound); + combinations.AddRange(combs.Select(combination => new CRound(combination))); + combinations.Shuffle(); } - private IEnumerable _GetPlayerDemand(int roundIndex) + private static CRound _GetMatchingCombination(List playersInRound, List combinations) { - List numPlayed = new List(_NumPlayer); - for (int i = 0; i < _NumPlayer; i++) - numPlayed.Add(0); - - for (int i = 0; i < roundIndex; i++) + foreach (CRound round in combinations) { - foreach (int playerIndex in Rounds[i].Player) - numPlayed[playerIndex]++; + int matching = round.Players.Count(playersInRound.Contains); + // A valid round contains only players from playersInRound or all players from playersInRound (and some others) + if (matching == round.Players.Count || matching == playersInRound.Count) + return round; } + Debug.Assert(false, "This should not happen!"); + return combinations[0]; + } - int max = numPlayed.Max(); - - int playersPerRound = Rounds[0].Player.Count; - List result = new List(); - List other = new List(); - List last = new List(); - - for (int i = 0; i < _NumPlayer; i++) + private void _BuildRounds(int numRounds, int numPlayer, int playersPerRound) + { + // What we want is that every player sung the same amount of songs + // So we keep track how many songs each palyer sung + List numSung = new List(numPlayer); + for (int i = 0; i < numPlayer; i++) + numSung.Add(0); + List combinations = new List(); + // Posibly add more rounds than requested if the song count per player is unequal + for (int i = 0; i < numRounds * 10; i++) { - if (roundIndex == 0 || numPlayed[i] < max) - { - if (roundIndex == 0 || roundIndex > 0 && numPlayed[i] < max && !Rounds[roundIndex - 1].Contains(i)) - result.Add(i); - else if (numPlayed[i] < max) - other.Add(i); - } - else - { - if (roundIndex > 0) - { - if (!Rounds[roundIndex - 1].Contains(i)) - other.Add(i); - else - last.Add(i); - } - else - last.Add(i); - } + if (combinations.Count == 0) + _AddCombinations(numPlayer, playersPerRound, combinations); + List playersInRound = _GetPlayersForRound(numSung); + CRound curRound = _GetMatchingCombination(playersInRound, combinations); + foreach (int player in curRound.Players) + numSung[player]++; + _Rounds.Add(curRound); + //Stop if we have enough rounds and all players sung the same amount of songs + if (_Rounds.Count >= numRounds && numSung.All(ct => ct == numSung[0])) + break; } - - while (result.Count < playersPerRound && other.Count > 0) - { - int n = other.Count; - int r = _Rand.Next(n); - result.Add(other[r]); - other.RemoveAt(r); - } - - while (result.Count < playersPerRound) - { - int n = last.Count; - int r = _Rand.Next(n); - result.Add(last[r]); - last.RemoveAt(r); - } - - while (result.Count > playersPerRound) - { - int n = result.Count; - int r = _Rand.Next(n); - result.RemoveAt(r); - } - - result.Sort(); - return result; + if (_Rounds.Count != numRounds) + CBase.Log.LogDebug("Number of rounds does not match. Expected: " + numRounds + ", Is: " + _Rounds.Count + " for " + numPlayer + "/" + playersPerRound); } } - class CCombinations + public class CRound { - private readonly List _Combs; - private readonly Random _Rand; - private readonly int _NumPlayer; - private readonly int _NumMics; + public readonly List Players; - public CCombinations(int numPlayer, int numMics, Random rand) + public CRound(List players) { - _Combs = new List(); - _NumMics = numMics; - _NumPlayer = numPlayer; - _Rand = rand; - } - - public CCombination GetNextCombination(IEnumerable playerNrDemand) - { - if (_Combs.Count == 0) - _Create(); - - if (_NumPlayer == _NumMics) - return _Combs[0]; - - //filter against PlayerNrDemand - List combsFiltered = new List(); - if (playerNrDemand != null) - combsFiltered.AddRange(_Combs.Where(t => t.ContainsAll(playerNrDemand))); - - //1st fallback - if (playerNrDemand != null && combsFiltered.Count == 0) - combsFiltered.AddRange(_Combs.Where(t => t.ContainsAny(playerNrDemand))); - - //2nd fallback - if (combsFiltered.Count == 0) - combsFiltered.AddRange(_Combs); - - int num = combsFiltered.Count; - int rand = _Rand.Next(num); - CCombination c = new CCombination(); - for (int i = 0; i < combsFiltered[rand].Player.Count; i++) - c.Player.Add(combsFiltered[rand].Player[i]); - _Combs.Remove(combsFiltered[rand]); - - return combsFiltered[rand]; - } - - private void _Create() - { - _Combs.Clear(); - - int num = (int)Math.Pow(2, _NumPlayer); - - for (int i = 1; i < num; i++) - { - CCombination c = new CCombination(); - - for (int j = 0; j < _NumPlayer; j++) - { - if ((i & (1 << j)) > 0) - c.Player.Add(j); - } - - if (c.Player.Count == _NumMics) - _Combs.Add(c); - } - } - } - - public class CCombination - { - public readonly List Player; - - public CCombination() - { - Player = new List(); - } - - public bool Contains(int playerIndex) - { - return Player.Any(p => p == playerIndex); - } - - public bool ContainsAll(IEnumerable playerIndices) - { - return playerIndices.All(Contains); - } - - public bool ContainsAny(IEnumerable playerIndices) - { - return playerIndices.Any(Contains); + Players = players.Shuffle(); } } } \ No newline at end of file diff --git a/PartyModes/PartyModeChallenge/CPartyModeChallenge.cs b/PartyModes/PartyModeChallenge/CPartyModeChallenge.cs index 4abd7de8..938bddc6 100644 --- a/PartyModes/PartyModeChallenge/CPartyModeChallenge.cs +++ b/PartyModes/PartyModeChallenge/CPartyModeChallenge.cs @@ -341,13 +341,13 @@ namespace VocaluxeLib.PartyModes.Challenge if (players.Length < GameData.NumPlayerAtOnce) return; - CCombination c = GameData.Rounds.GetRound(GameData.CurrentRoundNr - 1); + CRound c = GameData.Rounds[GameData.CurrentRoundNr - 1]; for (int i = 0; i < GameData.NumPlayerAtOnce; i++) { //try to fill with the right data if (c != null) - players[i].ProfileID = GameData.ProfileIDs[c.Player[i]]; + players[i].ProfileID = GameData.ProfileIDs[c.Players[i]]; else players[i].ProfileID = -1; } @@ -413,15 +413,10 @@ namespace VocaluxeLib.PartyModes.Challenge } _ScreenSongOptions.Selection.TeamNames = new string[GameData.NumPlayerAtOnce]; - CCombination c = GameData.Rounds.GetRound(GameData.CurrentRoundNr - 1); + CRound c = GameData.Rounds[GameData.CurrentRoundNr - 1]; for (int i = 0; i < GameData.NumPlayerAtOnce; i++) - { - if (c != null) - _ScreenSongOptions.Selection.TeamNames[i] = CBase.Profiles.GetPlayerName(GameData.ProfileIDs[c.Player[i]]); - else - _ScreenSongOptions.Selection.TeamNames[i] = "foobar"; - } + _ScreenSongOptions.Selection.TeamNames[i] = CBase.Profiles.GetPlayerName(GameData.ProfileIDs[c.Players[i]]); } private void _UpdateScores() diff --git a/PartyModes/PartyModeChallenge/CPartyScreenChallengeMain.cs b/PartyModes/PartyModeChallenge/CPartyScreenChallengeMain.cs index 0c06f9b7..5e10bcff 100644 --- a/PartyModes/PartyModeChallenge/CPartyScreenChallengeMain.cs +++ b/PartyModes/PartyModeChallenge/CPartyScreenChallengeMain.cs @@ -340,7 +340,7 @@ namespace VocaluxeLib.PartyModes.Challenge _Texts[_TextNextPlayerMessage].Visible = true; for (int i = 0; i < _PartyMode.GameData.NumPlayerAtOnce; i++) { - int id = _PartyMode.GameData.ProfileIDs[_PartyMode.GameData.Rounds[_PartyMode.GameData.CurrentRoundNr - 1].Player[i]]; + int id = _PartyMode.GameData.ProfileIDs[_PartyMode.GameData.Rounds[_PartyMode.GameData.CurrentRoundNr - 1].Players[i]]; _NextPlayerStatics[i].Texture = CBase.Profiles.GetAvatar(id); _NextPlayerTexts[i].Text = CBase.Profiles.GetPlayerName(id); _NextPlayerTexts[i].Color = CBase.Themes.GetPlayerColor(i + 1); @@ -452,14 +452,14 @@ namespace VocaluxeLib.PartyModes.Challenge { for (int p = 0; p < _RoundsTable[i].TextPlayer.Count; p++) { - if (_PartyMode.GameData.Rounds.Count > i + _RoundsTableOffset && _PartyMode.GameData.Rounds[i + _RoundsTableOffset].Player.Count > p) + if (_PartyMode.GameData.Rounds.Count > i + _RoundsTableOffset && _PartyMode.GameData.Rounds[i + _RoundsTableOffset].Players.Count > p) { _RoundsTable[i].Number.Visible = true; _RoundsTable[i].TextPlayer[p].Visible = true; _RoundsTable[i].TextScores[p].Visible = true; _RoundsTable[i].Number.Text = (i + 1 + _RoundsTableOffset) + ")"; _RoundsTable[i].TextPlayer[p].Text = - CBase.Profiles.GetPlayerName(_PartyMode.GameData.ProfileIDs[_PartyMode.GameData.Rounds[i + _RoundsTableOffset].Player[p]]); + CBase.Profiles.GetPlayerName(_PartyMode.GameData.ProfileIDs[_PartyMode.GameData.Rounds[i + _RoundsTableOffset].Players[p]]); // ReSharper disable ConvertIfStatementToConditionalTernaryExpression if ((_PartyMode.GameData.CurrentRoundNr - 1) > i + _RoundsTableOffset) // ReSharper restore ConvertIfStatementToConditionalTernaryExpression diff --git a/PartyModes/PartyModeChallengeMedley/CChallengeRounds.cs b/PartyModes/PartyModeChallengeMedley/CChallengeRounds.cs index 01b6068e..caa15ccc 100644 --- a/PartyModes/PartyModeChallengeMedley/CChallengeRounds.cs +++ b/PartyModes/PartyModeChallengeMedley/CChallengeRounds.cs @@ -17,218 +17,110 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using VocaluxeLib.Utils.Combinatorics; namespace VocaluxeLib.PartyModes.ChallengeMedley { public class CChallengeRounds { - private readonly Random _Rand; - private readonly int _NumPlayer; - - public readonly List Rounds; + private readonly List _Rounds = new List(); public int Count { - get { return Rounds.Count; } + get { return _Rounds.Count; } } - public CCombination this[int index] + public CRound this[int index] { - get { return Rounds[index]; } + get { return _Rounds[index]; } } - public CChallengeRounds(int numRounds, int numPlayer, int numPlayerAtOnce) + public CChallengeRounds(int numRounds, int numPlayer, int playersPerRound) { - Rounds = new List(); - _Rand = new Random(DateTime.Now.Millisecond); + if (numRounds < 1 || numPlayer < 1 || playersPerRound < 1) + throw new ArgumentException("Invalid paramters for the rounds"); - if (numPlayerAtOnce < 1 || numRounds < 1 || numPlayer == 0) - _NumPlayer = 0; - else - { - _NumPlayer = numPlayer; - _BuildRounds(numRounds, numPlayerAtOnce, _Rand); - } + if (playersPerRound > numPlayer) + playersPerRound = numPlayer; + + _BuildRounds(numRounds, numPlayer, playersPerRound); } - public CCombination GetRound(int roundNr) + /// + /// Divides players in 2 categories: Those how sung the most and the others + /// + /// List matching the playerIndex to the number of songs sung + private static List _GetPlayersForRound(IList numSung) { - if (roundNr >= Rounds.Count || _NumPlayer == 0 || Rounds.Count == 0) - return null; - - return Rounds[roundNr]; + List mustSing = new List(); + int max = numSung.Max(); + for (int i = 0; i < numSung.Count; i++) + { + if (numSung[i] < max) + mustSing.Add(i); + } + return mustSing; } - private void _BuildRounds(int numRounds, int numPlayerAtOnce, Random rand) + private static void _AddCombinations(int numPlayer, int playersPerRound, List combinations) { - CCombinations combinations = new CCombinations(_NumPlayer, numPlayerAtOnce, rand); - Rounds.Add(combinations.GetNextCombination(null)); - - for (int i = 1; i < numRounds; i++) - Rounds.Add(combinations.GetNextCombination(_GetPlayerDemand(i))); + List input = new List(numPlayer); + for (int i = 0; i < numPlayer; i++) + input.Add(i); + CCombinations combs = new CCombinations(input, playersPerRound); + combinations.AddRange(combs.Select(combination => new CRound(combination))); + combinations.Shuffle(); } - private IEnumerable _GetPlayerDemand(int roundIndex) + private static CRound _GetMatchingCombination(List playersInRound, List combinations) { - List numPlayed = new List(_NumPlayer); - for (int i = 0; i < _NumPlayer; i++) - numPlayed.Add(0); - - for (int i = 0; i < roundIndex; i++) + foreach (CRound round in combinations) { - foreach (int playerIndex in Rounds[i].Player) - numPlayed[playerIndex]++; + int matching = round.Players.Count(playersInRound.Contains); + // A valid round contains only players from playersInRound or all players from playersInRound (and some others) + if (matching == round.Players.Count || matching == playersInRound.Count) + return round; } + Debug.Assert(false, "This should not happen!"); + return combinations[0]; + } - int max = numPlayed.Max(); - - int num = Rounds[0].Player.Count; - List result = new List(); - List other = new List(); - List last = new List(); - - for (int i = 0; i < numPlayed.Count; i++) + private void _BuildRounds(int numRounds, int numPlayer, int playersPerRound) + { + // What we want is that every player sung the same amount of songs + // So we keep track how many songs each palyer sung + List numSung = new List(numPlayer); + for (int i = 0; i < numPlayer; i++) + numSung.Add(0); + List combinations = new List(); + // Posibly add more rounds than requested if the song count per player is unequal + for (int i = 0; i < numRounds * 10; i++) { - if (roundIndex == 0 || numPlayed[i] < max) - { - if (roundIndex == 0 || roundIndex > 0 && numPlayed[i] < max && !Rounds[roundIndex - 1].IsAvailable(i)) - result.Add(i); - else if (numPlayed[i] < max) - other.Add(i); - } - else - { - if (roundIndex > 0) - { - if (!Rounds[roundIndex - 1].IsAvailable(i)) - other.Add(i); - else - last.Add(i); - } - else - last.Add(i); - } + if (combinations.Count == 0) + _AddCombinations(numPlayer, playersPerRound, combinations); + List playersInRound = _GetPlayersForRound(numSung); + CRound curRound = _GetMatchingCombination(playersInRound, combinations); + foreach (int player in curRound.Players) + numSung[player]++; + _Rounds.Add(curRound); + //Stop if we have enough rounds and all players sung the same amount of songs + if (_Rounds.Count >= numRounds && numSung.All(ct => ct == numSung[0])) + break; } - - while (result.Count < num && other.Count > 0) - { - int n = other.Count; - int r = _Rand.Next(n); - result.Add(other[r]); - other.RemoveAt(r); - } - - while (result.Count < num) - { - int n = last.Count; - int r = _Rand.Next(n); - result.Add(last[r]); - last.RemoveAt(r); - } - - while (result.Count > num) - { - int n = result.Count; - int r = _Rand.Next(n); - result.RemoveAt(r); - } - - result.Sort(); - return result; + if (_Rounds.Count != numRounds) + CBase.Log.LogDebug("Number of rounds does not match. Expected: " + numRounds + ", Is: " + _Rounds.Count + " for " + numPlayer + "/" + playersPerRound); } } - class CCombinations + public class CRound { - private readonly List _Combs; - private readonly Random _Rand; - private readonly int _NumPlayer; - private readonly int _NumMics; + public readonly List Players; - public CCombinations(int numPlayer, int numMics, Random rand) + public CRound(List players) { - _Combs = new List(); - _NumMics = numMics; - _NumPlayer = numPlayer; - _Rand = rand; - } - - public CCombination GetNextCombination(IEnumerable playerNrDemand) - { - if (_Combs.Count == 0) - _Create(); - - if (_NumPlayer == _NumMics) - return _Combs[0]; - - //filter against PlayerNrDemand - List combsFiltered = new List(); - if (playerNrDemand != null) - combsFiltered.AddRange(_Combs.Where(t => t.IsAvailableAll(playerNrDemand))); - - //1st fallback - if (playerNrDemand != null && combsFiltered.Count == 0) - combsFiltered.AddRange(_Combs.Where(t => t.IsAvailableSomeone(playerNrDemand))); - - //2nd fallback - if (combsFiltered.Count == 0) - combsFiltered.AddRange(_Combs); - - int num = combsFiltered.Count; - int rand = _Rand.Next(num); - CCombination c = new CCombination(); - for (int i = 0; i < combsFiltered[rand].Player.Count; i++) - c.Player.Add(combsFiltered[rand].Player[i]); - _Combs.Remove(combsFiltered[rand]); - - return combsFiltered[rand]; - } - - private void _Create() - { - _Combs.Clear(); - - int num = (int)Math.Pow(2, _NumPlayer); - - for (int i = 1; i <= num; i++) - { - CCombination c = new CCombination(); - - for (int j = 0; j < _NumPlayer; j++) - { - if ((i & (1 << j)) > 0) - c.Player.Add(j); - } - - if (c.Player.Count == _NumMics) - _Combs.Add(c); - } - } - } - - public class CCombination - { - public readonly List Player; - - public CCombination() - { - Player = new List(); - } - - public bool IsAvailable(int playerIndex) - { - return Player.Any(p => p == playerIndex); - } - - public bool IsAvailableAll(IEnumerable playerIndices) - { - return playerIndices.All(IsAvailable); - } - - public bool IsAvailableSomeone(IEnumerable playerIndices) - { - return playerIndices.Any(IsAvailable); + Players = players.Shuffle(); } } } \ No newline at end of file diff --git a/PartyModes/PartyModeChallengeMedley/CPartyModeChallengeMedley.cs b/PartyModes/PartyModeChallengeMedley/CPartyModeChallengeMedley.cs index e3b4c45f..8d433eaf 100644 --- a/PartyModes/PartyModeChallengeMedley/CPartyModeChallengeMedley.cs +++ b/PartyModes/PartyModeChallengeMedley/CPartyModeChallengeMedley.cs @@ -366,13 +366,13 @@ namespace VocaluxeLib.PartyModes.ChallengeMedley if (players.Length < GameData.NumPlayerAtOnce) return false; - CCombination c = GameData.Rounds.GetRound(GameData.CurrentRoundNr - 1); + CRound c = GameData.Rounds[GameData.CurrentRoundNr - 1]; for (int i = 0; i < GameData.NumPlayerAtOnce; i++) { //try to fill with the right data if (c != null) - players[i].ProfileID = GameData.ProfileIDs[c.Player[i]]; + players[i].ProfileID = GameData.ProfileIDs[c.Players[i]]; else players[i].ProfileID = -1; } @@ -475,14 +475,14 @@ namespace VocaluxeLib.PartyModes.ChallengeMedley } _ScreenSongOptions.Selection.TeamNames = new string[GameData.NumPlayerAtOnce]; - CCombination c = GameData.Rounds.GetRound(GameData.CurrentRoundNr - 1); + CRound c = GameData.Rounds[GameData.CurrentRoundNr - 1]; for (int i = 0; i < GameData.NumPlayerAtOnce; i++) { if (c != null) { - if (GameData.ProfileIDs[c.Player[i]] < profiles.Length) - _ScreenSongOptions.Selection.TeamNames[i] = profiles[GameData.ProfileIDs[c.Player[i]]].PlayerName; + if (GameData.ProfileIDs[c.Players[i]] < profiles.Length) + _ScreenSongOptions.Selection.TeamNames[i] = profiles[GameData.ProfileIDs[c.Players[i]]].PlayerName; else _ScreenSongOptions.Selection.TeamNames[i] = "foobar"; } diff --git a/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyMain.cs b/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyMain.cs index f3e5723d..8e2841b1 100644 --- a/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyMain.cs +++ b/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyMain.cs @@ -340,7 +340,7 @@ namespace VocaluxeLib.PartyModes.ChallengeMedley _Texts[_TextNextPlayerMessage].Visible = true; for (int i = 0; i < _PartyMode.GameData.NumPlayerAtOnce; i++) { - int id = _PartyMode.GameData.ProfileIDs[_PartyMode.GameData.Rounds[_PartyMode.GameData.CurrentRoundNr - 1].Player[i]]; + int id = _PartyMode.GameData.ProfileIDs[_PartyMode.GameData.Rounds[_PartyMode.GameData.CurrentRoundNr - 1].Players[i]]; _NextPlayerStatics[i].Texture = CBase.Profiles.GetAvatar(id); _NextPlayerTexts[i].Text = CBase.Profiles.GetPlayerName(id); _NextPlayerTexts[i].Color = CBase.Themes.GetPlayerColor(i + 1); @@ -452,14 +452,14 @@ namespace VocaluxeLib.PartyModes.ChallengeMedley { for (int p = 0; p < _RoundsTable[i].TextPlayer.Count; p++) { - if (_PartyMode.GameData.Rounds.Count > i + _RoundsTableOffset && _PartyMode.GameData.Rounds[i + _RoundsTableOffset].Player.Count > p) + if (_PartyMode.GameData.Rounds.Count > i + _RoundsTableOffset && _PartyMode.GameData.Rounds[i + _RoundsTableOffset].Players.Count > p) { _RoundsTable[i].Number.Visible = true; _RoundsTable[i].TextPlayer[p].Visible = true; _RoundsTable[i].TextScores[p].Visible = true; _RoundsTable[i].Number.Text = (i + 1 + _RoundsTableOffset) + ")"; _RoundsTable[i].TextPlayer[p].Text = - CBase.Profiles.GetPlayerName(_PartyMode.GameData.ProfileIDs[_PartyMode.GameData.Rounds[i + _RoundsTableOffset].Player[p]]); + CBase.Profiles.GetPlayerName(_PartyMode.GameData.ProfileIDs[_PartyMode.GameData.Rounds[i + _RoundsTableOffset].Players[p]]); // ReSharper disable ConvertIfStatementToConditionalTernaryExpression if ((_PartyMode.GameData.CurrentRoundNr - 1) > i + _RoundsTableOffset) // ReSharper restore ConvertIfStatementToConditionalTernaryExpression diff --git a/PartyModes/PartyModeTicTacToe/CPartyModeTicTacToe.cs b/PartyModes/PartyModeTicTacToe/CPartyModeTicTacToe.cs index 62c281ba..81d3c308 100644 --- a/PartyModes/PartyModeTicTacToe/CPartyModeTicTacToe.cs +++ b/PartyModes/PartyModeTicTacToe/CPartyModeTicTacToe.cs @@ -415,17 +415,6 @@ namespace VocaluxeLib.PartyModes.TicTacToe } } - private static void _Shuffle(IList list) - { - for (int n = list.Count - 1; n >= 1; n--) - { - int k = CBase.Game.GetRandom(n); - T value = list[k]; - list[k] = list[n]; - list[n] = value; - } - } - public void UpdateSongList() { if (GameData.Songs.Count > 0) @@ -455,7 +444,7 @@ namespace VocaluxeLib.PartyModes.TicTacToe CBase.Songs.SetCategory(-1); break; } - _Shuffle(GameData.Songs); + GameData.Songs.Shuffle(); } private void _UpdatePlayerList() diff --git a/VocaluxeLib/Extensions.cs b/VocaluxeLib/Extensions.cs index 12f06137..5e36958d 100644 --- a/VocaluxeLib/Extensions.cs +++ b/VocaluxeLib/Extensions.cs @@ -178,6 +178,18 @@ namespace VocaluxeLib rect.Z); } + public static List Shuffle(this List list) + { + for (int n = list.Count - 1; n >= 1; n--) + { + int k = CBase.Game.GetRandom(n); + T value = list[k]; + list[k] = list[n]; + list[n] = value; + } + return list; + } + public static Size GetSize(this Bitmap bmp) { return new Size(bmp.Width, bmp.Height); diff --git a/VocaluxeLib/Utils/Combinatorics/CCombinations.cs b/VocaluxeLib/Utils/Combinatorics/CCombinations.cs index 5a147fc2..78e5c0b7 100644 --- a/VocaluxeLib/Utils/Combinatorics/CCombinations.cs +++ b/VocaluxeLib/Utils/Combinatorics/CCombinations.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; namespace VocaluxeLib.Utils.Combinatorics { @@ -143,8 +144,7 @@ namespace VocaluxeLib.Utils.Combinatorics get { List result = new List(_K); - for (int i = 0; i < _K; i++) - result.Add(_Parent._Values[_CurSet[i]]); + result.AddRange(_CurSet.Select(i => _Parent._Values[i])); return result; } } diff --git a/VocaluxeTests/PartyModeTests.cs b/VocaluxeTests/PartyModeTests.cs new file mode 100644 index 00000000..1678b7ff --- /dev/null +++ b/VocaluxeTests/PartyModeTests.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Vocaluxe.Base; +using VocaluxeLib; +using VocaluxeLib.PartyModes.Challenge; + +namespace VocaluxeTests +{ + [TestClass] + public class CPartyModeTests + { + [TestMethod] + public void TestRoundBoundaries() + { + // ReSharper disable ObjectCreationAsStatement + new CChallengeRounds(1, 1, 1); // Should succeed + // ReSharper restore ObjectCreationAsStatement + for (int inv = -1; inv <= 0; inv++) + { + int invL = inv; + CTestHelpers.AssertFail(() => new CChallengeRounds(invL, 1, 1)); + CTestHelpers.AssertFail(() => new CChallengeRounds(1, invL, 1)); + CTestHelpers.AssertFail(() => new CChallengeRounds(1, 1, invL)); + } + } + + private static void _CheckRounds(CChallengeRounds rounds, int numPlayer) + { + List numSongs = new List(numPlayer); + for (int i = 0; i < numPlayer; i++) + numSongs.Add(0); + for (int i = 0; i < rounds.Count; i++) + { + foreach (int player in rounds[i].Players) + { + Assert.IsTrue(player >= 0 && player < numPlayer); + numSongs[player]++; + } + } + Assert.IsTrue(numSongs.Min() == numSongs.Max(), "Some players have more songs than others"); + } + + [TestMethod] + public void TestRoundGeneration() + { + CBase.Game = new CBGame(); + CLog.Init(); + CBase.Log = new CBlog(); + for (int i = 0; i < 10; i++) + { + for (int numPlayer = 1; numPlayer <= 20; numPlayer++) + { + for (int numMic = 1; numMic <= 6; numMic++) + { + int roundFactor = (numPlayer % numMic == 0) ? numPlayer / numMic : numPlayer; + for (int numRounds = roundFactor; numRounds <= 100 && numRounds <= roundFactor * 5; numRounds += roundFactor) + { + CChallengeRounds rounds = new CChallengeRounds(numRounds, numPlayer, numMic); + Assert.IsTrue(rounds.Count >= numRounds); + _CheckRounds(rounds, numPlayer); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/VocaluxeTests/VocaluxeTests.csproj b/VocaluxeTests/VocaluxeTests.csproj index 3b8f65dc..88233b22 100644 --- a/VocaluxeTests/VocaluxeTests.csproj +++ b/VocaluxeTests/VocaluxeTests.csproj @@ -73,9 +73,14 @@ + + + {5a4053f1-5ad5-441f-b254-d97a088127b6} + PartyModeChallenge + {d2b6ddef-e0e1-4ddf-a73a-e6acef5c95ec} VocaluxeLib