This commit is contained in:
Scott Erickson 2014-02-14 14:10:35 -08:00
commit a8c483a59f
2 changed files with 364 additions and 93 deletions

View file

@ -0,0 +1,270 @@
SuperModel = require 'models/SuperModel'
LevelLoader = require 'lib/LevelLoader'
GoalManager = require 'lib/world/GoalManager'
module.exports = class Simulator
constructor: ->
@retryDelayInSeconds = 10
@taskURL = "/queue/scoring"
fetchAndSimulateTask: =>
$.ajax
url: @taskURL
type: "GET"
error: @handleFetchTaskError
success: @setupSimulationAndLoadLevel
cleanupSimulation: ->
handleFetchTaskError: (errorData) =>
console.log "There were no games to score. Error: #{JSON.stringify errorData}"
console.log "Retrying in #{@retryDelayInSeconds}"
_.delay @fetchAndSimulateTask, @retryDelayInSeconds * 1000
setupSimulationAndLoadLevel: (taskData) =>
@task = new SimulationTask(taskData)
@superModel = new SuperModel()
@god = new God()
@levelLoader = new LevelLoader @task.getLevelName(), @superModel, @task.getFirstSessionID()
@levelLoader.once 'loaded-all', @simulateGame
simulateGame: =>
@assignWorldAndLevelFromLevelLoaderAndDestroyIt()
@setupGod()
try
@commenceSimulationAndSetupCallback()
catch e
console.log "There was an error in simulation. Trying again in #{retryDelayInSeconds} seconds"
console.log "Error #{e}"
_.delay @fetchAndSimulateTask, @retryDelayInSeconds * 1000
assignWorldAndLevelFromLevelLoaderAndDestroyIt: ->
@world = @levelLoader.world
@level = @levelLoader.level
@levelLoader.destroy()
setupGod: ->
@god.level = @level.serialize @supermodel
@god.worldClassMap = world.classMap
@setupGoalManager()
@setupGodSpells()
setupGoalManager: ->
@god.goalManager = new GoalManager @world
@god.goalManager.goals = @fetchGoalsFromWorldNoteChain()
@god.goalManager.goalStates = @manuallyGenerateGoalStates()
commenceSimulationAndSetupCallback: ->
@god.createWorld()
Backbone.Mediator.subscribeOnce 'god:new-world-created', @processResults, @
processResults: (simulationResults) ->
taskResults = @formTaskResultsObject simulationResults
sendResultsBackToServer taskResults
sendResultsBackToServer: (results) =>
$.ajax
url: @taskURL
data: results
type: "PUT"
success: @handleTaskResultsTransferSuccess
error: @handleTaskResultsTransferError
complete: @cleanupAndSimulateAnotherTask()
handleTaskResultsTransferSuccess: (result) ->
console.log "Task registration result: #{JSON.stringify result}"
handleTaskResultsTransferError: (error) ->
console.log "Task registration error: #{JSON.stringify error}"
cleanupAndSimulateAnotherTask: =>
@cleanupSimulation()
@fetchAndSimulateTask()
formTaskResultsObject: (simulationResults) ->
taskResults =
taskID: @task.getTaskID()
receiptHandle: @task.getReceiptHandle()
calculationTime: 500
sessions: []
for session in @task.sessions
sessionResult =
sessionID: session.sessionID
sessionChangedTime: session.sessionChangedTime
metrics:
rank: @calculateSessionRank session.sessionID, simulationResults.goalStates
taskResults.sessions.push sessionResult
return taskResults
calculateSessionRank: (sessionID, goalStates) ->
humansDestroyed = goalStates["destroy-humans"].status is "success"
ogresDestroyed = goalStates["destroy-ogres"].status is "success"
console.log "Humans destroyed:#{humansDestroyed}"
console.log "Ogres destroyed:#{ogresDestroyed}"
console.log "Team Session Map: #{JSON.stringify @teamSessionMap}"
if humansDestroyed is ogresDestroyed
return 0
else if humansDestroyed and @teamSessionMap["ogres"] is sessionID
return 0
else if humansDestroyed and @teamSessionMap["ogres"] isnt sessionID
return 1
else if ogresDestroyed and @teamSessionMap["humans"] is sessionID
return 0
else
return 1
fetchGoalsFromWorldNoteChain: -> return @god.goalManager.world.scripts[0].noteChain[0].goals.add
manuallyGenerateGoalStates: ->
goalStates =
"destroy-humans":
keyFrame: 0
killed:
"Human Base": false
status: "incomplete"
"destroy-ogres":
keyFrame:0
killed:
"Ogre Base": false
status: "incomplete"
setupGodSpells: ->
@generateSpellsObject()
@god.spells = @spells
generateSpellsObject: (currentUserCodeMap) ->
@userCodeMap = currentUserCodeMap
@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
return true if methodBelongsToTemplateThang method
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) ->
spellKeyComponents = [thang.id, methodName]
pathComponents[0] = _.string.slugify pathComponents[0]
pathComponents.join '/'
createSpellAndAssignName: (spellKey, spellName) ->
@spells[spellKey] ?= {}
@spells[spellKey].name = methodName
createSpellThang: (thang, method, spellKey) ->
@spells[spellKey].thangs ?= {}
@spells[spellKey].thangs[thang.id] ?= {}
@spells[spellKey].thangs[thang.id].aether = @createAether @spells[spellKey].name, method
transpileSpell: (thang, spellKey, methodName) ->
slugifiedThangID = _.string.slugify thang.id
source = @currentUserCodeMap[slugifiedThangID]?[methodName] ? ""
@spells[spellKey].thangs[thang.id].aether.transpile source
createAether: (methodName, method) ->
aetherOptions =
functionName: methodName
protectAPI: false
includeFlow: false
return new Aether aetherOptions
class SimulationTask
constructor: (@rawData) ->
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
generateSpellKeyToSourceMap: ->
spellKeyToSourceMap = {}
for session in @rawData.sessions
teamSpells = session.teamSpells[session.team]
_.merge spellKeyToSourceMap, _.pick(session.code, teamSpells)
commonSpells = session.teamSpells["common"]
_.merge spellKeyToSourceMap, _.pick(session.code, commonSpells) if commonSpells?
spellKeyToSourceMap

