Fixed with better random session query distribution.

This commit is contained in:
Nick Winter 2015-04-12 11:34:19 -07:00
parent 4669d4cb0a
commit eb075b99a0
3 changed files with 33 additions and 6 deletions
app/schemas/models
server

View file

@ -208,6 +208,12 @@ _.extend LevelSessionSchema.properties,
type: 'boolean'
description: 'Whether this session is still in the first ranking chain after being submitted.'
randomSimulationIndex:
type: 'number'
description: 'A random updated every time the game is randomly simulated for a uniform random distribution of simulations (see #2448).'
minimum: 0
maximum: 1
unsubscribed:
type: 'boolean'
description: 'Whether the player has opted out of receiving email updates about ladder rankings for this session.'

View file

@ -24,9 +24,10 @@ LevelSessionSchema.index({team: 1}, {sparse: true})
LevelSessionSchema.index({totalScore: 1}, {sparse: true})
LevelSessionSchema.index({user: 1, changed: -1}, {name: 'last played index', sparse: true})
LevelSessionSchema.index({'level.original': 1, 'state.topScores.type': 1, 'state.topScores.date': -1, 'state.topScores.score': -1}, {name: 'top scores index', sparse: true})
LevelSessionSchema.index({submitted: 1, team: 1, level:1, totalScore: -1}, {name: 'rank counting index', sparse:true})
LevelSessionSchema.index({levelID: 1, submitted:1, team: 1},{name:'get all scores index',sparse:true})
LevelSessionSchema.index({submitted: 1, team: 1, levelID: 1, submitDate: -1}, {name: 'matchmaking index', sparse:true})
LevelSessionSchema.index({submitted: 1, team: 1, level:1, totalScore: -1}, {name: 'rank counting index', sparse: true})
LevelSessionSchema.index({levelID: 1, submitted:1, team: 1}, {name: 'get all scores index', sparse: true})
LevelSessionSchema.index({submitted: 1, team: 1, levelID: 1, submitDate: -1}, {name: 'matchmaking index', sparse: true})
LevelSessionSchema.index({submitted: 1, team: 1, levelID: 1, randomSimulationIndex: -1}, {name: 'matchmaking random index', sparse: true})
LevelSessionSchema.plugin(plugins.PermissionsPlugin)
LevelSessionSchema.plugin(AchievablePlugin)

View file

@ -107,10 +107,9 @@ findEarliestSubmission = (queryParams, callback) ->
result = earliestSubmissionCache[cacheKey] = earliest?.submitDate
callback null, result
findRandomSession = (queryParams, callback) ->
findRecentRandomSession = (queryParams, callback) ->
# We pick a random submitDate between the first submit date for the level and now, then do a $lt fetch to find a session to simulate.
# We bias it towards recently submitted sessions.
queryParams.submitted = true
findEarliestSubmission queryParams, (err, startDate) ->
return callback err, null unless startDate
now = new Date()
@ -122,6 +121,24 @@ findRandomSession = (queryParams, callback) ->
return callback err if err
callback null, session
findRandomSession = (queryParams, callback) ->
queryParams.submitted = true
favorRecent = queryParams.favorRecent
delete queryParams.favorRecent
if favorRecent
return findRecentRandomSession queryParams, callback
queryParams.randomSimulationIndex = $lte: Math.random()
selection = 'team totalScore transpiledCode submittedCodeLanguage teamSpells levelID creatorName creator submitDate'
sort = randomSimulationIndex: -1
console.log 'trying to find one with qparams', queryParams
LevelSession.findOne(queryParams).sort(sort).select(selection).lean().exec (err, session) ->
return callback err if err
return callback null, session if session
delete queryParams.randomSimulationIndex # Effectively switch to $gt, if our randomSimulationIndex was lower than the lowest one.
LevelSession.findOne(queryParams).sort(sort).select(selection).lean().exec (err, session) ->
return callback err if err
callback null, session
formatSessionInformation = (session) ->
sessionID: session._id
team: session.team ? 'No team'
@ -142,7 +159,8 @@ module.exports.getTwoGames = (req, res) ->
ladderGameIDs = ['dueling-grounds', 'cavern-survival', 'multiplayer-treasure-grove', 'harrowland', 'zero-sum']
levelID = _.sample ladderGameIDs
unless ogresGameID and humansGameID
async.map [{levelID: levelID, team: 'humans'}, {levelID: levelID, team: 'ogres'}], findRandomSession, (err, sessions) ->
recentHumans = Math.random() < 0.5 # We pick one session favoring recent submissions, then find another one uniformly to play against
async.map [{levelID: levelID, team: 'humans', favorRecent: recentHumans}, {levelID: levelID, team: 'ogres', favorRecent: not recentHumans}], findRandomSession, (err, sessions) ->
if err then return errors.serverError(res, "Couldn't get two games to simulate for #{levelID}.")
unless sessions.length is 2
res.send(204, 'No games to score.')
@ -252,6 +270,7 @@ updateSessionToSubmit = (transpiledCode, sessionToUpdate, callback) ->
numberOfWinsAndTies: 0
numberOfLosses: 0
isRanking: true
randomSimulationIndex: Math.random()
LevelSession.update {_id: sessionToUpdate._id}, sessionUpdateObject, (err, result) ->
callback err, sessionToUpdate
@ -485,6 +504,7 @@ updateScoreInSession = (scoreObject, callback) ->
standardDeviation: scoreObject.standardDeviation
totalScore: newTotalScore
$push: {scoreHistory: {$each: [scoreHistoryAddition], $slice: -1000}}
randomSimulationIndex: Math.random()
LevelSession.update {'_id': scoreObject.id}, updateObject, callback
#log.info "New total score for session #{scoreObject.id} is #{updateObject.totalScore}"