mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-25 00:28:31 -05:00
144 lines
5.4 KiB
CoffeeScript
144 lines
5.4 KiB
CoffeeScript
CocoClass = require 'core/CocoClass'
|
|
SuperModel = require 'models/SuperModel'
|
|
{createAetherOptions} = require 'lib/aether_utils'
|
|
God = require 'lib/God'
|
|
GoalManager = require 'lib/world/GoalManager'
|
|
LevelLoader = require 'lib/LevelLoader'
|
|
utils = require 'core/utils'
|
|
|
|
module.exports = class VerifierTest extends CocoClass
|
|
constructor: (@levelID, @updateCallback, @supermodel, @language) ->
|
|
super()
|
|
# TODO: turn this into a Subview
|
|
# TODO: listen to Backbone.Mediator.publish 'god:non-user-code-problem', problem: event.data.problem, god: @shared.god from Angel to detect when we can't load the thing
|
|
# TODO: listen to the progress report from Angel to show a simulation progress bar (maybe even out of the number of frames we actually know it'll take)
|
|
@supermodel ?= new SuperModel()
|
|
|
|
if utils.getQueryVariable('dev')
|
|
@supermodel.shouldSaveBackups = (model) -> # Make sure to load possibly changed things from localStorage.
|
|
model.constructor.className in ['Level', 'LevelComponent', 'LevelSystem', 'ThangType']
|
|
|
|
@language ?= 'python'
|
|
@load()
|
|
|
|
load: ->
|
|
@loadStartTime = new Date()
|
|
@god = new God maxAngels: 1, headless: true
|
|
@levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, headless: true, fakeSessionConfig: {codeLanguage: @language, callback: @configureSession}
|
|
@listenToOnce @levelLoader, 'world-necessities-loaded', @onWorldNecessitiesLoaded
|
|
|
|
onWorldNecessitiesLoaded: ->
|
|
# Called when we have enough to build the world, but not everything is loaded
|
|
@grabLevelLoaderData()
|
|
|
|
unless @solution
|
|
@error = 'No solution present...'
|
|
@state = 'no-solution'
|
|
@updateCallback? state: 'no-solution'
|
|
return
|
|
me.team = @team = 'humans'
|
|
@setupGod()
|
|
@initGoalManager()
|
|
@register()
|
|
|
|
configureSession: (session, level) =>
|
|
# TODO: reach into and find hero and get the config from the solution
|
|
try
|
|
hero = _.find level.get("thangs"), id: "Hero Placeholder"
|
|
config = _.find(hero.components, (x) -> x.config?.programmableMethods?.plan).config
|
|
programmable = config.programmableMethods.plan
|
|
solution = _.find (programmable.solutions ? []), language: session.get('codeLanguage')
|
|
solution.source = _.template(solution.source)(config?.programmableMethods?.plan.context)
|
|
session.solution = solution
|
|
|
|
session.set 'heroConfig', session.solution.heroConfig
|
|
session.set 'code', {'hero-placeholder': plan: session.solution.source}
|
|
state = session.get 'state'
|
|
state.flagHistory = session.solution.flagHistory
|
|
state.difficulty = session.solution.difficulty or 0
|
|
session.solution.seed = undefined unless _.isNumber session.solution.seed # TODO: migrate away from submissionCount/sessionID seed objects
|
|
catch e
|
|
@state = 'error'
|
|
@error = "Could not load the session solution for #{level.get('name')}: " + e.toString() + "\n" + e.stack
|
|
|
|
grabLevelLoaderData: ->
|
|
@world = @levelLoader.world
|
|
@level = @levelLoader.level
|
|
@session = @levelLoader.session
|
|
@solution = @levelLoader.session.solution
|
|
|
|
setupGod: ->
|
|
@god.setLevel @level.serialize @supermodel, @session
|
|
@god.setLevelSessionIDs [@session.id]
|
|
@god.setWorldClassMap @world.classMap
|
|
@god.lastFlagHistory = @session.get('state').flagHistory
|
|
@god.lastDifficulty = @session.get('state').difficulty
|
|
@god.lastFixedSeed = @session.solution.seed
|
|
@god.lastSubmissionCount = 0
|
|
|
|
initGoalManager: ->
|
|
@goalManager = new GoalManager(@world, @level.get('goals'), @team)
|
|
@god.setGoalManager @goalManager
|
|
|
|
register: ->
|
|
@listenToOnce @god, 'infinite-loop', @fail
|
|
@listenToOnce @god, 'user-code-problem', @onUserCodeProblem
|
|
@listenToOnce @god, 'goals-calculated', @processSingleGameResults
|
|
@god.createWorld @generateSpellsObject()
|
|
@updateCallback? state: 'running'
|
|
|
|
processSingleGameResults: (e) ->
|
|
console.log(e)
|
|
@goals = e.goalStates
|
|
@frames = e.totalFrames
|
|
@lastFrameHash = e.lastFrameHash
|
|
@state = 'complete'
|
|
@updateCallback? state: @state
|
|
@scheduleCleanup()
|
|
|
|
isSuccessful: () ->
|
|
return false unless @solution?
|
|
return false unless @frames == @solution.frameCount
|
|
if @goals and @solution.goals
|
|
for k of @goals
|
|
continue if not @solution.goals[k]
|
|
return false if @solution.goals[k] != @goals[k].status
|
|
return true
|
|
|
|
onUserCodeProblem: (e) ->
|
|
console.warn "Found user code problem:", e
|
|
|
|
onNonUserCodeProblem: (e) ->
|
|
console.error "Found non-user-code problem:", e
|
|
@error = "Failed due to non-user-code problem: #{JSON.stringify(e)}"
|
|
@state = 'error'
|
|
@updateCallback? state: @state
|
|
@scheduleCleanup()
|
|
|
|
fail: (e) ->
|
|
@error = 'Failed due to infinite loop.'
|
|
@state = 'error'
|
|
@updateCallback? state: @state
|
|
@scheduleCleanup()
|
|
|
|
generateSpellsObject: ->
|
|
aetherOptions = createAetherOptions functionName: 'plan', codeLanguage: @session.get('codeLanguage')
|
|
spellThang = aether: new Aether aetherOptions
|
|
spells = "hero-placeholder/plan": thangs: {'Hero Placeholder': spellThang}, name: 'plan'
|
|
source = @session.get('code')['hero-placeholder'].plan
|
|
try
|
|
spellThang.aether.transpile source
|
|
catch e
|
|
console.log "Couldn't transpile!\n#{source}\n", e
|
|
spellThang.aether.transpile ''
|
|
spells
|
|
|
|
scheduleCleanup: ->
|
|
setTimeout @cleanup, 100
|
|
|
|
cleanup: =>
|
|
if @god
|
|
@stopListening @god
|
|
@god.destroy()
|
|
|
|
@world = null
|