2014-02-14 10:49:16 -08:00
SuperModel = require ' models/SuperModel '
2014-03-16 13:37:17 +05:30
CocoClass = require ' lib/CocoClass '
2014-02-14 10:49:16 -08:00
LevelLoader = require ' lib/LevelLoader '
GoalManager = require ' lib/world/GoalManager '
2014-02-14 15:50:42 -08:00
God = require ' lib/God '
2014-02-14 10:49:16 -08:00
2014-03-16 13:37:17 +05:30
module.exports = class Simulator extends CocoClass
2014-02-14 14:55:30 -08:00
2014-02-14 10:49:16 -08:00
constructor: ->
2014-02-20 08:06:11 -08:00
_ . extend @ , Backbone . Events
@ trigger ' statusUpdate ' , ' Starting simulation! '
2014-02-14 10:49:16 -08:00
@retryDelayInSeconds = 10
2014-02-14 14:55:30 -08:00
@taskURL = ' /queue/scoring '
2014-03-11 19:17:58 -07:00
2014-03-07 12:15:16 -08:00
destroy: ->
@ off ( )
@ cleanupSimulation ( )
2014-03-16 13:37:17 +05:30
super ( )
2014-02-14 10:49:16 -08:00
fetchAndSimulateTask: =>
2014-03-16 20:36:02 -07:00
return if @ destroyed
2014-02-20 08:06:11 -08:00
@ trigger ' statusUpdate ' , ' Fetching simulation data! '
2014-02-14 10:49:16 -08:00
$ . ajax
url: @ taskURL
type: " GET "
error: @ handleFetchTaskError
success: @ setupSimulationAndLoadLevel
handleFetchTaskError: (errorData) =>
2014-03-11 19:17:58 -07:00
console . error " There was a horrible Error: #{ JSON . stringify errorData } "
@ trigger ' statusUpdate ' , ' There was an error fetching games to simulate. Retrying in 10 seconds. '
@ simulateAnotherTaskAfterDelay ( )
2014-02-14 10:49:16 -08:00
2014-03-11 19:17:58 -07:00
handleNoGamesResponse: ->
@ trigger ' statusUpdate ' , ' There were no games to simulate--nice. Retrying in 10 seconds. '
2014-02-14 14:55:30 -08:00
@ simulateAnotherTaskAfterDelay ( )
simulateAnotherTaskAfterDelay: =>
2014-03-11 19:17:58 -07:00
console . log " Retrying in #{ @ retryDelayInSeconds } "
2014-02-14 14:55:30 -08:00
retryDelayInMilliseconds = @ retryDelayInSeconds * 1000
_ . delay @ fetchAndSimulateTask , retryDelayInMilliseconds
2014-02-14 10:49:16 -08:00
2014-03-11 19:17:58 -07:00
setupSimulationAndLoadLevel: (taskData, textStatus, jqXHR) =>
return @ handleNoGamesResponse ( ) if jqXHR . status is 204
2014-02-20 08:06:11 -08:00
@ trigger ' statusUpdate ' , ' Setting up simulation! '
2014-02-14 10:49:16 -08:00
@task = new SimulationTask ( taskData )
2014-03-26 12:12:43 -07:00
try
levelID = @ task . getLevelName ( )
catch err
console . error err
@ trigger ' statusUpdate ' , " Error simulating game: #{ err } . Trying another game in #{ @ retryDelayInSeconds } seconds. "
@ simulateAnotherTaskAfterDelay ( )
return
2014-03-18 11:26:15 -07:00
@ supermodel ? = new SuperModel ( )
2014-02-15 17:29:54 -08:00
@god = new God maxWorkerPoolSize: 1 , maxAngels: 1 # Start loading worker.
2014-02-14 10:49:16 -08:00
2014-03-26 12:12:43 -07:00
@levelLoader = new LevelLoader supermodel: @ supermodel , levelID: levelID , sessionID: @ task . getFirstSessionID ( ) , headless: true
2014-03-24 22:28:34 +05:30
@ listenToOnce ( @ levelLoader , ' loaded-all ' , @ simulateGame )
2014-02-14 10:49:16 -08:00
2014-03-24 22:28:34 +05:30
simulateGame: ->
2014-03-16 13:37:17 +05:30
return if @ destroyed
2014-02-20 08:06:11 -08:00
@ trigger ' statusUpdate ' , ' All resources loaded, simulating! ' , @ task . getSessions ( )
2014-02-14 10:49:16 -08:00
@ assignWorldAndLevelFromLevelLoaderAndDestroyIt ( )
@ setupGod ( )
2014-02-14 14:55:30 -08:00
2014-02-14 10:49:16 -08:00
try
@ commenceSimulationAndSetupCallback ( )
2014-02-14 14:55:30 -08:00
catch err
2014-02-15 15:45:53 -08:00
console . log " There was an error in simulation( #{ err } ). Trying again in #{ @ retryDelayInSeconds } seconds "
2014-02-14 14:55:30 -08:00
@ simulateAnotherTaskAfterDelay ( )
2014-02-14 10:49:16 -08:00
assignWorldAndLevelFromLevelLoaderAndDestroyIt: ->
@world = @ levelLoader . world
@level = @ levelLoader . level
@ levelLoader . destroy ( )
2014-02-15 15:44:45 -08:00
@levelLoader = null
2014-02-14 10:49:16 -08:00
setupGod: ->
@god.level = @ level . serialize @ supermodel
2014-02-14 15:50:42 -08:00
@god.worldClassMap = @ world . classMap
2014-02-14 10:49:16 -08:00
@ setupGoalManager ( )
@ setupGodSpells ( )
setupGoalManager: ->
2014-04-08 14:58:34 -07:00
@god.goalManager = new GoalManager @ world , @ level . get ' goals '
2014-02-14 10:49:16 -08:00
commenceSimulationAndSetupCallback: ->
@ god . createWorld ( )
2014-03-26 11:25:05 -07:00
Backbone . Mediator . subscribeOnce ' god:infinite-loop ' , @ onInfiniteLoop , @
2014-02-14 10:49:16 -08:00
Backbone . Mediator . subscribeOnce ' god:new-world-created ' , @ processResults , @
2014-03-26 11:25:05 -07:00
onInfiniteLoop: ->
console . warn " Skipping infinitely looping game. "
2014-03-26 12:12:43 -07:00
@ trigger ' statusUpdate ' , " Infinite loop detected; grabbing a new game in #{ @ retryDelayInSeconds } seconds. "
2014-03-26 12:34:45 -07:00
_ . delay @ cleanupAndSimulateAnotherTask , @ retryDelayInSeconds * 1000
2014-03-26 11:25:05 -07:00
2014-02-14 10:49:16 -08:00
processResults: (simulationResults) ->
taskResults = @ formTaskResultsObject simulationResults
2014-02-14 15:50:42 -08:00
@ sendResultsBackToServer taskResults
2014-02-14 10:49:16 -08:00
sendResultsBackToServer: (results) =>
2014-02-20 08:06:11 -08:00
@ trigger ' statusUpdate ' , ' Simulation completed, sending results back to server! '
2014-02-18 11:46:14 -08:00
console . log " Sending result back to server! "
2014-04-11 17:11:55 -07:00
2014-02-14 10:49:16 -08:00
$ . ajax
2014-02-18 11:46:14 -08:00
url: " /queue/scoring "
2014-02-14 10:49:16 -08:00
data: results
type: " PUT "
success: @ handleTaskResultsTransferSuccess
error: @ handleTaskResultsTransferError
2014-02-14 16:53:34 -08:00
complete: @ cleanupAndSimulateAnotherTask
2014-02-14 10:49:16 -08:00
2014-02-20 08:06:11 -08:00
handleTaskResultsTransferSuccess: (result) =>
2014-02-14 10:49:16 -08:00
console . log " Task registration result: #{ JSON . stringify result } "
2014-02-20 08:06:11 -08:00
@ trigger ' statusUpdate ' , ' Results were successfully sent back to server! '
2014-03-20 19:18:46 -07:00
simulatedBy = parseInt ( $ ( ' # simulated-by-you ' ) . text ( ) , 10 ) + 1
$ ( ' # simulated-by-you ' ) . text ( simulatedBy )
2014-02-14 10:49:16 -08:00
2014-02-20 08:06:11 -08:00
handleTaskResultsTransferError: (error) =>
@ trigger ' statusUpdate ' , ' There was an error sending the results back to the server. '
2014-02-14 10:49:16 -08:00
console . log " Task registration error: #{ JSON . stringify error } "
cleanupAndSimulateAnotherTask: =>
@ cleanupSimulation ( )
@ fetchAndSimulateTask ( )
2014-02-14 14:55:30 -08:00
cleanupSimulation: ->
2014-03-07 12:15:16 -08:00
@ god ? . destroy ( )
2014-02-15 15:44:45 -08:00
@god = null
@world = null
@level = null
2014-02-14 10:49:16 -08:00
formTaskResultsObject: (simulationResults) ->
taskResults =
taskID: @ task . getTaskID ( )
receiptHandle: @ task . getReceiptHandle ( )
2014-02-26 12:14:02 -08:00
originalSessionID: @ task . getFirstSessionID ( )
originalSessionRank: - 1
2014-02-14 10:49:16 -08:00
calculationTime: 500
sessions: [ ]
2014-02-14 15:50:42 -08:00
for session in @ task . getSessions ( )
2014-02-26 17:45:08 -08:00
2014-02-14 10:49:16 -08:00
sessionResult =
sessionID: session . sessionID
2014-02-18 11:46:14 -08:00
submitDate: session . submitDate
creator: session . creator
2014-02-14 10:49:16 -08:00
metrics:
2014-02-14 15:50:42 -08:00
rank: @ calculateSessionRank session . sessionID , simulationResults . goalStates , @ task . generateTeamToSessionMap ( )
2014-02-26 12:14:02 -08:00
if session . sessionID is taskResults . originalSessionID
taskResults.originalSessionRank = sessionResult . metrics . rank
taskResults.originalSessionTeam = session . team
2014-02-14 10:49:16 -08:00
taskResults . sessions . push sessionResult
return taskResults
2014-02-14 15:50:42 -08:00
calculateSessionRank: (sessionID, goalStates, teamSessionMap) ->
2014-04-08 14:58:34 -07:00
ogreGoals = ( goalState for key , goalState of goalStates when goalState . team is ' ogres ' )
humanGoals = ( goalState for key , goalState of goalStates when goalState . team is ' humans ' )
ogresWon = _ . all ogreGoals , { status: ' success ' }
humansWon = _ . all humanGoals , { status: ' success ' }
if ogresWon is humansWon
2014-02-14 10:49:16 -08:00
return 0
2014-04-08 14:58:34 -07:00
else if ogresWon and teamSessionMap [ " ogres " ] is sessionID
2014-02-14 10:49:16 -08:00
return 0
2014-04-08 14:58:34 -07:00
else if ogresWon and teamSessionMap [ " ogres " ] isnt sessionID
2014-02-14 10:49:16 -08:00
return 1
2014-04-08 14:58:34 -07:00
else if humansWon and teamSessionMap [ " humans " ] is sessionID
2014-02-14 10:49:16 -08:00
return 0
else
return 1
setupGodSpells: ->
@ generateSpellsObject ( )
@god.spells = @ spells
2014-02-14 15:50:42 -08:00
generateSpellsObject: ->
@currentUserCodeMap = @ task . generateSpellKeyToSourceMap ( )
2014-02-14 10:49:16 -08:00
@spells = { }
for thang in @ level . attributes . thangs
continue if @ thangIsATemplate thang
@ generateSpellKeyToSourceMapPropertiesFromThang thang
thangIsATemplate: (thang) ->
for component in thang . components
continue unless @ componentHasProgrammableMethods component
for methodName , method of component . config . programmableMethods
2014-02-14 15:50:42 -08:00
return true if @ methodBelongsToTemplateThang method
2014-02-14 10:49:16 -08:00
return false
componentHasProgrammableMethods: (component) -> component . config ? and _ . has component . config , ' programmableMethods '
methodBelongsToTemplateThang: (method) -> typeof method is ' string '
generateSpellKeyToSourceMapPropertiesFromThang: (thang) =>
for component in thang . components
continue unless @ componentHasProgrammableMethods component
for methodName , method of component . config . programmableMethods
spellKey = @ generateSpellKeyFromThangIDAndMethodName thang . id , methodName
@ createSpellAndAssignName spellKey , methodName
@ createSpellThang thang , method , spellKey
@ transpileSpell thang , spellKey , methodName
generateSpellKeyFromThangIDAndMethodName: (thang, methodName) ->
2014-02-26 09:21:41 -08:00
spellKeyComponents = [ thang , methodName ]
2014-02-14 15:50:42 -08:00
spellKeyComponents [ 0 ] = _ . string . slugify spellKeyComponents [ 0 ]
2014-02-26 09:21:41 -08:00
spellKey = spellKeyComponents . join ' / '
spellKey
2014-02-26 17:45:08 -08:00
2014-02-14 10:49:16 -08:00
createSpellAndAssignName: (spellKey, spellName) ->
@ spells [ spellKey ] ? = { }
2014-02-14 15:50:42 -08:00
@ spells [ spellKey ] . name = spellName
2014-02-14 10:49:16 -08:00
createSpellThang: (thang, method, spellKey) ->
@ spells [ spellKey ] . thangs ? = { }
@ spells [ spellKey ] . thangs [ thang . id ] ? = { }
2014-04-11 17:11:55 -07:00
spellTeam = @ task . getSpellKeyToTeamMap ( ) [ spellKey ]
playerTeams = @ task . getPlayerTeams ( )
useProtectAPI = true
if spellTeam not in playerTeams then useProtectAPI = false
@ spells [ spellKey ] . thangs [ thang . id ] . aether = @ createAether @ spells [ spellKey ] . name , method , useProtectAPI
2014-02-14 10:49:16 -08:00
transpileSpell: (thang, spellKey, methodName) ->
slugifiedThangID = _ . string . slugify thang . id
2014-02-26 09:21:41 -08:00
source = @ currentUserCodeMap [ [ slugifiedThangID , methodName ] . join ' / ' ] ? " "
2014-03-04 08:30:50 -08:00
aether = @ spells [ spellKey ] . thangs [ thang . id ] . aether
try
aether . transpile source
catch e
console . log " Couldn ' t transpile #{ spellKey } : \n #{ source } \n " , e
aether . transpile ' '
2014-02-14 10:49:16 -08:00
2014-04-11 17:11:55 -07:00
createAether: (methodName, method, useProtectAPI) ->
2014-02-14 10:49:16 -08:00
aetherOptions =
functionName: methodName
2014-04-11 17:11:55 -07:00
protectAPI: useProtectAPI
2014-02-14 10:49:16 -08:00
includeFlow: false
2014-02-22 12:01:05 -08:00
requiresThis: true
yieldConditionally: false
problems:
jshint_W040: { level: " ignore " }
jshint_W030: { level: " ignore " } # aether_NoEffect instead
aether_MissingThis: { level: ' error ' }
#functionParameters: # TODOOOOO
if methodName is ' hear '
aetherOptions.functionParameters = [ ' speaker ' , ' message ' , ' data ' ]
2014-02-25 14:46:48 -08:00
#console.log "creating aether with options", aetherOptions
2014-02-14 10:49:16 -08:00
return new Aether aetherOptions
class SimulationTask
constructor: (@rawData) ->
2014-02-20 08:06:11 -08:00
console . log ' Simulating sessions ' , ( session for session in @ getSessions ( ) )
2014-04-11 17:11:55 -07:00
@spellKeyToTeamMap = { }
2014-02-14 10:49:16 -08:00
getLevelName: ->
levelName = @ rawData . sessions ? [ 0 ] ? . levelID
return levelName if levelName ?
@ throwMalformedTaskError " The level name couldn ' t be deduced from the task. "
generateTeamToSessionMap: ->
teamSessionMap = { }
for session in @ rawData . sessions
@ throwMalformedTaskError " Two players share the same team " if teamSessionMap [ session . team ] ?
teamSessionMap [ session . team ] = session . sessionID
teamSessionMap
throwMalformedTaskError: (errorString) ->
throw new Error " The task was malformed, reason: #{ errorString } "
getFirstSessionID: -> @ rawData . sessions [ 0 ] . sessionID
getTaskID: -> @ rawData . taskID
getReceiptHandle: -> @ rawData . receiptHandle
getSessions: -> @ rawData . sessions
2014-04-11 17:11:55 -07:00
getSpellKeyToTeamMap: -> @ spellKeyToTeamMap
getPlayerTeams: -> _ . pluck @ rawData . sessions , ' team '
2014-02-14 10:49:16 -08:00
generateSpellKeyToSourceMap: ->
2014-04-11 17:11:55 -07:00
playerTeams = _ . pluck @ rawData . sessions , ' team '
2014-02-14 10:49:16 -08:00
spellKeyToSourceMap = { }
for session in @ rawData . sessions
teamSpells = session . teamSpells [ session . team ]
2014-04-11 17:11:55 -07:00
allTeams = _ . keys session . teamSpells
nonPlayerTeams = _ . difference allTeams , playerTeams
for team in allTeams
for spell in session . teamSpells [ team ]
@ spellKeyToTeamMap [ spell ] = team
for nonPlayerTeam in nonPlayerTeams
teamSpells = teamSpells . concat ( session . teamSpells [ nonPlayerTeam ] )
2014-02-26 09:21:41 -08:00
teamCode = { }
2014-04-11 17:11:55 -07:00
2014-02-26 09:21:41 -08:00
for thangName , thangSpells of session . code
for spellName , spell of thangSpells
fullSpellName = [ thangName , spellName ] . join ' / '
if _ . contains ( teamSpells , fullSpellName )
teamCode [ fullSpellName ] = spell
2014-04-11 17:11:55 -07:00
2014-02-26 09:21:41 -08:00
_ . merge spellKeyToSourceMap , teamCode
2014-02-26 17:45:08 -08:00
2014-02-14 10:49:16 -08:00
spellKeyToSourceMap