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-05-10 18:24:50 -07:00
God = require ' lib/God '
2014-08-30 13:43:56 -07:00
{ createAetherOptions } = require ' lib/aether_utils '
2014-05-08 11:43:00 -07:00
2014-03-16 13:37:17 +05:30
module.exports = class Simulator extends CocoClass
2014-05-06 09:49:04 -07:00
constructor: (@options) ->
@ options ? = { }
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-05-06 05:07:34 +02:00
@simulatedByYou = 0
2014-05-10 18:24:50 -07:00
@god = new God maxAngels: 1 , workerCode: @ options . workerCode , headless: true # Start loading worker.
2014-03-11 19:17:58 -07:00
2014-03-07 12:15:16 -08:00
destroy: ->
@ off ( )
@ cleanupSimulation ( )
2014-05-10 18:24:50 -07:00
@ god ? . destroy ( )
2014-03-16 13:37:17 +05:30
super ( )
2014-05-20 08:00:44 -07:00
2014-05-19 10:11:20 -07:00
fetchAndSimulateOneGame: (humanGameID, ogresGameID) =>
return if @ destroyed
$ . ajax
2014-07-01 10:16:26 +08:00
url: ' /queue/scoring/getTwoGames '
type: ' POST '
2014-05-19 10:11:20 -07:00
parse: true
data:
2014-07-01 10:16:26 +08:00
' humansGameID ' : humanGameID
' ogresGameID ' : ogresGameID
2014-05-19 10:11:20 -07:00
error: (errorData) ->
2014-05-21 21:56:11 -07:00
console . warn " There was an error fetching two games! #{ JSON . stringify errorData } "
2014-05-19 10:11:20 -07:00
success: (taskData) =>
2014-05-20 08:00:44 -07:00
return if @ destroyed
2014-06-27 08:50:04 -07:00
unless taskData
@ trigger ' statusUpdate ' , " No games to simulate. Trying another game in #{ @ retryDelayInSeconds } seconds. "
@ simulateAnotherTaskAfterDelay ( )
return
2014-05-19 18:58:45 -07:00
@ trigger ' statusUpdate ' , ' Setting up simulation... '
2014-05-19 10:11:20 -07:00
#refactor this
@task = new SimulationTask ( taskData )
2014-05-20 08:00:44 -07:00
2014-05-19 10:11:20 -07:00
@ supermodel ? = new SuperModel ( )
@ supermodel . resetProgress ( )
2014-05-25 08:29:33 -07:00
@ stopListening @ supermodel , ' loaded-all '
2014-10-19 21:56:26 -07:00
@levelLoader = new LevelLoader supermodel: @ supermodel , levelID: @ task . getLevelName ( ) , sessionID: @ task . getFirstSessionID ( ) , opponentSessionID: @ task . getSecondSessionID ( ) , headless: true
2014-05-20 08:00:44 -07:00
2014-05-19 10:11:20 -07:00
if @ supermodel . finished ( )
@ simulateSingleGame ( )
else
@ listenToOnce @ supermodel , ' loaded-all ' , @ simulateSingleGame
2014-05-20 08:00:44 -07:00
2014-05-19 10:11:20 -07:00
simulateSingleGame: ->
return if @ destroyed
@ assignWorldAndLevelFromLevelLoaderAndDestroyIt ( )
2014-08-18 21:49:58 -07:00
@ trigger ' statusUpdate ' , ' Simulating... '
2014-05-19 10:11:20 -07:00
@ setupGod ( )
try
@ commenceSingleSimulation ( )
2014-05-21 21:56:11 -07:00
catch error
@ handleSingleSimulationError error
2014-05-20 08:00:44 -07:00
2014-05-19 10:11:20 -07:00
commenceSingleSimulation: ->
Backbone . Mediator . subscribeOnce ' god:infinite-loop ' , @ handleSingleSimulationInfiniteLoop , @
Backbone . Mediator . subscribeOnce ' god:goals-calculated ' , @ processSingleGameResults , @
2014-05-23 21:24:50 -07:00
@ god . createWorld @ generateSpellsObject ( )
2014-05-20 08:00:44 -07:00
2014-05-21 21:56:11 -07:00
handleSingleSimulationError: (error) ->
2014-07-01 10:16:26 +08:00
console . error ' There was an error simulating a single game! ' , error
2014-08-19 08:09:15 -07:00
return if @ destroyed
2014-06-06 12:26:28 -07:00
if @ options . headlessClient and @ options . simulateOnlyOneGame
2014-07-01 10:16:26 +08:00
console . log ' GAMERESULT:tie '
2014-05-19 10:11:20 -07:00
process . exit ( 0 )
2014-06-06 13:26:15 -07:00
@ cleanupAndSimulateAnotherTask ( )
2014-05-20 08:00:44 -07:00
2014-05-19 10:11:20 -07:00
handleSingleSimulationInfiniteLoop: ->
2014-07-01 10:16:26 +08:00
console . log ' There was an infinite loop in the single game! '
2014-08-19 08:09:15 -07:00
return if @ destroyed
2014-06-06 12:26:28 -07:00
if @ options . headlessClient and @ options . simulateOnlyOneGame
2014-07-01 10:16:26 +08:00
console . log ' GAMERESULT:tie '
2014-05-19 10:11:20 -07:00
process . exit ( 0 )
2014-06-06 13:26:15 -07:00
@ cleanupAndSimulateAnotherTask ( )
2014-05-20 08:00:44 -07:00
2014-05-19 10:11:20 -07:00
processSingleGameResults: (simulationResults) ->
2014-08-19 19:21:36 -07:00
return console . error " Weird, we destroyed the Simulator before it processed results? " if @ destroyed
2014-05-19 10:11:20 -07:00
taskResults = @ formTaskResultsObject simulationResults
2014-07-01 10:16:26 +08:00
console . log ' Processing results: ' , taskResults
2014-05-19 10:11:20 -07:00
humanSessionRank = taskResults . sessions [ 0 ] . metrics . rank
ogreSessionRank = taskResults . sessions [ 1 ] . metrics . rank
2014-06-06 12:18:00 -07:00
if @ options . headlessClient and @ options . simulateOnlyOneGame
2014-05-19 10:11:20 -07:00
if humanSessionRank is ogreSessionRank
2014-07-01 10:16:26 +08:00
console . log ' GAMERESULT:tie '
2014-05-19 10:11:20 -07:00
else if humanSessionRank < ogreSessionRank
2014-07-01 10:16:26 +08:00
console . log ' GAMERESULT:humans '
2014-05-19 10:11:20 -07:00
else if ogreSessionRank < humanSessionRank
2014-07-01 10:16:26 +08:00
console . log ' GAMERESULT:ogres '
2014-05-19 10:11:20 -07:00
process . exit ( 0 )
2014-05-19 18:58:45 -07:00
else
@ sendSingleGameBackToServer ( taskResults )
2014-05-20 08:00:44 -07:00
2014-05-19 18:58:45 -07:00
sendSingleGameBackToServer: (results) ->
@ trigger ' statusUpdate ' , ' Simulation completed, sending results back to server! '
2014-05-20 08:00:44 -07:00
2014-05-19 18:58:45 -07:00
$ . ajax
2014-07-01 10:16:26 +08:00
url: ' /queue/scoring/recordTwoGames '
2014-05-19 18:58:45 -07:00
data: results
2014-07-01 10:16:26 +08:00
type: ' PUT '
2014-05-19 18:58:45 -07:00
parse: true
success: @ handleTaskResultsTransferSuccess
error: @ handleTaskResultsTransferError
complete: @ cleanupAndSimulateAnotherTask
2014-05-20 08:00:44 -07:00
2014-02-14 10:49:16 -08:00
fetchAndSimulateTask: =>
2014-03-16 20:36:02 -07:00
return if @ destroyed
2014-05-06 05:07:34 +02:00
2014-05-06 09:49:04 -07:00
if @ options . headlessClient
2014-05-06 05:07:34 +02:00
if @ dumpThisTime # The first heapdump would be useless to find leaks.
2014-07-01 10:16:26 +08:00
console . log ' Writing snapshot. '
2014-05-06 09:49:04 -07:00
@ options . heapdump . writeSnapshot ( )
@dumpThisTime = true if @ options . heapdump
2014-05-06 05:07:34 +02:00
2014-05-06 09:49:04 -07:00
if @ options . testing
2014-07-01 10:16:26 +08:00
_ . delay @ setupSimulationAndLoadLevel , 0 , @ options . testFile , ' Testing... ' , status: 400
2014-05-06 05:07:34 +02:00
return
2014-02-20 08:06:11 -08:00
@ trigger ' statusUpdate ' , ' Fetching simulation data! '
2014-02-14 10:49:16 -08:00
$ . ajax
url: @ taskURL
2014-07-01 10:16:26 +08:00
type: ' GET '
2014-05-10 18:24:50 -07:00
parse: true
2014-02-14 10:49:16 -08:00
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-05-20 08:00:44 -07:00
2014-03-11 19:17:58 -07:00
handleNoGamesResponse: ->
2014-05-19 18:58:45 -07:00
info = ' Finding game to simulate... '
2014-05-06 05:07:34 +02:00
console . log info
@ trigger ' statusUpdate ' , info
2014-05-19 18:58:45 -07:00
@ fetchAndSimulateOneGame ( )
2014-07-01 10:16:26 +08:00
application . tracker ? . trackEvent ' Simulator Result ' , label: ' No Games ' , [ ' Google Analytics ' ]
2014-02-14 14:55:30 -08:00
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-05-14 11:13:36 -07:00
@ supermodel . resetProgress ( )
2014-05-25 08:29:33 -07:00
@ stopListening @ supermodel , ' loaded-all '
2014-10-19 21:56:26 -07:00
@levelLoader = new LevelLoader supermodel: @ supermodel , levelID: levelID , sessionID: @ task . getFirstSessionID ( ) , opponentSessionID: @ task . getSecondSessionID ( ) , headless: true
2014-04-29 16:54:57 -07:00
if @ supermodel . finished ( )
@ simulateGame ( )
else
@ listenToOnce @ supermodel , ' 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-05-06 05:07:34 +02:00
info = ' All resources loaded, simulating! '
console . log info
2014-02-14 10:49:16 -08:00
@ assignWorldAndLevelFromLevelLoaderAndDestroyIt ( )
2014-08-18 21:49:58 -07:00
@ trigger ' statusUpdate ' , info , @ task . getSessions ( )
2014-02-14 10:49:16 -08:00
@ 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-08-24 21:39:34 -07:00
console . error ' There was an error in simulation: ' , err , err . stack , " -- 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
2014-05-15 16:43:16 -07:00
@ task . setWorld ( @ world )
2014-02-14 10:49:16 -08:00
@level = @ levelLoader . level
2014-10-19 21:56:26 -07:00
@session = @ levelLoader . session
@otherSession = @ levelLoader . opponentSession
2014-02-14 10:49:16 -08:00
@ levelLoader . destroy ( )
2014-02-15 15:44:45 -08:00
@levelLoader = null
2014-02-14 10:49:16 -08:00
setupGod: ->
2014-10-19 21:56:26 -07:00
@ god . setLevel @ level . serialize ( @ supermodel , @ session , @ otherSession )
2014-05-20 08:00:44 -07:00
@ god . setLevelSessionIDs ( session . sessionID for session in @ task . getSessions ( ) )
2014-05-06 05:07:34 +02:00
@ god . setWorldClassMap @ world . classMap
@ god . setGoalManager new GoalManager ( @ world , @ level . get ' goals ' )
2014-02-14 10:49:16 -08:00
commenceSimulationAndSetupCallback: ->
2014-03-26 11:25:05 -07:00
Backbone . Mediator . subscribeOnce ' god:infinite-loop ' , @ onInfiniteLoop , @
2014-05-10 18:24:50 -07:00
Backbone . Mediator . subscribeOnce ' god:goals-calculated ' , @ processResults , @
2014-05-23 21:24:50 -07:00
@ god . createWorld @ generateSpellsObject ( )
2014-02-14 10:49:16 -08:00
2014-07-01 10:16:26 +08:00
# Search for leaks, headless-client only.
2014-05-06 09:49:04 -07:00
if @ options . headlessClient and @ options . leakTest and not @ memwatch ?
2014-05-06 05:07:34 +02:00
leakcount = 0
maxleakcount = 0
2014-07-01 10:16:26 +08:00
console . log ' Setting leak callbacks. '
2014-05-06 05:07:34 +02:00
@memwatch = require ' memwatch '
@ memwatch . on ' leak ' , (info) =>
console . warn " LEAK!! \n " + JSON . stringify ( info )
unless @ hd ?
if ( leakcount ++ is maxleakcount )
@hd = new @ memwatch . HeapDiff ( )
@ memwatch . on ' stats ' , (stats) =>
2014-07-01 10:16:26 +08:00
console . warn ' stats callback: ' + stats
2014-05-06 05:07:34 +02:00
diff = @ hd . end ( )
console . warn " HeapDiff: \n " + JSON . stringify ( diff )
2014-05-06 09:49:04 -07:00
if @ options . exitOnLeak
2014-07-01 10:16:26 +08:00
console . warn ' Exiting because of Leak. '
2014-05-06 05:07:34 +02:00
process . exit ( )
@hd = new @ memwatch . HeapDiff ( )
2014-03-26 11:25:05 -07:00
onInfiniteLoop: ->
2014-08-18 21:49:58 -07:00
return if @ destroyed
2014-07-01 10:16:26 +08:00
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) ->
2014-08-20 13:26:42 -07:00
return console . error " Weird, we destroyed the Simulator before it processed results? " if @ destroyed
2014-02-14 10:49:16 -08:00
taskResults = @ formTaskResultsObject simulationResults
2014-06-06 12:47:10 -07:00
unless taskResults . taskID
console . error " *** Error: taskResults has no taskID *** \n taskResults: " , taskResults
@ cleanupAndSimulateAnotherTask ( )
else
@ sendResultsBackToServer taskResults
2014-02-14 10:49:16 -08:00
2014-05-10 18:24:50 -07:00
sendResultsBackToServer: (results) ->
2014-08-18 21:49:58 -07:00
status = ' Recording: '
for session in results . sessions
states = [ ' wins ' , if _ . find ( results . sessions , (s) -> s . metrics . rank is 0 ) then ' loses ' else ' draws ' ]
status += " #{ session . name } #{ states [ session . metrics . rank ] } "
@ trigger ' statusUpdate ' , status
2014-07-01 10:16:26 +08:00
console . log ' Sending result back to server: '
2014-06-06 12:18:00 -07:00
console . log JSON . stringify results
2014-04-25 16:57:42 -07:00
2014-05-06 09:49:04 -07:00
if @ options . headlessClient and @ options . testing
2014-05-06 05:07:34 +02:00
return @ fetchAndSimulateTask ( )
2014-02-14 10:49:16 -08:00
$ . ajax
2014-07-01 10:16:26 +08:00
url: ' /queue/scoring '
2014-02-14 10:49:16 -08:00
data: results
2014-07-01 10:16:26 +08:00
type: ' PUT '
2014-05-10 18:24:50 -07:00
parse: true
2014-02-14 10:49:16 -08:00
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-05-20 08:00:44 -07:00
return if @ destroyed
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-07-01 10:16:26 +08:00
console . log ' Simulated by you: ' , @ simulatedByYou
2014-05-06 05:07:34 +02:00
@ simulatedByYou ++
2014-05-06 09:49:04 -07:00
unless @ options . headlessClient
2014-05-06 05:07:34 +02:00
simulatedBy = parseInt ( $ ( ' # simulated-by-you ' ) . text ( ) , 10 ) + 1
$ ( ' # simulated-by-you ' ) . text ( simulatedBy )
2014-07-01 10:16:26 +08:00
application . tracker ? . trackEvent ' Simulator Result ' , label: ' Success ' , [ ' Google Analytics ' ]
2014-02-14 10:49:16 -08:00
2014-02-20 08:06:11 -08:00
handleTaskResultsTransferError: (error) =>
2014-05-20 08:00:44 -07:00
return if @ destroyed
2014-02-20 08:06:11 -08:00
@ 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: =>
2014-05-20 08:00:44 -07:00
return if @ destroyed
2014-05-06 09:49:04 -07:00
@ cleanupSimulation ( )
2014-02-14 10:49:16 -08:00
@ fetchAndSimulateTask ( )
2014-05-06 09:49:04 -07:00
cleanupSimulation: ->
@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-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-05-16 14:52:49 -07:00
name: session . creatorName
totalScore: session . totalScore
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-07-01 10:16:26 +08:00
else if ogresWon and teamSessionMap [ ' ogres ' ] is sessionID
2014-02-14 10:49:16 -08:00
return 0
2014-07-01 10:16:26 +08:00
else if ogresWon and teamSessionMap [ ' ogres ' ] isnt sessionID
2014-02-14 10:49:16 -08:00
return 1
2014-07-01 10:16:26 +08:00
else if humansWon and teamSessionMap [ ' humans ' ] is sessionID
2014-02-14 10:49:16 -08:00
return 0
else
return 1
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
2014-05-10 18:24:50 -07:00
@ spells
2014-02-14 10:49:16 -08:00
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
2014-06-20 17:19:18 -07:00
if spellTeam not in playerTeams
useProtectAPI = false
else
spellSession = _ . filter ( @ task . getSessions ( ) , { team: spellTeam } ) [ 0 ]
unless codeLanguage = spellSession ? . submittedCodeLanguage
2014-07-01 10:16:26 +08:00
console . warn ' Session ' , spellSession . creatorName , spellSession . team , ' didn \' t have submittedCodeLanguage, just: ' , spellSession
2014-06-20 17:19:18 -07:00
@ spells [ spellKey ] . thangs [ thang . id ] . aether = @ createAether @ spells [ spellKey ] . name , method , useProtectAPI , codeLanguage ? ' javascript '
2014-02-14 10:49:16 -08:00
2014-05-15 16:44:16 -07:00
transpileSpell: (thang, spellKey, methodName) ->
2014-02-14 10:49:16 -08:00
slugifiedThangID = _ . string . slugify thang . id
2014-05-15 16:43:16 -07:00
generatedSpellKey = [ slugifiedThangID , methodName ] . join ' / '
2014-07-01 10:16:26 +08:00
source = @ currentUserCodeMap [ generatedSpellKey ] ? ' '
2014-03-04 08:30:50 -08:00
aether = @ spells [ spellKey ] . thangs [ thang . id ] . aether
2014-05-15 16:43:16 -07:00
unless _ . contains ( @ task . spellKeysToTranspile , generatedSpellKey )
aether.pure = source
else
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-06-20 17:19:18 -07:00
createAether: (methodName, method, useProtectAPI, codeLanguage) ->
2014-08-30 13:43:56 -07:00
aetherOptions = createAetherOptions functionName: methodName , codeLanguage: codeLanguage , skipProtectAPI: not useProtectAPI
2014-02-14 10:49:16 -08:00
return new Aether aetherOptions
class SimulationTask
constructor: (@rawData) ->
2014-04-11 17:11:55 -07:00
@spellKeyToTeamMap = { }
2014-05-15 16:43:16 -07:00
@spellKeysToTranspile = [ ]
2014-02-14 10:49:16 -08:00
getLevelName: ->
2014-04-26 14:21:26 -07:00
levelName = @ rawData . sessions ? [ 0 ] ? . levelID
2014-02-14 10:49:16 -08:00
return levelName if levelName ?
2014-07-01 10:16:26 +08:00
@ throwMalformedTaskError ' The level name couldn \' t be deduced from the task. '
2014-02-14 10:49:16 -08:00
generateTeamToSessionMap: ->
teamSessionMap = { }
for session in @ rawData . sessions
2014-07-01 10:16:26 +08:00
@ throwMalformedTaskError ' Two players share the same team ' if teamSessionMap [ session . team ] ?
2014-02-14 10:49:16 -08:00
teamSessionMap [ session . team ] = session . sessionID
teamSessionMap
throwMalformedTaskError: (errorString) ->
throw new Error " The task was malformed, reason: #{ errorString } "
getFirstSessionID: -> @ rawData . sessions [ 0 ] . sessionID
2014-10-19 21:56:26 -07:00
getSecondSessionID: -> @ rawData . sessions [ 1 ] . sessionID
2014-02-14 10:49:16 -08:00
getTaskID: -> @ rawData . taskID
getReceiptHandle: -> @ rawData . receiptHandle
getSessions: -> @ rawData . sessions
2014-04-25 16:57:42 -07:00
2014-04-11 17:11:55 -07:00
getSpellKeyToTeamMap: -> @ spellKeyToTeamMap
2014-04-25 16:57:42 -07:00
2014-04-11 17:11:55 -07:00
getPlayerTeams: -> _ . pluck @ rawData . sessions , ' team '
2014-02-14 10:49:16 -08:00
2014-05-15 16:44:16 -07:00
setWorld: (@world) ->
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
2014-05-15 16:43:16 -07:00
for spell in session . teamSpells [ nonPlayerTeam ]
spellKeyToSourceMap [ spell ] ? = @ getWorldProgrammableSource ( spell , @ world )
@ spellKeysToTranspile . push spell
2014-02-26 09:21:41 -08:00
teamCode = { }
2014-04-11 17:11:55 -07:00
2014-05-15 16:43:16 -07:00
for thangName , thangSpells of session . transpiledCode
2014-02-26 09:21:41 -08:00
for spellName , spell of thangSpells
2014-07-01 10:16:26 +08:00
fullSpellName = [ thangName , spellName ] . join ' / '
2014-02-26 09:21:41 -08:00
if _ . contains ( teamSpells , fullSpellName )
teamCode [ fullSpellName ] = spell
2014-04-25 16:57:42 -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
2014-05-15 16:43:16 -07:00
getWorldProgrammableSource: (desiredSpellKey ,world) ->
programmableThangs = _ . filter world . thangs , ' isProgrammable '
@ spells ? = { }
@ thangSpells ? = { }
for thang in programmableThangs
continue if @ thangSpells [ thang . id ] ?
@ thangSpells [ thang . id ] = [ ]
for methodName , method of thang . programmableMethods
pathComponents = [ thang . id , methodName ]
if method . cloneOf
pathComponents [ 0 ] = method . cloneOf # referencing another Thang's method
pathComponents [ 0 ] = _ . string . slugify pathComponents [ 0 ]
spellKey = pathComponents . join ' / '
@ thangSpells [ thang . id ] . push spellKey
if not method . cloneOf and spellKey is desiredSpellKey
2014-05-21 21:56:11 -07:00
#console.log "Setting #{desiredSpellKey} from world!"
2014-05-15 16:43:16 -07:00
return method . source