Last active
July 19, 2017 11:54
-
-
Save monsieurh/91d6a83308840ca75b60efe9682019dd to your computer and use it in GitHub Desktop.
Shuffles a list in C# (Unity compatible)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
namespace Utils | |
{ | |
/// <summary> | |
/// Implements "iTunes shuffle" that is to say, it's a random where you can't get twice the same value unless you | |
/// already got all the values. | |
/// | |
/// To avoid having a chance to get twice the same value when refilling the pool, we store the last picked value in | |
/// `_last` and do not put it back when refilling the pool | |
/// | |
/// author: [email protected] | |
/// </summary> | |
public class Shuffler<T> : IEnumerable | |
{ | |
private readonly List<T> _allItems = new List<T>(); // All items used in the shuffler | |
private readonly List<T> _pool = new List<T>(); // Remaining items for current series | |
private Random _random = new Random(); | |
private T _last; //Last object extracted from the pool | |
private int PoolCount | |
{ | |
get { return _pool.Count; } | |
} | |
public Shuffler(Random random = null) | |
{ | |
SetRandom(random); | |
} | |
public Shuffler(IEnumerable<T> allItems, Random random = null) | |
{ | |
_allItems.AddRange(allItems); | |
SetRandom(random); | |
} | |
/// <summary> | |
/// Always returns next element. If the pool is empty, GetNext() refills it before returning the next element | |
/// </summary> | |
/// <returns>The next element</returns> | |
public T GetNext() | |
{ | |
if (IsPoolEmpty()) PopulatePool(); | |
return PopRandomElement(); | |
} | |
/// <summary> | |
/// Enumerates the REMAINING elements in the pool. Stops when the pool is empty | |
/// </summary> | |
/// <returns>enumerator on elements left</returns> | |
public IEnumerator GetEnumerator() | |
{ | |
if (IsPoolEmpty()) PopulatePool(); | |
while (PoolCount > 0) | |
{ | |
yield return PopRandomElement(); | |
} | |
} | |
/// <summary> | |
/// A readonly list of all items in the Shuffler. Some of them may already have been picked and may not be | |
/// chosen until the pool is refiled | |
/// </summary> | |
/// <returns>readonly list of all possible values</returns> | |
public ReadOnlyCollection<T> GetAllPossibleValues() | |
{ | |
return _allItems.AsReadOnly(); | |
} | |
public void Add(T element) | |
{ | |
_allItems.Add(element); | |
} | |
private T PopRandomElement() | |
{ | |
if (PoolCount == 0) throw new ArgumentException("List of elements is empty !"); | |
int randomIndex = _random.Next(PoolCount); | |
T item = _pool[randomIndex]; | |
_pool.RemoveAt(randomIndex); | |
if (IsPoolEmpty()) _last = item; | |
return item; | |
} | |
private void PopulatePool() | |
{ | |
_pool.Clear(); | |
_pool.AddRange(_allItems); | |
if (PoolCount > 1) // If _allItems is size 1 we don't remove from the pool : it would be empty | |
{ | |
RemoveLastFromPool(); | |
} | |
} | |
private void RemoveLastFromPool() | |
{ | |
_pool.Remove(_last); | |
_last = default(T); | |
} | |
private bool IsPoolEmpty() | |
{ | |
return PoolCount == 0; | |
} | |
private void SetRandom(Random random) | |
{ | |
if (random != null) _random = random; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Rev 2 can now be used as follows :