Merge pull request #2009 from quietly-turning/wildcard+endless-fix

Course Wildcard + Endless Fix (replaces #1830)
This commit is contained in:
quietly-turning
2020-06-09 16:46:17 -04:00
committed by GitHub
2 changed files with 59 additions and 39 deletions
+58 -38
View File
@@ -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
View File
@@ -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;