1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-28 01:37:46 +08:00

Fix random and add tests

Also exposes SelectedBeatmapSet.
This commit is contained in:
Dean Herbert 2017-12-14 12:59:35 +09:00
parent 8646d5d1e0
commit 5cbb9b9b18
3 changed files with 97 additions and 44 deletions

View File

@ -10,6 +10,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Carousel;
@ -44,7 +45,7 @@ namespace osu.Game.Tests.Visual
AddStep("Load Beatmaps", () => AddStep("Load Beatmaps", () =>
{ {
carousel.Beatmaps = new[] carousel.BeatmapSets = new[]
{ {
createTestBeatmapSet(1), createTestBeatmapSet(1),
createTestBeatmapSet(2), createTestBeatmapSet(2),
@ -55,11 +56,11 @@ namespace osu.Game.Tests.Visual
void checkSelected(int set, int diff) => void checkSelected(int set, int diff) =>
AddAssert($"selected is set{set} diff{diff}", () => AddAssert($"selected is set{set} diff{diff}", () =>
carousel.SelectedBeatmap == carousel.Beatmaps.Skip(set - 1).First().Beatmaps.Skip(diff - 1).First()); carousel.SelectedBeatmap == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff - 1).First());
void setSelected(int set, int diff) => void setSelected(int set, int diff) =>
AddStep($"select set{set} diff{diff}", () => AddStep($"select set{set} diff{diff}", () =>
carousel.SelectBeatmap(carousel.Beatmaps.Skip(set - 1).First().Beatmaps.Skip(diff - 1).First())); carousel.SelectBeatmap(carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff - 1).First()));
void advanceSelection(bool diff, int direction = 1, int count = 1) void advanceSelection(bool diff, int direction = 1, int count = 1)
{ {
@ -77,7 +78,7 @@ namespace osu.Game.Tests.Visual
AddAssert($"{count} {(diff ? "diff" : "set")} visible", () => AddAssert($"{count} {(diff ? "diff" : "set")} visible", () =>
carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible) == count); carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible) == count);
AddUntilStep(() => carousel.Beatmaps.Any(), "Wait for load"); AddUntilStep(() => carousel.BeatmapSets.Any(), "Wait for load");
// test traversal // test traversal
@ -122,6 +123,55 @@ namespace osu.Game.Tests.Visual
checkSelected(1, 1); checkSelected(1, 1);
AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false));
checkSelected(1, 1); checkSelected(1, 1);
// test random non-repeating algorithm
Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>();
void nextRandom() =>
AddStep("select random next", () =>
{
carousel.RandomAlgorithm.Value = RandomSelectAlgorithm.RandomPermutation;
if (!selectedSets.Any() && carousel.SelectedBeatmap != null)
selectedSets.Push(carousel.SelectedBeatmapSet);
carousel.SelectNextRandom();
selectedSets.Push(carousel.SelectedBeatmapSet);
});
void ensureRandomDidntRepeat() =>
AddAssert("ensure no repeats", () => selectedSets.Distinct().Count() == selectedSets.Count());
void prevRandom() => AddStep("select random last", () =>
{
carousel.SelectPreviousRandom();
selectedSets.Pop();
});
void ensureRandomFetchSuccess() =>
AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet);
setSelected(1, 1);
nextRandom();
ensureRandomDidntRepeat();
nextRandom();
ensureRandomDidntRepeat();
nextRandom();
ensureRandomDidntRepeat();
prevRandom();
ensureRandomFetchSuccess();
prevRandom();
ensureRandomFetchSuccess();
nextRandom();
ensureRandomDidntRepeat();
nextRandom();
ensureRandomDidntRepeat();
nextRandom();
AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet));
} }
private BeatmapSetInfo createTestBeatmapSet(int i) private BeatmapSetInfo createTestBeatmapSet(int i)

View File