View file

@ -3,14 +3,14 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
loading: "Loading..." loading: "Loading..."
saving: "儲存中..." saving: "儲存中..."
sending: "發送中...." sending: "發送中...."
cancel: "退出" cancel: "取消"
# save: "Save" save: "存檔"
delay_1_sec: "1 秒" delay_1_sec: "1 秒"
delay_3_sec: "3 秒" delay_3_sec: "3 秒"
delay_5_sec: "5 秒" delay_5_sec: "5 秒"
manual: "手動手动" manual: "手動發動"
# fork: "Fork" fork: "Fork"
play: "" play: "播放" # Play timeline in a level.
modal: modal:
close: "關閉" close: "關閉"
@ -20,18 +20,18 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
page_not_found: "找不到網頁" page_not_found: "找不到網頁"
nav: nav:
play: "" play: "開始遊戲"
editor: "編輯" editor: "編輯"
blog: "博客" blog: "官方部落格" # Official blog
forum: "論壇" forum: "論壇"
admin: "超級管理員" admin: "系統管理員"
home: "首頁" home: "首頁"
contribute: "貢獻" contribute: "貢獻"
legal: "法律" legal: "版權聲明" # Copyright
about: "關於" about: "關於"
contact: "聯繫我們" contact: "聯繫我們"
twitter_follow: "關注" twitter_follow: "在Twitter關注"
# employers: "Employers" employers: "招募訊息"
# versions: # versions:
# save_version_title: "Save New Version" # save_version_title: "Save New Version"
@ -43,61 +43,61 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
login: login:
sign_up: "註冊" sign_up: "註冊"
log_in: "" log_in: ""
log_out: "退" log_out: ""
recover: "找回帳" recover: "找回帳"
recover: recover:
recover_account_title: "復原帳" recover_account_title: "復原帳"
# send_password: "Send Recovery Password" send_password: "送出新密碼"
signup: signup:
# create_account_title: "Create Account to Save Progress" create_account_title: "建立帳號儲存進度"
description: "這是免費的。超簡單的喲" description: "登入以儲存遊戲進度"
email_announcements: "通過郵件接收通知" email_announcements: "通過郵件接收通知"
coppa: "13歲以上或非美國公民" coppa: "13歲以上或非美國公民"
coppa_why: "爲什麽?" coppa_why: "爲什麽?"
creating: "戶創建中..." creating: "號建立中..."
sign_up: "註冊" sign_up: "註冊"
log_in: "" log_in: ""
home: home:
slogan: "通過玩遊戲學習Javascript 腳本語言" slogan: "通過玩遊戲學習Javascript 腳本語言"
no_ie: "抱歉Internet Explorer 9 等舊的瀏覽器打不開此網站" no_ie: "抱歉Internet Explorer 9 等舊的瀏覽器打不開此網站"
no_mobile: "CodeCombat 不是針對手機設備設計的,所以可能會出問題!" no_mobile: "CodeCombat 不是針對手機設備設計的,所以可能會出問題!"
play: "" play: "開始遊戲"
play: play:
choose_your_level: "選取難度" choose_your_level: "選取關卡"
adventurer_prefix: "你可以選擇以下任意關卡,或者討論以上的關卡 " adventurer_prefix: "你可以選擇以下任意關卡,或者討論以上的關卡 "
adventurer_forum: "冒險家論壇" adventurer_forum: "冒險家論壇"
adventurer_suffix: "." adventurer_suffix: "."
campaign_beginner: "新手作戰" campaign_beginner: "新手指南"
campaign_beginner_description: "...在這裡可以學到編程技巧。" campaign_beginner_description: "...在這裡可以學到編程技巧。"
campaign_dev: "隨機關卡" campaign_dev: "隨機關卡"
campaign_dev_description: "...在這裡你可以學到做一些複雜功能的接口" campaign_dev_description: "...在這裡你可以學到做一些較複雜的程式技巧"
campaign_multiplayer: "多人競技場" campaign_multiplayer: "多人競技場"
campaign_multiplayer_description: "...在這裡你可以和其他玩家們進行代碼近戰。" campaign_multiplayer_description: "...在這裡你可以和其他玩家進行對戰。"
campaign_player_created: "已創建的玩家" campaign_player_created: "玩家建立的關卡"
campaign_player_created_description: "...在這裡你可以與你的小夥伴的創造力戰鬥 <a href=\"/contribute#artisan\">技術指導</a>." campaign_player_created_description: "...挑戰同伴的創意 <a href=\"/contribute#artisan\">技術指導</a>."
level_difficulty: "難度" level_difficulty: "難度"
contact: contact:
contact_us: "聯繫我們" contact_us: "聯繫我們"
welcome: "很高興收到你的信!用這個表格給我們發電郵。 " welcome: "很高興收到你的信!用這個表格給我們發電郵。 "
contribute_prefix: "如果你想貢獻代碼,請看 " contribute_prefix: "如果你想貢獻程式,請看 "
contribute_page: "貢獻代碼頁面" contribute_page: "程式貢獻頁面"
contribute_suffix: "" contribute_suffix: ""
forum_prefix: "對於任何公共部份,放手去做吧 " forum_prefix: "如果有任何問題, 請至"
forum_page: "我們的論壇" forum_page: "論壇"
forum_suffix: "代替" forum_suffix: "討論"
sending: "發送中。。。" sending: "發送中。。。"
send: "意見反饋" send: "意見反饋"
diplomat_suggestion: diplomat_suggestion:
title: "幫我們翻譯CodeCombat" title: "幫我們翻譯CodeCombat"
sub_heading: "我們需要您的語言技能" sub_heading: "我們需要您的語言技能"
pitch_body: "我們開發了CodeCombat的英文版但是現在我們的玩家遍佈全球。很多人想玩中文(繁体)版的,卻不會說英文,所以如果你中英文都會,請考慮一下參加我們的翻譯工作,幫忙把 CodeCombat 網站還有所有的關卡翻譯成中文(繁体)。" pitch_body: "我們開發了CodeCombat的英文版但是現在我們的玩家遍佈全球。很多人想玩中文版的,卻不會說英文,所以如果你中英文都會,請考慮一下參加我們的翻譯工作,幫忙把 CodeCombat 網站還有所有的關卡翻譯成中文(繁体)。"
missing_translations: "直至所有正體中文的翻譯完畢,當無法提供正體中文時還會以英文顯示。" missing_translations: "直至所有正體中文的翻譯完畢,當無法提供正體中文時還會以英文顯示。"
learn_more: "關於成為外交官" learn_more: "關於成為外交官"
subscribe_as_diplomat: "註冊成為外交官" subscribe_as_diplomat: "註冊成為外交官"
@ -107,91 +107,92 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
# customize_avatar: "Customize Your Avatar" # customize_avatar: "Customize Your Avatar"
account_settings: account_settings:
title: "戶設置" title: "號設定"
not_logged_in: "登錄或創建一個帳戶來修改設置。" not_logged_in: "登錄或建立一個帳號來修改設置。"
autosave: "自動保存修改" autosave: "自動保存修改"
me_tab: "" me_tab: ""
picture_tab: "圖片" picture_tab: "頭像"
wizard_tab: "巫師" wizard_tab: "巫師"
password_tab: "密碼" password_tab: "密碼"
emails_tab: "郵件" emails_tab: "郵件"
gravatar_select: "選擇使用 Gravatar 照片" gravatar_select: "選擇一個Gravatar"
gravatar_add_photos: "添加小圖和照片到一个 Gravatar 帳戶供你選擇。" gravatar_add_photos: "上傳頭像到Gravatar"
gravatar_add_more_photos: "添加更多照片到你的 Gravatar 帳戶并查看。" # gravatar_add_more_photos: "Add thumbnails and photos to a Gravatar account for your email to choose an image."
wizard_color: "巫師 衣服 顏色" wizard_color: "巫師 衣服 顏色"
new_password: "新密碼" new_password: "新密碼"
new_password_verify: "核實" new_password_verify: "確認密碼"
email_subscriptions: "郵箱驗證" email_subscriptions: "訂閱"
email_announcements: "通知" email_announcements: "通知"
email_announcements_description: "接收關於 CodeCombat 最近的新聞和發展的郵件。" email_notifications_description: "接收帳號通知"
email_announcements_description: "接收關於 CodeCombat 的新聞和開發消息。"
contributor_emails: "貢獻者電郵" contributor_emails: "貢獻者電郵"
contribute_prefix: "我們在尋找志同道合的人!請到 " contribute_prefix: "我們在尋找志同道合的人!請到 "
contribute_page: "貢獻頁面" contribute_page: "貢獻頁面"
contribute_suffix: " 查看更多信息。" contribute_suffix: " 查看更多信息。"
email_toggle: "切換所有" email_toggle: "全選"
error_saving: "保存時出錯" error_saving: "保存時發生錯誤"
saved: "保存修改" saved: "修改已儲存"
password_mismatch: "密碼不匹配" password_mismatch: "密碼不正確"
account_profile: account_profile:
edit_settings: "編輯設置" edit_settings: "帳號設定"
profile_for_prefix: "關於TA的基本資料" profile_for_prefix: "關於"
# profile_for_suffix: "" profile_for_suffix: "的基本資料"
profile: "基本資料" profile: "基本資料"
user_not_found: "沒有找到用戶。檢查URL" user_not_found: "沒有找到用戶。檢查URL"
gravatar_not_found_mine: "我們找不到TA的基本資料" gravatar_not_found_mine: "我們找不到有關"
gravatar_not_found_email_suffix: "." gravatar_not_found_email_suffix: "的資料"
gravatar_signup_prefix: "去註冊 " gravatar_signup_prefix: "請至"
gravatar_signup_suffix: " 去設置!" gravatar_signup_suffix: " 註冊帳號"
gravatar_not_found_other: "哎呦,沒有與這個人的郵箱相關的資料。" gravatar_not_found_other: "哎呦,找不到這個地址的資料。"
gravatar_contact: "聯繫" gravatar_contact: "聯繫我們"
gravatar_websites: "網站" gravatar_websites: "網站"
gravatar_accounts: "顯示為" gravatar_accounts: "顯示為"
gravatar_profile_link: "完善 Gravatar 資料" gravatar_profile_link: "完善 Gravatar 資料"
play_level: play_level:
level_load_error: "關卡不能載入。" level_load_error: "載入關卡時發生錯誤"
done: "完成" done: "完成"
grid: "格子" grid: "格子"
customize_wizard: "自定義巫師" customize_wizard: "自定義巫師"
home: "" home: ""
guide: "指南" guide: "指南"
multiplayer: "多人遊戲" multiplayer: "多人遊戲"
restart: "重新開始" restart: "重新開始"
goals: "目標" goals: "目標"
action_timeline: "行動時間軸" action_timeline: "行動時間軸"
click_to_select: "點擊選擇一個單元。" click_to_select: "點擊選擇一個單元。"
reload_title: "載所有代碼?" reload_title: "新載入程式碼?"
reload_really: "確定重載這一關,返回開始處" reload_really: "確定重設所有的程式碼"
reload_confirm: "載所有" reload_confirm: "設所有程式碼"
# victory_title_prefix: "" victory_title_prefix: ""
victory_title_suffix: " 完成" victory_title_suffix: " 完成"
victory_sign_up: "保存進度" victory_sign_up: "保存進度"
victory_sign_up_poke: "想保存你的代碼?創建一個免費帳戶吧!" victory_sign_up_poke: "想保存你的程式碼?建立一個免費帳號吧!"
victory_rate_the_level: "評估關卡: " victory_rate_the_level: "評估關卡: "
victory_play_next_level: "下一關" victory_play_next_level: "下一關"
victory_go_home: "返回" victory_go_home: "返回"
victory_review: "給我們饋!" victory_review: "給我們饋!"
victory_hour_of_code_done: "你完成了嗎?" victory_hour_of_code_done: "你完成了嗎?"
victory_hour_of_code_done_yes: "是的,我完成了我的碼!" victory_hour_of_code_done_yes: "是的,我完成了我的程式碼!"
multiplayer_title: "多人遊戲設" multiplayer_title: "多人遊戲設"
multiplayer_link_description: "把這個鏈接告訴小夥伴們,一起玩吧。" multiplayer_link_description: "把這個連結告訴同伴們,一起玩吧。"
multiplayer_hint_label: "提示:" multiplayer_hint_label: "提示:"
multiplayer_hint: " 點擊全選,然後按 Apple-C苹果電腦或 Ctrl-C 複製鏈接" multiplayer_hint: " 點擊全選,然後按 ⌘-C 或 Ctrl-C 複製連結"
multiplayer_coming_soon: "多人遊戲的更多特性!" multiplayer_coming_soon: "請期待更多的多人關卡!"
guide_title: "指南" guide_title: "指南"
tome_minion_spells: "助手的咒語" tome_minion_spells: "助手的咒語"
tome_read_only_spells: "讀的咒語" tome_read_only_spells: "讀的咒語"
tome_other_units: "其他單" tome_other_units: "其他單"
tome_cast_button_castable: "發動" tome_cast_button_castable: "發動"
tome_cast_button_casting: "發動中" tome_cast_button_casting: "發動中"
tome_cast_button_cast: "咒語" tome_cast_button_cast: "咒語"
tome_autocast_delay: "自動施法延遲" tome_autocast_delay: "自動施法延遲"
tome_select_spell: "選擇一個法術" tome_select_spell: "選擇一個法術"
tome_select_a_thang: "選擇人物來 " tome_select_a_thang: "選擇一個人物來施放"
tome_available_spells: "可用的法術" tome_available_spells: "可用的法術"
hud_continue: "繼續 (按 shift-空格)" hud_continue: "繼續 (按 shift-空格)"
# spell_saved: "Spell Saved" spell_saved: "咒語已儲存"
# admin: # admin:
# av_title: "Admin Views" # av_title: "Admin Views"
@ -245,33 +246,33 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
# general: # general:
# and: "and" # and: "and"
or: "" # or: ""
name: "名字" # name: "名字"
# body: "Body" # body: "Body"
# version: "Version" # version: "Version"
# commit_msg: "Commit Message" # commit_msg: "Commit Message"
# version_history_for: "Version History for: " # version_history_for: "Version History for: "
# results: "Results" # results: "Results"
# description: "Description" # description: "Description"
email: "郵箱" # email: "Email"
message: "留言" # message: "訊息"
# about: about:
# who_is_codecombat: "Who is CodeCombat?" who_is_codecombat: "什麼是CodeCombat?"
# why_codecombat: "Why CodeCombat?" why_codecombat: "為什麼使用CodeCombat?"
# who_description_prefix: "together started CodeCombat in 2013. We also created " who_description_prefix: "在2013年共同創立了CodeCombat. 在2008年, 我們創立了"
# who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters." who_description_suffix: "排名第一的中、日文字的學習網頁及iOS系統應用程式。"
# who_description_ending: "Now it's time to teach people to write code." who_description_ending: "這次,我們將教大家如何寫程式。"
# why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that." why_paragraph_1: "當我們在研發Skritter時George不會寫程式所以常常無法展現他的想法。他嘗試去學然而課程成果緩慢。他的室友曾想學習新技能試過Codecademy但厭倦了。每周也都有其他朋友投入Codecademy卻都以失敗告終。我們發現這與我們藉由Skritter所想解決的問題是一致的─人們需要的不是繁瑣又密集的課程, 而是快速而大量的練習。我們知道該如何改善這個情況。"
# why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it." why_paragraph_2: "想學程式嗎? 你不需要課程。你需要的只是大量的時間去\"\"程式。"
# why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like" why_paragraph_3_prefix: "寫程式應該是有趣的。當然不是"
# why_paragraph_3_italic: "yay a badge" why_paragraph_3_italic: "「耶!拿到獎章了。」"
# why_paragraph_3_center: "but fun like" why_paragraph_3_center: "的有趣, 而是"
# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" why_paragraph_3_italic_caps: "「媽我不要出去玩,我要寫完這段!」"
# why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." why_paragraph_3_suffix: "般引人入勝。這是為甚麼CodeCombat被設計成多人對戰「遊戲」而不是遊戲化「課程」。在你對這遊戲無法自拔之前我們是不會放棄的─幫然這個遊戲將是有益於你的。"
# why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." why_paragraph_4: "如果你要沉迷遊戲的話就來沉迷CodeCombat成為科技時代的魔法師吧"
# why_ending: "And hey, it's free. " why_ending: "啊還有,他是免費的。"
# why_ending_url: "Start wizarding now!" why_ending_url: "那還等什麼? 馬上開始!"
# george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere." # george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere."
# scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one." # scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one."
# nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat." # nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat."