Rewrite round generation and add unit test
This commit is contained in:
1 parent
84e9163b96
commit
6d494b64b3
11 files changed
+240
-386
No files matched your search
@@ -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<CCombination> Rounds;
|
||||
private readonly List<CRound> _Rounds = new List<CRound>();
|
||||
|
||||
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<CCombination>();
|
||||
_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)
|
||||
/// <summary>
|
||||
/// Divides players in 2 categories: Those how sung the most and the others
|
||||
/// </summary>
|
||||
/// <param name="numSung">List matching the playerIndex to the number of songs sung</param>
|
||||
private static List<int> _GetPlayersForRound(IList<int> numSung)
|
||||
{
|
||||
if (roundNr >= Rounds.Count || _NumPlayer == 0 || Rounds.Count == 0)
|
||||
return null;
|
||||
|
||||
return Rounds[roundNr];
|
||||
List<int> mustSing = new List<int>();
|
||||
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<CRound> 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<int> input = new List<int>(numPlayer);
|
||||
for (int i = 0; i < numPlayer; i++)
|
||||
input.Add(i);
|
||||
CCombinations<int> combs = new CCombinations<int>(input, playersPerRound);
|
||||
combinations.AddRange(combs.Select(combination => new CRound(combination)));
|
||||
combinations.Shuffle();
|
||||
}
|
||||
|
||||
private IEnumerable<int> _GetPlayerDemand(int roundIndex)
|
||||
private static CRound _GetMatchingCombination(List<int> playersInRound, List<CRound> combinations)
|
||||
{
|
||||
List<int> numPlayed = new List<int>(_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<int> result = new List<int>();
|
||||
List<int> other = new List<int>();
|
||||
List<int> last = new List<int>();
|
||||
|
||||
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<int> numSung = new List<int>(numPlayer);
|
||||
for (int i = 0; i < numPlayer; i++)
|
||||
numSung.Add(0);
|
||||
List<CRound> combinations = new List<CRound>();
|
||||
// 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<int> 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<CCombination> _Combs;
|
||||
private readonly Random _Rand;
|
||||
private readonly int _NumPlayer;
|
||||
private readonly int _NumMics;
|
||||
public readonly List<int> Players;
|
||||
|
||||
public CCombinations(int numPlayer, int numMics, Random rand)
|
||||
public CRound(List<int> players)
|
||||
{
|
||||
_Combs = new List<CCombination>();
|
||||
_NumMics = numMics;
|
||||
_NumPlayer = numPlayer;
|
||||
_Rand = rand;
|
||||
}
|
||||
|
||||
public CCombination GetNextCombination(IEnumerable<int> playerNrDemand)
|
||||
{
|
||||
if (_Combs.Count == 0)
|
||||
_Create();
|
||||
|
||||
if (_NumPlayer == _NumMics)
|
||||
return _Combs[0];
|
||||
|
||||
//filter against PlayerNrDemand
|
||||
List<CCombination> combsFiltered = new List<CCombination>();
|
||||
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<int> Player;
|
||||
|
||||
public CCombination()
|
||||
{
|
||||
Player = new List<int>();
|
||||
}
|
||||
|
||||
public bool Contains(int playerIndex)
|
||||
{
|
||||
return Player.Any(p => p == playerIndex);
|
||||
}
|
||||
|
||||
public bool ContainsAll(IEnumerable<int> playerIndices)
|
||||
{
|
||||
return playerIndices.All(Contains);
|
||||
}
|
||||
|
||||
public bool ContainsAny(IEnumerable<int> playerIndices)
|
||||
{
|
||||
return playerIndices.Any(Contains);
|
||||
Players = players.Shuffle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<CCombination> Rounds;
|
||||
private readonly List<CRound> _Rounds = new List<CRound>();
|
||||
|
||||
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<CCombination>();
|
||||
_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)
|
||||
/// <summary>
|
||||
/// Divides players in 2 categories: Those how sung the most and the others
|
||||
/// </summary>
|
||||
/// <param name="numSung">List matching the playerIndex to the number of songs sung</param>
|
||||
private static List<int> _GetPlayersForRound(IList<int> numSung)
|
||||
{
|
||||
if (roundNr >= Rounds.Count || _NumPlayer == 0 || Rounds.Count == 0)
|
||||
return null;
|
||||
|
||||
return Rounds[roundNr];
|
||||
List<int> mustSing = new List<int>();
|
||||
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<CRound> 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<int> input = new List<int>(numPlayer);
|
||||
for (int i = 0; i < numPlayer; i++)
|
||||
input.Add(i);
|
||||
CCombinations<int> combs = new CCombinations<int>(input, playersPerRound);
|
||||
combinations.AddRange(combs.Select(combination => new CRound(combination)));
|
||||
combinations.Shuffle();
|
||||
}
|
||||
|
||||
private IEnumerable<int> _GetPlayerDemand(int roundIndex)
|
||||
private static CRound _GetMatchingCombination(List<int> playersInRound, List<CRound> combinations)
|
||||
{
|
||||
List<int> numPlayed = new List<int>(_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<int> result = new List<int>();
|
||||
List<int> other = new List<int>();
|
||||
List<int> last = new List<int>();
|
||||
|
||||
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<int> numSung = new List<int>(numPlayer);
|
||||
for (int i = 0; i < numPlayer; i++)
|
||||
numSung.Add(0);
|
||||
List<CRound> combinations = new List<CRound>();
|
||||
// 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<int> 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<CCombination> _Combs;
|
||||
private readonly Random _Rand;
|
||||
private readonly int _NumPlayer;
|
||||
private readonly int _NumMics;
|
||||
public readonly List<int> Players;
|
||||
|
||||
public CCombinations(int numPlayer, int numMics, Random rand)
|
||||
public CRound(List<int> players)
|
||||
{
|
||||
_Combs = new List<CCombination>();
|
||||
_NumMics = numMics;
|
||||
_NumPlayer = numPlayer;
|
||||
_Rand = rand;
|
||||
}
|
||||
|
||||
public CCombination GetNextCombination(IEnumerable<int> playerNrDemand)
|
||||
{
|
||||
if (_Combs.Count == 0)
|
||||
_Create();
|
||||
|
||||
if (_NumPlayer == _NumMics)
|
||||
return _Combs[0];
|
||||
|
||||
//filter against PlayerNrDemand
|
||||
List<CCombination> combsFiltered = new List<CCombination>();
|
||||
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<int> Player;
|
||||
|
||||
public CCombination()
|
||||
{
|
||||
Player = new List<int>();
|
||||
}
|
||||
|
||||
public bool IsAvailable(int playerIndex)
|
||||
{
|
||||
return Player.Any(p => p == playerIndex);
|
||||
}
|
||||
|
||||
public bool IsAvailableAll(IEnumerable<int> playerIndices)
|
||||
{
|
||||
return playerIndices.All(IsAvailable);
|
||||
}
|
||||
|
||||
public bool IsAvailableSomeone(IEnumerable<int> playerIndices)
|
||||
{
|
||||
return playerIndices.Any(IsAvailable);
|
||||
Players = players.Shuffle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -415,17 +415,6 @@ namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
}
|
||||
}
|
||||
|
||||
private static void _Shuffle<T>(IList<T> 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()
|
||||
|
||||
@@ -178,6 +178,18 @@ namespace VocaluxeLib
|
||||
rect.Z);
|
||||
}
|
||||
|
||||
public static List<T> Shuffle<T>(this List<T> 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);
|
||||
|
||||
@@ -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<T> result = new List<T>(_K);
|
||||
for (int i = 0; i < _K; i++)
|
||||
result.Add(_Parent._Values[_CurSet[i]]);
|
||||
result.AddRange(_CurSet.Select(i => _Parent._Values[i]));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ArgumentException>(() => new CChallengeRounds(invL, 1, 1));
|
||||
CTestHelpers.AssertFail<ArgumentException>(() => new CChallengeRounds(1, invL, 1));
|
||||
CTestHelpers.AssertFail<ArgumentException>(() => new CChallengeRounds(1, 1, invL));
|
||||
}
|
||||
}
|
||||
|
||||
private static void _CheckRounds(CChallengeRounds rounds, int numPlayer)
|
||||
{
|
||||
List<int> numSongs = new List<int>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,9 +73,14 @@
|
||||
<Compile Include="CTestHelpers.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="CombinatoricsTest.cs" />
|
||||
<Compile Include="PartyModeTests.cs" />
|
||||
<Compile Include="XmlSerializerTest.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PartyModes\PartyModeChallenge\PartyModeChallenge.csproj">
|
||||
<Project>{5a4053f1-5ad5-441f-b254-d97a088127b6}</Project>
|
||||
<Name>PartyModeChallenge</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\VocaluxeLib\VocaluxeLib.csproj">
|
||||
<Project>{d2b6ddef-e0e1-4ddf-a73a-e6acef5c95ec}</Project>
|
||||
<Name>VocaluxeLib</Name>
|
||||
|
||||
Reference in new issue
Block a user