Merge pull request #2009 from quietly-turning/wildcard+endless-fix
Course Wildcard + Endless Fix (replaces #1830)
This commit is contained in:
+58
-38
@@ -455,7 +455,7 @@ bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail )
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
vector<SongAndSteps> vSongAndSteps;
|
vector<SongAndSteps> vSongAndSteps;
|
||||||
for (std::vector<CourseEntry>::const_iterator e = entries.begin(); e != entries.end(); ++e)
|
for (auto e = entries.begin(); e != entries.end(); ++e)
|
||||||
{
|
{
|
||||||
SongAndSteps resolved; // fill this in
|
SongAndSteps resolved; // fill this in
|
||||||
SongCriteria soc = e->songCriteria;
|
SongCriteria soc = e->songCriteria;
|
||||||
@@ -480,6 +480,7 @@ bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail )
|
|||||||
|
|
||||||
if( pSong )
|
if( pSong )
|
||||||
{
|
{
|
||||||
|
vSongAndSteps.clear();
|
||||||
StepsUtil::GetAllMatching( pSong, stc, vSongAndSteps );
|
StepsUtil::GetAllMatching( pSong, stc, vSongAndSteps );
|
||||||
}
|
}
|
||||||
else if( vSongAndSteps.empty() || !( bSameSongCriteria && bSameStepsCriteria ) )
|
else if( vSongAndSteps.empty() || !( bSameSongCriteria && bSameStepsCriteria ) )
|
||||||
@@ -651,15 +652,12 @@ bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail )
|
|||||||
void Course::GetTrailUnsortedEndless( const vector<CourseEntry> &entries, Trail &trail, StepsType &st,
|
void Course::GetTrailUnsortedEndless( const vector<CourseEntry> &entries, Trail &trail, StepsType &st,
|
||||||
CourseDifficulty &cd, RandomGen &rnd, bool &bCourseDifficultyIsSignificant ) const
|
CourseDifficulty &cd, RandomGen &rnd, bool &bCourseDifficultyIsSignificant ) const
|
||||||
{
|
{
|
||||||
vector<Song*> vpAllPossibleSongs;
|
|
||||||
vector<SongAndSteps> vSongAndSteps;
|
|
||||||
vector<Song*> vpSongs;
|
|
||||||
typedef vector<Steps*> StepsVector;
|
typedef vector<Steps*> StepsVector;
|
||||||
map<Song*, StepsVector> mapSongToSteps;
|
|
||||||
int songIndex = 0;
|
std::set<Song*> alreadySelected;
|
||||||
bool vpSongsSorted = false;
|
Song* lastSongSelected;
|
||||||
// Resolve each entry to a Song and Steps.
|
vector<SongAndSteps> vSongAndSteps;
|
||||||
for (std::vector<CourseEntry>::const_iterator e = entries.begin(); e != entries.end(); ++e)
|
for (auto e = entries.begin(); e != entries.end(); ++e)
|
||||||
{
|
{
|
||||||
|
|
||||||
SongAndSteps resolved; // fill this in
|
SongAndSteps resolved; // fill this in
|
||||||
@@ -684,8 +682,11 @@ void Course::GetTrailUnsortedEndless( const vector<CourseEntry> &entries, Trail
|
|||||||
const bool bSameSongCriteria = e != entries.begin() && ( e - 1 )->songCriteria == soc;
|
const bool bSameSongCriteria = e != entries.begin() && ( e - 1 )->songCriteria == soc;
|
||||||
const bool bSameStepsCriteria = e != entries.begin() && ( e - 1 )->stepsCriteria == stc;
|
const bool bSameStepsCriteria = e != entries.begin() && ( e - 1 )->stepsCriteria == stc;
|
||||||
|
|
||||||
|
// If we're doing the same wildcard search as last entry,
|
||||||
|
// we can just reuse the vSongAndSteps vector.
|
||||||
if( pSong )
|
if( pSong )
|
||||||
{
|
{
|
||||||
|
vSongAndSteps.clear();
|
||||||
StepsUtil::GetAllMatchingEndless( pSong, stc, vSongAndSteps );
|
StepsUtil::GetAllMatchingEndless( pSong, stc, vSongAndSteps );
|
||||||
}
|
}
|
||||||
else if( vSongAndSteps.empty() || !( bSameSongCriteria && bSameStepsCriteria ) )
|
else if( vSongAndSteps.empty() || !( bSameSongCriteria && bSameStepsCriteria ) )
|
||||||
@@ -698,40 +699,64 @@ void Course::GetTrailUnsortedEndless( const vector<CourseEntry> &entries, Trail
|
|||||||
if( vSongAndSteps.empty() )
|
if( vSongAndSteps.empty() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( !vpSongsSorted && !vSongAndSteps.empty() ) {
|
// if we're doing a RANDOM wildcard search, try to avoid repetition
|
||||||
vector<Song*> vpSongs;
|
if (vSongAndSteps.size() > 1 && e->songSort == SongSort::SongSort_Randomize)
|
||||||
typedef vector<Steps*> StepsVector;
|
{
|
||||||
map<Song*,StepsVector> mapSongToSteps;
|
// Make a backup of the steplist so we can revert if we overfilter
|
||||||
for (SongAndSteps const &sas : vSongAndSteps)
|
std::vector<SongAndSteps> revertList = vSongAndSteps;
|
||||||
|
// Filter candidate list via blacklist
|
||||||
|
RemoveIf(vSongAndSteps, [&](const SongAndSteps& ss) {
|
||||||
|
return std::find(alreadySelected.begin(), alreadySelected.end(), ss.pSong) != alreadySelected.end();
|
||||||
|
});
|
||||||
|
// If every candidate is in the blacklist, pick random song that wasn't played last
|
||||||
|
// (Repeat songs may still occur if song after this is fixed; this algorithm doesn't look ahead)
|
||||||
|
if (vSongAndSteps.empty())
|
||||||
{
|
{
|
||||||
StepsVector &v = mapSongToSteps[sas.pSong];
|
vSongAndSteps = revertList;
|
||||||
v.push_back( sas.pSteps );
|
RemoveIf(vSongAndSteps, SongIsEqual(lastSongSelected));
|
||||||
if( v.size() == 1 )
|
|
||||||
vpSongs.push_back( sas.pSong );
|
// If the song that was played last was the only candidate, give up pick randomly
|
||||||
|
if (vSongAndSteps.empty())
|
||||||
|
{
|
||||||
|
vSongAndSteps = revertList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
vpSongsSorted = true;
|
|
||||||
CourseSortSongs( e->songSort, vpSongs, rnd );
|
|
||||||
songIndex = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT( e->iChooseIndex >= 0 );
|
std::vector<Song*> vpSongs;
|
||||||
if( e->iChooseIndex < static_cast<int>(vSongAndSteps.size()) )
|
std::map<Song*, StepsVector> songStepMap;
|
||||||
|
// Build list of songs for sorting, and mapping of songs to steps simultaneously
|
||||||
|
for (auto& ss : vSongAndSteps)
|
||||||
{
|
{
|
||||||
if( songIndex >= int(vpSongs.size()) ) {
|
StepsVector& stepsForSong = songStepMap[ss.pSong];
|
||||||
songIndex = 0;
|
// If we haven't noted this song yet, add it to the song list
|
||||||
|
if (stepsForSong.size() == 0)
|
||||||
|
{
|
||||||
|
vpSongs.push_back(ss.pSong);
|
||||||
}
|
}
|
||||||
resolved.pSong = vpSongs[ songIndex ];
|
|
||||||
const vector<Steps*> &mappedSongs = mapSongToSteps[ resolved.pSong ];
|
stepsForSong.push_back(ss.pSteps);
|
||||||
songIndex++;
|
|
||||||
resolved.pSteps = mappedSongs[ RandomInt( mappedSongs.size() ) ];
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
ASSERT(e->iChooseIndex >= 0);
|
||||||
|
// If we're trying to pick BEST100 when only 99 songs exist,
|
||||||
|
// we have a problem, so bail out
|
||||||
|
if (e->iChooseIndex >= vpSongs.size()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're not COURSE_DIFFICULTY_REGULAR, then we should be choosing steps that are
|
|
||||||
* either easier or harder than the base difficulty. If no such steps exist, then
|
// Otherwise, pick random steps corresponding to the selected song
|
||||||
|
CourseSortSongs(e->songSort, vpSongs, rnd);
|
||||||
|
resolved.pSong = vpSongs[e->iChooseIndex];
|
||||||
|
const vector<Steps*>& songSteps = songStepMap[resolved.pSong];
|
||||||
|
resolved.pSteps = songSteps[RandomInt(songSteps.size())];
|
||||||
|
|
||||||
|
lastSongSelected = resolved.pSong;
|
||||||
|
alreadySelected.emplace(resolved.pSong);
|
||||||
|
|
||||||
|
/* If we're not COURSE_DIFFICULTY_REGULAR, then we should be choosing steps that are
|
||||||
|
* either easier or harder than the base difficulty. If no such steps exist, then
|
||||||
* just use the one we already have. */
|
* just use the one we already have. */
|
||||||
Difficulty dc = resolved.pSteps->GetDifficulty();
|
Difficulty dc = resolved.pSteps->GetDifficulty();
|
||||||
int iLowMeter = e->stepsCriteria.m_iLowMeter;
|
int iLowMeter = e->stepsCriteria.m_iLowMeter;
|
||||||
@@ -825,11 +850,6 @@ void Course::GetTrailUnsortedEndless( const vector<CourseEntry> &entries, Trail
|
|||||||
}
|
}
|
||||||
|
|
||||||
trail.m_vEntries.push_back( te );
|
trail.m_vEntries.push_back( te );
|
||||||
if( trail.m_vEntries.size() > 0 && te.dc != cd )
|
|
||||||
{
|
|
||||||
trail.m_vEntries.reserve( trail.m_vEntries.size() - 1 );
|
|
||||||
trail.m_vEntries.resize( trail.m_vEntries.size() - 1 );
|
|
||||||
}
|
|
||||||
// LOG->Trace( "Chose: %s, %d", te.pSong->GetSongDir().c_str(), te.pSteps->GetMeter() );
|
// LOG->Trace( "Chose: %s, %d", te.pSong->GetSongDir().c_str(), te.pSteps->GetMeter() );
|
||||||
|
|
||||||
if( IsAnEdit() && MAX_SONGS_IN_EDIT_COURSE > 0 &&
|
if( IsAnEdit() && MAX_SONGS_IN_EDIT_COURSE > 0 &&
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ void StepsUtil::GetAllMatchingEndless( Song *pSong, const StepsCriteria &stc, ve
|
|||||||
successful = true;
|
successful = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !successful )
|
if( !successful && vSteps.size() > 0 )
|
||||||
{
|
{
|
||||||
Difficulty difficulty = ( *( vSteps.begin() ) )->GetDifficulty();
|
Difficulty difficulty = ( *( vSteps.begin() ) )->GetDifficulty();
|
||||||
Difficulty previousDifficulty = difficulty;
|
Difficulty previousDifficulty = difficulty;
|
||||||
|
|||||||
Reference in New Issue
Block a user