2014-02-14 13:49:16 -05:00
SuperModel = require ' models/SuperModel '
2014-11-28 20:49:41 -05:00
CocoClass = require ' core/CocoClass '
2014-02-14 13:49:16 -05:00
LevelLoader = require ' lib/LevelLoader '
GoalManager = require ' lib/world/GoalManager '
2014-05-10 21:24:50 -04:00
God = require ' lib/God '
2014-08-30 16:43:56 -04:00
{ createAetherOptions } = require ' lib/aether_utils '
2014-05-08 14:43:00 -04:00
2016-05-05 16:22:30 -04:00
SIMULATOR_VERSION = 4
2015-02-11 23:24:12 -05:00
simulatorInfo = { }
if $ . browser
simulatorInfo [ ' desktop ' ] = $ . browser . desktop if $ . browser . desktop
simulatorInfo [ ' name ' ] = $ . browser . name if $ . browser . name
simulatorInfo [ ' platform ' ] = $ . browser . platform if $ . browser . platform
simulatorInfo [ ' version ' ] = $ . browser . versionNumber if $ . browser . versionNumber
2014-03-16 04:07:17 -04:00
module.exports = class Simulator extends CocoClass
2014-05-06 12:49:04 -04:00
constructor: (@options) ->
@ options ? = { }
2015-02-11 23:24:12 -05:00
simulatorType = if @ options . headlessClient then ' headless ' else ' browser '
@simulator =
type: simulatorType
version: SIMULATOR_VERSION
info: simulatorInfo
2014-02-20 11:06:11 -05:00
_ . extend @ , Backbone . Events
@ trigger ' statusUpdate ' , ' Starting simulation! '
2014-12-09 12:00:34 -05:00
@retryDelayInSeconds = 2
2014-02-14 17:55:30 -05:00
@taskURL = ' /queue/scoring '
2014-05-05 23:07:34 -04:00
@simulatedByYou = 0
2014-05-10 21:24:50 -04:00
@god = new God maxAngels: 1 , workerCode: @ options . workerCode , headless: true # Start loading worker.
2014-03-11 22:17:58 -04:00
2014-03-07 15:15:16 -05:00
destroy: ->
@ off ( )
@ cleanupSimulation ( )
2014-05-10 21:24:50 -04:00
@ god ? . destroy ( )
2014-03-16 04:07:17 -04:00
super ( )
2014-05-20 11:00:44 -04:00
2014-05-19 13:11:20 -04:00
fetchAndSimulateOneGame: (humanGameID, ogresGameID) =>
return if @ destroyed
$ . ajax
2014-06-30 22:16:26 -04:00
url: ' /queue/scoring/getTwoGames '
type: ' POST '
2014-05-19 13:11:20 -04:00
parse: true
data:
2015-02-11 23:24:12 -05:00
humansGameID: humanGameID
ogresGameID: ogresGameID
simulator: @ simulator
2015-11-29 15:30:19 -05:00
background: Boolean ( @ options . background )
2015-12-06 12:20:30 -05:00
levelID: @ options . levelID
leagueID: @ options . leagueID
2014-05-19 13:11:20 -04:00
error: (errorData) ->
2014-05-22 00:56:11 -04:00
console . warn " There was an error fetching two games! #{ JSON . stringify errorData } "
2015-02-11 23:24:12 -05:00
if errorData ? . responseText ? . indexOf ( " Old simulator " ) isnt - 1
noty {
text: errorData . responseText
layout: ' center '
type: ' error '
}
2014-05-19 13:11:20 -04:00
success: (taskData) =>
2014-05-20 11:00:44 -04:00
return if @ destroyed
2014-06-27 11:50:04 -04:00
unless taskData
2015-12-15 19:37:53 -05:00
@retryDelayInSeconds = 10
2014-06-27 11:50:04 -04:00
@ trigger ' statusUpdate ' , " No games to simulate. Trying another game in #{ @ retryDelayInSeconds } seconds. "
@ simulateAnotherTaskAfterDelay ( )
return
2014-05-19 21:58:45 -04:00
@ trigger ' statusUpdate ' , ' Setting up simulation... '
2014-05-19 13:11:20 -04:00
#refactor this
@task = new SimulationTask ( taskData )
2014-05-20 11:00:44 -04:00
2014-05-19 13:11:20 -04:00
@ supermodel ? = new SuperModel ( )
@ supermodel . resetProgress ( )
2014-05-25 11:29:33 -04:00
@ stopListening @ supermodel , ' loaded-all '
2014-10-20 00:56:26 -04:00
@levelLoader = new LevelLoader supermodel: @ supermodel , levelID: @ task . getLevelName ( ) , sessionID: @ task . getFirstSessionID ( ) , opponentSessionID: @ task . getSecondSessionID ( ) , headless: true
2014-05-20 11:00:44 -04:00
2014-05-19 13:11:20 -04:00
if @ supermodel . finished ( )
@ simulateSingleGame ( )
else
@ listenToOnce @ supermodel , ' loaded-all ' , @ simulateSingleGame
2014-05-20 11:00:44 -04:00
2014-05-19 13:11:20 -04:00
simulateSingleGame: ->
return if @ destroyed
@ assignWorldAndLevelFromLevelLoaderAndDestroyIt ( )
2014-08-19 00:49:58 -04:00
@ trigger ' statusUpdate ' , ' Simulating... '
2014-05-19 13:11:20 -04:00
@ setupGod ( )
try
@ commenceSingleSimulation ( )
2014-05-22 00:56:11 -04:00
catch error
@ handleSingleSimulationError error
2014-05-20 11:00:44 -04:00
2014-05-19 13:11:20 -04:00
commenceSingleSimulation: ->
2015-11-29 15:30:19 -05:00
@ listenToOnce @ god , ' infinite-loop ' , @ handleSingleSimulationInfiniteLoop
@ listenToOnce @ god , ' goals-calculated ' , @ processSingleGameResults
2014-05-24 00:24:50 -04:00
@ god . createWorld @ generateSpellsObject ( )
2014-05-20 11:00:44 -04:00
2014-05-22 00:56:11 -04:00
handleSingleSimulationError: (error) ->
2014-06-30 22:16:26 -04:00
console . error ' There was an error simulating a single game! ' , error
2014-08-19 11:09:15 -04:00
return if @ destroyed
2014-06-06 15:26:28 -04:00
if @ options . headlessClient and @ options . simulateOnlyOneGame
2014-06-30 22:16:26 -04:00
console . log ' GAMERESULT:tie '
2014-05-19 13:11:20 -04:00
process . exit ( 0 )
2014-06-06 16:26:15 -04:00
@ cleanupAndSimulateAnotherTask ( )
2014-05-20 11:00:44 -04:00
2015-11-29 15:30:19 -05:00
handleSingleSimulationInfiniteLoop: (e) ->
2014-06-30 22:16:26 -04:00
console . log ' There was an infinite loop in the single game! '
2014-08-19 11:09:15 -04:00
return if @ destroyed
2014-06-06 15:26:28 -04:00
if @ options . headlessClient and @ options . simulateOnlyOneGame
2014-06-30 22:16:26 -04:00
console . log ' GAMERESULT:tie '
2014-05-19 13:11:20 -04:00
process . exit ( 0 )
2014-06-06 16:26:15 -04:00
@ cleanupAndSimulateAnotherTask ( )
2014-05-20 11:00:44 -04:00
2014-05-19 13:11:20 -04:00
processSingleGameResults: (simulationResults) ->
2014-12-09 12:00:34 -05:00
try
taskResults = @ formTaskResultsObject simulationResults
catch error
console . log " Failed to form task results: " , error
return @ cleanupAndSimulateAnotherTask ( )
2014-06-30 22:16:26 -04:00
console . log ' Processing results: ' , taskResults
2014-05-19 13:11:20 -04:00
humanSessionRank = taskResults . sessions [ 0 ] . metrics . rank
ogreSessionRank = taskResults . sessions [ 1 ] . metrics . rank
2014-06-06 15:18:00 -04:00
if @ options . headlessClient and @ options . simulateOnlyOneGame
2014-05-19 13:11:20 -04:00
if humanSessionRank is ogreSessionRank
2014-06-30 22:16:26 -04:00
console . log ' GAMERESULT:tie '
2014-05-19 13:11:20 -04:00
else if humanSessionRank < ogreSessionRank
2014-06-30 22:16:26 -04:00
console . log ' GAMERESULT:humans '
2014-05-19 13:11:20 -04:00
else if ogreSessionRank < humanSessionRank
2014-06-30 22:16:26 -04:00
console . log ' GAMERESULT:ogres '
2014-05-19 13:11:20 -04:00
process . exit ( 0 )
2014-05-19 21:58:45 -04:00
else
@ sendSingleGameBackToServer ( taskResults )
2014-05-20 11:00:44 -04:00
2014-05-19 21:58:45 -04:00
sendSingleGameBackToServer: (results) ->
@ trigger ' statusUpdate ' , ' Simulation completed, sending results back to server! '
2014-05-20 11:00:44 -04:00
2014-05-19 21:58:45 -04:00
$ . ajax
2014-06-30 22:16:26 -04:00
url: ' /queue/scoring/recordTwoGames '
2014-05-19 21:58:45 -04:00
data: results
2014-06-30 22:16:26 -04:00
type: ' PUT '
2014-05-19 21:58:45 -04:00
parse: true
success: @ handleTaskResultsTransferSuccess
error: @ handleTaskResultsTransferError
complete: @ cleanupAndSimulateAnotherTask
2014-05-20 11:00:44 -04:00
2014-02-14 13:49:16 -05:00
fetchAndSimulateTask: =>
2014-03-16 23:36:02 -04:00
return if @ destroyed
2015-12-06 07:36:41 -05:00
# Because there's some bug where the chained rankings don't work, let's just do getTwoGames until we fix it.
return @ fetchAndSimulateOneGame ( )
2014-05-05 23:07:34 -04:00
2014-05-06 12:49:04 -04:00
if @ options . headlessClient
2014-05-05 23:07:34 -04:00
if @ dumpThisTime # The first heapdump would be useless to find leaks.
2014-06-30 22:16:26 -04:00
console . log ' Writing snapshot. '
2014-05-06 12:49:04 -04:00
@ options . heapdump . writeSnapshot ( )
@dumpThisTime = true if @ options . heapdump
2014-05-05 23:07:34 -04:00
2014-05-06 12:49:04 -04:00
if @ options . testing
2014-06-30 22:16:26 -04:00
_ . delay @ setupSimulationAndLoadLevel , 0 , @ options . testFile , ' Testing... ' , status: 400
2014-05-05 23:07:34 -04:00
return
2014-02-20 11:06:11 -05:00
@ trigger ' statusUpdate ' , ' Fetching simulation data! '
2014-02-14 13:49:16 -05:00
$ . ajax
url: @ taskURL
2014-06-30 22:16:26 -04:00
type: ' GET '
2014-05-10 21:24:50 -04:00
parse: true
2014-02-14 13:49:16 -05:00
error: @ handleFetchTaskError
success: @ setupSimulationAndLoadLevel
2015-02-11 16:12:42 -05:00
cache: false
2014-02-14 13:49:16 -05:00
handleFetchTaskError: (errorData) =>
2014-03-11 22:17:58 -04: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 11:00:44 -04:00
2014-03-11 22:17:58 -04:00
handleNoGamesResponse: ->
2015-11-29 15:30:19 -05:00
@noTasks = true
2014-05-19 21:58:45 -04:00
info = ' Finding game to simulate... '
2014-05-05 23:07:34 -04:00
console . log info
@ trigger ' statusUpdate ' , info
2014-05-19 21:58:45 -04:00
@ fetchAndSimulateOneGame ( )
2014-02-14 17:55:30 -05:00
simulateAnotherTaskAfterDelay: =>
2014-03-11 22:17:58 -04:00
console . log " Retrying in #{ @ retryDelayInSeconds } "
2014-02-14 17:55:30 -05:00
retryDelayInMilliseconds = @ retryDelayInSeconds * 1000
_ . delay @ fetchAndSimulateTask , retryDelayInMilliseconds
2014-02-14 13:49:16 -05:00
2014-03-11 22:17:58 -04:00
setupSimulationAndLoadLevel: (taskData, textStatus, jqXHR) =>
return @ handleNoGamesResponse ( ) if jqXHR . status is 204
2014-02-20 11:06:11 -05:00
@ trigger ' statusUpdate ' , ' Setting up simulation! '
2014-02-14 13:49:16 -05:00
@task = new SimulationTask ( taskData )
2014-03-26 15:12:43 -04: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 14:26:15 -04:00
@ supermodel ? = new SuperModel ( )
2014-05-14 14:13:36 -04:00
@ supermodel . resetProgress ( )
2014-05-25 11:29:33 -04:00
@ stopListening @ supermodel , ' loaded-all '
2014-10-20 00:56:26 -04:00
@levelLoader = new LevelLoader supermodel: @ supermodel , levelID: levelID , sessionID: @ task . getFirstSessionID ( ) , opponentSessionID: @ task . getSecondSessionID ( ) , headless: true
2014-04-29 19:54:57 -04:00
if @ supermodel . finished ( )
@ simulateGame ( )
else
@ listenToOnce @ supermodel , ' loaded-all ' , @ simulateGame
2014-02-14 13:49:16 -05:00
2014-03-24 12:58:34 -04:00
simulateGame: ->
2014-03-16 04:07:17 -04:00
return if @ destroyed
2014-05-05 23:07:34 -04:00
info = ' All resources loaded, simulating! '
console . log info
2014-02-14 13:49:16 -05:00
@ assignWorldAndLevelFromLevelLoaderAndDestroyIt ( )
2014-08-19 00:49:58 -04:00
@ trigger ' statusUpdate ' , info , @ task . getSessions ( )
2014-02-14 13:49:16 -05:00
@ setupGod ( )
2014-02-14 17:55:30 -05:00
2014-02-14 13:49:16 -05:00
try
@ commenceSimulationAndSetupCallback ( )
2014-02-14 17:55:30 -05:00
catch err
2014-08-25 00:39:34 -04:00
console . error ' There was an error in simulation: ' , err , err . stack , " -- trying again in #{ @ retryDelayInSeconds } seconds "
2014-02-14 17:55:30 -05:00
@ simulateAnotherTaskAfterDelay ( )
2014-02-14 13:49:16 -05:00
assignWorldAndLevelFromLevelLoaderAndDestroyIt: ->
@world = @ levelLoader . world
2014-05-15 19:43:16 -04:00
@ task . setWorld ( @ world )
2014-02-14 13:49:16 -05:00
@level = @ levelLoader . level
2014-10-20 00:56:26 -04:00
@session = @ levelLoader . session
@otherSession = @ levelLoader . opponentSession
2014-02-14 13:49:16 -05:00
@ levelLoader . destroy ( )
2014-02-15 18:44:45 -05:00
@levelLoader = null
2014-02-14 13:49:16 -05:00
setupGod: ->
2014-10-20 00:56:26 -04:00
@ god . setLevel @ level . serialize ( @ supermodel , @ session , @ otherSession )
2014-05-20 11:00:44 -04:00
@ god . setLevelSessionIDs ( session . sessionID for session in @ task . getSessions ( ) )
2014-05-05 23:07:34 -04:00
@ god . setWorldClassMap @ world . classMap
2015-11-29 15:30:19 -05:00
@ god . setGoalManager new GoalManager @ world , @ level . get ( ' goals ' ) , null , { headless: true }
2015-02-12 13:59:26 -05:00
humanFlagHistory = _ . filter @ session . get ( ' state ' ) ? . flagHistory ? [ ] , (event) => event . source isnt ' code ' and event . team is ( @ session . get ( ' team ' ) ? ' humans ' )
ogreFlagHistory = _ . filter @ otherSession . get ( ' state ' ) ? . flagHistory ? [ ] , (event) => event . source isnt ' code ' and event . team is ( @ otherSession . get ( ' team ' ) ? ' ogres ' )
@god.lastFlagHistory = humanFlagHistory . concat ogreFlagHistory
#console.log 'got flag history', @god.lastFlagHistory, 'from', humanFlagHistory, ogreFlagHistory, @session.get('state'), @otherSession.get('state')
@god.lastSubmissionCount = 0 # TODO: figure out how to combine submissionCounts from both players so we can use submissionCount random seeds again.
@god.lastDifficulty = 0
2014-05-05 23:07:34 -04:00
2014-02-14 13:49:16 -05:00
commenceSimulationAndSetupCallback: ->
2015-11-29 15:30:19 -05:00
@ listenToOnce @ god , ' infinite-loop ' , @ onInfiniteLoop
@ listenToOnce @ god , ' goals-calculated ' , @ processResults
2014-05-24 00:24:50 -04:00
@ god . createWorld @ generateSpellsObject ( )
2014-02-14 13:49:16 -05:00
2014-06-30 22:16:26 -04:00
# Search for leaks, headless-client only.
2014-05-06 12:49:04 -04:00
if @ options . headlessClient and @ options . leakTest and not @ memwatch ?
2014-05-05 23:07:34 -04:00
leakcount = 0
maxleakcount = 0
2014-06-30 22:16:26 -04:00
console . log ' Setting leak callbacks. '
2014-05-05 23:07:34 -04: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-06-30 22:16:26 -04:00
console . warn ' stats callback: ' + stats
2014-05-05 23:07:34 -04:00
diff = @ hd . end ( )
console . warn " HeapDiff: \n " + JSON . stringify ( diff )
2014-05-06 12:49:04 -04:00
if @ options . exitOnLeak
2014-06-30 22:16:26 -04:00
console . warn ' Exiting because of Leak. '
2014-05-05 23:07:34 -04:00
process . exit ( )
@hd = new @ memwatch . HeapDiff ( )
2015-11-29 15:30:19 -05:00
onInfiniteLoop: (e) ->
2014-08-19 00:49:58 -04:00
return if @ destroyed
2014-06-30 22:16:26 -04:00
console . warn ' Skipping infinitely looping game. '
2014-03-26 15:12:43 -04:00
@ trigger ' statusUpdate ' , " Infinite loop detected; grabbing a new game in #{ @ retryDelayInSeconds } seconds. "
2014-03-26 15:34:45 -04:00
_ . delay @ cleanupAndSimulateAnotherTask , @ retryDelayInSeconds * 1000
2014-03-26 14:25:05 -04:00
2014-02-14 13:49:16 -05:00
processResults: (simulationResults) ->
2014-12-09 12:00:34 -05:00
try
taskResults = @ formTaskResultsObject simulationResults
catch error
console . log " Failed to form task results: " , error
return @ cleanupAndSimulateAnotherTask ( )
2014-06-06 15:47:10 -04:00
unless taskResults . taskID
console . error " *** Error: taskResults has no taskID *** \n taskResults: " , taskResults
@ cleanupAndSimulateAnotherTask ( )
else
@ sendResultsBackToServer taskResults
2014-02-14 13:49:16 -05:00
2014-05-10 21:24:50 -04:00
sendResultsBackToServer: (results) ->
2014-08-19 00:49:58 -04: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-06-30 22:16:26 -04:00
console . log ' Sending result back to server: '
2014-06-06 15:18:00 -04:00
console . log JSON . stringify results
2014-04-25 19:57:42 -04:00
2014-05-06 12:49:04 -04:00
if @ options . headlessClient and @ options . testing
2014-05-05 23:07:34 -04:00
return @ fetchAndSimulateTask ( )
2014-02-14 13:49:16 -05:00
$ . ajax
2014-06-30 22:16:26 -04:00
url: ' /queue/scoring '
2014-02-14 13:49:16 -05:00
data: results
2014-06-30 22:16:26 -04:00
type: ' PUT '
2014-05-10 21:24:50 -04:00
parse: true
2014-02-14 13:49:16 -05:00
success: @ handleTaskResultsTransferSuccess
error: @ handleTaskResultsTransferError
2014-02-14 19:53:34 -05:00
complete: @ cleanupAndSimulateAnotherTask
2014-02-14 13:49:16 -05:00
2014-02-20 11:06:11 -05:00
handleTaskResultsTransferSuccess: (result) =>
2014-05-20 11:00:44 -04:00
return if @ destroyed
2014-02-14 13:49:16 -05:00
console . log " Task registration result: #{ JSON . stringify result } "
2014-02-20 11:06:11 -05:00
@ trigger ' statusUpdate ' , ' Results were successfully sent back to server! '
2014-05-05 23:07:34 -04:00
@ simulatedByYou ++
2014-05-06 12:49:04 -04:00
unless @ options . headlessClient
2014-05-05 23:07:34 -04:00
simulatedBy = parseInt ( $ ( ' # simulated-by-you ' ) . text ( ) , 10 ) + 1
$ ( ' # simulated-by-you ' ) . text ( simulatedBy )
2014-02-14 13:49:16 -05:00
2014-02-20 11:06:11 -05:00
handleTaskResultsTransferError: (error) =>
2014-05-20 11:00:44 -04:00
return if @ destroyed
2014-02-20 11:06:11 -05:00
@ trigger ' statusUpdate ' , ' There was an error sending the results back to the server. '
2014-02-14 13:49:16 -05:00
console . log " Task registration error: #{ JSON . stringify error } "
cleanupAndSimulateAnotherTask: =>
2014-05-20 11:00:44 -04:00
return if @ destroyed
2014-05-06 12:49:04 -04:00
@ cleanupSimulation ( )
2015-11-29 15:30:19 -05:00
if @ options . background or @ noTasks
@ fetchAndSimulateOneGame ( )
else
@ fetchAndSimulateTask ( )
2014-02-14 13:49:16 -05:00
2014-05-06 12:49:04 -04:00
cleanupSimulation: ->
2015-11-29 15:30:19 -05:00
@ stopListening @ god
2014-05-06 12:49:04 -04:00
@world = null
@level = null
2014-02-14 13:49:16 -05:00
formTaskResultsObject: (simulationResults) ->
taskResults =
taskID: @ task . getTaskID ( )
receiptHandle: @ task . getReceiptHandle ( )
2014-02-26 15:14:02 -05:00
originalSessionID: @ task . getFirstSessionID ( )
originalSessionRank: - 1
2014-02-14 13:49:16 -05:00
calculationTime: 500
sessions: [ ]
2015-02-11 23:24:12 -05:00
simulator: @ simulator
2015-02-12 20:07:00 -05:00
randomSeed: @ task . world . randomSeed
2014-02-14 13:49:16 -05:00
2014-02-14 18:50:42 -05:00
for session in @ task . getSessions ( )
2014-02-14 13:49:16 -05:00
sessionResult =
sessionID: session . sessionID
2014-02-18 14:46:14 -05:00
submitDate: session . submitDate
creator: session . creator
2014-05-16 17:52:49 -04:00
name: session . creatorName
totalScore: session . totalScore
2014-02-14 13:49:16 -05:00
metrics:
2014-02-14 18:50:42 -05:00
rank: @ calculateSessionRank session . sessionID , simulationResults . goalStates , @ task . generateTeamToSessionMap ( )
2015-12-06 12:20:30 -05:00
shouldUpdateLastOpponentSubmitDateForLeague: session . shouldUpdateLastOpponentSubmitDateForLeague
2014-02-26 15:14:02 -05:00
if session . sessionID is taskResults . originalSessionID
taskResults.originalSessionRank = sessionResult . metrics . rank
taskResults.originalSessionTeam = session . team
2014-02-14 13:49:16 -05:00
taskResults . sessions . push sessionResult
return taskResults
2014-02-14 18:50:42 -05:00
calculateSessionRank: (sessionID, goalStates, teamSessionMap) ->
2014-04-08 17:58:34 -04: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 13:49:16 -05:00
return 0
2014-06-30 22:16:26 -04:00
else if ogresWon and teamSessionMap [ ' ogres ' ] is sessionID
2014-02-14 13:49:16 -05:00
return 0
2014-06-30 22:16:26 -04:00
else if ogresWon and teamSessionMap [ ' ogres ' ] isnt sessionID
2014-02-14 13:49:16 -05:00
return 1
2014-06-30 22:16:26 -04:00
else if humansWon and teamSessionMap [ ' humans ' ] is sessionID
2014-02-14 13:49:16 -05:00
return 0
else
return 1
2014-02-14 18:50:42 -05:00
generateSpellsObject: ->
@currentUserCodeMap = @ task . generateSpellKeyToSourceMap ( )
2014-02-14 13:49:16 -05:00
@spells = { }
for thang in @ level . attributes . thangs
continue if @ thangIsATemplate thang
@ generateSpellKeyToSourceMapPropertiesFromThang thang
2014-05-10 21:24:50 -04:00
@ spells
2014-02-14 13:49:16 -05:00
thangIsATemplate: (thang) ->
for component in thang . components
continue unless @ componentHasProgrammableMethods component
for methodName , method of component . config . programmableMethods
2014-02-14 18:50:42 -05:00
return true if @ methodBelongsToTemplateThang method
2014-02-14 13:49:16 -05: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 12:21:41 -05:00
spellKeyComponents = [ thang , methodName ]
2014-02-14 18:50:42 -05:00
spellKeyComponents [ 0 ] = _ . string . slugify spellKeyComponents [ 0 ]
2014-02-26 12:21:41 -05:00
spellKey = spellKeyComponents . join ' / '
spellKey
2014-02-26 20:45:08 -05:00
2014-02-14 13:49:16 -05:00
createSpellAndAssignName: (spellKey, spellName) ->
@ spells [ spellKey ] ? = { }
2014-02-14 18:50:42 -05:00
@ spells [ spellKey ] . name = spellName
2014-02-14 13:49:16 -05:00
createSpellThang: (thang, method, spellKey) ->
@ spells [ spellKey ] . thangs ? = { }
@ spells [ spellKey ] . thangs [ thang . id ] ? = { }
2014-04-11 20:11:55 -04:00
spellTeam = @ task . getSpellKeyToTeamMap ( ) [ spellKey ]
playerTeams = @ task . getPlayerTeams ( )
useProtectAPI = true
2014-06-20 20:19:18 -04:00
if spellTeam not in playerTeams
useProtectAPI = false
else
spellSession = _ . filter ( @ task . getSessions ( ) , { team: spellTeam } ) [ 0 ]
unless codeLanguage = spellSession ? . submittedCodeLanguage
2014-06-30 22:16:26 -04:00
console . warn ' Session ' , spellSession . creatorName , spellSession . team , ' didn \' t have submittedCodeLanguage, just: ' , spellSession
2014-06-20 20:19:18 -04:00
@ spells [ spellKey ] . thangs [ thang . id ] . aether = @ createAether @ spells [ spellKey ] . name , method , useProtectAPI , codeLanguage ? ' javascript '
2014-02-14 13:49:16 -05:00
2014-05-15 19:44:16 -04:00
transpileSpell: (thang, spellKey, methodName) ->
2014-02-14 13:49:16 -05:00
slugifiedThangID = _ . string . slugify thang . id
2014-05-15 19:43:16 -04:00
generatedSpellKey = [ slugifiedThangID , methodName ] . join ' / '
2014-06-30 22:16:26 -04:00
source = @ currentUserCodeMap [ generatedSpellKey ] ? ' '
2014-03-04 11:30:50 -05:00
aether = @ spells [ spellKey ] . thangs [ thang . id ] . aether
2016-05-05 16:22:30 -04:00
#unless _.contains(@task.spellKeysToTranspile, generatedSpellKey)
try
aether . transpile source
catch e
console . log " Couldn ' t transpile #{ spellKey } : \n #{ source } \n " , e
aether . transpile ' '
2014-02-14 13:49:16 -05:00
2014-06-20 20:19:18 -04:00
createAether: (methodName, method, useProtectAPI, codeLanguage) ->
2016-05-24 15:00:04 -04:00
aetherOptions = createAetherOptions functionName: methodName , codeLanguage: codeLanguage , skipProtectAPI: not useProtectAPI
2014-02-14 13:49:16 -05:00
return new Aether aetherOptions
class SimulationTask
constructor: (@rawData) ->
2014-04-11 20:11:55 -04:00
@spellKeyToTeamMap = { }
2014-02-14 13:49:16 -05:00
getLevelName: ->
2014-04-26 17:21:26 -04:00
levelName = @ rawData . sessions ? [ 0 ] ? . levelID
2014-02-14 13:49:16 -05:00
return levelName if levelName ?
2014-06-30 22:16:26 -04:00
@ throwMalformedTaskError ' The level name couldn \' t be deduced from the task. '
2014-02-14 13:49:16 -05:00
generateTeamToSessionMap: ->
teamSessionMap = { }
for session in @ rawData . sessions
2014-06-30 22:16:26 -04:00
@ throwMalformedTaskError ' Two players share the same team ' if teamSessionMap [ session . team ] ?
2014-02-14 13:49:16 -05: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-20 00:56:26 -04:00
getSecondSessionID: -> @ rawData . sessions [ 1 ] . sessionID
2014-02-14 13:49:16 -05:00
getTaskID: -> @ rawData . taskID
getReceiptHandle: -> @ rawData . receiptHandle
getSessions: -> @ rawData . sessions
2014-04-25 19:57:42 -04:00
2014-04-11 20:11:55 -04:00
getSpellKeyToTeamMap: -> @ spellKeyToTeamMap
2014-04-25 19:57:42 -04:00
2014-04-11 20:11:55 -04:00
getPlayerTeams: -> _ . pluck @ rawData . sessions , ' team '
2014-02-14 13:49:16 -05:00
2014-05-15 19:44:16 -04:00
setWorld: (@world) ->
2014-02-14 13:49:16 -05:00
generateSpellKeyToSourceMap: ->
2016-05-05 16:22:30 -04:00
# TODO: we always now only have hero-placeholder/plan vs. hero-placeholder-1/plan on humans vs. ogres, always just have to retranspile for Esper, and never need to transpile for NPCs or other methods, so we can get rid of almost all of this stuff.
2014-04-11 20:11:55 -04:00
playerTeams = _ . pluck @ rawData . sessions , ' team '
2014-02-14 13:49:16 -05:00
spellKeyToSourceMap = { }
for session in @ rawData . sessions
teamSpells = session . teamSpells [ session . team ]
2014-04-11 20:11:55 -04:00
allTeams = _ . keys session . teamSpells
for team in allTeams
for spell in session . teamSpells [ team ]
@ spellKeyToTeamMap [ spell ] = team
2014-02-26 12:21:41 -05:00
teamCode = { }
2014-04-11 20:11:55 -04:00
2016-05-05 16:22:30 -04:00
for thangName , thangSpells of session . submittedCode
2014-02-26 12:21:41 -05:00
for spellName , spell of thangSpells
2014-06-30 22:16:26 -04:00
fullSpellName = [ thangName , spellName ] . join ' / '
2014-02-26 12:21:41 -05:00
if _ . contains ( teamSpells , fullSpellName )
2016-05-05 16:22:30 -04:00
teamCode [ fullSpellName ] = LZString . decompressFromUTF16 spell
2014-04-25 19:57:42 -04:00
2014-02-26 12:21:41 -05:00
_ . merge spellKeyToSourceMap , teamCode
2014-02-26 20:45:08 -05:00
2014-02-14 13:49:16 -05:00
spellKeyToSourceMap