mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-30 19:06:59 -05:00
Merge branch 'master' of https://github.com/codecombat/codecombat
This commit is contained in:
commit
943bcf3162
1 changed files with 210 additions and 176 deletions
|
@ -43,17 +43,17 @@ module.exports.addPairwiseTaskToQueueFromRequest = (req, res) ->
|
||||||
|
|
||||||
addPairwiseTaskToQueue = (taskPair, cb) ->
|
addPairwiseTaskToQueue = (taskPair, cb) ->
|
||||||
LevelSession.findOne(_id:taskPair[0]).lean().exec (err, firstSession) =>
|
LevelSession.findOne(_id:taskPair[0]).lean().exec (err, firstSession) =>
|
||||||
if err? then return cb err, false
|
if err? then return cb err
|
||||||
LevelSession.find(_id:taskPair[1]).exec (err, secondSession) =>
|
LevelSession.find(_id:taskPair[1]).exec (err, secondSession) =>
|
||||||
if err? then return cb err, false
|
if err? then return cb err
|
||||||
try
|
try
|
||||||
taskPairs = generateTaskPairs(secondSession, firstSession)
|
taskPairs = generateTaskPairs(secondSession, firstSession)
|
||||||
catch e
|
catch e
|
||||||
if e then return cb e, false
|
if e then return cb e
|
||||||
|
|
||||||
sendEachTaskPairToTheQueue taskPairs, (taskPairError) ->
|
sendEachTaskPairToTheQueue taskPairs, (taskPairError) ->
|
||||||
if taskPairError? then return cb taskPairError,false
|
if taskPairError? then return cb taskPairError
|
||||||
cb null, true
|
cb null
|
||||||
|
|
||||||
module.exports.resimulateAllSessions = (req, res) ->
|
module.exports.resimulateAllSessions = (req, res) ->
|
||||||
unless isUserAdmin req then return errors.unauthorized res, "Unauthorized. Even if you are authorized, you shouldn't do this"
|
unless isUserAdmin req then return errors.unauthorized res, "Unauthorized. Even if you are authorized, you shouldn't do this"
|
||||||
|
@ -68,8 +68,8 @@ module.exports.resimulateAllSessions = (req, res) ->
|
||||||
majorVersion: levelMajorVersion
|
majorVersion: levelMajorVersion
|
||||||
|
|
||||||
query = LevelSession
|
query = LevelSession
|
||||||
.find(findParameters)
|
.find(findParameters)
|
||||||
.lean()
|
.lean()
|
||||||
|
|
||||||
query.exec (err, result) ->
|
query.exec (err, result) ->
|
||||||
if err? then return errors.serverError res, err
|
if err? then return errors.serverError res, err
|
||||||
|
@ -107,7 +107,7 @@ module.exports.createNewTask = (req, res) ->
|
||||||
originalLevelID = req.body.originalLevelID
|
originalLevelID = req.body.originalLevelID
|
||||||
currentLevelID = req.body.levelID
|
currentLevelID = req.body.levelID
|
||||||
requestLevelMajorVersion = parseInt(req.body.levelMajorVersion)
|
requestLevelMajorVersion = parseInt(req.body.levelMajorVersion)
|
||||||
|
|
||||||
async.waterfall [
|
async.waterfall [
|
||||||
validatePermissions.bind(@,req,requestSessionID)
|
validatePermissions.bind(@,req,requestSessionID)
|
||||||
fetchAndVerifyLevelType.bind(@,currentLevelID)
|
fetchAndVerifyLevelType.bind(@,currentLevelID)
|
||||||
|
@ -115,12 +115,12 @@ module.exports.createNewTask = (req, res) ->
|
||||||
updateSessionToSubmit
|
updateSessionToSubmit
|
||||||
fetchInitialSessionsToRankAgainst.bind(@, requestLevelMajorVersion, originalLevelID)
|
fetchInitialSessionsToRankAgainst.bind(@, requestLevelMajorVersion, originalLevelID)
|
||||||
generateAndSendTaskPairsToTheQueue
|
generateAndSendTaskPairsToTheQueue
|
||||||
|
|
||||||
], (err, successMessageObject) ->
|
], (err, successMessageObject) ->
|
||||||
if err? then return errors.serverError res, "There was an error submitting the game to the queue:#{err}"
|
if err? then return errors.serverError res, "There was an error submitting the game to the queue:#{err}"
|
||||||
sendResponseObject req, res, successMessageObject
|
sendResponseObject req, res, successMessageObject
|
||||||
|
|
||||||
|
|
||||||
validatePermissions = (req,sessionID, callback) ->
|
validatePermissions = (req,sessionID, callback) ->
|
||||||
if isUserAnonymous req then return callback "You are unauthorized to submit that game to the simulator"
|
if isUserAnonymous req then return callback "You are unauthorized to submit that game to the simulator"
|
||||||
if isUserAdmin req then return callback null
|
if isUserAdmin req then return callback null
|
||||||
|
@ -136,7 +136,7 @@ validatePermissions = (req,sessionID, callback) ->
|
||||||
query.exec (err, retrievedSession) ->
|
query.exec (err, retrievedSession) ->
|
||||||
if err? then return callback err
|
if err? then return callback err
|
||||||
userHasPermissionToSubmitCode = retrievedSession.creator is req.user?.id and
|
userHasPermissionToSubmitCode = retrievedSession.creator is req.user?.id and
|
||||||
not _.isEqual(retrievedSession.code, retrievedSession.submittedCode)
|
not _.isEqual(retrievedSession.code, retrievedSession.submittedCode)
|
||||||
unless userHasPermissionToSubmitCode then return callback "You are unauthorized to submit that game to the simulator"
|
unless userHasPermissionToSubmitCode then return callback "You are unauthorized to submit that game to the simulator"
|
||||||
callback null
|
callback null
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ generateAndSendTaskPairsToTheQueue = (sessionToRankAgainst,submittedSession, cal
|
||||||
sendEachTaskPairToTheQueue taskPairs, (taskPairError) ->
|
sendEachTaskPairToTheQueue taskPairs, (taskPairError) ->
|
||||||
if taskPairError? then return callback taskPairError
|
if taskPairError? then return callback taskPairError
|
||||||
callback null, {"message": "All task pairs were succesfully sent to the queue"}
|
callback null, {"message": "All task pairs were succesfully sent to the queue"}
|
||||||
|
|
||||||
|
|
||||||
module.exports.dispatchTaskToConsumer = (req, res) ->
|
module.exports.dispatchTaskToConsumer = (req, res) ->
|
||||||
async.waterfall [
|
async.waterfall [
|
||||||
|
@ -221,7 +221,7 @@ module.exports.dispatchTaskToConsumer = (req, res) ->
|
||||||
constructTaskLogObject.bind(@, getUserIDFromRequest(req))
|
constructTaskLogObject.bind(@, getUserIDFromRequest(req))
|
||||||
processTaskObject
|
processTaskObject
|
||||||
], (err, taskObjectToSend) ->
|
], (err, taskObjectToSend) ->
|
||||||
if err?
|
if err?
|
||||||
if typeof err is "string" and err.indexOf "No more games in the queue" isnt -1
|
if typeof err is "string" and err.indexOf "No more games in the queue" isnt -1
|
||||||
res.send(204, "No games to score.")
|
res.send(204, "No games to score.")
|
||||||
return res.end()
|
return res.end()
|
||||||
|
@ -229,16 +229,16 @@ module.exports.dispatchTaskToConsumer = (req, res) ->
|
||||||
return errors.serverError res, "There was an error dispatching the task: #{err}"
|
return errors.serverError res, "There was an error dispatching the task: #{err}"
|
||||||
sendResponseObject req, res, taskObjectToSend
|
sendResponseObject req, res, taskObjectToSend
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
checkSimulationPermissions = (req, cb) ->
|
checkSimulationPermissions = (req, cb) ->
|
||||||
if isUserAnonymous req
|
if isUserAnonymous req
|
||||||
cb "You need to be logged in to simulate games"
|
cb "You need to be logged in to simulate games"
|
||||||
else
|
else
|
||||||
cb null
|
cb null
|
||||||
|
|
||||||
receiveMessageFromSimulationQueue = (cb) ->
|
receiveMessageFromSimulationQueue = (cb) ->
|
||||||
scoringTaskQueue.receiveMessage (err, message) ->
|
scoringTaskQueue.receiveMessage (err, message) ->
|
||||||
if err? then return cb "No more games in the queue, error:#{err}"
|
if err? then return cb "No more games in the queue, error:#{err}"
|
||||||
if messageIsInvalid(message) then return cb "Message received from queue is invalid"
|
if messageIsInvalid(message) then return cb "Message received from queue is invalid"
|
||||||
cb null, message
|
cb null, message
|
||||||
|
@ -292,90 +292,190 @@ processTaskObject = (taskObject,taskLogObject, message, cb) ->
|
||||||
cb null, taskObject
|
cb null, taskObject
|
||||||
|
|
||||||
getSessionInformation = (sessionIDString, callback) ->
|
getSessionInformation = (sessionIDString, callback) ->
|
||||||
findParameters =
|
findParameters =
|
||||||
_id: sessionIDString
|
_id: sessionIDString
|
||||||
selectString = 'submitDate team submittedCode teamSpells levelID creator creatorName'
|
selectString = 'submitDate team submittedCode teamSpells levelID creator creatorName'
|
||||||
query = LevelSession
|
query = LevelSession
|
||||||
.findOne(findParameters)
|
.findOne(findParameters)
|
||||||
.select(selectString)
|
.select(selectString)
|
||||||
.lean()
|
.lean()
|
||||||
|
|
||||||
query.exec (err, session) ->
|
query.exec (err, session) ->
|
||||||
if err? then return callback err, {"error":"There was an error retrieving the session."}
|
if err? then return callback err, {"error":"There was an error retrieving the session."}
|
||||||
callback null, session
|
callback null, session
|
||||||
|
|
||||||
|
|
||||||
module.exports.processTaskResult = (req, res) ->
|
module.exports.processTaskResult = (req, res) ->
|
||||||
clientResponseObject = verifyClientResponse req.body, res
|
async.waterfall [
|
||||||
|
verifyClientResponse.bind(@,req.body)
|
||||||
|
fetchTaskLog.bind(@)
|
||||||
|
checkTaskLog.bind(@)
|
||||||
|
deleteQueueMessage.bind(@)
|
||||||
|
fetchLevelSession.bind(@)
|
||||||
|
checkSubmissionDate.bind(@)
|
||||||
|
logTaskComputation.bind(@)
|
||||||
|
updateSessions.bind(@)
|
||||||
|
indexNewScoreArray.bind(@)
|
||||||
|
addMatchToSessions.bind(@)
|
||||||
|
updateUserSimulationCounts.bind(@, req.user._id)
|
||||||
|
determineIfSessionShouldContinueAndUpdateLog.bind(@)
|
||||||
|
findNearestBetterSessionID.bind(@)
|
||||||
|
addNewSessionsToQueue.bind(@)
|
||||||
|
], (err, results) ->
|
||||||
|
if err is "shouldn't continue"
|
||||||
|
sendResponseObject req, res, {"message":"The scores were updated successfully, person lost so no more games are being inserted!"}
|
||||||
|
else if err is "no session was found"
|
||||||
|
sendResponseObject req, res, {"message":"There were no more games to rank (game is at top)!"}
|
||||||
|
else if err?
|
||||||
|
errors.serverError res, "There was an error:#{err}"
|
||||||
|
else
|
||||||
|
sendResponseObject req, res, {"message":"The scores were updated successfully and more games were sent to the queue!"}
|
||||||
|
|
||||||
return unless clientResponseObject?
|
verifyClientResponse = (responseObject, callback) ->
|
||||||
TaskLog.findOne {_id: clientResponseObject.taskID}, (err, taskLog) ->
|
#TODO: better verification
|
||||||
return errors.serverError res, "There was an error retrieiving the task log object" if err?
|
unless typeof responseObject is "object"
|
||||||
|
callback "The response to that query is required to be a JSON object."
|
||||||
|
else
|
||||||
|
@clientResponseObject = responseObject
|
||||||
|
log.info "Verified client response!"
|
||||||
|
callback null, responseObject
|
||||||
|
|
||||||
taskLogJSON = taskLog.toObject()
|
fetchTaskLog = (responseObject, callback) ->
|
||||||
|
findParameters =
|
||||||
|
_id: responseObject.taskID
|
||||||
|
query = TaskLog
|
||||||
|
.findOne(findParameters)
|
||||||
|
query.exec (err, taskLog) =>
|
||||||
|
@taskLog = taskLog
|
||||||
|
log.info "Fetched task log!"
|
||||||
|
callback err, taskLog.toObject()
|
||||||
|
|
||||||
return errors.badInput res, "That computational task has already been performed" if taskLogJSON.calculationTimeMS
|
checkTaskLog = (taskLog, callback) ->
|
||||||
return handleTimedOutTask req, res, clientResponseObject if hasTaskTimedOut taskLogJSON.sentDate
|
if taskLog.calculationTimeMS then return callback "That computational task has already been performed"
|
||||||
|
if hasTaskTimedOut taskLog.sentDate then return callback "The task has timed out"
|
||||||
|
log.info "Checked task log"
|
||||||
|
callback null
|
||||||
|
|
||||||
scoringTaskQueue.deleteMessage clientResponseObject.receiptHandle, (err) ->
|
deleteQueueMessage = (callback) ->
|
||||||
console.log "Deleted message."
|
scoringTaskQueue.deleteMessage @clientResponseObject.receiptHandle, (err) ->
|
||||||
if err? then return errors.badInput res, "The queue message is already back in the queue, rejecting results."
|
log.info "Deleted queue message"
|
||||||
|
callback err
|
||||||
|
|
||||||
LevelSession.findOne(_id: clientResponseObject.originalSessionID).lean().exec (err, levelSession) ->
|
fetchLevelSession = (callback) ->
|
||||||
if err? then return errors.serverError res, "There was a problem finding the level session:#{err}"
|
findParameters =
|
||||||
|
_id: @clientResponseObject.originalSessionID
|
||||||
supposedSubmissionDate = new Date(clientResponseObject.sessions[0].submitDate)
|
query = LevelSession
|
||||||
|
.findOne(findParameters)
|
||||||
if Number(supposedSubmissionDate) isnt Number(levelSession.submitDate)
|
.lean()
|
||||||
return sendResponseObject req, res, {"message":"The game has been resubmitted. Removing from queue..."}
|
query.exec (err, session) =>
|
||||||
|
@levelSession = session
|
||||||
logTaskComputation clientResponseObject, taskLog, (logErr) ->
|
log.info "Fetched level session"
|
||||||
if logErr? then return errors.serverError res, "There as a problem logging the task computation: #{logErr}"
|
callback err
|
||||||
|
|
||||||
updateSessions clientResponseObject, (updateError, newScoreArray) ->
|
|
||||||
if updateError? then return errors.serverError res, "There was an error updating the scores.#{updateError}"
|
|
||||||
|
|
||||||
newScoresObject = _.indexBy newScoreArray, 'id'
|
|
||||||
|
|
||||||
addMatchToSessions clientResponseObject, newScoresObject, (err, data) ->
|
|
||||||
if err? then return errors.serverError res, "There was an error updating the sessions with the match! #{JSON.stringify err}"
|
|
||||||
|
|
||||||
incrementUserSimulationCount req.user._id, 'simulatedBy'
|
|
||||||
incrementUserSimulationCount levelSession.creator, 'simulatedFor'
|
|
||||||
|
|
||||||
originalSessionID = clientResponseObject.originalSessionID
|
|
||||||
originalSessionTeam = clientResponseObject.originalSessionTeam
|
|
||||||
originalSessionRank = parseInt clientResponseObject.originalSessionRank
|
|
||||||
|
|
||||||
determineIfSessionShouldContinueAndUpdateLog originalSessionID, originalSessionRank, (err, sessionShouldContinue) ->
|
|
||||||
if err? then return errors.serverError res, "There was an error determining if the session should continue, #{err}"
|
|
||||||
|
|
||||||
if sessionShouldContinue
|
|
||||||
opposingTeam = calculateOpposingTeam(originalSessionTeam)
|
|
||||||
opponentID = _.pull(_.keys(newScoresObject), originalSessionID)
|
|
||||||
sessionNewScore = newScoresObject[originalSessionID].totalScore
|
|
||||||
opponentNewScore = newScoresObject[opponentID].totalScore
|
|
||||||
|
|
||||||
levelOriginalID = levelSession.level.original
|
|
||||||
levelOriginalMajorVersion = levelSession.level.majorVersion
|
|
||||||
findNearestBetterSessionID levelOriginalID, levelOriginalMajorVersion, originalSessionID, sessionNewScore, opponentNewScore, opponentID, opposingTeam, (err, opponentSessionID) ->
|
|
||||||
if err? then return errors.serverError res, "There was an error finding the nearest sessionID!"
|
|
||||||
if opponentSessionID
|
|
||||||
addPairwiseTaskToQueue [originalSessionID, opponentSessionID], (err, success) ->
|
|
||||||
if err? then return errors.serverError res, "There was an error sending the pairwise tasks to the queue!"
|
|
||||||
sendResponseObject req, res, {"message":"The scores were updated successfully and more games were sent to the queue!"}
|
|
||||||
else
|
|
||||||
LevelSession.update {_id: originalSessionID}, {isRanking: false}, {multi: false}, (err, affected) ->
|
|
||||||
if err? then return errors.serverError res, "There was an error marking the victorious session as not being ranked."
|
|
||||||
return sendResponseObject req, res, {"message":"There were no more games to rank (game is at top)!"}
|
|
||||||
else
|
|
||||||
console.log "Player lost, achieved rank #{originalSessionRank}"
|
|
||||||
LevelSession.update {_id: originalSessionID}, {isRanking: false}, {multi: false}, (err, affected) ->
|
|
||||||
if err? then return errors.serverError res, "There was an error marking the completed session as not being ranked."
|
|
||||||
sendResponseObject req, res, {"message":"The scores were updated successfully, person lost so no more games are being inserted!"}
|
|
||||||
|
|
||||||
|
|
||||||
determineIfSessionShouldContinueAndUpdateLog = (sessionID, sessionRank, cb) ->
|
checkSubmissionDate = (callback) ->
|
||||||
|
supposedSubmissionDate = new Date(@clientResponseObject.sessions[0].submitDate)
|
||||||
|
if Number(supposedSubmissionDate) isnt Number(@levelSession.submitDate)
|
||||||
|
callback "The game has been resubmitted. Removing from queue..."
|
||||||
|
else
|
||||||
|
log.info "Checked submission date"
|
||||||
|
callback null
|
||||||
|
|
||||||
|
logTaskComputation = (callback) ->
|
||||||
|
@taskLog.set('calculationTimeMS',@clientResponseObject.calculationTimeMS)
|
||||||
|
@taskLog.set('sessions')
|
||||||
|
@taskLog.calculationTimeMS = @clientResponseObject.calculationTimeMS
|
||||||
|
@taskLog.sessions = @clientResponseObject.sessions
|
||||||
|
@taskLog.save (err, saved) ->
|
||||||
|
log.info "Logged task computation"
|
||||||
|
callback err
|
||||||
|
|
||||||
|
updateSessions = (callback) ->
|
||||||
|
sessionIDs = _.pluck @clientResponseObject.sessions, 'sessionID'
|
||||||
|
|
||||||
|
async.map sessionIDs, retrieveOldSessionData, (err, oldScores) =>
|
||||||
|
if err? then callback err, {"error": "There was an error retrieving the old scores"}
|
||||||
|
|
||||||
|
oldScoreArray = _.toArray putRankingFromMetricsIntoScoreObject @clientResponseObject, oldScores
|
||||||
|
newScoreArray = bayes.updatePlayerSkills oldScoreArray
|
||||||
|
saveNewScoresToDatabase newScoreArray, callback
|
||||||
|
|
||||||
|
|
||||||
|
saveNewScoresToDatabase = (newScoreArray, callback) ->
|
||||||
|
async.eachSeries newScoreArray, updateScoreInSession, (err) ->
|
||||||
|
log.info "Saved new scores to database"
|
||||||
|
callback err,newScoreArray
|
||||||
|
|
||||||
|
|
||||||
|
updateScoreInSession = (scoreObject,callback) ->
|
||||||
|
LevelSession.findOne {"_id": scoreObject.id}, (err, session) ->
|
||||||
|
if err? then return callback err, null
|
||||||
|
|
||||||
|
session = session.toObject()
|
||||||
|
newTotalScore = scoreObject.meanStrength - 1.8 * scoreObject.standardDeviation
|
||||||
|
scoreHistoryAddition = [Date.now(), newTotalScore]
|
||||||
|
updateObject =
|
||||||
|
meanStrength: scoreObject.meanStrength
|
||||||
|
standardDeviation: scoreObject.standardDeviation
|
||||||
|
totalScore: newTotalScore
|
||||||
|
$push: {scoreHistory: {$each: [scoreHistoryAddition], $slice: -1000}}
|
||||||
|
|
||||||
|
LevelSession.update {"_id": scoreObject.id}, updateObject, callback
|
||||||
|
log.info "New total score for session #{scoreObject.id} is #{updateObject.totalScore}"
|
||||||
|
|
||||||
|
indexNewScoreArray = (newScoreArray, callback) ->
|
||||||
|
newScoresObject = _.indexBy newScoreArray, 'id'
|
||||||
|
@newScoresObject = newScoresObject
|
||||||
|
callback null, newScoresObject
|
||||||
|
|
||||||
|
addMatchToSessions = (newScoreObject, callback) ->
|
||||||
|
matchObject = {}
|
||||||
|
matchObject.date = new Date()
|
||||||
|
matchObject.opponents = {}
|
||||||
|
for session in @clientResponseObject.sessions
|
||||||
|
sessionID = session.sessionID
|
||||||
|
matchObject.opponents[sessionID] = {}
|
||||||
|
matchObject.opponents[sessionID].sessionID = sessionID
|
||||||
|
matchObject.opponents[sessionID].userID = session.creator
|
||||||
|
matchObject.opponents[sessionID].metrics = {}
|
||||||
|
matchObject.opponents[sessionID].metrics.rank = Number(newScoreObject[sessionID].gameRanking)
|
||||||
|
|
||||||
|
log.info "Match object computed, result: #{matchObject}"
|
||||||
|
log.info "Writing match object to database..."
|
||||||
|
#use bind with async to do the writes
|
||||||
|
sessionIDs = _.pluck @clientResponseObject.sessions, 'sessionID'
|
||||||
|
async.each sessionIDs, updateMatchesInSession.bind(@,matchObject), (err) -> callback err
|
||||||
|
|
||||||
|
updateMatchesInSession = (matchObject, sessionID, callback) ->
|
||||||
|
currentMatchObject = {}
|
||||||
|
currentMatchObject.date = matchObject.date
|
||||||
|
currentMatchObject.metrics = matchObject.opponents[sessionID].metrics
|
||||||
|
opponentsClone = _.cloneDeep matchObject.opponents
|
||||||
|
opponentsClone = _.omit opponentsClone, sessionID
|
||||||
|
opponentsArray = _.toArray opponentsClone
|
||||||
|
currentMatchObject.opponents = opponentsArray
|
||||||
|
|
||||||
|
sessionUpdateObject =
|
||||||
|
$push: {matches: {$each: [currentMatchObject], $slice: -200}}
|
||||||
|
log.info "Updating session #{sessionID}"
|
||||||
|
LevelSession.update {"_id":sessionID}, sessionUpdateObject, callback
|
||||||
|
|
||||||
|
updateUserSimulationCounts = (reqUserID,callback) ->
|
||||||
|
incrementUserSimulationCount reqUserID, 'simulatedBy', (err) =>
|
||||||
|
if err? then return callback err
|
||||||
|
incrementUserSimulationCount @levelSession.creator, 'simulatedFor', callback
|
||||||
|
|
||||||
|
incrementUserSimulationCount = (userID, type, callback) =>
|
||||||
|
inc = {}
|
||||||
|
inc[type] = 1
|
||||||
|
User.update {_id: userID}, {$inc: inc}, (err, affected) ->
|
||||||
|
log.error "Error incrementing #{type} for #{userID}: #{err}" if err
|
||||||
|
callback err
|
||||||
|
|
||||||
|
determineIfSessionShouldContinueAndUpdateLog = (cb) ->
|
||||||
|
sessionID = @clientResponseObject.originalSessionID
|
||||||
|
sessionRank = parseInt @clientResponseObject.originalSessionRank
|
||||||
|
|
||||||
queryParameters =
|
queryParameters =
|
||||||
_id: sessionID
|
_id: sessionID
|
||||||
|
|
||||||
|
@ -394,18 +494,28 @@ determineIfSessionShouldContinueAndUpdateLog = (sessionID, sessionRank, cb) ->
|
||||||
totalNumberOfGamesPlayed = updatedSession.numberOfWinsAndTies + updatedSession.numberOfLosses
|
totalNumberOfGamesPlayed = updatedSession.numberOfWinsAndTies + updatedSession.numberOfLosses
|
||||||
if totalNumberOfGamesPlayed < 10
|
if totalNumberOfGamesPlayed < 10
|
||||||
console.log "Number of games played is less than 10, continuing..."
|
console.log "Number of games played is less than 10, continuing..."
|
||||||
cb null, true
|
cb null
|
||||||
else
|
else
|
||||||
ratio = (updatedSession.numberOfLosses) / (totalNumberOfGamesPlayed)
|
ratio = (updatedSession.numberOfLosses) / (totalNumberOfGamesPlayed)
|
||||||
if ratio > 0.33
|
if ratio > 0.33
|
||||||
cb null, false
|
cb "shouldn't continue"
|
||||||
console.log "Ratio(#{ratio}) is bad, ending simulation"
|
console.log "Ratio(#{ratio}) is bad, ending simulation"
|
||||||
else
|
else
|
||||||
console.log "Ratio(#{ratio}) is good, so continuing simulations"
|
console.log "Ratio(#{ratio}) is good, so continuing simulations"
|
||||||
cb null, true
|
cb null
|
||||||
|
|
||||||
|
|
||||||
|
findNearestBetterSessionID = (cb) ->
|
||||||
|
levelOriginalID = @levelSession.level.original
|
||||||
|
levelMajorVersion = @levelSession.level.majorVersion
|
||||||
|
sessionID = @clientResponseObject.originalSessionID
|
||||||
|
sessionTotalScore = @newScoresObject[sessionID].totalScore
|
||||||
|
opponentSessionID = _.pull(_.keys(@newScoresObject), sessionID)
|
||||||
|
opponentSessionTotalScore = @newScoresObject[opponentSessionID].totalScore
|
||||||
|
opposingTeam = calculateOpposingTeam(@clientResponseObject.originalSessionTeam)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
findNearestBetterSessionID = (levelOriginalID, levelMajorVersion, sessionID, sessionTotalScore, opponentSessionTotalScore, opponentSessionID, opposingTeam, cb) ->
|
|
||||||
retrieveAllOpponentSessionIDs sessionID, (err, opponentSessionIDs) ->
|
retrieveAllOpponentSessionIDs sessionID, (err, opponentSessionIDs) ->
|
||||||
if err? then return cb err, null
|
if err? then return cb err, null
|
||||||
|
|
||||||
|
@ -434,23 +544,23 @@ findNearestBetterSessionID = (levelOriginalID, levelMajorVersion, sessionID, ses
|
||||||
selectString = '_id totalScore'
|
selectString = '_id totalScore'
|
||||||
|
|
||||||
query = LevelSession.findOne(queryParameters)
|
query = LevelSession.findOne(queryParameters)
|
||||||
.sort(sortParameters)
|
.sort(sortParameters)
|
||||||
.limit(limitNumber)
|
.limit(limitNumber)
|
||||||
.select(selectString)
|
.select(selectString)
|
||||||
.lean()
|
.lean()
|
||||||
|
|
||||||
console.log "Finding session with score near #{opponentSessionTotalScore}"
|
console.log "Finding session with score near #{opponentSessionTotalScore}"
|
||||||
query.exec (err, session) ->
|
query.exec (err, session) ->
|
||||||
if err? then return cb err, session
|
if err? then return cb err, session
|
||||||
unless session then return cb err, null
|
unless session then return cb "no session was found"
|
||||||
console.log "Found session with score #{session.totalScore}"
|
console.log "Found session with score #{session.totalScore}"
|
||||||
cb err, session._id
|
cb err, session._id
|
||||||
|
|
||||||
|
|
||||||
retrieveAllOpponentSessionIDs = (sessionID, cb) ->
|
retrieveAllOpponentSessionIDs = (sessionID, cb) ->
|
||||||
query = LevelSession.findOne({"_id":sessionID})
|
query = LevelSession.findOne({"_id":sessionID})
|
||||||
.select('matches.opponents.sessionID matches.date submitDate')
|
.select('matches.opponents.sessionID matches.date submitDate')
|
||||||
.lean()
|
.lean()
|
||||||
query.exec (err, session) ->
|
query.exec (err, session) ->
|
||||||
if err? then return cb err, null
|
if err? then return cb err, null
|
||||||
opponentSessionIDs = (match.opponents[0].sessionID for match in session.matches when match.date > session.submitDate)
|
opponentSessionIDs = (match.opponents[0].sessionID for match in session.matches when match.date > session.submitDate)
|
||||||
|
@ -462,44 +572,11 @@ calculateOpposingTeam = (sessionTeam) ->
|
||||||
opposingTeams = _.pull teams, sessionTeam
|
opposingTeams = _.pull teams, sessionTeam
|
||||||
return opposingTeams[0]
|
return opposingTeams[0]
|
||||||
|
|
||||||
incrementUserSimulationCount = (userID, type) ->
|
|
||||||
inc = {}
|
|
||||||
inc[type] = 1
|
|
||||||
User.update {_id: userID}, {$inc: inc}, (err, affected) ->
|
|
||||||
log.error "Error incrementing #{type} for #{userID}: #{err}" if err
|
|
||||||
|
|
||||||
|
addNewSessionsToQueue = (sessionID, callback) ->
|
||||||
|
sessions = [@clientResponseObject.originalSessionID, sessionID]
|
||||||
|
addPairwiseTaskToQueue sessions, callback
|
||||||
|
|
||||||
addMatchToSessions = (clientResponseObject, newScoreObject, callback) ->
|
|
||||||
matchObject = {}
|
|
||||||
matchObject.date = new Date()
|
|
||||||
matchObject.opponents = {}
|
|
||||||
for session in clientResponseObject.sessions
|
|
||||||
sessionID = session.sessionID
|
|
||||||
matchObject.opponents[sessionID] = {}
|
|
||||||
matchObject.opponents[sessionID].sessionID = sessionID
|
|
||||||
matchObject.opponents[sessionID].userID = session.creator
|
|
||||||
matchObject.opponents[sessionID].metrics = {}
|
|
||||||
matchObject.opponents[sessionID].metrics.rank = Number(newScoreObject[sessionID].gameRanking)
|
|
||||||
|
|
||||||
log.info "Match object computed, result: #{matchObject}"
|
|
||||||
log.info "Writing match object to database..."
|
|
||||||
#use bind with async to do the writes
|
|
||||||
sessionIDs = _.pluck clientResponseObject.sessions, 'sessionID'
|
|
||||||
async.each sessionIDs, updateMatchesInSession.bind(@,matchObject), (err) -> callback err, null
|
|
||||||
|
|
||||||
updateMatchesInSession = (matchObject, sessionID, callback) ->
|
|
||||||
currentMatchObject = {}
|
|
||||||
currentMatchObject.date = matchObject.date
|
|
||||||
currentMatchObject.metrics = matchObject.opponents[sessionID].metrics
|
|
||||||
opponentsClone = _.cloneDeep matchObject.opponents
|
|
||||||
opponentsClone = _.omit opponentsClone, sessionID
|
|
||||||
opponentsArray = _.toArray opponentsClone
|
|
||||||
currentMatchObject.opponents = opponentsArray
|
|
||||||
|
|
||||||
sessionUpdateObject =
|
|
||||||
$push: {matches: {$each: [currentMatchObject], $slice: -200}}
|
|
||||||
log.info "Updating session #{sessionID}"
|
|
||||||
LevelSession.update {"_id":sessionID}, sessionUpdateObject, callback
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -545,49 +622,6 @@ hasTaskTimedOut = (taskSentTimestamp) -> taskSentTimestamp + scoringTaskTimeoutI
|
||||||
|
|
||||||
handleTimedOutTask = (req, res, taskBody) -> errors.clientTimeout res, "The results weren't provided within the timeout"
|
handleTimedOutTask = (req, res, taskBody) -> errors.clientTimeout res, "The results weren't provided within the timeout"
|
||||||
|
|
||||||
verifyClientResponse = (responseObject, res) ->
|
|
||||||
unless typeof responseObject is "object"
|
|
||||||
errors.badInput res, "The response to that query is required to be a JSON object."
|
|
||||||
null
|
|
||||||
else
|
|
||||||
responseObject
|
|
||||||
|
|
||||||
logTaskComputation = (taskObject,taskLogObject, callback) ->
|
|
||||||
taskLogObject.calculationTimeMS = taskObject.calculationTimeMS
|
|
||||||
taskLogObject.sessions = taskObject.sessions
|
|
||||||
taskLogObject.save callback
|
|
||||||
|
|
||||||
|
|
||||||
updateSessions = (taskObject,callback) ->
|
|
||||||
sessionIDs = _.pluck taskObject.sessions, 'sessionID'
|
|
||||||
|
|
||||||
async.map sessionIDs, retrieveOldSessionData, (err, oldScores) ->
|
|
||||||
if err? then callback err, {"error": "There was an error retrieving the old scores"}
|
|
||||||
|
|
||||||
oldScoreArray = _.toArray putRankingFromMetricsIntoScoreObject taskObject, oldScores
|
|
||||||
newScoreArray = bayes.updatePlayerSkills oldScoreArray
|
|
||||||
saveNewScoresToDatabase newScoreArray, callback
|
|
||||||
|
|
||||||
|
|
||||||
saveNewScoresToDatabase = (newScoreArray, callback) ->
|
|
||||||
async.eachSeries newScoreArray, updateScoreInSession, (err) -> callback err,newScoreArray
|
|
||||||
|
|
||||||
|
|
||||||
updateScoreInSession = (scoreObject,callback) ->
|
|
||||||
LevelSession.findOne {"_id": scoreObject.id}, (err, session) ->
|
|
||||||
if err? then return callback err, null
|
|
||||||
|
|
||||||
session = session.toObject()
|
|
||||||
newTotalScore = scoreObject.meanStrength - 1.8 * scoreObject.standardDeviation
|
|
||||||
scoreHistoryAddition = [Date.now(), newTotalScore]
|
|
||||||
updateObject =
|
|
||||||
meanStrength: scoreObject.meanStrength
|
|
||||||
standardDeviation: scoreObject.standardDeviation
|
|
||||||
totalScore: newTotalScore
|
|
||||||
$push: {scoreHistory: {$each: [scoreHistoryAddition], $slice: -1000}}
|
|
||||||
|
|
||||||
LevelSession.update {"_id": scoreObject.id}, updateObject, callback
|
|
||||||
log.info "New total score for session #{scoreObject.id} is #{updateObject.totalScore}"
|
|
||||||
|
|
||||||
|
|
||||||
putRankingFromMetricsIntoScoreObject = (taskObject,scoreObject) ->
|
putRankingFromMetricsIntoScoreObject = (taskObject,scoreObject) ->
|
||||||
|
|
Loading…
Reference in a new issue