mirror of
https://github.com/ppy/osu.git
synced 2026-06-13 04:33:38 +08:00
Use song title as tie-breaker before date added when sorting song select (#36971)
<details> <summary> Greetings </summary> Hello! This is my first PR for `osu!`, I hope you'll welcome me with as much enthusiasm as I have opening this! The following PR comes from some frustration on my end when using song select. I have not found issues or opened/closed PRs that address this topic, so I thought I'd shoot my shot! To give some more context, I've been pretty inactive on `osu!` and playing almost exclusively songs I imported from stable. From what I understand, this makes all my beatmapsets have the same `DateAdded`. This triggers the `ID` fallback comparison, which, as noted in the code comments, is essentially random. Writing this, I realize that I haven't checked if this addresses a behavior that changes from stable to lazer, or something that was always here! </details> This PR aims at making a small part of song-select sorting more intuitive by using the song's title as a fallback sorting method. My feeling is that `DateAdded` "looks more random" than the title, especially in cases like mine where the `DateAdded` fallback-comparison outputs `0`. The implementation is the same as `Artist` with `Title` fallback. <details> <summary> Screenshots </summary> On my installed osu version: sorting by `BPM` puts "Asymetry" in the middle of two mapsets of "Snow halation" <img width="1053" height="841" alt="image" src="https://github.com/user-attachments/assets/00a89251-5695-43b5-a388-248404c24f02" /> After the patch, mapsets with the same BPM will also be sorted by song title, for example at 173 BPM: <img width="1033" height="1177" alt="image" src="https://github.com/user-attachments/assets/de7de0b9-f2aa-4e81-80f8-459bfcf6dc95" /> And further below: <img width="1050" height="929" alt="image" src="https://github.com/user-attachments/assets/dfdee7ad-7b5b-45c7-abfe-22ce3c561dd1" /> </details> --------- Co-authored-by: Kenny Lorin <kenny.lorin@enioka.com> Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
3b4dfd5903
commit
e484cf4553
@@ -90,6 +90,42 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Assert.That(results.TakeWhile(b => b.BeatmapSet!.DateSubmitted != null).Count(), Is.EqualTo(30), () => "non-missing dates should be at the start");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestSortByBpmUsesTitleAsTiebreaker()
|
||||
{
|
||||
List<BeatmapSetInfo> beatmapSets = [];
|
||||
|
||||
// 2 sets with same BPM but different titles
|
||||
const int diff_count = 1;
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
set.DateAdded = new DateTimeOffset(2025, 6, 11, 10, 0, 0, TimeSpan.Zero);
|
||||
set.Beatmaps.ForEach(b =>
|
||||
{
|
||||
b.ID = Guid.Parse("00000000-0000-0000-0000-000000000000");
|
||||
b.BPM = 175;
|
||||
b.Metadata.Title = "ZZZ";
|
||||
});
|
||||
beatmapSets.Add(set);
|
||||
}
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
set.DateAdded = new DateTimeOffset(2025, 6, 10, 10, 0, 0, TimeSpan.Zero);
|
||||
set.Beatmaps.ForEach(b =>
|
||||
{
|
||||
b.ID = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff");
|
||||
b.BPM = 175;
|
||||
b.Metadata.Title = "AAA";
|
||||
});
|
||||
beatmapSets.Add(set);
|
||||
}
|
||||
|
||||
var results = (await runSorting(SortMode.BPM, beatmapSets)).ToList();
|
||||
|
||||
Assert.That(results[0].Metadata.Title, Is.EqualTo("AAA"));
|
||||
Assert.That(results[1].Metadata.Title, Is.EqualTo("ZZZ"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestSortByArtistUsesTitleAsTiebreaker()
|
||||
{
|
||||
|
||||
@@ -323,15 +323,16 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
SelectNextSet();
|
||||
// both sets have a difficulty with 0.00* star rating.
|
||||
// in the case of a tie when sorting, the first tie-breaker is `DateAdded` descending, which will pick the last set added (see `TestResources.CreateTestBeatmapSetInfo()`).
|
||||
WaitForSetSelection(1, 0);
|
||||
// in the case of a tie when sorting, the first tie-breaker is `Title` ascending, which will pick the first set added as the title contains the set ID
|
||||
// (see `TestResources.CreateTestBeatmapSetInfo()`).
|
||||
WaitForSetSelection(0, 0);
|
||||
|
||||
SelectNextSet();
|
||||
WaitForSetSelection(0, 0);
|
||||
WaitForSetSelection(1, 0);
|
||||
|
||||
SelectNextPanel();
|
||||
Select();
|
||||
WaitForSetSelection(1, 1);
|
||||
WaitForSetSelection(0, 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -115,12 +115,15 @@ namespace osu.Game.Screens.Select
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// If the initial sort could not differentiate, attempt to use DateAdded to order sets in a stable fashion.
|
||||
// If the initial sort could not differentiate, attempt to use Title then DateAdded to order sets in a stable fashion.
|
||||
if (comparison == 0)
|
||||
comparison = OrdinalSortByCaseStringComparer.DEFAULT.Compare(a.BeatmapSet!.Metadata.Title, b.BeatmapSet!.Metadata.Title);
|
||||
|
||||
// The directionality of this matches the current SortMode.DateAdded, but we may want to reconsider if that becomes a user decision (ie. asc / desc).
|
||||
if (comparison == 0)
|
||||
comparison = b.BeatmapSet!.DateAdded.CompareTo(a.BeatmapSet!.DateAdded);
|
||||
|
||||
// If DateAdded fails to break the tie, fallback to our internal GUID for stability.
|
||||
// If both failed to break the tie, fallback to our internal GUID for stability.
|
||||
// This basically means it's a stable random sort.
|
||||
if (comparison == 0)
|
||||
comparison = b.BeatmapSet!.ID.CompareTo(a.BeatmapSet!.ID);
|
||||
|
||||
Reference in New Issue
Block a user