1
0
mirror of https://github.com/ppy/osu synced 2025-03-19 17:44:30 +00: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
osu.Game.Tests/Visual
osu.Game/Screens/Select

View File

@ -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)

View File

@ -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)

View File

@ -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;