mirror of
https://github.com/ppy/osu
synced 2025-03-18 17:15:28 +00:00
Fix random and add tests
Also exposes SelectedBeatmapSet.
This commit is contained in:
parent
8646d5d1e0
commit
5cbb9b9b18
@ -10,6 +10,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
|
||||
@ -44,7 +45,7 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
AddStep("Load Beatmaps", () =>
|
||||
{
|
||||
carousel.Beatmaps = new[]
|
||||
carousel.BeatmapSets = new[]
|
||||
{
|
||||
createTestBeatmapSet(1),
|
||||
createTestBeatmapSet(2),
|
||||
@ -55,11 +56,11 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
void checkSelected(int set, int 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) =>
|
||||
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)
|
||||
{
|
||||
@ -77,7 +78,7 @@ namespace osu.Game.Tests.Visual
|
||||
AddAssert($"{count} {(diff ? "diff" : "set")} visible", () =>
|
||||
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
|
||||
|
||||
@ -122,6 +123,55 @@ namespace osu.Game.Tests.Visual
|
||||
checkSelected(1, 1);
|
||||
AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false));
|
||||
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)
|
||||
|
@ -27,15 +27,24 @@ namespace osu.Game.Screens.Select
|
||||
public class BeatmapCarousel : OsuScrollContainer
|
||||
{
|
||||
/// <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>
|
||||
public Action BeatmapsChanged;
|
||||
public Action BeatmapSetsChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected beatmap.
|
||||
/// </summary>
|
||||
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>
|
||||
/// Raised when the <see cref="SelectedBeatmap"/> is changed.
|
||||
/// </summary>
|
||||
@ -43,7 +52,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public override bool HandleInput => AllowSelection;
|
||||
|
||||
public IEnumerable<BeatmapSetInfo> Beatmaps
|
||||
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
||||
{
|
||||
get { return carouselSets.Select(g => g.BeatmapSet); }
|
||||
set
|
||||
@ -72,7 +81,7 @@ namespace osu.Game.Screens.Select
|
||||
Items = root.Drawables.Value.ToList();
|
||||
|
||||
yPositionsCache.Invalidate();
|
||||
BeatmapsChanged?.Invoke();
|
||||
BeatmapSetsChanged?.Invoke();
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -85,17 +94,14 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private readonly List<CarouselBeatmapSet> carouselSets = new List<CarouselBeatmapSet>();
|
||||
|
||||
private Bindable<RandomSelectAlgorithm> randomSelectAlgorithm;
|
||||
private readonly List<CarouselBeatmapSet> seenSets = new List<CarouselBeatmapSet>();
|
||||
public Bindable<RandomSelectAlgorithm> RandomAlgorithm = new Bindable<RandomSelectAlgorithm>();
|
||||
private readonly List<CarouselBeatmapSet> previouslyVisitedRandomSets = new List<CarouselBeatmapSet>();
|
||||
|
||||
protected List<DrawableCarouselItem> Items = new List<DrawableCarouselItem>();
|
||||
private CarouselGroup root = new CarouselGroup();
|
||||
|
||||
private readonly Stack<CarouselBeatmap> randomSelectedBeatmaps = new Stack<CarouselBeatmap>();
|
||||
|
||||
private CarouselBeatmap selectedBeatmap;
|
||||
private CarouselBeatmapSet selectedBeatmapSet => carouselSets.FirstOrDefault(s => s.State == CarouselItemState.Selected);
|
||||
|
||||
public BeatmapCarousel()
|
||||
{
|
||||
Add(new OsuContextMenuContainer
|
||||
@ -173,12 +179,6 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
}
|
||||
|
||||
private void selectNullBeatmap()
|
||||
{
|
||||
selectedBeatmap = null;
|
||||
SelectionChanged?.Invoke(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increment selection in the carousel in a chosen direction.
|
||||
/// </summary>
|
||||
@ -187,9 +187,9 @@ namespace osu.Game.Screens.Select
|
||||
public void SelectNext(int direction = 1, bool skipDifficulties = true)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -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()
|
||||
{
|
||||
if (carouselSets.Count == 0)
|
||||
return;
|
||||
|
||||
var visibleGroups = getVisibleGroups();
|
||||
if (!visibleGroups.Any())
|
||||
var visible = getVisibleSets().ToList();
|
||||
if (!visible.Any())
|
||||
return;
|
||||
|
||||
if (selectedBeatmap != null)
|
||||
{
|
||||
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);
|
||||
if (!notSeenGroups.Any())
|
||||
var notYetVisitedSets = visible.Except(previouslyVisitedRandomSets).ToList();
|
||||
if (!notYetVisitedSets.Any())
|
||||
{
|
||||
seenSets.Clear();
|
||||
notSeenGroups = visibleGroups;
|
||||
previouslyVisitedRandomSets.Clear();
|
||||
notYetVisitedSets = visible;
|
||||
}
|
||||
|
||||
group = notSeenGroups.ElementAt(RNG.Next(notSeenGroups.Count()));
|
||||
seenSets.Add(group);
|
||||
set = notYetVisitedSets.ElementAt(RNG.Next(notYetVisitedSets.Count()));
|
||||
previouslyVisitedRandomSets.Add(set);
|
||||
}
|
||||
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(item);
|
||||
select(set.Beatmaps[RNG.Next(set.Beatmaps.Count)]);
|
||||
}
|
||||
|
||||
public void SelectPreviousRandom()
|
||||
{
|
||||
if (!randomSelectedBeatmaps.Any())
|
||||
return;
|
||||
|
||||
while (randomSelectedBeatmaps.Any())
|
||||
{
|
||||
var beatmap = randomSelectedBeatmaps.Pop();
|
||||
|
||||
if (beatmap.Visible)
|
||||
if (!beatmap.Filtered)
|
||||
{
|
||||
if (RandomAlgorithm == RandomSelectAlgorithm.RandomPermutation)
|
||||
previouslyVisitedRandomSets.Remove(selectedBeatmapSet);
|
||||
select(beatmap);
|
||||
break;
|
||||
}
|
||||
@ -351,7 +355,6 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
if (v == CarouselItemState.Selected)
|
||||
{
|
||||
selectedBeatmap = c;
|
||||
SelectionChanged?.Invoke(c.Beatmap);
|
||||
yPositionsCache.Invalidate();
|
||||
Schedule(() => ScrollToSelected());
|
||||
@ -365,7 +368,7 @@ namespace osu.Game.Screens.Select
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
randomSelectAlgorithm = config.GetBindable<RandomSelectAlgorithm>(OsuSetting.RandomSelectAlgorithm);
|
||||
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
|
||||
}
|
||||
|
||||
private void removeBeatmapSet(CarouselBeatmapSet set)
|
||||
|
@ -113,7 +113,7 @@ namespace osu.Game.Screens.Select
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
SelectionChanged = carouselSelectionChanged,
|
||||
BeatmapsChanged = carouselBeatmapsLoaded,
|
||||
BeatmapSetsChanged = carouselBeatmapsLoaded,
|
||||
},
|
||||
FilterControl = new FilterControl
|
||||
{
|
||||
@ -193,7 +193,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
initialAddSetsTask = new CancellationTokenSource();
|
||||
|
||||
carousel.Beatmaps = this.beatmaps.GetAllUsableBeatmapSets();
|
||||
carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSets();
|
||||
|
||||
Beatmap.ValueChanged += beatmap_ValueChanged;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user