@ -27,15 +27,24 @@ namespace osu.Game.Screens.Select
public class BeatmapCarousel : OsuScrollContainer public class BeatmapCarousel : OsuScrollContainer
{ {
/// <summary> /// <summary>
/// Triggered when the <see cref="Beatmaps"/> loaded change and are completely loaded. /// Triggered when the <see cref="BeatmapSets"/> loaded change and are completely loaded.
/// </summary> /// </summary>
public Action BeatmapsChanged; public Action BeatmapSetsChanged;
/// <summary> /// <summary>
/// The currently selected beatmap. /// The currently selected beatmap.
/// </summary> /// </summary>
public BeatmapInfo SelectedBeatmap => selectedBeatmap?.Beatmap; public BeatmapInfo SelectedBeatmap => selectedBeatmap?.Beatmap;
private CarouselBeatmap selectedBeatmap => selectedBeatmapSet?.Beatmaps.FirstOrDefault(s => s.State == CarouselItemState.Selected);
/// <summary>
/// The currently selected beatmap set.
/// </summary>
public BeatmapSetInfo SelectedBeatmapSet => selectedBeatmapSet?.BeatmapSet;
private CarouselBeatmapSet selectedBeatmapSet => carouselSets.FirstOrDefault(s => s.State == CarouselItemState.Selected);
/// <summary> /// <summary>
/// Raised when the <see cref="SelectedBeatmap"/> is changed. /// Raised when the <see cref="SelectedBeatmap"/> is changed.
/// </summary> /// </summary>
@ -43,7 +52,7 @@ namespace osu.Game.Screens.Select
public override bool HandleInput => AllowSelection; public override bool HandleInput => AllowSelection;
public IEnumerable<BeatmapSetInfo> Beatmaps public IEnumerable<BeatmapSetInfo> BeatmapSets
{ {
get { return carouselSets.Select(g => g.BeatmapSet); } get { return carouselSets.Select(g => g.BeatmapSet); }
set set
@ -72,7 +81,7 @@ namespace osu.Game.Screens.Select
Items = root.Drawables.Value.ToList(); Items = root.Drawables.Value.ToList();
yPositionsCache.Invalidate(); yPositionsCache.Invalidate();
BeatmapsChanged?.Invoke(); BeatmapSetsChanged?.Invoke();
}); });
}); });
} }
@ -85,17 +94,14 @@ namespace osu.Game.Screens.Select
private readonly List<CarouselBeatmapSet> carouselSets = new List<CarouselBeatmapSet>(); private readonly List<CarouselBeatmapSet> carouselSets = new List<CarouselBeatmapSet>();
private Bindable<RandomSelectAlgorithm> randomSelectAlgorithm; public Bindable<RandomSelectAlgorithm> RandomAlgorithm = new Bindable<RandomSelectAlgorithm>();
private readonly List<CarouselBeatmapSet> seenSets = new List<CarouselBeatmapSet>(); private readonly List<CarouselBeatmapSet> previouslyVisitedRandomSets = new List<CarouselBeatmapSet>();
protected List<DrawableCarouselItem> Items = new List<DrawableCarouselItem>(); protected List<DrawableCarouselItem> Items = new List<DrawableCarouselItem>();
private CarouselGroup root = new CarouselGroup(); private CarouselGroup root = new CarouselGroup();
private readonly Stack<CarouselBeatmap> randomSelectedBeatmaps = new Stack<CarouselBeatmap>(); private readonly Stack<CarouselBeatmap> randomSelectedBeatmaps = new Stack<CarouselBeatmap>();
private CarouselBeatmap selectedBeatmap;
private CarouselBeatmapSet selectedBeatmapSet => carouselSets.FirstOrDefault(s => s.State == CarouselItemState.Selected);
public BeatmapCarousel() public BeatmapCarousel()
{ {
Add(new OsuContextMenuContainer Add(new OsuContextMenuContainer
@ -173,12 +179,6 @@ namespace osu.Game.Screens.Select
} }
} }
private void selectNullBeatmap()
{
selectedBeatmap = null;
SelectionChanged?.Invoke(null);
}
/// <summary> /// <summary>
/// Increment selection in the carousel in a chosen direction. /// Increment selection in the carousel in a chosen direction.
/// </summary> /// </summary>
@ -187,9 +187,9 @@ namespace osu.Game.Screens.Select
public void SelectNext(int direction = 1, bool skipDifficulties = true) public void SelectNext(int direction = 1, bool skipDifficulties = true)
{ {
// todo: we may want to refactor and remove this as an optimisation in the future. // todo: we may want to refactor and remove this as an optimisation in the future.
if (carouselSets.All(g => g.State == CarouselItemState.Hidden)) if (carouselSets.All(g => !g.Visible))
{ {
selectNullBeatmap(); SelectionChanged?.Invoke(null);
return; return;
} }
@ -218,53 +218,57 @@ namespace osu.Game.Screens.Select
} }
} }
private IEnumerable<CarouselBeatmapSet> getVisibleGroups() => carouselSets.Where(select => select.State != CarouselItemState.NotSelected); private IEnumerable<CarouselBeatmapSet> getVisibleSets() => carouselSets.Where(select => select.Visible);
public void SelectNextRandom() public void SelectNextRandom()
{ {
if (carouselSets.Count == 0) if (carouselSets.Count == 0)
return; return;
var visibleGroups = getVisibleGroups(); var visible = getVisibleSets().ToList();
if (!visibleGroups.Any()) if (!visible.Any())
return; return;
if (selectedBeatmap != null) if (selectedBeatmap != null)
{
randomSelectedBeatmaps.Push(selectedBeatmap); randomSelectedBeatmaps.Push(selectedBeatmap);
CarouselBeatmapSet group; // when performing a random, we want to add the current set to the previously visited list
// else the user may be "randomised" to the existing selection.
if (previouslyVisitedRandomSets.LastOrDefault() != selectedBeatmapSet)
previouslyVisitedRandomSets.Add(selectedBeatmapSet);
}
if (randomSelectAlgorithm == RandomSelectAlgorithm.RandomPermutation) CarouselBeatmapSet set;
if (RandomAlgorithm == RandomSelectAlgorithm.RandomPermutation)
{ {
var notSeenGroups = visibleGroups.Except(seenSets); var notYetVisitedSets = visible.Except(previouslyVisitedRandomSets).ToList();
if (!notSeenGroups.Any()) if (!notYetVisitedSets.Any())
{ {
seenSets.Clear(); previouslyVisitedRandomSets.Clear();
notSeenGroups = visibleGroups; notYetVisitedSets = visible;
} }
group = notSeenGroups.ElementAt(RNG.Next(notSeenGroups.Count())); set = notYetVisitedSets.ElementAt(RNG.Next(notYetVisitedSets.Count()));
seenSets.Add(group); previouslyVisitedRandomSets.Add(set);
} }
else else
group = visibleGroups.ElementAt(RNG.Next(visibleGroups.Count())); set = visible.ElementAt(RNG.Next(visible.Count()));
CarouselBeatmap item = group.Beatmaps[RNG.Next(group.Beatmaps.Count)]; select(set.Beatmaps[RNG.Next(set.Beatmaps.Count)]);
select(item);
} }
public void SelectPreviousRandom() public void SelectPreviousRandom()
{ {
if (!randomSelectedBeatmaps.Any())
return;
while (randomSelectedBeatmaps.Any()) while (randomSelectedBeatmaps.Any())
{ {
var beatmap = randomSelectedBeatmaps.Pop(); var beatmap = randomSelectedBeatmaps.Pop();
if (beatmap.Visible) if (!beatmap.Filtered)
{ {
if (RandomAlgorithm == RandomSelectAlgorithm.RandomPermutation)
previouslyVisitedRandomSets.Remove(selectedBeatmapSet);
select(beatmap); select(beatmap);
break; break;
} }
@ -351,7 +355,6 @@ namespace osu.Game.Screens.Select
{ {
if (v == CarouselItemState.Selected) if (v == CarouselItemState.Selected)
{ {
selectedBeatmap = c;
SelectionChanged?.Invoke(c.Beatmap); SelectionChanged?.Invoke(c.Beatmap);
yPositionsCache.Invalidate(); yPositionsCache.Invalidate();
Schedule(() => ScrollToSelected()); Schedule(() => ScrollToSelected());
@ -365,7 +368,7 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
randomSelectAlgorithm = config.GetBindable<RandomSelectAlgorithm>(OsuSetting.RandomSelectAlgorithm); config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
} }
private void removeBeatmapSet(CarouselBeatmapSet set) private void removeBeatmapSet(CarouselBeatmapSet set)

View File

@ -113,7 +113,7 @@ namespace osu.Game.Screens.Select
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
SelectionChanged = carouselSelectionChanged, SelectionChanged = carouselSelectionChanged,
BeatmapsChanged = carouselBeatmapsLoaded, BeatmapSetsChanged = carouselBeatmapsLoaded,
}, },
FilterControl = new FilterControl FilterControl = new FilterControl
{ {
@ -193,7 +193,7 @@ namespace osu.Game.Screens.Select
initialAddSetsTask = new CancellationTokenSource(); initialAddSetsTask = new CancellationTokenSource();
carousel.Beatmaps = this.beatmaps.GetAllUsableBeatmapSets(); carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSets();
Beatmap.ValueChanged += beatmap_ValueChanged; Beatmap.ValueChanged += beatmap_ValueChanged;