diff --git a/.editorconfig b/.editorconfig
index 2795b754d..016543a0a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -3,9 +3,9 @@ root = true
# Matches multiple files with brace expansion notation
# Set default charset
-[*.{js,json}]
+[*.{js,json, styl, jade}]
charset = utf-8
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
-insert_final_newline = true
\ No newline at end of file
+insert_final_newline = true
diff --git a/.gitignore b/.gitignore
index ec76856fd..f3eb67c8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,9 @@
www/*.html
-www/*/*.html
+www/**/*.html
www/css/*.css
www/js/index.js
+www/assets/challenges/descriptors
+www/components/**/**
.tmp
node_modules
npm-debug.log
@@ -9,4 +11,5 @@ npm-debug.log
**.sublime-workspace
*.pyc
*.swp
-dist
\ No newline at end of file
+dist
+bower_components
diff --git a/.jshintrc b/.jshintrc
index 4d5a2652d..67ad46989 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -1,8 +1,19 @@
{
"browser": true,
"esnext": true,
- "globals": { "angular": true },
- "globalstrict": false,
+ "globals": { "angular": true,
+ "describe": true,
+ "xdescribe": true,
+ "ddescribe": true,
+ "it": true,
+ "xit": true,
+ "iit": true,
+ "beforeEach": true,
+ "afterEach": true,
+ "expect": true,
+ "pending": true
+ },
+ "globalstrict": true,
"quotmark": false,
"undef": true,
"unused": true,
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 000000000..8b966d68e
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,55 @@
+#!groovy
+node {
+ stage('check environment') {
+ if (env.BRANCH_NAME=="master" || env.BRANCH_NAME=="jenkins") {
+ env.NODE_ENV = "staging"
+ } else if (env.BRANCH_NAME=="prod") {
+ env.NODE_ENV = "production"
+ }
+
+ if (env.NODE_ENV == "staging") {
+ load "/var/lib/jenkins/userContent/make-art-config/staging.env"
+ } else if (env.NODE_ENV == "production") {
+ load "/var/lib/jenkins/userContent/make-art-config/prod.env"
+ }
+ }
+
+ stage('checkout') {
+ checkout scm
+ }
+
+ stage('clean') {
+ sh "rm -rf node_modules"
+ }
+
+ stage('install dependencies') {
+ sh "npm install --ignore-scripts"
+ sh "bower i"
+ }
+
+ stage('build') {
+ sh "gulp default"
+ }
+
+ stage('compress') {
+ sh "gulp compress"
+ }
+
+ stage('deploy') {
+ if (env.BRANCH_NAME == "jenkins") {
+ echo 'deploy skipped'
+ } else if (env.NODE_ENV=="staging") {
+ deploy_staging()
+ } else if (env.NODE_ENV=="production") {
+ deploy_prod()
+ }
+ }
+}
+
+def deploy_staging() {
+ sh 'aws s3 sync ./www s3://art-staging.kano.me --region eu-west-1 --cache-control "max-age=600"'
+}
+
+def deploy_prod() {
+ sh 'aws s3 sync ./www s3://make-art-site-prod --region us-west-1 --cache-control "max-age=600"'
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..22fbe5dba
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ {signature of Ty Coon}, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
\ No newline at end of file
diff --git a/README.md b/README.md
index 6d5fbf95a..f67c84bd1 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ You can try the app [here »](http://art.kano.me/)
git clone git@github.com:KanoComputing/make-art.git
cd make-art
npm install
+ bower install
## Build
@@ -29,3 +30,24 @@ Open your browser at [http://localhost:3000](http://localhost:3000)
Run the watch script when developing
npm run watch
+
+## Offline
+
+To build in the offline mode for Kano OS add these ENV vars:
+
+ OFFLINE=true NODE_ENV=production gulp
+
+Since Node JS isn't included by default on the kit and the offline backend is
+[implemented in Python](/kano_draw/server.py), the easiest way to
+debug on the kit is to build the static assets on your machine and rsync them
+over to the kit as follows:
+
+ rsync -av make-art "user@ip:~/make-art"
+
+On Kano OS, go to the `bin/` directory and launch the `kano-draw` script
+which will start the server and open a
+[python-webkit](https://wiki.python.org/moin/PythonWebKit) browser with Make
+Art.
+
+ user@kano-os ~ $ cd ~/make-art/bin
+ user@kano-os ~ $ cd ./kano-draw
diff --git a/TRANSLATION.md b/TRANSLATION.md
new file mode 100644
index 000000000..9824ad12b
--- /dev/null
+++ b/TRANSLATION.md
@@ -0,0 +1,52 @@
+# Translation
+
+Translation files are found in the following directories:
+- `lib/challenges/locales`: translation of the challenges
+- `locales`: translation of the views
+- `content/docs.json`: translation of the documentation
+
+## i18n not released yet
+
+Kano OS is not fully i18n-aware and locales are not installed for end users, yet. You can translate this application, but as of now, users will still see the default English message strings.
+
+## Build
+
+Translations are part of the normal build process: when you type `npm run build`, the translated challenges and docs will be copied in the `www` directory; and the view templates will be localized by mapping the resources _for each language_. So it means that all resources (views, challenges and docs translations) are prepared in advance.
+
+## Runtime
+
+At runtime, the proper translation will be picked based on the browser language.
+
+## How to add a new translation
+
+You need to add the new language in 4 places:
+
+1. Create `lib/challenges/locales/` and copy the whole `worlds` directory and `index.json`
+ These are the translations of the challenges, the main content of Make-Art.
+
+2. Create new .po files for each locale - one from the `messages.pot` and one
+ from the `messages-doc.pot`. These are the translations of the views and
+ documentation.
+
+3. After translation run the `po/lang-po-to-json` and `po/lang-docs-po-to-json`
+ to create the necessary json files from the po files.
+
+4. Add your language to `SUPPORTED_LOCALES` array in `lib/i18n.js`
+
+## How to make sure your code is i18n-aware
+
+For the challenges, if you create a new challenge in English, there is nothing special to do, it can be translated to other languages by copying the .json file to the other locales directories.
+
+For the views (jade templates), you need to enclose all your strings in `${{` and `}}$` as this is the convention used by gulp-html-i18n plugin to find and replace the messages in the view templates. Then, you need to add the new string to the corresponding .json file in the (top-level) `locales` directory. Let's say you want to add a string "Happy Birthday" to the challenge.jade template; you would write it as follows in the challenge.jade: `${{ challenge.hapy_birthday }}$`. Then, you would add an entry to the challenge.json map, like:
+```
+{
+ ...
+ "happy_birthday": "Happy Birthday"
+}
+```
+
+Finally, for the documentation, if you add / modify text in `content/docs.json` the translations will need to be added for other languages, in the same file.
+
+## To-Do
+
+Tool to make it easier to add new languages.
diff --git a/bin/kano-draw b/bin/kano-draw
index 9523b1ed7..6828bdd6c 100755
--- a/bin/kano-draw
+++ b/bin/kano-draw
@@ -1,21 +1,23 @@
-#!/usr/bin/kano-splash /usr/share/kano-draw/media/splash.png /usr/bin/env python
+#!/usr/bin/kano-splash loader-animation /usr/bin/env python
# kano-draw
#
-# Copyright (C) 2014 Kano Computing Ltd.
-# License: http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
+# Copyright (C) 2014-2018 Kano Computing Ltd.
+# License: http://www.gnu.org/licenses/gpl-2.0.txt GNU GPL v2
#
# The Draw App implementation
#
# Before loading anything else, declare a profiling timepoint
-from kano.profiling import declare_timepoint
-declare_timepoint("load",True)
+# from kano.profiling import declare_timepoint
+# declare_timepoint("load", True)
import os
import sys
import multiprocessing
import signal
+import time
+import atexit
if __name__ == '__main__' and __package__ is None:
DIR_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
@@ -26,14 +28,56 @@ if __name__ == '__main__' and __package__ is None:
from kano_profile.tracker import Tracker
kanotracker = Tracker()
+from kano.utils import run_cmd
+from kano.logging import logger
from kano_draw.video import play_intro
-from kano_draw.draw import Draw
+from kano_draw.draw import start_draw
import kano_draw.server
+SERVER_PROC = None
+UI_PROC = None
+
+def _kill_subprocess(proc_handle):
+ if proc_handle is not None and proc_handle.is_alive():
+ proc_handle.terminate()
+ os.kill(proc_handle.pid, signal.SIGKILL)
+
+
+def _kill_lingering_processes():
+ """ Look for processes still attached to port 8000 whose name starts with
+ 'dbus-' and kill them
+ """
+ pid_no, dummy, dummy2 = run_cmd(
+ "lsof -i :8000 | grep 'dbus-' | awk '{ print $2 }'"
+ )
+ if pid_no:
+ pids = pid_no.splitlines()
+ for pid in pids:
+ try:
+ os.kill(int(pid), signal.SIGTERM)
+ except ValueError:
+ pass
+
+
+def _at_exit_hook():
+ """ Ensure that the two subprocesses are killed before this process exits
+ """
+ _kill_subprocess(UI_PROC)
+ # Give the server subprocess 10 secs to die
+ SERVER_PROC.join(5)
+ # Now kill it anyway
+ _kill_subprocess(SERVER_PROC)
+ # Cleanup the lingering processes (usually produced by omxplayer)
+ _kill_lingering_processes()
+
+
+# At this point cleaning up just exits. This is triggered once the server
+# (which is a child process) notifies us to exit
def cleanup(signum, frame):
sys.exit(0)
+atexit.register(_at_exit_hook)
signal.signal(signal.SIGINT, cleanup)
play_intro()
@@ -44,12 +88,33 @@ SERVER_PROC = multiprocessing.Process(target=kano_draw.server.start,
kwargs={
'parent_pid': APP_PID
})
-SERVER_PROC.daemon = True
SERVER_PROC.start()
# Init the web app
+kwargs = {}
if len(sys.argv) > 1:
- DRAW = Draw(load_path=sys.argv[1])
-else:
- DRAW = Draw()
-DRAW.run()
+ arg = sys.argv[1]
+
+ if arg == 'make':
+ kwargs['make'] = True
+ elif arg == 'play':
+ kwargs['play'] = True
+ else:
+ kwargs['load_path'] = arg
+
+UI_PROC = multiprocessing.Process(target=start_draw, kwargs=kwargs)
+
+UI_PROC.daemon = True
+UI_PROC.start()
+
+# This process is really a factory/watchdog process. It launches the UI and
+# server processes as children and makes sure to terminate both once they have
+# one of them has been terminated
+try:
+ while UI_PROC.is_alive() and SERVER_PROC.is_alive():
+ time.sleep(3)
+ sys.exit(0)
+except Exception as exc:
+ # the SIGINT handler has executed here
+ logger.error('Error while in the main waiting loop: [{}]'.format(exc))
+ sys.exit(0)
diff --git a/bower.json b/bower.json
new file mode 100644
index 000000000..5d9f7feec
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,28 @@
+{
+ "name": "make-art",
+ "description": "App to learn programming using a basic CoffeeScript drawing API",
+ "main": "lib/index.js",
+ "authors": [
+ "Kano Computing"
+ ],
+ "license": "MIT2",
+ "homepage": "http://art.kano.me/challenges",
+ "private": true,
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "dependencies": {
+ "web-components": "git@github.com:KanoComputing/web-components",
+ "iron-image": "PolymerElements/iron-image#^1.2.5"
+ },
+ "resolutions": {
+ "iron-image": "^2.1.2",
+ "iron-flex-layout": "^2.0.0",
+ "polymer": "1.9 - 2",
+ "webcomponentsjs": "^v1.0.19"
+ }
+}
diff --git a/content/docs.json b/content/docs.json
index 9493c2cb0..571d201a1 100644
--- a/content/docs.json
+++ b/content/docs.json
@@ -1,268 +1,1643 @@
-[
- {
- "label" : "Shapes",
- "icon" : "shapes",
- "commands" : [
- {
- "call" : "circle",
- "unlockedAt" : 1,
- "description" : "Draw a circle",
- "args" : [
- [ "radius", "number", "The circle size", true ]
- ],
- "defaults": [ 100 ]
- },
- {
- "call" : "ellipse",
- "unlockedAt" : 999,
- "description" : "Draw an ellipse",
- "args" : [
- [ "radius-x", "number", "The ellipse width", true ],
- [ "radius-y", "number", "The ellipse height", true ]
- ],
- "defaults": [ 50, 100 ]
- },
- {
- "call" : "square",
- "unlockedAt" : 2,
- "description" : "Draw a square",
- "args" : [
- [ "size", "number", "The square size", true ]
- ],
- "defaults": [ 200 ]
- },
- {
- "call" : "rectangle",
- "unlockedAt" : 999,
- "description" : "Draw a rectangle",
- "args" : [
- [ "width", "number", "The rectangle width", true ],
- [ "height", "number", "The rectangle height", true ]
- ],
- "defaults": [ 100, 200 ]
- },
- {
- "call" : "arc",
- "unlockedAt" : 999,
- "description" : "Draw part of a circle",
- "args" : [
- [ "radius", "number", "The size", true ],
- [ "start", "number", "The start point (between 0 to 2)", true ],
- [ "end", "number", "The end point (between 0 to 2)", true ],
- [ "close", "bool", "Close the shape", false ]
- ],
- "defaults": [ 100, 1, 2, true ]
- },
- {
- "call" : "polygon",
- "unlockedAt" : 999,
- "description" : "Draw a many sided shape",
- "args" : [
- [ "x1", "number", "Point X position", true ],
- [ "y1", "number", "Point Y position", true ],
- [ "...", "", "More polygon points positions", true ],
- [ "close", "bool", "Close the path in the middle", false ]
- ],
- "defaults": [ 0, 0, 100, 0, 100, 100 ]
- }
- ]
- },
- {
- "label" : "Lines",
- "icon" : "lines",
- "commands" : [
- {
- "call" : "line",
- "unlockedAt" : 3,
- "description" : "Draw a line of a certain size",
- "args" : [
- [ "x", "number", "Horizontal distance", true ],
- [ "y", "number", "Vertical distance", false ]
- ],
- "defaults": [ 100, 50 ]
- },
- {
- "call" : "lineTo",
- "unlockedAt" : 999,
- "description" : "Draw a line to a certain point",
- "args" : [
- [ "x", "number", "The horizontal destination", true ],
- [ "y", "number", "The vertical destination", true ]
- ],
- "defaults": [ 0, 0 ]
- }
- ]
- },
- {
- "label" : "Position",
- "icon" : "position",
- "commands" : [
- {
- "call" : "move",
- "unlockedAt" : 6,
- "description" : "Move the cursor a certain distance away",
- "args" : [
- [ "x", "number", "Horizontal distance", true ],
- [ "y", "number", "Vertical distance", false ]
- ],
- "defaults": [ 100, 50 ]
- },
- {
- "call" : "moveTo",
- "unlockedAt" : 999,
- "description" : "Move the cursor to a particular position",
- "args" : [
- [ "x", "number", "The horizontal destination", true ],
- [ "y", "number", "The vertical destination", true ]
- ],
- "defaults": [ "center", "center" ]
- }
- ]
- },
- {
- "label" : "Text",
- "icon" : "text",
- "commands" : [
- {
- "call" : "text",
- "unlockedAt" : 999,
- "description" : "Write text",
- "args" : [
- [ "message", "string", "The message to write", true ]
- ],
- "defaults": [ "Say something!" ]
- },
- {
- "call" : "font",
- "unlockedAt" : 999,
- "description" : "Set size and/or font",
- "args" : [
- [ "font", "string", "The font family name", false ],
- [ "size", "number", "The text size in pixels", false ]
- ],
- "defaults": [ "Bariol", 30 ]
- },
- {
- "call" : "bold",
- "unlockedAt" : 999,
- "description" : "Sets bold text on (true) or off (false)",
- "args" : [
- [ "state", "bool", "Bold state (True by default)", false ]
- ],
- "defaults": [ true ]
- },
- {
- "call" : "italic",
- "unlockedAt" : 999,
- "description" : "Sets italic text on (true) or off (false)",
- "args" : [
- [ "state", "bool", "Italic state (True by default)", false ]
- ],
- "defaults": [ true ]
- }
- ]
- },
- {
- "label" : "General",
- "icon" : "general",
- "commands" : [
- {
- "call" : "for",
- "unlockedAt" : 9,
- "description" : "Repeat code",
- "args" : [
- [ "i in [x..y]", "number", "The start and end point for the variable i", true ]
- ],
- "example" : "for i in [1..5]\n circle i",
- "defaults": null
- },
- {
- "call" : "random",
- "unlockedAt" : 9,
- "description" : "Get a random number in a range.",
- "args" : [
- [ "min", "number", "The minimum value", true ],
- [ "max", "number", "The maximum value", true ],
- [ "float", "bool", "Set to true to return decimal values", false ]
- ],
- "example" : "random 5, 10",
- "defaults": [ 5, 10 ]
- }
- ]
- },
- {
- "label" : "Colors",
- "icon" : "colors",
- "commands" : [
- {
- "call" : "background",
- "unlockedAt" : 999,
- "description" : "Set the background color",
- "args" : [
- [ "color", "string", "The background color to set", true ]
- ],
- "defaults": [ "blue" ]
- },
- {
- "call" : "color",
- "unlockedAt" : 4,
- "description" : "Change the color in use",
- "args" : [
- [ "color", "string", "The object color to set", true ]
- ],
- "defaults": [ "red" ]
- },
- {
- "call" : "stroke",
- "unlockedAt" : 5,
- "description" : "Change the width and color of the stroke (border)",
- "args" : [
- [ "color", "string", "A string with the color to use - E.g. 'red', 'blue'..", false ],
- [ "size", "number", "(number) The width to use", false ]
- ],
- "defaults": [ 10, "purple" ]
- },
- {
- "call" : "setBrightness",
- "unlockedAt" : 999,
- "description" : "Set a colour's brightness",
- "args" : [
- [ "color", "string", "Color", true ],
- [ "amount", "number", "amount of brightness to set (-100 to 100)", false ]
- ],
- "defaults": [ "yellow", 30 ]
- },
- {
- "call" : "setSaturation",
- "unlockedAt" : 999,
- "description" : "Set a colour's saturation",
- "args" : [
- [ "color", "string", "Color", true ],
- [ "amount", "number", "amount of saturation to set (-100 to 100)", false ]
- ],
- "defaults": [ "grey", 30 ]
- },
- {
- "call" : "rotate",
- "unlockedAt" : 999,
- "description" : "Rotate a color's hue angle by given amount",
- "args" : [
- [ "color", "string", "Color to rotate", true ],
- [ "amount", "number", "angle to rotate (-360 - 360)", true ]
- ],
- "defaults": [ "red", 100 ]
- },
- {
- "call" : "setTransparency",
- "unlockedAt" : 999,
- "description" : "Set how see through a color is",
- "args" : [
- [ "color", "string", "Color to saturate", true ],
- [ "amount", "number", "amount of opacity to subtract (-100 to 100)", true ]
- ],
- "defaults": [ "black", 50 ]
- }
-
- ]
- }
-]
+{
+ "es-AR": [
+ {
+ "commands": [
+ {
+ "unlockedAt": 1,
+ "args": [
+ [
+ "radius",
+ "number",
+ "Tamaño del círculo",
+ true
+ ]
+ ],
+ "call": "circle",
+ "description": "Dibuja un círculo",
+ "defaults": [
+ 100
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "radius-x",
+ "number",
+ "Ancho de la elipse",
+ true
+ ],
+ [
+ "radius-y",
+ "number",
+ "Altura de la elipse",
+ true
+ ]
+ ],
+ "call": "ellipse",
+ "description": "Dibuja una elipse",
+ "defaults": [
+ 50,
+ 100
+ ]
+ },
+ {
+ "unlockedAt": 2,
+ "args": [
+ [
+ "size",
+ "number",
+ "Tamaño del cuadrado",
+ true
+ ]
+ ],
+ "call": "square",
+ "description": "Dibuja un cuadrado",
+ "defaults": [
+ 200
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "width",
+ "number",
+ "Ancho del rectángulo",
+ true
+ ],
+ [
+ "height",
+ "number",
+ "Altura del rectángulo",
+ true
+ ]
+ ],
+ "call": "rectangle",
+ "description": "Dibuja un rectángulo",
+ "defaults": [
+ 100,
+ 200
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "radius",
+ "number",
+ "Tamaño",
+ true
+ ],
+ [
+ "start",
+ "number",
+ "El punto de inicio (entre 0 y 2)",
+ true
+ ],
+ [
+ "end",
+ "number",
+ "El punto de fin (entre 0 y 2)",
+ true
+ ],
+ [
+ "close",
+ "bool",
+ "Cierra la figura",
+ false
+ ]
+ ],
+ "call": "arc",
+ "description": "Dibuja parte de un círculo",
+ "defaults": [
+ 100,
+ 1,
+ 2,
+ true
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "x1",
+ "number",
+ "Indica la posición X",
+ true
+ ],
+ [
+ "y1",
+ "number",
+ "Indica la posición Y",
+ true
+ ],
+ [
+ "...",
+ "",
+ "Más puntos de posición del polígono",
+ true
+ ],
+ [
+ "close",
+ "bool",
+ "Cierra la figura en el medio",
+ false
+ ]
+ ],
+ "call": "polygon",
+ "description": "Dibuja una figura de muchos lados",
+ "defaults": [
+ 0,
+ 0,
+ 100,
+ 0,
+ 100,
+ 100
+ ]
+ }
+ ],
+ "icon": "shapes",
+ "label": "Figuras"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 3,
+ "args": [
+ [
+ "x",
+ "number",
+ "Distancia horizontal",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "Distancia vertical",
+ false
+ ]
+ ],
+ "call": "line",
+ "description": "Dibuja una línea de un determinado tamaño",
+ "defaults": [
+ 100,
+ 50
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "x",
+ "number",
+ "El punto de destino horizontal",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "El punto de destino vertical",
+ true
+ ]
+ ],
+ "call": "lineTo",
+ "description": "Dibuja una línea hasta un determinado punto",
+ "defaults": [
+ 0,
+ 0
+ ]
+ }
+ ],
+ "icon": "lines",
+ "label": "Líneas"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 6,
+ "args": [
+ [
+ "x",
+ "number",
+ "Distancia horizontal",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "Distancia vertical",
+ false
+ ]
+ ],
+ "call": "move",
+ "description": "Mueve el cursor una determinada distancia",
+ "defaults": [
+ 100,
+ 50
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "x",
+ "number",
+ "El punto de destino horizontal",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "El punto de destino vertical",
+ true
+ ]
+ ],
+ "call": "moveTo",
+ "description": "Mueve el cursor a una determinada posición",
+ "defaults": [
+ "center",
+ "center"
+ ]
+ }
+ ],
+ "icon": "position",
+ "label": "Posición"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "message",
+ "string",
+ "El mensaje a escribir",
+ true
+ ]
+ ],
+ "call": "text",
+ "description": "Escribe texto",
+ "defaults": [
+ "Say something!"
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "font",
+ "string",
+ "El nombre de la fuente",
+ false
+ ],
+ [
+ "size",
+ "number",
+ "El tamaño del texto en píxeles",
+ false
+ ]
+ ],
+ "call": "font",
+ "description": "Configura el tamaño y/o la fuente",
+ "defaults": [
+ "Bariol",
+ 30
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "state",
+ "bool",
+ "Estado de la negrita (Verdadero es el predeterminado)",
+ false
+ ]
+ ],
+ "call": "bold",
+ "description": "Activa la negrita (verdadero) o desactívala (falso)",
+ "defaults": [
+ true
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "state",
+ "bool",
+ "Estado de la cursiva (Verdadero es el predeterminado)",
+ false
+ ]
+ ],
+ "call": "italic",
+ "description": "Activa la cursiva (verdadero) o desactívala (falso)",
+ "defaults": [
+ true
+ ]
+ }
+ ],
+ "icon": "text",
+ "label": "Texto"
+ },
+ {
+ "commands": [
+ {
+ "description": "Repetir código",
+ "args": [
+ [
+ "i in [x..y]",
+ "number",
+ "Punto de inicio y fin de la variable i",
+ true
+ ]
+ ],
+ "call": "for",
+ "defaults": null,
+ "unlockedAt": 9,
+ "example": "for i in [1..5]\n circle i"
+ },
+ {
+ "description": "Obtén un número aleatorio dentro de un rango.",
+ "args": [
+ [
+ "min",
+ "number",
+ "El valor mínimo",
+ true
+ ],
+ [
+ "max",
+ "number",
+ "El valor máximo",
+ true
+ ],
+ [
+ "float",
+ "bool",
+ "Configura a verdadero para recibir valores decimales",
+ false
+ ]
+ ],
+ "call": "random",
+ "defaults": [
+ 5,
+ 10
+ ],
+ "unlockedAt": 9,
+ "example": "random 5, 10"
+ }
+ ],
+ "icon": "general",
+ "label": "General"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "El color del fondo a configurar",
+ true
+ ]
+ ],
+ "call": "background",
+ "description": "Configura el color del fondo",
+ "defaults": [
+ "blue"
+ ]
+ },
+ {
+ "unlockedAt": 4,
+ "args": [
+ [
+ "color",
+ "string",
+ "El color del objeto a configurar",
+ true
+ ]
+ ],
+ "call": "color",
+ "description": "Cambia el color en uso",
+ "defaults": [
+ "red"
+ ]
+ },
+ {
+ "unlockedAt": 5,
+ "args": [
+ [
+ "color",
+ "string",
+ "El color a usar - Ej. 'red', 'blue'..",
+ false
+ ],
+ [
+ "size",
+ "number",
+ "(número) El ancho a usar",
+ false
+ ]
+ ],
+ "call": "stroke",
+ "description": "Cambia el color y el ancho del trazo (borde)",
+ "defaults": [
+ 10,
+ "purple"
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "Color",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "monto del brillo a configurar (-100 a 100)",
+ false
+ ]
+ ],
+ "call": "setBrightness",
+ "description": "Configura el brillo del color",
+ "defaults": [
+ "yellow",
+ 30
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "Color",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "monto de saturación a configurar (-100 a 100)",
+ false
+ ]
+ ],
+ "call": "setSaturation",
+ "description": "Configura la saturación del color",
+ "defaults": [
+ "grey",
+ 30
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "Color a cambiar",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "tono a configurar (-360 - 360)",
+ true
+ ]
+ ],
+ "call": "rotate",
+ "description": "Cambia el tono de un color en base a un determinado valor",
+ "defaults": [
+ "red",
+ 100
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "Color a saturar",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "monto de opacidad (-100 a 100)",
+ true
+ ]
+ ],
+ "call": "setTransparency",
+ "description": "Configura cómo se ve a través de un color",
+ "defaults": [
+ "black",
+ 50
+ ]
+ }
+ ],
+ "icon": "colors",
+ "label": "Colores"
+ }
+ ],
+ "en": [
+ {
+ "commands": [
+ {
+ "unlockedAt": 1,
+ "args": [
+ [
+ "radius",
+ "number",
+ "The circle size",
+ true
+ ]
+ ],
+ "call": "circle",
+ "description": "Draw a circle",
+ "defaults": [
+ 100
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "radius-x",
+ "number",
+ "The ellipse width",
+ true
+ ],
+ [
+ "radius-y",
+ "number",
+ "The ellipse height",
+ true
+ ]
+ ],
+ "call": "ellipse",
+ "description": "Draw an ellipse",
+ "defaults": [
+ 50,
+ 100
+ ]
+ },
+ {
+ "unlockedAt": 2,
+ "args": [
+ [
+ "size",
+ "number",
+ "The square size",
+ true
+ ]
+ ],
+ "call": "square",
+ "description": "Draw a square",
+ "defaults": [
+ 200
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "width",
+ "number",
+ "The rectangle width",
+ true
+ ],
+ [
+ "height",
+ "number",
+ "The rectangle height",
+ true
+ ]
+ ],
+ "call": "rectangle",
+ "description": "Draw a rectangle",
+ "defaults": [
+ 100,
+ 200
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "radius",
+ "number",
+ "The size",
+ true
+ ],
+ [
+ "start",
+ "number",
+ "The start point (between 0 to 2)",
+ true
+ ],
+ [
+ "end",
+ "number",
+ "The end point (between 0 to 2)",
+ true
+ ],
+ [
+ "close",
+ "bool",
+ "Close the shape",
+ false
+ ]
+ ],
+ "call": "arc",
+ "description": "Draw part of a circle",
+ "defaults": [
+ 100,
+ 1,
+ 2,
+ true
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "x1",
+ "number",
+ "Point X position",
+ true
+ ],
+ [
+ "y1",
+ "number",
+ "Point Y position",
+ true
+ ],
+ [
+ "...",
+ "",
+ "More polygon points positions",
+ true
+ ],
+ [
+ "close",
+ "bool",
+ "Close the path in the middle",
+ false
+ ]
+ ],
+ "call": "polygon",
+ "description": "Draw a many sided shape",
+ "defaults": [
+ 0,
+ 0,
+ 100,
+ 0,
+ 100,
+ 100
+ ]
+ }
+ ],
+ "label": "Shapes",
+ "icon": "shapes"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 3,
+ "args": [
+ [
+ "x",
+ "number",
+ "Horizontal distance",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "Vertical distance",
+ false
+ ]
+ ],
+ "call": "line",
+ "description": "Draw a line of a certain size",
+ "defaults": [
+ 100,
+ 50
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "x",
+ "number",
+ "The horizontal destination",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "The vertical destination",
+ true
+ ]
+ ],
+ "call": "lineTo",
+ "description": "Draw a line to a certain point",
+ "defaults": [
+ 0,
+ 0
+ ]
+ }
+ ],
+ "label": "Lines",
+ "icon": "lines"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 6,
+ "args": [
+ [
+ "x",
+ "number",
+ "Horizontal distance",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "Vertical distance",
+ false
+ ]
+ ],
+ "call": "move",
+ "description": "Move the cursor a certain distance away",
+ "defaults": [
+ 100,
+ 50
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "x",
+ "number",
+ "The horizontal destination",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "The vertical destination",
+ true
+ ]
+ ],
+ "call": "moveTo",
+ "description": "Move the cursor to a particular position",
+ "defaults": [
+ "center",
+ "center"
+ ]
+ }
+ ],
+ "label": "Position",
+ "icon": "position"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "message",
+ "string",
+ "The message to write",
+ true
+ ]
+ ],
+ "call": "text",
+ "description": "Write text",
+ "defaults": [
+ "Say something!"
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "font",
+ "string",
+ "The font family name",
+ false
+ ],
+ [
+ "size",
+ "number",
+ "The text size in pixels",
+ false
+ ]
+ ],
+ "call": "font",
+ "description": "Set size and/or font",
+ "defaults": [
+ "Bariol",
+ 30
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "state",
+ "bool",
+ "Bold state (True by default)",
+ false
+ ]
+ ],
+ "call": "bold",
+ "description": "Sets bold text on (true) or off (false)",
+ "defaults": [
+ true
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "state",
+ "bool",
+ "Italic state (True by default)",
+ false
+ ]
+ ],
+ "call": "italic",
+ "description": "Sets italic text on (true) or off (false)",
+ "defaults": [
+ true
+ ]
+ }
+ ],
+ "label": "Text",
+ "icon": "text"
+ },
+ {
+ "commands": [
+ {
+ "description": "Repeat code",
+ "args": [
+ [
+ "i in [x..y]",
+ "number",
+ "The start and end point for the variable i",
+ true
+ ]
+ ],
+ "call": "for",
+ "defaults": null,
+ "unlockedAt": 9,
+ "example": "for i in [1..5]\n circle i"
+ },
+ {
+ "description": "Get a random number in a range.",
+ "args": [
+ [
+ "min",
+ "number",
+ "The minimum value",
+ true
+ ],
+ [
+ "max",
+ "number",
+ "The maximum value",
+ true
+ ],
+ [
+ "float",
+ "bool",
+ "Set to true to return decimal values",
+ false
+ ]
+ ],
+ "call": "random",
+ "defaults": [
+ 5,
+ 10
+ ],
+ "unlockedAt": 9,
+ "example": "random 5, 10"
+ }
+ ],
+ "label": "General",
+ "icon": "general"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "The background color to set",
+ true
+ ]
+ ],
+ "call": "background",
+ "description": "Set the background color",
+ "defaults": [
+ "blue"
+ ]
+ },
+ {
+ "unlockedAt": 4,
+ "args": [
+ [
+ "color",
+ "string",
+ "The object color to set",
+ true
+ ]
+ ],
+ "call": "color",
+ "description": "Change the color in use",
+ "defaults": [
+ "red"
+ ]
+ },
+ {
+ "unlockedAt": 5,
+ "args": [
+ [
+ "color",
+ "string",
+ "A string with the color to use - E.g. 'red', 'blue'..",
+ false
+ ],
+ [
+ "size",
+ "number",
+ "(number) The width to use",
+ false
+ ]
+ ],
+ "call": "stroke",
+ "description": "Change the width and color of the stroke (border)",
+ "defaults": [
+ 10,
+ "purple"
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "Color",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "amount of brightness to set (-100 to 100)",
+ false
+ ]
+ ],
+ "call": "setBrightness",
+ "description": "Set a colour's brightness",
+ "defaults": [
+ "yellow",
+ 30
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "Color",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "amount of saturation to set (-100 to 100)",
+ false
+ ]
+ ],
+ "call": "setSaturation",
+ "description": "Set a colour's saturation",
+ "defaults": [
+ "grey",
+ 30
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "Color to rotate",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "angle to rotate (-360 - 360)",
+ true
+ ]
+ ],
+ "call": "rotate",
+ "description": "Rotate a color's hue angle by given amount",
+ "defaults": [
+ "red",
+ 100
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "Color to saturate",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "amount of opacity to subtract (-100 to 100)",
+ true
+ ]
+ ],
+ "call": "setTransparency",
+ "description": "Set how see through a color is",
+ "defaults": [
+ "black",
+ 50
+ ]
+ }
+ ],
+ "label": "Colors",
+ "icon": "colors"
+ }
+ ],
+ "ja": [
+ {
+ "commands": [
+ {
+ "unlockedAt": 1,
+ "args": [
+ [
+ "radius",
+ "number",
+ "円形の大きさ(半径)",
+ true
+ ]
+ ],
+ "call": "circle",
+ "description": "円形を描く",
+ "defaults": [
+ 100
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "radius-x",
+ "number",
+ "楕円の横幅",
+ true
+ ],
+ [
+ "radius-y",
+ "number",
+ "楕円の高さ",
+ true
+ ]
+ ],
+ "call": "ellipse",
+ "description": "楕円を描く",
+ "defaults": [
+ 50,
+ 100
+ ]
+ },
+ {
+ "unlockedAt": 2,
+ "args": [
+ [
+ "size",
+ "number",
+ "四角の大きさ",
+ true
+ ]
+ ],
+ "call": "square",
+ "description": "四角を描く",
+ "defaults": [
+ 200
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "width",
+ "number",
+ "長方形の幅",
+ true
+ ],
+ [
+ "height",
+ "number",
+ "長方形の高さ",
+ true
+ ]
+ ],
+ "call": "rectangle",
+ "description": "長方形を描く",
+ "defaults": [
+ 100,
+ 200
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "radius",
+ "number",
+ "大きさ(半径)",
+ true
+ ],
+ [
+ "start",
+ "number",
+ "開始点(0〜2)",
+ true
+ ],
+ [
+ "end",
+ "number",
+ "最後の点(0〜2)",
+ true
+ ],
+ [
+ "close",
+ "bool",
+ "形を閉じるかどうか",
+ false
+ ]
+ ],
+ "call": "arc",
+ "description": "円形の一部を描く",
+ "defaults": [
+ 100,
+ 1,
+ 2,
+ true
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "x1",
+ "number",
+ "ー点目のX値",
+ true
+ ],
+ [
+ "y1",
+ "number",
+ "ー点目のY値",
+ true
+ ],
+ [
+ "...",
+ "",
+ "他の点の位置",
+ true
+ ],
+ [
+ "close",
+ "bool",
+ "パスを真ん中に閉じるかどうか",
+ false
+ ]
+ ],
+ "call": "polygon",
+ "description": "多辺形を描く",
+ "defaults": [
+ 0,
+ 0,
+ 100,
+ 0,
+ 100,
+ 100
+ ]
+ }
+ ],
+ "label": "形",
+ "icon": "shapes"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 3,
+ "args": [
+ [
+ "x",
+ "number",
+ "水平距離",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "垂直距離",
+ false
+ ]
+ ],
+ "call": "line",
+ "description": "ある大きさの線を描く",
+ "defaults": [
+ 100,
+ 50
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "x",
+ "number",
+ "水平の目的点",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "垂直の目的点",
+ true
+ ]
+ ],
+ "call": "lineTo",
+ "description": "ある点までに線を描く",
+ "defaults": [
+ 0,
+ 0
+ ]
+ }
+ ],
+ "label": "線",
+ "icon": "lines"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 6,
+ "args": [
+ [
+ "x",
+ "number",
+ "水平距離",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "垂直距離",
+ false
+ ]
+ ],
+ "call": "move",
+ "description": "カーソルをある距離に動かす",
+ "defaults": [
+ 100,
+ 50
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "x",
+ "number",
+ "水平目的点",
+ true
+ ],
+ [
+ "y",
+ "number",
+ "垂直目的点",
+ true
+ ]
+ ],
+ "call": "moveTo",
+ "description": "カーソルをある位置までに動かす",
+ "defaults": [
+ "center",
+ "center"
+ ]
+ }
+ ],
+ "label": "位置",
+ "icon": "position"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "message",
+ "string",
+ "メッセージ",
+ true
+ ]
+ ],
+ "call": "text",
+ "description": "文字を書く",
+ "defaults": [
+ "何か言って!"
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "font",
+ "string",
+ "フォントファミリー名",
+ false
+ ],
+ [
+ "size",
+ "number",
+ "ピクセルでのフォントの大きさ",
+ false
+ ]
+ ],
+ "call": "font",
+ "description": "フォントや大きさを設定",
+ "defaults": [
+ "Bariol",
+ 30
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "state",
+ "bool",
+ "太字の状態(デフォルトはtrue)",
+ false
+ ]
+ ],
+ "call": "bold",
+ "description": "太字を有効(true)または無効(false)",
+ "defaults": [
+ true
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "state",
+ "bool",
+ "車体の状態(デフォルトはtrue)",
+ false
+ ]
+ ],
+ "call": "italic",
+ "description": "車体を有効(true)または無効(false)",
+ "defaults": [
+ true
+ ]
+ }
+ ],
+ "label": "テキスト",
+ "icon": "text"
+ },
+ {
+ "commands": [
+ {
+ "description": "コードを繰り返す",
+ "args": [
+ [
+ "i in [x..y]",
+ "number",
+ "i変数の最初と最後の値",
+ true
+ ]
+ ],
+ "call": "for",
+ "defaults": null,
+ "unlockedAt": 9,
+ "example": "for i in [1..5]\n circle i"
+ },
+ {
+ "description": "ある範囲内でランダムな数字を生成する",
+ "args": [
+ [
+ "min",
+ "number",
+ "最低値",
+ true
+ ],
+ [
+ "max",
+ "number",
+ "最高地",
+ true
+ ],
+ [
+ "float",
+ "bool",
+ "trueにすると少数が得られる",
+ false
+ ]
+ ],
+ "call": "random",
+ "defaults": [
+ 5,
+ 10
+ ],
+ "unlockedAt": 9,
+ "example": "random 5, 10"
+ }
+ ],
+ "label": "一般",
+ "icon": "general"
+ },
+ {
+ "commands": [
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "設定したい背景の色",
+ true
+ ]
+ ],
+ "call": "background",
+ "description": "背景の色を設定する",
+ "defaults": [
+ "blue"
+ ]
+ },
+ {
+ "unlockedAt": 4,
+ "args": [
+ [
+ "color",
+ "string",
+ "設定したい鉛筆の色",
+ true
+ ]
+ ],
+ "call": "color",
+ "description": "鉛筆の色を変える",
+ "defaults": [
+ "red"
+ ]
+ },
+ {
+ "unlockedAt": 5,
+ "args": [
+ [
+ "color",
+ "string",
+ "設定したい色。例えば'red', 'blue'..",
+ false
+ ],
+ [
+ "size",
+ "number",
+ "鉛筆の太さ",
+ false
+ ]
+ ],
+ "call": "stroke",
+ "description": "筆の太さと色を変える",
+ "defaults": [
+ 10,
+ "purple"
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "色",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "設定したい明るさ(-100〜100)",
+ false
+ ]
+ ],
+ "call": "setBrightness",
+ "description": "色の明るさを設定する",
+ "defaults": [
+ "yellow",
+ 30
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "色",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "設定したい飽和(-100〜100)",
+ false
+ ]
+ ],
+ "call": "setSaturation",
+ "description": "色の飽和を設定する",
+ "defaults": [
+ "grey",
+ 30
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "変えたい色",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "回転の角度(-360〜360)",
+ true
+ ]
+ ],
+ "call": "rotate",
+ "description": "色相を回転させる(変更する)",
+ "defaults": [
+ "red",
+ 100
+ ]
+ },
+ {
+ "unlockedAt": 999,
+ "args": [
+ [
+ "color",
+ "string",
+ "色",
+ true
+ ],
+ [
+ "amount",
+ "number",
+ "引き算する不透明度(-100〜100)",
+ true
+ ]
+ ],
+ "call": "setTransparency",
+ "description": "色の透明度を設定する",
+ "defaults": [
+ "black",
+ 50
+ ]
+ }
+ ],
+ "label": "色",
+ "icon": "colors"
+ }
+ ]
+}
diff --git a/debian/changelog b/debian/changelog
index b820c62be..46a34ed19 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,46 @@
+kano-draw (3.15.0-0) unstable; urgency=low
+
+ * Small challenge copy fixes
+ * Disabled kano.profiling calls
+
+ -- Team Kano Wed, 31 Jan 2018 15:37:00 +0100
+
+kano-draw (3.14.0-0) unstable; urgency=low
+
+ * Applied changes from v3.9.2 es_AR backports to latest
+ * Added Loading animation splash window
+ * Fixed returning from shares when launched through challenge pack pages
+
+ -- Team Kano Mon, 4 Dec 2017 10:50:00 +0100
+
+kano-draw (3.11.0-1) unstable; urgency=low
+
+ * Bump version for OS 3.11.0 release
+
+ -- Team Kano Tue, 25 Jul 2017 15:35:00 +0100
+
+kano-draw (3.9.2-0) unstable; urgency=low
+
+ * Initial es_AR customer release
+ * Updated es_AR translations of nearly all challenges (backport)
+
+ -- Team Kano Mon, 16 Oct 2017 11:08:00 +0100
+
+kano-draw (2.3.0-1) unstable; urgency=low
+
+ * Introduce Pixel Hack challenge group
+ * Use latest version of kano-toolset
+
+ -- Team Kano Fri, 29 Jan 2016 11:00:00 +0100
+
+kano-draw (2.2.0-1) unstable; urgency=low
+
+ * Introduce Art Worlds for grouping challenges
+
+ -- Team Kano Wed, 14 Oct 2015 00:23:58 +0100
+
kano-draw (1.0-1) UNRELEASED; urgency=low
- * Initial release. (Closes: #XXXXXX)
+ * Initial release.
- -- Alex Wed, 16 Apr 2014 17:40:58 +0100
+ -- Alex Wed, 16 Apr 2014 17:40:58 +0100
diff --git a/debian/control b/debian/control
index 3a77da6b4..6fcbfb8a3 100755
--- a/debian/control
+++ b/debian/control
@@ -3,11 +3,17 @@ Maintainer: Team Kano
Section: misc
Priority: optional
Standards-Version: 3.9.4
-Build-Depends: debhelper (>= 9)
+Build-Depends: debhelper (>= 9), python, python-polib, python-docopt, nodejs
Package: kano-draw
Architecture: all
-Depends: ${shlibs:Depends}, ${misc:Depends}, kano-toolset (>= 1.3-8),
+Depends: ${shlibs:Depends}, ${misc:Depends}, kano-toolset (>= 2.3.0-5),
kano-profile (>= 1.3-2), python-flask
Suggests: kano-video-files (>= 1.2-1)
Description: Draw with code
+ An app for learning programming using a basic CoffeeScript drawing API
+
+Package: kano-draw-i18n-orig
+Architecture: all
+Description: Data for working on translations of kano-draw
+Multi-Arch: foreign
diff --git a/debian/copyright b/debian/copyright
index 54d8baa5d..f37e92d73 100755
--- a/debian/copyright
+++ b/debian/copyright
@@ -4,4 +4,4 @@ Source: https://github.com/KanoComputing/kano-draw
Files: *
Copyright: 2014,2015 Kano Computing Ltd.
-License: GPL-2+
+License: GPL-2+ On Debian systems the complete text of the GPL is in /usr/share/common-licenses/GPL-2
diff --git a/debian/kano-draw-i18n-orig.install b/debian/kano-draw-i18n-orig.install
new file mode 100644
index 000000000..dce2cf02b
--- /dev/null
+++ b/debian/kano-draw-i18n-orig.install
@@ -0,0 +1,2 @@
+locales/messages.pot /mnt/translations/translations/kano-draw/
+locales/messages-docs.pot /mnt/translations/translations/kano-draw/
diff --git a/debian/kano-draw.install b/debian/kano-draw.install
index 3fca6ac3b..9261a9a22 100644
--- a/debian/kano-draw.install
+++ b/debian/kano-draw.install
@@ -5,7 +5,10 @@ www/* /usr/share/kano-draw
kdesktop/Draw.lnk usr/share/kano-desktop/kdesk/kdesktop/
kdesktop/kano-draw.xml /usr/share/mime/packages/
icon/kano-draw.png usr/share/icons/Kano/88x88/apps
+icon/locale/es_AR/*.app /usr/share/applications/locale/es_AR/
icon/application-x-kano-draw.png usr/share/icons/Kano/88x88/mimetypes
icon/kano-draw-hover.png usr/share/icons/Kano/88x88/apps
icon/kano-draw.app usr/share/applications
media /usr/share/kano-draw/
+
+kano-content-blacklist/kano-draw.json /usr/share/kano-content/blacklists
diff --git a/debian/kano-draw.lintian-overrides b/debian/kano-draw.lintian-overrides
new file mode 100644
index 000000000..6804a5cdc
--- /dev/null
+++ b/debian/kano-draw.lintian-overrides
@@ -0,0 +1,2 @@
+kano-draw binary: privacy-breach-facebook
+kano-draw binary: privacy-breach-twitter
diff --git a/debian/rules b/debian/rules
index ba1fdb3a2..93d8ad88b 100755
--- a/debian/rules
+++ b/debian/rules
@@ -5,17 +5,15 @@
override_dh_auto_build:
# NodeJS and npm
- mkdir -p nodejs
- cd ./nodejs
- NODE_DIR=`pwd`
- curl http://nodejs.org/dist/node-latest.tar.gz | tar xz --strip-components=1
- ./configure --prefix=.
- make
- ln -sf ./deps/npm/bin/npm-cli.js ./npm
- cd ..
- # See about actually doing the install
- ls ./nodejs
- PATH=$(NODE_DIR):$(PATH) npm install
- PATH=$(NODE_DIR):$(PATH) NODE_ENV=production OFFLINE=true npm run build
+ cd po && ./lang-extract-doc-strings
+ cd po && ./lang-extract-strings
+ # Add NPM repository and install Node
+ curl --silent --location https://deb.nodesource.com/setup_6.x | bash -
+ apt-get install --yes nodejs
+ # Actually build
+ npm install
+ ./node_modules/bower/bin/bower --allow-root install
+ NODE_ENV=production OFFLINE=true npm run build
+ dh_lintian
override_dh_auto_install override_dh_auto_test:
diff --git a/dump.rdb b/dump.rdb
new file mode 100644
index 000000000..56af04ea1
--- /dev/null
+++ b/dump.rdb
@@ -0,0 +1 @@
+REDIS0006�ܳC�Z��V
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
index f525795f7..1ab9ecba7 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,3 +1,4 @@
+"use strict";
var gulp = require('gulp'),
jade = require('gulp-jade'),
browserify = require('gulp-browserify'),
@@ -8,97 +9,277 @@ var gulp = require('gulp'),
jadeHelpers = require('./utils/jadeHelpers'),
_ = require('lodash'),
color = require('cli-color'),
+ uglify = require('gulp-uglify'),
+ ngAnnotate = require('gulp-ng-annotate'),
partialify = require('partialify/custom'),
griddy = require('griddy'),
- nib = require('nib');
-
-var server = lr(),
+ nib = require('nib'),
+ fs = require('fs'),
+ i18n = require('gulp-html-i18n'),
+ del = require('del'),
+ server = lr(),
env = process.env.NODE_ENV || 'development',
production = env === 'production',
segmentioId = process.env.SEGMENTIO_ID || null,
facebookAppId = process.env.FACEBOOK_APP_ID || null,
- mailServer = process.env.MAILSERVER || null,
+ unknown_user = process.env.UNKNOWN_USER || null,
api_url = process.env.API_URL || null,
world_url = process.env.WORLD_URL || null,
offline = process.env.OFFLINE === 'true',
- testmode = process.env.TEST_MODE === 'true';
-
-var paths = {
- views : { watch: [ 'views/**/*.jade', 'content/**/*' ], src: 'views/**/*.jade', out: 'www' },
- browserify : { watch: [ 'lib/**/*', 'content/**/*', 'lib/**/**/*' ] , src: 'lib/index.js', out: 'www/js' },
- styles : { watch: 'styles/**/*.styl', src: 'styles/main.styl', out: 'www/css' }
-};
+ testmode = process.env.TEST_MODE === 'true',
+ chDescriptorsPath = 'www/assets/challenges/descriptors/',
+ libPath = 'lib/challenges/',
+ locales = ["", "ja", "es"],
-function beep() {
- console.log('\007');
-}
+ paths = {
+ views : { watch: ['views/**/*.jade', 'content/**/*'], src: 'views/**/*.jade', out: 'www' },
+ viewsi18n : { watch: 'www/**/*.html', src: 'www/**/*.html', out: 'www/locales' },
+ browserify : { watch: ['lib/**/*', 'content/**/*', 'lib/**/**/*'], src: 'lib/index.js', out: 'www/js' },
+ styles : { watch: 'styles/**/*.styl', src: 'styles/main.styl', out: 'www/css' },
+ content : { watch: 'lib/challenges/**/*' }
+ };
function handleError(error) {
- beep(error);
console.log(color.bold('[ error caught ]:\n') + color.red(error));
}
gulp.task('browserify', function () {
- gulp.src(paths.browserify.src, { read: false })
- .pipe(browserify({
- transform : [
- partialify.alsoAllow('md'),
- partialify.alsoAllow('coffee')
- ],
- }))
- .on('error', handleError)
- .pipe(rename('index.js'))
- .pipe(gulp.dest(paths.browserify.out))
- .pipe(livereload(server));
+ return gulp.src(paths.browserify.src, { read: false })
+ .pipe(browserify({
+ transform : [
+ partialify.alsoAllow('md'),
+ partialify.alsoAllow('coffee')
+ ]
+ }))
+ .on('error', handleError)
+ .pipe(rename('index.js'))
+ .pipe(gulp.dest(paths.browserify.out))
+ .pipe(livereload(server));
});
gulp.task('styles', function () {
- gulp.src(paths.styles.src)
- .pipe(stylus({
- pretty : !production,
- use : [ griddy(), nib() ]
- }))
- .on('error', handleError)
- .pipe(gulp.dest(paths.styles.out))
- .pipe(livereload(server));
+ return gulp.src(paths.styles.src)
+ .pipe(stylus({
+ pretty : !production,
+ use : [griddy(), nib()]
+ }))
+ .on('error', handleError)
+ .pipe(gulp.dest(paths.styles.out))
+ .pipe(livereload(server));
});
gulp.task('views', function () {
- gulp.src(paths.views.src)
- .pipe(jade({
- pretty : !production,
- locals : _.extend({
- env : env,
- production : production,
- offline : offline,
- segmentioId : segmentioId,
- facebookAppId : facebookAppId,
- mailServer : mailServer,
- api_url : api_url,
- world_url : world_url,
- testmode : testmode
- }, jadeHelpers)
- }))
- .on('error', handleError)
- .pipe(gulp.dest(paths.views.out))
- .pipe(livereload(server));
+ return gulp.src(paths.views.src)
+ .pipe(jade({
+ pretty : !production,
+ locals : _.extend({
+ env : env,
+ production : production,
+ offline : offline,
+ segmentioId : segmentioId,
+ facebookAppId : facebookAppId,
+ api_url : api_url,
+ world_url : world_url,
+ testmode : testmode,
+ challenges_url : "/assets/challenges/descriptors",
+ unknown_user : unknown_user
+ }, jadeHelpers)
+ }))
+ .on('error', handleError)
+ .pipe(gulp.dest(paths.views.out))
+ .pipe(livereload(server));
+});
+
+gulp.task('clean-i18n', function (next) {
+ del([paths.viewsi18n.out], next);
+});
+
+gulp.task('views-i18n', ['clean-i18n', 'views'], function () {
+ return gulp.src(paths.viewsi18n.src)
+ .pipe(i18n({ langDir: './locales',
+ createLangDirs: true }))
+ .on('error', handleError)
+ .pipe(gulp.dest(paths.viewsi18n.out))
+ .pipe(livereload(server));
+});
+
+/**
+ * This function returns a function that modifies the challenge
+ * index to contain basic information about the single challenges,
+ * so that we can show a calendar without loading all the single
+ * challenges.
+ * The returned function will be called for each locale.
+ */
+function apifyChallengeFn(next) {
+ return function (locale) {
+
+ var index,
+ worldsNum,
+ //fields that are copied from ./index.json to /world//index.json
+ copyWorldFields = [
+ 'id',
+ 'name',
+ 'description',
+ 'world_path',
+ 'cover',
+ 'css_class',
+ 'visibility',
+ 'dependency',
+ 'type',
+ 'share_strategy',
+ 'sales_popup_after',
+ 'certificate_after',
+ 'display_menu',
+ 'hero_header',
+ 'updateForm',
+ 'socialText'
+ ],
+ countNext = 0,
+ formattedIndex,
+ localePath = locale ? 'locales/' + locale + '/' : '';
+ function localNext() {
+ if (++countNext === worldsNum + 1) {
+ next();
+ }
+ }
+ //work with them
+ index = require('./' + chDescriptorsPath + localePath + 'index.json');
+ worldsNum = index.worlds.length;
+
+ index.worlds.forEach(function (world) {
+ var worldPath = './' + chDescriptorsPath + localePath + world.world_path,
+ libWorldPath = './' + libPath + localePath + world.world_path,
+ worldObj = {},
+ challenges = [],
+ formattedJSON;
+
+ worldObj = JSON.parse(JSON.stringify(require(libWorldPath + '/index.json')));
+
+ copyWorldFields.forEach(function (field) {
+ if (world[field]) {
+ worldObj[field] = world[field];
+ }
+ });
+
+ //load all the challenges in an array
+ worldObj.challenges.forEach(function (ch, idx, arr) {
+ var chFileName = worldPath + ch.substr(1, ch.length - 1),
+ challenge = require(chFileName),
+ index = idx + 1,
+ hasNext = index !== (arr.length),
+ ch_obj = {
+ id: challenge.id,
+ title: challenge.title,
+ short_title: challenge.short_title,
+ cover: challenge.cover,
+ index: index,
+ hasNext: hasNext,
+ start_date: challenge.start_date
+ };
+ challenge.index = index ;
+ challenge.hasNext = hasNext;
+
+ fs.writeFile(chFileName + ".json", JSON.stringify(challenge, null, 4), function (err) {
+ if (err) {
+ throw err;
+ }
+ });
+ challenges.push(ch_obj);
+ });
+
+
+ worldObj.challenges = challenges;
+
+ world.challenges_num = challenges.length;
+
+ formattedJSON = JSON.stringify(worldObj, null, 4);
+ //write the challenges with headers
+ fs.writeFile(worldPath + '/index.json', formattedJSON, function (err) {
+ if (err) {
+ throw err;
+ } else {
+ localNext();
+ }
+ });
+ });
+ //copy back the index
+ formattedIndex = JSON.stringify(index, null, 4);
+ fs.writeFile(chDescriptorsPath + localePath + 'index.json', formattedIndex, function (err) {
+ if (err) {
+ throw err;
+ } else {
+ localNext();
+ }
+ });
+ }
+}
+
+gulp.task('apify-challenges', ['copy-challenges'], function (next) {
+ var countNext = 0;
+
+ function localNext() {
+ if (++countNext === locales.length) {
+ next();
+ }
+ }
+
+ locales.forEach(apifyChallengeFn(localNext));
+});
+
+gulp.task('copy-challenges', function (next) {
+ var counter = 0,
+ stream1,
+ stream2,
+ stream3;
+ function localNext() {
+ if (++counter === 3) {
+ next();
+ }
+ }
+ //copy the files
+ stream1 = gulp.src('lib/challenges/worlds/**/*.json')
+ .pipe(gulp.dest(chDescriptorsPath + "/worlds"));
+ stream2 = gulp.src('lib/challenges/index.json')
+ .pipe(gulp.dest(chDescriptorsPath));
+ stream3 = gulp.src('lib/challenges/locales/**/*.json')
+ .pipe(gulp.dest(chDescriptorsPath + "/locales"));
+ stream1.on('end', localNext);
+ stream2.on('end', localNext);
+ stream3.on('end', localNext);
+
+});
+
+gulp.task('copy-vendor', function () {
+ return gulp.src('bower_components/**')
+ .pipe(gulp.dest('www/components'));
+});
+
+gulp.task('compress', function () {
+ return gulp.src('www/js/index.js', { base: 'www' })
+ .pipe(ngAnnotate())
+ .pipe(uglify())
+ .pipe(gulp.dest('www'));
});
gulp.task('livereload', function (next) {
- if (server) { livereload(server); }
+ if (server) {
+ livereload(server);
+ }
next();
});
gulp.task('listen', function (next) {
server.listen(35729, next);
});
+gulp.task('prepare-challenges', ['copy-challenges', 'apify-challenges']);
-gulp.task('build', [ 'browserify', 'styles', 'views' ]);
+gulp.task('build', ['browserify', 'copy-vendor', 'styles', 'views-i18n', 'prepare-challenges']);
-gulp.task('watch', [ 'build', 'listen' ], function () {
- gulp.watch(paths.browserify.watch, [ 'browserify' ]);
- gulp.watch(paths.styles.watch, [ 'styles' ]);
- gulp.watch(paths.views.watch, [ 'views' ]);
+gulp.task('watch', ['build', 'listen'], function () {
+ gulp.watch(paths.browserify.watch, ['browserify']);
+ gulp.watch(paths.styles.watch, ['styles']);
+ gulp.watch(paths.content.watch, ['prepare-challenges']);
+ gulp.watch(paths.views.watch, ['views']);
});
-gulp.task('default', [ 'build' ]);
+gulp.task('default', ['build']);
diff --git a/icon/kano-draw.app b/icon/kano-draw.app
index 98d98620a..128a8f5f1 100644
--- a/icon/kano-draw.app
+++ b/icon/kano-draw.app
@@ -5,7 +5,7 @@
"slug": "kano-draw",
"icon": "kano-draw",
- "colour": "#7BAA64",
+ "colour": "#51555C",
"categories": ["code"],
"mime_type": "application/x-kano-draw",
@@ -14,5 +14,6 @@
"dependencies": ["kano-draw"],
"launch_command": "kano-draw",
"overrides": [],
- "desktop": false
+ "desktop": false,
+ "priority": 950
}
diff --git a/icon/kano-draw.png b/icon/kano-draw.png
index 7764b27e9..a667679f7 100644
Binary files a/icon/kano-draw.png and b/icon/kano-draw.png differ
diff --git a/icon/locale/es/kano-draw.app b/icon/locale/es/kano-draw.app
new file mode 100644
index 000000000..e128b6dff
--- /dev/null
+++ b/icon/locale/es/kano-draw.app
@@ -0,0 +1,20 @@
+{
+ "launch_command": "kano-draw",
+ "description": "Observa c\u00f3mo aparecen formas a medida que escribes c\u00f3digo. \u00c9ste es un primer prototipo de una aplicaci\u00f3n de dibujo bastante inusual. Te permite dibujar sobre lienzo con JavaScript. Puedes combinar l\u00edneas b\u00e1sicas, cuadros y c\u00edrculos de varios colores y tama\u00f1os para crear tus propias obras maestras digitales.",
+ "title": "Make Art",
+ "tagline": "Dibujar con c\u00f3digo",
+ "colour": "#51555C",
+ "desktop": false,
+ "priority": 950,
+ "dependencies": [
+ "kano-draw"
+ ],
+ "mime_type": "application/x-kano-draw",
+ "overrides": [],
+ "packages": [],
+ "slug": "kano-draw",
+ "categories": [
+ "code"
+ ],
+ "icon": "kano-draw"
+}
diff --git a/kano-content-blacklist/kano-draw.json b/kano-content-blacklist/kano-draw.json
new file mode 100644
index 000000000..d1f400a6d
--- /dev/null
+++ b/kano-content-blacklist/kano-draw.json
@@ -0,0 +1,5 @@
+{
+ "do_not_download": [
+ "56619f9dac92811100b98e4b"
+ ]
+}
diff --git a/kano_draw/draw.py b/kano_draw/draw.py
index eeeb84bb9..14e53b48e 100644
--- a/kano_draw/draw.py
+++ b/kano_draw/draw.py
@@ -1,13 +1,23 @@
from kano.webapp import WebApp
+
class Draw(WebApp):
- def __init__(self, load_path=''):
+ def __init__(self, load_path='', make=False, play=False):
super(Draw, self).__init__()
- self._index = "http://localhost:8000"
- if load_path:
- self._index = '{}/localLoad/{}'.format(self._index,
- load_path.strip('/'))
+ base_url = 'http://localhost:8000/{path}'
+
+ if make:
+ url = base_url.format(path='challenges')
+ elif play:
+ url = base_url.format(path='playground')
+ elif load_path:
+ path = 'localLoad/{}'.format(load_path.strip('/'))
+ url = base_url.format(path=path)
+ else:
+ url = base_url.format(path='')
+
+ self._index = url
self._title = "Art"
self._app_icon = '/usr/share/icons/Kano/88x88/apps/kano-draw.png'
@@ -17,3 +27,9 @@ def __init__(self, load_path=''):
# Enable developer extras to allow error reporting to work
self._inspector = True
+
+
+# We require this function for starting the UI as a subprocess
+def start_draw(load_path='', make=False, play=False):
+ draw_instance = Draw(load_path=load_path, make=make, play=play)
+ draw_instance.run()
diff --git a/kano_draw/server.py b/kano_draw/server.py
index f2caef4ef..25d3c2a15 100644
--- a/kano_draw/server.py
+++ b/kano_draw/server.py
@@ -1,18 +1,24 @@
-from flask import Flask, Response, request, send_from_directory
+# server.py
+#
+# Copyright (C) 2014-2015, 2017 Kano Computing Ltd.
+# License: http://www.gnu.org/licenses/gpl-2.0.txt GNU GPL v2
+#
+
import json
import os
import time
import logging
-from kano.utils import ensure_dir
-from kano_profile.badges import save_app_state_variable_with_dialog, \
- calculate_xp
+from flask import Flask, Response, request, send_from_directory
+
from kano_profile.apps import load_app_state_variable
-from kano_profile.badges import increment_app_state_variable_with_dialog
-from kano_world.functions import login_using_token
-from kano_world.share import upload_share
+from kano_profile.badges import increment_app_state_variable_with_dialog, \
+ save_app_state_variable_with_dialog, calculate_xp
+from kano_world.share_helpers import login_and_share
from kano.network import is_internet
-from kano.utils import play_sound
+from kano.utils.audio import play_sound
+from kano.utils.file_operations import ensure_dir
+from kano.logging import logger
APP_NAME = 'kano-draw'
@@ -20,20 +26,25 @@
CHALLENGE_DIR = os.path.expanduser('~/Draw-content')
WALLPAPER_DIR = os.path.join(CHALLENGE_DIR, 'wallpapers')
+STATIC_ASSET_DIR = os.path.join(os.path.expanduser('~'),
+ '.make-art-assets')
+# Create the directories if necessary
ensure_dir(CHALLENGE_DIR)
ensure_dir(WALLPAPER_DIR)
-
+ensure_dir(STATIC_ASSET_DIR)
def _get_static_dir():
- bin_path = os.path.abspath(os.path.dirname(__file__))
+ '''
+ Returns directory where http server content is located
+ '''
+ return '/usr/share/kano-draw'
- if bin_path.startswith('/usr'):
- return '/usr/share/kano-draw'
- else:
- return os.path.abspath(os.path.join(bin_path, '../www'))
def _get_image_from_str(img_str):
+ '''
+ Returns a base64 encoded data of the img_str image file.
+ '''
import base64
image_b64 = img_str.split(',')[-1]
@@ -41,7 +52,11 @@ def _get_image_from_str(img_str):
return image_data
+
def _save(data):
+ '''
+ Save the current creation in the user home directory
+ '''
filename = data['filename']
try:
desc = data['description']
@@ -71,14 +86,21 @@ def _save(data):
return (filename, filepath)
+# Start the http server now
+
server = Flask(__name__, static_folder=_get_static_dir(), static_url_path='/')
server_logger = logging.getLogger('werkzeug')
server_logger.setLevel(logging.ERROR)
+# Server backend methods
+
@server.route('/')
-# Redirect a localLoad back to index for routing in Angular
+# Return the homepage for pages routed through Angular
@server.route('/localLoad/')
+@server.route('/challenges')
+@server.route('/challenges/')
+@server.route('/playground')
def root(path=None):
return server.send_static_file('index.html')
@@ -97,11 +119,15 @@ def save_challenge(filename):
return ''
+
@server.route("/challenge/local/", methods=['GET'])
def load_challenge(path):
directory, filename = os.path.split(path)
- return send_from_directory('/{}'.format(directory), filename, as_attachment=True)
+ return send_from_directory('/{}'.format(directory),
+ filename,
+ as_attachment=True)
+
@server.route("/challenge/local/wallpaper/", methods=['POST'])
def save_wallpaper(filename):
@@ -113,8 +139,10 @@ def save_wallpaper(filename):
'16-9': _get_image_from_str(data['image_16_9'])
}
- img_path = os.path.join(WALLPAPER_DIR,
- '{filename}-{{ratio}}.png'.format(filename=filename))
+ img_path = os.path.join(
+ WALLPAPER_DIR,
+ '{filename}-{{ratio}}.png'.format(filename=filename)
+ )
for ratio, img_data in imgs.iteritems():
with open(img_path.format(ratio=ratio), 'wb') as f:
@@ -122,6 +150,7 @@ def save_wallpaper(filename):
return ''
+
@server.route("/challenge/web/", methods=['POST'])
def share(filename):
# TODO: Move this connection handling into a function in Kano Utils
@@ -133,16 +162,9 @@ def share(filename):
if not is_internet():
return 'You have no internet'
- success, _ = login_using_token()
- if not success:
- os.system('kano-login 3')
- success, _ = login_using_token()
- if not success:
- return 'Cannot login'
-
data = json.loads(request.data)
filename, filepath = _save(data)
- success, msg = upload_share(filepath, filename, APP_NAME)
+ success, msg = login_and_share(filepath, filename, APP_NAME)
if not success:
return msg
@@ -151,6 +173,7 @@ def share(filename):
return ''
+
@server.route('/challenge/web', methods=['GET'])
def load_share():
# TODO: Import kano-share python module and use return code instead
@@ -171,40 +194,95 @@ def load_share():
return send_from_directory(directory, filename, as_attachment=True)
-@server.route('/progress/', methods=['POST'])
-def _save_level(level):
+@server.route('/progress//', methods=['POST'])
+def _save_level(world, challengeNo):
+
old_xp = calculate_xp()
+ needsToSave = False
- value = int(level) - 1
+ groups = load_app_state_variable(APP_NAME, 'groups')
- save_app_state_variable_with_dialog(APP_NAME, 'level', value)
- new_xp = calculate_xp()
+ # We might need to load the worlds file here so that we're sure that
+ # no one is abusing the API from the OS
+ if groups is None:
+ groups = {}
+ if world in groups:
+ if groups[world]['challengeNo'] < challengeNo:
+ groups[world]['challengeNo'] = challengeNo
+ needsToSave = True
+
+ else:
+ groups[world] = {'challengeNo': challengeNo}
+ needsToSave = True
+
+ if needsToSave:
+ save_app_state_variable_with_dialog(APP_NAME, 'groups', groups)
+
+ new_xp = calculate_xp()
return str(new_xp - old_xp)
+
@server.route('/progress', methods=['GET'])
def _load_level():
- value = load_app_state_variable(APP_NAME, 'level')
- # If every challenge is unlocked, value is 999
+ value = {
+ 'groups': load_app_state_variable(APP_NAME, 'groups'),
+ 'challenge': load_app_state_variable(APP_NAME, 'challenge')
+ }
+ # Previously we used to save the progress as "level"
+ level = load_app_state_variable(APP_NAME, 'level')
+ if value['groups'] is None:
+ value['groups'] = {}
+
+ # Replace the Challege var here.
+ if level > value['challenge']:
+ value['challenge'] = level
+
+ value = json.dumps(value)
+
+ return Response(value, content_type='application/json')
+
+
+@server.route('/lines-of-code', methods=['POST'])
+def _increment_lines_of_code():
+ data = json.loads(request.data)
+ new_lines = data['newLines']
+
+ increment_app_state_variable_with_dialog(
+ APP_NAME, 'lines_of_code', new_lines
+ )
+
+ return ''
- if value is not None:
- return str(value + 1)
- else:
- _save_level(1)
- return Response('1')
@server.route('/shutdown', methods=['POST'])
def _shutdown():
+ '''
+ Obtain the entry point to stop the http server.
+ We do not kill the parent launcher proces (kano-draw)
+ because he is keeping an eye on both the http and ui processes.
+ '''
import signal
- # Send signal to parent to initiate shutdown
- os.kill(PARENT_PID, signal.SIGINT)
+ try:
+ server_shutdown = request.environ.get('werkzeug.server.shutdown')
+ if server_shutdown is not None:
+ logger.info('Will attempt to shutdown the server')
+ server_shutdown()
+ except Exception as exc:
+ logger.error(
+ 'Error while trying to shut down the server: [{}]'.format(exc)
+ )
+
+ return ''
+
@server.route('/browsemore', methods=['POST'])
def _browsemore():
- import subprocess
+ from kano import run_bg
+
+ run_bg("chromium http://world.kano.me/shares/kano-draw")
- p = subprocess.Popen(["chromium", "http://world.kano.me/shares/kano-draw"])
@server.errorhandler(404)
def page_not_found(err):
@@ -212,14 +290,15 @@ def page_not_found(err):
return err_msg, 404
+
@server.route('/play_sound/', methods=['POST'])
def play_sounds(filename):
- print os.path.realpath(os.path.join(_get_static_dir(), filename))
sound_file = os.path.realpath(os.path.join(_get_static_dir(), filename))
play_sound(sound_file)
return ''
+
def start(parent_pid=None):
"""
The server process will receive any requests to shutdown but
@@ -230,5 +309,10 @@ def start(parent_pid=None):
PARENT_PID = parent_pid
# Run the server
- server.run(port=8000)
+ try:
+ server.run(port=8000)
+ except EnvironmentError as exc:
+ if exc.errno == 98:
+ logger.error('Another server is running bound at our port')
+ _shutdown()
time.sleep(2)
diff --git a/kano_draw/video.py b/kano_draw/video.py
index 2fe03fd4a..8a5ee0a6c 100644
--- a/kano_draw/video.py
+++ b/kano_draw/video.py
@@ -13,15 +13,25 @@
def play_intro():
- level = load_app_state_variable(APP_NAME, 'level')
- # Skip the intro in case the user progressed past level 1
- if level > 0:
- return
+ # Skip the intro in case the user progressed level 1
+ has_progressed = False
- try:
- from kano_video.logic.player import play_video
- # stop the splash screen before the video starts
- os.system('/usr/bin/kano-stop-splash')
- play_video(localfile=VIDEO_PATH)
- except ImportError:
- pass
+ groups = load_app_state_variable(APP_NAME, 'groups')
+ if groups is not None and isinstance(groups, dict):
+ for group in groups.itervalues():
+ if 'challengeNo' in group and group['challengeNo'] > 0:
+ has_progressed = True
+ break
+ else:
+ level = load_app_state_variable(APP_NAME, 'level')
+ if level > 0:
+ has_progressed = True
+
+ if not has_progressed:
+ try:
+ from kano_video.logic.player import play_video
+ # stop the splash screen before the video starts
+ os.system('/usr/bin/kano-stop-splash')
+ play_video(localfile=VIDEO_PATH)
+ except ImportError:
+ pass
diff --git a/lib/api.js b/lib/api.js
index bd6e79759..077d735c2 100644
--- a/lib/api.js
+++ b/lib/api.js
@@ -1,14 +1,16 @@
+"use strict";
/*
* API module
*
* Exports appropriate core functionality API depending on OFFLINE env variable
*/
-var offline = require('./api/offline/index');
module.exports = function (config) {
- var online = require('./api/online/index')(config);
- var api = config.OFFLINE ? offline : online;
- api.online = require('./api/online/world-api')(config);
- return api;
-};
\ No newline at end of file
+ var online = require('./api/online/index')(config),
+ offline = require('./api/offline/index')(config),
+ api = config.OFFLINE ? offline : online;
+
+ api.online = require('./api/online/world-api')(config);
+ return api;
+};
diff --git a/lib/api/offline/challenge-io.js b/lib/api/offline/challenge-io.js
index 0c0d5d9b7..3cb314db0 100644
--- a/lib/api/offline/challenge-io.js
+++ b/lib/api/offline/challenge-io.js
@@ -1,8 +1,9 @@
+"use strict";
var api = require('./local-api'),
notify = require('../notify');
module.exports = {
- save: function(filename, code, desc, image) {
+ save: function (filename, code, desc, image) {
api.challenge.save({
filename : filename,
code : code,
@@ -11,22 +12,34 @@ module.exports = {
})
.then(notify.success, notify.failure);
},
- share: function(filename, code, desc, image) {
+ share: function (filename, code, desc, image, world, challengeId, cb) {
api.challenge.share({
filename : filename,
code : code,
description : desc,
- image : image
+ image : image,
+ campaignRef : {
+ code : world,
+ challengeId: challengeId
+ }
})
- .then(notify.success, notify.failure);
+ .then(function (res) {
+ if (res.body === '') {
+ cb(res);
+ notify.success();
+ } else if (res.body && !res.body.item) {
+ cb(res);
+ notify.failure(res);
+ }
+ }, notify.failure);
},
- load: function(cb) {
+ load: function (cb) {
api.challenge.load()
.then(function (res) {
cb(res.body);
}, notify.failure);
},
- localLoad: function(path, cb) {
+ localLoad: function (path, cb) {
api.challenge.localLoad({
path : path
})
@@ -34,16 +47,16 @@ module.exports = {
cb(res.body);
}, notify.failure);
},
- saveWallpaper: function(filename, image_1024, image_4_3, image_16_9) {
+ saveWallpaper: function (filename, image_1024, image_4_3, image_16_9) {
api.challenge.saveWallpaper({
filename : filename,
image_1024 : image_1024,
image_4_3 : image_4_3,
- image_16_9 : image_16_9,
+ image_16_9 : image_16_9
})
.then(notify.success, notify.failure);
},
- browseMore: function() {
+ browseMore: function () {
api.challenge.browseMore();
}
};
diff --git a/lib/api/offline/index.js b/lib/api/offline/index.js
index 799f7e563..4182c274f 100644
--- a/lib/api/offline/index.js
+++ b/lib/api/offline/index.js
@@ -1,7 +1,12 @@
-module.exports = {
- auth : require('./auth'),
- progress : require('./progress'),
- server : require('./server'),
- challengeIO : require('./challenge-io'),
- sound : require('./sound')
-};
\ No newline at end of file
+"use strict";
+module.exports = function (cfg) {
+
+ return {
+ auth : require('./auth'),
+ progress : require('./progress')(cfg),
+ server : require('./server'),
+ challengeIO : require('./challenge-io'),
+ sound : require('./sound'),
+ profile : require('./userprofile')
+ };
+};
diff --git a/lib/api/offline/local-api.js b/lib/api/offline/local-api.js
index 21e3613f7..e96887659 100644
--- a/lib/api/offline/local-api.js
+++ b/lib/api/offline/local-api.js
@@ -1,17 +1,17 @@
-var Service = require('api-service');
-
-var apiService = new Service('http://localhost:8000');
+"use strict";
+var Service = require('api-service'),
+ apiService = new Service('http://localhost:8000');
apiService.add('challenge.save', {
method : 'post',
route : '/challenge/local/:filename',
- params : [ 'filename', 'description', 'code', 'image' ]
+ params : ['filename', 'description', 'code', 'image']
});
apiService.add('challenge.saveWallpaper', {
method : 'post',
route : '/challenge/local/wallpaper/:filename',
- params : [ 'filename', 'image_1024', 'image_4_3', 'image_16_9' ]
+ params : ['filename', 'image_1024', 'image_4_3', 'image_16_9']
});
apiService.add('challenge.load', {
@@ -22,7 +22,7 @@ apiService.add('challenge.load', {
apiService.add('challenge.share', {
method : 'post',
route : '/challenge/web/:filename',
- params : [ 'filename', 'description', 'code', 'image' ]
+ params : ['filename', 'description', 'code', 'image']
});
apiService.add('challenge.localLoad', {
@@ -38,7 +38,13 @@ apiService.add('progress.load', {
apiService.add('progress.save', {
method : 'post',
- route : '/progress/:progress'
+ route : '/progress/:world/:challenge'
+});
+
+apiService.add('linesOfCode.increment', {
+ method : 'post',
+ route : '/lines-of-code',
+ params : ['newLines']
});
apiService.add('server.shutdown', {
@@ -49,7 +55,7 @@ apiService.add('server.shutdown', {
apiService.add('sound.play', {
method : 'post',
route : '/play_sound/:filename',
- params : [ 'filename']
+ params : ['filename']
});
apiService.add('challenge.browseMore', {
diff --git a/lib/api/offline/progress.js b/lib/api/offline/progress.js
index c8a7a59f9..5d5c0a0ef 100644
--- a/lib/api/offline/progress.js
+++ b/lib/api/offline/progress.js
@@ -1,25 +1,74 @@
+"use strict";
var api = require('./local-api'),
- notify = require('../notify'),
- onlineProgress = require('../online/progress');
-
-function save(progress, callback) {
- api.progress.save({ progress: progress })
- .then(function(res) {
- var xpGain = res.body;
-
- callback(xpGain);
- }, notify.failure);
-}
-
-function load(callback) {
- api.progress.load()
- .then(function (res) {
- callback(res.body);
- }, notify.failure);
-}
-
-module.exports = {
- save : save,
- load : load,
- trackLinesOfCode : onlineProgress.trackLinesOfCode
+ notify = require('../notify');
+
+
+module.exports = function (cfg) {
+
+ function save(challengeNo, groupName, callback) {
+ var data = { groups: {} },
+ lsGroups = (localStorage.groups) ? JSON.parse(localStorage.groups) : {},
+ opts;
+
+ data.groups[groupName] = {};
+ data.groups[groupName].challengeNo = challengeNo;
+ lsGroups[groupName] = data.groups[groupName];
+ localStorage.groups = JSON.stringify(lsGroups);
+ opts = {
+ world: groupName,
+ challenge: challengeNo
+ };
+ api.progress.save(opts).then(function (res) {
+ var xpGain = res.body;
+ callback(xpGain);
+ }, notify.failure);
+ }
+
+ function load(callback) {
+ var challenge = 0,
+ groupsData = localStorage.groups ? JSON.parse(localStorage.groups) : {};
+
+ if (localStorage.challenge) {
+ //this is for backwards compatibility
+ challenge = parseInt(localStorage.challenge, 10) || 0;
+ groupsData.basic = groupsData.basic || {};
+ groupsData.basic.challengeNo = challenge;
+ }
+
+ api.progress.load().then(function (res) {
+ var data = res.body;
+
+ if (data && data.challenge && data.challenge > challenge) {
+
+ //the old version of "progress" refers to the "basic challenges"
+ challenge = data.challenge;
+ data.groups.basic = data.groups.basic || {challengeNo: 1};
+ if (challenge > data.groups.basic.challengeNo) {
+ data.groups.basic.challengeNo = challenge;
+ }
+
+ }
+ if (!data.groups) {
+ //if we still have no joy
+ data.groups = groupsData;
+ }
+ callback(data.groups);
+ }, function (err) {
+ if (groupsData.basic) {
+ callback(groupsData);
+ } else {
+ notify.failure(err);
+ }
+ });
+ }
+
+ function trackLinesOfCode(increment) {
+ api.linesOfCode.increment({newLines: increment});
+ }
+
+ return {
+ save : save,
+ load : load,
+ trackLinesOfCode : trackLinesOfCode
+ };
};
diff --git a/lib/api/offline/server.js b/lib/api/offline/server.js
index 93a0332ca..2dfbd5613 100644
--- a/lib/api/offline/server.js
+++ b/lib/api/offline/server.js
@@ -3,7 +3,7 @@ var api = require('./local-api'),
var shutdown = function() {
api.server.shutdown({})
- .then(notify.success, notify.failure);
+ .then(function(){}, notify.failure);
};
module.exports = {
diff --git a/lib/api/offline/userprofile.js b/lib/api/offline/userprofile.js
new file mode 100644
index 000000000..8172ac87a
--- /dev/null
+++ b/lib/api/offline/userprofile.js
@@ -0,0 +1,9 @@
+"use strict";
+
+function getProfile() {
+ return null;
+}
+
+module.exports = {
+ getProfile: getProfile
+};
diff --git a/lib/api/online/auth.js b/lib/api/online/auth.js
index 5d89d3bba..267458bc4 100644
--- a/lib/api/online/auth.js
+++ b/lib/api/online/auth.js
@@ -1,5 +1,4 @@
-var sdk,
- analytics = require('../../core/analytics');
+var sdk;
/*
* Initialise Kano World SDK with auth
@@ -8,22 +7,28 @@ var sdk,
* @return void
*/
-function auth (config) {
+function auth(config) {
sdk = require('kano-world-sdk')(config);
return {
init : function (callback) {
- sdk.init(function(err, user) {
- if (!err) {
- analytics.identify(user);
- }
-
- callback(err, user);
- });
+ sdk.registerForms();
+ sdk.auth.initSession(callback);
+ },
+ login : function (token, callback) {
+ sdk.auth.setToken(token);
+ sdk.auth.initSession(callback);
+ },
+ saveLogin : function (token, callback) {
+ sdk.auth.setToken(token);
+ if (callback) {
+ callback();
+ }
},
- login : sdk.auth.login,
- logout: sdk.auth.logout
+ logout: function (reload) {
+ sdk.auth.logout(reload);
+ }
};
}
-module.exports = auth;
\ No newline at end of file
+module.exports = auth;
diff --git a/lib/api/online/challenge-io.js b/lib/api/online/challenge-io.js
index 154fe4ffc..0d3e47e93 100644
--- a/lib/api/online/challenge-io.js
+++ b/lib/api/online/challenge-io.js
@@ -6,7 +6,7 @@ module.exports = function (config) {
api = require('./world-api')(config);
return {
- share: function(title, code, description, image, challengeNo) {
+ share: function(title, code, description, image, world, challengeId, callback) {
var cover = fileUtil.dataURItoBlob(image),
attachment = new Blob([ code ], { type: 'text/plain' });
@@ -20,18 +20,18 @@ module.exports = function (config) {
files : {
cover : cover,
attachment : attachment
+ },
+ campaignRef : {
+ code : world,
+ challengeId : challengeId
}
};
- if (challengeNo) {
-
- data.campaignRef = {};
- data.campaignRef.code = 'summerCamp';
- data.campaignRef.challengeNo = parseInt(challengeNo, 10);
- }
-
api.share.post(data)
- .then(function () {
+ .then(function (res) {
+ if (typeof callback === 'function') {
+ callback(res);
+ }
notify.success();
return true;
}, function (res) {
diff --git a/lib/api/online/index.js b/lib/api/online/index.js
index 015dba060..41f5a8dff 100644
--- a/lib/api/online/index.js
+++ b/lib/api/online/index.js
@@ -1,10 +1,13 @@
+"use strict";
module.exports = function (config) {
- return {
- auth : require('./auth')(config),
- progress : require('./progress')(config),
- server : {},
- challengeIO : require('./challenge-io')(config),
- summercamp : require('./summer-camp.js')(config),
- profile : require('./userprofile.js')(config)
- };
-};
\ No newline at end of file
+ return {
+ auth : require('./auth')(config),
+ progress : require('./progress')(config),
+ server : {},
+ challengeIO : require('./challenge-io')(config),
+ summercamp : require('./summer-camp')(config),
+ profile : require('./userprofile')(config),
+ questions : require('./questions')(config),
+ mailer : require('./mailer')(config)
+ };
+};
diff --git a/lib/api/online/mailer.js b/lib/api/online/mailer.js
new file mode 100644
index 000000000..ef33462dc
--- /dev/null
+++ b/lib/api/online/mailer.js
@@ -0,0 +1,14 @@
+"use strict";
+
+function mailer(config) {
+ var api = require('kano-world-sdk')(config).api;
+
+ api.add('mailer', {
+ method : 'post',
+ route : '/mail'
+ });
+
+ return api.mailer;
+}
+
+module.exports = mailer;
diff --git a/lib/api/online/progress.js b/lib/api/online/progress.js
index 27523319d..0916501ca 100644
--- a/lib/api/online/progress.js
+++ b/lib/api/online/progress.js
@@ -1,24 +1,25 @@
+"use strict";
var sdk;
-function progress (config) {
+function progress(config) {
sdk = require('kano-world-sdk')(config);
return {
save: function (challengeNo, groupName, callback) {
- var data = { groups: {} };
+ var data = { groups: {} },
+ lsGroups = (localStorage.groups) ? JSON.parse(localStorage.groups) : {};
- if (groupName) {
- data.groups[groupName] = {};
- data.groups[groupName].challengeNo = challengeNo;
- localStorage.groups = JSON.stringify(data.groups);
- }
- else {
- data.challenge = challengeNo;
- localStorage.challenge = challengeNo;
+ if (!groupName) {
+ throw "Hey, we're aren't still in 2014";
}
+ data.groups[groupName] = {};
+ data.groups[groupName].challengeNo = challengeNo;
+ lsGroups[groupName] = data.groups[groupName];
+ localStorage.groups = JSON.stringify(lsGroups);
if (sdk.auth.getUser()) {
sdk.appStorage.set('kano-draw', data, function () {
+ localStorage.challenge = undefined;
callback(0);
});
} else {
@@ -27,29 +28,41 @@ function progress (config) {
},
load: function (callback) {
- var challenge = 0;
+ var challenge = 0,
+ groupsData = localStorage.groups ? JSON.parse(localStorage.groups) : {};
if (localStorage.challenge) {
- challenge = parseInt(localStorage.challenge, 10);
+ //this is for backwards compatibility
+ challenge = parseInt(localStorage.challenge, 10) || 0;
+ groupsData.basic = groupsData.basic || {};
+ groupsData.basic.challengeNo = challenge;
}
if (sdk.auth.getUser()) {
sdk.appStorage.get('kano-draw', function (err, data) {
if (data && data.challenge && data.challenge > challenge) {
+ //the old version of "progress" refers to the "basic challenges"
challenge = data.challenge;
+ data.groups.basic = data.groups.basic || {challengeNo: 1};
+ if (challenge > data.groups.basic.challengeNo) {
+ data.groups.basic.challengeNo = challenge;
+ }
}
- callback(challenge, data.groups);
+ callback(data.groups);
});
} else {
- callback(challenge, localStorage.groups ? JSON.parse(localStorage.groups) : {});
+ callback(groupsData);
}
},
trackLinesOfCode: function (increment) {
sdk.appStorage.get('kano-draw', function (err, res) {
- if (err) { return; }
+ var current;
+ if (err) {
+ return;
+ }
- var current = typeof res.lines_of_code === 'number' ? res.lines_of_code : 0;
+ current = typeof res.lines_of_code === 'number' ? res.lines_of_code : 0;
sdk.appStorage.set('kano-draw', { lines_of_code: current + increment });
});
@@ -57,4 +70,4 @@ function progress (config) {
};
}
-module.exports = progress;
\ No newline at end of file
+module.exports = progress;
diff --git a/lib/api/online/questions.js b/lib/api/online/questions.js
new file mode 100644
index 000000000..fd17cc191
--- /dev/null
+++ b/lib/api/online/questions.js
@@ -0,0 +1,25 @@
+"use strict";
+
+function questions(config) {
+ var api = require('kano-world-sdk')(config).api;
+
+ api.add('questions.create', {
+ method : 'post',
+ route : '/questions'
+ });
+
+ api.add('questions.list', {
+ method : 'get',
+ route : '/questions'
+ });
+
+ api.add('questions.respond', {
+ method : 'post',
+ route : '/questions/responses',
+ params : ['username', 'email', 'answers']
+ });
+
+ return api.questions;
+}
+
+module.exports = questions;
diff --git a/lib/api/online/userprofile.js b/lib/api/online/userprofile.js
index 104403f72..6a8361fbf 100644
--- a/lib/api/online/userprofile.js
+++ b/lib/api/online/userprofile.js
@@ -1,16 +1,17 @@
+"use strict";
var api, sdk;
-function userProfile (config) {
- sdk = require('kano-world-sdk')(config);
- api = sdk.api;
+function userProfile(config) {
+ sdk = require('kano-world-sdk')(config);
+ api = sdk.api;
- api.add('getProfile', {
- method : 'get',
- route : '/users/detail/:userId',
- params : ['userId']
- });
+ api.add('getProfile', {
+ method : 'get',
+ route : '/users/detail/:userId',
+ params : ['userId']
+ });
- return api;
+ return api;
}
-module.exports = userProfile;
\ No newline at end of file
+module.exports = userProfile;
diff --git a/lib/app.js b/lib/app.js
index 0aaeca292..8ef9802b2 100644
--- a/lib/app.js
+++ b/lib/app.js
@@ -1,3 +1,4 @@
+"use strict";
/*
* Angular app module
*
@@ -5,11 +6,10 @@
*/
var api, auth, cfg, envCfg,
- challenges = require('./challenges/index'),
- summer = require('./challenges/summer_camp/index'),
config = require('./core/config'),
- analytics = require('./core/analytics'),
- app = angular.module('draw', ['ngRoute']);
+ tracking = require('./core/tracking'),
+ app = angular.module('draw', ['ngRoute']),
+ domainCfg;
cfg = angular.extend(config.default, config[window.ENV]);
@@ -23,6 +23,10 @@ auth = require('./core/auth')(envCfg);
api = require('./api')(envCfg);
window.CONFIG = cfg;
+domainCfg = cfg.DOMAIN_CFG;
+
+app.constant('API', api);
+app.constant('AUTH', auth);
// Configure app
app.config(function ($locationProvider) {
@@ -30,119 +34,213 @@ app.config(function ($locationProvider) {
});
-
// Run app
app.run(function ($rootScope, $window) {
- var win = angular.element($window);
+ var win = angular.element($window),
+ progressGroups;
- // Define global app initial values
- $rootScope.challenges = challenges;
- $rootScope.summerChallenges = summer;
- $rootScope.progress = {};
- $rootScope.progress.groups = {};
- $rootScope.cfg = cfg;
- $rootScope.api = api;
- $rootScope.offline = cfg.OFFLINE;
- $rootScope.loggedIn = false;
- $rootScope.user = null;
- $rootScope.login = auth.login;
- $rootScope.logout = auth.logout;
- $rootScope.shutdown = api.server.shutdown;
-
- // Prefill progress from localStorage
- $rootScope.progress.challengeNo = parseInt(localStorage.challenge || 0, 10);
- $rootScope.progress.groups = localStorage.groups ? JSON.parse(localStorage.groups) : {};
-
- function summerCampStarted() {
- var diff = new Date() - new Date('2015-08-10T06:00:00');
- return diff > 0;
+ /*
+ * Get last visited challenge index
+ *
+ * @return {Number}
+ */
+ function getLastChallenge(groupName) {
+ return parseInt(localStorage[groupName + 'lastChallengeVisited'] || 1, 10);
}
- /**
- * Loads the user profile inside the $rootScope
- * @param {string} userId the UserId
+ /*
+ * Set last visited challenge index
+ *
+ * @param {Number} index
+ * @return void
*/
- function loadUserProfile(userId) {
- api.profile.getProfile({userId: userId}).then(function (res) {
- var user = (res && res.body) ? res.body.user : undefined;
- if (user) {
- $rootScope.user.profile = user.profile;
- }
- $rootScope.$broadcast('user-profile-loaded');
-
- });
+ function setLastChallenge(groupName, index) {
+ if (groupName) {
+ localStorage[groupName + 'lastChallengeVisited'] = index;
+ }
}
- $rootScope.summerCampStarted = summerCampStarted();
-
+ /**
+ *
+ * Redirects to a certain world if
+ * it matches the domain
+ * e.g. hourofcode.kano.me redirects to challenges/hourofcode
+ */
+ function redirectIfNeeded() {
+ var host = window.location.hostname,
+ path = window.location.pathname,
+ hashed = (window.location.href.indexOf('#') > -1);
- // Initialised auth state
- auth.init(function () {
- var userObj, userId;
- $rootScope.loggedIn = auth.getState();
- userObj = auth.getUser();
- $rootScope.user = userObj;
+ if (domainCfg[host] && !hashed && path === '/' && domainCfg[host].mapToWorld && path !== domainCfg[host].mapToWorld) {
+ window.location.href = '/challenges/' + domainCfg[host].mapToWorld;
+ }
+ }
- if (userObj) {
- loadUserProfile(userObj.id);
+ /**
+ * Initialises the progress variables
+ */
+ function initProgress() {
+ var lsChallenge = parseInt(localStorage.challenge, 10);
+ $rootScope.progress = { groups: {}};
+ // Prefill progress from localStorage
+ $rootScope.progress.groups = localStorage.groups ? JSON.parse(localStorage.groups) : {};
+ progressGroups = $rootScope.progress.groups;
+ progressGroups.basic = progressGroups.basic || {challengeNo: 1};
+
+ //backwards compatibility
+ if (lsChallenge && progressGroups.basic.challengeNo < lsChallenge) {
+ progressGroups.basic.challengeNo = lsChallenge;
}
+ }
- $rootScope.$watch('user', function (user) {
- if (user) {
- userId = user.id;
- if (!localStorage.uuid) {
- localStorage.uuid = userId;
- loadUserProfile(userId);
- } else {
- if (localStorage.uuid !== userId) {
- localStorage.uuid = userId;
- }
+ /**
+ * Loads the user profile inside the $rootScope
+ * @param {string} userId the UserId
+ */
+ $rootScope.loadUserProfile = function (userId) {
+ if (!cfg.OFFLINE) {
+ api.profile.getProfile({userId: userId}).then(function (res) {
+ var user = (res && res.body) ? res.body.user : undefined;
+ if (user) {
+ $rootScope.user.profile = user.profile;
}
- }
- });
+ $rootScope.$broadcast('user-profile-loaded');
+ $rootScope.$apply();
+ });
+ }
+ }
+ /**
+ * Updates the user and loggedIn state in the $rootScope
+ */
+ $rootScope.updateUser = function (user) {
+ $rootScope.user = user;
+ if (user) {
+ $rootScope.loggedIn = true;
+ if (user.admin_level > 0) {
+ $rootScope.loadUserProfile(user.id);
+ }
+ $rootScope.loadProgress();
+ $rootScope.$apply();
+ } else {
+ $rootScope.loggedIn = false;
+ }
+ };
+ /**
+ * Loads the user progress into the $rootScope
+ */
+ $rootScope.loadProgress = function () {
// Load progress
- api.progress.load(function (challengeNo, groups) {
- $rootScope.progress.challengeNo = challengeNo || 1;
- if (groups) {
- $rootScope.progress.groups = groups;
+ api.progress.load(function (groups) {
+ if (groups && Object.keys(groups).length) {
+ //incremental merge of the groups
+ Object.keys(groups).forEach(function (group) {
+ var grp_obj = groups[group],
+ rsGroup;
+
+ $rootScope.progress.groups[group] = $rootScope.progress.groups[group] || {challengeNo: 1};
+ rsGroup = $rootScope.progress.groups[group];
+ Object.keys(grp_obj).forEach(function (key) {
+ //only merge the challengeNo if there's more progress in the API
+ if (key === "challengeNo") {
+ if (!rsGroup.challengeNo || (rsGroup.challengeNo && rsGroup.challengeNo < grp_obj.challengeNo)) {
+ rsGroup.challengeNo = grp_obj.challengeNo;
+ }
+ } else {
+ rsGroup[key] = grp_obj[key];
+ }
+ });
+ });
+ $rootScope.$apply();
}
- $rootScope.$apply();
+ redirectIfNeeded();
});
+ }
- });
-
- // Initialise analytics
- analytics.init();
-
- /*
- * Get last visited challenge index
- *
- * @return {Number}
+ /**
+ * Loads the user progress into the $rootScope
*/
- function getLastChallenge(groupName) {
- if (groupName) {
- return parseInt(localStorage[groupName + 'lastChallengeVisited'] || 1, 10);
- }
- return parseInt(localStorage.lastChallengeVisited || 1, 10);
+ function resetProgress () {
+ Object.keys($rootScope.progress.groups).forEach(function(key) {
+ $rootScope.progress.groups[key].challengeNo = 1;
+ })
}
- /*
- * Set last visited challenge index
- *
- * @param {Number} index
- * @return void
- */
- function setLastChallenge(index, groupName) {
- if (groupName) {
- localStorage[groupName + 'lastChallengeVisited'] = index;
+ initProgress();
+
+ // Define global app initial values
+ $rootScope.cfg = cfg;
+ $rootScope.api = api;
+ $rootScope.offline = cfg.OFFLINE;
+ $rootScope.loggedIn = false;
+ $rootScope.user = null;
+ $rootScope.splash = {
+ display: false,
+ hasDisplayed: false,
+ close: function () {
+ this.display = false;
+ this.hasDisplayed = true;
+ },
+ open: function () {
+ this.display = true;
}
- else {
- localStorage.lastChallengeVisited = index;
+ };
+ $rootScope.auth = {
+ mode: 'login',
+ showModal: false,
+ openModal: function (type) {
+ this.mode = type;
+ this.showModal = true;
+ },
+ closeModal: function () {
+ this.showModal = false;
}
- }
+ };
+ $rootScope.logout = function() {
+ auth.logout(function () {
+ $rootScope.updateUser();
+ resetProgress();
+ });
+ };
+ $rootScope.shutdown = api.server.shutdown;
+ $rootScope.banner = {
+ showModal: false,
+ openModal: function () { this.showModal = true; },
+ closeModal: function () { this.showModal = false; }
+ };
+ $rootScope.feedback = {
+ showModal: false,
+ openModal: function () { this.showModal = true; },
+ closeModal: function () { this.showModal = false; }
+ };
+ $rootScope.$watch('user', function (user) {
+ if (user) {
+ if (!localStorage.uuid) {
+ localStorage.uuid = user.id;
+ if (user.admin_level > 0) {
+ $rootScope.loadUserProfile(user.id);
+ }
+ } else {
+ if (localStorage.uuid !== user.id) {
+ localStorage.uuid = user.id;
+ }
+ }
+ }
+ });
+ $rootScope.feedbackLoaded = function () {
+ var el = document.querySelector('.kano-feedback-container');
+ el.addEventListener('animationend', function () {
+ document.querySelector('.kano-feedback-container').classList.remove('fadeInUp');
+ });
+ };
+
+ // Initialised auth state
+ auth.init($rootScope.updateUser);
+
+ // Initialise tracking
+ tracking.init();
/*
* Attach lastChallengeVisited get / set object to $rootScope
@@ -154,28 +252,19 @@ app.run(function ($rootScope, $window) {
/*
* Update progress
- *
- * @param {Number} challengeNo
+ * @param {string} worldId the id of the world
+ * @param {Number} challengeNo the new challenge
* @return void
*/
- $rootScope.updateProgress = function (challengeNo, groupName) {
- if (groupName) {
- api.progress.save(challengeNo, groupName, function(xpGain) {
- $rootScope.xpGain = xpGain;
- $rootScope.$apply();
- });
- $rootScope.progress.groups = {};
- $rootScope.progress.groups[groupName] = {};
- $rootScope.progress.groups[groupName].challengeNo = challengeNo;
- }
- else if (challengeNo > $rootScope.progress.challengeNo) {
- api.progress.save(challengeNo, null, function(xpGain) {
+ $rootScope.updateProgress = function (worldId, challengeNo) {
+ if (worldId) {
+ api.progress.save(challengeNo, worldId, function (xpGain) {
$rootScope.xpGain = xpGain;
$rootScope.$apply();
});
- $rootScope.progress.challengeNo = challengeNo;
- $rootScope.$apply();
-
+ $rootScope.progress.groups = $rootScope.progress.groups || {};
+ $rootScope.progress.groups[worldId] = $rootScope.progress.groups[worldId] || {};
+ $rootScope.progress.groups[worldId].challengeNo = challengeNo;
} else {
$rootScope.xpGain = 0;
$rootScope.$apply();
@@ -189,12 +278,14 @@ app.run(function ($rootScope, $window) {
}
});
-
// Update basePath on route change
$rootScope.$on('$routeChangeSuccess', function (e, route) {
var path = route.$$route ? route.$$route.originalPath : null;
+ var redirection = route.$$route ? route.$$route.redirectTo : null;
$rootScope.basePath = path ? path.split('/')[1] : '';
- analytics.page(path);
+ if (path && !redirection) {
+ tracking.dispatchVirtualPageView();
+ }
});
});
diff --git a/lib/challenges/baloon.js b/lib/challenges/baloon.js
deleted file mode 100644
index 2a6955c68..000000000
--- a/lib/challenges/baloon.js
+++ /dev/null
@@ -1,72 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'blue-baloon',
- title : 'Blue Balloon',
- description : 'Draw a balloon floating in the air using polygons!',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the color to blue - **type** `color blue`',
- 'color blue',
- [
- [ 'color', { color: palette.blue } ]
- ]
- ],
- [
- 'Set the stroke to 0, to avoid drawing lines',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Draw a circle with a size of 100',
- 'circle 100',
- [
- [ 'ellipse', { rx: 100, isCircle: true } ]
- ]
- ],
- [
- 'Move down by 100 - **type** `move 0, 100`',
- 'move 0, 100',
- [
- [ 'move-to', { dx: 0, dy: 100 } ]
- ]
- ],
- [
- 'Draw the knot - **type** `polygon 15, 15, -15, 15`',
- 'polygon 15, 15, -15, 15',
- [
- [ 'polygon', { points: [
- { x: 0, y: 0 },
- { x: 15, y: 15 },
- { x: -15, y: 15 }
- ] } ]
- ]
- ],
- [
- 'Move down a little more - **type** `move 0, 15`',
- 'move 0, 15',
- [
- [ 'move-to', { dx: 0, dy: 15 } ]
- ]
- ],
- [
- 'Choose a thick black stroke - **type** `stroke black, 5`',
- 'stroke black, 5',
- [
- [ 'stroke-color', { color: palette.black } ],
- [ 'stroke-width', { width: 5 } ]
- ]
- ],
- [
- 'Draw the line of the balloon thread - **type** `line 0, 200`',
- 'line 0, 200',
- [
- [ 'line', { dx: 0, dy: 200 } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/breakfast.js b/lib/challenges/breakfast.js
deleted file mode 100644
index 792464998..000000000
--- a/lib/challenges/breakfast.js
+++ /dev/null
@@ -1,327 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'breakfast',
- title : 'Breakfast',
- description : 'Draw a bacon and eggs breakfast!',
- startAt : 2,
- fatherDay : true,
- steps : generate.fromSequence([
- [
- 'Set the background to blue - **type** `background blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Set the stroke to 0, to avoid drawing lines',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Set the color to white',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Draw a circle with a size of 240',
- '\n#Plate\ncircle 240',
- [
- [ 'ellipse', { rx: 240, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to `\'#eee\'` - **type** `color \'#eee\'`',
- 'color \'#eee\'',
- [
- [ 'color', { color: '#eee' } ]
- ]
- ],
- [
- 'Draw a circle with a size of 200',
- 'circle 200',
- [
- [ 'ellipse', { rx: 200, isCircle: true } ]
- ]
- ],
- [
- 'Move up and left - **type** `move -70, -70`',
- '\n#Egg\nmove -70, -70',
- [
- [ 'move-to', { dx: -70, dy: -70 } ]
- ]
- ],
- [
- 'Set the color to white',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Draw a circle with a size of 80',
- 'circle 80',
- [
- [ 'ellipse', { rx: 80, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to yellow',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Draw a circle with a size of 30',
- 'circle 30',
- [
- [ 'ellipse', { rx: 30, isCircle: true } ]
- ]
- ],
- [
- 'Move up and right - **type** `move 90, -60`',
- '\n#Bacon 1\nmove 90, -60',
- [
- [ 'move-to', { dx: 90, dy: -60 } ]
- ]
- ],
- [
- 'Set the color to red',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Draw a rectangle with a size of 60 and 200 - **type** `rectangle 60, 200`',
- 'rectangle 60, 200',
- [
- [ 'rectangle', { width: 60, height: 200 } ]
- ]
- ],
- [
- 'Move 30 to the right - **type** `move 30`',
- 'move 30',
- [
- [ 'move-to', { dx: 30, dy: 0 } ]
- ]
- ],
- [
- 'Set the color to `\'#aa0000\'` - **type** `color \'#aa0000\'`',
- 'color #aa0000',
- [
- [ 'color', { color: '#aa0000' } ]
- ]
- ],
- [
- 'Draw a rectangle with a size of 30 and 200 - **type** `rectangle 30, 200`',
- 'rectangle 30, 200',
- [
- [ 'rectangle', { width: 30, height: 200 } ]
- ]
- ],
- [
- 'Move down and left - **type** `move -3, 10`',
- 'move -3, 10',
- [
- [ 'move-to', { dx: -3, dy: 10 } ]
- ]
- ],
- [
- 'Set the color to white',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Draw a rectangle with a size of 6 and 180 - **type** `rectangle 6, 180`',
- 'rectangle 6, 180',
- [
- [ 'rectangle', { width: 6, height: 180 } ]
- ]
- ],
- [
- 'Move down and right - **type** `move 40, 50`',
- '\n#Bacon 2\nmove 40, 50',
- [
- [ 'move-to', { dx: 40, dy: 50 } ]
- ]
- ],
- [
- 'Set the color to red',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Draw a rectangle with a size of 60 and 200 - **type** `rectangle 60, 200`',
- 'rectangle 60, 200',
- [
- [ 'rectangle', { width: 60, height: 200 } ]
- ]
- ],
- [
- 'Move 30 to the right - **type** `move 30`',
- 'move 30',
- [
- [ 'move-to', { dx: 30, dy: 0 } ]
- ]
- ],
- [
- 'Set the color to `\'#aa0000\'` - **type** `color \'#aa0000\'`',
- 'color #aa0000',
- [
- [ 'color', { color: '#aa0000' } ]
- ]
- ],
- [
- 'Draw a rectangle with a size of 30 and 200 - **type** `rectangle 30, 200`',
- 'rectangle 30, 200',
- [
- [ 'rectangle', { width: 30, height: 200 } ]
- ]
- ],
- [
- 'Move down and left - **type** `move -3, 10`',
- 'move -3, 10',
- [
- [ 'move-to', { dx: -3, dy: 10 } ]
- ]
- ],
- [
- 'Set the color to white',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Draw a rectangle with a size of 6 and 180 - **type** `rectangle 6, 180`',
- 'rectangle 6, 180',
- [
- [ 'rectangle', { width: 6, height: 180 } ]
- ]
- ],
- [
- 'Move down and left - **type** `move -200, 150`',
- '\n#Tomato 1\nmove -200, 150',
- [
- [ 'move-to', { dx: -200, dy: 150 } ]
- ]
- ],
- [
- 'Set the color to red',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Draw a circle with a size of 40',
- 'circle 40',
- [
- [ 'ellipse', { rx: 40, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to `\'#cc4444\'` - **type** `color \'#cc4444\'`',
- 'color \'#cc4444\'',
- [
- [ 'color', { color: '#cc4444' } ]
- ]
- ],
- [
- 'Draw a circle with a size of 35',
- 'circle 35',
- [
- [ 'ellipse', { rx: 35, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to `\'#aa4444\'` - **type** `color \'#aa4444\'`',
- 'color \'#aa4444\'',
- [
- [ 'color', { color: '#aa4444' } ]
- ]
- ],
- [
- 'Draw an ellipse with a size of 30 and 10',
- 'ellipse 30, 10',
- [
- [ 'ellipse', { rx: 30, ry: 10 } ]
- ]
- ],
- [
- 'Draw an ellipse with a size of 10 and 30',
- 'ellipse 10, 30',
- [
- [ 'ellipse', { rx: 10, ry: 30 } ]
- ]
- ],
- [
- 'Move down and right - **type** `move 70, 50`',
- '\n#Tomato 2\nmove 70, 50',
- [
- [ 'move-to', { dx: 70, dy: 50 } ]
- ]
- ],
- [
- 'Set the color to red',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Draw a circle with a size of 40',
- 'circle 40',
- [
- [ 'ellipse', { rx: 40, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to `\'#cc4444\'` - **type** `color \'#cc4444\'`',
- 'color \'#cc4444\'',
- [
- [ 'color', { color: '#cc4444' } ]
- ]
- ],
- [
- 'Draw a circle with a size of 35',
- 'circle 35',
- [
- [ 'ellipse', { rx: 35, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to `\'#aa4444\'` - **type** `color \'#aa4444\'`',
- 'color \'#aa4444\'',
- [
- [ 'color', { color: '#aa4444' } ]
- ]
- ],
- [
- 'Draw an ellipse with a size of 30 and 10',
- 'ellipse 30, 10',
- [
- [ 'ellipse', { rx: 30, ry: 10 } ]
- ]
- ],
- [
- 'Draw an ellipse with a size of 10 and 30',
- 'ellipse 10, 30',
- [
- [ 'ellipse', { rx: 10, ry: 30 } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/dots.js b/lib/challenges/dots.js
deleted file mode 100644
index aee3bdd15..000000000
--- a/lib/challenges/dots.js
+++ /dev/null
@@ -1,93 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'dots',
- title : 'Dots Pattern',
- description : 'Create a fancy dots pattern using loops and circles!',
- code : '',
- startAt : 1,
- steps : generate.fromSequence([
- [
- 'Set the background to red',
- 'background red',
- [
- [ 'background', { color: palette.red } ]
- ]
- ],
- [
- 'Set the stroke to 0',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Set the color to yellow',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Now open a loop - **type** `for x in [ 0 .. 25 ]`',
- '\nfor x in [ 0 .. 25 ]',
- [
- [ 'for-loop', { iterator: 'x', range: '0..25' } ]
- ]
- ],
- [
- 'Inside this loop, open a second loop - **type** `for y in [ 0 .. 25 ]`',
- ' for y in [ 0 .. 25 ]',
- function () {
- var out = [],
- x;
-
- for (x = 0; x < 26; x += 1) {
- out.push([ 'for-loop', { iterator: 'y', range: '0..25' } ]);
- }
-
- return out;
- }
- ],
- [
- 'Move around, **type** `moveTo x * 20, y * 20`',
- ' moveTo x * 20, y * 20',
- function () {
- var out = [],
- x, y;
-
- for (x = 0; x < 26; x += 1) {
- out.push([ 'for-loop', { iterator: 'y', range: '0..25' } ]);
-
- for (y = 0; y < 26; y += 1) {
- out.push([ 'move-to', { x: x * 20, y: y * 20 } ]);
- }
- }
-
- return out;
- },
- { override: true }
- ],
- [
- 'Now for the dots - **type** `circle 6`',
- ' circle 6',
- function () {
- var out = [],
- x, y;
-
- for (x = 0; x < 26; x += 1) {
- out.push([ 'for-loop', { iterator: 'y', range: '0..25' } ]);
-
- for (y = 0; y < 26; y += 1) {
- out.push([ 'move-to', { x: x * 20, y: y * 20 } ]);
- out.push([ 'ellipse', { rx: 6, isCircle: true } ]);
- }
- }
-
- return out;
- },
- { override: true }
- ]
- ])
-};
\ No newline at end of file
diff --git a/lib/challenges/gradient.js b/lib/challenges/gradient.js
deleted file mode 100644
index 62a4ab8cb..000000000
--- a/lib/challenges/gradient.js
+++ /dev/null
@@ -1,112 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate'),
- colors = require('../language/modules/colors');
-
-module.exports = {
- id : 'gradient',
- title : 'Rainbow gradient',
- description : 'Combine for loops and colors to make something magical!',
- code : '',
- startAt : 1,
- steps : generate.fromSequence([
- [
- 'Start by opening a for loop - **type** `for x in [ 0 .. 25 ]`',
- 'for x in [ 0 .. 25 ]',
- [
- [ 'for-loop', { iterator: 'x', range: '0..25' } ]
- ]
- ],
- [
- 'Now lets open a second for loop inside - **type** `for y in [ 0 .. 25 ]`',
- ' for y in [ 0 .. 25 ]',
- function () {
- var out = [],
- x;
-
- for (x = 0; x < 26; x += 1) {
- out.push([ 'for-loop', { iterator: 'y', range: '0..25' } ]);
- }
-
- return out;
- }
- ],
- [
- 'Let\'s rotate through the color spectrum - **type** `color rotate red, 10 * x + 10 * y`',
- ' color rotate red, 10 * x + 10 * y',
- function () {
- var out = [],
- x, y;
-
- for (x = 0; x < 26; x += 1) {
- out.push([ 'for-loop', { iterator: 'y', range: '0..25' } ]);
-
- for (y = 0; y < 26; y += 1) {
- out.push([ 'color', {
- color : colors.rotate(palette.red, 10 * x + 10 * y)
- } ]);
- }
- }
-
- return out;
- },
- { override: true }
- ],
- [
- 'Now we need to move every time we draw - **type** `moveTo 20 * x , 20 * y`',
- ' moveTo 20 * x , 20 * y',
- function () {
- var out = [],
- x, y;
-
- for (x = 0; x < 26; x += 1) {
- out.push([ 'for-loop', { iterator: 'y', range: '0..25' } ]);
-
- for (y = 0; y < 26; y += 1) {
- out.push([ 'color', {
- color : colors.rotate(palette.red, 10 * x + 10 * y)
- } ]);
-
- out.push([ 'move-to', {
- x : x * 20,
- y : y * 20
- } ]);
- }
- }
-
- return out;
- },
- { override: true }
- ],
- [
- 'Now for the shapes draw a square of size 20',
- ' square 20',
- function () {
- var out = [],
- x, y;
-
- for (x = 0; x < 26; x += 1) {
- out.push([ 'for-loop', { iterator: 'y', range: '0..25' } ]);
-
- for (y = 0; y < 26; y += 1) {
- out.push([ 'color', {
- color : colors.rotate(palette.red, 10 * x + 10 * y)
- } ]);
-
- out.push([ 'move-to', {
- x : x * 20,
- y : y * 20
- } ]);
-
- out.push([ 'rectangle', {
- width : 20,
- isSquare : true
- } ]);
- }
- }
-
- return out;
- },
- { override: true }
- ]
- ])
-};
diff --git a/lib/challenges/house.js b/lib/challenges/house.js
deleted file mode 100644
index aa7e4b316..000000000
--- a/lib/challenges/house.js
+++ /dev/null
@@ -1,149 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'house',
- title : 'House',
- description : 'Draw a cosy house!',
- code : '',
- startAt : 1,
- steps : generate.fromSequence([
- [
- 'Let\'s start with the sky! Set the `background` to `blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Now set the stroke to 0',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Move to the left and up, **type** `move -150, -50`',
- 'move -150, -50',
- [
- [ 'move-to', { dx: -150, dy: -50 } ]
- ]
- ],
- [
- 'Now set the color to beige',
- 'color beige',
- [
- [ 'color', { color: palette.beige } ]
- ]
- ],
- [
- 'Now draw rectangle, **type** `rectangle 300, 200`',
- 'rectangle 300, 200',
- [
- [ 'rectangle', { width: 300, height: 200 } ]
- ]
- ],
- [
- 'Move to the right and up, **type** `move 150, -100`',
- 'move 150, -100',
- [
- [ 'move-to', { dx: 150, dy: -100 } ]
- ]
- ],
- [
- 'Set the color to darkred for the roof',
- 'color darkred',
- [
- [ 'color', { color: palette.darkred } ]
- ]
- ],
- [
- 'Now let\'s draw the roof, **type** `polygon 170, 100, -170, 100`',
- 'polygon 170, 100, -170, 100',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 170, y: 100 },
- { x: -170, y: 100 }
- ] } ]
- ]
- ],
- [
- 'Move to the left and down, **type** `move -250, 300`',
- 'move -250, 300',
- [
- [ 'move-to', { dx: -250, dy: 300 } ]
- ]
- ],
- [
- 'Now set the color to green for the grass',
- 'color green',
- [
- [ 'color', { color: palette.green } ]
- ]
- ],
- [
- 'Draw rectangle for the grass, width a size of 500, 100',
- 'rectangle 500, 100',
- [
- [ 'rectangle', { width: 500, height: 100 } ]
- ]
- ],
- [
- 'Move to the right and up, **type** `move 220, -80`',
- 'move 220, -80',
- [
- [ 'move-to', { dx: 220, dy: -80 } ]
- ]
- ],
- [
- 'Now set the color to brown for the wooden door',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Draw rectangle for the door, with a size of 60, 80',
- 'rectangle 60, 80',
- [
- [ 'rectangle', { width: 60, height: 80 } ]
- ]
- ],
- [
- 'Set the color to aqua for the windows',
- 'color aqua',
- [
- [ 'color', { color: palette.aqua } ]
- ]
- ],
- [
- 'Move to the left and up, **type** `move -80, -80`',
- 'move -80, -80',
- [
- [ 'move-to', { dx: -80, dy: -80 } ]
- ]
- ],
- [
- 'Draw square with a size of 50',
- 'square 50',
- [
- [ 'rectangle', { width: 50, isSquare: true } ]
- ]
- ],
- [
- 'Move to the right, **type** `move 170`',
- 'move 170',
- [
- [ 'move-to', { dx: 170 } ]
- ]
- ],
- [
- 'Draw square with a size of 50',
- 'square 50',
- [
- [ 'rectangle', { width: 50, isSquare: true } ]
- ]
- ]
- ])
-};
\ No newline at end of file
diff --git a/lib/challenges/index.js b/lib/challenges/index.js
deleted file mode 100644
index fbd6211d5..000000000
--- a/lib/challenges/index.js
+++ /dev/null
@@ -1,18 +0,0 @@
-module.exports = [
- require('./japan'),
- require('./sweden'),
- require('./stare'),
- require('./smiley'),
- require('./baloon'),
- require('./stickman'),
- require('./shrinking'),
- require('./random'),
- require('./starry'),
- require('./house'),
-// require('./medal'),
- require('./dots'),
- require('./gradient'),
- require('./planet'),
- require('./pizza'),
- require('./breakfast')
- ];
\ No newline at end of file
diff --git a/lib/challenges/index.json b/lib/challenges/index.json
new file mode 100644
index 000000000..15cb6a949
--- /dev/null
+++ b/lib/challenges/index.json
@@ -0,0 +1,88 @@
+{
+ "worlds": [
+ {
+ "id": "basic",
+ "name": "Basic",
+ "description": "This is a first set of challenges which will make you started with Make-Art",
+ "cover": "world_covers/basic",
+ "world_path": "worlds/basic",
+ "css_class": "basic-challenges",
+ "share_strategy": "optional",
+ "type": "normal"
+
+ },
+ {
+ "id": "medium",
+ "name": "Medium",
+ "description": "Medium challenges",
+ "cover": "world_covers/medium",
+ "world_path": "worlds/medium",
+ "css_class": "medium",
+ "dependency": ["basic"],
+ "share_strategy": "optional",
+ "type": "normal"
+ },
+ {
+ "id": "pixelhack",
+ "name": "Pixel Hack",
+ "display_menu": true,
+ "hero_header": {
+ "links": [{
+ "image": "pixelhack/teacher-pack.svg",
+ "location": "/assets/guides/PixelHackEducatorGuide.zip",
+ "text": "Download the teacher resource guide"
+ }, {
+ "image": "pixelhack/about-kano.svg",
+ "location": "https://world.kano.me/start",
+ "text": "Learn more about Kano"
+ }]
+ },
+ "description": "This is a group of challenges for the hour of code",
+ "cover": "world_covers/pixelhack",
+ "world_path": "worlds/pixelhack",
+ "css_class": "pixelhack",
+ "type": "campaign",
+ "certificate_after": 2,
+ "share_strategy": "optional",
+ "socialText": {
+ "email": "Hack your way through video game history with Pixel Hack from Kano!",
+ "facebook": "Hack your way through video game history with #PixelHack from Kano",
+ "twitter": "Hack your way through video game history with #PixelHack from @TeamKano!\nhttp://art.kano.me/challenges/pixelhack/"
+ }
+ },
+ {
+ "id": "summercamp",
+ "name": "Summercamp",
+ "description": "This is a first set of challenges which will make you started with Make-Art",
+ "cover": "world_covers/summercamp",
+ "world_path": "worlds/summercamp",
+ "css_class": "_summercamp",
+ "share_strategy": "optional",
+ "type": "campaign"
+
+ },
+ {
+ "id": "mischiefweek2015",
+ "name": "Mischief Week",
+ "description": "This is a Halloween dedicated world",
+ "cover": "world_covers/mischiefweek",
+ "world_path": "worlds/mischiefweek2015",
+ "start_date": "2015-10-26T06:00:00",
+ "css_class": "mischiefweek2015",
+ "type": "campaign",
+ "share_strategy": "optional",
+ "sales_popup_after": 2
+ },
+ {
+ "id": "ozwaldboateng",
+ "name": "Ozwald Boateng × Kano",
+ "description": "Today, Kano and Ozwald Boateng are teaming up to teach creativity and a coding language thousands of years old.",
+ "cover": "world_covers/ozwaldboateng",
+ "world_path": "worlds/ozwaldboateng",
+ "css_class": "ozwaldboateng",
+ "type": "campaign",
+ "share_strategy": "optional",
+ "sales_popup_after": 2
+ }
+ ]
+}
diff --git a/lib/challenges/japan.js b/lib/challenges/japan.js
deleted file mode 100644
index a20b10e8c..000000000
--- a/lib/challenges/japan.js
+++ /dev/null
@@ -1,40 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'flag-japan',
- title : 'Japanese flag',
- description : 'Code a flag with a few simple commands',
- code : '',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the background to white - **type** `background white`',
- 'background white',
- [
- [ 'background', { color: palette.white } ]
- ]
- ],
- [
- 'Set the drawing color to red - in a new line **type** `color red`',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Set the stroke to 0, the stroke is the outline of a shape - in a new line **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Draw a circle with a size of 100 - in a new line **type** `circle 100`',
- 'circle 100',
- [
- [ 'ellipse', { rx: 100, isCircle: true } ]
- ]
- ],
- ])
-};
\ No newline at end of file
diff --git a/lib/challenges/locales/es/index.json b/lib/challenges/locales/es/index.json
new file mode 100755
index 000000000..76d92aa75
--- /dev/null
+++ b/lib/challenges/locales/es/index.json
@@ -0,0 +1,66 @@
+{
+ "worlds": [
+ {
+ "id": "basic",
+ "name": "Básico",
+ "description": "Este es un primer conjunto de desafíos que te introducirá a Hacer-Arte",
+ "cover": "world_covers/basic",
+ "world_path": "worlds/basic",
+ "css_class": "basic-challenges",
+ "share_strategy": "optional",
+ "type": "normal"
+ },
+ {
+ "id": "medium",
+ "name": "Medio",
+ "description": "Desafíos de nivel medio",
+ "cover": "world_covers/medium",
+ "world_path": "worlds/medium",
+ "css_class": "medium",
+ "dependency": ["basic"],
+ "share_strategy": "optional",
+ "type": "normal"
+ },
+ {
+ "id": "pixelhack",
+ "name": "Pixel Hack",
+ "description": "Este es un grupo de desafíos para la hora de programación",
+ "cover": "world_covers/pixelhack",
+ "world_path": "worlds/pixelhack",
+ "css_class": "pixelhack",
+ "type": "campaign",
+ "certificate_after": 2,
+ "teachers_guide": "/guides/PixelHackEducatorGuide.zip",
+ "share_strategy": "optional",
+ "socialText": {
+ "email": "¡Hackea tu propio camino a lo largo de la historia de los videojuegos con Pixel Hack desde Kano!",
+ "facebook": "¡Hackea tu propio camino a lo largo de la historia de los videojuegos con #PixelHack desde Kano!",
+ "twitter": "¡Hackea tu propio camino a lo largo de la historia de los videojuegos con #PixelHack desde @TeamKano! \nhttp://art.kano.me/challenges/pixelhack/"
+ }
+ },
+ {
+ "id": "summercamp",
+ "name": "Campamento de verano",
+ "description": "Este es un primer conjunto de desafíos que te introducirá a Hacer-Arte",
+ "cover": "world_covers/summercamp",
+ "world_path": "worlds/summercamp",
+ "css_class": "_summercamp",
+ "dependency": ["medium"],
+ "share_strategy": "optional",
+ "type": "campaign"
+
+ },
+ {
+ "id": "mischiefweek2015",
+ "name": "Semana de travesuras",
+ "description": "Este es un mundo dedicado a Halloween",
+ "cover": "world_covers/mischiefweek",
+ "world_path": "worlds/mischiefweek2015",
+ "start_date": "2015-10-26T06:00:00",
+ "css_class": "mischiefweek2015",
+ "type": "campaign",
+ "share_strategy": "optional",
+ "sales_popup_after": 2
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/basic/baloon.json b/lib/challenges/locales/es/worlds/basic/baloon.json
new file mode 100755
index 000000000..cb3914be1
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/basic/baloon.json
@@ -0,0 +1,42 @@
+{
+ "id": "baloon",
+ "title": "Globo Azul",
+ "cover": "blue-baloon.png",
+ "description": "¡Dibuja un globo flotando en el aire usando polígonos!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Configura el color a azul - **escribe** `color blue`",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Configura el trazo a 0, para evitar dibujar líneas. Recuerda, trazo en inglés es **stroke**",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Dibuja un círculo de 100 de tamaño",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Muévete 100 hacia abajo - **escribe** `move 0, 100`",
+ "solution": "move 0, 100"
+ },
+ {
+ "hint": "Dibuja el nudo del globo usando un polígono, o **polygon** en inglés - **escribe** `polygon 15, 15, -15, 15`",
+ "solution": "polygon 15, 15, -15, 15"
+ },
+ {
+ "hint": "Muévete un poco más hacia abajo - **escribe** `move 0, 15`",
+ "solution": "move 0, 15"
+ },
+ {
+ "hint": "Elige un trazo negro y grueso - **escribe** `stroke black, 5`",
+ "solution": "stroke black, 5"
+ },
+ {
+ "hint": "Dibuja la línea del lazo del globo - **escribe** `line 0, 200`",
+ "solution": "line 0, 200"
+ }
+ ],
+ "completion_text": "¡Increíble globo! ¿Por qué no intentas cambiarle el color antes de continuar?"
+}
diff --git a/lib/challenges/locales/es/worlds/basic/breakfast.json b/lib/challenges/locales/es/worlds/basic/breakfast.json
new file mode 100755
index 000000000..94166db7b
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/basic/breakfast.json
@@ -0,0 +1,197 @@
+{
+ "id": "breakfast",
+ "title": "Desayuno",
+ "description": "¡Dibuja un desayuno inglés de tocino y huevos!",
+ "startAt": 2,
+ "fatherDay": true,
+ "steps": [
+ {
+ "hint": "Configura el color del fondo a azul - **escribe** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Configura el trazo a 0, para evitar dibujar líneas",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configura el color a blanco",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja un círculo de 240 de tamaño",
+ "solution": "\n#Plate\ncircle 240",
+ "validate": "circle 240"
+ },
+ {
+ "hint": "Configura el color a `'#eee'` - **escribe** `color '#eee'`",
+ "solution": "color '#eee'"
+ },
+ {
+ "hint": "Dibuja un círculo de 200 de tamaño",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Muévete arriba y hacia la izquierda - **escribe** `move -70, -70`",
+ "solution": "\n#Egg\nmove -70, -70",
+ "validate": "move -70,-70"
+ },
+ {
+ "hint": "Configura el color a blanco",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja un círculo de 80 de tamaño",
+ "solution": "circle 80"
+ },
+ {
+ "hint": "Configura el color a amarillo",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Dibuja un círculo de 30 de tamaño",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Muévete arriba y hacia la derecha - **escribe** `move 90, -60`",
+ "solution": "\n#Bacon 1\nmove 90, -60",
+ "validate": "move 90,-60"
+ },
+ {
+ "hint": "Configura el color a rojo",
+ "solution": "color red"
+ },
+ {
+ "hint": "Dibuja un rectángulo de un tamaño de 60 por 200 - **escribe** `rectangle 60, 200`",
+ "solution": "rectangle 60, 200"
+ },
+ {
+ "hint": "Muévete 30 hacia la derecha - **escribe** `move 30`",
+ "solution": "move 30"
+ },
+ {
+ "hint": "Configura el color a `'#aa0000'` - **escribe** `color '#aa0000'`",
+ "solution": "color '#aa0000'"
+ },
+ {
+ "hint": "Dibuja un rectángulo de un tamaño de 30 por 200 - **escribe** `rectangle 30, 200`",
+ "solution": "rectangle 30, 200"
+ },
+ {
+ "hint": "Muévete hacia abajo y a la izquierda - **escribe** `move -3, 10`",
+ "solution": "move -3, 10"
+ },
+ {
+ "hint": "Configura el color a blanco",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja un rectángulo de un tamaño de 6 por 180 - **escribe** `rectangle 6, 180`",
+ "solution": "rectangle 6, 180"
+ },
+ {
+ "hint": "Muévete hacia abajo y a la derecha - **escribe** `move 40, 50`",
+ "solution": "\n#Bacon 2\nmove 40, 50",
+ "validate": "move 40, 50"
+ },
+ {
+ "hint": "Configura el color a rojo",
+ "solution": "color red"
+ },
+ {
+ "hint": "Dibuja un rectángulo de un tamaño de 60 por 200 - **escribe** `rectangle 60, 200`",
+ "solution": "rectangle 60, 200"
+ },
+ {
+ "hint": "Muévete hacia la derecha 30 - **escribe** `move 30`",
+ "solution": "move 30"
+ },
+ {
+ "hint": "Configura el color a`'#aa0000'` - **escribe** `color '#aa0000'`",
+ "solution": "color '#aa0000'"
+ },
+ {
+ "hint": "Dibuja un rectángulo de un tamaño de 30 por 200 - **escribe** `rectangle 30, 200`",
+ "solution": "rectangle 30, 200"
+ },
+ {
+ "hint": "Muévete hacia abajo y a la izquierda - **escribe** `move -3, 10`",
+ "solution": "move -3, 10"
+ },
+ {
+ "hint": "Configura el color a blanco",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja un rectángulo de un tamaño de 6 por 180 - **escribe** `rectangle 6, 180`",
+ "solution": "rectangle 6, 180"
+ },
+ {
+ "hint": "Muévete hacia abajo y a la izquierda - **escribe** `move -200, 150`",
+ "solution": "\n#Tomato 1\nmove -200, 150",
+ "validate": "move -200, 150"
+ },
+ {
+ "hint": "Configura el color a rojo",
+ "solution": "color red"
+ },
+ {
+ "hint": "Dibuja un círculo de 40 de tamaño",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Configura el color a `'#cc4444'` - **escribe** `color '#cc4444'`",
+ "solution": "color '#cc4444'"
+ },
+ {
+ "hint": "Dibuja un círculo de 35 de tamaño",
+ "solution": "circle 35"
+ },
+ {
+ "hint": "Configura el color a `'#aa4444'` - **escribe** `color '#aa4444'`",
+ "solution": "color '#aa4444'"
+ },
+ {
+ "hint": "Dibuja una elipse de un tamaño de 30 por 10",
+ "solution": "ellipse 30, 10"
+ },
+ {
+ "hint": "Dibuja una elipse de un tamaño de 10 por 30",
+ "solution": "ellipse 10, 30"
+ },
+ {
+ "hint": "Muévete hacia abajo y a la derecha - **escribe** `move 70, 50`",
+ "solution": "\n#Tomato 2\nmove 70, 50",
+ "validate": "move 70, 50"
+ },
+ {
+ "hint": "Configura el color a rojo",
+ "solution": "color red"
+ },
+ {
+ "hint": "Dibuja un círculo de un tamaño de 40",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Configura el color a `'#cc4444'` - **escribe** `color '#cc4444'`",
+ "solution": "color '#cc4444'"
+ },
+ {
+ "hint": "Dibuja un círculo de un tamaño de 35",
+ "solution": "circle 35"
+ },
+ {
+ "hint": "Configura el color a `'#aa4444'` - **escribe** `color '#aa4444'`",
+ "solution": "color '#aa4444'"
+ },
+ {
+ "hint": "Dibuja una elipse de un tamaño de 30 por 10",
+ "solution": "ellipse 30, 10"
+ },
+ {
+ "hint": "Dibuja una elipse de un tamaño de 10 por 30",
+ "solution": "ellipse 10, 30"
+ }
+ ],
+ "completion_text": "¡Bien hecho!"
+ "cover": "breakfast.png"
+}
\ No newline at end of file
diff --git a/lib/challenges/locales/es/worlds/basic/index.json b/lib/challenges/locales/es/worlds/basic/index.json
new file mode 100644
index 000000000..f8e39b164
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/basic/index.json
@@ -0,0 +1,10 @@
+{
+ "challenges": [
+ "./sunnyday",
+ "./swissflag",
+ "./stare",
+ "./smiley",
+ "./baloon",
+ "./stickman"
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/basic/smiley.json b/lib/challenges/locales/es/worlds/basic/smiley.json
new file mode 100755
index 000000000..6b2f4d730
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/basic/smiley.json
@@ -0,0 +1,50 @@
+{
+ "id": "smiley",
+ "title": "Tu primera cara",
+ "description": "Dibuja una cara",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Configura el color de dibujo a amarillo - **escribe** `color yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Elige un trazo grueso y negro - **escribe** `stroke black, 20`",
+ "solution": "stroke black, 20"
+ },
+ {
+ "hint": "Dibuja un círculo de un tamaño de 200. Recuerda, círculo en inglés es **circle**",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Muévete 80 a la izquierda y 80 hacia arriba - **escribe** `move -80, -80`",
+ "solution": "move -80, -80"
+ },
+ {
+ "hint": "Configura el color de dibujo a negro",
+ "solution": "color black"
+ },
+ {
+ "hint": "Dibuja un círculo de un radio de 20",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Muévete 160 a la derecha - **escribe** `move 160`",
+ "solution": "move 160"
+ },
+ {
+ "hint": "Dibuja otro círculo de un tamaño de 20",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Muévete al centro y hacia abajo - En una nueva línea, **escribe** `moveTo 250, 270`",
+ "solution": "moveTo 250, 270"
+ },
+ {
+ "hint": "Un arco es la mitad de un círculo, y en inglés se dice casi igual - **arc**. Podemos dibujarlo como si fuera la boca - **escribe** `arc 100, 1, 2`",
+ "solution": "arc 100, 1, 2"
+ }
+ ],
+ "completion_text": "¡Bien! ¡Sigue trabajando de esta manera, genio dibujante de caras!",
+ "cover": "smiley.png"
+}
diff --git a/lib/challenges/locales/es/worlds/basic/stare.json b/lib/challenges/locales/es/worlds/basic/stare.json
new file mode 100755
index 000000000..4eb3fa24a
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/basic/stare.json
@@ -0,0 +1,55 @@
+{
+ "id": "stare",
+ "title": "Mirada en la oscuridad",
+ "description": "Dibuja ojos fantasmales en la oscuridad",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Configura el color del fondo a negro. Negro en inglés se dice **black**; fondo es **background**.",
+ "solution": "background black"
+ },
+ {
+ "hint": "Muévete 80 a la izquierda **escribe** `move -80`",
+ "solution": "move -80",
+ "validate": "move -80"
+ },
+ {
+ "hint": "Configura el color de dibujo a blanco. ¿Te acuerdas cómo se dice en inglés?",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja una elipse: es como un círculo estirado. Elipse en inglés se dice casi igual **ellipse** - **escribe** `ellipse 60, 40`",
+ "solution": "ellipse 60, 40"
+ },
+ {
+ "hint": "Configura el color de dibujo a negro - **escribe** `color black`",
+ "solution": "color black"
+ },
+ {
+ "hint": "Dibuja un círculo de 10 de tamaño - **escribe** `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Muévete 160 a la derecha - **escribe** `move 160`",
+ "solution": "move 160"
+ },
+ {
+ "hint": "Configura el color de dibujo a blanco otra vez",
+ "solution": "color white"
+ },
+ {
+ "hint": "Ahora dibuja otra elipse de 60 de ancho por 40 de alto - **escribe** `ellipse 60, 40`",
+ "solution": "ellipse 60, 40"
+ },
+ {
+ "hint": "Configura el color de dibujo a negro",
+ "solution": "color black"
+ },
+ {
+ "hint": "Termínalo dibujando un círculo de un tamaño de 10 ¡No olvides usar el comando en inglés!",
+ "solution": "circle 10"
+ }
+ ],
+ "completion_text": "¡Eres un mago! ¡La próxima vez que necesite ojos fantasmales ya sé a quién llamar!",
+ "cover": "stare.png"
+}
diff --git a/lib/challenges/locales/es/worlds/basic/stickman.json b/lib/challenges/locales/es/worlds/basic/stickman.json
new file mode 100755
index 000000000..78eb5f0f5
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/basic/stickman.json
@@ -0,0 +1,54 @@
+{
+ "id": "stickman",
+ "title": "Hombre de palo",
+ "description": "Dibuja un hombre de palo usando círculos y líneas",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Configura el `trazo` a `negro` y de un tamaño de `10` ¡usando las palabras en inglés!",
+ "solution": "stroke black, 10"
+ },
+ {
+ "hint": "Muévete hacia arriba, **escribe** `move 0, -50`",
+ "solution": "move 0, -50"
+ },
+ {
+ "hint": "Dibuja el cuerpo del hombre de palo usando `line` - `line 0, 150`",
+ "solution": "line 0, 150"
+ },
+ {
+ "hint": "Dibuja el brazo izquierdo del hombre de palo - `line -80, 120`",
+ "solution": "line -80, 120"
+ },
+ {
+ "hint": "Bien, ahora el brazo derecho - `line 80, 120`",
+ "solution": "line 80, 120"
+ },
+ {
+ "hint": "Muévete hacia abajo, **escribe** `move 0, 150`",
+ "solution": "move 0, 150"
+ },
+ {
+ "hint": "Dibuja la pierna izquierda del hombre de palo: `line -80, 120`",
+ "solution": "line -80, 120"
+ },
+ {
+ "hint": "Bien, ahora la pierna derecha - `line 80, 120`",
+ "solution": "line 80, 120"
+ },
+ {
+ "hint": "Muévete a la parte de arriba del dibujo, **escribe** `moveTo 250, 100`",
+ "solution": "moveTo 250, 100"
+ },
+ {
+ "hint": "Configura el trazo nuevamente a 0 usando `stroke`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Ahora para dibujar una figura con muchos lados - **escribe** `polygon -60, 50, 0, 100, 60, 50`",
+ "solution": "polygon -60, 50, 0, 100, 60, 50"
+ }
+ ],
+ "completion_text": "¡Felicitaciones! El fondo tiene 500 de ancho por 500 de alto - hay que ser muy hábil para moverse dentro de él",
+ "cover": "stickman.png"
+}
diff --git a/lib/challenges/locales/es/worlds/basic/sunnyday.json b/lib/challenges/locales/es/worlds/basic/sunnyday.json
new file mode 100755
index 000000000..faf1227a9
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/basic/sunnyday.json
@@ -0,0 +1,23 @@
+{
+ "id": "sunnyday",
+ "title": "Día soleado",
+ "description": "Aprende lo básico de programación haciendo un despejado y soleado día.",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Siempre que ingresemos un comando debemos escribirlo en inglés. Primero vamos a llenar el fondo con un lindo cielo azul usando la palabra en inglés: **background** - **escribe** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Ahora necesitamos cambiar el color de dibujo a un amarillo brillante. Vamos a utilizar la palabra en inglés **yellow** - en una nueva línea **escribe** `color yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Finalmente vamos a dibujar el sol. Usamos el comando de círculo seguido del tamaño que queremos que tenga. La palabra en inglés para círculo es **circle**. En una nueva línea **escribe** `circle 150`",
+ "solution": "circle 150"
+ }
+ ],
+ "completion_text": "¡Muy bien! Es un día soleado y brillante, ahora cambia el número que está al lado del círculo y mira lo que pasa",
+ "cover": "sunny-day.png"
+}
diff --git a/lib/challenges/locales/es/worlds/basic/swissflag.json b/lib/challenges/locales/es/worlds/basic/swissflag.json
new file mode 100755
index 000000000..415d6d1e6
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/basic/swissflag.json
@@ -0,0 +1,39 @@
+{
+ "id": "swissflag",
+ "title": "Bandera de Suiza",
+ "description": "Haz la bandera de Suiza programando",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Bueno, es hora de probar algo un poco más desafiante. Podemos hacer del fondo un color sólido. Recuerda que fondo en inglés se dice **background**. **Escribe** `background red`.",
+ "solution": "background red"
+ },
+ {
+ "hint": "Vamos a dibujar líneas. Necesitamos configurar un lindo trazo grueso. Cada vez que lo hagamos vamos a usar la palabra en inglés **stroke**. **Escribe** `stroke 66`",
+ "solution": "stroke 66"
+ },
+ {
+ "hint": "Ahora configuremos el color del trazo a blanco. Vamos a necesitar la palabra ingelsa para blanco que es **white**. En la tercera línea **escribe** `stroke white`",
+ "solution": "stroke white"
+ },
+ {
+ "hint": "Dibujemos nuestra primera línea. Ya decidimos que será blanca y que tendrá 66 píxeles de ancho. Vamos a dibujar una línea de 100 de largo con la ayuda de otra palabra en inglés **line**: **escribiendo** `line 100`.",
+ "solution": "line 100"
+ },
+ {
+ "hint": "¡Qué línea corta y gruesa! Necesita una amiga. También vamos a hacerla de 100 píxeles de largo pero ahora en la dirección opuesta. **Escribe** `line -100`.",
+ "solution": "line -100"
+ },
+ {
+ "hint": "Mucho mejor, ahora necesitamos líneas que vayan hacia arriba y hacia abajo. Pero ¿cómo lo vamos a hacer? La función de la línea puede contener dos números. El primero controla la posición horizontal, el segundo la posición vertical. **Escribe** `line 0, 100`",
+ "solution": "line 0, 100"
+ },
+ {
+ "hint": "¡La línea va hacia abajo! Para que vaya hacia arriba tenemos que escribir **escribe** `line 0, -100`.",
+ "solution": "line 0, -100"
+ }
+ ],
+ "completion_text": "¡Qué linda bandera! Los suizos se caracterizan por sus excelentes diseños minimalistas.",
+ "cover": "swissflag.png"
+}
diff --git a/lib/challenges/locales/es/worlds/medium/dots.json b/lib/challenges/locales/es/worlds/medium/dots.json
new file mode 100755
index 000000000..681c9ad06
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/medium/dots.json
@@ -0,0 +1,39 @@
+{
+ "id": "dots",
+ "title": "Patrón de puntos",
+ "description": "¡Crea un patrón de puntos usando ciclos y círculos!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "Configura el color del fondo a rojo",
+ "solution": "background red"
+ },
+ {
+ "hint": "Configura el trazo a 0",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configura el color a amarillo",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Ahora abre un ciclo - **escribe** `for x in [ 0 .. 25 ]`",
+ "solution": "for x in [ 0..25 ]"
+ },
+ {
+ "hint": "Dentro del ciclo, abre un segundo ciclo - **escribe** `for y in [ 0 .. 25 ]`",
+ "solution": " for y in [ 0..25 ]"
+ },
+ {
+ "hint": "Muévete, **escribe** `moveTo x * 20, y * 20`",
+ "solution": " moveTo x * 20, y * 20"
+ },
+ {
+ "hint": "Ahora para hacer los puntos - **escribe** `circle 6`",
+ "solution": " circle 6"
+ }
+ ],
+ "completion_text": "¡Es matemático! Cambia los números y fíjate lo que sucede.",
+ "cover": "dots.png"
+}
diff --git a/lib/challenges/locales/es/worlds/medium/gradient.json b/lib/challenges/locales/es/worlds/medium/gradient.json
new file mode 100755
index 000000000..c78e0042e
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/medium/gradient.json
@@ -0,0 +1,31 @@
+{
+ "id": "gradient",
+ "title": "Arcoíris en degradé",
+ "description": "¡Combina ciclos y colores para hacer algo mágico!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "Empecemos por abrir un ciclo - **escribe** `for x in [ 0 .. 25 ]`",
+ "solution": "for x in [ 0..25 ]"
+ },
+ {
+ "hint": "Ahora abramos un segundo ciclo dentro del anterior - **escribe** `for y in [ 0 .. 25 ]`",
+ "solution": " for y in [ 0..25 ]"
+ },
+ {
+ "hint": "Vamos a desplegar el espectro de colores - **escribe** `color rotate red, 10 * x + 10 * y`",
+ "solution": " color rotate red, 10 * x + 10 * y"
+ },
+ {
+ "hint": "Ahora necesitamos repetir la distancia que nos vamos a mover cada vez que dibujemos - **escribe** `moveTo 20 * x, 20 * y`",
+ "solution": " moveTo 20 * x, 20 * y"
+ },
+ {
+ "hint": "Ahora dibujemos los cuadrados de un tamaño de 20",
+ "solution": " square 20"
+ }
+ ],
+ "completion_text": "Intenta cambiar los números en el espectro de colores y fíjate lo que sucede.",
+ "cover": "gradient.png"
+}
diff --git a/lib/challenges/locales/es/worlds/medium/house.json b/lib/challenges/locales/es/worlds/medium/house.json
new file mode 100755
index 000000000..5bc306dc2
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/medium/house.json
@@ -0,0 +1,87 @@
+{
+ "id": "house",
+ "title": "Casa",
+ "description": "¡Dibuja una casa acogedora!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "¡Empecemos por el cielo! Configura el color del fondo a azul, usando los comandos en inglés",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Ahora configura el trazo a 0 usando stroke",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Muévete hacia la izquierda y arriba, **escribe** `move -150, -50`",
+ "solution": "move -150, -50"
+ },
+ {
+ "hint": "Ahora configura el color a beige ¡se dice igual en inglés!",
+ "solution": "color beige"
+ },
+ {
+ "hint": "Ahora dibuja un rectángulo, **escribe** `rectangle 300, 200`",
+ "solution": "rectangle 300, 200"
+ },
+ {
+ "hint": "Muévete hacia la derecha y arriba, **escribe** `move 150, -100`",
+ "solution": "move 150, -100"
+ },
+ {
+ "hint": "Configura el color a rojo oscuro, o darkred en inglés, para el techo",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "Ahora dibujemos el techo, **escribe** `polygon 170, 100, -170, 100`",
+ "solution": "polygon 170, 100, -170, 100"
+ },
+ {
+ "hint": "Muévete hacia la izquierda y abajo, **escribe** `move -250, 300`",
+ "solution": "move -250, 300"
+ },
+ {
+ "hint": "Ahora configura el color a verde para hacer el césped",
+ "solution": "color green"
+ },
+ {
+ "hint": "Dibuja un rectángulo para el césped con las siguientes medidas 500, 100 usando `rectangle`",
+ "solution": "rectangle 500, 100"
+ },
+ {
+ "hint": "Muévete hacia la derecha y arriba, **escribe** `move 220, -80`",
+ "solution": "move 220, -80"
+ },
+ {
+ "hint": "Ahora configura el color a marrón (o brown en inglés) para hacer la puerta de madera",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Dibuja un rectángulo para la puerta con las siguientes medidas 60, 80",
+ "solution": "rectangle 60, 80"
+ },
+ {
+ "hint": "Configura el color a 'aqua' para las ventanas",
+ "solution": "color aqua"
+ },
+ {
+ "hint": "Muévete hacia la izquierda y arriba, **escribe** `move -80, -80`",
+ "solution": "move -80, -80"
+ },
+ {
+ "hint": "Dibuja un cuadrado de 50 de tamaño, **escribe** `square 50`",
+ "solution": "square 50"
+ },
+ {
+ "hint": "Muévete hacia la derecha, **escribe** `move 170`",
+ "solution": "move 170"
+ },
+ {
+ "hint": "Dibuja otro cuadrado de 50 de tamaño",
+ "solution": "square 50"
+ }
+ ],
+ "completion_text": "¡Construiste una increíble casa usando código!",
+ "cover": "house.png"
+}
diff --git a/lib/challenges/locales/es/worlds/medium/index.json b/lib/challenges/locales/es/worlds/medium/index.json
new file mode 100644
index 000000000..6d4e6623b
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/medium/index.json
@@ -0,0 +1,13 @@
+{
+ "challenges": [
+ "./shrinking",
+ "./random",
+ "./starry",
+ "./house",
+ "./dots",
+ "./gradient",
+ "./planet",
+ "./pizza"
+ ]
+}
+
diff --git a/lib/challenges/locales/es/worlds/medium/pizza.json b/lib/challenges/locales/es/worlds/medium/pizza.json
new file mode 100755
index 000000000..ed5fa2e29
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/medium/pizza.json
@@ -0,0 +1,50 @@
+{
+ "id": "pizza",
+ "title": "Pizza",
+ "description": "¡Programa una rica pizza!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Configura el color del trazo a beige",
+ "solution": "stroke beige"
+ },
+ {
+ "hint": "Configura el tamaño del trazo a 50",
+ "solution": "stroke 50"
+ },
+ {
+ "hint": "Configura el color a rojo",
+ "solution": "color red"
+ },
+ {
+ "hint": "Dibuja un círculo de 200 de tamaño",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "¡Se ve sabrosa! Configura el trazo otra vez a 0",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Cúbrela con un rico queso amarillo en forma de círculo, de un tamaño de 195",
+ "solution": "color yellow\ncircle 195",
+ "validate": [
+ "color yellow",
+ "circle 195"
+ ]
+ },
+ {
+ "hint": "No es una pizza si no lleva ingredientes salpicadas por encima - configura el color de dibujo a rojo oscuro",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "Ahora muévete `moveTo 164, 290`",
+ "solution": "moveTo 164, 290"
+ },
+ {
+ "hint": "Bien, ahora dibuja un círculo de 20 de tamaño",
+ "solution": "circle 20"
+ }
+ ],
+ "completion_text": "¡Qué rico! Termínala agregando más ingedientes, ¡deja fluir tu imaginación antes de compartirla al mundo!",
+ "cover": "pizza.png"
+}
diff --git a/lib/challenges/locales/es/worlds/medium/planet.json b/lib/challenges/locales/es/worlds/medium/planet.json
new file mode 100755
index 000000000..f09fc4302
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/medium/planet.json
@@ -0,0 +1,60 @@
+{
+ "id": "planet",
+ "title": "Pintor de planetas",
+ "description": "¡Programa tu propio planeta!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Configura el fondo con un código hexadecimal- **escribe** `background '#444444'`",
+ "solution": "background '#444444'",
+ "validate": "background '\\#444444'"
+ },
+ {
+ "hint": "Configura el trazo a 0, para evitar dibujar líneas",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configura el color a amarillo",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Ahora abramos un ciclo - **escribe** `for i in [ 0 .. 32 ]`",
+ "solution": "for i in [ 0..32 ]"
+ },
+ {
+ "hint": "Ahora para configurar dónde irá cada estrella - **escribe** `moveTo (random 1, 500), (random 1, 500)`",
+ "solution": " moveTo (random 1, 500), (random 1, 500)",
+ "validate": "__ moveTo ?\\( *random +1, +500 *\\), +\\( *random +1, +500 *\\)"
+ },
+ {
+ "hint": "Dibuja un círculo de un tamaño de 5 para las estrellas, se repetirán",
+ "solution": " circle 5"
+ },
+ {
+ "hint": "Ahora para dibujar el planeta asegúrate de estar fuera del ciclo (es decir, sin sangría de línea) y **escribe** `color purple`",
+ "solution": "color purple"
+ },
+ {
+ "hint": "Ahora muévete `moveTo 250, 250`",
+ "solution": "moveTo 250, 250"
+ },
+ {
+ "hint": "Ahora dibuja un círculo de 170 de tamaño",
+ "solution": "circle 170"
+ },
+ {
+ "hint": "Configura el color a blanco",
+ "solution": "color white"
+ },
+ {
+ "hint": "Elige un trazo grueso y blanco - en una nueva línea, **escribe** `stroke white, 5`",
+ "solution": "stroke white, 5"
+ },
+ {
+ "hint": "Ahora dibuja una elipse - **escribe** `ellipse 220, 4`",
+ "solution": "ellipse 220, 4"
+ }
+ ],
+ "completion_text": "¡Estupendo! ¡Cambia los colores y fíjate qué puedes agregar para hacerlo más atractivo antes de compartirlo!",
+ "cover": "planet.png"
+}
diff --git a/lib/challenges/locales/es/worlds/medium/random.json b/lib/challenges/locales/es/worlds/medium/random.json
new file mode 100755
index 000000000..a64f9f776
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/medium/random.json
@@ -0,0 +1,47 @@
+{
+ "id": "random",
+ "title": "¡Aleatorio!",
+ "description": "¿No estás seguro de dónde ubicar algo? - ¡hay una función para eso!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Mueve el cursor a cualquier sitio - **escribe** `moveTo (random 1, 500), (random 1, 500)`",
+ "solution": "moveTo (random 1, 500), (random 1, 500)",
+ "validate": "__moveTo ?\\( *random +1, +500 *\\), +\\( *random +1, +500 *\\)"
+ },
+ {
+ "hint": "Configura el color a rojo, en inglés es red - **escribe** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Ahora dibujemos un círculo en algún sitio aleatorio - **escribe** `circle 200`",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Configura el color de dibujo a verde",
+ "solution": "color green"
+ },
+ {
+ "hint": "Ahora dibuja un círculo de 150 de tamaño usando circle",
+ "solution": "circle 150"
+ },
+ {
+ "hint": "Configura el color de dibujo a amarillo",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Ahora dibuja un círculo de 100 de tamaño",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Configura el color de dibujo a azul",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Termínalo dibujando un círculo de 50 de tamaño",
+ "solution": "circle 50"
+ }
+ ],
+ "completion_text": "La función aleatoria nos asigna un número para ubicar objetos en un punto aleatorio del lienzo.",
+ "cover": "random.png"
+}
diff --git a/lib/challenges/locales/es/worlds/medium/shrinking.json b/lib/challenges/locales/es/worlds/medium/shrinking.json
new file mode 100755
index 000000000..4740b1f85
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/medium/shrinking.json
@@ -0,0 +1,27 @@
+{
+ "id": "shrinking",
+ "title": "Círculos que se encogen",
+ "description": "¡Círculos que se van encogiendo!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "Configura el trazo a gris- **escribe** `stroke gray, 3`",
+ "solution": "stroke gray, 3"
+ },
+ {
+ "hint": "Configura el color a transparente usando null (nulo) para poder ver a través - **escribe** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "Vamos a dibujar 32 círculos todos de una vez, necesitaremos abrir un ciclo con un nuevo comando - **escribe** `for i in [ 0 .. 32 ]`",
+ "solution": "for i in [ 0..32 ]"
+ },
+ {
+ "hint": "Genial, ahora para hacer los círculos - dentro del ciclo, **escribe** `circle 10 * i`",
+ "solution": " circle 10 * i"
+ }
+ ],
+ "completion_text": "¡Genial! Los ciclos nos permiten repetir secciones de código (¡nos ahorramos escribir!)",
+ "cover": "shrinking.png"
+}
diff --git a/lib/challenges/locales/es/worlds/medium/starry.json b/lib/challenges/locales/es/worlds/medium/starry.json
new file mode 100644
index 000000000..14f3d801e
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/medium/starry.json
@@ -0,0 +1,37 @@
+{
+ "id": "starry",
+ "title": "Cielo estrellado",
+ "description": "¡Programa tu propio cielo estrellado usando la función aleatoria!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Configura el color del fondo a azul oscuro, darkblue en inglés. Recuerda usar el comando también en inglés",
+ "solution": "background darkblue",
+ "validate": "^background darkblue$"
+ },
+ {
+ "hint": "Ahora configura el trazo a 0",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Y configura el color de dibujo a amarillo",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Ahora abramos un nuevo ciclo - **escribe** `for i in [ 0 .. 32 ]`",
+ "solution": "for i in [ 0..32 ]"
+ },
+ {
+ "hint": "Ahora para determinar dónde irán las estrellas - **escribe** `moveTo (random 1, 500), (random 1, 500)`",
+ "solution": " moveTo (random 1, 500), (random 1, 500)",
+ "validate": "__ moveTo ?\\( *random +1, +500 *\\), +\\( *random +1, +500 *\\)"
+
+ },
+ {
+ "hint": "Finalmente dibuja las estrellas dibujando círculos de 5 de tamaño",
+ "solution": " circle 5"
+ }
+ ],
+ "completion_text": "¡Genial! Presiona la barra espaciadora y fíjate qué sucede con las estrellas...",
+ "cover": "starry-sky.png"
+}
diff --git a/lib/challenges/locales/es/worlds/mischiefweek2015/cat.json b/lib/challenges/locales/es/worlds/mischiefweek2015/cat.json
new file mode 100755
index 000000000..772fa4f9a
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/mischiefweek2015/cat.json
@@ -0,0 +1,139 @@
+{
+ "id": "cat",
+ "title": "Gato Misterioso",
+ "cover": "mischiefweek2015/mw-002-cat.png",
+ "description": "Crea un gato misterioso programando",
+ "start_date": "2015-10-27T06:00:00",
+ "startAt": 2,
+ "completion_text": "Buen trabajo ¡Hiciste un gato negro súper misterioso! Intenta cambiarle el color o quizás su cola. No olvides compartir tu gato cuando acabes.",
+ "steps": [
+ {
+ "hint": "Podemos hacer del fondo algo negro y escalofriante **escribiendo** `background black`.",
+ "solution": "background black"
+ },
+ {
+ "hint": "Configura el trazo a 0 así evitamos dibujar líneas. **Escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Ahora configuremos el color de la luna. **Escribe** `color lightgray`",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Dibuja un círculo grande para la luna. **Escribe** `circle 230`.",
+ "solution": "circle 230"
+ },
+ {
+ "hint": "Ahora muévete hacia abajo para poder dibujar el césped. **Escribe** `moveTo 250, 500`.",
+ "solution": "moveTo 250, 500"
+ },
+ {
+ "hint": "Es de noche, por eso usaremos un verde oscuro para el césped. **Escribe** `color darkgreen`.",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Dibujemos una loma cubierta de césped usando una elipse. **Escribe** `ellipse 300, 100`",
+ "solution": "ellipse 300, 100"
+ },
+ {
+ "hint": "Ahora configura el color de tu gato. Para hacerlo negro, **escribe** `color black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Mueve el cursor para dibujarlo en el medio **Escribe** `moveTo 250, 250`.",
+ "solution": "moveTo 250, 250"
+ },
+ {
+ "hint": "Dibujemos la cabeza con una elipse. **Escribe** `ellipse 60, 40`.",
+ "solution": "ellipse 60, 40"
+ },
+ {
+ "hint": "Vamos a movernos hacia la derecha y hacia arriba para dibujar una oreja. **Escribe** `moveTo 280, 220`.",
+ "solution": "moveTo 280, 220"
+ },
+ {
+ "hint": "¡Vamos a usar la función polygon para dibujarle una linda y punteaguda oreja! **Escribe** `polygon 0, -40, 28, 20, close`.",
+ "solution": "polygon 0, -40, 28, 20, close"
+ },
+ {
+ "hint": "Muévete hacia la izquierda para poder dibujar la otra oreja. **Escribe** `move -60`.",
+ "solution": "move -60"
+ },
+ {
+ "hint": "¡Esta oreja es un reflejo de la anterior! **Escribe** `polygon 0, -40, -28, 20, close`.",
+ "solution": "polygon 0, -40, -28, 20, close"
+ },
+ {
+ "hint": "Dibujemos el cuello. **Escribe** `moveTo 250, 310`.",
+ "solution": "moveTo 250, 310"
+ },
+ {
+ "hint": "¡Dibújalo usando una elipse! **Escribe** `ellipse 30, 50`.",
+ "solution": "ellipse 30, 50"
+ },
+ {
+ "hint": "Ahora haremos el cuerpo. **Escribe** `moveTo 250, 360`.",
+ "solution": "moveTo 250, 360"
+ },
+ {
+ "hint": "¡Utilizaremos una elipse más grande esta vez! **Escribe** `ellipse 50, 60`.",
+ "solution": "ellipse 50, 60"
+ },
+ {
+ "hint": "¡Ahora necesitamos movernos hacia abajo y a la derecha para hacerle la cola! **escribe** `moveTo 320, 380`.",
+ "solution": "moveTo 320, 380"
+ },
+ {
+ "hint": "Vamos a utilizar un truco con la función text para hacerle una cola curva. **Escribe** `font 150`.",
+ "solution": "font 150"
+ },
+ {
+ "hint": "Utilizaremos el texto en negrita. Para activarla **escribe** `bold true`.",
+ "solution": "bold true"
+ },
+ {
+ "hint": "¡Bien! Ahora dibuja la cola **escribiendo** `text 'S'`.",
+ "solution": "text 'S'"
+ },
+ {
+ "hint": "Finalmente, haremos los ojos. **Escribe** `moveTo 225, 250`.",
+ "solution": "moveTo 225, 250"
+ },
+ {
+ "hint": "Configura el color a amarillo, yellow en inglés. **Escribe** `color yellow`.",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Dibujaremos el primer ojo usando una elipse. **Escribe** `ellipse 14, 6`.",
+ "solution": "ellipse 14, 6"
+ },
+ {
+ "hint": "Ahora para la pupila, configura el color a negro. **Escribe** `color black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Dibuja un círculo de un radio de 6. **Escribe** `circle 6`.",
+ "solution": "circle 6"
+ },
+ {
+ "hint": "¡Genial! Ahora muévete hacia la derecha para dibujar el otro ojo. **Escribe** `moveTo 275, 250`.",
+ "solution": "moveTo 275, 250"
+ },
+ {
+ "hint": "Configura el color de dibujo a amarillo. **Escribe** `color yellow`.",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Dibuja la misma elipse que dibujaste antes. **Escribe** `ellipse 14,6`.",
+ "solution": "ellipse 14, 6"
+ },
+ {
+ "hint": "Configura el color otra vez a negro. **Escribe** `color black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Por último, dibuja la segunda pupila. **Escribe** `circle 6`.",
+ "solution": "circle 6"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/mischiefweek2015/ghost.json b/lib/challenges/locales/es/worlds/mischiefweek2015/ghost.json
new file mode 100755
index 000000000..8332feefa
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/mischiefweek2015/ghost.json
@@ -0,0 +1,136 @@
+{
+ "id": "ghost",
+ "title": "Fantasma",
+ "description": "Dibuja tu propio fantasma ¡Cámbiale los colores!",
+ "start_date": "2015-10-30T06:00:00",
+ "code": "ghostColor = '#809B79'\nfaceColor = '#634E42'\ntongueColor = '#7A5750'\n",
+ "startAt": 0,
+ "steps": [
+ {
+ "hint": "Necesitamos configurar algunas variables para el color del fantasma, ghost en inglés - **Escribe** `ghostColor = '#809B79'`",
+ "solution": "ghostColor = '#809B79'"
+ },
+ {
+ "hint": "Otra variable para la cara, face en inglés - **Escribe** `faceColor = '#634E42'`",
+ "solution": "faceColor = '#634E42'"
+ },
+ {
+ "hint": "Finalmente una variable para la lengua, tongue en inglés - **Escribe** `tongueColor = '#7A5750'`",
+ "solution": "tongueColor = '#7A5750'"
+ },
+ {
+ "hint": "Usaremos figuras sólidas, sin trazo `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Nuestro fantasma dejará una estela por detrás que necesitamos dibujar primero. Configuremos el color a un tono más oscuro con `color darken(ghostColor, 10)`",
+ "solution": "color darken(ghostColor, 10)"
+ },
+ {
+ "hint": "Muévete a la posición donde estará la estela con `move 0, 60`",
+ "solution": "move 0, 60"
+ },
+ {
+ "hint": "Dibuja la estela con `circle 70`",
+ "solution": "circle 70"
+ },
+ {
+ "hint": "Ahora dibujaremos el cuerpo del fantasma. Configura el color del fantasma a nuestra variable con `color ghostColor`",
+ "solution": "color ghostColor"
+ },
+ {
+ "hint": "Ahora muévete otra vez hacia arriba y al centro para hacer el cuerpo con `move 0, -60`",
+ "solution": "move 0, -60"
+ },
+ {
+ "hint": "Dibuja el cuerpo con `circle 100`",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Ahora dibujaremos la boca con el color definido en la variable faceColor. **Escribe** `color faceColor`.",
+ "solution": "color faceColor"
+ },
+ {
+ "hint": "Ahora muévete hacia la posición de la boca con `move -40, 10`",
+ "solution": "move -40, 10"
+ },
+ {
+ "hint": "Dibuja un rectángulo para la boca con `rectangle 80, 35`",
+ "solution": "rectangle 80, 35"
+ },
+ {
+ "hint": "Los dientes son polígonos. **Pista:** puedes copiar y pegar esto: `polygon 10, -10, 20, 0, 30, -10, 40, 0, 50, -10, 60, 0, 70, -10, 80, 0`",
+ "solution": "polygon 10, -10, 20, 0, 30, -10, 40, 0, 50, -10, 60, 0, 70, -10, 80, 0"
+ },
+ {
+ "hint": "Ahora para la lengua, muévete a la parte baja de la boca con `move 40, 35`",
+ "solution": "move 40, 35"
+ },
+ {
+ "hint": "Configura el color de dibujo al de nuestra variable con `color tongueColor`",
+ "solution": "color tongueColor"
+ },
+ {
+ "hint": "La lengua es la mitad de un círculo de un radio de 25, el cual podemos dibujar con `arc 25, 0, 1`",
+ "solution": "arc 25, 0, 1"
+ },
+ {
+ "hint": "Para los ojos, configura el color de dibujo con `color faceColor`",
+ "solution": "color faceColor"
+ },
+ {
+ "hint": "Muévete a la posición de los ojos `move -40, -80`",
+ "solution": "move -40, -80"
+ },
+ {
+ "hint": "El ojo lo dibujaremos con `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Muévete para el segundo ojo con `move 80, 0`",
+ "solution": "move 80, 0"
+ },
+ {
+ "hint": "Dibuja el segundo ojo con `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Para las manos, hands en inglés, vamos a hacer algo nuevo: crear una función. **Escribe** `hand = ->`",
+ "solution": "hand = ->"
+ },
+ {
+ "hint": "Esta sección sangrada es donde escribirás tu función. Cada vez que ingreses esta función, ejecutará todos los comandos que escribiremos a continuación. Configuremos el trazo para dibujar la mano con `stroke 10, darken(ghostColor, 10)`",
+ "solution": " stroke 10, darken(ghostColor, 10)"
+ },
+ {
+ "hint": "Luego dibuja el primero de los tres dedos que haremos con `line 20, 30`",
+ "solution": " line 20, 30"
+ },
+ {
+ "hint": "Luego el segundo con `line 0, 35`",
+ "solution": " line 0, 35"
+ },
+ {
+ "hint": "El tercero con `line -20, 30`",
+ "solution": " line -20, 30"
+ },
+ {
+ "hint": "Ahora para dibujar las manos asegúrate de estar fuera de la función presionando la tecla **BORRAR**. Luego **Escribe** `move 60, 100` para la primera mano.",
+ "solution": "move 60, 100"
+ },
+ {
+ "hint": "Dibuja la mano en la posición actual con `hand()`",
+ "solution": "hand()"
+ },
+ {
+ "hint": "Muévete hacia la izquierda para dibujar la otra mano con `move -200, 0`",
+ "solution": "move -200, 0"
+ },
+ {
+ "hint": "Dibuja la última mano con `hand()`",
+ "solution": "hand()"
+ }
+ ],
+ "completion_text": "¡Bien hecho! Puedes cambiar el color de tu fantasma configurando otros colores que quieras para las variables ghostColor, faceColor, y tongueColor.",
+ "cover": "mischiefweek2015/mw-005-ghost.png"
+}
diff --git a/lib/challenges/locales/es/worlds/mischiefweek2015/hauntedhouse.json b/lib/challenges/locales/es/worlds/mischiefweek2015/hauntedhouse.json
new file mode 100755
index 000000000..9af452867
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/mischiefweek2015/hauntedhouse.json
@@ -0,0 +1,11 @@
+{
+ "id": "hauntedhouse",
+ "title": "Casa Embrujada",
+ "description": "Acondiciona esta casa para Halloween y expresa tu creatividad.",
+ "start_date": "2015-11-01T06:00:00",
+ "completion_text": "¡Es noche de Halloween! Cambia los colores para hacer de esto un escenario nocturno y luego expresa tu creatividad combinando los desafíos anteriores o haciendo algunas travesuras. En los comentarios encontrarás algunas ideas para empezar.",
+ "startAt": 0,
+ "code": "#Colores que necesitan ser más escalofriantes\nsky = aqua\nground = green\nsun = yellow\nbricks = brown\nroof = red\nframes = gray\nwindows = blue\ndoor = setBrightness(brown,-40)\n\n#Cielo\nbackground sky\nstroke 0\nmoveTo 100, 100\ncolor sun\ncircle 60\n#Por qué no agregar nubes o murciélagos\n\n#Suelo\nmoveTo 250,530\ncolor ground\nellipse 500,150\n\n#Casa\nmoveTo 100,180\ncolor bricks\nrectangle 300,270\n\n#Ventanas\n#Haz alguna travesura, puedes ingeniártela para tirar huevos en solo tres líneas de comandos\ndrawWindow = (x,y) ->\n color setBrightness(frames,30)\n moveTo x,y\n rectangle 60,80\n color windows\n moveTo x+5,y+5\n rectangle 50,70\n color setBrightness(frames,30)\n moveTo x, y+37.5\n rectangle 60,5\n moveTo x+27.5, y\n rectangle 5,80\ndrawWindow(120,200)\ndrawWindow(220,200)\ndrawWindow(120,310)\ndrawWindow(320,200)\ndrawWindow(320,310)\n\n#Techo\ncolor roof\nmoveTo 100, 180\npolygon 150, -100, 300, 0\n#Puedes usar la función arc para cubrir el techo de papel higiénico \n\n\n#Puerta\ncolor setBrightness(frames,-10)\nmoveTo 215, 330\nrectangle 70,110\nmove 5,5\ncolor door\nrectangle 60,105\ncolor setBrightness(frames,-40) \nmove -15,105\nrectangle 90,20\nmove 65,-50\ncolor setBrightness(door,80)\ncircle 3\n#¿Qué tal si dibujas una calabaza al lado de la puerta?",
+ "steps": [],
+ "cover": "mischiefweek2015/mw-007-hauntedhouse.png"
+}
diff --git a/lib/challenges/locales/es/worlds/mischiefweek2015/index.json b/lib/challenges/locales/es/worlds/mischiefweek2015/index.json
new file mode 100644
index 000000000..2bbe6689e
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/mischiefweek2015/index.json
@@ -0,0 +1,11 @@
+{
+ "challenges": [
+ "./skull",
+ "./cat",
+ "./pumpkin",
+ "./potion",
+ "./ghost",
+ "./spiderweb",
+ "./hauntedhouse"
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/mischiefweek2015/potion.json b/lib/challenges/locales/es/worlds/mischiefweek2015/potion.json
new file mode 100755
index 000000000..80ab0cf95
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/mischiefweek2015/potion.json
@@ -0,0 +1,96 @@
+{
+ "id": "potion",
+ "title": "Frasco de Poción",
+ "description": "Elabora tu propia poción programando.",
+ "start_date": "2015-10-29T06:00:00",
+ "code": "",
+ "startAt": 2,
+ "completion_text": "¡Bien hecho! Puedes cambiar el color de tu poción al que tú quieras configurando potionColor. El vidrio lo haces opacando el blanco. Usando la función en inglés opacity(white, .4) le dará un buen efecto con cualquier color que elijas.",
+ "cover": "mischiefweek2015/mw-004-potion.png",
+ "steps": [
+ {
+ "hint": "Queremos utilizar figuras sólidas para este desafío, entonces **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Para que parezca oscuro configura el fondo con `background charcoal`",
+ "solution": "background charcoal"
+ },
+ {
+ "hint": "Vamos a configurar una variable para el color de nuestra poción. Para hacerlo **escribe** `potionColor = purple`",
+ "solution": "potionColor = purple"
+ },
+ {
+ "hint": "Muévete a la pocición con `move 0, 50`",
+ "solution": "move 0, 50"
+ },
+ {
+ "hint": "Configura el color de dibujo con `color potionColor`",
+ "solution": "color potionColor"
+ },
+ {
+ "hint": "Finalmente podemos dibujar la poción con `circle 90`",
+ "solution": "circle 90"
+ },
+ {
+ "hint": "Queremos que el frasco esté completamente lleno, vamos a usar una línea para hacerlo. Configura el estilo de la línea con `stroke potionColor, 40`",
+ "solution": "stroke potionColor, 40"
+ },
+ {
+ "hint": "Dibuja la línea desde el medio con `line 0, -120`.",
+ "solution": "line 0, -120"
+ },
+ {
+ "hint": "Configura el trazo a cero para hacer el vidrio del frasco. **Escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Agreguemos otro círculo al interior del frasco para darle un toque natural a nuestra poción. Muévete hacia arriba y a la derecha con `move 30, -30`.",
+ "solution": "move 30, -30"
+ },
+ {
+ "hint": "Configura el color de dibujo a un tono más oscuro del elegido para la poción con `color darken(potionColor, 10)`",
+ "solution": "color darken(potionColor, 10)"
+ },
+ {
+ "hint": "Finalmente dibuja el círculo con `circle 20`",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Muévete otra vez al centro con `move -30, 30`",
+ "solution": "move -30, 30"
+ },
+ {
+ "hint": "Configura el color del vidrio del frasco haciéndolo opaco el blanco, usa `color opacity(white, .4)`",
+ "solution": "color opacity(white, .4)"
+ },
+ {
+ "hint": "Luego dibuja el frasco haciendo un arco. **Escribe** `arc 100, 1.4, 1.6`",
+ "solution": "arc 100, 1.4, 1.6"
+ },
+ {
+ "hint": "Observa cómo el arco casi llega a completar un círculo entero pero deja un espacio para encontrarse con el cuello de la botella. Dibujemos un corcho. **Escribe** `move 0, -130`",
+ "solution": "move 0, -130"
+ },
+ {
+ "hint": "Configura el color de dibujo para el corcho con `color tan`",
+ "solution": "color tan"
+ },
+ {
+ "hint": "Dibuja el corcho con `polygon 20, 0, 30, -40, -30, -40, -20, 0`",
+ "solution": "polygon 20, 0, 30, -40, -30, -40, -20, 0"
+ },
+ {
+ "hint": "Ahora para el cuello del frasco, configuremos el trazo otra vez a color blanco transparente . **Escribe** `stroke 60, opacity(white, .4)`",
+ "solution": "stroke 60, opacity(white, .4)"
+ },
+ {
+ "hint": "Muévete un poco hacia arriba para el cuello con `move 0, -25`",
+ "solution": "move 0, -25"
+ },
+ {
+ "hint": "Finalmente dibuja el cuello de la botella con `line 0, 60`",
+ "solution": "line 0, 60"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/mischiefweek2015/pumpkin.json b/lib/challenges/locales/es/worlds/mischiefweek2015/pumpkin.json
new file mode 100755
index 000000000..a04dbddbc
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/mischiefweek2015/pumpkin.json
@@ -0,0 +1,87 @@
+{
+ "id": "pumpkin",
+ "title": "Calabaza",
+ "cover": "mischiefweek2015/mw-003-pumpkin.png",
+ "description": "No sería Halloween sin calabazas escalofriantes. Aprende a crear una calabaza y sé creativo cambiándole la cara.",
+ "start_date": "2015-10-28T06:00:00",
+ "completion_text": "¡Es una excelente linterna de calabaza! Pero necesita estar iluminada para ser verdaderamente escalofriante. ¿Puedes ingeniártelas para encender la calabaza cambiando algunos colores? También podrías alterar la cara para que se vea más escalofriante. Demuéstranos tu creatividad y luego comparte tu creación.",
+ "startAt": 0,
+ "steps": [
+ {
+ "hint": "Vamos a configurar un escenario escalofriante, hazlo de noche - **escribe** `background charcoal`",
+ "solution": "background charcoal"
+ },
+ {
+ "hint": "Lo más distintivo de las calabazas es su fuerte color naranja, para configurar el color de relleno - **escribe** `color orange`",
+ "solution": "color orange"
+ },
+ {
+ "hint": "Vamos a usar trazos para aparentar la forma ondulada de la calabaza, usa la función darken (oscurecer) para crear contrastes en la cáscara de la calabaza - **escribe** `stroke darken(orange, 20), 30`",
+ "solution": "stroke darken(orange, 20), 30"
+ },
+ {
+ "hint": "Crea tu calabaza parte por parte usando elipses - **escribe** `ellipse 180, 140`",
+ "solution": "ellipse 180, 140"
+ },
+ {
+ "hint": "Dibuja otra parte - **escribe** `ellipse 130, 140`",
+ "solution": "ellipse 130, 140"
+ },
+ {
+ "hint": "Ya comienza a tomar forma, dibuja otra parte - **escribe** `ellipse 50, 140`",
+ "solution": "ellipse 50, 140"
+ },
+ {
+ "hint": "Las calabazas son frutos y por eso tenemos que dibujarle un tallo verde - **escribe** `color green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Configura el trazo a 0, por ahora no lo utilizaremos - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Mueve el cursor a la parte de arriba de la calabaza - **escribe** `move -20, -180`",
+ "solution": "move -20, -180"
+ },
+ {
+ "hint": "Dibuja el tallo grueso de la calabaza - **escribe** `square 40`",
+ "solution": "square 40"
+ },
+ {
+ "hint": "Ahora que ya tenemos una calabaza fresca podemos avanzar a la parte divertida dándole diseño - **escribe** `color black`",
+ "solution": "color black"
+ },
+ {
+ "hint": "Vamos a dibujarle la cara, mueve el cursor al lugar de los ojos - **escribe** `moveTo 190, 275`",
+ "solution": "moveTo 190, 275"
+ },
+ {
+ "hint": "Dibuja un agujero para el primer ojo - **escribe** `circle 15`",
+ "solution": "circle 15"
+ },
+ {
+ "hint": "Mueve el cursor a donde estará el segundo ojo - **escribe** `moveTo 310, 275`",
+ "solution": "moveTo 310, 275"
+ },
+ {
+ "hint": "Dibuja un segundo agujero del mismo tamaño que el primero - **escribe** `circle 15`",
+ "solution": "circle 15"
+ },
+ {
+ "hint": "Ahora necesitamos darle una boca - **escribe** `moveTo 250, 320`",
+ "solution": "moveTo 250, 320"
+ },
+ {
+ "hint": "Necesitamos eliminar el color de relleno configurándolo a transparente - **escribe** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "Utilizaremos un trazo grueso y negro para la boca - **escribe** `stroke 15, black`",
+ "solution": "stroke 15, black"
+ },
+ {
+ "hint": "Finalmente, vamos a utilizar el comando arc (dibujará un arco) para darle una sonrisa. - **escribe** `arc 40, 1, 2, true`",
+ "solution": "arc 40, 1, 2, true"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/mischiefweek2015/skull.json b/lib/challenges/locales/es/worlds/mischiefweek2015/skull.json
new file mode 100755
index 000000000..cc5cec4f8
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/mischiefweek2015/skull.json
@@ -0,0 +1,75 @@
+{
+ "id": "skull",
+ "title": "Calavera",
+ "cover": "mischiefweek2015/mw-001-skull.png",
+ "start_date": "2015-10-26T06:00:00",
+ "description": "",
+ "completion_text": "¡Increíble calavera! Puedes agregarle un fondo con algún patrón o algunos accesorios faciales. Cuando hayas finalizado, ¡no olvides compartir tu creación!",
+ "startAt": 0,
+ "steps": [
+ {
+ "hint": "Primero debemos ambientar el escenario escalofriante, hazlo de noche - **escribe** `background charcoal`",
+ "solution": "background charcoal"
+ },
+ {
+ "hint": "Vamos a dibujar figuras sólidas sin utilizar el trazo, entonces **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Podemos movernos hacia la posición de la calavera utilizando `moveTo 250, 200`",
+ "solution": "moveTo 250, 200"
+ },
+ {
+ "hint": "Nuestra calavera debería ser blanca, entonces configura el color de dibujo a blanco usando `color white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "La calavera la haremos con una elipse, una especie de círculo estirado. Dibújala escribiendo `ellipse 140, 120`",
+ "solution": "ellipse 140, 120"
+ },
+ {
+ "hint": "Ya comienza a tomar forma, para terminar el cráneo mueve el cursor - **escribe** `move -60, 90`",
+ "solution": "move -60, 90"
+ },
+ {
+ "hint": "La mandíbula de la calavera es un cuadrado, dibújalo usando `square 120`",
+ "solution": "square 120"
+ },
+ {
+ "hint": "Muévete hacia arriba para dibujar los ojos - **escribe** `move 10, -50`",
+ "solution": "move 10, -50"
+ },
+ {
+ "hint": "Los ojos de nuestra calavera están tan vacíos que te penetran el alma, llevarán el mismo color del fondo. Configúralo usando `color charcoal`",
+ "solution": "color charcoal"
+ },
+ {
+ "hint": "El ojo es un círculo. Dibújalo con `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Muévete para dibujar el otro ojo. **Escribe** `move 100`",
+ "solution": "move 100"
+ },
+ {
+ "hint": "Ahora dibuja el otro ojo con `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Muévete para hacer la nariz con `move -50, 60`",
+ "solution": "move -50, 60"
+ },
+ {
+ "hint": "Luego dibuja la nariz con `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Finalmente, para la boca muévete hacia abajo **escribiendo** `move -40, 60`",
+ "solution": "move -40, 60"
+ },
+ {
+ "hint": "Finalmente, dibuja la boca con un rectángulo, usa `rectangle 80, 20`",
+ "solution": "rectangle 80, 20"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/mischiefweek2015/spiderweb.json b/lib/challenges/locales/es/worlds/mischiefweek2015/spiderweb.json
new file mode 100644
index 000000000..c18530fe5
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/mischiefweek2015/spiderweb.json
@@ -0,0 +1,12 @@
+{
+ "id": "spiderweb",
+ "title": "Telaraña",
+ "description": "¡Juega con la telaraña!",
+ "completion_text": "¡Feliz Halloween! El desafío de hoy es que juegues con los comandos que ya están ingresados. ¿Puedes cambiar el color de la telaraña? ¿O el tamaño? ¡Gracias al miembro de la comunidad @NOPx90 que programó esta telaraña!",
+ "cover": "mischiefweek2015/mw-006-spiderweb.png",
+ "start_date": "2015-10-31T06:00:00",
+ "startAt": 0,
+ "code": "# JUEGA CON ESTO\nradius = 370\nframes = 30\nbridges = 10\ndegrees = 360\nrotation = 0\n\nbodySize = 80\nlegSpread = 90\nlegLength = 100\neyeSize = 4\neyeSpacing = 12\n\n\n# Esto define la telaraña\nclass SpiderWeb\n constructor: (@x, @y, @radius, @frames, @bridges, @degrees, @rotation) ->\n @x = @x ? 250\n @y = @y ? 250\n @radius = @radius ? 250\n @frames = @frames ? 16\n @bridges = @bridges ? 20\n @degrees = @degrees ? 360\n @rotation = @rotation ? 0\n @frameAngle = @degrees / @frames\n @draw()\n drawFrame: () ->\n moveTo @x , @y\n for i in [ 0 .. @frames ]\n if i * @frameAngle != 360\n radians = (i * @frameAngle + @rotation)*(Math.PI / 180)\n x = Math.cos(radians)\n y = -Math.sin(radians)\n line (x*@radius) , (y*@radius)\n drawBridge: () ->\n r = @radius\n for web in [ 1 .. @bridges ]\n r /= 1.2\n for i in [ 0 ... @frames ]\n # Posición inicial\n r1 = (i * @frameAngle + @rotation)*(Math.PI / 180)\n x1 = Math.cos(r1)\n y1 = -Math.sin(r1)\n \n # Posición final\n r2 = (i * @frameAngle + @frameAngle + @rotation)*(Math.PI / 180)\n x2 = Math.cos(r2)\n y2 = -Math.sin(r2)\n moveTo x1 * r + @x , y1 * r + @y\n lineTo x2 * r + @x , y2 * r + @y\n draw: ( ) ->\n this.drawFrame()\n this.drawBridge()\n\n# Aquí se ubica el dibujo\nbackground setSaturation(darkpurple,-25)\nstroke white\nweb = new SpiderWeb(250,250, radius, frames, bridges, degrees, rotation)\nCuerpo de la Araña\nstroke white, 10\nmoveTo 250, 0\nline 0, 200\nmove 0, 200\ncolor black\nstroke 0\nellipse bodySize * 0.8, bodySize\n# Patas de la Arañas\nstroke black, 5\ncolor null\npairOfLegs = (flipHori,flipVert) ->\n spread = legSpread * flipHori\n length = legLength * flipVert\n polygon spread, length, spread * 0.7, length * 2\n polygon spread * 1.3, length * 0.4, spread * 1.7, length * 1.5\npairOfLegs(1,1)\npairOfLegs(-1,1)\npairOfLegs(1,-1)\npairOfLegs(-1,-1)\n\n#Cabeza de la Araña\nstroke 0\ncolor black\nmove 0, bodySize\nellipse bodySize * 0.5, bodySize * 0.4\ncolor orangered\nmove eyeSpacing * -1.5, eyeSpacing / -2\nfor [1 .. 4]\n circle eyeSize\n move 0, eyeSpacing\n circle eyeSize\n move eyeSpacing, eyeSpacing * -1\n",
+ "steps": [
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/chest.json b/lib/challenges/locales/es/worlds/pixelhack/chest.json
new file mode 100755
index 000000000..71161b6c4
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/chest.json
@@ -0,0 +1,104 @@
+{
+ "id": "chest",
+ "title": "Cofre",
+ "description": "Descubre un misterioso cofre programando",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Los Juegos de rol se convirtieron en uno de los géneros de videojuegos más populares de los años 80. Uno de los objetos más comunes de estos juegos es el cofre. Empecemos por ambientar el escenario configurando el color del fondo **escribe** `background darkslategray`",
+ "solution": "background darkslategray"
+ },
+ {
+ "hint": "Luego vamos a desactivar el trazo **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Posiciona el cursor en la esquina izquierda de arriba donde estará ubicado el cofre - **escribe** `move -150,-100`",
+ "solution": "move -150,-100"
+ },
+ {
+ "hint": "Configura el color a dorado para dar la impresión de riquezas dentro del cofre **escribe** `color gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Vamos a usar capas para limitar el número de formas que tenemos que dibujar. Primero dibuja el borde dorado con un rectángulo de 300 por 200 de tamaño",
+ "solution": "rectangle 300,200"
+ },
+ {
+ "hint": "Ahora vamos a realzar el color dorado **escribe** `color lightyellow`",
+ "solution": "color lightyellow"
+ },
+ {
+ "hint": "En el arte en 8-bits, los detalles simples significan mucho para el objeto, agrega un rectángulo de 25 por 100 para darle brillo al dorado",
+ "solution": "rectangle 25,100"
+ },
+ {
+ "hint": "Mueve el cursor y prepárate para dibujar la parte de madera del cofre **escribe** `move 25,0`",
+ "solution": "move 25,0"
+ },
+ {
+ "hint": "Cambia el color de relleno a marrón **escribe** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Usaremos marrón sobre el dorado así parece que un borde dorado mantiene unidas las tablas de madera del cofre **escribe** `rectangle 250,175`",
+ "solution": "rectangle 250,175"
+ },
+ {
+ "hint": "Mueve el cursor para dividir el cofre dibujando la tapa **escribe** `move 0,60`",
+ "solution": "move 0,60"
+ },
+ {
+ "hint": "Cambia el `color` a dorado otra vez",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Dibuja la juntura con un rectángulo de 250 por 25",
+ "solution": "rectangle 250,25"
+ },
+ {
+ "hint": "Ahora continuaremos con los efectos para realzar los colores **escribe** `color lightyellow`",
+ "solution": "color lightyellow"
+ },
+ {
+ "hint": "Continua con los efectos dibujando un cuadrado de 25 de tamaño",
+ "solution": "square 25"
+ },
+ {
+ "hint": "Finalmente, lo terminaremos dibujando una cerradura. Mueve el cursor al medio del cofre **escribe** `move 75,-25`",
+ "solution": "move 75,-25"
+ },
+ {
+ "hint": "Configura el `color` a dorado para que combine con el borde",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Dibuja un rectángulo de 100 por 75",
+ "solution": "rectangle 100,75"
+ },
+ {
+ "hint": "Muévete para terminar la cerradura **escribe** `move 25,25`",
+ "solution": "move 25,25"
+ },
+ {
+ "hint": "Configura el color a marrón oscuro",
+ "solution": "color darkbrown"
+ },
+ {
+ "hint": "Por último, dibuja un rectángulo de 50 por 75",
+ "solution": "rectangle 50,75"
+ }
+ ],
+ "completion_text": "Ese cofre se ve genial, ¡me pregunto qué esconderá dentro!",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "chest-nether.png",
+ "chest-sunken.png",
+ "chest-open.png"
+ ]
+ },
+ "cover": "pixel-chest.png",
+ "guide": "#### Nuevas palabras\n**square** size | square 25\n\nEsta es una versión más corta del rectángulo \n\n#### Lo que crearás\n1. Los Juegos de rol se convirtieron en uno de los géneros de videojuegos más populares de los años 80. Uno de los objetos más comunes de estos juegos es el cofre, empecemos por ambientar el escenario configurando el color del fondo **escribe** `background darkslategray`\n2. Luego vamos a desactivar el trazo **escribe** `stroke 0`\n3. Posiciona el cursor en la esquina izquierda de arriba donde estará ubicado el cofre - **escribe** `move -150,-100`\n4. Configura el color a dorado para dar la impresión de riquezas dentro del cofre **escribe** `color gold`\n5. Vamos a usar capas para limitar el número de formas que tenemos que dibujar, primero dibuja el borde dorado con un rectángulo de 300 por 200 de tamaño\n6. Ahora vamos a realzar el color dorado **escribe** `color lightyellow`\n7. En el arte en 8-bits, los detalles simples significan mucho para el objeto, agrega un rectángulo de 25 por 100 para darle brillo al dorado\n8. Mueve el cursor y prepárate para dibujar la parte de madera del cofre **escribe** `move 25,0`\n9. Cambia el color de dibujo a marrón **escribe** `color brown`\n10. Usaremos marrón sobre el dorado así parece que un borde dorado mantiene unidas las tablas de madera del cofre **escribe** `rectangle 250,175`\n11. Mueve el cursor para dividir el cofre dibujando la tapa **escribe** `move 0,60`\n12. Cambia el `color` a dorado\n13. Dibuja la juntura con un rectángulo de 250 por 25\n14. Ahora continuaremos con los efectos para realzar los colores **escribe** `color lightyellow`\n15. Continua con los efectos dibujando un cuadrado de 25 de tamaño\n16. Finalmente, lo terminaremos dibujando una cerradura. Mueve el cursor al medio del cofre **escribe** `move 75,-25`\n17. Configura el `color` a dorado para que combine con el borde\n18. Dibuja un rectángulo de 100 por 75\n19. Muévete para terminar la cerradura **escribe** `move 25,25`\n20. Configura el color a marrón oscuro\n21. Por último, dibuja un rectángulo de 50 por 75\n\n#### Lo que hackearás\nAl cambiar los colores y agregar formas puedes generar un clima particular alrededor de tu cofre. Para algo más complejo, cambia los comandos para que tu cofre esté abierto y se vea algún premio dentro.\n\n#### Informe\nEl cofre ha aparecido en muchos videojuegos. Predominantemente en los juegos de The Legend of Zelda, donde escondían desde pequeños premios de dinero hasta poderosas armas y herramientas. Lo que contenga este cofre, depende de ti. Cuando termines de crearlo, ábrelo y colócale dentro el premio que se te ocurra.\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/colorfrenzy.json b/lib/challenges/locales/es/worlds/pixelhack/colorfrenzy.json
new file mode 100755
index 000000000..22239bd35
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/colorfrenzy.json
@@ -0,0 +1,43 @@
+{
+ "id": "colorfrenzy",
+ "title": "Frenesí de colores",
+ "description": "Utiliza un ciclo para dibujar un patrón en 8-bits.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Únicamente utilizaremos figuras sólidas sin trazo. Entonces **escribe** `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Para este dibujo usaremos dos ciclos. Uno para que se repita verticalmente y otro horizontalmente **Escribe** `for x in [ 0 .. 20 ]`",
+ "solution": "for x in [ 0 .. 20 ]"
+ },
+ {
+ "hint": "Para el segundo ciclo **escribe** `for y in [ 0 .. 20 ]`",
+ "solution": " for y in [ 0 .. 20 ]"
+ },
+ {
+ "hint": "Configura el color utilizando `color rotate red, x + y * 10`",
+ "solution": " color rotate red, x + y * 10"
+ },
+ {
+ "hint": "Posiciónate utilizando `moveTo x * 25, y * 25`",
+ "solution": " moveTo x * 25, y * 25"
+ },
+ {
+ "hint": "Finalmente, agreguemos cuadrados. **Escribe** `square 25`",
+ "solution": " square 25"
+ }
+ ],
+ "completion_text": "¡Bien hecho! La función rotate, rota todos los colores creando el efecto de un arcoíris.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "color-quint.png",
+ "color-multi.png",
+ "color-random.png"
+ ]
+ },
+ "cover": "pixel-colorfrenzy.png",
+ "guide": "#### Lo que crearás\n1. Únicamente utilizaremos figuras sólidas sin trazo. Entonces **escribe** `stroke 0`.\n2. Para este dibujo usaremos dos ciclos. Uno para que se repita verticalmente y otro horizontalmente **Escribe** `for x in [ 0 .. 20 ]`\n3. Para el segundo ciclo **escribe** `for y in [ 0 .. 20 ]`\n4. Configura el color utilizando `color rotate red, x * 10`\n5. Posiciónate utilizando `moveTo x * 25, y * 25`\n6. Finalmente, agreguemos cuadrados. **Escribe** `square 25`\n\n#### Lo que hackearás\nComo estás viajando a través de los ejes X e Y, puedes obtener lindas combinaciones de colores. Usando la matemática, puedes hacer que el arcoíris se repita de manera interesante, y cambiar tu forma de dibujar usando la función aleatoria. Hay muchas variaciones posibles, ¡fíjate lo que puedes descubrir!\n\n#### Informe\nUtilizamos un sólo ciclo en los desafíos anteriores para reptir una única acción, esta vez estaremos utilizando dos. El primero recorrerá la cooredenada X y el segundo la coordenada Y. Al tener dos ciclos, podemos controlar el color en ambas direcciones (horizontal y vertical) y también dibujar cuadrados en esas direcciones ¡Nos permitirán llenar el fondo de puro color!\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/diamondblock.json b/lib/challenges/locales/es/worlds/pixelhack/diamondblock.json
new file mode 100755
index 000000000..c25430255
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/diamondblock.json
@@ -0,0 +1,72 @@
+{
+ "id": "diamondblock",
+ "title": "Bloque de diamantes en 8-bits",
+ "description": "Crea un bloque de diamantes con ciclos.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Configura el trazo a 0 usando stroke.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Utilizaremos dos ciclos para la piedra. **Escribe** `for x in [0 ... 16]`",
+ "solution": "for x in [0 ... 16]"
+ },
+ {
+ "hint": "Para Y utiliza `for y in [0 ... 16]`",
+ "solution": " for y in [0 ... 16]"
+ },
+ {
+ "hint": "Utilizaremos distintos tonos de gris elegidos aleatoriamente para la piedra. **Escribe** `color darken gray, random -6, 9`",
+ "solution": " color darken gray, random -6, 9",
+ "validate": " color darken gray, random -6, 9"
+ },
+ {
+ "hint": "Muévete en la posición para cada cuadrado utilizando `moveTo x * 31.25, y * 31.25`",
+ "solution": " moveTo x * 31.25, y * 31.25"
+ },
+ {
+ "hint": "Finalmente dibuja los cuadrados utilizando `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ },
+ {
+ "hint": "Sal de este ciclo posicionando el cursor al comienzo del renglón usando la tecla para borrar. Abriremos un nuevo par de ciclos para dibujar el diamante. **Escribe** `for x in [2 ... 14]`",
+ "solution": "for x in [2 ... 14]"
+ },
+ {
+ "hint": "Esta vez empezaremos en 2 y terminaremos en 14 porque queremos que el diamante aparezca en la mitad del bloque.**Escribe** `for y in [2 ... 14]`",
+ "solution": " for y in [2 ... 14]"
+ },
+ {
+ "hint": "El diamante debería aparecer aleatoriamente, una sentencia condicional (if statement) nos permitirá hacerlo. **Escribe** `if 1 == random 0, 4`",
+ "solution": " if 1 == random 0, 4",
+ "validate": " if 1 == random 0, 4"
+ },
+ {
+ "hint": "Configura el color del diamante utilizando `color lighten lightblue, random 0, 40`",
+ "solution": " color lighten lightblue, random 0, 40",
+ "validate": " color lighten lightblue, random 0, 40"
+ },
+ {
+ "hint": "Muévete en la posición para cada cuadrado utilizando `moveTo x * 31.25, y * 31.25`",
+ "solution": " moveTo x * 31.25, y * 31.25"
+ },
+ {
+ "hint": "Finalmente dibuja los cuadrados de diamante utilizando `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ }
+ ],
+ "completion_text": "¡Has hackeado los píxeles! ¡Felicitaciones y bien hecho!",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "diamond-obsidian.png",
+ "diamond-rainbow.png",
+ "diamond-lotsof.png"
+ ]
+ },
+ "cover": "pixel-diamondblock.png",
+ "guide": "#### Lo que crearás\n1. Configura el trazo a 0 usando stroke.\n2. Utilizaremos dos ciclos para la piedra. **Escribe** `for x in [0 ... 16]`\n3. Para Y utiliza `for y in [0 ... 16]`\n4. Utilizaremos distintos tonos de gris elegidos aleatoriamente para la piedra. **Escribe** `color darken gray, random -6, 9`\n5. Muévete en la posición para cada cuadrado utilizando `moveTo x * 31.25, y * 31.25`\n6. Finalmente dibuja los cuadrados utilizando `square 32`\n7. Sal de este ciclo posicionando el cursor al comienzo del renglón usando la tecla para borrar. Abriremos un nuevo par de ciclos para dibujar el diamante. **Escribe** `for x in [2 ... 14]`\n8. Esta vez empezaremos en 2 y terminaremos en 14 porque queremos que el diamante aparezca en la mitad del bloque.**Escribe** `for y in [2 ... 14]`\n9. El diamante debería aparecer aleatoriamente, la función if nos permitirá hacerlo. **Escribe** `if 1 == random 0, 4`\n10. Configura el color del diamante utilizando `color lighten lightblue, random 0, 40`\n11. Muévete en la posición para cada cuadrado utilizando `moveTo x * 31.25, y * 31.25`\n12. Finalmente dibuja los cuadrados de diamante utilizando `square 32`\n\n#### Lo que hackearás\nPara personalizar este bloque de diamantes de Minecraft todo lo que debes hacer es cambiar dos variables: el color de arriba y el color de abajo. Crea una fresa, un bloque de agua o de cualquier otra cosa únicamente cambiando esas dos líneas de comandos. Para añadir complejidad, crea un bloque de micelio usando un ciclo y la función aleatoria. Si de verdad buscas un desafío, intenta incorporar lo que aprendimos antes y utiliza los valores de X e Y para rotar los tonos de los colores.\n\n#### Informe\nLa gran final: diamantes. Este es el bloque más preciado y buscado en Minecraft. Utilizarás un ciclo anidado para dibujar la piedra, otro para dibujar los diamantes. Pero como los diamantes deberían estar salpicados a lo largo de la piedra, utilizaremos una sentencia condicional (if statement) y un número aleatorio para determinar dónde aparecerán los cuadrados. Además, queremos dibujar el diamante únicamente en el sector del medio del bloque, por eso el ciclo irá de 2 a 14 y no de 0 a 16.\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/forloop.json b/lib/challenges/locales/es/worlds/pixelhack/forloop.json
new file mode 100755
index 000000000..c6f7d601d
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/forloop.json
@@ -0,0 +1,95 @@
+{
+ "id": "forloop",
+ "title": "For Loop",
+ "description": "Usa un Ciclo for para crear fácilmente una viborita.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Vamos a recrear el clásico juego de la serpiente usando el poder de los ciclos for. For significa por en inglés y esta función útil nos permite evitar dibujar círculo por círculo. Primero configura un fondo sucio - **escribe** `background tan`",
+ "solution": "background tan"
+ },
+ {
+ "hint": "Configura el trazo a 0 **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Primero posicionemos la cabeza de la viborita **escribe** `moveTo 80,250`",
+ "solution": "moveTo 80,250"
+ },
+ {
+ "hint": "Configura un color de víbora **escribe** `color olivedrab`",
+ "solution": "color olivedrab"
+ },
+ {
+ "hint": "Dibuja un círculo de 30 píxeles de tamaño para la cabeza",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Este es el momento en el que configuraremos el ciclo, usando la función for. El primer número indica dónde comenzar a contar y el segundo hasta qué número contar. Entonces 1 .. 7 hace que la computadora cuente hasta 7 - **escribe** `for [1 .. 7]`",
+ "solution": "for [1 .. 7]"
+ },
+ {
+ "hint": "Cualquier cosa dentro de este ciclo se repetirá 7 veces, necesitamos utilizar la función move para posicionar cada parte del cuerpo - **escribe** `move 50,0`",
+ "solution": " move 50,0"
+ },
+ {
+ "hint": "Configura el color del cuerpo - **escribe** `color olivedrab`",
+ "solution": " color olivedrab"
+ },
+ {
+ "hint": "Una vez que dibujemos un círculo, verás como se repite el código una cierta cantidad de veces - **escribe** `circle 30`",
+ "solution": " circle 30"
+ },
+ {
+ "hint": "Agreguemos cuadrados para que el cuerpo parezca más segmentado, fíjate que aparecen 7 figuras por cada línea del código - **escribe** `square 30`",
+ "solution": " square 30"
+ },
+ {
+ "hint": "Le agregaremos otro detalle a la cola, configura el color a verde oscuro, darkgreen en inglés",
+ "solution": " color darkgreen"
+ },
+ {
+ "hint": "Dibuja otro círculo, el código todavía está programado para que se repita 7 veces - **escribe** `circle 20`",
+ "solution": " circle 20"
+ },
+ {
+ "hint": "Finalmente, dibuja un cuadrado de un tamaño de 20 píxeles",
+ "solution": " square 20"
+ },
+ {
+ "hint": "Para lo siguiente asegúrate de estar fuera del ciclo for presionando la tecla borrar. Vamos a dibujar el ojo - **escribe** `moveTo 70,240`",
+ "solution": "moveTo 70,240"
+ },
+ {
+ "hint": "Cambia el `color` de dibujo a negro",
+ "solution": "color black"
+ },
+ {
+ "hint": "Dibuja una `ellipse` de 10 píxeles de ancho y 5 de alto.",
+ "solution": "ellipse 10,5"
+ },
+ {
+ "hint": "Finalmente le dibujaremos una lengua, para que pueda silbar como toda víbora - **escribe** `color salmon`",
+ "solution": "color salmon"
+ },
+ {
+ "hint": "Posiciónala en la cara - **escribe** `move -15, 20`",
+ "solution": "move -15, 20"
+ },
+ {
+ "hint": "Dibujaremos un rectángulo con el ancho en negativo para hacer que la lengua salga directo de la boca - **escribe** `rectangle -20,4`",
+ "solution": "rectangle -20,4"
+ }
+ ],
+ "completion_text": "Ya has experimentado cómo hacer arte con menos líneas de códigos usando ciclos de manera inteligente. ¿Por qué no intentar cambiar el número 7 y ver qué sucede?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "snake-tiny.png",
+ "snake-apple.png",
+ "snake-corner.png"
+ ]
+ },
+ "cover": "pixel-forloop.png",
+ "guide": "#### Nueva palabra\n**for i in [rangoMinimo ... rangoMax]**| for in [0 .. 10]\n\nEl ciclo for ejecuta el comando tantas veces como lo indique el rango. 10 veces para un rango de [0 … 10] y 20 veces para un rango de [0 … 20].\n\n#### Lo que crearás\n1. Vamos a recrear el clásico juego de la Viborita usando el poder de los ciclos para evitar dibujar círculo por círculo. Primero configura un fondo sucio - **escribe** `background tan`\n2. Configura el trazo a 0 **escribe** `stroke 0`\n3. Primero posicionemos la cabeza de la viborita **escribe** `moveTo 80,250`\n4. Configura un color de víbora **escribe** `color olivedrab`\n5. Dibuja un círculo de 30 píxeles de tamaño para la cabeza\n6. Este es el momento en el que configuraremos el ciclo, usando la función for. El primer número indica dónde comenzar a contar y el segundo hasta qué número contar. Entonces 1 .. 7 hace que la computadora cuente hasta 7 - **escribe** `for [1 .. 7]`\n7. Cualquier cosa dentro de este ciclo se repetirá 7 veces, necesitamos utilizar la función move para posicionar cada parte del cuerpo - **escribe** `move 50,0`\n8. Configura el color del cuerpo - **escribe** `color olivedrab`\n9.Una vez que dibujemos un círculo, verás como se repite el código una cierta cantidad de veces - **escribe** `circle 30`\n10. Agreguemos cuadrados para que el cuerpo parezca más segmentado, fíjate que aparecen 7 figuras por cada línea del código - **escribe** `square 30`\n11. Le agregaremos otro detalle a la cola, configura el color a verde oscuro, darkgreen en inglés\n12. Dibuja otro círculo, el código todavía está programado para que se repita 7 veces - **escribe** `circle 20`\n13. Finalmente, dibuja un cuadrado de un tamaño de 20 píxeles\n14. Para lo siguiente asegúrate de estar fuera del ciclo for presionando la tecla borrar. Vamos a dibujar el ojo - **escribe** `moveTo 70,240`\n15. Cambia el `color` de dibujo a negro\n16. Dibuja una `ellipse` de 10 píxeles de ancho y 5 de alto.\n17. Finalmente le dibujaremos una lengua, para que pueda silbar como toda víbora - **escribe** `color salmon`\n18. Posiciónala en la cara - **escribe** `move -15, 20`\n19. Dibujaremos un rectángulo con el ancho en negativo para hacer que la lengua salga directo de la boca - **escribe** `rectangle -20,4`\n\n#### Lo que hackearás\nEl encanto del ciclo es que puedes cambiar el código una vez y todas las figuras que has dibujado se actualizarán. Esta es otro motivo por el cual utilizar los ciclos, puedes realizar grandes cambios sin necesidad de ingresar muchos códigos.\n\n#### Informe\n¿Cuántas veces has escrito el mismo comando una y otra vez en los últimos desafíos? En el desafío de la viborita, su cuerpo está compuesto por muchos círculos. En lugar de escribir un comando por cada círculo, ¡la computadora puede hacerlo por ti!\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/gradient.json b/lib/challenges/locales/es/worlds/pixelhack/gradient.json
new file mode 100755
index 000000000..7d87fdb6a
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/gradient.json
@@ -0,0 +1,53 @@
+{
+ "id": "gradient",
+ "title": "Atardecer en 8-bits",
+ "description": "Usa un ciclo para dibujar un atardecer en 8-bits.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Únicamente utilizaremos figuras sólidas sin trazo, entonces **escribe** `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Para ver mejor los resultados en esta prueba activa la negrita del texto utilizando el comando `bold true`. Bold significa negrita y true verdadero, eso quiere decir que está activado",
+ "solution": "bold true"
+ },
+ {
+ "hint": "Utilizaremos un ciclo para dibujar el atardecer. Esta vez irá de 0 a 10. **Escribe ** `for y in [ 0 .. 10 ]`",
+ "solution": "for y in [ 0 .. 10 ]"
+ },
+ {
+ "hint": "Para entender mejor cómo funciona nuestro ciclo, vamos a dibujar cada uno de los valores. Muévete a la posición usando `moveTo 250, y * 50`",
+ "solution": " moveTo 250, y * 50"
+ },
+ {
+ "hint": "Vamos a inspeccionar qué hace nuestro ciclo. La función de texto (text en inglés) nos permitirá verlo. **Escribe ** `text y` para ver los valores de Y a través de la pantalla.",
+ "solution": " text y",
+ "validate": " text y"
+ },
+ {
+ "hint": "La variable Y crece a medida que avanza el ciclo. Va de 0 (que se encuentra fuera de la pantalla) a 10. Para dibujar el atardecer necesitamos movernos al borde izquierdo **Escribe ** `moveTo 0, y * 50`",
+ "solution": " moveTo 0, y * 50"
+ },
+ {
+ "hint": "Utilizaremos una función especial para ir oscureciendo el color en cada valor del ciclo. **Escribe ** `color darken blue, y * 3`.",
+ "solution": " color darken blue, y * 3"
+ },
+ {
+ "hint": "Ahora para el cielo haremos rectángulos que ocupan toda la pantalla, cubrirán los números. **Escribe ** `rectangle 500, 50`.",
+ "solution": " rectangle 500, 50",
+ "validate": " rectangle 500, 50"
+ }
+ ],
+ "completion_text": "¡Qué bonito! Observa cómo se va oscureciendo el azul en tu pantalla. La variable Y se transfería a la función de oscurecer. A medida que aumentaba el valor de Y, el color azul se fue oscureciendo.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "grad-yello.png",
+ "grad-sword.png",
+ "grad-mage.png"
+ ]
+ },
+ "cover": "pixel-gradient.png",
+ "guide": "#### Nuevas palabras\n**bold** state | bold true\n\nPuede configurar si el texto será escrito en formato negrita o no. True significa verdadero y activa la negrita. False significa falso, lo cual desactiva la negrita en el texto.\n\n**text** string | text “hola mundoâ€\n\nDibuja texto centrado en donde se ubique el cursor de dibujo. Las propiedades del texto se controlan con funciones de color, fuente, negrita y cursiva.\n\n\n#### Lo que crearás\n1. Únicamente utilizaremos figuras sólidas sin trazo, entonces **escribe ** `stroke 0`.\n2. Para ver mejor los resultados en esta prueba activa la negrita del texto utilizando el comando `bold true` bold significa negrita y true verdadero, eso quiere decir que está activado\n3. Utilizaremos un ciclo para dibujar el atardecer. Esta vez irá de 0 a 10. **escribe ** `for y in [ 0 .. 10 ]`\n4. Para entender mejor cómo funciona un ciclo, vamos a dibujar cada uno de los valores del ciclo. Muévete a la posición usando `moveTo 250, y * 50`\n5. Vamos a inspeccionar qué hace nuestro ciclo. La función de texto (text en inglés) nos permitirá verlo. **Escribe ** `text y` para ver los valores de Y a través de la pantalla.\n6. La variable Y crece a medida que avanza el ciclo. Va de 0 (que se encuentra fuera de la pantalla) a 10. Para dibujar el atardecer necesitamos movernos al borde izquierdo. **Escribe ** `moveTo 0, y * 50`\n7. Utilizaremos una función especial para ir oscureciendo el color en cada valor del ciclo. **Escribe ** `color darken blue, y * 3`.\n8. Ahora para el cielo haremos rectángulos que ocupan toda la pantalla, cubrirán los números. **Escribe ** `rectangle 500, 50`.\n\n\n#### Lo que hackearás\nAhora que ya tienes amplia experiencia en arte en píxeles ¿por qué no traes aquí tus creaciones y les damos el tratamiento de fondo que se merecen?\n\n#### Informe\nCon un ciclo puedes pedirle a la computadora que repita algo una y otra vez. Con el ciclo `for`, puedes perdirle que lo haga una determinada cantidad de veces y, dependiendo de tipo de ciclo que sea, cambiar qué hace.\n\nPara el atardecer, queremos dibujar bloques rectangulares del mismo tamaño y queremos movernos hacia abajo una determinada distancia cada vez que se dibuje uno. Pero esta vez, necesitamos comunicarle a la computadora que queremos que tengan un color diferente, ¿cómo lo hacemos? Con variables. El modo en el que puedes preguntar cuántas veces el ciclo se ejecutó es con una palabra con la que comience. Cuando escribimos `for y in [ 0 .. 10]`, estamos convirtiendo a la palabra `Y` igual a la cantidad de veces que se ha ejecutado el contenido del ciclo. En cualquier momento que se utilice la variable `Y`, le dirá al programa cuántas veces se ha repetido.\n\nComo aumenta de a un número por vez, podemos utilizarla para viajar a través de la sombra de los colores oscureciendo de a un tono el color a medida que crece la variable Y. Entonces al comienzo del ciclo tendremos un valor pequeño para Y y un color regular, hacia el final tendremos un valor más grande para Y y un color más oscuro.\n\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/grassblock.json b/lib/challenges/locales/es/worlds/pixelhack/grassblock.json
new file mode 100755
index 000000000..64dc8abd7
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/grassblock.json
@@ -0,0 +1,60 @@
+{
+ "id": "grassblock",
+ "title": "Bloque de hierba en 8-bits",
+ "description": "Crea un bloque que podrías trasladar a Minecraft.",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "Únicamente utilizaremos figuras sólidas sin trazo. Entonces **escribe** `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Precisamos dos ciclos para este dibujo. **Escribe** `for x in [0 ... 16]`",
+ "solution": "for x in [0 ... 16]"
+ },
+ {
+ "hint": "Para Y utiliza `for y in [0 ... 16]`",
+ "solution": " for y in [0 ... 16]",
+ "validate": " for y in \\[0 ... 16\\]"
+ },
+ {
+ "hint": "Utilizaremos un tono de marrón elegido aleatoriamente para representar la tierra. **Escribe** `color darken brown, random 0, 25`",
+ "solution": " color darken brown, random 0, 25",
+ "validate": " color darken brown, random 0, 25"
+ },
+ {
+ "hint": "Necesitaremos movernos para cada cuadrado utilizando `moveTo x * 31.25, y * 31.25`",
+ "solution": " moveTo x * 31.25, y * 31.25"
+ },
+ {
+ "hint": "Finalmente dibujemos los cuadrados de tierra utilizando `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ },
+ {
+ "hint": "El césped debería aparecer en la parte de arriba del bloque por eso determinemos una condición que posicione los bits verdes utilizando `if 4 > y + random 0, 3`",
+ "solution": " if 4 > y + random 0, 3"
+ },
+ {
+ "hint": "Configura el color verde con la función para oscurecer. **Escribe** `color darken green, random 0, 25`",
+ "solution": " color darken green, random 0, 25",
+ "validate": " color darken green, random 0, 25"
+ },
+ {
+ "hint": "Finalmente dibuja los cuadrados utilizando `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ }
+ ],
+ "completion_text": "¡Muy bonito! Ahora puedes cambiar el color marrón y el verde para obtener efectos interesantes. ¿Puedes hacer un bloque que se parezca a una fresa?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "grass-overgrown.png",
+ "grass-strawb.png",
+ "grass-myce.png"
+ ]
+ },
+ "cover": "pixel-grassblock.png",
+ "guide": "#### Lo que crearás\n1. Únicamente utilizaremos figuras sólidas sin trazo. Entonces **escribe** `stroke 0`.\n2. Precisamos dos ciclos para este dibujo. **Escribe** `for x in [0 ... 16]`\n3. Para y utiliza `for y in [0 ... 16]`\n4. Utilizaremos un tono de marrón elegido aleatoriamente para representar la tierra. **Escribe** `color darken brown, random 0, 25`\n5. Necesitaremos movernos para cada cuadrado utilizando `moveTo x * 31.25, y * 31.25`\n6. Finalmente dibujemos los cuadrados de tierra utilizando `square 32`\n7. El césped debería aparecer en la parte de arriba del bloque por eso determinemos una condición que posicione los bits verdes utilizando `if 4 > y + random 0, 3`\n8. Configura el color verde con la función para oscurecer. **Escribe** `color darken green, random 0, 25`\n9. Finalmente dibuja los cuadrados utilizando `square 32`\n\n#### Lo que hackearás\nPara personalizar este bloque de hierba de Minecraft todo lo que debes hacer es cambiar dos variables: el color de arriba y el color de abajo. Crea una fresa, un bloque de agua o cualquier otra cosa únicamente cambiando esas dos líneas de comandos. Para añadir complejidad, crea un bloque de micelio usando un ciclo y la función aleatoria. Si de verdad buscas un desafío, intenta incorporar lo que aprendimos en el dibujo anterior y utiliza los valores de X e Y para rotar los tonos de los colores.\n\n#### Resumiendo \nUno de los bloques más comunes y básicos en Minecraft es el de hierba. Únicamente 16 cuadrados hacia arriba y hacia abajo, es una imagen muy simple, pero utilizaremos la función aleatoria para darle textura. Dibujaremos dos capas distintas, la del césped y la de la tierra.\n\nEn cada capa utilizaremos un ciclo para dibujar horizontal y verticalmente a través de la pantalla y únicamente cambiaremos una cosa de cada una: el color.\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/index.json b/lib/challenges/locales/es/worlds/pixelhack/index.json
new file mode 100755
index 000000000..2552d1e17
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/index.json
@@ -0,0 +1,17 @@
+{
+ "challenges": [
+ "./pong",
+ "./ship",
+ "./tetris",
+ "./chest",
+ "./variables",
+ "./sword",
+ "./steve",
+ "./mage",
+ "./forloop",
+ "./gradient",
+ "./colorfrenzy",
+ "./grassblock",
+ "./diamondblock"
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/mage.json b/lib/challenges/locales/es/worlds/pixelhack/mage.json
new file mode 100755
index 000000000..47911302e
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/mage.json
@@ -0,0 +1,208 @@
+{
+ "id": "mage",
+ "title": "Maga",
+ "description": "Dibuja una Maga Aqua en 8-bits",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Crearemos el personaje de una Maga en un Juego de Rol. Primero necesitamos montar el escenario, utilizaremos la función de oscurecer para cambiar el color verde azulado a un tono más similar al azul oscuro - **escribe** `background darken teal,15 `",
+ "solution": "background darken teal,15"
+ },
+ {
+ "hint": "Estamos haciendo arte en píxeles, no necesitamos el trazo - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configuremos el color a un tono de marrón para su cabello - **escribe** `color saddlebrown`",
+ "solution": "color saddlebrown"
+ },
+ {
+ "hint": "Utilizaremos la función moveTo para mover el cursor a una coordenada específica - **escribe** `moveTo 120,80`",
+ "solution": "moveTo 120,80"
+ },
+ {
+ "hint": "Primero dibujaremos su cabello y el resto de los rasgos de la cara por encima para crearle profundidad - **escribe** `rectangle 320,400`",
+ "solution": "rectangle 320,400"
+ },
+ {
+ "hint": "Dibujaremos con bloques de 20 por 20 píxeles de tamaño- **escribe** `move 20,-20`",
+ "solution": "move 20,-20"
+ },
+ {
+ "hint": "Dibuja otra capa de cabello - **escribe** `rectangle 280,20`",
+ "solution": "rectangle 280,20"
+ },
+ {
+ "hint": "Muévete hacia arriba otra vez para crear un efecto redondeado - **escribe** `move 20,-20`",
+ "solution": "move 20,-20"
+ },
+ {
+ "hint": "Dibuja una fila más de cabello - **escribe** `rectangle 240,20`",
+ "solution": "rectangle 240,20"
+ },
+ {
+ "hint": "Ahora dibujaremos la cara, - **escribe** `color sandybrown`",
+ "solution": "color sandybrown"
+ },
+ {
+ "hint": "Mueve el cursor a la esquina de arriba en donde dibujaremos la cara - **escribe** `move 0,120`",
+ "solution": "move 0,120"
+ },
+ {
+ "hint": "Dibuja la cara utilizando un rectángulo de 240 por 140 de tamaño",
+ "solution": "rectangle 240,140"
+ },
+ {
+ "hint": "La cara se ve un poco cuadrada, por eso mueve el cursor hacia la frente - **escribe** `move 80,-40`",
+ "solution": "move 80,-40"
+ },
+ {
+ "hint": "Dibuja otro rectángulo para darle un flequillo - **escribe** `rectangle 80,40`",
+ "solution": "rectangle 80,40"
+ },
+ {
+ "hint": "Mueve el cursor hacia abajo en la cara - **escribe** `move -40,180`",
+ "solution": "move -40,180"
+ },
+ {
+ "hint": "Termina la cara dibujando el mentón - **escribe** `rectangle 160,20`",
+ "solution": "rectangle 160,20"
+ },
+ {
+ "hint": "Ahora le dibujaremos una túnica, nuestra maga se maneja en la Magia de Agua entonces la haremos azul- **escribe** `color paleturquoise`",
+ "solution": "color paleturquoise"
+ },
+ {
+ "hint": "Posiciona el cursor para dibujarle las mangas - **escribe** `move -100,20`",
+ "solution": "move -100,20"
+ },
+ {
+ "hint": "Dibuja un rectángulo de 360 de largo y 80 de alto para las mangas",
+ "solution": "rectangle 360,80"
+ },
+ {
+ "hint": "Posiciona el cursor para dibujar el cuerpo de la túnica - **escribe** `move 60,80`",
+ "solution": "move 60,80"
+ },
+ {
+ "hint": "Dibuja un rectángulo de 240 de ancho y 100 de alto para el cuerpo de la túnica",
+ "solution": "rectangle 240,100"
+ },
+ {
+ "hint": "Ahora dibujaremos una costura en la túnica, cambia el color a verde azulado",
+ "solution": "color teal"
+ },
+ {
+ "hint": "Mueve el cursor a la mitad de la túnica - **escribe** `move 120,-80`",
+ "solution": "move 120,-80"
+ },
+ {
+ "hint": "Dibuja un rectángulo angosto para la costura - **escribe** `rectangle 40,180`",
+ "solution": "rectangle 40,180"
+ },
+ {
+ "hint": "Ahora dibujaremos los brazos - **escribe** `move 140,40`",
+ "solution": "move 140,40"
+ },
+ {
+ "hint": "Cambia el color a un tono de marrón, sandybrown en inglés",
+ "solution": "color sandybrown"
+ },
+ {
+ "hint": "Dibuja un brazo utilizando un rectángulo de 40 por 140",
+ "solution": "rectangle 40,140"
+ },
+ {
+ "hint": "Muévete hacia el otro lado del cuerpo - **escribe** `move -380,40`",
+ "solution": "move -380,40"
+ },
+ {
+ "hint": "Este brazo no estará unido al cuerpo pero no te preocupes, no lo hemos terminado aún - **escribe** `rectangle 40,100`",
+ "solution": "rectangle 40,100"
+ },
+ {
+ "hint": "Toda maga necesita un bastón para lanzar su magia, dibujaremos eso - **escribe** `color darkmagenta`",
+ "solution": "color darkmagenta"
+ },
+ {
+ "hint": "Muévete hacia arriba del bastón - **escribe** `move 60,-120`",
+ "solution": "move 60,-120"
+ },
+ {
+ "hint": "Dibuja el bastón por encima del brazo para que parezca que la sostiene en su mano - **escribe** `rectangle -40,220`",
+ "solution": "rectangle -40,220"
+ },
+ {
+ "hint": "Como dijimos al comienzo, nuestro personaje usa magia acuática, entonces configura el color a aguamarina - **escribe** `color aquamarine`",
+ "solution": "color aquamarine"
+ },
+ {
+ "hint": "Dibuja el extremo del bastón, si utilizas números negativos las figuras se darán vuelta - **escribe** `square -80`",
+ "solution": "square -80"
+ },
+ {
+ "hint": "También dibujaremos una diadema mágica, muévete hacia la frente - **escribe** `move 60,-200`",
+ "solution": "move 60,-200"
+ },
+ {
+ "hint": "Dibuja un rectángulo de 240 por 20",
+ "solution": "rectangle 240,20"
+ },
+ {
+ "hint": "Muévete un espacio hacia abajo en la grilla - **escribe** `move -20,20`",
+ "solution": "move -20,20"
+ },
+ {
+ "hint": "Termina la diadema - **escribe** `rectangle 280,20`",
+ "solution": "rectangle 280,20"
+ },
+ {
+ "hint": "Aún debemos terminar la cara, posiciona el cursor en el sector de los ojos - **escribe** `move 60,60`",
+ "solution": "move 60,60"
+ },
+ {
+ "hint": "Configura el color a negro",
+ "solution": "color black"
+ },
+ {
+ "hint": "Dibuja los ojos largos y finos - **escribe** `rectangle 40,80`",
+ "solution": "rectangle 40,80"
+ },
+ {
+ "hint": "Muévete al sector del otro ojo - **escribe** `move 120,0`",
+ "solution": "move 120,0"
+ },
+ {
+ "hint": "Dibuja el otro ojo - **escribe** `rectangle 40,80`",
+ "solution": "rectangle 40,80"
+ },
+ {
+ "hint": "Un último detalle. Haremos los ojos un poco más brillantes - **escribe** `color white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja un rectángulo más pequeño - **escribe** `rectangle 20,40`",
+ "solution": "rectangle 20,40"
+ },
+ {
+ "hint": "Muévete al otro ojo - **escribe** `move -120,0`",
+ "solution": "move -120,0"
+ },
+ {
+ "hint": "Termínalo con un último rectángulo - **escribe** `rectangle 20,40`",
+ "solution": "rectangle 20,40"
+ }
+ ],
+ "completion_text": "¡Bien hecho! Tu maga está lista para ingresar a alguna cueva y lanzar algunos hechizos. ¿Por qué no cambias los colores de su vestimenta a rojo para que sea una Maga de Fuego?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "mage-fire.png",
+ "mage-light.png",
+ "mage-warrior.png"
+ ]
+ },
+ "cover": "pixel-mage.png",
+ "guide": "#### Lo que crearás\n1. Crearemos el personaje de una Maga en un Juego de Rol. Primero necesitamos montar el escenario, utilizaremos la función de oscurecer para cambiar el color aguamarina a un tono más similar al azul oscuro - **escribe** `background darken teal,15 `\n2. Estamos haciendo arte en píxeles, no necesitamos el trazo - **escribe** `stroke 0`\n3. Configuremos el color a un tono de marrón para su cabello - **escribe** `color saddlebrown`\n4. Utilizaremos la función moveTo para mover el cursor a una coordenada específica - **escribe** `moveTo 120,80`\n5. Primero dibujaremos su cabello y el resto de los rasgos de la cara por encima para crearle profundidad - **escribe** `rectangle 320,400`\n6. Dibujaremos con bloques de 20 por 20 píxeles de tamaño- **escribe** `move 20,-20`\n7. Dibuja otra capa de cabello - **escribe** `rectangle 280,20`\n8. Muévete hacia arriba otra vez para crear un efecto redondeado - **escribe** `move 20,-20`\n9. Dibuja una fila más de cabello - **escribe** `rectangle 240,20`\n10. Ahora dibujaremos la cara, - **escribe** `color sandybrown`\n11. Mueve el cursor a la esquina de arriba en donde dibujaremos la cara- **escribe** `move 0,120`\n12. Dibuja la cara utilizando un rectángulo de 240 por 140 de tamaño\n13. La cara se ve un poco cuadrada, por eso mueve el cursor hacia la frente - **escribe** `move 80,-40`\n14. Dibuja otro rectángulo para darle un flequillo - **escribe** `rectangle 80,40`\n15. Mueve el cursor hacia abajo en la cara - **escribe** `move -40,180`\n16. Termina la cara dibujando la mentón - **escribe** `rectangle 160,20`\n17. Ahora le dibujaremos una túnica, nuestra maga se maneja en la Magia de Agua entonces la haremos azul- **escribe** `color paleturquoise`\n18. Posiciona el cursor para dibujarle las mangas - **escribe** `move -100,20`\n19. Dibuja un rectángulo de 360 de largo y 80 de alto para las mangas \n20. Posiciona el cursor para dibujar el cuerpo de la túnica - **escribe** `move 60,80`\n21. Dibuja un rectángulo de 240 de ancho y 100 de alto para el cuerpo de la túnica\n22. Ahora dibujaremos una costura en la túnica, cambia el color a aguamarina\n23. Mueve el cursor a la mitad de la túnica - **escribe** `move 120,-80`\n24. Dibuja un rectángulo angosto para la costura - **escribe** `rectangle 40,180`\n25. Ahora dibujaremos los brazos - **escribe** `move 140,40`\n26. Cambia el color a un tono de marrón, sandybrown en inglés\n27. Dibuja un brazo utilizando un rectángulo de 40 por 140\n28. Muévete hacia el otro lado del cuerpo - **escribe** `move -380,40`\n29. Este brazo no estará unido al cuerpo pero no te preocupes, no lo hemos terminado aún - **escribe** `rectangle 40,100`\n30. Toda maga necesita un bastón para lanzar su magia, dibujaremos eso - **escribe** `color darkmagenta`\n31. Muévete hacia arriba del bastón - **escribe** `move 60,-120`\n32. Dibuja el bastón por encima del brazo para que parezca que la sostiene en su mano - **escribe** `rectangle -40,220`\n33. Como dijimos al comienzo, nuestro personaje usa magia acuática, entonces configura el color a aguamarina - **escribe** `color aquamarine`\n34. Dibuja el extremo del bastón, si utilizas números negativos las figuras se darán vuelta - **escribe** `square -80`\n35. También dibujaremos una diadema mágica, muévete hacia la frente - **escribe** `move 60,-200`\n36. Dibuja un rectángulo de 240 por 20\n37. Muévete un espacio hacia abajo en la grilla - **escribe** `move -20,20`\n38. Termina la diadema - **escribe** `rectangle 280,20`\n39. Aún debemos terminar la cara, posiciona el cursor en el sector de los ojos - **escribe** `move 60,60`\n40. Configura el color a negro\n41. Dibuja los ojos largos y finos - **escribe** `rectangle 40,80`\n42. Muévete al sector del otro ojo - **escribe** `move 120,0`\n43. Dibuja el otro ojo - **escribe** `rectangle 40,80`\n44. Un último detalle. Haremos los ojos un poco más brillantes - **escribe** `color white`\n45.Dibuja un rectángulo más pequeño - **escribe** `rectangle 20,40`\n46. Muévete al otro ojo - **escribe** `move -120,0`\n47. Termínalo con un último rectángulo - **escribe** `rectangle 20,40`\n\n#### Lo que hackearás\nEste es un dibujo mucho más intrincado, hay muchas cosas para cambiar. Puedes ir a través del dibujo y cambiar los colores creando una nueva paleta de colores y agregar nuevos objetos. Echa un vistazo a la guerrera y a otras magas elementales como ejemplos para mejorarla con nuevos poderes, armaduras y armas.\n\n#### Informe\nLas figuras simples que has estado juntando en estos diseños pueden ser redefinidas y transformadas en creaciones más complejas. La Maga de los Juegos de Rol sigue un diseño un poco más sofisticado, pero se utilizan las mismas funciones para crearla.\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/pong.json b/lib/challenges/locales/es/worlds/pixelhack/pong.json
new file mode 100755
index 000000000..78b9cebbb
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/pong.json
@@ -0,0 +1,40 @@
+{
+ "id": "pong",
+ "title": "Pong",
+ "description": "Programa una partida de Pong",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Pong (1972) fue una de las primeras consolas de videojuegos domésticas. Es un increíblemente básico juego de tenis de mesa. Y ¿qué necesitamos para jugar tenis de mesa? Primero, una pelota. Usaremos los comandos en inglés. Círculo se dice circle **escribe** `circle 15`",
+ "solution": "circle 15"
+ },
+ {
+ "hint": "Luego necesitamos dibujar las raquetas. Usaremos la función moveTo, que significa mover a, para ubicar objetos. El primer número después de moveTo dice la posición horizontal del objeto (izquierda y derecha). El segundo número, dice la posición vertical (arriba y abajo). **escribe** `moveTo 30,100`",
+ "solution": "moveTo 30,100"
+ },
+ {
+ "hint": "Dibujaremos la primera raqueta usando la función del rectángulo. Rectángulo se dice rectangle **escribe** `rectangle 20, 100`",
+ "solution": "rectangle 20, 100"
+ },
+ {
+ "hint": "No es divertido jugar Pong sin un oponente... **escribe** `moveTo 450,300`",
+ "solution": "moveTo 450,300"
+ },
+ {
+ "hint": "Finalmente, dibujemos la raqueta del oponente. El primer número es el ancho, y el segundo la altura. **escribe** `rectangle 20, 100`",
+ "solution": "rectangle 20, 100"
+ }
+ ],
+ "completion_text": "¡Bien hecho! Has recreado uno de los primeros videojuegos de la historia, bastante aburrido de mirar, ¿no? ¿Por qué no intentas hacer click en el número 100 de la primera función moveTo? y utilizar el control deslizante para reposicionar la raqueta?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "pong-white.png",
+ "pong-movecolor.png",
+ "pong-movepaddles.png"
+ ]
+ },
+ "cover": "pixel-pong.png",
+ "guide": "#### Nuevas palabras\n**circle** radius | circle 15\n\nDibuja un círculo en la posición actual del cursor. Cuánto más grande el número de radio que ingreses, más grande será el círculo: el radio es la distancia desde el centro del círculo al borde.\n\n**moveTo** x, y | moveTo 30, 100\n\nMueve la posición de dibujo al nuevo lugar que asignes en X, Y. El primer valor es el valor de la coordenada X, la cual controla la ubicación en el eje horizontal. El segundo valor es la coordenada Y, la cual controla la ubicación en el eje vertical.\n\n**rectangle** width, height | rectangle 20, 100\n\nDibuja un rectángulo en la posición actual del cursor. La anchura y la altura controlan independientemente cuán ancho y cuán alto será el rectángulo.\n\n**color** name | color red\n\nCambia el color de dibujo. Las siguientes figuras y texto serán dibujados con el color que configures hasta cambiarlo nuevamente. Para configurarlo a transparente escribe `color null`\n\n**background** colorName | background white\n\nCambia el color del fondo.\n\n#### Lo que crearás\n1. Pong (1972) fue una de las primeras consolas de videojuegos. Es un increíblemente básico juego de tenis de mesa. Y ¿qué necesitamos para jugar tenis de mesa? Primero, una pelota. Usaremos los comandos en inglés. Círculo se dice circle **Escribe** `circle 15`\n2. Luego necesitamos dibujar las raqueta. Usamos la función moveTo para ubicar objetos. El primer número después de moveTo dice la posición horizontal del objeto (izquierda y derecha). El segundo número, dice la posición vertical (arriba y abajo). **escribe** `moveTo 30,100`\n3. Dibujaremos la primera raqueta usando la función del rectángulo. Rectángulo se dice rectangle **escribe** `rectangle 20, 100`\n4. No es divertido jugar Pong sin un oponente **escribe** `moveTo 450,300`\n5. Finalmente, dibujemos la raqueta del oponente, el primer número es el ancho, y el segundo la altura. **escribe** `rectangle 20, 100`\n\n#### Lo que hackearás\nExiste una función especial que todo artista de programación debería conocer: el color. El color predeterminado siempre será el negro y cada vez que una figura sea dibujada en Hacer Arte, la función de dibujo comprueba qué color se debe utilizar. Entonces, como no has especificado un color, todo será dibujado de color negro.\n\nPuedes cambiar el color a rojo, por ejemplo, escribiendo color red luego del primer comando `moveTo` y antes del comando `rectangle 20, 100`para cambiar el color de ambas raquetas. Nota: esto solamente afectará a las figuras dibujadas luego de este comando, por eso escribirlo al principio del desafío no hará nada. \n\nEl color del fondo puede ser modificado con background charcoal. Puedes poner esto en cualquier parte del código, y para la mayoría de creaciones sólo lo ingresarás una vez. Es mejor ponerlo al comienzo, antes de cualquier código.\n\nFinalmente, puedes cambiar la posición de las raquetas hacia arriba y hacia abajo cambiando el segundo número al lado de la función moveTo. Puedes hacer click en el número y jugar con la posición usando el control deslizante para cambiar el número.\n\n#### Informe\nEn 1972, Al Alcorn recibió un ejercicio de calentamiento en su nuevo empleo en la compañía Atari, y nació Pong: dos raquetas, una pelota, y poco después, cola de gente a la vuelta de la esquina para comenzar a jugarlo. Pong se convirtió en una sensación a nivel internacional, vendiendo millones de copias. Fue tan famoso, que docenas de imitaciones fueron creadas por competidores que intentaban aprovecharse del éxito de la compañía Atari. Lo que lo hizo tan simple y universal terminó por convertirlo en fácil de copiar. \n\nTu primera tarea como hacker es crear tu propia versión de Pong. Uno de los secretos de un gran hacker es mezclar ideas de otros lugares para crear algo nuevo. ¡Puedes tomar la idea original de Pong y hackearla para crear tu propio juego! Crearás un escenario básico de dos raquetas y una pelota. Luego, ¡depende de ti hackearlo para crear algo nuevo! \n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/ship.json b/lib/challenges/locales/es/worlds/pixelhack/ship.json
new file mode 100755
index 000000000..04a4e92a7
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/ship.json
@@ -0,0 +1,68 @@
+{
+ "id": "ship",
+ "title": "Asteroides",
+ "description": "Programa una nave espacial de vectores usando líneas",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Asteroides (1979) fue uno de los primeros videojuegos de arcade importantes. Está situado en el espacio, así que programemos un fondo adecuado **escribe** `background black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "Los gráficos en Asteroides eran líneas simples denominadas vectores. Hoy en día, aún se utiliza una versión más compleja de este concepto en los gráficos de las computadoras. Primero necesitamos configurar el tamaño del trazo y su color **escribe** `stroke white,5`",
+ "solution": "stroke white,5"
+ },
+ {
+ "hint": "Luego usamos la función move para configurar el punto de partida de nuestra primera línea de la nave. **escribe** `move 0, -70`",
+ "solution": "move 0, -70"
+ },
+ {
+ "hint": "'Función' es la palabra que utilizamos para definir lo que estamos dibujando. Dibujemos una línea que empiece justo donde movimos el cursor y termine abajo a la izquierda. Primero escribimos la función (line) y luego dónde queremos que empiece y termine la línea **escribe** `line -50,150` Intenta cambiar el valor de Y, de 150 a -150 y fíjate lo que sucede. ¡La línea va hacia arriba! ¡Mejor, muévela nuevamente a 150!",
+ "solution": "line -50,150"
+ },
+ {
+ "hint": "Para mover el cursor al punto donde queremos que nazca la próxima línea **escribe** `move -50,150`",
+ "solution": "move -50,150"
+ },
+ {
+ "hint": "Dibujamos una línea hacia adentro y hacia afuera para crear el propulsor de nuestra nave - **escribe** `line 50,-25`",
+ "solution": "line 50,-25"
+ },
+ {
+ "hint": "Muévete a las mismas coordenadas para continuar la línea en otra dirección **escribe** `move 50,-25`",
+ "solution": "move 50,-25"
+ },
+ {
+ "hint": "Replicamos la forma para dibujar el lado derecho de la nave dando vuelta la segunda coordenada de la línea anterior para dibujar hacia abajo en vez de hacia arriba **escribe** `line 50,25`",
+ "solution": "line 50,25"
+ },
+ {
+ "hint": "Muévete al extremo de esa línea **escribe** `move 50,25`",
+ "solution": "move 50,25"
+ },
+ {
+ "hint": "Nuestra nave comienza a tener forma. Para hacer una línea que conecte el extremo de abajo con el de arriba **escribe** `line -50,-150`",
+ "solution": "line -50,-150"
+ },
+ {
+ "hint": "Finalmente volvemos al punto de partida para darle un toque final **escribe** `move -50,-150`",
+ "solution": "move -50,-150"
+ },
+ {
+ "hint": "Dibujamos una línea que atraviese el centro para que esta simple figura se parezca más a una nave. Los primeros videojuegos contaban sólo con una parte del gran poder informático con el que contamos hoy, por eso los gráficos tenían que ser muy básicos. **Escribe** `line 0,125`",
+ "solution": "line 0,125"
+ }
+ ],
+ "completion_text": "Tal vez no es gran cosa pero los gráficos simples de los primeros videojuegos, sentaron las bases para los gráficos 3D que se usan hoy en día.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "ship-golden.png",
+ "ship-thrust.png",
+ "ship-sleek.png"
+ ]
+ },
+ "cover": "pixel-ship.png",
+ "guide": "#### Nuevas palabras\n**move** x, y | move 50, 25\n\nMueve el cursor de dibujo desde la posición actual hacia la cual indiques en los números X e Y. La diferencia con moveTo es que la última es relativa, no absoluta. `move 50, 25` moverá el cursor 50 hacia la derecha y 25 hacia abajo desde la posición actual. El primer valor es el valor de la coordenada X, la cual controla la ubicación en el eje horizontal. El segundo valor es la coordenada Y, la cual controla la ubicación en el eje vertical. Los valores negativos los ubicarán en las direcciones opuestas (izquierda y arriba).\n\n**line** x, y | line 50, 25\n\nDibuja una línea desde la posición actual hasta la que se indique. Las coordenadas X, Y controlan el punto relativo hasta el cual se dibujará la línea.\n\n**stroke** color, width | stroke white, 5\n\nConfigura el trazo del dibujo. Las siguientes figuras y el texto serán dibujados con el color y el ancho del trazo que configures hasta cambiarlo nuevamente. Esta función es inteligente y puede aceptar el color o el tamaño en cualquier orden, o uno por vez.\n\n#### Lo que crearás\n1.Asteroides (1979) fue uno de los primeros videojuegos de arcade importantes. Está situado en el espacio, por eso vamos a tener que establecer el escenario **escribe** `background black`\n2. Los gráficos en Asteroides eran líneas simples denominadas vectores. Hoy en día, aún se utiliza una versión más compleja de este concepto en los gráficos de las computadoras. Primero necesitamos configurar el tamaño del trazo y su color **escribe** `stroke white,5`\n3. Luego usamos la función move para configurar el punto de partida de nuestra primera línea de la nave. **escribe** `move 0, -70`\n4. Utilizamos la función line para dibujar una línea relativa al punto de partida, utilizamos un número negativo para que vaya hacia la izquierda y luego un número positivo para que vaya hacia abajo **escribe** `line -50,150`\n5. Para mover el cursor al punto donde queremos que nazca la próxima línea **escribe** `move -50,150`\n6. Dibujamos una línea hacia adentro y hacia afuera para crear el propulsor de nuestra nave - **escribe** `line 50,-25`\n7. Muévete a las siguientes coordenadas para continuar la línea en otra dirección **escribe** `move 50,-25`\n8. Replicamos la forma para dibujar el lado derecho de la nave dando vuelta la segunda coordenada de la línea anterior para dibujar hacia abajo en vez de hacia arriba **escribe** `line 50,25`\n9. Muévete al extremo de esa línea **escribe** `move 50,25`\n10. Nuestra nave comienza a tener forma. Para hacer una línea que conecte el extremo de abajo con el de arriba **escribe** `line -50,-150`\n11. Finalmente volvemos al punto de partida para darle un toque final **escribe** `move -50,-150`\n12. Dibujamos una línea que atraviese el centro para que esta simple figura se parezca más a una nave, los primeros videojuegos contaban sólo con una parte del gran poder informático con el que contamos hoy, por eso los gráficos tenían que ser muy básicos. **Escribe** `line 0,125`\n\n#### Lo que hackearás\nTal vez no sean gran cosa pero los gráficos simples de los primeros videojuegos, sentaron las bases para los gráficos 3D que se utilizan hoy en día. Puedes transformar tu nave configurando el color y el ancho del trazo. \n\nPara algo más avanzado, intenta dibujar más líneas.\n\n#### Informe\nAsteroides (1979) fue uno de los primeros videojuegos de arcade importantes. Limitados por hardware primitivo, los diseñadores del juego únicamente podían utilizar líneas simples basadas en gráficos llamadas vectores. Hoy en día se utiliza una versión más compleja de este concepto en los gráficos de computadora.\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/steve.json b/lib/challenges/locales/es/worlds/pixelhack/steve.json
new file mode 100755
index 000000000..7178c1225
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/steve.json
@@ -0,0 +1,127 @@
+{
+ "id": "steve",
+ "title": "Steve",
+ "description": "Crea la cara de Steve utilizando figuras simples.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Únicamente utilizaremos cuadrados y rectángulos sólidos, por eso configuremos el trazo a 0 usando `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configura el color de fondo a marrón claro",
+ "solution": "background lightbrown"
+ },
+ {
+ "hint": "Configura el color de dibujo a marrón utilizando `color brown`.",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Su cabello comenzará en la esquina de arriba a la izquierda, entonces **escribe** `moveTo 0, 0`.",
+ "solution": "moveTo 0, 0"
+ },
+ {
+ "hint": "Dibujaremos un rectángulo con parámetros de anchura y altura. **Escribe** `rectangle 500, 150`",
+ "solution": "rectangle 500, 150"
+ },
+ {
+ "hint": "Configura el color de dibujo a marrón claro**escribe** `color lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Muévete para dibujar la frente, **escribe** `moveTo 100, 100`",
+ "solution": "moveTo 100, 100"
+ },
+ {
+ "hint": "Ahora para dibujar la frente, **escribe** `rectangle 300, 50`",
+ "solution": "rectangle 300, 50"
+ },
+ {
+ "hint": "Para los ojos, configura el color a blanco",
+ "solution": "color white"
+ },
+ {
+ "hint": "Ahora muévete usando `moveTo 100, 200`",
+ "solution": "moveTo 100, 200"
+ },
+ {
+ "hint": "Dibuja un ojo usando `square 50`",
+ "solution": "square 50"
+ },
+ {
+ "hint": "Para el otro ojo, **escribe** `moveTo 350, 200`",
+ "solution": "moveTo 350, 200"
+ },
+ {
+ "hint": "Y dibuja otro cuadrado de un tamaño de 50",
+ "solution": "square 50"
+ },
+ {
+ "hint": "Para el iris utilizaremos color violeta: recuerda que en inglés se dice purple.",
+ "solution": "color purple"
+ },
+ {
+ "hint": "Muévete a las coordenadas `150, 200` utilizando moveTo.",
+ "solution": "moveTo 150, 200"
+ },
+ {
+ "hint": "Dibuja un cuadrado de un tamaño de 50.",
+ "solution": "square 50"
+ },
+ {
+ "hint": "Muévete a las coordenadas `300, 200`.",
+ "solution": "moveTo 300, 200"
+ },
+ {
+ "hint": "Y dibuja el iris final con un cuadrado de un tamaño de 50.",
+ "solution": "square 50"
+ },
+ {
+ "hint": "Ahora dibujaremos la nariz. **Escribe** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Muévete utilizando `moveTo 200, 250`.",
+ "solution": "moveTo 200, 250"
+ },
+ {
+ "hint": "Dibuja un rectángulo de `100` de anchura y `50` de altura.",
+ "solution": "rectangle 100, 50"
+ },
+ {
+ "hint": "Para hacer la boca configura el color a otro tono de marrón utilizando `color saddlebrown`.",
+ "solution": "color saddlebrown"
+ },
+ {
+ "hint": "Muévete a las coordenadas `150, 300`",
+ "solution": "moveTo 150, 300"
+ },
+ {
+ "hint": "La boca debería ser un gran rectángulo. **Escribe** `rectangle 200, 100`.",
+ "solution": "rectangle 200, 100"
+ },
+ {
+ "hint": "Para mejorar la boca deberíamos cortarla un poco. Configura el color de dibujo al mismo color de la piel. **Escribe** `color lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Muévete a las coordenadas `200, 300`.",
+ "solution": "moveTo 200, 300"
+ },
+ {
+ "hint": "Termínalo con un rectángulo utilizando `rectangle 100, 50` ",
+ "solution": "rectangle 100, 50"
+ }
+ ],
+ "completion_text": "¡Bien hecho! Nuestro personaje se ve muy bien. ¿Qué partes podrías cambiar para que se parezca más a ti?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "Steve-blue.png",
+ "Steve-beard.png",
+ "Steve-Alex.png"
+ ]
+ },
+ "cover": "pixel-steve.png",
+ "guide": "#### Lo que crearás\n1. Únicamente utilizaremos cuadrados y rectángulos, por eso configuremos el trazo a 0 usando `stroke 0`\n2. Configura el color de fondo a marrón claro\n3. Configura el color de dibujo a marrón utilizando `color brown`.\n4. Su cabello comenzará en la esquina de arriba a la izquierda, entonces **escribe** `moveTo 0, 0`.\n5. Dibujaremos un rectángulo con parámetros de anchura y altura. **Escribe** `rectangle 500, 150`\n6. Configura el color de dibujo a marrón claro **escribe** `color lightbrown`\n7. Muévete para dibujar la frente, **escribe** `moveTo 100, 100`\n8. Ahora para dibujar la frente, **escribe** `rectangle 300, 50`\n9. Para los ojos, configura el color de dibujo a blanco\n10. Ahora muévete usando `moveTo 100, 200`\n11. Dibuja un ojo usando `square 50`\n12. Para el otro ojo, **escribe** `moveTo 350, 200`\n13. Y dibuja otro cuadrado de un tamaño de 50\n14. Para el iris utilizaremos color violeta: recuerda que en inglés se dice purple.\n15.Muévete a las coordenadas `150, 200` utilizando moveTo.\n16. Dibuja un cuadrado de un tamaño de 50.\n17. Muévete a las coordenadas `300, 200`.\n18. Y dibuja el iris final con un cuadrado de un tamaño de 50.\n19. Dibujaremos la nariz. **Escribe** `color brown`\n20. Muévete utilizando `moveTo 200, 250`.\n21. Dibuja un rectángulo de `100` de anchura y `50` de altura.\n22. Para hacer la boca configura el color a otro tono de marrón utilizando `color saddlebrown`.\n23. Muévete a las coordenadas `150, 300`\n24. La boca debería ser un gran rectángulo. **Escribe** `200, 100`.\n25. Para mejorar la boca deberíamos cortarla un poco. Configura el color de dibujo al mismo color de la piel. **Escribe** `color lightbrown`\n26. Muévete a las coordenadas `200, 300`.\n27. Termínalo con un rectángulo utilizando `rectangle 100, 50` \n\n#### Lo que hackearás\nLas figuras aquí son muy simples, ¡eso mismo lo hace muy fácil de cambiar y hackear! Cambia el color de la piel, del cabello, los ojos o agrega tus propios rasgos.\n\n#### Informe\nSteve es el héroe de Minecraft. No se conoce demasiado acerca de Steve: quién es, de dónde es o incluso si es humano. Sin embargo, es la cara visible del juego más sensacional de la década. Esta simple creación con cuadrados y rectángulos cobra vida gracias a hermosos colores.\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/sword.json b/lib/challenges/locales/es/worlds/pixelhack/sword.json
new file mode 100755
index 000000000..02fba6a73
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/sword.json
@@ -0,0 +1,96 @@
+{
+ "id": "sword",
+ "title": "Espada de diamantes",
+ "description": "Programa una espada forjada en puros diamantes",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Vamos a dibujar una espada de diamantes, por eso primero configuremos algunas variables para representar los colores. Espada en inglés se dice sword **escribe** `sword = aquamarine`",
+ "solution": "sword = aquamarine"
+ },
+ {
+ "hint": "Configuremos las variables necesarias para el área oscura de nuestra espada, podemos utilizar la función darken (que significa oscurecer) para alterar el color ya existente de la espada. **escribe** `darksword = darken sword,40`",
+ "solution": "darksword = darken sword,40"
+ },
+ {
+ "hint": "Empecemos por configurar el fondo de color gris pizarra, slategray en inglés",
+ "solution": "background slategray"
+ },
+ {
+ "hint": "Ahora establecemos el color de relleno utilizando la variable que configuramos antes **escribe** `color sword`",
+ "solution": "color sword"
+ },
+ {
+ "hint": "y configura el color del trazo para que sea una versión más oscura que la preestablecida **escribe** `stroke darksword,20`",
+ "solution": "stroke darksword,20"
+ },
+ {
+ "hint": "Mueve el cursor hacia donde estará el extremo de la cuchilla **escribe** `move -20,-180`",
+ "solution": "move -20,-180"
+ },
+ {
+ "hint": "Dibuja la cuchilla de la espada **escribe** `rectangle 40,230`",
+ "solution": "rectangle 40,230"
+ },
+ {
+ "hint": "Ya empieza a tomar forma, hagamos el mango de la espada **escribe** `move 10,250`",
+ "solution": "move 10,250"
+ },
+ {
+ "hint": "El mango no estará hecho de diamantes, configura el color del trazo a marrón oscuro",
+ "solution": "stroke darkbrown"
+ },
+ {
+ "hint": "y el `color` a marrón",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Dibuja un rectángulo de 20 por 100",
+ "solution": "rectangle 20,100"
+ },
+ {
+ "hint": "Ninguna espada estaría completa sin un guardamano **escribe** `move -70,-10`",
+ "solution": "move -70,-10"
+ },
+ {
+ "hint": "Vuelve a configurar el color del trazo a nuestra segunda variable más oscura",
+ "solution": "stroke darksword"
+ },
+ {
+ "hint": "Vamos a utilizar otra función de color: lighten (que significa aclarar) para cambiar un poco el color del diamante **escribe** `color lighten darksword,10`",
+ "solution": "color lighten darksword,10"
+ },
+ {
+ "hint": "Luego dibuja un rectángulo de 160 por 20",
+ "solution": "rectangle 160,20"
+ },
+ {
+ "hint": "Fíjate cómo la función para aclarar cambió el color del diamante. **escribe** `move 80, -240`",
+ "solution": "move 80, -240"
+ },
+ {
+ "hint": "Finalmente vamos a agregar detalles de brillo para terminar la espada **escribe** `color white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Configura el trazo a 0 **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Dibuja un rectángulo de 10 por 230",
+ "solution": "rectangle 10, 230"
+ }
+ ],
+ "completion_text": "Esa espada se ve estupenda, perfecta para luchar al escapar de alguna cueva. Intenta cambiar el color de la primera línea y fíjate cómo cambian los demás colores de la espada por haber utilizado variables.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "sword-golden.png",
+ "sword-shovel.png",
+ "sword-pickaxe.png"
+ ]
+ },
+ "cover": "pixel-sword.png",
+ "guide": "#### Nuevas palabras\n**darken**(color, amount) | darken(blue, 40)\n\nDevuelve un color más oscuro dependiendo del número que ingreses. El número puede ser entre 0 y 100.\n\n**lighten**(color, amount) | lighten(blue, 40)\n\nDevuelve un color más claro dependiendo del número que ingreses. El número puede ser entre 0 y 100.\n\n\n#### Lo que crearás\n1. Vamos a crear una espada pero primero configuraremos ciertas variables para representar los colores **escribe** `sword = aquamarine`\n2. Configuremos las variables necesarias para el área oscura de nuestra espada, podemos utilizar la función darken (que significa oscurecer) para alterar el color ya existente de la espada. **escribe** `darksword = darken sword,40`\n3. Empecemos por configurar el fondo de color gris pizarra\n4.Ahora establecemos el color de dibujo utilizando la variable que configuramos antes **escribe** `color sword`\n5. y configura el color del trazo para que sea una versión más oscura que la preestablecida **escribe** `stroke darksword,20`\n6. Mueve el cursor hacia donde estará el extremo de la cuchilla **escribe** `move -20,-180`\n7. Dibuja la cuchilla de la espada **escribe** `rectangle 40,230`\n8. Ya empieza a tomar forma, hagamos el mango de la espada **escribe** `move 10,250`\n9. El mango no estará hecho de diamantes, configura el color del trazo a marrón oscuro\n10. y el `color` de dibujo a marrón\n11. Dibuja un rectángulo de 20 por 100\n12. Ninguna espada estaría completa sin un guardamano **escribe** `move -70,-10`\n13. Vuelve a configurar el color del trazo a nuestra segunda variable más oscura\n14. Vamos a utilizar otra función de color: lighten (que significa aclarar) para cambiar un poco el color del diamante **escribe** `color lighten darksword,10`\n15. Luego dibuja un rectángulo de 160 por 20\n16. Fíjate cómo la función para aclarar cambió el color del diamante. **escribe** `move 80, -240`\n17. Finalmente vamos a agregar detalles de brillo para terminar la espada **escribe** `color white`\n18. Configura el trazo a 0 **escribe** `stroke 0`\n19. Dibuja un rectángulo de 10 por 230\n\n#### Lo que hackearás\nYa que todos los colores que utilizaremos serán sombras del mismo color de la espada, al modificar una variable todos las sombras más oscuras y más claras cambiarán también. Podemos transformar una espada de diamantes en oro cambiando el color de la variable.\n\nUsando el mismo código y estilo, ¿qué otras creaciones inspiradas en Minecraft te animas a hacer?\n\n#### Informe\nLa espada de diamantes es el arma más poderosa en Minecraft. También es la más difícil de crear dada la rareza de sus diamantes. Todos los diferentes tipos de espadas en Minecraft (diamante, oro, acero, piedra…) siguen un patrón de diseño simple. Lo único que las diferencia visualmente es el color. Para la espada de diamantes, crearemos una palabra llamada \"sword\" la cual estará configurada al color \"aquamarine\". Para seleccionar diferentes sombras en el mango y la cuchilla, utilizaremos el color establecido junto con funciones especiales de color."
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/tetris.json b/lib/challenges/locales/es/worlds/pixelhack/tetris.json
new file mode 100755
index 000000000..0fd2902a8
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/tetris.json
@@ -0,0 +1,96 @@
+{
+ "id": "tetris",
+ "title": "Tetris",
+ "description": "Programa un amontonamiento de piezas de Tetris",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Tetris (1984) fue el primer videojuego en ser exportado de la URSS a EEUU, vendiendo 170 millones de copias. Haremos algunas piezas de Tetris, pero primero desactivemos el trazo - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Luego mueve el cursor al punto de partida de la primera pieza que dibujaremos **escribe** `moveTo 150,200`",
+ "solution": "moveTo 150,200"
+ },
+ {
+ "hint": "Usaremos colores para diferenciar las piezas. Cambia el color de relleno de las piezas usando la función 'color' **escribe** `color crimson`",
+ "solution": "color crimson"
+ },
+ {
+ "hint": "Primero dibujaremos una pieza con forma de L usando dos rectángulos, en inglés es rectangle **escribe** `rectangle 50,150`",
+ "solution": "rectangle 50,150"
+ },
+ {
+ "hint": "Nuestras piezas se encuentran en una grilla conformada por celdas de 50x50, por eso notarás que todos nuestros comandos move (es decir de movimiento) serán múltiplos de 50. **escribe** `move 0,100`",
+ "solution": "move 0,100"
+ },
+ {
+ "hint": "Termina la pieza con un rectángulo de 100 por 50",
+ "solution": "rectangle 100,50"
+ },
+ {
+ "hint": "Muévete para empezar la siguiente pieza **escribe** `move 50,-100`",
+ "solution": "move 50,-100"
+ },
+ {
+ "hint": "Vamos a cambiar el color para diferenciarla de la anterior. La función 'color' afecta a todas las figuras que dibujemos después de escribirla. ¿Qué tal un bello color verdemar? **escribe** `color seagreen`",
+ "solution": "color seagreen"
+ },
+ {
+ "hint": "Luego dibujaremos una pieza con forma de S **escribe** `rectangle 50,100`",
+ "solution": "rectangle 50,100"
+ },
+ {
+ "hint": "Muévete un espacio en diagonal hacia abajo en la grilla **escribe** `move 50,50`",
+ "solution": "move 50,50"
+ },
+ {
+ "hint": "Para terminar la forma de S crearemos otro rectángulo de 50 por 100, utiliza rectangle para el comando",
+ "solution": "rectangle 50,100"
+ },
+ {
+ "hint": "Avancemos a la siguiente pieza **escribe** `move 50,-100`",
+ "solution": "move 50,-100"
+ },
+ {
+ "hint": "Cambiemos el color de dibujo a índigo ¡se dice igual en inglés! **escribe** `color indigo`",
+ "solution": "color indigo"
+ },
+ {
+ "hint": "Luego dibujaremos aquella pieza larga y delgada, dibuja un rectángulo de 50 por 200",
+ "solution": "rectangle 50,200"
+ },
+ {
+ "hint": "Nos queda una pieza para completar el cuadrado **escribe** `move -150,0`",
+ "solution": "move -150,0"
+ },
+ {
+ "hint": "Cambiemos el color a dorado para destacarla. Si no recuerdas cómo se dice en inglés, haz click en pista.",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Otra pieza con forma de L encajaría perfectamente **escribe** `rectangle 150,50`",
+ "solution": "rectangle 150,50"
+ },
+ {
+ "hint": "Muévete al pie de la L **escribe** `move 100,0`",
+ "solution": "move 100,0"
+ },
+ {
+ "hint": "Completa el pie **escribe** `rectangle 50,100`",
+ "solution": "rectangle 50,100"
+ }
+ ],
+ "completion_text": "Bien hecho, ¡creaste un buen amontonamiento de piezas de Tetris! ¿Por qué no intentas cambiarles el color o dibujar algunas más para cubrir la pantalla?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "tetris-gameboy.png",
+ "tetris-3D.png",
+ "tetris-row.png"
+ ]
+ },
+ "cover": "pixel-tetris.png",
+ "guide": "#### Nuevas palabras\n**color** nombre | color red\n\nCambia el color de dibujo. Todas las próximas figuras serán dibujadas con el color que configures hasta que lo cambies nuevamente. Para configurar el color de dibujo a transparente, escribe, `color null`\n\n#### Lo que crearás\n1. Tetris (1984) fue el primer videojuego en ser exportado de la URSS a EEUU, vendiendo 170 millones de copias. Haremos algunas piezas de Tetris, pero primero desactivaremos el trazo **escribe** `stroke 0`\n2. Luego mueve el cursor al punto de partida de la primera pieza que dibujaremos **escribe** `moveTo 150,200`\n3. Necesitamos usar colores para diferenciar las piezas, cambia el color de relleno de las piezas usando la función 'color' **escribe** `color crimson`\n4. Primero dibujaremos una pieza con forma de L usando dos rectángulos, en inglés es rectangle **escribe** `rectangle 50,150`\n5. Nuestras piezas se encuentran en una grilla conformada por celdas de 50x50, por eso notarás que todos nuestros comandos de movimiento serán múltiplos de 50 **escribe** `move 0,100`\n6. Termina la pieza con un rectángulo de 100 por 50\n7. Muévete para empezar la siguiente pieza **escribe** `move 50,-100`\n8. Vamos a cambiar el color para diferenciarla de la anterior. La función 'color' afecta a todas las figuras que dibujemos después de escribirla. Usemos un bello verdemar **escribe** `color seagreen`\n9. Luego dibujaremos una pieza con forma de S **escribe** `rectangle 50,100`\n10. Muévete un espacio en diagonal hacia abajo en la grilla **escribe** `move 50,50`\n11. Para terminar la forma de S crearemos otro rectángulo de 50 por 100, utiliza rectangle para el comando\n12. Avancemos a la siguiente pieza **escribe** `move 50,-100`\n13. Cambiemos el color de dibujo a índigo ¡se dice igual en inglés! **escribe** `color indigo`\n14. Luego dibujaremos aquella pieza larga y delgada, dibuja un rectángulo de 50 por 200\n15. Nos queda una pieza para completar el cuadrado **escribe** `move -150,0`\n16. Cambiemos el color a dorado para destacarla\n17. Otra pieza con forma de L encajaría perfecto **escribe** `rectangle 150,50`\n18. Muévete al pie de la L **escribe** `move 100,0`\n19. Completa el pie **escribe** `rectangle 50,100`\n\n#### Lo que hackearás\nAhora que has creado un cubo, puedes darles a las piezas el color que tú quieras y agrega más piezas.\n\n#### Informe \nTetris (1984) fue el primer videojuego en ser exportado de la URSS a EEUU, vendiendo 170 millones de copias. Haremos algunas piezas de Tetris, las cuales se llaman tetrimonios. Cada tetrimonio está formado por cuatro cuadrados que se conectan para crear diferentes formas. En el juego, los tetrimonios caen sin parar y el objetivo es hacerlos encajar evitando dejar espacios vacíos entre ellos.\n"
+}
diff --git a/lib/challenges/locales/es/worlds/pixelhack/variables.json b/lib/challenges/locales/es/worlds/pixelhack/variables.json
new file mode 100755
index 000000000..27de7a293
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/pixelhack/variables.json
@@ -0,0 +1,92 @@
+{
+ "id": "variables",
+ "title": "Variables",
+ "description": "Usa variables para crear un fantasma que mora en los laberintos",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Este clásico personaje del fantasma apareció por primera vez en Pac-Man (1980). Como que es un fantasma, necesitaremos un fondo escalofriante. Un azul marino debería servir - **escribe** `background navy`",
+ "solution": "background navy"
+ },
+ {
+ "hint": "Configura el trazo a 0 **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configura el `color` a rojo",
+ "solution": "color red"
+ },
+ {
+ "hint": "Ahora haremos algo nuevo, vamos a configurar el tamaño del fantasma usando una variable. Le daremos un nombre en inglés que se refiere al tamaño del fantasma, ghostsize. Luego la utilizaremos para repetir ese mismo valor que le asignemos **escribe** `ghostsize = 100`",
+ "solution": "ghostsize = 100"
+ },
+ {
+ "hint": "Mueve el cursor hacia arriba, donde empezará la cabeza del fantasma **escribe** `move 0,-50`",
+ "solution": "move 0,-50"
+ },
+ {
+ "hint": "Vamos a utilizar la variable que configuramos para dibujar la cabeza. Como la configuramos a 100, este comando dibujará un círculo de 100 píxeles **escribe** `circle ghostsize`",
+ "solution": "circle ghostsize"
+ },
+ {
+ "hint": "Vamos a utilizar la variable una vez más, esta vez con el comando move. Usando el símbolo menos por delante, este comando sería lo mismo que moverse -100,0 **escribe** `move -ghostsize,0`",
+ "solution": "move -ghostsize,0"
+ },
+ {
+ "hint": "Es hora de dibujar el resto del cuerpo del fantasma. Como que el cuerpo se encuentra en relación con el tamaño de la cabeza, vamos a utilizar la variable otra vez **escribe** `square ghostsize * 2`",
+ "solution": "square ghostsize * 2"
+ },
+ {
+ "hint": "Vamos a dibujarle ojos. Para asegurarnos de que están ubicados correctamente dentro de la cabeza, vamos a utilizar la variable otra vez. **type** `move ghostsize / 2`",
+ "solution": "move ghostsize / 2"
+ },
+ {
+ "hint": "Configura el `color` de dibujo a blanco",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja un ojo haciendo un círculo de 20 píxeles de tamaño",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Ahora configura el color del iris **escribe** `color blue`",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Finalmente dibujemos el iris **escribe** `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Vamos a utilizar la variable por última vez para ubicar correctamente el segundo ojo **escribe** `move ghostsize,0`",
+ "solution": "move ghostsize,0"
+ },
+ {
+ "hint": "Repite el proceso para el segundo ojo - configura el `color` de dibujo a blanco",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja otro círculo de 20 píxeles de tamaño",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Configura el `color` a azul",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Finalmente dibuja el círculo de 10 píxeles de tamaño",
+ "solution": "circle 10"
+ }
+ ],
+ "completion_text": "¡Qué bonito! Como has utilizado una variable, puedes pulsar en el número que le otorgaste a la variable ghostsize y utilizar el control deslizante para cambiar el tamaño del fantasma. Verás cómo los ojos se mantienen del mismo tamaño y todo lo demás cambia.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "ghost-big.png",
+ "ghost-tired.png",
+ "ghost-scared.png"
+ ]
+ },
+ "cover": "pixel-variables.png",
+ "guide": "#### Nueva palabra\n**variable** = valor | ghostsize = 100\nCrea una palabra a la cual se le asignará un valor y puede ser usado en otras ocasiones.\n\n#### Lo que crearás\n1. Este clásico personaje del fantasma apareció por primera vez en Pac-Man (1980). Como es un fantasma, necesitaremos un fondo escalofriante. Un azul marino debería servir - **escribe** `background navy`\n2. Configura el trazo a 0 **escribe** `stroke 0`\n3. Configura el `color` a rojo\n4. Ahora haremos algo nuevo, vamos a configurar el tamaño del fantasma usando una variable. Le daremos un nombre en inglés que se refiere al tamaño del fantasma, ghostsize. Luego la utilizaremos para repetir ese mismo valor que le asignemos **escribe** `ghostsize = 100`\n5. Mueve el cursor hacia arriba, donde empezará la cabeza del fantasma **escribe** `move 0,-50`\n6. Vamos a utilizar la variable que configuramos para dibujar la cabeza. Como la configuramos a 100, este comando dibujará un círculo de 100 píxeles **escribe** `circle ghostsize`\n7. Vamos a utilizar la variable una vez más, esta vez con el comando move. Usando el símbolo menos por delante, este comando sería lo mismo que moverse -100,0 **escribe** `move -ghostsize,0`\n8. Es hora de dibujar el resto del cuerpo del fantasma, como el cuerpo se encuentra en relación con el tamaño de la cabeza, vamos a utilizar la variable otra vez **escribe** `square ghostsize * 2`\n9. Vamos a dibujarle ojos. Para asegurarnos de que están ubicados correctamente dentro de la cabeza, vamos a utilizar la variable otra vez. **type** `move ghostsize / 2`\n10. Configura el`color` de dibujo a blanco\n11. Dibuja un ojo haciendo un círculo de 20 píxeles de tamaño\n12. Ahora configura el color del iris **escribe** `color blue`\n13. Finalmente dibujemos el iris **escribe** `circle 10`\n14. Vamos a utilizar la variable por última vez para ubicar correctamente el segundo ojo **escribe** `move ghostsize,0`\n15. Repite el proceso para el segundo ojo - configura el `color` de dibujo a blanco\n16. Dibuja otro círculo de 20 píxeles de tamaño\n17. Configura el `color` a azul\n18. Finalmente dibuja el círculo de 10 píxeles de tamaño\n\n#### Lo que hackearás\nYa que utilizaste una variable para las diferentes partes del cuerpo del fantasma, puedes cambiar el valor para ver cómo cambia el tamaño del cuerpo. Sin embargo, los ojos no cambian. ¿Podrías configurar otra variable que controle el tamaño de los ojos?\n\n#### Informe\nCuando programas le otorgas una serie de tareas a tu computadora. Y cuando ejecutas los comandos, la computadora lee todas esas tareas y las realiza. Hasta ahora, programamos de manera tal que se ejecuten los comandos siempre igual, pero si quisiéramos que los comandos se ejecuten de manera distinta deberíamos utilizar variables.\n\nUna variable es una palabra que adquiere el significado de otra cosa. En este desafío determinarás el tamaño de muchas partes del cuerpo del fantasma con una variable. Cambiando el valor de la variable verás cómo afecta el tamaño de las diferentes formas, otorgándote el poder de agrandar o achicar el fantasma.\n"
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/backpack.json b/lib/challenges/locales/es/worlds/summercamp/backpack.json
new file mode 100755
index 000000000..d10490a84
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/backpack.json
@@ -0,0 +1,143 @@
+{
+ "id": "backpack",
+ "title": "Empaca tus cosas",
+ "short_title": "Mochila",
+ "icon_class": "challenge_backpack",
+ "description": "Dick Kelty fue quien inventó las mochilas con marco de aluminio. Kelty tuvo la idea en 1951 cuando se fue de excursión con una amigo a Sierra Nevada. Trabajando en su garage y con un préstamo de $500, Kelty y su mujer decidieron empezar el negocio; él cortaba y soldaba el aluminio mientras ella cosía el nylon. Vendían las mochilas terminadas a $24 cada una.",
+ "cover": "summercamp/day_8.png",
+ "completion_text": "Tengo un gran desafío para ti: intenta dibujar tu personaje detrás de la mochila ¡Cabeza, brazos y piernas!",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Es un hermoso día para ir a caminar por el bosque - **escribe** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Primero, configura el trazo a 0 para evitar dibujar líneas usando `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Mueve el cursor para dibujar el piso - en una nueva línea **escribe** `moveTo 0, 250`",
+ "solution": "moveTo 0, 250"
+ },
+ {
+ "hint": "Configura el `color` del césped a verde o `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Dibuja el piso con un rectángulo - **escribe** `rectangle 500, 250`",
+ "solution": "rectangle 500, 250"
+ },
+ {
+ "hint": "Parece un lindo sitio para un árbol - **escribe** `moveTo 220`",
+ "solution": "moveTo 220"
+ },
+ {
+ "hint": "Configura el `color` del tronco a marrón claro o `lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Dibuja el tronco usando la función `rectangle`, y con un tamaño de `50` por `400`",
+ "solution": "rectangle 50, 400"
+ },
+ {
+ "hint": "¡Hermoso! Ahora solamente necesita algunas hojas - **escribe** `move 30, -120` para ubicarlas",
+ "solution": "move 30, -120"
+ },
+ {
+ "hint": "Configura el `color` de las hojas a verde oscuro o `darkgreen`",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Dibuja un círculo usando `circle` de un tamaño de `200` para representar las hojas",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "¡Se ve genial! Vamos a mover el cursor para dibujar la mochila - **escribe** `moveTo 150, 200`",
+ "solution": "moveTo 150, 200"
+ },
+ {
+ "hint": "Vamos a elegir el `color` marrón o `brown` para nuestra mochila",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Dibuja la parte principal de la mochila usando un cuadrado - **escribe** `square 200`",
+ "solution": "square 200"
+ },
+ {
+ "hint": "Ahora mueve el cursor a la parte de abajo de la mochila - **escribe** `moveTo 250, 400`",
+ "solution": "moveTo 250, 400"
+ },
+ {
+ "hint": "Dibuja la parte de abajo usando una elipse - **escribe** `ellipse 100, 30`",
+ "solution": "ellipse 100, 30"
+ },
+ {
+ "hint": "Necesitamos guardar la bolsa de dormir en la parte de arriba de la mochila - **escribe** `moveTo 250, 200`",
+ "solution": "moveTo 250, 200"
+ },
+ {
+ "hint": "Configura el `color` de la bolsa de dormir a rojo oscuro o `darkred`",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "¿Listo para dibujar la bolsa de dormir? Usa una elipse - **escribe** `ellipse 115, 40`",
+ "solution": "ellipse 115, 40"
+ },
+ {
+ "hint": "Necesitamos algo para asegurar la bolsa de dormir y que no se caiga - **escribe** `moveTo 200, 160`",
+ "solution": "moveTo 200, 160"
+ },
+ {
+ "hint": "El anaranjado o `orangered` en inglés, parece un lindo `color` para las tiras que la sostendrán",
+ "solution": "color orangered"
+ },
+ {
+ "hint": "Dibuja la tira izquierda - **escribe** `rectangle 15, 90`",
+ "solution": "rectangle 15, 90"
+ },
+ {
+ "hint": "Posiciona el cursor hacia la derecha para dibujar la segunda - **escribe** `moveTo 290, 160`",
+ "solution": "moveTo 290, 160"
+ },
+ {
+ "hint": "Usa la función `rectangle` otra vez para la tira de la derecha, igual que la anterior",
+ "solution": "rectangle 15, 90"
+ },
+ {
+ "hint": "¡Excelente! Necesitamos espacio para guardar cosas. Mueve el cursor al medio - **escribe** `moveTo 205, 320`",
+ "solution": "moveTo 205, 320"
+ },
+ {
+ "hint": "Usa la función `rectangle` para hacer un bolsillo, de `90` por `60` será suficiente",
+ "solution": "rectangle 90, 60"
+ },
+ {
+ "hint": "Necesitamos cerrar el bolsillo para que no se metan insectos - **escribe** `moveTo 250, 320`",
+ "solution": "moveTo 250, 320"
+ },
+ {
+ "hint": "Configura el `color` a marrón claro o `lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Usa la función `ellipse` de un tamaño de `48` por `7`",
+ "solution": "ellipse 48, 7"
+ },
+ {
+ "hint": "¡Se ve genial! Vamos a ponerle un botón a ese bolsillo - **escribe** `moveTo 250, 330`",
+ "solution": "moveTo 250, 330"
+ },
+ {
+ "hint": "Configura el `color` a marrón o `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Dibuja un círculo usando `circle` de un tamaño de `10`",
+ "solution": "circle 10"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/bear.json b/lib/challenges/locales/es/worlds/summercamp/bear.json
new file mode 100755
index 000000000..dda525b53
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/bear.json
@@ -0,0 +1,91 @@
+{
+ "id": "bear",
+ "title": "Oso atrevido",
+ "short_title": "Oso",
+ "description": "Los osos son muy inteligentes y incluso activan trampas para osos con rocas para poder comer el cebo de manera segura. Estos animales pueden correr a 64km/h, lo suficientemente rápido como para atrapar a un caballo que corre. Ten en cuenta que la persona más rápida del mundo es Usain Bolt ¡puede correr a 43km/h! Ya que los osos pueden caminar distancias cortas con sus patas traseras, los nativos de Estados Unidos los llaman “las bestias que caminan como humanos.”",
+ "icon_class": "challenge_bear",
+ "cover": "summercamp/day_11.png",
+ "completion_text": "Ese oso tan tierno necesita un cuerpo y un hábitat. Usa tus habilidades de programación para impresionar a tus compañeros. Recuerda que los íconos de la izquierda pueden ayudarte a alcanzar lo que estás pensando.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "El oso vive en el bosque. Configura el color del fondo a verde - **escribe** `background green`",
+ "solution": "background green"
+ },
+ {
+ "hint": "El pelaje de nuestro oso será marrón, empecemos por configurar el color - **escribe** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Configura el trazo a 0 para evitar dibujar líneas - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Dibuja la cara con un círculo - **escribe** `circle 100`",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Vamos a mover el cursor para dibujar la oreja izquierda - **escribe** `move -80, -80`",
+ "solution": "move -80, -80"
+ },
+ {
+ "hint": "Dibuja la oreja en forma de círculo usando `circle` hazla de un tamaño de `30`",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Mueve el cursor a la derecha para dibujar la otra oreja - **escribe** `move 160`",
+ "solution": "move 160"
+ },
+ {
+ "hint": "Dibuja la oreja, igual a la otra",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Ahora muévete hacia donde estará el ojo izquierdo - **escribe** `move -110, 50`",
+ "solution": "move -110, 50"
+ },
+ {
+ "hint": "El resto de las partes de la cara serán negras por eso configura el `color` a negro o `black` en inglés.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Dibuja el primer ojo - **escribe** `circle 5`",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Ahora muévete hacia donde estará el otro ojo - **escribe** `move 60`",
+ "solution": "move 60"
+ },
+ {
+ "hint": "Dibuja el ojo, idéntico al anterior",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Muévete hacia el centro para dibujar lo más característico de los osos- **escribe** `move -30, 50`",
+ "solution": "move -30, 50"
+ },
+ {
+ "hint": "Dibuja la nariz con un triángulo usando la función polygon - **escribe** `polygon 12, -16, -12, -16`",
+ "solution": "polygon 12, -16, -12, -16"
+ },
+ {
+ "hint": "Vamos a ubicarnos para dibujarle la boca - **escribe** `move 0, 20`",
+ "solution": "move 0, 20"
+ },
+ {
+ "hint": "Configura el trazo `stroke` a negro o `black` y de un tamaño de `3`",
+ "solution": "stroke black, 3"
+ },
+ {
+ "hint": "No queremos que tenga un color de relleno, por eso configura el color a transparente - **escribe** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "Un arco es la mitad de un círculo, nos sirve para representar una leve sonrisa - **escribe** `arc 15, 0.5, 2`",
+ "solution": "arc 15, 0.5, 2"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/bow_and_arrow.json b/lib/challenges/locales/es/worlds/summercamp/bow_and_arrow.json
new file mode 100755
index 000000000..0a2162760
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/bow_and_arrow.json
@@ -0,0 +1,108 @@
+{
+ "id": "bow_and_arrow",
+ "title": "Arco y flecha",
+ "short_title": "Arco y flecha",
+ "icon_class": "challenge_bow",
+ "description": "Lanzar una flecha con el arco a un determinado punto es un arte que requiere mucha concentración. Este desafío consiste en dibujar el arco e intentar dejarlo quieto mientras tiembla tu mano... tal como lo han hecho muchas civilizaciones más de mil años atrás para cazar y alimentarse ¡El arco más antiguo fue encontrado en Dinamarca y es del año 9000 A.C!",
+ "cover": "summercamp/day_15.png",
+ "completion_text": "¡Haz click en Refrescar y verás el blanco moverse mientras buscas sostener el arco! Intenta deshacerte del fondo movedizo para que sea más fácil dar en el blanco. También puedes intentar convertir tu arco en una ballesta.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "El clima de hoy es agradable...no hay viento que nos desvíe las flechas. Configura el fondo a azul - **escribe** `background blue`.",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Queremos que el fondo se agite, así que comenzaremos por dibujarlo en un punto aleatorio. **escribe** `moveTo (random -20, 0), (random 240, 260)`.",
+ "solution": "moveTo (random -20, 0), (random 240, 260)",
+ "validate": "__moveTo ?\\( *random +-20, +0 *\\), +\\( *random +240, +260 *\\)"
+ },
+ {
+ "hint": "Nuestro blanco está en un campo de césped, configuremos el color a verde usando `color green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Dibuja el césped usando `rectangle`, y hazlo de un tamaño de `550` píxeles de alto por `300` píxeles de ancho",
+ "solution": "rectangle 550, 300"
+ },
+ {
+ "hint": "Vamos a mover el cursor para dibujar la diana que contiene el blanco y los anillos - **escribe** `move 275, 10`",
+ "solution": "move 275, 10"
+ },
+ {
+ "hint": "Primero dibujaremos las patas de madera que la sostienen - **escribe** `stroke brown, 15`",
+ "solution": "stroke brown, 15"
+ },
+ {
+ "hint": "Dibuja la pata izquierda - **escribe** `line -80, 120`",
+ "solution": "line -80, 120"
+ },
+ {
+ "hint": "... y ahora la pata derecha - **escribe** `line 80, 120`",
+ "solution": "line 80, 120"
+ },
+ {
+ "hint": "Vamos a dibujar una diana con anillos blancos y rojos - **escribe** `stroke white, 50`",
+ "solution": "stroke white, 50"
+ },
+ {
+ "hint": "Ahora configura el interior del círculo a rojo - **escribe** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Primero, dibuja un círculo con un radio de 80 píxeles - **escribe** `circle 80`",
+ "solution": "circle 80"
+ },
+ {
+ "hint": "Ahora dibuja otro círculo usando `circle` de un radio de `30` píxeles",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Mueve el cursor para dibujar la punta de la flecha - **escribe** `moveTo 250, 220`",
+ "solution": "moveTo 250, 220"
+ },
+ {
+ "hint": "Nuestra punta de la flecha tendrá un fino borde gris - **escribe** `stroke gray, 1`",
+ "solution": "stroke gray, 1"
+ },
+ {
+ "hint": "Tendrá un `color dimgray`, es un tono de gris",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Dibuja la punta de la flecha como si fuera un diamante - **escribe** `polygon -10, 40, 0, 80, 10, 40`",
+ "solution": "polygon -10, 40, 0, 80, 10, 40"
+ },
+ {
+ "hint": "Ahora vamos a mover el cursor para dibujar el palo de la flecha - **escribe** `move 0, 40`",
+ "solution": "move 0, 40"
+ },
+ {
+ "hint": "La flecha estará hecha de una liviana madera color marrón - **escribe** `stroke lightbrown, 20`",
+ "solution": "stroke lightbrown, 20"
+ },
+ {
+ "hint": "Dibuja una línea - **escribe** `line 200, 360`",
+ "solution": "line 200, 360"
+ },
+ {
+ "hint": "Lo último que dibujaremos será el arco. Mueve el cursor fuera de la pantalla - **escribe** `moveTo 800, 375`",
+ "solution": "moveTo 800, 375"
+ },
+ {
+ "hint": "Claro que vamos a dibujarlo con forma de arco, dibújalo con un trazo grueso de color madera oscura - **escribe** `stroke darkbrown, 40`",
+ "solution": "stroke darkbrown, 40"
+ },
+ {
+ "hint": "No queremos que el arco tenga relleno, únicamente necesitamos el borde - **escribe** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "Dibuja el arco - **escribe** `arc 500, 0, 2`",
+ "solution": "arc 500, 0, 2"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/camera.json b/lib/challenges/locales/es/worlds/summercamp/camera.json
new file mode 100755
index 000000000..ec10ddeb7
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/camera.json
@@ -0,0 +1,109 @@
+{
+ "id": "camera",
+ "title": "¡Saca una fotografía!",
+ "short_title": "Cámara",
+ "icon_class": "challenge_camera",
+ "description": "Hacia 1820, las primeras cámaras tardaban algunas horas en capturar un simple momento. Con largas y molestas horas de exposición, fotografiar gente con una sonrisa en la cara resultaba imposible. ¿Por qué? Bueno, intenta sostener una linda sonrisa durante horas sin moverte y obtendrás la respuesta. Hoy en día, la cantidad de fotografías tomadas cada dos minutos es la misma cantidad de fotografías tomadas por la humanidad entera en el 1800",
+ "cover": "summercamp/day_10.png",
+ "completion_text": "Intenta dibujarte a ti mismo tomando una fotografía detrás de esa bonita cámara. ¿Tal vez también puedas agregar la luz del flash?",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "Configura el fondo a azul usando `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Por ahora, configuraremos el trazo a 0 para evitar resaltar el borde de las figuras usando `stroke`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Mueve el cursor para posicionar la cámara en el centro - **escribe** `moveTo 100, 150`",
+ "solution": "moveTo 100, 150"
+ },
+ {
+ "hint": "Configura el `color` de la cámara a un tono de gris, `dimgray` en inglés",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Ahora dibuja el cuerpo de la cámara usando un rectángulo - **escribe** `rectangle 300, 200`",
+ "solution": "rectangle 300, 200"
+ },
+ {
+ "hint": "Mueve el cursor para dibujar la lente - **escribe** `move 150, 100`",
+ "solution": "move 150, 100"
+ },
+ {
+ "hint": "Configura el `color` de la lente a celeste o `lightblue`",
+ "solution": "color lightblue"
+ },
+ {
+ "hint": "Configura el borde de la lente usando `stroke` a negro o `black` en inglés y de un tamaño de `10`",
+ "solution": "stroke black, 10"
+ },
+ {
+ "hint": "La lente es un círculo, dibújala con `circle` y un tamaño de `50`",
+ "solution": "circle 50"
+ },
+ {
+ "hint": "Configura el `color` de la segunda mitad de la lente a gris claro o `lightgray` en inglés",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Dibuja un arco - **escribe** `arc 50, 2, 1`",
+ "solution": "arc 50, 2, 1"
+ },
+ {
+ "hint": "¡Esta cámara necesita un flash! Mueve el cursor para ubicarlo - **escribe** `move -30, -125`",
+ "solution": "move -30, -125"
+ },
+ {
+ "hint": "Dibuja el flash con un rectángulo, recuerda que en inglés se dice `rectangle` - hazlo de un tamaño de `60` por `20`",
+ "solution": "rectangle 60, 20"
+ },
+ {
+ "hint": "Configura el trazo a `0` usando `stroke`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configura el `color` del flash a celeste o `lightblue`",
+ "solution": "color lightblue"
+ },
+ {
+ "hint": "Dibuja un polígono para el reflejo del flash - **escribe** `polygon 40, 0, 0, 20`",
+ "solution": "polygon 40, 0, 0, 20"
+ },
+ {
+ "hint": "Solamente nos queda hacer el botón para tomar la fotografía - **escribe** `move 100, 10`",
+ "solution": "move 100, 10"
+ },
+ {
+ "hint": "Configura el `color` del botón a rojo o `red` en inglés.",
+ "solution": "color red"
+ },
+ {
+ "hint": "Dibuja el botón con un rectángulo, usa `rectangle` y hazlo de un tamaño de `50` por `15`",
+ "solution": "rectangle 50, 15"
+ },
+ {
+ "hint": "¡Un último detalle! Mueve el cursor - **escribe** `move 25, -20`",
+ "solution": "move 25, -20"
+ },
+ {
+ "hint": "Configura el `color` a amarillo o `yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Configura la fuente del texto usando `font 'ComicSansMS', `30`",
+ "solution": "font 'ComicSansMS', 30"
+ },
+ {
+ "hint": "Escribe texto - **escribe** `text 'Click!'`",
+ "solution": "text 'Click!'"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/camp_badge.json b/lib/challenges/locales/es/worlds/summercamp/camp_badge.json
new file mode 100755
index 000000000..68e946209
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/camp_badge.json
@@ -0,0 +1,56 @@
+{
+ "id": "camp_badge",
+ "title": "Insignia de Campamento",
+ "short_title": "Insignia de Campamento",
+ "icon_class": "challenge_badge",
+ "description": "La insignia de primeros auxilios es la más popular en los campamentos de verano. De hecho, 6.9 millones de Scouts la han obtenido desde 1911. Las otras más populares son las de Ciencias Ambientales, Búsqueda y Rescate y nuestra favorita, ¡la de Programación y Diseño de Juegos! También existen otras más extrañas. Por ejemplo, la de Transporte en Camiones: imagina que tienes que cargar en un camión 300 kilos de mercadería de un pueblo que está a 500 millas del tuyo. Tu pedido debe llegar en el transcurso de tres días. Explica por escrito...” También la de Ciencias Nucleares: “Prepara dos platos de comida, uno con alimentos irradiados y otro no, luego compara el gusto y las texturas.”",
+ "completion_text": "Esta insignia distinguirá tu campamento de otros. Sorprende al mundo con una creación asombrosa.",
+ "cover": "summercamp/day_5.png",
+ "difficulty": 1,
+ "startAt": 4,
+ "start_date": "2015-10-07 06:00:00",
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Configura el fondo a negro **escribe** `background black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "Configura el `color` de tu insignia a celeste o `lightblue` en inglés",
+ "solution": "color lightblue"
+ },
+ {
+ "hint": "Configura el trazo para el primer círculo - **escribe** `stroke red, 20`",
+ "solution": "stroke red, 20"
+ },
+ {
+ "hint": "Dibuja un círculo, o `circle` en inglés, de un tamaño de `200`",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Configura el trazo para el segundo círculo - **escribe** `stroke yellow, 20`",
+ "solution": "stroke yellow, 20"
+ },
+ {
+ "hint": "Dibújalo usando `circle` de un tamaño de `190`",
+ "solution": "circle 190"
+ },
+ {
+ "hint": "Configura el trazo, `stroke`, para el tercer círculo a violeta o `purple` en inglés y de un tamaño de `20`",
+ "solution": "stroke purple, 20"
+ },
+ {
+ "hint": "Dibújalo usando `circle` de un tamaño de `180`",
+ "solution": "circle 180"
+ },
+ {
+ "hint": "Agreguemos algo de decoración. Configura el `color` a verde o `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Un arco es la mitad de un círculo, dibuja uno que encaje en el círculo interior - **escribe** `arc 180, 1, 2`",
+ "solution": "arc 180, 1, 2"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/camp_sign.json b/lib/challenges/locales/es/worlds/summercamp/camp_sign.json
new file mode 100755
index 000000000..3a8e70d11
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/camp_sign.json
@@ -0,0 +1,87 @@
+{
+ "id": "camp_sign",
+ "title": "Tu letrero de Campamento de Verano",
+ "short_title": "Letrero de Campamento",
+ "description": "¡Bienvenido a tu primer día de campamento! Antes de salir, tu letrero de campamento necesita un poco de diseño ¡Vuélvete creativo! Usa una divertida combinación de colores o demuestra lo que te gustaría hacer estando de campamento. \nEn la región del Noroeste Pacífico de Estados Unidos, los pueblos indígenas tallaban Tótems para contar las historias de sus ancestros y culturas. Estas hermosas esculturas están talladas en árboles y a menudo representan personajes y hechos de leyendas. Puedes buscar en internet imágenes de los Tótems para inspirarte.",
+ "icon_class": "challenge_campsign",
+ "cover": "summercamp/day_1.png",
+ "completion_text": "¡Bien hecho! Ahora otórgale a tu letrero tu propio texto y estilo. Cambia el nombre de tu campamento en el renglón 18, o el color en el 3 ¿Por qué no agregar un lindo fondo y alguna decoración? Los íconos de utilidades a la izquierda pueden ayudarte a empezar.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "¡Es un día soleado! Configura el fondo a azul - **escribe** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Mueve el cursor al punto en el que queremos comenzar a dibujar nuestro letrero.\n\nRecuerda presionar ENTER para agregar la próxima instrucción y - **escribe** `moveTo 100, 150`",
+ "solution": "moveTo 100, 150"
+ },
+ {
+ "hint": "Imagina que en Make Art dibujamos figuras con bolígrafos digitales. Antes de dibujar la próxima figura, necesitaremos elegir el color de la tinta para el relleno de la figura. Seleccionemos un bolígrafo marrón configurando el color a marrón claro, o lightbrown en inglés.\n\n En una nueva línea **escribe** `color lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Cuando dibujamos una figura, podemos seleccionar el grosor del trazo que nuestro bolígrafo deja en el borde. Esta línea del trazo en inglés se llama **stroke**.\n\n Presiona enter y configura el trazo a 0 para evitar dibujar líneas - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Ahora que ya configuramos el bolígrafo de dibujo ¡podemos empezar a hacer nuestro letrero de campamento!\n\nDibuja el cartel con un rectángulo. Recuerda rectángulo en inglés es rectangle - **escribe** `rectangle 300, 200`",
+ "solution": "rectangle 300, 200"
+ },
+ {
+ "hint": "Dibujemos una sombra. Primero, configura el color a marrón - **escribe** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Dibuja una fina sombra utilizando un rectángulo alargado - **escribe** `rectangle 300, 5`",
+ "solution": "rectangle 300, 5"
+ },
+ {
+ "hint": "Ese letrero se ve demasiado limpio. Tenemos que dibujarle algunas imperfecciones.\n\nMueve el cursor al lado izquierdo del letrero - **escribe** `moveTo 100, 220`",
+ "solution": "moveTo 100, 220"
+ },
+ {
+ "hint": "Nuestra imperfección será un triángulo en el borde del letrero. Primero necesitamos configurar el `color` a azul, `blue` en inglés, para que sea el mismo que el del fondo.",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Configura el trazo a un tamaño de 5 y de color marrón (para que combine con el letrero). Puedes concretar estas dos acciones de tamaño y color en un solo comando - **escribe** `stroke brown, 5`",
+ "solution": "stroke brown, 5"
+ },
+ {
+ "hint": "Ahora vamos a usar el comando `polygon` (significa polígono) para dibujar nuestra imperfección en forma de triángulo. Para hacerlo, necesitaremos enumerar la posición de las demás esquinas del triángulo - **escribe** `polygon 16, 10, 0, 11`",
+ "solution": "polygon 16, 10, 0, 11"
+ },
+ {
+ "hint": "Este letrero necesita un palo que lo sostenga - **escribe** `moveTo 230, 350`",
+ "solution": "moveTo 230, 350"
+ },
+ {
+ "hint": "Configura el color del palo a marrón - **escribe** `color brown` ",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Configura el trazo a 0 - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Ahora que ya configuramos el color, dibuja el palo con un rectángulo - **escribe** `rectangle 40, 150`",
+ "solution": "rectangle 40, 150"
+ },
+ {
+ "hint": "Necesitamos que nuestro letrero diga algo. Vamos a agregarle texto. Mueve el cursor a la posición del texto - **escribe** `moveTo 250, 270`",
+ "solution": "moveTo 250, 270"
+ },
+ {
+ "hint": "Ahora necesitamos elegir la fuente. Hay muchas para elegir, puede ser `Bariol`, `Georgia`, `Comic Sans MS`, `cursive`, y `Courier`.\n\nElige tu favorita y escribe el nombre de la fuente entre comillas, así - `font 'Bariol', 70`.",
+ "solution": "font 'Bariol', 70"
+ },
+ {
+ "hint": "Ahora podemos escribir algo usando el comando de texto. En inglés se dice text - **escribe** `text 'My Camp'`",
+ "solution": "text 'My Camp'"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/campfire.json b/lib/challenges/locales/es/worlds/summercamp/campfire.json
new file mode 100755
index 000000000..aa2adba51
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/campfire.json
@@ -0,0 +1,90 @@
+{
+ "id": "campfire",
+ "title": "Fogata",
+ "short_title": "Fogata",
+ "description": "Es tiempo de iniciar una fogata. En el descampado, ésta podría ser tu única fuente de luz y calor. Ardiendo por encima de los 600 grados centígrados, ¡esto será seguro para mantener el calor y alimentarte también! Sobre la llama y con la ayuda de algún pinche puedes cocinar carne, vegetales y malvaviscos. \nLas fogatas se arman con leños pero tú la harás programando y utilizando un ciclo. A medida que tu fuego crece y se mezcla con el oxígeno, cambia de color entre los distintos tonos de naranja. Tus códigos de programación darán lugar a cientos de colores utilizando únicamente algunas líneas de comandos.",
+ "icon_class": "challenge_fire",
+ "completion_text": "¡Intenta agregar un cielo estrellado, más llamas de fuego o un aro de rocas alrededor de tu fogata para que sea más seguro cocinar algo!",
+ "difficulty": 2,
+ "cover": "summercamp/day_6.png",
+ "startAt": 5,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Es una noche oscura. Configura el fondo usando `background` a azul oscuro o `darkblue` en inglés",
+ "solution": "background darkblue"
+ },
+ {
+ "hint": "Configura el trazo a 0 para evitar dibujar líneas - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Mueve el cursor para dibujar el piso - **escribe** `moveTo 0, 300`",
+ "solution": "moveTo 0, 300"
+ },
+ {
+ "hint": "Configura el `color` a verde oscuro o `darkgreen`",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Dibuja el césped usando un rectángulo - **escribe** `rectangle 500, 200`",
+ "solution": "rectangle 500, 200"
+ },
+ {
+ "hint": "Mueve el cursor para dibujar el efecto de luz que genera el fuego en el piso - **escribe** `moveTo 250, 400`",
+ "solution": "moveTo 250, 400"
+ },
+ {
+ "hint": "Configura el `color` a verde o `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Dibuja la luz usando una elipse - **escribe** `ellipse 200, 30`",
+ "solution": "ellipse 200, 30"
+ },
+ {
+ "hint": "Necesitamos leños de madera para crear el fuego - **escribe** `moveTo 110, 390`",
+ "solution": "moveTo 110, 390"
+ },
+ {
+ "hint": "Configura el trazo para los leños - **escribe** `stroke brown, 25`",
+ "solution": "stroke brown, 25"
+ },
+ {
+ "hint": "Bien, ahora dibuja el primero - **escribe** `line 270, 30`",
+ "solution": "line 270, 30"
+ },
+ {
+ "hint": "Mueve el cursor para dibujar el segundo - **escribe** `moveTo 420, 390`",
+ "solution": "moveTo 420, 390"
+ },
+ {
+ "hint": "Dibuja el segundo leño - **escribe** `line -290, 30`",
+ "solution": "line -290, 30"
+ },
+ {
+ "hint": "¡Excelente! Ahora estamos listos para encender el fuego - **escribe** `moveTo 180, 400`",
+ "solution": "moveTo 180, 400"
+ },
+ {
+ "hint": "Nuestro fuego estará formado por 150 líneas de color naranja - **escribe** `for i in [ 1 .. 150 ]`",
+ "solution": "for i in [ 1 .. 150 ]"
+ },
+ {
+ "hint": "Configura el tamaño y el color de cada línea - **escribe** `stroke 'rgba(255, ' + (i + 50) + ', 0, 0.3)', 10`",
+ "solution": " stroke 'rgba(255, ' + (i + 50) + ', 0, 0.3)', 10",
+ "validate": "__^ stroke 'rgba*\\(255, ' \\+ \\(i *\\+ *50\\) *\\+ *', *0, *0.3\\)', 10"
+ },
+ {
+ "hint": "Ahora dibuja la línea - **escribe** `line 150 - i`",
+ "solution": " line 150 - i"
+ },
+ {
+ "hint": "Mueve el cursor para la siguiente línea - **escribe** `move 0.5, -2`",
+ "solution": " move 0.5, -2"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/campsite.json b/lib/challenges/locales/es/worlds/summercamp/campsite.json
new file mode 100755
index 000000000..a52d655db
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/campsite.json
@@ -0,0 +1,97 @@
+{
+ "id": "campsite",
+ "title": "Dibuja tu Camping",
+ "short_title": "Camping",
+ "description": "Es hora de buscar un lugar agradable para tu campamento. Lo más importante a la hora de buscar sitio para construir un campamento es que sea un terreno llano. Si es posible, que haya alguna fuente de agua cerca, esto facilitará el momento de la cocina y la limpieza. Finalmente, asegúrate de que esté rodeado de algunos árboles, ya que brindan leña y una buena protección del sol. \nTus amigos querrán ver cómo se ve tu camping, cuando tengas uno en mente ¡dibújalo!",
+ "icon_class": "challenge_location",
+ "completion_text": "¡El camping se ve genial! Intenta agregar más árboles, flores, piedras... ¿tal vez una cerca?",
+ "cover": "summercamp/day_2.png",
+ "difficulty": 1,
+ "startAt": 1,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "Hoy es un hermoso día - **escribe** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Configura el trazo a 0 para evitar dibujar líneas - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Mueve el cursor a donde se ubicará el sol - **escribe** `moveTo 400, 50`",
+ "solution": "moveTo 400, 50"
+ },
+ {
+ "hint": "Configura el `color` del sol a amarillo usando el equivalente en inglés `yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Dibuja el sol con un círculo - **escribe** `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Hay una nube en el horizonte - **escribe** `moveTo 440, 80`",
+ "solution": "moveTo 440, 80"
+ },
+ {
+ "hint": "Configura el `color` de la nube a blanco o `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja la nube usando una elipse - **escribe** `ellipse 60, 20`",
+ "solution": "ellipse 60, 20"
+ },
+ {
+ "hint": "El lago tiene un hermoso `color` aguamarina o `aquamarine`",
+ "solution": "color aquamarine"
+ },
+ {
+ "hint": "Mueve el cursor a donde se ubicará el lago - **escribe** `moveTo 0, 190`",
+ "solution": "moveTo 0, 190"
+ },
+ {
+ "hint": "Dibuja el lago usando un simple rectángulo - **escribe** `rectangle 500, 310`",
+ "solution": "rectangle 500, 310"
+ },
+ {
+ "hint": "Configura el `color` a verde, o `green` en inglés, para el césped",
+ "solution": "color green"
+ },
+ {
+ "hint": "Mueve el cursor antes de dibujar el césped - **escribe** `moveTo 250, 700`",
+ "solution": "moveTo 250, 700"
+ },
+ {
+ "hint": "Dibuja el césped con un círculo - **escribe** `circle 500`",
+ "solution": "circle 500"
+ },
+ {
+ "hint": "Se ve un poco vacío, vamos a dibujar un árbol - **escribe** `moveTo 120, 300`",
+ "solution": "moveTo 120, 300"
+ },
+ {
+ "hint": "Configura el `color` del tronco a marrón o `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Dibuja el tronco del árbol usando un rectángulo - **escribe** `rectangle 15, 100`",
+ "solution": "rectangle 15, 100"
+ },
+ {
+ "hint": "Ahora posiciónate para dibujar la copa del árbol - **escribe** `move 8, -120`",
+ "solution": "move 8, -120"
+ },
+ {
+ "hint": "Configura el `color` a verde oscuro o `darkgreen`",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Dibuja la copa del árbol con un tríangulo usando el comando `polygon` - **escribe** `polygon -40, 160, 40, 160`",
+ "solution": "polygon -40, 160, 40, 160"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/cinema.json b/lib/challenges/locales/es/worlds/summercamp/cinema.json
new file mode 100755
index 000000000..34e41cf68
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/cinema.json
@@ -0,0 +1,117 @@
+{
+ "id": "cinema",
+ "title": "Relájate viendo una película",
+ "short_title": "Cine",
+ "icon_class": "challenge_cinema",
+ "description": "En 1895 y luego de transmitir una presentación de los hermanos Skladanowsky que consistía en una seguidilla de películas cortas de 6 segundos de duración acompañadas de una pieza musical, el teatro Berlín Wintergarten se convirtió en el primer teatro-cine. Desafortunadamente, el teatro fue destruido en 1944 durante la Segunda Guerra Mundial a causa de bombardeos.",
+ "cover": "summercamp/day_14.png",
+ "completion_text": "¿Qué tan divertido es un cine si no transmite una película? Intenta dibujar una escena de tu película favorita en la pantalla grande y tal vez algunos amigos mirando la película contigo. ¡No olvides las palomitas de maíz!",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Está oscuro en el cine. Configura el fondo,`background` a negro o `black`, en inglés",
+ "solution": "background black"
+ },
+ {
+ "hint": "En una nueva línea configura el trazo a `0` usando `stroke`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Primero, mueve el cursor para dibujar el piso - **escribe** `moveTo 0, 300`",
+ "solution": "moveTo 0, 300"
+ },
+ {
+ "hint": "Configura el `color` del piso a gris o `gray`",
+ "solution": "color gray"
+ },
+ {
+ "hint": "Dibuja el piso usando un rectángulo - **escribe** `rectangle 500, 200`",
+ "solution": "rectangle 500, 200"
+ },
+ {
+ "hint": "Ahora, mueve el cursor para ubicar la pantalla - **escribe** `moveTo 50, 20`",
+ "solution": "moveTo 50, 20"
+ },
+ {
+ "hint": "Configura el `color` de la pantalla a blanco, `white` en inglés",
+ "solution": "color white"
+ },
+ {
+ "hint": "La pantalla es un rectángulo perfecto de `400` por `250`, recuerda usar la función `rectangle`",
+ "solution": "rectangle 400, 250"
+ },
+ {
+ "hint": "¡Increíble! Este teatro necesita cortinas - **escribe** `moveTo 0`",
+ "solution": "moveTo 0"
+ },
+ {
+ "hint": "Configura el `color` de las cortinas a rojo o `red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Dibuja un rectángulo vertical usando `rectangle` Hazlo de un tamaño de `30` por `330`",
+ "solution": "rectangle 30, 330"
+ },
+ {
+ "hint": "Ahora dibuja una cortina horizontal de un tamaño de `500` por `10`",
+ "solution": "rectangle 500, 10"
+ },
+ {
+ "hint": "¡Se ve genial! Ubica la cortina de la derecha - **escribe** `move 470`",
+ "solution": "move 470"
+ },
+ {
+ "hint": "Dibuja una cortina vertical, igual a la de la izquierda",
+ "solution": "rectangle 30, 330"
+ },
+ {
+ "hint": "Este cine necesita asientos - **escribe** `moveTo 0, 360`",
+ "solution": "moveTo 0, 360"
+ },
+ {
+ "hint": "Configura el borde de los asientos usando `stroke` Hazlo anaranjado o `orangered` y de un tamaño de `10`.",
+ "solution": "stroke orangered, 10"
+ },
+ {
+ "hint": "Vamos a dibujar 3 filas de 6 asientos usando un ciclo - **escribe** `for x in [ 1 .. 3 ]`",
+ "solution": "for x in [ 1 .. 3 ]"
+ },
+ {
+ "hint": "Configura el `color` de los asientos a rojo o en inglés: `red`",
+ "solution": " color red"
+ },
+ {
+ "hint": "Usa la función `rectangle` para los asientos, hazlos de un tamaño de `500` por `40`",
+ "solution": " rectangle 500, 40"
+ },
+ {
+ "hint": "Mueve el cursor para dibujar la parte de atrás de los asientos - **escribe** `move 0, 50`",
+ "solution": " move 0, 50"
+ },
+ {
+ "hint": "Dentro del ciclo, abre un segundo ciclo para la parte de atrás de los asientos - **escribe** `for y in [ 1 .. 6 ]`",
+ "solution": " for y in [ 1 .. 6 ]"
+ },
+ {
+ "hint": "Configura el `color` de la parte trasera de los asientos a rojo oscuro o `darkred`",
+ "solution": " color darkred"
+ },
+ {
+ "hint": "¡Brillante! Ya casi está listo. Ahora dibuja la parte trasera de los asientos usando un arco - **escribe** `arc 60, 0, 1`",
+ "solution": " arc 60, 0, 1"
+ },
+ {
+ "hint": "Ahora separa los asientos entre sí una distancia de `130` píxeles a la derecha",
+ "solution": " move 130"
+ },
+ {
+ "hint": "Por último, asegúrate de que los asientos estén ubicados correctamente - presiona **Enter**, luego la tecla **Borrar** una sola vez y **escribe** `move -800` dentro del **primer** ciclo",
+ "solution": " move -800"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/compass.json b/lib/challenges/locales/es/worlds/summercamp/compass.json
new file mode 100755
index 000000000..b8b86414d
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/compass.json
@@ -0,0 +1,93 @@
+{
+ "id": "compass",
+ "title": "Una brújula es una herramienta esencial",
+ "short_title": "Brújula",
+ "icon_class": "challenge_compass",
+ "description": "Una brújula es simplemente un imán, generalmente una aguja magnetizada. Dado que los opuestos se atraen, el polo sur de la aguja se encuentra atraído al polo norte magnético y natural de la Tierra. ¿Sabías que puedes construir la tuya propia con un clip, un corcho y un imán?",
+ "cover": "summercamp/day_9.png",
+ "completion_text": "Ahora intenta agregar los otros componentes de la brújula: los demás puntos cardinales (E, S, O y también podría ser NE, NO, SE y SO) ¿Por qué no una mano que la sostenga?",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Elige gris claro o `lightgray` para la función `background`",
+ "solution": "background lightgray"
+ },
+ {
+ "hint": "En una nueva línea, configura el trazo de color dorado usando `stroke`. Dorado en inglés es `gold` y hazlo de un tamaño de `20`",
+ "solution": "stroke gold, 20"
+ },
+ {
+ "hint": "Configura el `color` de la brújula a gris o `gray`",
+ "solution": "color gray"
+ },
+ {
+ "hint": "Tu brújula será un círculo o `circle` de un tamaño de `110`",
+ "solution": "circle 110"
+ },
+ {
+ "hint": "Configura el `color` a gris oscuro o `darkgray` para la segunda mitad de la brújula",
+ "solution": "color darkgray"
+ },
+ {
+ "hint": "Configura el trazo o `stroke` otra vez a `0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Dibuja un arco que encaje con la mitad de arriba de la brújula - **escribe** `arc 105, 2, 1`",
+ "solution": "arc 105, 2, 1"
+ },
+ {
+ "hint": "Tenemos que marcar el norte - en una nueva línea **escribe** `moveTo 250, 175`",
+ "solution": "moveTo 250, 175"
+ },
+ {
+ "hint": "Configura el `color` a dorado o `gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Configura la fuente del texto o `font` a `'cursive'` de un tamaño de `25`",
+ "solution": "font 'cursive', 25"
+ },
+ {
+ "hint": "Escribe una N que represente el norte - **escribe** `text 'N'`",
+ "solution": "text 'N'"
+ },
+ {
+ "hint": "Ahora necesitamos una aguja con dos puntas. Configura el `color` a `red` - que es rojo - para la primera punta",
+ "solution": "color red"
+ },
+ {
+ "hint": "Mueve el cursor a la posición de la aguja **escribe** `moveTo 230, 250`",
+ "solution": "moveTo 230, 250"
+ },
+ {
+ "hint": "Dibuja un triángulo usando un polígono - **escribe** `polygon 20, -90, 40, 0`",
+ "solution": "polygon 20, -90, 40, 0"
+ },
+ {
+ "hint": "¡Excelente! Configura el `color` a blanco o `white` para la segunda punta de la aguja",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja la segunda punta - **escribe** `polygon 20, 90, 40, 0`",
+ "solution": "polygon 20, 90, 40, 0"
+ },
+ {
+ "hint": "Termínala con un último detalle. Configura el `color` a dorado o `gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Mueve el cursor al centro de la brújula **escribe** `move 20`",
+ "solution": "move 20"
+ },
+ {
+ "hint": "Dibuja un círculo usando `circle` de un tamaño de `20`",
+ "solution": "circle 20"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/fireworks.json b/lib/challenges/locales/es/worlds/summercamp/fireworks.json
new file mode 100755
index 000000000..3307a370b
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/fireworks.json
@@ -0,0 +1,50 @@
+{
+ "id": "fireworks",
+ "title": "Fuegos Artificiales",
+ "short_title": "Fuegos Artificiales",
+ "icon_class": "challenge_fireworks",
+ "description": "¡El campamento está llegando a su fin y tenemos fuegos artificiales para celebrar! Los fuegos artificiales fueron utilizados durante muchos siglos: se cree que los inventaron los chinos. Estamos listos para empezar el show pero necesitamos un poco de ayuda diseñando los fuegos artificiales ¿podrías hacerlo?",
+ "cover": "summercamp/day_21.png",
+ "completion_text": "¡Buen trabajo! ¡Hiciste una función para dibujar fuegos artificiales! ¿Puedes mejorarla? ¡Dibújalos a lo largo de toda la pantalla, agrega otras creaciones y comparte tu dibujo!",
+ "difficulty": 3,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "El sol ya bajó y hay luna nueva, vamos a dibujar un cielo oscuro con `background black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "Configura el trazo usando `stroke` a rojo o `red` para nuestro fuego artificial",
+ "solution": "stroke red"
+ },
+ {
+ "hint": "Queremos dibujar 60 líneas que salen de un mismo punto, vamos a usar un ciclo `for i in [0 ... 60]`",
+ "solution": "for i in [0 ... 60]"
+ },
+ {
+ "hint": "Todas las líneas deberían diferenciarse en su longitud. Configuremos una longitud aleatoria para cada una usando `length = random 1, 200`.",
+ "solution": " length = random 1, 200"
+ },
+ {
+ "hint": "En cada vuelta de nuestro ciclo, dibujaremos una nueva línea que sale del centro. Cada vez que se ejecute el comando, el ángulo de la línea debería cambiar. Para esto necesitamos un poco de matemática avanzada pero no temas, sólo **escribe** `angle = (360 / 60 * i) * (Math.PI / 180)`.",
+ "solution": " angle = (360 / 60 * i) * (Math.PI / 180)",
+ "validate": "__^ angle *= *\\(360 */ *60 \\* i\\) *\\* *\\(Math[.]PI */ *180\\) *$"
+ },
+ {
+ "hint": "Ya hemos configurado aleatoriamente los ángulos y la longitud de las líneas, ahora configuremos aleatoriamente el punto de la coordenada X donde terminarán las líneas. **Escribe** `dx = 250 + Math.sin(angle) * length`.",
+ "solution": " dx = 250 + Math.sin(angle) * length",
+ "validate": "__^ dx *= *250 *\\+ *Math.sin\\(angle\\) *\\* *length"
+ },
+ {
+ "hint": "Ahora calculemos el punto de la coordenada Y para el final de las líneas. **Escribe** `dy = 250 + Math.cos(angle) * length`.",
+ "solution": " dy = 250 + Math.cos(angle) * length",
+ "validate": "__^ dy *= *250 *\\+ *Math.cos\\(angle\\) *\\* *length"
+ },
+ {
+ "hint": "Finalmente, con todas las coordenadas ya configuradas estamos listos para dibujar las líneas. **Escribe** `lineTo dx, dy` ",
+ "solution": " lineTo dx, dy"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/fishing.json b/lib/challenges/locales/es/worlds/summercamp/fishing.json
new file mode 100755
index 000000000..6b9d86451
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/fishing.json
@@ -0,0 +1,110 @@
+{
+ "id": "fishing",
+ "title": "Pesca",
+ "short_title": "Pesca",
+ "icon_class": "challenge_fishing",
+ "description": "¡Es hora de ir a pescar! Dentro del lago del campamento hay trucha, bagre y perca nadando por un bocado. Toma tu caña de pescar, alguna carnada y encamínate hacia el lago para la aventura de hoy.",
+ "cover": "summercamp/day_17.png",
+ "completion_text": "¡Buen trabajo! Cada vez que presionas una tecla, la imagen se re-dibuja. Intenta mantener apretada la barra espaciadora y fíjate cómo se mueven las olas. Además intenta agregar algún pez o hacer que desaparezca el corcho. ",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "¡Es un día soleado! Configura el fondo a celeste - **escribe** `background lightblue`",
+ "solution": "background lightblue"
+ },
+ {
+ "hint": "Vamos a empezar por dibujar las olas en el lago. Queremos dibujarlas sin color de relleno - configura el `color` a `null`.",
+ "solution": "color null"
+ },
+ {
+ "hint": "Nuestras olas serán de un azul oscuro - **escribe** `stroke darkblue, 8`",
+ "solution": "stroke darkblue, 8"
+ },
+ {
+ "hint": "Mueve el cursor al punto izquierdo en el cual empezarán - **escribe** `moveTo 10, 250`",
+ "solution": "moveTo 10, 250"
+ },
+ {
+ "hint": "Vamos a crear 25 olas dentro de un ciclo - **escribe** `for i in [1 .. 25]`",
+ "solution": "for i in [1 .. 25]"
+ },
+ {
+ "hint": "Crea una ola usando un arco de un radio de 20 píxeles - **escribe** `arc 20, 0.8, 0.2`",
+ "solution": " arc 20, 0.8, 0.2"
+ },
+ {
+ "hint": "Mueve cada ola de manera aleatoria hacia la izquierda - **escribe** `move (random 24, 32)`",
+ "solution": " move (random 24, 32)",
+ "validate": "__ move \\(random 24, +32\\)"
+ },
+ {
+ "hint": "Presiona ENTER y la tecla borrar para eliminar la sangría y salir del ciclo.\n\nLuego muévete al sitio donde dibujaremos el resto del agua - **escribe** `moveTo 0, 270`.",
+ "solution": "moveTo 0, 270"
+ },
+ {
+ "hint": "Configura el `color` del agua para que sea igual al de las olas",
+ "solution": "color darkblue"
+ },
+ {
+ "hint": "Dibuja un rectángulo con `rectangle` y hazlo de un tamaño de `500` píxeles de alto y `250` píxeles de ancho",
+ "solution": "rectangle 500, 250"
+ },
+ {
+ "hint": "Vamos a prepararnos y agregar algunas olas pequeñas - **escribe** `stroke blue, 2`",
+ "solution": "stroke blue, 2"
+ },
+ {
+ "hint": "Vamos a dibujar olas 100 veces - **escribe** `for i in [1 .. 100]`",
+ "solution": "for i in [1 .. 100]"
+ },
+ {
+ "hint": "Mueve cada ola a una posición aleatoria en la superficie del agua - **escribe** `moveTo (random 0, 500), (random 260, 500)`",
+ "solution": " moveTo (random 0, 500), (random 260, 500)",
+ "validate": "__ moveTo ?\\( *random +0, +500 *\\), +\\( *random +260, +500 *\\)"
+ },
+ {
+ "hint": "Ahora dibuja cada ola pequeña como si fuera un arco - **escribe** `arc 10, 0.8, 0.2`",
+ "solution": " arc 10, 0.8, 0.2"
+ },
+ {
+ "hint": "¡Brillante! ¡Cada vez que presiones la barra espaciadora, nuevas olas aparecerán en lugares aleatorios, le da un efecto de movimiento al agua!\n\nPara poder pescar algo necesitaremos una caña de pescar marrón - Sal del ciclo y **escribe** `stroke brown, 10`.",
+ "solution": "stroke brown, 10"
+ },
+ {
+ "hint": "Ahora muévete al punto donde dibujaremos el final de la caña - **escribe** `moveTo 300, 100`",
+ "solution": "moveTo 300, 100"
+ },
+ {
+ "hint": "Dibuja una línea usando `line` de `150` por`400`",
+ "solution": "line 150, 400"
+ },
+ {
+ "hint": "Ahora tenemos que dibujar el hilo - **escribe** `stroke lightgray, 1`",
+ "solution": "stroke lightgray, 1"
+ },
+ {
+ "hint": "Dibújalo para que vaya hacia abajo y a la izquierda - **escribe** `line -100, 200`",
+ "solution": "line -100, 200"
+ },
+ {
+ "hint": "Para enterarnos cuando un pez muerde la carnada tenemos que hacer un corcho flotando. Muévete hacia el final del hilo - **escribe** `move -100, (random 197, 202)`",
+ "solution": "move -100, (random 197, 202)",
+ "valudate": "__move -100, \\(random 197, 202\\)"
+ },
+ {
+ "hint": "El corcho no tendrá líneas de borde - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configura el `color` del corcho a rojo o `red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Ahora dibuja el corcho flotando en el agua - `arc 8, 0, 1`",
+ "solution": "arc 8, 0, 1"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/flag.json b/lib/challenges/locales/es/worlds/summercamp/flag.json
new file mode 100755
index 000000000..bad95be29
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/flag.json
@@ -0,0 +1,77 @@
+{
+ "id": "flag",
+ "title": "Crea tu Bandera",
+ "short_title": "Bandera",
+ "description": "Es tiempo de mostrar tus verdaderos colores. El camping va tomando forma, y como todo líder de campamento necesitas hacer tu propia bandera. Te llevamos a lo básico con una simple bandera y su mástil, pero depende de ti personalizarla. \nPuedes buscar inspiración en otras banderas. Países como Francia, Nigeria, Suecia o Suiza tienen banderas con colores y diseños simples, mientras que España, Brasil, Kenia y Arabia Saudita tienen diseños más complejos. ¡Cuando hayas terminado comparte tu bandera para poder marcar tu territorio!",
+ "icon_class": "challenge_flag",
+ "completion_text": "Tienes un lienzo blanco esperando ser pintado, úsalo sabiamente. ¡Crea la bandera más increíble de todos los tiempos!",
+ "cover": "summercamp/day_4.png",
+ "difficulty": 1,
+ "startAt": 3,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "Dibuja el cielo en el que flameará tu bandera - **escribe** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Configura el trazo a 0 para evitar dibujar líneas - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Vamos a mover el cursor hacia donde estará el mástil de nuestra bandera - **escribe** `moveTo 100, 100`",
+ "solution": "moveTo 100, 100"
+ },
+ {
+ "hint": "Configura el `color` del mástil a gris oscuro o `darkgray` en inglés",
+ "solution": "color darkgray"
+ },
+ {
+ "hint": "Dibuja un fino y largo mástil con un rectángulo - **escribe** `rectangle 10, 400`",
+ "solution": "rectangle 10, 400"
+ },
+ {
+ "hint": "Mueve el cursor a lo alto del mástil para dibujarle un adorno - **escribe** `move 5`",
+ "solution": "move 5"
+ },
+ {
+ "hint": "Configura el `color` del adorno a dorado o `gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Dibuja un círculo de un tamaño de 8 - en una nueva línea **escribe** `circle 8`",
+ "solution": "circle 8"
+ },
+ {
+ "hint": "Ahora mueve el cursor para empezar a dibujar la bandera - **escribe** `moveTo 115, 123`",
+ "solution": "moveTo 115, 123"
+ },
+ {
+ "hint": "Configura el `color` de la bandera a blanco o `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Dibuja la bandera usando la función `rectangle` de un tamaño de `261` por `171`",
+ "solution": "rectangle 261, 171"
+ },
+ {
+ "hint": "La bandera necesita una soga que la sostenga al mástil - **escribe** `moveTo 100, 140`",
+ "solution": "moveTo 100, 140"
+ },
+ {
+ "hint": "Configura el `color` de la soga a marrón o `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Dibuja la soga usando `rectangle` de un tamaño de `20` por `5` ",
+ "solution": "rectangle 20, 5"
+ },
+ {
+ "hint": "¡Ahora es tu turno! Dale a tu bandera el toque final con tu propio diseño - **escribe** `moveTo 120, 125`",
+ "solution": "moveTo 120, 125"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/flashlight.json b/lib/challenges/locales/es/worlds/summercamp/flashlight.json
new file mode 100755
index 000000000..5cec61265
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/flashlight.json
@@ -0,0 +1,93 @@
+{
+ "id": "flashlight",
+ "title": "¡Luces!",
+ "short_title": "Linterna",
+ "icon_class": "challenge_flashlight",
+ "description": "Antes de las linternas se usaban antorchas encendidas con fuego; la llama permitía alumbrar el camino de muchos exploradores. Con la llegada de la electricidad y la invención de las pilas en 1887, los caminos eran alumbrados con bombillas de luz incandescente. Si bien la tecnología de las linternas ha evolucionado, siguen siendo tan útiles como siempre. Mientras tanto, las antorchas que se usaban antes hoy se pueden encontrar en actos de circo donde son arrojadas al aire para hacer malabares.",
+ "cover": "summercamp/day_12.png",
+ "completion_text": "Eres un aventurero con una linterna en la oscuridad. ¿Qué has descubierto gracias a la luz de tu linterna? Intenta dibujar un zorro o una lechuza ululando en un árbol.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "¡Afuera está oscureciendo! Configura el fondo usando `background` de azul oscuro o `darkblue` en inglés",
+ "solution": "background darkblue"
+ },
+ {
+ "hint": "Configura el trazo usando `stroke` a `0`, en este desafío no lo necesitaremos",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Mueve el cursor a donde estará la linterna - **escribe** `moveTo 200, 200`",
+ "solution": "moveTo 200, 200"
+ },
+ {
+ "hint": "Configura el `color` de la linterna a un tono de gris o `dimgray` en inglés",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Dibuja la cabeza de tu linterna usando un rectángulo - **escribe** `rectangle 100, 50`",
+ "solution": "rectangle 100, 50"
+ },
+ {
+ "hint": "Ahora mueve el cursor hacia abajo para dibujar el cuello de la linterna - **escribe** `move 0, 50`",
+ "solution": "move 0, 50"
+ },
+ {
+ "hint": "Configura el `color` de esta parte de la linterna a gris o `gray`",
+ "solution": "color gray"
+ },
+ {
+ "hint": "Dibuja un polígono - **escribe** `polygon 20, 40, 80, 40, 100, 0`",
+ "solution": "polygon 20, 40, 80, 40, 100, 0"
+ },
+ {
+ "hint": "¡Emocionante! Ahora mueve el cursor hacia abajo para dibujar el cuerpo - **escribe** `move 20, 40`",
+ "solution": "move 20, 40"
+ },
+ {
+ "hint": "Configura el `color` a gris oscuro o `darkgray`",
+ "solution": "color darkgray"
+ },
+ {
+ "hint": "Dibuja el cuerpo de la linterna usando la función `rectangle` `60` por `200`",
+ "solution": "rectangle 60, 200"
+ },
+ {
+ "hint": "Lo único que falta ahora es el botón de encendido. Configura el `color` a `dimgray`",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Ahora mueve el cursor hacia abajo para dibujar el botón - **escribe** `move 30, 50`",
+ "solution": "move 30, 50"
+ },
+ {
+ "hint": "Dibuja una elipse - **escribe** `ellipse 10, 30`",
+ "solution": "ellipse 10, 30"
+ },
+ {
+ "hint": "Configura el `color` del botón a rojo o `red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Dibuja el botón con una elipse - **escribe** `ellipse 10, 20`",
+ "solution": "ellipse 10, 20"
+ },
+ {
+ "hint": "¡Encendiste la linterna! Configura el color del rayo de luz - **escribe** `color \"rgba(255, 255, 0, 0.8)\"`",
+ "solution": "color \"rgba(255, 255, 0, 0.8)\""
+ },
+ {
+ "hint": "Coloca el rayo de luz enfrente de la linterna - **escribe** `move -50, -140`",
+ "solution": "move -50, -140"
+ },
+ {
+ "hint": "Dibuja el rayo de luz usando la función polygon - **escribe** `polygon 100, 0, 180, -300, -80, -300`",
+ "solution": "polygon 100, 0, 180, -300, -80, -300"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/foraging.json b/lib/challenges/locales/es/worlds/summercamp/foraging.json
new file mode 100755
index 000000000..9939283f4
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/foraging.json
@@ -0,0 +1,57 @@
+{
+ "id": "foraging",
+ "title": "Recolectar",
+ "short_title": "Recolectar",
+ "icon_class": "challenge_foraging",
+ "description": "Los lugares silvestres están repletos de plantas y fauna para comer. Desde hongos hasta fresas y una gran diversidad de bayas, cualquier comida puede ser improvisada con los alimentos que nos da la naturaleza. Las bayas son muy comunes en muchas partes del mundo y en Camp Kano crecen silvestremente aunque son difíciles de encontrar. Tendrás que usar tus habilidades de programación para encontrarlas.",
+ "cover": "summercamp/day_19.png",
+ "completion_text": "¡Espera! ¿Dónde están esas bayas que deberías estar recolectando? ¡Usa `color red`, `moveTo`, y `square 25` para agregar algunas al dibujo!",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Configura el trazo a `0` usando `stroke`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "¡Es un día soleado! Configura el fondo a azul usando `background` - recuerda que azul en inglés es `blue`.",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Nuestra escena de recolección de bayas necesita un campo de césped para empezar. Configura el color de dibujo a verde que es `green` en inglés.",
+ "solution": "color green"
+ },
+ {
+ "hint": "Queremos el césped en la mitad de abajo de la pantalla. Muévete a la posición con `moveTo 0, 300`",
+ "solution": "moveTo 0, 300"
+ },
+ {
+ "hint": "Dibuja el césped usando un rectángulo grande, usa `rectangle` y hazlo de un tamaño de `500, 200`",
+ "solution": "rectangle 500, 200"
+ },
+ {
+ "hint": "Ahora agreguemos un lindo árbol a nuestra escena de recolección. Comienza por moverte exactamente a las coordenadas `50, 100`.",
+ "solution": "moveTo 50, 100"
+ },
+ {
+ "hint": "Las hojas las representaremos con un cuadrado, usa `square 150`.",
+ "solution": "square 150"
+ },
+ {
+ "hint": "Ahora debemos dibujar el tronco. Mueve el cursor usando `moveTo 100, 250`.",
+ "solution": "moveTo 100, 250"
+ },
+ {
+ "hint": "La madera es de un bonito color marrón oscuro o `darkbrown` en inglés. Configura el color de dibujo.",
+ "solution": "color darkbrown"
+ },
+ {
+ "hint": "Para el tronco usa `rectangle` con un ancho de `40` y alto de `150`",
+ "solution": "rectangle 40, 150"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/ghost.json b/lib/challenges/locales/es/worlds/summercamp/ghost.json
new file mode 100755
index 000000000..63c954219
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/ghost.json
@@ -0,0 +1,91 @@
+{
+ "id": "ghost",
+ "title": "Encuentro con Fantasma",
+ "short_title": "Fantasma",
+ "description": "¡Bu! A medida que cae la noche, los espíritus salen a jugar. Estudios recientes reportaron que un 87% de trabajadores de oficina chinos creen en los fantasmas y según otro estudio un 25% de británicos dicen haber visto alguno. Está oscureciendo, se hace difícil ver con claridad pero creo que... ¡encontramos un fantasma! Usando tus poderes creativos de programación puedes decidir si es amigable o no...",
+ "icon_class": "challenge_ghost",
+ "completion_text": "¡Escalofriante! ¿Puedes convertirlo en un fantasma más aterrador? Tal vez si pudiera decir \"Boo!\"...",
+ "difficulty": 2,
+ "cover": "summercamp/day_7.png",
+ "startAt": 6,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Necesitamos un color escalofriante para el fondo tal como violeta oscuro - **escribe** `background darkpurple`",
+ "solution": "background darkpurple"
+ },
+ {
+ "hint": "Configura el trazo a `0` para evitar dibujar líneas usando `stroke`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configura el `color` del fantasma a blanco o `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Ahora dibuja el cuerpo del fantasma usando una elipse - **escribe** `ellipse 120, 140`",
+ "solution": "ellipse 120, 140"
+ },
+ {
+ "hint": "Configura el `color` a negro o `black` para los ojos",
+ "solution": "color black"
+ },
+ {
+ "hint": "Mueve el cursor para dibujar el primer ojo - **escribe** `move -40, -50`",
+ "solution": "move -40, -50"
+ },
+ {
+ "hint": "Dibuja el ojo con una elipse - **escribe** `ellipse 20, 30`",
+ "solution": "ellipse 20, 30"
+ },
+ {
+ "hint": "Configura el `color` a gris claro usando `lightgray`",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Ahora dibuja un círculo usando la función `circle` de un tamaño de `5`",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "¡Se ve bien! Ahora mueve el cursor para dibujar el segundo ojo - **escribe** `move 30`",
+ "solution": "move 30"
+ },
+ {
+ "hint": "Configura el `color` del ojo a negro o `black` de nuevo",
+ "solution": "color black"
+ },
+ {
+ "hint": "Dibuja el segundo ojo usando una elipse - **escribe** `ellipse 20, 30`",
+ "solution": "ellipse 20, 30"
+ },
+ {
+ "hint": "Configura el `color` a gris claro o `lightgray`",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Dibuja un círculo con la función `circle` de un tamaño de `5`",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "¡Ya casi lo terminas! Configura el `color` a blanco o `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Ahora mueve el cursor hacia arriba - **escribe** `move -80, 150`",
+ "solution": "move -80, 150"
+ },
+ {
+ "hint": "Abramos un ciclo - **escribe** `for i in [ 1 .. 5 ]`",
+ "solution": "for i in [ 1 .. 5 ]"
+ },
+ {
+ "hint": "Dibuja un círculo usando `circle` de un tamaño de `45`",
+ "solution": " circle 45"
+ },
+ {
+ "hint": "Y mueve el cursor levemente hacia la derecha - **escribe** `move 45`",
+ "solution": " move 45"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/guitar.json b/lib/challenges/locales/es/worlds/summercamp/guitar.json
new file mode 100755
index 000000000..25170a796
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/guitar.json
@@ -0,0 +1,128 @@
+{
+ "id": "guitar",
+ "title": "Toca una linda y dulce canción",
+ "short_title": "Guitarra",
+ "icon_class": "challenge_guitar",
+ "description": "¿Sabías que la guitarra más antigua del mundo es de 3500 años atrás? Era un instrumento de 3 cuerdas. Su dueño era un cantante egipcio llamado Har-Mose, y cuando falleció la guitarra fue enterrada a su lado. El instrumento fue desenterrado y, hoy en día, se exhibe en el Museo Arqueológico del Cairo en Egipto.",
+ "cover": "summercamp/day_13.png",
+ "completion_text": "¿De qué sirve una guitarra si no hay una fogata y gente cantando alrededor? Intenta dibujar una linda fogata con algunos malvaviscos para ser tostados. Tal vez puedes hacer una luna en el horizonte. ¡Se creativo!",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "¡Es una hermosa noche! Configura el color del fondo a azul oscuro - **escribe** `background darkblue`",
+ "solution": "background darkblue"
+ },
+ {
+ "hint": "Configura el trazo a 0 usando `stroke`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Configura el `color` de tu guitarra a marrón claro o `lightbrown`.",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Mueve el cursor al punto donde ubicaremos la caja, la parte más grande de tu guitarra - **escribe** `moveTo 120, 250`",
+ "solution": "moveTo 120, 250"
+ },
+ {
+ "hint": "Dibuja la primera parte de la caja usando una elipse - **escribe** `ellipse 60, 65`",
+ "solution": "ellipse 60, 65"
+ },
+ {
+ "hint": "Ahora para la segunda parte mueve el cursor `70` píxeles a la derecha usando `move`.",
+ "solution": "move 70"
+ },
+ {
+ "hint": "Dibuja un círculo de un tamaño de `55` usando `circle`.",
+ "solution": "circle 55"
+ },
+ {
+ "hint": "Toda guitarra necesita un mástil, mueve el cursor para ubicarlo - **escribe** `move 20, -10`",
+ "solution": "move 20, -10"
+ },
+ {
+ "hint": "Configura el `color` del mástil a marrón o `brown`.",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Dibuja el mástil con un rectángulo - **escribe** `rectangle 150, 20`",
+ "solution": "rectangle 150, 20"
+ },
+ {
+ "hint": "Ahora ubiquemos el clavijero - **escribe** `move 150, -5`",
+ "solution": "move 150, -5"
+ },
+ {
+ "hint": "Dibuja el clavijero usando la función `rectangle` hazlo de `40` por `30`.",
+ "solution": "rectangle 40, 30"
+ },
+ {
+ "hint": "Ahora necesitamos una boca - **escribe** `move -170, 15`",
+ "solution": "move -170, 15"
+ },
+ {
+ "hint": "Configura el borde de la boca usando `stroke` `brown` y hazla de un tamaño de `10`.",
+ "solution": "stroke brown, 10"
+ },
+ {
+ "hint": "Configura el `color` de la boca a negro o `black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Dibuja la boca con un círculo de `20` de tamaño, usa `circle`.",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "El puente es la pieza que sostiene las cuerdas a la caja. Ubiquémoslo - **escribe** `move -50, -15`",
+ "solution": "move -50, -15"
+ },
+ {
+ "hint": "Configura el borde del puente usando `stroke` `red` y de un tamaño de `10`.",
+ "solution": "stroke red, 10"
+ },
+ {
+ "hint": "Dibuja el puente con un rectángulo de un tamaño de `2` por `35`. Usa `rectangle`.",
+ "solution": "rectangle 2, 35"
+ },
+ {
+ "hint": "Esa guitarra se ve muy bien. Es hora de hacer las cuerdas - **escribe** `move 0, 9`",
+ "solution": "move 0, 9"
+ },
+ {
+ "hint": "Configura las propiedades de las cuerdas usando `stroke` `white` de un tamaño de `1`.",
+ "solution": "stroke white, 1"
+ },
+ {
+ "hint": "Dibujemos las 4 cuerdas de una sola vez usando un ciclo - **escribe** `for i in [ 1 .. 4 ]`",
+ "solution": "for i in [ 1 .. 4 ]"
+ },
+ {
+ "hint": "Dibuja una cuerda usando una línea - **escribe** `line 250`",
+ "solution": " line 250"
+ },
+ {
+ "hint": "Y mueve el cursor levemente para dibujar las siguientes - **escribe** `move 0, 4`",
+ "solution": " move 0, 4"
+ },
+ {
+ "hint": "Presiona **Enter** y la tecla **Borrar** para configurar el `color` a blanco o `white` fuera del ciclo",
+ "solution": "color white"
+ },
+ {
+ "hint": "¡Estás listo para tocar música! Abre un nuevo ciclo - **escribe** `for i in [ 1 .. 10 ]`",
+ "solution": "for i in [ 1 .. 10 ]"
+ },
+ {
+ "hint": "Elige una posición aleatoria - **escribe** `moveTo (random 200, 400), (random 100, 400)`",
+ "solution": " moveTo (random 200, 400), (random 100, 400)",
+ "validate": "__ moveTo ?\\( *random +200, +400 *\\), +\\( *random +100, +400 *\\)"
+ },
+ {
+ "hint": "¡Vamos, toca algunas notas! - **copia y pega** `text '♪'`",
+ "solution": " text '♪'"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/hiking.json b/lib/challenges/locales/es/worlds/summercamp/hiking.json
new file mode 100755
index 000000000..cb8117156
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/hiking.json
@@ -0,0 +1,112 @@
+{
+ "id": "hiking",
+ "title": "Cartel de Excursionismo",
+ "short_title": "Excursionismo",
+ "icon_class": "challenge_hiking",
+ "description": "La hermosa sierra de Camp Kano es conocida por su profundo color azul. Sin embargo las 10 montañas de la sierra parecen estar cambiando de posición todo el tiempo lo que hace difícil escalarlas. ¿Deberían los acampantes intentar escalar? ¡Quién sabe! Pero la dirección del campamento quiere un cartel promocionando la actividad.",
+ "cover": "summercamp/day_18.png",
+ "completion_text": "¡Bien hecho, has diseñado un increíble cartel publicitario! Ahora juega con los valores aleatorios y el texto. ¿Le falta otra cosa más a este cartel?",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Por encima de las nubes el cielo se ve azul, **escribe** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Las montañas de Camp Kano son de un color azul oscuro. Configura el color usando `color darkblue`.",
+ "solution": "color darkblue"
+ },
+ {
+ "hint": "Las montañas tienen un bonito borde grueso, configúralo con `stroke white, 10`",
+ "solution": "stroke white, 10"
+ },
+ {
+ "hint": "Vamos a dibujar las diez montañas de Camp Kano utilizando un ciclo. Esto será más rápido que dibujarlas una por una. **Escribe** `for i in [ 0 ... 10 ]` para abrir el ciclo.",
+ "solution": "for i in [ 0 ... 10 ]"
+ },
+ {
+ "hint": "Por cada vez que se ejecuta el ciclo, queremos que se dibuje el pico de una montaña aleatoriamente a través de la pantalla. Vamos a seleccionar un valor para X en un rango usando `x = random 0, 500`",
+ "solution": " x = random 0, 500"
+ },
+ {
+ "hint": "Sin embargo, para el valor de Y, queremos que cada pico de montaña sea dibujado en la parte alta de la pantalla. **Escribe** `y = random 0, 200`",
+ "solution": " y = random 0, 200"
+ },
+ {
+ "hint": "Ahora con nuestros valores X e Y configurados podemos mover el cursor. **Escribe** `moveTo x, y`",
+ "solution": " moveTo x, y"
+ },
+ {
+ "hint": "Dibuja una montaña para cada ciclo usando la función polygon. **Escribe** `polygon 400, 500, -400, 500`",
+ "solution": " polygon 400, 500, -400, 500"
+ },
+ {
+ "hint": "Primero, sal del ciclo usando la tecla `borrar`. Ahora dibujemos algunas nubes, empieza por suprimir el trazo con `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Las nubes están hechas de gotitas de agua y es fácil ver a través de ellas. Para poder darle al color un efecto de transparencia podemos usar la función 'opacity'. **Escribe** `color (opacity white, .1)`",
+ "solution": "color (opacity white, .1)",
+ "validate": "__color \\(opacity white, .1\\)"
+ },
+ {
+ "hint": "Ahora abramos un nuevo ciclo para dibujar las nubes algodonadas, necesitaremos usar muchos círculos por eso pidámosle al ciclo que los repita unas 250 veces. **Escribe** `for j in [ 0 ... 250 ]`.",
+ "solution": "for j in [ 0 ... 250 ]"
+ },
+ {
+ "hint": "Queremos círculos de nubes salpicados aleatoriamente alrededor de la pantalla, elijamos un rango para X entre 0 y 500 usando `x = random 0, 500`",
+ "solution": " x = random 0, 500"
+ },
+ {
+ "hint": "Sin embargo, para el valor de Y, queremos que sean dibujadas únicamente en la parte de abajo de la pantalla. **Escribe** `y = random 300, 500`",
+ "solution": " y = random 300, 500"
+ },
+ {
+ "hint": "Ahora con los valores de X e Y configurados, podemos mover el cursor. **Escribe** `moveTo x, y`",
+ "solution": " moveTo x, y"
+ },
+ {
+ "hint": "Dibujemos las nubes con círculos salpicados por la pantalla usando `circle`, hazlos de un tamaño de `100`",
+ "solution": " circle 100"
+ },
+ {
+ "hint": "Presiona la tecla `borrar` una vez para salir del ciclo. Ahora mueve el cursor a donde escribiremos un poco de texto `moveTo 250, 350`",
+ "solution": "moveTo 250, 350"
+ },
+ {
+ "hint": "Configura el color de dibujo a rojo - **Escribe** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "El mensaje tiene que ser fuerte e impactante, configuremos la fuente a negrita usando `bold true`",
+ "solution": "bold true"
+ },
+ {
+ "hint": "También queremos que el texto esté en cursiva, hazlo con `italic true`",
+ "solution": "italic true"
+ },
+ {
+ "hint": "Finalmente configuremos el estilo de fuente a Bariol y de un tamaño de 40 usando `font 'Bariol', 40`",
+ "solution": "font 'Bariol', 40"
+ },
+ {
+ "hint": "Ingresa el texto con `text 'Excursión a las montañas de'`",
+ "solution": "text 'Excursión a las montañas de'"
+ },
+ {
+ "hint": "Ahora, queremos una nueva línea para el gran final **escribe** `font 90`",
+ "solution": "font 90"
+ },
+ {
+ "hint": "Movamos el cursor hacia abajo para terminar nuestro mensaje `move 0, 90`",
+ "solution": "move 0, 90"
+ },
+ {
+ "hint": "El toque final: **Escribe** `text 'Camp Kano!'`",
+ "solution": "text 'Camp Kano!'"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/index.json b/lib/challenges/locales/es/worlds/summercamp/index.json
new file mode 100644
index 000000000..a54bd7049
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/index.json
@@ -0,0 +1,26 @@
+{
+
+ "challenges": [
+ "./camp_sign",
+ "./campsite",
+ "./tent",
+ "./flag",
+ "./camp_badge",
+ "./campfire",
+ "./ghost",
+ "./backpack",
+ "./compass",
+ "./camera",
+ "./bear",
+ "./flashlight",
+ "./guitar",
+ "./cinema",
+ "./bow_and_arrow",
+ "./table_tennis",
+ "./fishing",
+ "./hiking",
+ "./foraging",
+ "./tree",
+ "./fireworks"
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/table_tennis.json b/lib/challenges/locales/es/worlds/summercamp/table_tennis.json
new file mode 100755
index 000000000..bbef6d480
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/table_tennis.json
@@ -0,0 +1,121 @@
+{
+ "id": "table_tennis",
+ "title": "Tenis de mesa",
+ "short_title": "Tenis de mesa",
+ "icon_class": "challenge_tennis",
+ "description": "El Tenis de mesa se juega de a dos personas, una de cada lado de una mesa que refleja una miniatura de una cancha de tenis. Los games se juegan a 11 o 21 puntos y una vez finalizada la partida, el ganador tira su pala al piso y expulsa su grito de gloria.",
+ "cover": "summercamp/day_16.png",
+ "completion_text": "¡Bien hecho! Hiciste una hermosa mesa de Ping Pong en 3D. ¡Pero no hay nadie jugando ni mirando la partida! Usa tu creatividad y los comandos para dibujar algunas personas disfrutando del juego.",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Comienza por configurar el trazo a 0 con `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "¡Es un hermoso día soleado! Configura el color del fondo a azul - **escribe** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Configura el color a verde para hacer el césped. **escribe** `color green`.",
+ "solution": "color green"
+ },
+ {
+ "hint": "Mueve el cursor al lugar adecuado con `moveTo 0, 350`",
+ "solution": "moveTo 0, 350"
+ },
+ {
+ "hint": "Cubre la parte de abajo del lienzo con césped. Dibuja un rectángulo usando `rectangle` Hazlo de `500` por `150`.",
+ "solution": "rectangle 500, 150"
+ },
+ {
+ "hint": "Dale a tu mesa patas sólidas para sostenerla. Configura el `color` a marrón usando la palabra inglesa `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Mueve el cursor hacia donde ubicaremos la primera pata. **Escribe** `moveTo 20, 310`",
+ "solution": "moveTo 20, 310"
+ },
+ {
+ "hint": "Ahora dibuja la primera pata. **Escribe** `rectangle 15, 150`.",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "Ahora la segunda pata **Escribe** `moveTo 465, 310`.",
+ "solution": "moveTo 465, 310"
+ },
+ {
+ "hint": "Ahora dibuja la segunda pata. **Escribe** `rectangle 15, 150`.",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "Y la tercera. **Escribe** `moveTo 40, 250`.",
+ "solution": "moveTo 40, 250"
+ },
+ {
+ "hint": "Ahora dibuja la tercera pata idéntica a las anteriores",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "Finalmente, la cuarta pata. **Escribe** `moveTo 445, 250`.",
+ "solution": "moveTo 445, 250"
+ },
+ {
+ "hint": "Dibuja la cuarta pata",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "¡Ahora a poner la mesa! Configura el `color` de la mesa a verde oscuro usando el equivalente inglés `darkgreen`.",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Muévete para ubicar la mesa. **Escribe** `moveTo 10, 300`.",
+ "solution": "moveTo 10, 300"
+ },
+ {
+ "hint": "Tu mesa tiene perspectiva, para dibujarla usa la función `polygon 30, -60, 450, -60, 480, 0`",
+ "solution": "polygon 30, -60, 450, -60, 480, 0"
+ },
+ {
+ "hint": "Para un efecto 3D, aclara el color de dibujo con `color lighten darkgreen, 10`",
+ "solution": "color lighten darkgreen, 10"
+ },
+ {
+ "hint": "**Dibuja** el lado de la mesa con `rectangle 480, 10`",
+ "solution": "rectangle 480, 10"
+ },
+ {
+ "hint": "Para la red, configura el `color` a blanco o `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Mueve el cursor a `moveTo 248, 220`",
+ "solution": "moveTo 248, 220"
+ },
+ {
+ "hint": "Dibuja la red, usa la función `rectangle` de un tamaño de `4` por `80`",
+ "solution": "rectangle 4, 80"
+ },
+ {
+ "hint": "¡Necesitamos una pelota saltarina! Usa la función aleatoria (random) para elegir al alzar de qué lado de la mesa estará la pelota. **Escribe** `x = random 40, 460`.",
+ "solution": "x = random 40, 460"
+ },
+ {
+ "hint": "¡Ahora usaremos la función random otra vez para decidir cuán alto estará la pelota! **Escribe** `y = random 150, 250`.",
+ "solution": "y = random 150, 250"
+ },
+ {
+ "hint": "Mueve el cursor a las variables de X e Y que acabas de crear. **Escribe** `moveTo x, y`.",
+ "solution": "moveTo x, y"
+ },
+ {
+ "hint": "Finalmente, dibuja tu pelota con un círculo de un radio de 7. **Escribe** `circle 7`.",
+ "solution": "circle 7"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/tent.json b/lib/challenges/locales/es/worlds/summercamp/tent.json
new file mode 100755
index 000000000..321f8dd7f
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/tent.json
@@ -0,0 +1,73 @@
+{
+ "id": "tent",
+ "title": "Arma tu carpa",
+ "short_title": "Carpa",
+ "description": "No hay campamento sin carpa. Te hemos proporcionado una carpa básica, que se hace con lona, cuerdas y estacas ¡Pero con tus poderes creativos puedes transformarla en la carpa que tú quieras! \nLa gente ha vivido en carpas tipis y las yurtas por miles de años. ¿Qué podrías agregarle a tu carpa para que te mantenga seguro y cálido? Te invitamos a inspirarte mirando otras creaciones en Mundo Kano o buscando diseños en internet.",
+ "icon_class": "challenge_setcamp",
+ "completion_text": "¡Buen trabajo! Ya aprendiste en el último desafío a hacer un árbol, ¿por qué no lo agregás a tu dibujo? ¿Es un pájaro aquello que vuela en el cielo?",
+ "cover": "summercamp/day_3.png",
+ "difficulty": 1,
+ "startAt": 2,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Dibuja un día soleado - **escribe** `background lightblue`",
+ "solution": "background lightblue"
+ },
+ {
+ "hint": "Configura el trazo a 0 para evitar dibujar líneas - **escribe** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Mueve el cursor al punto donde ubicaremos el sol - **escribe** `moveTo 400, 50`",
+ "solution": "moveTo 400, 50"
+ },
+ {
+ "hint": "Configura el `color` del sol a amarillo, `yellow` en inglés",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Dibuja el sol usando un círculo - **escribe** `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Mueve el cursor para dibujar el césped - **escribe** `moveTo 0, 350`",
+ "solution": "moveTo 0, 350"
+ },
+ {
+ "hint": "Configura el `color` del césped a verde, `green` en inglés",
+ "solution": "color green"
+ },
+ {
+ "hint": "Dibuja el césped usando la función rectangle - **escribe** `rectangle 500, 150`",
+ "solution": "rectangle 500, 150"
+ },
+ {
+ "hint": "¡Excelente! Ahora estamos listos para dibujar la carpa - **escribe** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Posiciona el cursor sobre el césped - **escribe** `moveTo 100, 350`",
+ "solution": "moveTo 100, 350"
+ },
+ {
+ "hint": "Dibuja un triángulo usando `polygon` - **escribe** `polygon 150, -200, 300, 0`",
+ "solution": "polygon 150, -200, 300, 0"
+ },
+ {
+ "hint": "Necesitamos dibujarle la entrada ahora. Configura el `color` a rojo oscuro escbriendo `darkred`",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "Posiciona el cursor en la punta de la carpa - **escribe** `moveTo 250, 150`",
+ "solution": "moveTo 250, 150"
+ },
+ {
+ "hint": "Dibuja la entrada - **escribe** `polygon 30, 200, -30, 200`",
+ "solution": "polygon 30, 200, -30, 200"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/es/worlds/summercamp/tree.json b/lib/challenges/locales/es/worlds/summercamp/tree.json
new file mode 100755
index 000000000..189bdf922
--- /dev/null
+++ b/lib/challenges/locales/es/worlds/summercamp/tree.json
@@ -0,0 +1,17 @@
+{
+ "id": "tree",
+ "title": "Árbol Fractal",
+ "short_title": "Árbol",
+ "icon_class": "challenge_tree",
+ "description": "¡Es hora de jugar! los supervisores del campamento han hecho algo para que juegues, un árbol cuyas ramas pueden crecer ampliamente hacia afuera y contrastar al interior. Todo desarrollado con comandos ¡Personalízalo!",
+ "cover": "summercamp/day_20.png",
+ "completion_text": "Juega con los números azules de los primeros comandos ¿Qué función cumplen? ¿Por qué tienen ese efecto? Moldea el árbol a tu gusto. Si algo se complica, vuelve al menú inicial y empieza de nuevo.",
+ "difficulty": 3,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "code": "# Juega con estos números azules\narmLength = 50\niterations = 9 # Pueden chocar si pasas el 15!\ndegreeChange = 20\nhasBlossoms = 3\nblossomSize = 70\nblossomOpacity = .02\n# ^^^ Juega con estos números azules ^^^\n\n###\nEsta es una función que dibuja las ramas\ndel árbol, cuando se completa, vuelve a ejecutarse\nuna y otra vez hasta que todas las\nramas sean dibujadas!\n###\ndrawBranch = (x, y, branchesLeft, startAngle) ->\n if branchesLeft > 0 \n # Muévete hacia donde será dibujada la\n # siguiente rama:\n moveTo x, y\n \n # Las ramas siempre serán azules pero el ancho\n # depende de la distancia respecto a la base del árbol\n stroke blue, branchesLeft \n \n # Un poco de trigonometría, ¡está bien\n # if no entiendes nada de esto! Esto \n # calcula las coordenadas donde debería \n # dibujarse la ramas, y donde\n # debería empezar la siguiente.\n dx = Math.cos(startAngle) * armLength \n dy = -Math.sin(startAngle) * armLength \n \n # Dibuja una línea en las coordenadas que\n # acabamos de calcular\n line dx, dy \n \n # Este bloque de comandos únicamente se ejecuta si\n # la rama tiene flores ¡Puedes\n # cambiar la variable hasBlossoms \n # arriba!\n if branchesLeft <= hasBlossoms \n # Configura el color de dibujo a un lindo rojo\n color opacity \"rgb(247, 45, 99)\", blossomOpacity\n # Nuestras flores no deberían tener un trazo\n stroke 0 \n # ¡Dibuja las flores! Estas flores\n # se superponen unas a otras para crear un\n # lindo efecto.\n circle blossomSize\n \n # Comienza la siguiente rama de la derecha\n drawBranch(x + dx, y + dy, branchesLeft - 1, startAngle - Math.PI / 180 * degreeChange) \n # Comienza la siguiente rama de la izquierda\n drawBranch(x + dx, y + dy, branchesLeft - 1, startAngle + Math.PI / 180 * degreeChange)\n \n###\nFinalmente, ejecutamos la función y vemos qué \nsucede. Juega con los números azules de los primeros \ncomandos y fíjate qué puedes cambiar de este árbol\n###\ndrawBranch(stage.width * .5, stage.height, iterations, Math.PI / 2, length)",
+ "steps": []
+}
diff --git a/lib/challenges/locales/ja/index.json b/lib/challenges/locales/ja/index.json
new file mode 100644
index 000000000..d5083d36b
--- /dev/null
+++ b/lib/challenges/locales/ja/index.json
@@ -0,0 +1,71 @@
+{
+ "worlds": [
+ {
+ "id": "basic",
+ "name": "初級",
+ "description": "このチャレンジを通して、メークアートの基礎を身につけよう",
+ "cover": "world_covers/basic",
+ "world_path": "worlds/basic",
+ "css_class": "basic-challenges",
+ "share_strategy": "optional",
+ "type": "normal"
+
+ },
+ {
+ "id": "medium",
+ "name": "中級",
+ "description": "中級向けのチャレンジ",
+ "cover": "world_covers/medium",
+ "world_path": "worlds/medium",
+ "css_class": "medium",
+ "dependency": ["basic"],
+ "share_strategy": "optional",
+ "type": "normal"
+ },
+ {
+ "id": "pixelhack",
+ "name": "ピクセル・ハック",
+ "description": "ハウアー・オブ・コード用のチャレンジです",
+ "cover": "world_covers/pixelhack",
+ "world_path": "worlds/pixelhack",
+ "css_class": "pixelhack",
+ "type": "campaign",
+ "certificate_after": 2,
+ "teachers_guide": "/guides/PixelHackEducatorGuide.zip",
+ "share_strategy": "optional",
+ "updateForm": {
+ "url": "https://docs.google.com/forms/d/1nTJ-waAVLQCO3myxKMDQLafL5Fnf9uTPtiejuH-74sY/formResponse",
+ "field": "entry.1965349241"
+ },
+ "socialText": {
+ "email": "Kanoのピクセルハックで、ビデオゲームのアート歴史をハックしよう。",
+ "facebook": "Kanoの #ピクセルハック で、ビデオゲームのアート歴史をハックしよう。",
+ "twitter": "@TeamKano の #ピクセルハック で、ビデオゲームのアート歴史をハックしよう!\nhttp://art.kano.me/challenges/pixelhack/"
+ }
+ },
+ {
+ "id": "summercamp",
+ "name": "夏キャンプ",
+ "description": "このチャレンジを通して、メークアートの基礎を身につけよう",
+ "cover": "world_covers/summercamp",
+ "world_path": "worlds/summercamp",
+ "css_class": "_summercamp",
+ "dependency": ["medium"],
+ "share_strategy": "optional",
+ "type": "campaign"
+
+ },
+ {
+ "id": "mischiefweek2015",
+ "name": "いたずらの週",
+ "description": "ハロウィーン専用の世界です",
+ "cover": "world_covers/mischiefweek",
+ "world_path": "worlds/mischiefweek2015",
+ "start_date": "2015-10-26T06:00:00",
+ "css_class": "mischiefweek2015",
+ "type": "campaign",
+ "share_strategy": "optional",
+ "sales_popup_after": 2
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/basic/baloon.json b/lib/challenges/locales/ja/worlds/basic/baloon.json
new file mode 100644
index 000000000..122d81caa
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/basic/baloon.json
@@ -0,0 +1,42 @@
+{
+ "id": "baloon",
+ "title": "青い風船",
+ "cover": "blue-baloon.png",
+ "description": "多辺形で空を飛ぶ風船を描こう!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "鉛筆の色を青に変えよう。`color blue`を**タイプ**",
+ "solution": "color blue"
+ },
+ {
+ "hint": "筆の幅を0にして、線を描かないようにしよう。",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "100の大きさの円形を描こう。",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "100歩で下に動こう。`move 0, 100`を**タイプ**",
+ "solution": "move 0, 100"
+ },
+ {
+ "hint": "結び目を描こう。`polygon 15, 15, -15, 15`を**タイプ**",
+ "solution": "polygon 15, 15, -15, 15"
+ },
+ {
+ "hint": "もう少し下に動こう。`move 0, 15`を**タイプ**",
+ "solution": "move 0, 15"
+ },
+ {
+ "hint": "太くて黒い筆を選ぼう。`stroke black, 5`を**タイプ**",
+ "solution": "stroke black, 5"
+ },
+ {
+ "hint": "風船の糸を描こう。`line 0, 200`を**タイプ**",
+ "solution": "line 0, 200"
+ }
+ ],
+ "completion_text": "素晴らしい風船!次に進む前に色を変えてみない?"
+}
diff --git a/lib/challenges/locales/ja/worlds/basic/breakfast.json b/lib/challenges/locales/ja/worlds/basic/breakfast.json
new file mode 100644
index 000000000..b42d3d54d
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/basic/breakfast.json
@@ -0,0 +1,197 @@
+{
+ "id": "breakfast",
+ "title": "Breakfast",
+ "description": "Draw a bacon and eggs breakfast!",
+ "startAt": 2,
+ "fatherDay": true,
+ "steps": [
+ {
+ "hint": "Set the background to blue - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a circle with a size of 240",
+ "solution": "\n#Plate\ncircle 240",
+ "validate": "circle 240"
+ },
+ {
+ "hint": "Set the color to `'#eee'` - **type** `color '#eee'`",
+ "solution": "color '#eee'"
+ },
+ {
+ "hint": "Draw a circle with a size of 200",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Move up and left - **type** `move -70, -70`",
+ "solution": "\n#Egg\nmove -70, -70",
+ "validate": "move -70,-70"
+ },
+ {
+ "hint": "Set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a circle with a size of 80",
+ "solution": "circle 80"
+ },
+ {
+ "hint": "Set the color to yellow",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Draw a circle with a size of 30",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Move up and right - **type** `move 90, -60`",
+ "solution": "\n#Bacon 1\nmove 90, -60",
+ "validate": "move 90,-60"
+ },
+ {
+ "hint": "Set the color to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 60 and 200 - **type** `rectangle 60, 200`",
+ "solution": "rectangle 60, 200"
+ },
+ {
+ "hint": "Move 30 to the right - **type** `move 30`",
+ "solution": "move 30"
+ },
+ {
+ "hint": "Set the color to `'#aa0000'` - **type** `color '#aa0000'`",
+ "solution": "color '#aa0000'"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 30 and 200 - **type** `rectangle 30, 200`",
+ "solution": "rectangle 30, 200"
+ },
+ {
+ "hint": "Move down and left - **type** `move -3, 10`",
+ "solution": "move -3, 10"
+ },
+ {
+ "hint": "Set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 6 and 180 - **type** `rectangle 6, 180`",
+ "solution": "rectangle 6, 180"
+ },
+ {
+ "hint": "Move down and right - **type** `move 40, 50`",
+ "solution": "\n#Bacon 2\nmove 40, 50",
+ "validate": "move 40, 50"
+ },
+ {
+ "hint": "Set the color to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 60 and 200 - **type** `rectangle 60, 200`",
+ "solution": "rectangle 60, 200"
+ },
+ {
+ "hint": "Move 30 to the right - **type** `move 30`",
+ "solution": "move 30"
+ },
+ {
+ "hint": "Set the color to `'#aa0000'` - **type** `color '#aa0000'`",
+ "solution": "color '#aa0000'"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 30 and 200 - **type** `rectangle 30, 200`",
+ "solution": "rectangle 30, 200"
+ },
+ {
+ "hint": "Move down and left - **type** `move -3, 10`",
+ "solution": "move -3, 10"
+ },
+ {
+ "hint": "Set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 6 and 180 - **type** `rectangle 6, 180`",
+ "solution": "rectangle 6, 180"
+ },
+ {
+ "hint": "Move down and left - **type** `move -200, 150`",
+ "solution": "\n#Tomato 1\nmove -200, 150",
+ "validate": "move -200, 150"
+ },
+ {
+ "hint": "Set the color to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a circle with a size of 40",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Set the color to `'#cc4444'` - **type** `color '#cc4444'`",
+ "solution": "color '#cc4444'"
+ },
+ {
+ "hint": "Draw a circle with a size of 35",
+ "solution": "circle 35"
+ },
+ {
+ "hint": "Set the color to `'#aa4444'` - **type** `color '#aa4444'`",
+ "solution": "color '#aa4444'"
+ },
+ {
+ "hint": "Draw an ellipse with a size of 30 and 10",
+ "solution": "ellipse 30, 10"
+ },
+ {
+ "hint": "Draw an ellipse with a size of 10 and 30",
+ "solution": "ellipse 10, 30"
+ },
+ {
+ "hint": "Move down and right - **type** `move 70, 50`",
+ "solution": "\n#Tomato 2\nmove 70, 50",
+ "validate": "move 70, 50"
+ },
+ {
+ "hint": "Set the color to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a circle with a size of 40",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Set the color to `'#cc4444'` - **type** `color '#cc4444'`",
+ "solution": "color '#cc4444'"
+ },
+ {
+ "hint": "Draw a circle with a size of 35",
+ "solution": "circle 35"
+ },
+ {
+ "hint": "Set the color to `'#aa4444'` - **type** `color '#aa4444'`",
+ "solution": "color '#aa4444'"
+ },
+ {
+ "hint": "Draw an ellipse with a size of 30 and 10",
+ "solution": "ellipse 30, 10"
+ },
+ {
+ "hint": "Draw an ellipse with a size of 10 and 30",
+ "solution": "ellipse 10, 30"
+ }
+ ],
+ "completion_text": "Well done!",
+ "cover": "breakfast.png"
+}
\ No newline at end of file
diff --git a/lib/challenges/locales/ja/worlds/basic/index.json b/lib/challenges/locales/ja/worlds/basic/index.json
new file mode 100644
index 000000000..2369ad6ca
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/basic/index.json
@@ -0,0 +1,11 @@
+{
+ "challenges": [
+ "./sunnyday",
+ "./swissflag",
+ "./stare",
+ "./smiley",
+ "./baloon",
+ "./stickman"
+
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/basic/smiley.json b/lib/challenges/locales/ja/worlds/basic/smiley.json
new file mode 100644
index 000000000..b92e8d918
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/basic/smiley.json
@@ -0,0 +1,50 @@
+{
+ "id": "smiley",
+ "title": "初めての笑顔",
+ "description": "顔を描こう",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "鉛筆の色を黄色に変えよう。`color yellow`を**タイプ**",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "太くて黒い筆を選ぼう。`stroke black, 20`を**タイプ**",
+ "solution": "stroke black, 20"
+ },
+ {
+ "hint": "200の大きさの丸を描こう",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "左上を80歩ずつ動こう。`move -80, -80`を**タイプ**",
+ "solution": "move -80, -80"
+ },
+ {
+ "hint": "鉛筆の色を黒にしよう。",
+ "solution": "color black"
+ },
+ {
+ "hint": "半径20の丸を描こう。",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "160歩右へ動こう。`move 160`を**タイプ**",
+ "solution": "move 160"
+ },
+ {
+ "hint": "半径20の丸をもう一つ描こう。",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "中央と下まで動こう。次の行で`moveTo 250, 270`を**タイプ**",
+ "solution": "moveTo 250, 270"
+ },
+ {
+ "hint": "アーク(円弧)は丸の一部で、口に一つ描けば良さそう。`arc 100, 1, 2`を**タイプ**",
+ "solution": "arc 100, 1, 2"
+ }
+ ],
+ "completion_text": "よくやった!その調子で、笑顔描きジーニャスさん!",
+ "cover": "smiley.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/basic/stare.json b/lib/challenges/locales/ja/worlds/basic/stare.json
new file mode 100644
index 000000000..4f502b635
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/basic/stare.json
@@ -0,0 +1,55 @@
+{
+ "id": "stare",
+ "title": "暗やみでじろじろ",
+ "description": "暗やみでジロジロ見つめる目を描きましょう",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "背景を黒(black)にしましょう",
+ "solution": "background black"
+ },
+ {
+ "hint": "左に80歩動こう。`move -80`を**タイプ**",
+ "solution": "move -80",
+ "validate": "move -80"
+ },
+ {
+ "hint": "鉛筆の色を白(white)に変えよう",
+ "solution": "color white"
+ },
+ {
+ "hint": "楕円を描こう。伸ばした円形のように。`ellipse 60, 40`を**タイプ**",
+ "solution": "ellipse 60, 40"
+ },
+ {
+ "hint": "鉛筆の色を黒に変えよう。`color black`を**タイプ**",
+ "solution": "color black"
+ },
+ {
+ "hint": "大きさ10の丸を描こう。`circle 10`を**タイプ**",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "160歩右側に動こう。`move 160`を**タイプ**",
+ "solution": "move 160"
+ },
+ {
+ "hint": "また鉛筆の色を白に変えよう。",
+ "solution": "color white"
+ },
+ {
+ "hint": "もう一つの幅60で高さ40の楕円を描こう。`ellipse 60, 40`を**タイプ**",
+ "solution": "ellipse 60, 40"
+ },
+ {
+ "hint": "鉛筆の色を黒に変えよう。",
+ "solution": "color black"
+ },
+ {
+ "hint": "10の大きさの丸を描いて、できあがり!",
+ "solution": "circle 10"
+ }
+ ],
+ "completion_text": "本当の魔法使いだ!またジロジロ見つめる目を描きたかったら君に頼むよ!",
+ "cover": "stare.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/basic/stickman.json b/lib/challenges/locales/ja/worlds/basic/stickman.json
new file mode 100644
index 000000000..5e5f20c52
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/basic/stickman.json
@@ -0,0 +1,54 @@
+{
+ "id": "stickman",
+ "title": "棒人間",
+ "description": "円形と線で棒人間を描こう",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "筆(`stroke`)の色を黒(`black`)に、幅を`10`に設定しよう",
+ "solution": "stroke black, 10"
+ },
+ {
+ "hint": "カーソルを動かそう。`move 0, -50`を**タイプ**",
+ "solution": "move 0, -50"
+ },
+ {
+ "hint": "棒人間の体を描こう。`line 0, 150`",
+ "solution": "line 0, 150"
+ },
+ {
+ "hint": "棒人間の左腕を描こう。`line -80, 120`",
+ "solution": "line -80, 120"
+ },
+ {
+ "hint": "よっし、次は右腕を。`line 80, 120`",
+ "solution": "line 80, 120"
+ },
+ {
+ "hint": "下へ行こう。`move 0, 150`を**タイプ**",
+ "solution": "move 0, 150"
+ },
+ {
+ "hint": "棒人間の左足を描こう:`line -80, 120`",
+ "solution": "line -80, 120"
+ },
+ {
+ "hint": "よっし、次は右足を:`line 80, 120`",
+ "solution": "line 80, 120"
+ },
+ {
+ "hint": "絵の上に動こう。`moveTo 250, 100`を**タイプ**",
+ "solution": "moveTo 250, 100"
+ },
+ {
+ "hint": "筆の太さを0に戻そう。",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "最後に、多変形で頭を描こう。`polygon -60, 50, 0, 100, 60, 50`を**タイプ**",
+ "solution": "polygon -60, 50, 0, 100, 60, 50"
+ }
+ ],
+ "completion_text": "良くできました!絵の高さと幅は500ピクセルずつで、正しく動くことは難しい。",
+ "cover": "stickman.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/basic/sunnyday.json b/lib/challenges/locales/ja/worlds/basic/sunnyday.json
new file mode 100644
index 000000000..97ec66720
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/basic/sunnyday.json
@@ -0,0 +1,23 @@
+{
+ "id": "sunnyday",
+ "title": "晴れの日",
+ "description": "晴れの日をコードで表して、基礎を覚えよう。",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "まず、背景をきれいな空色で塗りましょう。- `background blue`を**タイプ**",
+ "solution": "background blue"
+ },
+ {
+ "hint": "次は、鉛筆の色を明るい黄色に変えよう。- 次の行に`color yellow`を**タイプ**",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "最後に、「circle」(円型)コマンドで、太陽を描こう。円の大きさを数字で表そう。- 次の行に`circle 150`を**タイプ**",
+ "solution": "circle 150"
+ }
+ ],
+ "completion_text": "おめでとう!さっぱりした晴れの日ができた。次に、円の大きさを変えて、どうなるか確認して見よう。",
+ "cover": "sunny-day.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/basic/swissflag.json b/lib/challenges/locales/ja/worlds/basic/swissflag.json
new file mode 100644
index 000000000..d440e5186
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/basic/swissflag.json
@@ -0,0 +1,39 @@
+{
+ "id": "swissflag",
+ "title": "スイスの国旗",
+ "description": "スイスの国旗をコードで作ろう",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "もう少し難しいチャレンジ。まず、壁紙を塗ろう:`background red`を**タイプ**",
+ "solution": "background red"
+ },
+ {
+ "hint": "これから太い線を描くので、筆の太さを変えよう。`stroke 66`を**タイプ**",
+ "solution": "stroke 66"
+ },
+ {
+ "hint": "次に筆の色を白に変えよう。3行目に`stroke white`を**タイプ**",
+ "solution": "stroke white"
+ },
+ {
+ "hint": "最初の線を描いてみよう。線の色は白で、幅は66ピクセルに設定済み。では、100ピクセルの線を描いてみよう:`line 100`を**タイプ**",
+ "solution": "line 100"
+ },
+ {
+ "hint": "短くて丈夫な線ができた!もう一本必要。次も100ピクセルの長さだけど、反対方向に。`line -100`を**タイプ**",
+ "solution": "line -100"
+ },
+ {
+ "hint": "いいね!次は上下の線も描かなきゃ。どうしたらいいかね?実は「line」(線)に2つの数字を入れられる。一つ目は左右の位置で、2つ目は上下の位置。`line 0, 100`を**タイプ**",
+ "solution": "line 0, 100"
+ },
+ {
+ "hint": "線は下に向いてるね!次は上へ行くには`line 0, -100`を**タイプ**",
+ "solution": "line 0, -100"
+ }
+ ],
+ "completion_text": "素晴らしい国旗ができた!シンプルなデザインはスイスの特徴だ。",
+ "cover": "swissflag.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/medium/dots.json b/lib/challenges/locales/ja/worlds/medium/dots.json
new file mode 100644
index 000000000..aa4798975
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/medium/dots.json
@@ -0,0 +1,40 @@
+{
+ "id": "dots",
+ "title": "水玉模様",
+ "description": "ループと丸を使って、可愛い水玉模様を描こう。",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "背景を赤(`red`)にしよう。",
+ "solution": "background red"
+ },
+ {
+ "hint": "筆の幅を0にしよう。",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "色を黄色(`yellow`)にしよう。",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "次はループを始めよう。`for x in [ 0 .. 25 ]`を**タイプ**",
+ "solution": "for x in [ 0..25 ]"
+ },
+ {
+ "hint": "ループの中にはもう一つのループを。`for y in [ 0 .. 25 ]`を**タイプ**",
+ "solution": " for y in [ 0..25 ]"
+ },
+ {
+ "hint": "縦と横へ動こう。`moveTo x * 20, y * 20`を**タイプ**",
+ "solution": " moveTo x * 20, y * 20",
+ "validate": " moveTo x *\\* *20,y *\\* *20"
+ },
+ {
+ "hint": "そして水玉を描こう。`circle 6`を**タイプ**",
+ "solution": " circle 6"
+ }
+ ],
+ "completion_text": "できた!数字を変えて、どうなるか見てみよう。",
+ "cover": "dots.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/medium/gradient.json b/lib/challenges/locales/ja/worlds/medium/gradient.json
new file mode 100644
index 000000000..d9e1b60ff
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/medium/gradient.json
@@ -0,0 +1,33 @@
+{
+ "id": "gradient",
+ "title": "虹のパレット",
+ "description": "ループと色を組み合わせ、魔法の絵を!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "まず、forループを始めよう。`for x in [ 0 .. 25 ]`を**タイプ**",
+ "solution": "for x in [ 0..25 ]"
+ },
+ {
+ "hint": "次は2つ目のforループを始めよう。`for y in [ 0 .. 25 ]`を**タイプ**",
+ "solution": " for y in [ 0..25 ]"
+ },
+ {
+ "hint": "色のトーンを回転させよう。`color rotate red, 10 * x + 10 * y`を**タイプ**",
+ "solution": " color rotate red, 10 * x + 10 * y",
+ "validate": " color rotate +red,10 *\\* *x *\\+ *10 *\\* *y"
+ },
+ {
+ "hint": "そして描くときに毎回動かなきゃ。`moveTo 20 * x, 20 * y`を**タイプ**",
+ "solution": " moveTo 20 * x, 20 * y",
+ "validate": " moveTo 20 *\\* *x,20 *\\* *y"
+ },
+ {
+ "hint": "最後に大きさ20の四角を描こう。",
+ "solution": " square 20"
+ }
+ ],
+ "completion_text": "回転(rotate)で数字を変えると、どうなるんだろう?",
+ "cover": "gradient.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/medium/house.json b/lib/challenges/locales/ja/worlds/medium/house.json
new file mode 100644
index 000000000..3a786977e
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/medium/house.json
@@ -0,0 +1,87 @@
+{
+ "id": "house",
+ "title": "家",
+ "description": "居心地のよい家を描こう!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "まず空を描こう!背景を青(`blue`)に設定しよう。",
+ "solution": "background blue"
+ },
+ {
+ "hint": "筆の幅を0にしよう。",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "左上へ動こう。`move -150, -50`を**タイプ**",
+ "solution": "move -150, -50"
+ },
+ {
+ "hint": "色をベージュ(`beige`)にしよう。",
+ "solution": "color beige"
+ },
+ {
+ "hint": "次は長方形を描こう。`rectangle 300, 200`を**タイプ**",
+ "solution": "rectangle 300, 200"
+ },
+ {
+ "hint": "右上へ動こう。`move 150, -100`を**タイプ**",
+ "solution": "move 150, -100"
+ },
+ {
+ "hint": "屋根には色を暗赤色(`darkred`)にしよう。",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "次は屋根を描こう。`polygon 170, 100, -170, 100`を**タイプ**",
+ "solution": "polygon 170, 100, -170, 100"
+ },
+ {
+ "hint": "左下へ動こう。`move -250, 300`を**タイプ**",
+ "solution": "move -250, 300"
+ },
+ {
+ "hint": "芝生を描くには色を緑(`green`)に設定しよう。",
+ "solution": "color green"
+ },
+ {
+ "hint": "次は芝生を500, 100の長方形(`rectangle`)で描こう。",
+ "solution": "rectangle 500, 100"
+ },
+ {
+ "hint": "右上へ動こう。`move 220, -80`を**タイプ**",
+ "solution": "move 220, -80"
+ },
+ {
+ "hint": "木のドアの色を茶色(`brown`)に設定しよう。",
+ "solution": "color brown"
+ },
+ {
+ "hint": "ドアを60, 80の長方形で描こう。",
+ "solution": "rectangle 60, 80"
+ },
+ {
+ "hint": "窓の色を水色(`aqua`)に設定しよう。",
+ "solution": "color aqua"
+ },
+ {
+ "hint": "左上へ動こう。`move -80, -80`を**タイプ**",
+ "solution": "move -80, -80"
+ },
+ {
+ "hint": "50の四角を描こう。",
+ "solution": "square 50"
+ },
+ {
+ "hint": "右へ行こう。`move 170`を**タイプ**",
+ "solution": "move 170"
+ },
+ {
+ "hint": "50の四角を描こう。",
+ "solution": "square 50"
+ }
+ ],
+ "completion_text": "すごい家をコードで描いた!",
+ "cover": "house.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/medium/index.json b/lib/challenges/locales/ja/worlds/medium/index.json
new file mode 100644
index 000000000..6d4e6623b
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/medium/index.json
@@ -0,0 +1,13 @@
+{
+ "challenges": [
+ "./shrinking",
+ "./random",
+ "./starry",
+ "./house",
+ "./dots",
+ "./gradient",
+ "./planet",
+ "./pizza"
+ ]
+}
+
diff --git a/lib/challenges/locales/ja/worlds/medium/pizza.json b/lib/challenges/locales/ja/worlds/medium/pizza.json
new file mode 100644
index 000000000..592049006
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/medium/pizza.json
@@ -0,0 +1,50 @@
+{
+ "id": "pizza",
+ "title": "ピザ",
+ "description": "おいしいピザをコードで作ろう!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "筆の色をベージュ(`beige`)にしよう。",
+ "solution": "stroke beige"
+ },
+ {
+ "hint": "筆の幅を50に。",
+ "solution": "stroke 50"
+ },
+ {
+ "hint": "色を赤(`red`)にしよう。",
+ "solution": "color red"
+ },
+ {
+ "hint": "半径200の円形を描こう。",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "美味しそう!筆の幅を0に戻そう。",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "ソースに黄色で大きさ195の円形を描こう。",
+ "solution": "color yellow\ncircle 195",
+ "validate": [
+ "color yellow",
+ "circle 195"
+ ]
+ },
+ {
+ "hint": "そしてトッピング。色を暗赤色(`darkred`)にしよう。",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "そして`moveTo 164, 290`",
+ "solution": "moveTo 164, 290"
+ },
+ {
+ "hint": "よっし、最後は半径20の円形を描こう。",
+ "solution": "circle 20"
+ }
+ ],
+ "completion_text": "美味しそう!想像を働かせて、他のトッピングを考えてみてから、ピザを友達と共有しよう。",
+ "cover": "pizza.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/medium/planet.json b/lib/challenges/locales/ja/worlds/medium/planet.json
new file mode 100644
index 000000000..4a4f343ca
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/medium/planet.json
@@ -0,0 +1,60 @@
+{
+ "id": "planet",
+ "title": "惑星画家",
+ "description": "自分だけの惑星をコードで描こう!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "背景をヘキサのコードで設定しよう。`background '#444444'`を**タイプ**",
+ "solution": "background '#444444'",
+ "validate": "background '\\#444444'"
+ },
+ {
+ "hint": "線を書かないように、筆の幅を0にしよう。",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "色は黄色(`yellow`)にしよう。",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "次はループを始めよう。`for i in [ 0 .. 32 ]`を**タイプ**",
+ "solution": "for i in [ 0..32 ]"
+ },
+ {
+ "hint": "そして、それぞれの星の位置を設定しよう。`moveTo (random 1, 500), (random 1, 500)`を**タイプ**",
+ "solution": " moveTo (random 1, 500), (random 1, 500)",
+ "validate": " moveTo ( *random +1,500 *),( *random +1,500 *)"
+ },
+ {
+ "hint": "星は、半径5の丸で描こう。",
+ "solution": " circle 5"
+ },
+ {
+ "hint": "次は、ループの外(インデントなし)で、色を紫にしよう。`color purple`を**タイプ**",
+ "solution": "color purple"
+ },
+ {
+ "hint": "次は`moveTo 250, 250`",
+ "solution": "moveTo 250, 250"
+ },
+ {
+ "hint": "半径170の円形を描こう。",
+ "solution": "circle 170"
+ },
+ {
+ "hint": "色を白(`white`)に。",
+ "solution": "color white"
+ },
+ {
+ "hint": "筆を太めで白に。`stroke white, 5`を**タイプ**",
+ "solution": "stroke white, 5"
+ },
+ {
+ "hint": "最後に楕円形を描こう。`ellipse 220, 4`を**タイプ**",
+ "solution": "ellipse 220, 4"
+ }
+ ],
+ "completion_text": "いやー、まるで宇宙だ!共有する前に、色を変えて、惑星をもっと面白くしてみよう!",
+ "cover": "planet.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/medium/random.json b/lib/challenges/locales/ja/worlds/medium/random.json
new file mode 100644
index 000000000..bc2dd60e7
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/medium/random.json
@@ -0,0 +1,46 @@
+{
+ "id": "random",
+ "title": "でたらめ",
+ "description": "物をどこに置けば良いか迷っていたら、そのための関数はあるよ!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "カーソルを適当(不特定)な位置に動かそう。`moveTo (random 1, 500), (random 1, 500)`を**タイプ**",
+ "solution": "moveTo (random 1, 500), (random 1, 500)"
+ },
+ {
+ "hint": "色を赤(red)にしよう。`color red`を**タイプ**",
+ "solution": "color red"
+ },
+ {
+ "hint": "次は円形をこの適当な位置で描こう。`circle 200`を**タイプ**",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "色を緑(green)にしよう。",
+ "solution": "color green"
+ },
+ {
+ "hint": "そして半径150の円形を描こう。",
+ "solution": "circle 150"
+ },
+ {
+ "hint": "色を黄色(yellow)にしよう。",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "半径100の円形を描こう。",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "色を青(blue)にしよう。",
+ "solution": "color blue"
+ },
+ {
+ "hint": "最後に半径50の円形を描こう。",
+ "solution": "circle 50"
+ }
+ ],
+ "completion_text": "randomで形をデタラメな場所で描けるんだ。面白いね。",
+ "cover": "random.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/medium/shrinking.json b/lib/challenges/locales/ja/worlds/medium/shrinking.json
new file mode 100644
index 000000000..e22bd1e26
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/medium/shrinking.json
@@ -0,0 +1,28 @@
+{
+ "id": "shrinking",
+ "title": "縮む円形",
+ "description": "forループで無限に縮む円形!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "鉛筆を設定しよう。`stroke gray, 3`を**タイプ**",
+ "solution": "stroke gray, 3"
+ },
+ {
+ "hint": "透明な色を設定しよう。`color null`を**タイプ**",
+ "solution": "color null"
+ },
+ {
+ "hint": "32個の円形を一気に描こう。`for i in [ 0 .. 32 ]`を**タイプ**",
+ "solution": "for i in [ 0..32 ]"
+ },
+ {
+ "hint": "完璧、そしてforループの中で円形を描こう。`circle 10 * i`を**タイプ**",
+ "solution": " circle 10 * i",
+ "validate": " circle 10 *\\* *i"
+ }
+ ],
+ "completion_text": "素晴らしい!forループでコードを繰り返すことができるんだ(一つ一つタイプしなくて良い)!",
+ "cover": "shrinking.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/medium/starry.json b/lib/challenges/locales/ja/worlds/medium/starry.json
new file mode 100644
index 000000000..1e43bec4a
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/medium/starry.json
@@ -0,0 +1,35 @@
+{
+ "id": "starry",
+ "title": "星空",
+ "description": "random関数で自分だけの星空をコードで描こう!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "背景を紺色(darkblue)にしよう。",
+ "solution": "background darkblue",
+ "validate": "^background darkblue$"
+ },
+ {
+ "hint": "次は鉛筆の幅を0にしよう。",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "そして鉛筆の色を黄色(yellow)に。",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "ループを始めよう。`for i in [ 0 .. 32 ]`を**タイプ**",
+ "solution": "for i in [ 0..32 ]"
+ },
+ {
+ "hint": "それぞれの星の位置をランダムに設定しよう。`moveTo (random 1, 500), (random 1, 500)`を**タイプ**",
+ "solution": " moveTo (random 1, 500), (random 1, 500)"
+ },
+ {
+ "hint": "最後に星をそれぞれ5ピクセルの円形で描こう。",
+ "solution": " circle 5"
+ }
+ ],
+ "completion_text": "素晴らしい!スペースキーを押して、星を見てみよう…",
+ "cover": "starry-sky.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/mischiefweek2015/cat.json b/lib/challenges/locales/ja/worlds/mischiefweek2015/cat.json
new file mode 100644
index 000000000..b7838444b
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/mischiefweek2015/cat.json
@@ -0,0 +1,139 @@
+{
+ "id": "cat",
+ "title": "Spooky Cat",
+ "cover": "mischiefweek2015/mw-002-cat.png",
+ "description": "Make a spooky cat with code",
+ "start_date": "2015-10-27T06:00:00",
+ "startAt": 2,
+ "completion_text": "Nice work - you made a spooky black cat! Try changing its color or even its tail! Don't forget to share your cat afterwards.",
+ "steps": [
+ {
+ "hint": "We can make the background a spooky black by **typing** `background black`.",
+ "solution": "background black"
+ },
+ {
+ "hint": "Set the stroke to zero so we have no outlines. **Type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Now let's set the color of the full moon. **Type** `color lightgray`",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Create a nice big circle for the moon. **Type** `circle 230`.",
+ "solution": "circle 230"
+ },
+ {
+ "hint": "Now move downwards so we can draw the grass on the ground. **Type** `moveTo 250, 500`.",
+ "solution": "moveTo 250, 500"
+ },
+ {
+ "hint": "It's night time, so we'll make the grass a dark green. **Type** `color darkgreen`.",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Draw the grassy hill with an ellipse so it's round. **Type** `ellipse 300, 100`",
+ "solution": "ellipse 300, 100"
+ },
+ {
+ "hint": "Now set the color of the cat! To make it black, **type** `color black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Move the cursor to draw the cat in the middle! **Type** `moveTo 250, 250`.",
+ "solution": "moveTo 250, 250"
+ },
+ {
+ "hint": "Let's draw the head with an ellipse. **Type** `ellipse 60, 40`.",
+ "solution": "ellipse 60, 40"
+ },
+ {
+ "hint": "Let's move to right and up to draw the first ear. **Type** `moveTo 280, 220`.",
+ "solution": "moveTo 280, 220"
+ },
+ {
+ "hint": "We'll use the polygon function to make a nice pointy ear! **Type** `polygon 0, -40, 28, 20, close`.",
+ "solution": "polygon 0, -40, 28, 20, close"
+ },
+ {
+ "hint": "Move to the left by 60 so we can draw the other ear! **Type** `move -60`.",
+ "solution": "move -60"
+ },
+ {
+ "hint": "This ear is a reflection of the other one! **Type** `polygon 0, -40, -28, 20, close`.",
+ "solution": "polygon 0, -40, -28, 20, close"
+ },
+ {
+ "hint": "Let's make the neck underneath. **Type** `moveTo 250, 310`.",
+ "solution": "moveTo 250, 310"
+ },
+ {
+ "hint": "Draw it using an ellipse! **Type** `ellipse 30, 50`.",
+ "solution": "ellipse 30, 50"
+ },
+ {
+ "hint": "Now lets make the body below. **Type** `moveTo 250, 360`.",
+ "solution": "moveTo 250, 360"
+ },
+ {
+ "hint": "We'll use a bigger ellipse this time round! **type** `ellipse 50, 60`.",
+ "solution": "ellipse 50, 60"
+ },
+ {
+ "hint": "Now we need to move down and right to make the tail! **Type** `moveTo 320, 380`.",
+ "solution": "moveTo 320, 380"
+ },
+ {
+ "hint": "Let's use a sneaky text trick to make a curvy tail. **Type** `font 150`.",
+ "solution": "font 150"
+ },
+ {
+ "hint": "We'll make the text bold to make a large. **Type** `bold true`.",
+ "solution": "bold true"
+ },
+ {
+ "hint": "Great! Now draw the tail by **typing** `text 'S'`.",
+ "solution": "text 'S'"
+ },
+ {
+ "hint": "Finally, we'll make the eyes. **Type** `moveTo 225, 250`.",
+ "solution": "moveTo 225, 250"
+ },
+ {
+ "hint": "Set the color to yellow. **Type** `color yellow`.",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "We'll draw it using an ellipse. **Type** `ellipse 14, 6`.",
+ "solution": "ellipse 14, 6"
+ },
+ {
+ "hint": "Now for the pupil! Set the color to black. **Type** `color black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Make a circle of radius 6. **Type** `circle 6`.",
+ "solution": "circle 6"
+ },
+ {
+ "hint": "Great! Now lets move right to do the other eye. **Type** `moveTo 275, 250`.",
+ "solution": "moveTo 275, 250"
+ },
+ {
+ "hint": "Set the color back to yellow. **Type** `color yellow`.",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Make the same ellipse as before. **Type** `ellipse 14,6`.",
+ "solution": "ellipse 14, 6"
+ },
+ {
+ "hint": "Set the color back to black. **type** `color black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Lastly create the second pupil. **Type** `circle 6`.",
+ "solution": "circle 6"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/mischiefweek2015/ghost.json b/lib/challenges/locales/ja/worlds/mischiefweek2015/ghost.json
new file mode 100644
index 000000000..456d04e4f
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/mischiefweek2015/ghost.json
@@ -0,0 +1,136 @@
+{
+ "id": "ghost",
+ "title": "Ghost",
+ "description": "Draw your very own ghost. Change the colors to win!",
+ "start_date": "2015-10-30T06:00:00",
+ "code": "ghostColor = '#809B79'\nfaceColor = '#634E42'\ntongueColor = '#7A5750'\n",
+ "startAt": 0,
+ "steps": [
+ {
+ "hint": "We need to set up some variables to color the ghost - **Type** `ghostColor = '#809B79'`",
+ "solution": "ghostColor = '#809B79'"
+ },
+ {
+ "hint": "Another variable for the face - **Type** `faceColor = '#634E42'`",
+ "solution": "faceColor = '#634E42'"
+ },
+ {
+ "hint": "Finally a variable for the tongue - **Type** `tongueColor = '#7A5750'`",
+ "solution": "tongueColor = '#7A5750'"
+ },
+ {
+ "hint": "We’ll use solid shapes, so set the stroke to zero with `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Our ghost leaves a trail behind that we need to draw first, so we'll set the draw color to something slightly darker with `color darken(ghostColor, 10)`",
+ "solution": "color darken(ghostColor, 10)"
+ },
+ {
+ "hint": "Move into position for the trail with `move 0, 60`",
+ "solution": "move 0, 60"
+ },
+ {
+ "hint": "And then draw the trail with `circle 70`",
+ "solution": "circle 70"
+ },
+ {
+ "hint": "Now we’ll draw the ghost’s body. Set the ghost’s color to our variable with `color ghostColor`",
+ "solution": "color ghostColor"
+ },
+ {
+ "hint": "Now move back to draw the body, move back up to the center with `move 0, -60`",
+ "solution": "move 0, -60"
+ },
+ {
+ "hint": "Draw the body with `circle 100`",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Now we’ll draw the mouth with the faceColor variable we defined. **Type** `color faceColor`.",
+ "solution": "color faceColor"
+ },
+ {
+ "hint": "Now move into position for the mouth with `move -40, 10`",
+ "solution": "move -40, 10"
+ },
+ {
+ "hint": "Draw the mouth with `rectangle 80, 35`",
+ "solution": "rectangle 80, 35"
+ },
+ {
+ "hint": "The teeth are a polygon. **Hint:** you can copy and paste this: `polygon 10, -10, 20, 0, 30, -10, 40, 0, 50, -10, 60, 0, 70, -10, 80, 0`",
+ "solution": "polygon 10, -10, 20, 0, 30, -10, 40, 0, 50, -10, 60, 0, 70, -10, 80, 0"
+ },
+ {
+ "hint": "Now for the tongue, move to the bottom of the mouth with `move 40, 35`",
+ "solution": "move 40, 35"
+ },
+ {
+ "hint": "Set the drawing color to our variable with `color tongueColor`",
+ "solution": "color tongueColor"
+ },
+ {
+ "hint": "The tongue is a half-circle of radius 25, which we can draw with `arc 25, 0, 1`",
+ "solution": "arc 25, 0, 1"
+ },
+ {
+ "hint": "For the eyes, set the drawing color with `color faceColor`",
+ "solution": "color faceColor"
+ },
+ {
+ "hint": "Move into position for the eyes with `move -40, -80`",
+ "solution": "move -40, -80"
+ },
+ {
+ "hint": "The eye is then drawn with `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Move for the second eye with `move 80, 0`",
+ "solution": "move 80, 0"
+ },
+ {
+ "hint": "Draw the second eye with `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "For our hands, were going to do something new: make a function. **Type** `hand = ->`",
+ "solution": "hand = ->"
+ },
+ {
+ "hint": "This indented block is where your function goes. Any time you call this function this entire block of code will run. Lets set the stroke for our hand with `stroke 10, darken(ghostColor, 10)`",
+ "solution": " stroke 10, darken(ghostColor, 10)"
+ },
+ {
+ "hint": "Then draw the first of the three fingers with `line 20, 30`",
+ "solution": " line 20, 30"
+ },
+ {
+ "hint": "Then the second with `line 0, 35`",
+ "solution": " line 0, 35"
+ },
+ {
+ "hint": "The final finger with `line -20, 30`",
+ "solution": " line -20, 30"
+ },
+ {
+ "hint": "Now to draw the hands: make sure you are out of the function block by pressing **BACKSPACE**. Then **type** `move 60, 100` for the first hand.",
+ "solution": "move 60, 100"
+ },
+ {
+ "hint": "Draw the hand at the current location with `hand()`",
+ "solution": "hand()"
+ },
+ {
+ "hint": "Move over to the left to draw the other hand with `move -200, 0`",
+ "solution": "move -200, 0"
+ },
+ {
+ "hint": "Draw the final hand with `hand()`",
+ "solution": "hand()"
+ }
+ ],
+ "completion_text": "Well done! You can change the color of your ghost by setting ghostColor, faceColor, and tongueColor to whatever you want.",
+ "cover": "mischiefweek2015/mw-005-ghost.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/mischiefweek2015/hauntedhouse.json b/lib/challenges/locales/ja/worlds/mischiefweek2015/hauntedhouse.json
new file mode 100644
index 000000000..cca16f838
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/mischiefweek2015/hauntedhouse.json
@@ -0,0 +1,11 @@
+{
+ "id": "hauntedhouse",
+ "title": "Haunted House",
+ "description": "Dress the house up for Halloween and express your creativity.",
+ "start_date": "2015-11-01T06:00:00",
+ "completion_text": "It's Halloween Night! Change the colours at the top of the code to make this into a night scene then express your creativity by combining previous challenges with it or causing mischief, some ideas to get you started are in the comments.",
+ "startAt": 0,
+ "code": "#Colors, that need to be made spookier\nsky = aqua\nground = green\nsun = yellow\nbricks = brown\nroof = red\nframes = gray\nwindows = blue\ndoor = setBrightness(brown,-40)\n\n#Sky\nbackground sky\nstroke 0\nmoveTo 100, 100\ncolor sun\ncircle 60\n#Why not add some spooky clouds or bats\n\n#Ground\nmoveTo 250,530\ncolor ground\nellipse 500,150\n\n#House\nmoveTo 100,180\ncolor bricks\nrectangle 300,270\n\n#Windows\n#Cause some mischief, can you figure out how to throw eggs on all the windows in just 3 lines of code\ndrawWindow = (x,y) ->\n color setBrightness(frames,30)\n moveTo x,y\n rectangle 60,80\n color windows\n moveTo x+5,y+5\n rectangle 50,70\n color setBrightness(frames,30)\n moveTo x, y+37.5\n rectangle 60,5\n moveTo x+27.5, y\n rectangle 5,80\ndrawWindow(120,200)\ndrawWindow(220,200)\ndrawWindow(120,310)\ndrawWindow(320,200)\ndrawWindow(320,310)\n\n#Roof\ncolor roof\nmoveTo 100, 180\npolygon 150, -100, 300, 0\n#You could use the arc function to cover the roof in toilet paper \n\n\n#Door\ncolor setBrightness(frames,-10)\nmoveTo 215, 330\nrectangle 70,110\nmove 5,5\ncolor door\nrectangle 60,105\ncolor setBrightness(frames,-40) \nmove -15,105\nrectangle 90,20\nmove 65,-50\ncolor setBrightness(door,80)\ncircle 3\n#How about putting a pumpkin beside the door",
+ "steps": [],
+ "cover": "mischiefweek2015/mw-007-hauntedhouse.png"
+}
diff --git a/lib/challenges/locales/ja/worlds/mischiefweek2015/index.json b/lib/challenges/locales/ja/worlds/mischiefweek2015/index.json
new file mode 100644
index 000000000..2bbe6689e
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/mischiefweek2015/index.json
@@ -0,0 +1,11 @@
+{
+ "challenges": [
+ "./skull",
+ "./cat",
+ "./pumpkin",
+ "./potion",
+ "./ghost",
+ "./spiderweb",
+ "./hauntedhouse"
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/mischiefweek2015/potion.json b/lib/challenges/locales/ja/worlds/mischiefweek2015/potion.json
new file mode 100644
index 000000000..cfb784232
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/mischiefweek2015/potion.json
@@ -0,0 +1,96 @@
+{
+ "id": "potion",
+ "title": "Potion Flask",
+ "description": "Brew a potion with code.",
+ "start_date": "2015-10-29T06:00:00",
+ "code": "",
+ "startAt": 2,
+ "completion_text": "Well done! You can change the color of your potion by setting potionColor to whatever you want. The clear glass you made with opacity(white, .4) will give it a cool effect, whatever color you choose.",
+ "cover": "mischiefweek2015/mw-004-potion.png",
+ "steps": [
+ {
+ "hint": "We want solid shapes for this challenge, so **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "For a dark look, lets set `background charcoal`",
+ "solution": "background charcoal"
+ },
+ {
+ "hint": "If we want to change our potion color we can set it to a variable. To do that **type** `potionColor = purple`",
+ "solution": "potionColor = purple"
+ },
+ {
+ "hint": "Let’s move into position with `move 0, 50`",
+ "solution": "move 0, 50"
+ },
+ {
+ "hint": "And set the drawing color with `color potionColor`",
+ "solution": "color potionColor"
+ },
+ {
+ "hint": "Finally, we can draw the potion with `circle 90`",
+ "solution": "circle 90"
+ },
+ {
+ "hint": "Our potion is going to go up the neck of the flask, and we will use a line. Set up the line style with `stroke potionColor, 40`",
+ "solution": "stroke potionColor, 40"
+ },
+ {
+ "hint": "And draw the line up from the middle with `line 0, -120`.",
+ "solution": "line 0, -120"
+ },
+ {
+ "hint": "Set the stroke back to zero for the flask’s glass. **Type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Let’s add in another circle to give our potion a natural look. Move to the right and up with `move 30, -30`.",
+ "solution": "move 30, -30"
+ },
+ {
+ "hint": "Set the drawing color to a shade slightly darker than the potion color with `color darken(potionColor, 10)`",
+ "solution": "color darken(potionColor, 10)"
+ },
+ {
+ "hint": "Finally, draw the circle with `circle 20`",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Move back to the center with `move -30, 30`",
+ "solution": "move -30, 30"
+ },
+ {
+ "hint": "Set the color for the glass flask with `color opacity(white, .4)`",
+ "solution": "color opacity(white, .4)"
+ },
+ {
+ "hint": "Then draw the flask with an arc. **Type** `arc 100, 1.4, 1.6`",
+ "solution": "arc 100, 1.4, 1.6"
+ },
+ {
+ "hint": "See how the arc draws almost a complete circle, but leaves a nice flat top for us? Our flask neck will meet it there, but first we need to draw a cork behind the glass. **Type** `move 0, -130`",
+ "solution": "move 0, -130"
+ },
+ {
+ "hint": "Set the drawing color for the cork with `color tan`",
+ "solution": "color tan"
+ },
+ {
+ "hint": "And draw the cork with `polygon 20, 0, 30, -40, -30, -40, -20, 0`",
+ "solution": "polygon 20, 0, 30, -40, -30, -40, -20, 0"
+ },
+ {
+ "hint": "Now for the neck, lets choose the see-through white color for our stroke. **Type** `stroke 60, opacity(white, .4)`",
+ "solution": "stroke 60, opacity(white, .4)"
+ },
+ {
+ "hint": "Move up just a little bit for the neck with `move 0, -25`",
+ "solution": "move 0, -25"
+ },
+ {
+ "hint": "Finally, draw the neck with `line 0, 60`",
+ "solution": "line 0, 60"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/mischiefweek2015/pumpkin.json b/lib/challenges/locales/ja/worlds/mischiefweek2015/pumpkin.json
new file mode 100644
index 000000000..16e88cd86
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/mischiefweek2015/pumpkin.json
@@ -0,0 +1,87 @@
+{
+ "id": "pumpkin",
+ "title": "Kan-o-lantern",
+ "cover": "mischiefweek2015/mw-003-pumpkin.png",
+ "description": "It wouldn't be Halloween without pumpkins, learn how to carve a spooky pumpkin then get creative by changing the face.",
+ "start_date": "2015-10-28T06:00:00",
+ "completion_text": "That's a nice Kan-o-lantern! But it needs to be lit to be truly spooky, can you figure out how to light the pumpkin by changing some colors, you could also try carving a spookier looking face. Show off your creativity then share your creation.",
+ "startAt": 0,
+ "steps": [
+ {
+ "hint": "Let’s set a spooky scene, make it night time - **type** `background charcoal`",
+ "solution": "background charcoal"
+ },
+ {
+ "hint": "The most distinctive thing about pumpkins is their bright orange skin, to set your fill color - **type** `color orange`",
+ "solution": "color orange"
+ },
+ {
+ "hint": "We're going to use strokes to build the bulbous shape of the pumpkin, use the darken function to build contrast against the skin - **type** `stroke darken(orange, 20), 30`",
+ "solution": "stroke darken(orange, 20), 30"
+ },
+ {
+ "hint": "Build your pumpkin slice by slice using ellipses - **type** `ellipse 180, 140`",
+ "solution": "ellipse 180, 140"
+ },
+ {
+ "hint": "Add another slice - **type** `ellipse 130, 140`",
+ "solution": "ellipse 130, 140"
+ },
+ {
+ "hint": "It's starting to take shape, add the final slice the same way - **type** `ellipse 50, 140`",
+ "solution": "ellipse 50, 140"
+ },
+ {
+ "hint": "Pumpkins are actually fruits, so we need to draw a green stem - **type** `color green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Turn off the orange stroke - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to the top of the pumpkin - **type** `move -20, -180`",
+ "solution": "move -20, -180"
+ },
+ {
+ "hint": "Draw the chunky stem of the pumpkin - **type** `square 40`",
+ "solution": "square 40"
+ },
+ {
+ "hint": "Now we have a fresh pumpkin, we can get to the fun part, carving your design - **type** `color black`",
+ "solution": "color black"
+ },
+ {
+ "hint": "We're going to give them a face, move the cursor to where the eye will be - **type** `moveTo 190, 275`",
+ "solution": "moveTo 190, 275"
+ },
+ {
+ "hint": "Cut a hole for the first eye - **type** `circle 15`",
+ "solution": "circle 15"
+ },
+ {
+ "hint": "Move the cursor to where the second eye will be - **type** `moveTo 310, 275`",
+ "solution": "moveTo 310, 275"
+ },
+ {
+ "hint": "Cut the second hole the same size as the first - **type** `circle 15`",
+ "solution": "circle 15"
+ },
+ {
+ "hint": "Next we need to give them a mouth - **type** `moveTo 250, 320`",
+ "solution": "moveTo 250, 320"
+ },
+ {
+ "hint": "We need to turn the fill colour off by setting it to null - **type** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "We'll use a thick black stroke for the mouth - **type** `stroke 15, black`",
+ "solution": "stroke 15, black"
+ },
+ {
+ "hint": "Finally we will use the arc command to give them a smile. - **type** `arc 40, 1, 2, true`",
+ "solution": "arc 40, 1, 2, true"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/mischiefweek2015/skull.json b/lib/challenges/locales/ja/worlds/mischiefweek2015/skull.json
new file mode 100644
index 000000000..ba27a9392
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/mischiefweek2015/skull.json
@@ -0,0 +1,75 @@
+{
+ "id": "skull",
+ "title": "Skull",
+ "cover": "mischiefweek2015/mw-001-skull.png",
+ "start_date": "2015-10-26T06:00:00",
+ "description": "",
+ "completion_text": "Amazing skull! You can dress it up with some background patterns or other facial accessories. When you are done, share your creation for a chance to win it on a t-shirt!",
+ "startAt": 0,
+ "steps": [
+ {
+ "hint": "First we need to set the spooky scene, make it night time - **type** `background charcoal`",
+ "solution": "background charcoal"
+ },
+ {
+ "hint": "We want to draw solid shapes with no stroke, so **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "We can move into position for the skull with `moveTo 250, 200`",
+ "solution": "moveTo 250, 200"
+ },
+ {
+ "hint": "Our skull should be white, so set the drawing color with `color white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "The skull is made with an ellipse—a kind of squashed circle. Make this with `ellipse 140, 120`",
+ "solution": "ellipse 140, 120"
+ },
+ {
+ "hint": "It's starting to take shape, add the final slice the same way - **type** `move -60, 90`",
+ "solution": "move -60, 90"
+ },
+ {
+ "hint": "The mouth of the skull is a square we will draw with `square 120`",
+ "solution": "square 120"
+ },
+ {
+ "hint": "Lets move up and over for the eyes - **type** `move 10, -50`",
+ "solution": "move 10, -50"
+ },
+ {
+ "hint": "Our skull’s empty eyes peer into your soul. Set their color to charcoal with `color charcoal`",
+ "solution": "color charcoal"
+ },
+ {
+ "hint": "The eye is a circle. Draw it with `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Move on over for the other eye. **Type** `move 100`",
+ "solution": "move 100"
+ },
+ {
+ "hint": "Now draw the other eye with `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Move on over for the nose with `move -50, 60`",
+ "solution": "move -50, 60"
+ },
+ {
+ "hint": "Then draw the nose with `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Finally, for the mouth move down by **typing** `move -40, 60`",
+ "solution": "move -40, 60"
+ },
+ {
+ "hint": "Finally, you draw the mouth with `rectangle 80, 20`",
+ "solution": "rectangle 80, 20"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/mischiefweek2015/spiderweb.json b/lib/challenges/locales/ja/worlds/mischiefweek2015/spiderweb.json
new file mode 100644
index 000000000..7d3befdb4
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/mischiefweek2015/spiderweb.json
@@ -0,0 +1,12 @@
+{
+ "id": "spiderweb",
+ "title": "Spider Web",
+ "description": "Play around with a spider web!",
+ "completion_text": "Happy Halloween! Today’s challenge is for you to play around with the code here. Can you make a different-colored web? Or a bigger spider? Thanks to community member @NOPx90 who coded the spiderweb!",
+ "cover": "mischiefweek2015/mw-006-spiderweb.png",
+ "start_date": "2015-10-31T06:00:00",
+ "startAt": 0,
+ "code": "# PLAY WITH THESE\nradius = 370\nframes = 30\nbridges = 10\ndegrees = 360\nrotation = 0\n\nbodySize = 80\nlegSpread = 90\nlegLength = 100\neyeSize = 4\neyeSpacing = 12\n\n\n# This defines the spiderweb\nclass SpiderWeb\n constructor: (@x, @y, @radius, @frames, @bridges, @degrees, @rotation) ->\n @x = @x ? 250\n @y = @y ? 250\n @radius = @radius ? 250\n @frames = @frames ? 16\n @bridges = @bridges ? 20\n @degrees = @degrees ? 360\n @rotation = @rotation ? 0\n @frameAngle = @degrees / @frames\n @draw()\n drawFrame: () ->\n moveTo @x , @y\n for i in [ 0 .. @frames ]\n if i * @frameAngle != 360\n radians = (i * @frameAngle + @rotation)*(Math.PI / 180)\n x = Math.cos(radians)\n y = -Math.sin(radians)\n line (x*@radius) , (y*@radius)\n drawBridge: () ->\n r = @radius\n for web in [ 1 .. @bridges ]\n r /= 1.2\n for i in [ 0 ... @frames ]\n # Starting postition\n r1 = (i * @frameAngle + @rotation)*(Math.PI / 180)\n x1 = Math.cos(r1)\n y1 = -Math.sin(r1)\n \n # End position\n r2 = (i * @frameAngle + @frameAngle + @rotation)*(Math.PI / 180)\n x2 = Math.cos(r2)\n y2 = -Math.sin(r2)\n moveTo x1 * r + @x , y1 * r + @y\n lineTo x2 * r + @x , y2 * r + @y\n draw: ( ) ->\n this.drawFrame()\n this.drawBridge()\n\n# Here is the drawing\nbackground setSaturation(darkpurple,-25)\nstroke white\nweb = new SpiderWeb(250,250, radius, frames, bridges, degrees, rotation)\n# Spider Body\nstroke white, 10\nmoveTo 250, 0\nline 0, 200\nmove 0, 200\ncolor black\nstroke 0\nellipse bodySize * 0.8, bodySize\n# Spider Legs\nstroke black, 5\ncolor null\npairOfLegs = (flipHori,flipVert) ->\n spread = legSpread * flipHori\n length = legLength * flipVert\n polygon spread, length, spread * 0.7, length * 2\n polygon spread * 1.3, length * 0.4, spread * 1.7, length * 1.5\npairOfLegs(1,1)\npairOfLegs(-1,1)\npairOfLegs(1,-1)\npairOfLegs(-1,-1)\n\n#Spider Head\nstroke 0\ncolor black\nmove 0, bodySize\nellipse bodySize * 0.5, bodySize * 0.4\ncolor orangered\nmove eyeSpacing * -1.5, eyeSpacing / -2\nfor [1 .. 4]\n circle eyeSize\n move 0, eyeSpacing\n circle eyeSize\n move eyeSpacing, eyeSpacing * -1\n",
+ "steps": [
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/chest.json b/lib/challenges/locales/ja/worlds/pixelhack/chest.json
new file mode 100644
index 000000000..60635a1ec
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/chest.json
@@ -0,0 +1,104 @@
+{
+ "id": "chest",
+ "title": "Loot Chest",
+ "description": "A mysterious chest found with your code",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Role Playing Games (RPGs) became one of the most popular genres of games in the late 80s. One of the most common objects in these games is the loot chest, let's start by setting a moody scene with a background **type** `background darkslategray`",
+ "solution": "background darkslategray"
+ },
+ {
+ "hint": "Next we're going to turn the stroke off **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Position the cursor to the top left corner of where the chest will be - **type** `move -150,-100`",
+ "solution": "move -150,-100"
+ },
+ {
+ "hint": "Set the color to gold to give the impression of riches inside **type** `color gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "We're going to layer this up to keep the number of shapes we have to draw to a minimum, first we draw the golden frame draw a `rectangle` 300 by 200",
+ "solution": "rectangle 300,200"
+ },
+ {
+ "hint": "Next we are going to add a highlight to the gold **type** `color lightyellow`",
+ "solution": "color lightyellow"
+ },
+ {
+ "hint": "With 8-bit art, simple details add a lot to an object, add a 25 by 100 `rectangle` to give the gold a nice sheen",
+ "solution": "rectangle 25,100"
+ },
+ {
+ "hint": "Move your cursor to get ready to draw the wooden part of the chest **type** `move 25,0`",
+ "solution": "move 25,0"
+ },
+ {
+ "hint": "Change the fill color to `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "We use a trick of overlaying brown over the gold so the gold looks like a frame holding the wooden panels together **type** `rectangle 250,175`",
+ "solution": "rectangle 250,175"
+ },
+ {
+ "hint": "Move the cursor into place to split the wooden part in half with a lid **type** `move 0,60`",
+ "solution": "move 0,60"
+ },
+ {
+ "hint": "Switch your `color` back to gold",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Draw the seam with a `rectangle` 250 by 25",
+ "solution": "rectangle 250,25"
+ },
+ {
+ "hint": "Next we'll continue our highlight effect **type** `color lightyellow`",
+ "solution": "color lightyellow"
+ },
+ {
+ "hint": "Continue the highlight effect by drawing a `square` 25 big",
+ "solution": "square 25"
+ },
+ {
+ "hint": "Finally we're going to finish with a clasp, move to the middle of the chest **type** `move 75,-25`",
+ "solution": "move 75,-25"
+ },
+ {
+ "hint": "Set the `color` to gold so it matches the frame",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Draw a `rectangle` 100 by 75",
+ "solution": "rectangle 100,75"
+ },
+ {
+ "hint": "Move in to draw the actual clasp **type** `move 25,25`",
+ "solution": "move 25,25"
+ },
+ {
+ "hint": "Set the `color` to darkbrown",
+ "solution": "color darkbrown"
+ },
+ {
+ "hint": "Finally draw a `rectangle` 50 by 75",
+ "solution": "rectangle 50,75"
+ }
+ ],
+ "completion_text": "That chest looks great, I wonder what loot is hiding within!",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "chest-nether.png",
+ "chest-sunken.png",
+ "chest-open.png"
+ ]
+ },
+ "cover": "pixel-chest.png",
+ "guide": "#### New Words\n**square** size | square 25\n\nThis is just a shorthand version of the rectangle \n\n#### What you'll make\n1. Role Playing Games (RPGs) became one of the most popular genres of games in the late 80s. One of the most common objects in these games is the loot chest, let's start by setting a moody scene with a background **type** `background darkslategray`\n2. Next we're going to turn the stroke off **type** `stroke 0`\n3. Position the cursor to the top left corner of where the chest will be **type** `move -150,-100`\n4. Set the color to gold to give the impression of riches inside **type** `color gold`\n5. We're going to layer this up to keep the number of shapes we have to draw to a minimum, first we draw the golden frame draw a `rectangle` 300 by 200\n6. Next we are going to add a highlight to the gold **type** `color lightyellow`\n7. With 8-bit art, simple details add a lot to an object, add a 25 by 100 `rectangle` to give the gold a nice sheen\n8. Move your cursor to get ready to draw the wooden part of the chest **type** `move 25,0`\n9. Change the fill color to `color brown`\n10. We use a trick of overlaying brown over the gold so the gold looks like a frame holding the wooden panels together **type** `rectangle 250,175`\n11. Move the cursor into place to split the wooden part in half with a lid **type** `move 0,60`\n12. Switch your `color` back to gold\n13. Draw the seam with a `rectangle` 250 by 25\n14. Next we'll continue our highlight effect **type** `color lightyellow`\n15. Continue the highlight effect by drawing a `square` 25 big\n16. Finally we're going to finish with a clasp, move to the middle of the chest **type** `move 75,-25`\n17. Set the `color` to gold so it matches the frame\n18. Draw a `rectangle` 100 by 75\n19. Move in to draw the actual clasp **type** `move 25,25`\n20. Set the `color` to darkbrown\n21. Finally draw a `rectangle` 50 by 75\n\n#### What you'll hack\nBy changing the colours and adding a few shapes you can build an environment around your chest. For a more complex challenge change your code to open the chest and code some loot within.\n\n#### Briefing \nThe loot chest has made appearances in too many video games to list. Possibly most prominently in games from the Legend of Zelda, where they would hold everything from small gifts of money to powerful new tools and weapons. What is in this loot chest? That’s up to you. When you’re done with the chest, open it up and put in your own creation as the loot.\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/colorfrenzy.json b/lib/challenges/locales/ja/worlds/pixelhack/colorfrenzy.json
new file mode 100644
index 000000000..ee72945ed
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/colorfrenzy.json
@@ -0,0 +1,47 @@
+{
+ "id": "colorfrenzy",
+ "title": "Color Frenzy",
+ "description": "Use a loop to draw an 8-bit pattern.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We’ll just use solid shapes with no stroke. So **type** `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "For this one lets use two loops, one to go horizontally across the screen, and one to go down vertically. **Type** `for x in [ 0 .. 20 ]`",
+ "solution": "for x in [ 0 .. 20 ]"
+ },
+ {
+ "hint": "The second loop should be `for y in [ 0 .. 20 ]`",
+ "solution": " for y in [ 0 .. 20 ]",
+ "validate": " for y in \\[ 0 .. 20 \\]"
+ },
+ {
+ "hint": "Set the color with `color rotate red, x + y * 10`",
+ "solution": " color rotate red, x + y * 10",
+ "validate": " color rotate red, x \\+ y \\* 10"
+ },
+ {
+ "hint": "Move into position with `moveTo x * 25, y * 25`",
+ "solution": " moveTo x * 25, y * 25",
+ "validate": " moveTo x \\* 25, y \\* 25"
+ },
+ {
+ "hint": "Finally, lets add squares. **Type** `square 25`",
+ "solution": " square 25",
+ "validate": " square 25"
+ }
+ ],
+ "completion_text": "Well done! The rotate function cycles through colours creating a rainbow effect.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "color-quint.png",
+ "color-multi.png",
+ "color-random.png"
+ ]
+ },
+ "cover": "pixel-colorfrenzy.png",
+ "guide": "#### What you'll make\n1. We’ll just use solid shapes with no stroke. So **type** `stroke 0`.\n2. For this one lets use two loops, one to go horizontally across the screen, and one to go down vertically. **Type** `for x in [ 0 .. 20 ]`\n3. The second loop should be `for y in [ 0 .. 20 ]`\n4. Set the color with `color rotate red, x * 10`\n5. Move into position with `moveTo x * 25, y * 25`\n6. Finally, lets add some randomness to the mix. **Type** `square random 20, 25`\n\n#### What you’ll hack\nBecause you are travelling across both the x and the y axes, you can have some pretty crazy color combinations. Using math you can make the rainbows repeat in interesting ways, and then change up how you draw on the screen using a random function. There are loads of variations, see what you can find!\n\n#### Briefing\nWe used one loop in the previous exercises to repeat actions, and then change one thing about the loop. In this challenge, we are going to be looping twice. The first loop is going to loop across the x coordinates—then the second loop is going to loop across the y coordinates. Because we have two loops, we are able to control color going in two different directions, and draw squares in two different directions. These “nested” loops will allow us to cover the canvas with color.\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/diamondblock.json b/lib/challenges/locales/ja/worlds/pixelhack/diamondblock.json
new file mode 100644
index 000000000..c2e73a762
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/diamondblock.json
@@ -0,0 +1,74 @@
+{
+ "id": "diamondblock",
+ "title": "8-bit Diamond Block",
+ "description": "Make a diamond block with loops.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Set stroke to 0.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "We’ll use two loops for the stone. **Type** `for x in [0 ... 16]`",
+ "solution": "for x in [0 ... 16]"
+ },
+ {
+ "hint": "For y use `for y in [0 ... 16]`",
+ "solution": " for y in [0 ... 16]"
+ },
+ {
+ "hint": "We’ll use a randomly chosen shade of gray to make the stone. **Type** `color darken gray, random -6, 9`",
+ "solution": " color darken gray, random -6, 9",
+ "validate": " color darken gray, random -6, 9"
+ },
+ {
+ "hint": "Move into position for each square with `moveTo x * 31.25, y * 31.25`",
+ "solution": " moveTo x * 31.25, y * 31.25",
+ "validate": " moveTo x \\* 31.25, y \\* 31.25"
+ },
+ {
+ "hint": "Finally, draw the stone square with `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ },
+ {
+ "hint": "Get out of the loop by bringing your cursor to the beginning of the line with **BACKSPACE**. We’ll start a new pair of loops to draw the diamond. **Type** `for x in [2 ... 14]`",
+ "solution": "for x in [2 ... 14]"
+ },
+ {
+ "hint": "This time we are starting at 2 and ending at 14 because we only want the diamond to appear in the middle of the block. **Type** `for y in [2 ... 14]`",
+ "solution": " for y in [2 ... 14]"
+ },
+ {
+ "hint": "The diamond should appear randomly, this if statement will only randomly draw the diamond. **Type** `if 1 == random 0, 4`",
+ "solution": " if 1 == random 0, 4",
+ "validate": " if 1 == random 0, 4"
+ },
+ {
+ "hint": "Set the diamond color with `color lighten lightblue, random 0, 40`",
+ "solution": " color lighten lightblue, random 0, 40",
+ "validate": " color lighten lightblue, random 0, 40"
+ },
+ {
+ "hint": "Move into position for each square with `moveTo x * 31.25, y * 31.25`",
+ "solution": " moveTo x * 31.25, y * 31.25",
+ "validate": " moveTo x \\* 31.25, y \\* 31.25"
+ },
+ {
+ "hint": "Finally, draw the diamond square with `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ }
+ ],
+ "completion_text": "You’ve hacked the pixels and won! What’s next? More challenges and 8-bit art projects here at Kano if you are interested. Congratulations and well done!",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "diamond-obsidian.png",
+ "diamond-rainbow.png",
+ "diamond-lotsof.png"
+ ]
+ },
+ "cover": "pixel-diamondblock.png",
+ "guide": "#### What you'll make\n1. Set stroke to 0.\n2. We’ll use two loops for the stone. **Type** `for x in [0 ... 16]`\n3. For y use `for y in [0 ... 16]`\n4. We’ll use a randomly chosen shade of gray to make the stone. **Type** `color darken gray, random -6, 9`\n5. Move into position for each square with `moveTo x * 31.25, y * 31.25`\n6. Finally, draw the stone square with `square 32`\n7. Get out of the loop by bringing your cursor to the beginning of the line with **BACKSPACE**. We’ll start a new pair of loops to draw the diamond. **Type** `for x in [2 ... 14]`\n8. This time we are starting at 2 and ending at 14 because we only want the diamond to appear in the middle of the block. **Type** `for y in [2 ... 14]`\n9. The diamond should appear randomly, this if statement will only randomly draw the diamond. **Type** `if 1 == random 0, 4`\n10. Set the diamond color with `color lighten lightblue, random 0, 40`\n11. Move into position for each square with `moveTo x * 31.25, y * 31.25`\n12. Finally, draw the diamond square with `square 32`\n\n#### What you'll hack\nTo make this Minecraft Block your own all you need to change are two variables: the colour of the top and bottom parts. Make a strawberry, a water block, or anything else by just changing these two lines. For added complexity, make a Mycelium block with some randomness and another for loop. If you really want to have a challenge, try incorporating what you learned in the previous lesson and use the x and y values to add some extra color rotation to the dirt.\n\n#### Briefing\nThe grand finale: diamonds. This is the most precious and sought after Minecraft block. You’ll use nested for loops again to draw the stone, and then another pair of nested for loops to draw the stone, and then another nested for loop to draw the diamond. But as the diamond should be sprinkled throughout the stone we’ll use an if statement and use a random number to determine whether or not we should draw the square. Additionally, we only want to draw the diamond on the inside of the block, so our diamond-drawing loop will run from 2 to 14, rather than 0 to 16.\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/forloop.json b/lib/challenges/locales/ja/worlds/pixelhack/forloop.json
new file mode 100644
index 000000000..28798c88a
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/forloop.json
@@ -0,0 +1,95 @@
+{
+ "id": "forloop",
+ "title": "For Loop",
+ "description": "Use a for loop to build a snake with ease.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We're going to recreate the classic game Snake using the power of For Loops. First let's set a dirt background - **type** `background tan`",
+ "solution": "background tan"
+ },
+ {
+ "hint": "Turn off the stroke **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "First we position the head of the snake **type** `moveTo 80,250`",
+ "solution": "moveTo 80,250"
+ },
+ {
+ "hint": "Set a snake-like color **type** `color olivedrab`",
+ "solution": "color olivedrab"
+ },
+ {
+ "hint": "Draw a `circle` 30 pixels big for the head",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "This is where we will set the loop up, using the for function, the first number is where to start counting from and the second number is what to count to. So 1 .. 7 makes the computer count to 7 - **type** `for [1 .. 7]`",
+ "solution": "for [1 .. 7]"
+ },
+ {
+ "hint": "Everything within this indent will be repeated 7 times, we need a move function to position each part of the body - **type** `move 50,0`",
+ "solution": " move 50,0"
+ },
+ {
+ "hint": "We set the body color - **type** `color olivedrab`",
+ "solution": " color olivedrab"
+ },
+ {
+ "hint": "Once we add a circle it'll be clear what is going on with the code being repeated several times - **type** `circle 30`",
+ "solution": " circle 30"
+ },
+ {
+ "hint": "Let's add a square so the body looks more segmented, notice how we're now drawing 7 shapes for every line of code - **type** `square 30`",
+ "solution": " square 30"
+ },
+ {
+ "hint": "We're going to add another detail to the tail so set the `color` to darkgreen",
+ "solution": " color darkgreen"
+ },
+ {
+ "hint": "Draw another circle, the code is still indented this will be repeated 7 times - **type** `circle 20`",
+ "solution": " circle 20"
+ },
+ {
+ "hint": "Finally draw a `square` 20 pixels big",
+ "solution": " square 20"
+ },
+ {
+ "hint": "First make sure you are no longer in the for loop by hitting backspace so we're no longer indented. - **type** `moveTo 70,240`",
+ "solution": "moveTo 70,240"
+ },
+ {
+ "hint": "Change the `color` to black",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw an `ellipse` 10 pixels wide and 5 tall.",
+ "solution": "ellipse 10,5"
+ },
+ {
+ "hint": "Finally we'll give it a tongue so it can hiss like a snake should - **type** `color salmon`",
+ "solution": "color salmon"
+ },
+ {
+ "hint": "Position it on the face - **type** `move -15, 20`",
+ "solution": "move -15, 20"
+ },
+ {
+ "hint": "We'll draw a rectangle with a negative width to stretch it out from the mouth - **type** `rectangle -20,4`",
+ "solution": "rectangle -20,4"
+ }
+ ],
+ "completion_text": "Now you have seen how we can create more art with less lines of code by using loops in clever ways, why not try changing the 7 in the for loop and see what happenes",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "snake-tiny.png",
+ "snake-apple.png",
+ "snake-corner.png"
+ ]
+ },
+ "cover": "pixel-forloop.png",
+ "guide": "#### New words\n**for i in [rangeMinimum ... rangeMax]**| for in [0 .. 10]\n\nThe for loop executes once for every number in the range provided. 10 times for the range [0 … 10] and 20 times for the range [0 … 20]. The loop executes any code that is indented four spaces appearing after it.\n\n#### What you'll make\n1. We're going to recreate the classic game Snake using the power of For Loops. First let's set a dirt background - **type** `background tan`\n2. Turn off the stroke **type** `stroke 0`\n3. First we position the head of the snake **type** `moveTo 80,250`\n4. Set a snake-like color **type** `color olivedrab`\n5. Draw a `circle` 30 pixels big for the head\n6. This is where we will set the loop up, using the for function, the first number is where to start counting from and the second number is what to count to. So 1 .. 7 makes the computer count to 7 - **type** `for [1 .. 7]`\n7. Everything within this indent will be repeated 7 times, we need a move function to position each part of the body - **type** `move 50,0`\n8. We set the body color - **type** `color olivedrab`\n9. Once we add a circle it'll be clear what is going on with the code being repeated several times - **type** `circle 30`\n10. Let's add a square so the body looks more segmented, notice how we're now drawing 7 shapes for every line of code - **type** `square 30`\n11. We're going to add another detail to the tail so set the `color` to darkgreen\n12. Draw another circle, the code is still indented this will be repeated 7 times - **type** `circle 20`\n13. Finally draw a `square` 20 pixels big\n14. First make sure you are no longer in the for loop by hitting backspace so we're no longer indented. - **type** `moveTo 70,240`\n15. Change the `color` to black\n16. Draw an `ellipse` 10 pixels wide and 5 tall.\n17. Finally we'll give it a tongue so it can hiss like a snake should - **type** `color salmon`\n18. Position it on the face - **type** `move -15, 20`\n19. We'll draw a rectangle with a negative width to stretch it out from the mouth - **type** `rectangle -20,4`\n\n#### What you’ll hack\nThe beauty of the loop is you can change your code once, and every single shape you draw will update. This is another reason to use loops, you can make big changes without rewriting big amounts of code.\n\n#### Briefing\nHow many times have you written the same commands over and over again in the last few challenges? For this snake challenge, each part of the snake is made up of the same circle shape. Instead of writing the same circle code over and over, you can have the computer do it for you!\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/gradient.json b/lib/challenges/locales/ja/worlds/pixelhack/gradient.json
new file mode 100644
index 000000000..acffb6a61
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/gradient.json
@@ -0,0 +1,56 @@
+{
+ "id": "gradient",
+ "title": "8-bit sunset",
+ "description": "Use a loop to draw an 8-bit sunset.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We’ll just use solid shapes with no stroke. So **type** `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "To better see the effect of our test, text to bold with `bold true`",
+ "solution": "bold true"
+ },
+ {
+ "hint": "We’ll use a loop to draw the sunset. This time going from 0 to 10. **Type** `for y in [ 0 .. 10 ]`",
+ "solution": "for y in [ 0 .. 10 ]"
+ },
+ {
+ "hint": "To understand how this loop works, we’ll draw the loop value out. Move into position with `moveTo 250, y * 50`",
+ "solution": " moveTo 250, y * 50",
+ "validate": " moveTo 250, y \\* 50"
+ },
+ {
+ "hint": "Let’s inspect what our loop is doing. You can use the text function with plain text, but you can also pass it a variable. **Type** `text y` to see what the y values are across the screen.",
+ "solution": " text y",
+ "validate": " text y"
+ },
+ {
+ "hint": "The y variable is increasing every time we go through the loop. It goes from 0 (which is off the screen) to 10. To draw our sunset we need to just move to the left edge. **Type** `moveTo 0, y * 50`",
+ "solution": " moveTo 0, y * 50",
+ "validate": " moveTo 0, y \\* 50"
+ },
+ {
+ "hint": "We’ll use a special function to darken the color every loop. **Type** `color darken blue, y * 3`.",
+ "solution": " color darken blue, y * 3",
+ "validate": " color darken blue, y \\* 3"
+ },
+ {
+ "hint": "Now for the sky, this will draw over your numbers. **Type** `rectangle 500, 50`.",
+ "solution": " rectangle 500, 50",
+ "validate": " rectangle 500, 50"
+ }
+ ],
+ "completion_text": "Beautiful! See how the blue gets darker as it goes down the screen. The y variable was being passed to the darken function. As the y value increased, the darken function made the blue color get darker.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "grad-yello.png",
+ "grad-sword.png",
+ "grad-mage.png"
+ ]
+ },
+ "cover": "pixel-gradient.png",
+ "guide": "#### New words\n**bold** state | bold true\n\nCan set whether text is drawn as bold or regular. True is bold, false is regular.\n\n**text** string | text “hello world”\n\nDraws text centered on the drawing cursor. The properties of the text are controlled by the color, font, bold, and italic functions.\n\n\n#### What you'll make\n1. We’ll just use solid shapes with no stroke. So **type** `stroke 0`.\n2. To better see the effect of our test, text to bold with `bold true`\n3. We’ll use a loop to draw the sunset. This time going from 0 to 10. **Type** `for y in [ 0 .. 10 ]`\n4. To understand how this loop works, we’ll draw the loop value out. Move into position with `moveTo 250, y * 50`\n5. Let’s inspect what our loop is doing. You can use the text function with plain text, but you can also pass it a variable. **Type** `text y` to see what the y values are across the screen.\n6. The y variable is increasing every time we go through the loop. It goes from 0 (which is off the screen) to 10. To draw our sunset we need to just move to the left edge. **Type** `moveTo 0, y * 50`\n7. We’ll use a special function to darken the color every loop. **Type** `color darken blue, y * 3`.\n8. Now for the sky, this will draw over your numbers. **Type** `rectangle 500, 50`.\n\n\n#### What you’ll hack\nNow that you’ve got a few pixel art creations under your belt, why not bring them in here and give them the full background treatment they deserve!\n\n#### Briefing\nWith a loop you can tell a computer to do something over and over again. With the `for` loop, you can ask how many times the loop has happened, and depending on what loop it is vary what it is the loop is doing.\n\nIn this sunset, we want to draw rectangular blocks of the same size, and we want to move down a certain distance every time, but this time we need a way of telling the computer that they will have a different color every time. How do we do this? With variables. The way you can ask how many times the loop has happened in a `for` loop is with the word you start the loop with. When we say `for y in [ 0 .. 10]`, we are making the word `y` equal to how many times the loop has executed. It is incremented for us, so any time we use the variable `y`, it will tell the program how many times it has looped.\n\nBecause it increments by one every time, we can use it to travel across color shades by making a color darker in step as our y variable gets bigger. So at the beginning of the loop we have a small y and a regular color, and at the end we have a big y and a dark color.\n\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/grassblock.json b/lib/challenges/locales/ja/worlds/pixelhack/grassblock.json
new file mode 100644
index 000000000..e3ed59246
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/grassblock.json
@@ -0,0 +1,62 @@
+{
+ "id": "grassblock",
+ "title": "8-bit Grass Block",
+ "description": "Make a block you can bring into Minecraft.",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "We’ll just use solid shapes with no stroke. So **type** `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "We’ll have to use two loops here. **Type** `for x in [0 ... 16]`",
+ "solution": "for x in [0 ... 16]"
+ },
+ {
+ "hint": "For y use `for y in [0 ... 16]`",
+ "solution": " for y in [0 ... 16]",
+ "validate": " for y in \\[0 ... 16\\]"
+ },
+ {
+ "hint": "We’ll use a randomly chosen shade of brown to make the dirt. **Type** `color darken brown, random 0, 25`",
+ "solution": " color darken brown, random 0, 25",
+ "validate": " color darken brown, random 0, 25"
+ },
+ {
+ "hint": "We’ll need to move into position for each square with `moveTo x * 31.25, y * 31.25`",
+ "solution": " moveTo x * 31.25, y * 31.25",
+ "validate": " moveTo x \\* 31.25, y \\* 31.25"
+ },
+ {
+ "hint": "Finally, draw the dirt square with `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ },
+ {
+ "hint": "The grass should only appear on the top of the block, so we’ll decide if the block should have a green bit drawn over it with `if 4 > y + random 0, 3`",
+ "solution": " if 4 > y + random 0, 3",
+ "validate": " if 4 > y \\+ random 0, 3"
+ },
+ {
+ "hint": "Set the green color with the darken function. **Type** `color darken green, random 0, 25`",
+ "solution": " color darken green, random 0, 25",
+ "validate": " color darken green, random 0, 25"
+ },
+ {
+ "hint": "Finally, draw the square with `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ }
+ ],
+ "completion_text": "Nice! Now you can change the brown and green colors to get an interesting effect. Can you make a block that looks like a strawberry?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "grass-overgrown.png",
+ "grass-strawb.png",
+ "grass-myce.png"
+ ]
+ },
+ "cover": "pixel-grassblock.png",
+ "guide": "#### What you'll make\n1. We’ll just use solid shapes with no stroke. So **type** `stroke 0`.\n2. We’ll have to use two loops here. **Type** `for x in [0 ... 16]`\n3. For y use `for y in [0 ... 16]`\n4. We’ll use a randomly chosen shade of brown to make the dirt. **Type** `color darken brown, random 0, 25`\n5. We’ll need to move into position for each square with `moveTo x * 31.25, y * 31.25`\n6. Finally, draw the dirt square with `square 32`\n7. The grass should only appear on the top of the block, so we’ll decide if the block should have a green bit drawn over it with `if 4 > y + random 0, 3`\n8. Set the green color with the darken function. **Type** `color darken green, random 0, 25`\n9. Finally, draw the square with `square 32`\n\n#### What you’ll hack\nTo make this Minecraft Block your own all you need to change are two variables: the colour of the top and bottom parts. Make a strawberry, a water block, or anything else by just changing these two lines. For added complexity, make a Mycelium block with some randomness and another for loop. If you really want to have a challenge, try incorporating what you learned in the previous lesson and use the x and y values to add some extra color rotation to the dirt.\n\n#### Briefing \nOne of the most basic and commonly found blocks in minecraft is the grass block. Just sixteen blocks across and down, it is a very simple image, but we’re going to use randomness to give it a bit of texture. Two different layers will be used: one for the grass, and one for the dirt. \n\nIn each layer we will use the nested for loops to draw across and down the screen, and we’ll only change one thing about each: the color.\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/index.json b/lib/challenges/locales/ja/worlds/pixelhack/index.json
new file mode 100644
index 000000000..2552d1e17
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/index.json
@@ -0,0 +1,17 @@
+{
+ "challenges": [
+ "./pong",
+ "./ship",
+ "./tetris",
+ "./chest",
+ "./variables",
+ "./sword",
+ "./steve",
+ "./mage",
+ "./forloop",
+ "./gradient",
+ "./colorfrenzy",
+ "./grassblock",
+ "./diamondblock"
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/mage.json b/lib/challenges/locales/ja/worlds/pixelhack/mage.json
new file mode 100644
index 000000000..4486614f5
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/mage.json
@@ -0,0 +1,208 @@
+{
+ "id": "mage",
+ "title": "RPG Mage",
+ "description": "Draw an 8-bit Aqua Mage",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We're going to make a Mage character for an RPG game so first we need to set the scene, we'll use the darken function to change the color teal to a more suitable shade of dark blue - **type** `background darken teal,15 `",
+ "solution": "background darken teal,15"
+ },
+ {
+ "hint": "We're making pixel art so we'll turn the stroke off - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Let's set the color to saddlebrown for her hair - **type** `color saddlebrown`",
+ "solution": "color saddlebrown"
+ },
+ {
+ "hint": "We'll use the moveTo function to move the cursor to a specific canvas coordinate - **type** `moveTo 120,80`",
+ "solution": "moveTo 120,80"
+ },
+ {
+ "hint": "We're going to draw the hair first so we can layer the rest of the character over it to create depth - **type** `rectangle 320,400`",
+ "solution": "rectangle 320,400"
+ },
+ {
+ "hint": "We're going to be drawing with blocks 20 by 20 pixels big - **type** `move 20,-20`",
+ "solution": "move 20,-20"
+ },
+ {
+ "hint": "Stack up another layer of hair - **type** `rectangle 280,20`",
+ "solution": "rectangle 280,20"
+ },
+ {
+ "hint": "Move upwards again to create a rounded effect - **type** `move 20,-20`",
+ "solution": "move 20,-20"
+ },
+ {
+ "hint": "Draw the final row of hair in - **type** `rectangle 240,20`",
+ "solution": "rectangle 240,20"
+ },
+ {
+ "hint": "Next we'll draw the face, - **type** `color sandybrown`",
+ "solution": "color sandybrown"
+ },
+ {
+ "hint": "Move the cursor to the top corner of where the face will be - **type** `move 0,120`",
+ "solution": "move 0,120"
+ },
+ {
+ "hint": "Draw the face with a `rectangle` that is 240 by 140",
+ "solution": "rectangle 240,140"
+ },
+ {
+ "hint": "The face is a little too square so move the cursor up to the forehead - **type** `move 80,-40`",
+ "solution": "move 80,-40"
+ },
+ {
+ "hint": "Draw another rectangle to create a fringe - **type** `rectangle 80,40`",
+ "solution": "rectangle 80,40"
+ },
+ {
+ "hint": "Move the cursor down the face - **type** `move -40,180`",
+ "solution": "move -40,180"
+ },
+ {
+ "hint": "Finish the face off by drawing a chin - **type** `rectangle 160,20`",
+ "solution": "rectangle 160,20"
+ },
+ {
+ "hint": "Next we'll draw some robes, our mage specialises in water magic so we'll make them blue - **type** `color paleturquoise`",
+ "solution": "color paleturquoise"
+ },
+ {
+ "hint": "Position the cursor for drawing the sleeves - **type** `move -100,20`",
+ "solution": "move -100,20"
+ },
+ {
+ "hint": "Draw a `rectangle` 360 wide and 80 tall for the robe sleeves",
+ "solution": "rectangle 360,80"
+ },
+ {
+ "hint": "Position the cursor to draw the body of the robe - **type** `move 60,80`",
+ "solution": "move 60,80"
+ },
+ {
+ "hint": "Draw a `rectangle` 240 wide and 100 tall for the robe body",
+ "solution": "rectangle 240,100"
+ },
+ {
+ "hint": "Next we'll draw a seam on the robe, change the `color` to teal",
+ "solution": "color teal"
+ },
+ {
+ "hint": "Move the cursor to the middle of the robe - **type** `move 120,-80`",
+ "solution": "move 120,-80"
+ },
+ {
+ "hint": "Draw a thin rectangle for the seam - **type** `rectangle 40,180`",
+ "solution": "rectangle 40,180"
+ },
+ {
+ "hint": "Next up we'll draw the arms - **type** `move 140,40`",
+ "solution": "move 140,40"
+ },
+ {
+ "hint": "Change the `color` back to sandybrown",
+ "solution": "color sandybrown"
+ },
+ {
+ "hint": "Draw the arm with a `rectangle` 40 by 140",
+ "solution": "rectangle 40,140"
+ },
+ {
+ "hint": "Move to the the other side of the body - **type** `move -380,40`",
+ "solution": "move -380,40"
+ },
+ {
+ "hint": "This arm will be disconnected at first but don't worry, we haven't finished yet - **type** `rectangle 40,100`",
+ "solution": "rectangle 40,100"
+ },
+ {
+ "hint": "Every mage needs a staff to cast magic, so we'll draw that next - **type** `color darkmagenta`",
+ "solution": "color darkmagenta"
+ },
+ {
+ "hint": "Move to the top of the staff - **type** `move 60,-120`",
+ "solution": "move 60,-120"
+ },
+ {
+ "hint": "Draw the staff over the arm so it looks like it's being held - **type** `rectangle -40,220`",
+ "solution": "rectangle -40,220"
+ },
+ {
+ "hint": "As we said at the start our character uses water magic so set the color to aquamarine - **type** `color aquamarine`",
+ "solution": "color aquamarine"
+ },
+ {
+ "hint": "Draw an orb at the top of the staff, if we use negative numbers then shapes are drawn backwards - **type** `square -80`",
+ "solution": "square -80"
+ },
+ {
+ "hint": "We're also going to draw a magic circlet, move to the forehead - **type** `move 60,-200`",
+ "solution": "move 60,-200"
+ },
+ {
+ "hint": "Draw a `rectangle` that is 240 by 20",
+ "solution": "rectangle 240,20"
+ },
+ {
+ "hint": "Shift down a row on our grid - **type** `move -20,20`",
+ "solution": "move -20,20"
+ },
+ {
+ "hint": "Finish off the circlet - **type** `rectangle 280,20`",
+ "solution": "rectangle 280,20"
+ },
+ {
+ "hint": "We just need to finish off the face so position the cursor over the eyes - **type** `move 60,60`",
+ "solution": "move 60,60"
+ },
+ {
+ "hint": "Set the `color` to black",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw the eyes long and thin - **type** `rectangle 40,80`",
+ "solution": "rectangle 40,80"
+ },
+ {
+ "hint": "Move across for the other eye - **type** `move 120,0`",
+ "solution": "move 120,0"
+ },
+ {
+ "hint": "Draw the other eye - **type** `rectangle 40,80`",
+ "solution": "rectangle 40,80"
+ },
+ {
+ "hint": "One last detail, we're going to make the eyes shiny - **type** `color white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a smaller rectangle within the pupil - **type** `rectangle 20,40`",
+ "solution": "rectangle 20,40"
+ },
+ {
+ "hint": "Move to the other eye - **type** `move -120,0`",
+ "solution": "move -120,0"
+ },
+ {
+ "hint": "Finish off with one last rectangle - **type** `rectangle 20,40`",
+ "solution": "rectangle 20,40"
+ }
+ ],
+ "completion_text": "Well done! Your mage is ready to raid some dungeons and cast some spells. Why not change the colours of the outfit to red for fire magic.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "mage-fire.png",
+ "mage-light.png",
+ "mage-warrior.png"
+ ]
+ },
+ "cover": "pixel-mage.png",
+ "guide": "#### What you'll make\n1. We're going to make a Mage character for an RPG game so first we need to set the scene, we'll use the darken function to change the color teal to a more suitable shade of dark blue - **type** `background darken teal,15 `\n2. We're making pixel art so we'll turn the stroke off - **type** `stroke 0`\n3. Let's set the color to saddlebrown for her hair - **type** `color saddlebrown`\n4. We'll use the moveTo function to move the cursor to a specific canvas coordinate - **type** `moveTo 120,80`\n5. We're going to draw the hair first so we can layer the rest of the character over it to create depth - **type** `rectangle 320,400`\n6. We're going to be drawing with blocks 20 by 20 pixels big - **type** `move 20,-20`\n7. Stack up another layer of hair - **type** `rectangle 280,20`\n8. Move upwards again to create a rounded effect - **type** `move 20,-20`\n9. Draw the final row of hair in - **type** `rectangle 240,20`\n10. Next we'll draw the face, - **type** `color sandybrown`\n11. Move the cursor to the top corner of where the face will be - **type** `move 0,120`\n12. Draw the face with a `rectangle` that is 240 by 140\n13. The face is a little too square so move the cursor up to the forehead - **type** `move 80,-40`\n14. Draw another rectangle to create a fringe - **type** `rectangle 80,40`\n15. Move the cursor down the face - **type** `move -40,180`\n16. Finish the face off by drawing a chin - **type** `rectangle 160,20`\n17. Next we'll draw some robes, our mage specialises in water magic so we'll make them blue - **type** `color paleturquoise`\n18. Position the cursor for drawing the sleeves - **type** `move -100,20`\n19. Draw a `rectangle` 360 wide and 80 tall for the robe sleeves\n20. Position the cursor to draw the body of the robe - **type** `move 60,80`\n21. Draw a `rectangle` 240 wide and 100 tall for the robe body\n22. Next we'll draw a seam on the robe, change the `color` to teal\n23. Move the cursor to the middle of the robe - **type** `move 120,-80`\n24. Draw a thin rectangle for the seam - **type** `rectangle 40,180`\n25. Next up we'll draw the arms - **type** `move 140,40`\n26. Change the `color` back to sandybrown\n27. Draw the arm with a `rectangle` 40 by 140\n28. Move to the the other side of the body - **type** `move -380,40`\n29. This arm will be disconnected at first but don't worry, we haven't finished yet - **type** `rectangle 40,100`\n30. Every mage needs a staff to cast magic, so we'll draw that next - **type** `color darkmagenta`\n31. Move to the top of the staff - **type** `move 60,-120`\n32. Draw the staff over the arm so it looks like it's being held - **type** `rectangle -40,220`\n33. As we said at the start our character uses water magic so set the color to aquamarine - **type** `color aquamarine`\n34. Draw an orb at the top of the staff, if we use negative numbers then shapes are drawn backwards - **type** `square -80`\n35. We're also going to draw a magic circlet, move to the forehead - **type** `move 60,-200`\n36. Draw a `rectangle` that is 240 by 20\n37. Shift down a row on our grid - **type** `move -20,20`\n38. Finish off the circlet - **type** `rectangle 280,20`\n39. We just need to finish off the face so position the cursor over the eyes - **type** `move 60,60`\n40. Set the `color` to black\n41. Draw the eyes long and thin - **type** `rectangle 40,80`\n42. Move across for the other eye - **type** `move 120,0`\n43. Draw the other eye - **type** `rectangle 40,80`\n44. One last detail, we're going to make the eyes shiny - **type** `color white`\n45. Draw a smaller rectangle within the pupil - **type** `rectangle 20,40`\n46. Move to the other eye - **type** `move -120,0`\n47. Finish off with one last rectangle - **type** `rectangle 20,40`\n\n#### What you’ll hack\nThis is a much more intricate drawing, so there are many more things to change. You can go through the colours making up the image and change them to a new palette, and add in new objects. Take a look at the warrior and the lightning mage as ways of powering up your warrior with new powers, armour and weapons.\n\n#### Briefing\nThe simple shapes you have been putting together into simple designs can be refined and brought into more complex creations. This RPG Mage is a much more intricate design, but uses just the same tools to create them.\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/pong.json b/lib/challenges/locales/ja/worlds/pixelhack/pong.json
new file mode 100644
index 000000000..360721423
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/pong.json
@@ -0,0 +1,40 @@
+{
+ "id": "pong",
+ "title": "ポン",
+ "description": "ポン試合をコードで描こう。",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "ポンは(1972年)最初の家庭用ゲームコンソールの一つだった。とても基本的な卓球ゲームなんだ。卓球をやるには何が必要だろう?そうだ、ボールだね。`circle 15`を**タイプ**",
+ "solution": "circle 15"
+ },
+ {
+ "hint": "次はパドルが必要だ。moveTo関数を使って、物の位置を決められる。moveToの後の最初の数字は水平(左右)の位置を、2つ目の数字は垂直(上下)の位置を示している。`moveTo 30,100`を**タイプ**",
+ "solution": "moveTo 30,100"
+ },
+ {
+ "hint": "パドルを長方形(`rectangle`)関数で描こう。`rectangle 20, 100`を**タイプ**",
+ "solution": "rectangle 20, 100"
+ },
+ {
+ "hint": "一人でポンはつまらないから、相手が必要。`moveTo 450,300`を**タイプ**",
+ "solution": "moveTo 450,300"
+ },
+ {
+ "hint": "最後は相手のパドルを描こう。最初の数字は幅で、2つ目の数は高さを示している。`rectangle 20, 100`を**タイプ**",
+ "solution": "rectangle 20, 100"
+ }
+ ],
+ "completion_text": "できた!昔のビデオゲームを再現できた。でもそのままだとちょっとつまらないね。moveToの後の100にクリックし、値を変えてパドルを動かしてみよう。",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "pong-white.png",
+ "pong-movecolor.png",
+ "pong-movepaddles.png"
+ ]
+ },
+ "cover": "pixel-pong.png",
+ "guide": "#### 新しい単語\n**circle** radius | circle 15\n\n現在位置で円形を描く。数字が大きければ大きいほど、円形も大きくなる。半径(radius)は円形の中心から、端までの距離だ。\n\n**moveTo** x, y | moveTo 30, 100\n\n現在位置を新しい(x, y)場所へ動かす。最初の値はxで、水平の位置を示している。2つ目の値はyで、垂直(上下)の位置を示している。\n\n**rectangle** width, height | rectangle 20, 100\n\n長方形を現在位置で描く。幅(width)と高さ(height)をそれぞれ設定することで長方形の大きさを指定できる。\n\n**color** name | color red\n\n鉛筆の色(color)を変更する。色を設定すると、全ての形や線や文字がこの色になる。色を透明にもできる:`color null`をタイプするだけ。\n\n**background** colorName | background white\n\n背景の色を変える。\n\n#### これから作るのは\n1. ポンは(1972年)最初の家庭用ゲームコンソールの一つだった。とても基本的な卓球ゲームなんだ。卓球をやるには何が必要だろう?そうだ、ボールだね。`circle 15`を**タイプ**\n2. 次はパドルが必要だ。moveTo関数を使って、物の位置を決められる。moveToの後の最初の数字は水平(左右)の位置を、2つ目の数字は垂直(上下)の位置を示している。`moveTo 30,100`を**タイプ**\n3. パドルを長方形(`rectangle`)関数で描こう。`rectangle 20, 100`を**タイプ**\n4. 一人でポンはつまらないから、相手が必要。`moveTo 450,300`を**タイプ**\n5. 最後は相手のパドルを描こう。最初の数字は幅で、2つ目の数は高さを示している。`rectangle 20, 100`を**タイプ**\n\n#### 追加チャレンジ\n美術家の重要なツールの一つは「色」である。最初の色は黒(black)で、形などを描く度に黒で描かれる。\n\n色を例えば赤(red)に変えることができる:最初の`moveTo`の後に`color red`を`rectangle 20, 100`の前にタイプすれば、パドルを違う色で描ける。ただし、色が変わるのはcolorの後だけなので、最後の行にcolorをタイプしても何も変わらない。\n\n背景の色をbackgroundで変えることができる。backgroundをどこに入れても大丈夫で、普通は一回だけ入れることになるが、一番上に入れることがお勧め。\n\n最後に、moveToの後の2つ目の数字を変えると、パドルを上下に動かすことができる。数字を簡単に変えるには、数字にクリックし、スライダーを動かせばいい。\n\n#### 概要\n1972年にアル・アルコーンさんはアタリ社入社後の新入社員課題として、ポンというゲームを作った。ボール一個とパドル2つで、非常にシンプルだったが、短期間で有名になった。そして何千万個も売れて、世界中で流行った。そこまで有名になったので、多くの競合他社は類似物も作り、アタリ社の成功を分けてもらおうとしていた。このゲームの成功のワケはシンプルでコピーしやすいことが一番だったと思われる。\n\nハッカーとして最初の仕事はポンゲームを作ることだ。ハッカーの秘密の一つは、リミックスだ:他人のコードとアイディアをパクり、組み合わせることだ。ポンのオリジナルアイディアを元に、自分だけのゲームが作れるんだ!まずスクリーンにボールとパドルを描く。そしてこれを元に新しい物を作ろう!\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/ship.json b/lib/challenges/locales/ja/worlds/pixelhack/ship.json
new file mode 100644
index 000000000..3e74d8152
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/ship.json
@@ -0,0 +1,68 @@
+{
+ "id": "ship",
+ "title": "Asteroids Ship",
+ "description": "Code a vector spaceship using lines",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Asteroids (1979) was one of the first major arcade games. It is set in space, so we're going to need to set the scene. **type** `background black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "The graphics in Asteroids were simple line based graphics called vectors, we still use a more complex version of this concept in computer graphics today. First we need to set our line color and width **type** `stroke white,5`",
+ "solution": "stroke white,5"
+ },
+ {
+ "hint": "Next we use the move function to set the starting point for our first line. **type** `move 0, -70`",
+ "solution": "move 0, -70"
+ },
+ {
+ "hint": "'Function' is the word we use to define what we are drawing. Let’s draw a line that starts where we just moved the cursor and ends at the bottom left. We type where we want the line to end after the function, so **type** `line -50,150` Try changing the Y value from 150 to -150 watch what happens. The line goes up, above where we started at -50. Let’s move it back down to 150!",
+ "solution": "line -50,150"
+ },
+ {
+ "hint": "To move our cursor to the start of the next line we feed the previous coordinates into the move function. **type** `move -50,150`",
+ "solution": "move -50,150"
+ },
+ {
+ "hint": "We draw a line inwards and upwards to form the thruster of the ship - **type** `line 50,-25`",
+ "solution": "line 50,-25"
+ },
+ {
+ "hint": "Again moving to the same coordinates for a continuous line **type** `move 50,-25`",
+ "solution": "move 50,-25"
+ },
+ {
+ "hint": "We replicate the shape for the right side of the ship by reversing the second coordinate to draw downwards instead of up **type** `line 50,25`",
+ "solution": "line 50,25"
+ },
+ {
+ "hint": "Follow through with the line again **type** `move 50,25`",
+ "solution": "move 50,25"
+ },
+ {
+ "hint": "Our ship starts to take shape. To make a line that goes from the end of the one we just drew to connect back up at the top **type** `line -50,-150`",
+ "solution": "line -50,-150"
+ },
+ {
+ "hint": "Finally we move back to our starting point to add a final touch **type** `move -50,-150`",
+ "solution": "move -50,-150"
+ },
+ {
+ "hint": "We draw a line down the centre to help the simple shape look more like a ship, early videogames had only a fraction of computing power we are used to today, so graphics had to be basic. **type** `line 0,125`",
+ "solution": "line 0,125"
+ }
+ ],
+ "completion_text": "It might not look like much but simple graphics like this were where videogames we know today began and games like this lay the foundations for the 3D graphics we're used to today.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "ship-golden.png",
+ "ship-thrust.png",
+ "ship-sleek.png"
+ ]
+ },
+ "cover": "pixel-ship.png",
+ "guide": "#### New words\n**move** x, y | move 50, 25\n\nMoves the drawing cursor from the current position. The difference from moveTo is that this is relative, not absolute. `move 50, 25` will move the cursor 50 to the right, and 25 down from the current position, as opposed to from the top-left corner. The first value is the x value, which controls how far across horizontally the x part of the coordinate should go. The second value is the y coordinate, which controls how far down vertically the y coordinate should go. Negative values will send it in the opposite direction.\n\n**line** x, y | line 50, 25\n\nDraws a line from the current position to a relative position. The x, y coordinates control the point relative to the cursor where the line will be drawn to.\n\n**stroke** color, width | stroke white, 5\n\nSets the drawing stroke. All following shapes and text will be drawn with the stroke color and width set to this until you change it again. This function is smart and can accept arguments in any order, or only one at a time.\n\n#### What you'll make\n1. Asteroids (1979) was one of the first major arcade games. It was set in space so we're going to need to set the scene **type** `background black`\n2. The graphics in Asteroids were simple line based graphics called vectors, we still use a more complex version of this concept in computer graphics today. First we need to set our line color and width **type** `stroke white,5`\n3. Next we use the move function to set the starting point for our first line. **type** `move 0, -70`\n4. We use the line function to draw a line relative to our starting position, we use a negative number first to move to the left and a positive number second to move downward **type** `line -50,150`\n5. To move our cursor to the start of the next line we feed the previous coordinates into the move function. **type** `move -50,150`\n6. We draw a line inwards and upwards to form the thruster of the ship **type** `line 50,-25`\n7. Again moving to the same coordinates for a continuous line **type** `move 50,-25`\n8. We replicate the shape for the right side of the ship by reversing the second coordinate to draw downwards instead of up **type** `line 50,25`\n9. Follow through with the line again **type** `move 50,25`\n10. Our ship starts to take shape **type** `line -50,-150`\n11. Finally we move back to our starting point to add a final touch **type** `move -50,-150`\n12. We draw a line down the centre to help the simple shape look more like a ship, early videogames had only a fraction of computing power we are used to today, so graphics had to be basic. **type** `line 0,125`\n\n#### What you’ll hack\nIt might not look like much but graphics like this are where the videogames we know today began. Games like this lay the foundations for the 3D graphics we're used to today. You can turn your spaceship into a variety of different things by customising the stroke color and width. \n\nFor something more advanced, try drawing more lines.\n\n#### Briefing\nAsteroids (1979) was one of the first major arcade games. Limited by primitive drawing hardware, the game designers could only draw using simple line based graphics called vectors, we still use a more complex version of this concept in computer graphics today.\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/steve.json b/lib/challenges/locales/ja/worlds/pixelhack/steve.json
new file mode 100644
index 000000000..ed0339cc0
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/steve.json
@@ -0,0 +1,127 @@
+{
+ "id": "steve",
+ "title": "Steve",
+ "description": "Make Steve's face with simple shapes.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We are going to just be using solid squares and rectangles, so lets turn our stroke off with `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the background to lightbrown",
+ "solution": "background lightbrown"
+ },
+ {
+ "hint": "Set the drawing color for the hair with `color brown`.",
+ "solution": "color brown"
+ },
+ {
+ "hint": "The hair starts in the top-left corner, so **type** `moveTo 0, 0`.",
+ "solution": "moveTo 0, 0"
+ },
+ {
+ "hint": "We’ll draw a rectangle, which takes a width and height parameter. **Type** `rectangle 500, 150`",
+ "solution": "rectangle 500, 150"
+ },
+ {
+ "hint": "Set the drawing color to lightbrown**type** `color lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Move into position for the forehead, **type** `moveTo 100, 100`",
+ "solution": "moveTo 100, 100"
+ },
+ {
+ "hint": "Now to draw the forehead, **type** `rectangle 300, 50`",
+ "solution": "rectangle 300, 50"
+ },
+ {
+ "hint": "For the eyes, set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Now let’s move with `moveTo 100, 200`",
+ "solution": "moveTo 100, 200"
+ },
+ {
+ "hint": "Draw the eye with `square 50`",
+ "solution": "square 50"
+ },
+ {
+ "hint": "For the other eye, **type** `moveTo 350, 200`",
+ "solution": "moveTo 350, 200"
+ },
+ {
+ "hint": "And another square of size 50",
+ "solution": "square 50"
+ },
+ {
+ "hint": "For the iris we’ll use the color purple.",
+ "solution": "color purple"
+ },
+ {
+ "hint": "Move to `150, 200`.",
+ "solution": "moveTo 150, 200"
+ },
+ {
+ "hint": "Draw a square of size 50.",
+ "solution": "square 50"
+ },
+ {
+ "hint": "Move to `300, 200`.",
+ "solution": "moveTo 300, 200"
+ },
+ {
+ "hint": "And draw the final iris with a square of size 50.",
+ "solution": "square 50"
+ },
+ {
+ "hint": "We’ll draw the nose next. **Type** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Move into position with `moveTo 200, 250`.",
+ "solution": "moveTo 200, 250"
+ },
+ {
+ "hint": "Draw out a rectangle of width `100` and height `50`.",
+ "solution": "rectangle 100, 50"
+ },
+ {
+ "hint": "For the mouth set the drawing color to `saddlebrown`.",
+ "solution": "color saddlebrown"
+ },
+ {
+ "hint": "Move to `150, 300`",
+ "solution": "moveTo 150, 300"
+ },
+ {
+ "hint": "The mouth should be wide rectangle. **Type** `200, 100`.",
+ "solution": "rectangle 200, 100"
+ },
+ {
+ "hint": "To make the mouth just right we need to cut out a bit of the top. Set the drawing color to the same color as the skin. **Type** `color lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Move to `200, 300`.",
+ "solution": "moveTo 200, 300"
+ },
+ {
+ "hint": "Finish up with `rectangle 100, 50` ",
+ "solution": "rectangle 100, 50"
+ }
+ ],
+ "completion_text": "Well done! Our character is looking good. What parts can you change to make the character look more like you?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "Steve-blue.png",
+ "Steve-beard.png",
+ "Steve-Alex.png"
+ ]
+ },
+ "cover": "pixel-steve.png",
+ "guide": "#### What you'll make\n1. We are going to just be using solid squares and rectangles, so lets turn our stroke off with `stroke 0`\n2. Set the background to lightbrown\n3. Set the drawing color for the hair with `color brown`.\n4. The hair starts in the top-left corner, so **type** `moveTo 0, 0`.\n5. We’ll draw a rectangle, which takes a width and height parameter. **Type** `rectangle 500, 150`\n6. Set the drawing color to lightbrown**type** `color lightbrown`\n7. Move into position for the forehead, **type** `moveTo 100, 100`\n8. Now to draw the forehead, **type** `rectangle 300, 50`\n9. For the eyes, set the color to white\n10. Now let’s move with `moveTo 100, 200`\n11. Draw the eye with `square 50`\n12. For the other eye, **type** `moveTo 350, 200`\n13. And another square of size 50\n14. For the iris we’ll use the color purple.\n15. Move to `150, 200`.\n16. Draw a square of size 50.\n17. Move to `300, 200`.\n18. And draw the final iris with a square of size 50.\n19. We’ll draw the nose next. **Type** `color brown`\n20. Move into position with `moveTo 200, 250`.\n21. Draw out a rectangle of width `100` and height `50`.\n22. For the mouth set the drawing color to `saddlebrown`.\n23. Move to `150, 300`\n24. The mouth should be wide rectangle. **Type** `200, 100`.\n25. To make the mouth just right we need to cut out a bit of the top. Set the drawing color to the same color as the skin. **Type** `color lightbrown`\n26. Move to `200, 300`.\n27. Finish up with `rectangle 100, 50` \n\n#### What you’ll hack\nThe shapes here are simple, but that is what makes it so easy to remix and hack! Change the colors of the skin, eyes, or hair, or add in your own facial features to win!\n\n#### Briefing\nSteve is the hero of Minecraft. Not very much is known about Steve: who he is, where he came from, and whether he is even human. But he is the face of the most sensational game and building system of the decade. This simple composition with just squares and rectangles is brought to life with beautiful colors.\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/sword.json b/lib/challenges/locales/ja/worlds/pixelhack/sword.json
new file mode 100644
index 000000000..bc5491c80
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/sword.json
@@ -0,0 +1,96 @@
+{
+ "id": "sword",
+ "title": "Diamond Sword",
+ "description": "Code a sword forged from pure diamonds",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We're going to make a sword out of diamond so first we will set up some variables to represent the colors **type** `sword = aquamarine`",
+ "solution": "sword = aquamarine"
+ },
+ {
+ "hint": "We set up enough variable to represent the darker areas of the sword, we can use the darken function to alter the color of our existing sword color. **type** `darksword = darken sword,40`",
+ "solution": "darksword = darken sword,40"
+ },
+ {
+ "hint": "We start by setting the `background` color to slategray",
+ "solution": "background slategray"
+ },
+ {
+ "hint": "Next we set our fill color to the variable we set up before **type** `color sword`",
+ "solution": "color sword"
+ },
+ {
+ "hint": "and set our stroke color to the darker version of our sword color **type** `stroke darksword,20`",
+ "solution": "stroke darksword,20"
+ },
+ {
+ "hint": "Move the cursor to where the top of the blade will be **type** `move -20,-180`",
+ "solution": "move -20,-180"
+ },
+ {
+ "hint": "Draw the blade of the sword **type** `rectangle 40,230`",
+ "solution": "rectangle 40,230"
+ },
+ {
+ "hint": "It's already starting to take shape, let's create the handle **type** `move 10,250`",
+ "solution": "move 10,250"
+ },
+ {
+ "hint": "The grip isn't going to be made of diamond so set the `stroke` color to darkbrown",
+ "solution": "stroke darkbrown"
+ },
+ {
+ "hint": "and the `color` to brown",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw a `rectangle` 20 by 100 for the grip",
+ "solution": "rectangle 20,100"
+ },
+ {
+ "hint": "We're going to create the crossguard next **type** `move -70,-10`",
+ "solution": "move -70,-10"
+ },
+ {
+ "hint": "Change the `stroke` color back to our darksword variable",
+ "solution": "stroke darksword"
+ },
+ {
+ "hint": "We're going to use another color function: lighten to slightly change our diamond color **type** `color lighten darksword,10`",
+ "solution": "color lighten darksword,10"
+ },
+ {
+ "hint": "Next draw a `rectangle` 160 by 20",
+ "solution": "rectangle 160,20"
+ },
+ {
+ "hint": "Notice how the lighten function altered the diamond color. **type** `move 80, -240`",
+ "solution": "move 80, -240"
+ },
+ {
+ "hint": "Finally we're going to add a highlight detail to finish the sword **type** `color white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Turn the stroke off **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Draw a `rectangle` 10 by 230",
+ "solution": "rectangle 10, 230"
+ }
+ ],
+ "completion_text": "That sword looks exquisite, perfect for fighting your way out of some caves. Try changing the color on the first line, see how it changes all the other colors on the blade because of the variables.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "sword-golden.png",
+ "sword-shovel.png",
+ "sword-pickaxe.png"
+ ]
+ },
+ "cover": "pixel-sword.png",
+ "guide": "#### New words\n**darken**(color, amount) | darken(blue, 40)\n\nReturns a lightened color by the amount you give it. The amount can be between 0 and 100.\n\n**lighten**(color, amount) | lighten(blue, 40)\n\nReturns a lightened color by the amount you give it. The amount can be between 0 and 100.\n\n\n#### What you'll make\n1. We're going to make a sword out of diamond so first we will set up some variables to represent the colors **type** `sword = aquamarine`\n2. We set up enough variable to represent the darker areas of the sword, we can use the darken function to alter the color of our existing sword color. **type** `darksword = darken sword,40`\n3. We start by setting the `background` color to slategray\n4. Next we set our fill color to the variable we set up before **type** `color sword`\n5. and set our stroke color to the darker version of our sword color **type** `stroke darksword,20`\n6. Move the cursor to where the top of the blade will be **type** `move -20,-180`\n7. Draw the blade of the sword **type** `rectangle 40,230`\n8. It's already starting to take shape, let's create the handle **type** `move 10,250`\n9. The grip isn't going to be made of diamond so set the `stroke` color to darkbrown\n10. and the `color` to brown\n11. Draw a `rectangle` 20 by 100 for the grip\n12. We're going to create the crossguard next **type** `move -70,-10`\n13. Change the `stroke` color back to our darksword variable\n14. We're going to use another color function: lighten to slightly change our diamond color **type** `color lighten darksword,10`\n15. Next draw a `rectangle` 160 by 20\n16. Notice how the lighten function altered the diamond color. **type** `move 80, -240`\n17. Finally we're going to add a highlight detail to finish the sword **type** `color white`\n18. Turn the stroke off **type** `stroke 0`\n19. Draw a `rectangle` 10 by 230\n\n#### What you’ll hack\nBecause all of the colors we used are just shades of our sword color, by changing one variable all the darker and lighter shades will change as well! We can make a diamond sword into a gold one by just changing the sword color.\n\nUsing the same code and style, what other Minecraft-inspired creations can you make?\n\n#### Briefing\nThe diamond sword is the most powerful weapon in the Minecraft players toolbelt. It is also the most difficult to make given the rarity of diamond. All of the different types of swords in Minecraft (diamond, gold, steel, stone…) follow a simple pattern in their design. The only thing that separates them visually is their color. For the diamond sword we’ll make a word called \"sword\" which is set to the color \"aquamarine\". To select different shades for the hilt and outlines, we’ll use the color with special color functions."
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/tetris.json b/lib/challenges/locales/ja/worlds/pixelhack/tetris.json
new file mode 100644
index 000000000..bb9cfbc9f
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/tetris.json
@@ -0,0 +1,96 @@
+{
+ "id": "tetris",
+ "title": "Tetris",
+ "description": "Stack some Tetris blocks with code",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Tetris (1984) was the first videogame to be exported from the USSR to the US, going on to sell 170 million copies. We're going to make some Tetris blocks, first turn the stroke off **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Next move your cursor to the start of the first block **type** `moveTo 150,200`",
+ "solution": "moveTo 150,200"
+ },
+ {
+ "hint": "We're going to need some colors to tell the blocks apart, change the fill color of your shapes with the color function **type** `color crimson`",
+ "solution": "color crimson"
+ },
+ {
+ "hint": "First we're going to draw an L shaped block **type** `rectangle 50,150`",
+ "solution": "rectangle 50,150"
+ },
+ {
+ "hint": "Our blocks are all going to be to a 50x50 grid, so you'll notice all our draw and move commands are multiples of 50 **type** `move 0,100`",
+ "solution": "move 0,100"
+ },
+ {
+ "hint": "Finish off the block with a 100,50 `rectangle`",
+ "solution": "rectangle 100,50"
+ },
+ {
+ "hint": "Let's move to start the next block **type** `move 50,-100`",
+ "solution": "move 50,-100"
+ },
+ {
+ "hint": "We'll change the color to tell them apart, color functions affect all shapes drawn below them in the code **type** `color seagreen`",
+ "solution": "color seagreen"
+ },
+ {
+ "hint": "Next we'll be drawing the S shaped block **type** `rectangle 50,100`",
+ "solution": "rectangle 50,100"
+ },
+ {
+ "hint": "Move one gridspace across and one down **type** `move 50,50`",
+ "solution": "move 50,50"
+ },
+ {
+ "hint": "To make an S shape we create another `rectangle` of 50 by 100",
+ "solution": "rectangle 50,100"
+ },
+ {
+ "hint": "On to the next block **type** `move 50,-100`",
+ "solution": "move 50,-100"
+ },
+ {
+ "hint": "We change the color again **type** `color indigo`",
+ "solution": "color indigo"
+ },
+ {
+ "hint": "Next we will draw a long and thin block, draw a `rectangle` 50 by 200",
+ "solution": "rectangle 50,200"
+ },
+ {
+ "hint": "One final block to complete the square **type** `move -150,0`",
+ "solution": "move -150,0"
+ },
+ {
+ "hint": "Let's make the `color` gold to stand out",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Another L block will fit nicely so draw the stem with **type** `rectangle 150,50`",
+ "solution": "rectangle 150,50"
+ },
+ {
+ "hint": "Move into place to draw the foot **type** `move 100,0`",
+ "solution": "move 100,0"
+ },
+ {
+ "hint": "Finally fill in the foot **type** `rectangle 50,100`",
+ "solution": "rectangle 50,100"
+ }
+ ],
+ "completion_text": "Well done you created a nice stack of tetrominoes, why not try changing the color of some of the blocks or drawing a few more to fill the screen.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "tetris-gameboy.png",
+ "tetris-3D.png",
+ "tetris-row.png"
+ ]
+ },
+ "cover": "pixel-tetris.png",
+ "guide": "#### New words\n**color** name | color red\n\nChanges the drawing color. All following shapes and text will be drawn with the fill color set to this color until you change it again. To set the drawing color to transparent, type `color null`\n\n#### What you'll make\n1. Tetris (1984) was the first videogame to be exported from the USSR to the US, going on to sell 170 million copies. We're going to make some Tetris blocks, first turn the stroke off **type** `stroke 0`\n2. Next move your cursor to the start of the first block **type** `moveTo 150,200`\n3. We're going to need some colors to tell the blocks apart, change the fill color of your shapes with the color function **type** `color crimson`\n4. First we're going to draw an L shaped block **type** `rectangle 50,150`\n5. Our blocks are all going to be to a 50x50 grid, so you'll notice all our draw and move commands are multiples of 50 **type** `move 0,100`\n6. Finish off the block with a 100,50 `rectangle`\n7. Let's move to start the next block **type** `move 50,-100`\n8. We'll change the color to tell them apart, color functions affect all shapes drawn below them in the code **type** `color seagreen`\n9. Next we'll be drawing the S shaped block **type** `rectangle 50,100`\n10. Move one gridspace across and one down **type** `move 50,50`\n11. To make an S shape we create another `rectangle` of 50 by 100\n12. On to the next block **type** `move 50,-100`\n13. We change the color again **type** `color indigo`\n14. Next we will draw a long and thin block, draw a `rectangle` 50 by 200\n15. One final block to complete the square **type** `move -150,0`\n16. Let's make the `color` gold to stand out\n17. Another L block will fit nicely so draw the stem with **type** `rectangle 150,50`\n18. Move into place to draw the foot **type** `move 100,0`\n19. Finally fill in the foot **type** `rectangle 50,100`\n\n#### What you’ll hack\nNow you’ve made a cube, give each tetrimino whatever color you want, and add in more.\n\n#### Briefing \nTetris (1984) was the first videogame to be exported from the USSR to the US, going on to sell 170 million copies. We're going to make some Tetris blocks, which are known as tetriminos. Each tetrimino is made up of four squares that connect together, and fit together in interesting ways. In the game, tetriminos fall down endlessly and the goal is to make as many of them fit together without empty spaces.\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/pixelhack/variables.json b/lib/challenges/locales/ja/worlds/pixelhack/variables.json
new file mode 100644
index 000000000..e0972ff3d
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/pixelhack/variables.json
@@ -0,0 +1,93 @@
+{
+ "id": "variables",
+ "title": "Variables",
+ "description": "Use variables to create a maze dwelling ghost",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "This classic ghost character first appeared in Pac-Man (1980), because it's a ghost we're going to need a spooky background **type** `background navy`",
+ "solution": "background navy"
+ },
+ {
+ "hint": "Next turn the stroke off by typing `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the `color` to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Here we're introducing something new, we're going to set the size of the ghost using a variable, variables are a way of repeating the same value in your code **type** `ghostsize = 100`",
+ "solution": "ghostsize = 100"
+ },
+ {
+ "hint": "Move the cursor up to where the ghosts head will start **type** `move 0,-50`",
+ "solution": "move 0,-50"
+ },
+ {
+ "hint": "We're going to use that variable we set earlier to draw the head, because we set the variable to 100 this command will create a circle 100 pixels big **type** `circle ghostsize`",
+ "solution": "circle ghostsize"
+ },
+ {
+ "hint": "We're going to use the variable again, this time with the move command, by placing a minus symbol in front of it this command becomes the same as moving -100,0 **type** `move -ghostsize,0`",
+ "solution": "move -ghostsize,0"
+ },
+ {
+ "hint": "Time to draw the rest of the ghost's body, because the body is relative to the head size we will use the variable again **type** `square ghostsize * 2`",
+ "solution": "square ghostsize * 2",
+ "validate": "square ghostsize \\* 2"
+ },
+ {
+ "hint": "We're going to give it some eyes so we need to use the variable again to make sure they're positioned correctly on the head **type** `move ghostsize / 2`",
+ "solution": "move ghostsize / 2"
+ },
+ {
+ "hint": "Set the `color` to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Start the eyes off with a `circle` 20 pixels big",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Now set the iris color **type** `color blue`",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Finally draw the iris **type** `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "We're gonna use the variable one last time to position the other eye correctly **type** `move ghostsize,0`",
+ "solution": "move ghostsize,0"
+ },
+ {
+ "hint": "Repeat the process for the second eye, set the `color` to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a `circle` 20 pixels big",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Set the `color` to blue",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Finally draw a `circle` 10 pixels big",
+ "solution": "circle 10"
+ }
+ ],
+ "completion_text": "Nice! Now because you have used a variable, you can click on the ghostsize number and use the slider to change the value. Notice how the eyes stay the same size because they are hardcoded numbers and everything else scales together.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "ghost-big.png",
+ "ghost-tired.png",
+ "ghost-scared.png"
+ ]
+ },
+ "cover": "pixel-variables.png",
+ "guide": "#### New words\n**variable** = value | ghostsize = 100\n\nCreates a word that is assigned a value and can be used elsewhere.\n\n#### What you'll make\n1. This classic ghost character first appeared in Pac-Man (1980), because it's a ghost we're going to need a spooky background **type** `background navy`\n2. Next turn the stroke off by typing `stroke 0`\n3. Set the `color` to red\n4. Here we're introducing something new, we're going to set the size of the ghost using a variable, variables are a way of repeating the same value in your code **type** `ghostsize = 100`\n5. Move the cursor up to where the ghosts head will start **type** `move 0,-50`\n6. We're going to use that variable we set earlier to draw the head, because we set the variable to 100 this command will create a circle 100 pixels big **type** `circle ghostsize`\n7. We're going to use the variable again, this time with the move command, by placing a minus symbol in front of it this command becomes the same as moving -100,0 **type** `move -ghostsize,0`\n8. Time to draw the rest of the ghost's body, because the body is relative to the head size we will use the variable again **type** `square ghostsize * 2`\n9. We're going to give it some eyes so we need to use the variable again to make sure they're positioned correctly on the head **type** `move ghostsize / 2`\n10. Set the `color` to white\n11. Start the eyes off with a `circle` 20 pixels big\n12. Now set the iris color **type** `color blue`\n13. Finally draw the iris **type** `circle 10`\n14. We're gonna use the variable one last time to position the other eye correctly **type** `move ghostsize,0`\n15. Repeat the process for the second eye, set the `color` to white\n16. Draw a `circle` 20 pixels big\n17. Set the `color` to blue\n18. Finally draw a `circle` 10 pixels big\n\n#### What you’ll hack\nBecause you used a variable for all the different versions of the ghost’s body, you can change it once to see how the ghost’s body resizes! If you notice, the eyes position change with the ghostsize variable, but the eyes’ size don’t. Could you set up another variable that controls the size of the eyes?\n\n#### Briefing\nWhen you write a computer program you are telling a computer to do a series of tasks. And when you run the program, the computer reads all of the tasks and does them. So far we have written programs that will run the same way every single time you do the program, but what if we want the program to be different every time we run the program? If we wanted to vary the way in which the program works every time it was run, we need to use variables.\n\nA variable is a word that takes on the meaning of something else. And when you use it somewhere else it will pass on the word it means. In this challenge you will base the size of many of the ghost’s parts off of a variable. Changing the variable with a slider will then affect all of the different individual shapes sizes, giving you the power to make the whole ghost bigger or smaller at once!\n"
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/backpack.json b/lib/challenges/locales/ja/worlds/summercamp/backpack.json
new file mode 100644
index 000000000..fdb455022
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/backpack.json
@@ -0,0 +1,143 @@
+{
+ "id": "backpack",
+ "title": "Pack your things",
+ "short_title": "Backpack",
+ "icon_class": "challenge_backpack",
+ "description": "Dick Kelty is the inventor of the aluminium frame backpack. Kelty got the idea for his backpack in 1951 when he and a friend were hiking in the Sierra Nevada. Working in his garage, and with $500 borrowed against his house, Kelty and his wife went into production; he cut and welded the aluminium frames, while she stitched the nylon. They sold the finished backpacks for $24 apiece.",
+ "cover": "summercamp/day_8.png",
+ "completion_text": "I have a big challenge for you: try drawing your character behind that backpack! Head, arms, legs… ",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "It's a beautiful day to go for a walk in the forest - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Firstly, set the `stroke` to `0`, to avoid drawing the outlines of the shapes",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to place the floor - in a new line **type** `moveTo 0, 250`",
+ "solution": "moveTo 0, 250"
+ },
+ {
+ "hint": "Set the `color` of the grass to `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Draw the floor with a rectangle - **type** `rectangle 500, 250`",
+ "solution": "rectangle 500, 250"
+ },
+ {
+ "hint": "This looks like a nice place for a tree - **type** `moveTo 220`",
+ "solution": "moveTo 220"
+ },
+ {
+ "hint": "Set the `color` of the trunk to `lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Draw the trunk with a `rectangle` of size `50` by `400`",
+ "solution": "rectangle 50, 400"
+ },
+ {
+ "hint": "Beautiful! Now it just needs some leaves - **type** `move 30, -120` to place them",
+ "solution": "move 30, -120"
+ },
+ {
+ "hint": "Set the `color` of the leaves to `darkgreen`",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Draw a `circle` to represent the leaves with size `200`",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Looking great! Let's move the cursor to place our backpack - **type** `moveTo 150, 200`",
+ "solution": "moveTo 150, 200"
+ },
+ {
+ "hint": "Let's choose `color` `brown` for our backpack",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw the main part of the rucksack with a square - **type** `square 200`",
+ "solution": "square 200"
+ },
+ {
+ "hint": "Now move the cursor to bottom part of our backpack - **type** `moveTo 250, 400`",
+ "solution": "moveTo 250, 400"
+ },
+ {
+ "hint": "Draw the bottom using an ellipse - **type** `ellipse 100, 30`",
+ "solution": "ellipse 100, 30"
+ },
+ {
+ "hint": "We need to store the sleeping bag at the top - **type** `moveTo 250, 200`",
+ "solution": "moveTo 250, 200"
+ },
+ {
+ "hint": "Set the `color` of the sleeping bag to `darkred`",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "Ready to draw the sleeping bag? Use an ellipse - **type** `ellipse 115, 40`",
+ "solution": "ellipse 115, 40"
+ },
+ {
+ "hint": "We need something to secure your sleeping bag to your backpack - **type** `moveTo 200, 160`",
+ "solution": "moveTo 200, 160"
+ },
+ {
+ "hint": "The `orangered` seems like a nice `color` for the holders",
+ "solution": "color orangered"
+ },
+ {
+ "hint": "Draw the left one first - **type** `rectangle 15, 90`",
+ "solution": "rectangle 15, 90"
+ },
+ {
+ "hint": "Position the cursor to the right to draw the next holder - **type** `moveTo 290, 160`",
+ "solution": "moveTo 290, 160"
+ },
+ {
+ "hint": "Use a `rectangle` again for the right holder, same as the previous one",
+ "solution": "rectangle 15, 90"
+ },
+ {
+ "hint": "Excellent! We need more storage space. Move the cursor to the middle - **type** `moveTo 205, 320`",
+ "solution": "moveTo 205, 320"
+ },
+ {
+ "hint": "Use a `rectangle` for the outside pocket, `90` by `60` will suffice",
+ "solution": "rectangle 90, 60"
+ },
+ {
+ "hint": "That pocket needs to be closed so bugs can't get in - **type** `moveTo 250, 320`",
+ "solution": "moveTo 250, 320"
+ },
+ {
+ "hint": "Set the `color` to `lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "An `ellipse` of size `48` by `7` will help here",
+ "solution": "ellipse 48, 7"
+ },
+ {
+ "hint": "Looking great! Let’s place a button on that pocket - **type** `moveTo 250, 330`",
+ "solution": "moveTo 250, 330"
+ },
+ {
+ "hint": "Set the `color` to `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw a `circle` button of size `10`",
+ "solution": "circle 10"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/bear.json b/lib/challenges/locales/ja/worlds/summercamp/bear.json
new file mode 100644
index 000000000..3282e8b5c
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/bear.json
@@ -0,0 +1,91 @@
+{
+ "id": "bear",
+ "title": "Cheeky Bear",
+ "short_title": "Bear",
+ "description": "Bears are very smart and have been known to roll rocks into bear traps to set off the trap and then eat the bait in safety. These animals can run up to 40 miles per hour, fast enough to catch a running horse. Take into account that the fastest known human alive today is Usain Bolt, who can run at 27mph! And because bears can walk short distances on their hind legs, some Native Americans called them “the beast that walks like a man.”",
+ "icon_class": "challenge_bear",
+ "cover": "summercamp/day_11.png",
+ "completion_text": "That cute bear needs a body, and a habitat. Use your code skills to impress other campers with your abilities! Remember that the icons on your left can help you achieve what you have in mind.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "The bear lives in the forest. Set the background color to green - **type** `background green`",
+ "solution": "background green"
+ },
+ {
+ "hint": "The bear has brown fur, let's start to draw his face by getting our paint ready - **type** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Draw the face with a circle - **type** `circle 100`",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Now let's move the cursor to draw his left ear - **type** `move -80, -80`",
+ "solution": "move -80, -80"
+ },
+ {
+ "hint": "Draw the ear with a `circle` of size `30`",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Move the cursor to the right to draw his other ear - **type** `move 160`",
+ "solution": "move 160"
+ },
+ {
+ "hint": "Draw another ear (just like the last one)",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Now head over to where the left eye will go - **type** `move -110, 50`",
+ "solution": "move -110, 50"
+ },
+ {
+ "hint": "The rest of the bear's facial features will be `black`, so lets set the `color`",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw the first eye - **type** `circle 5`",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Next move over to where the other eye goes - **type** `move 60`",
+ "solution": "move 60"
+ },
+ {
+ "hint": "Fill in the other eye just like the last one",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Move to the center of the face for the bear's most distinctive feature - **type** `move -30, 50`",
+ "solution": "move -30, 50"
+ },
+ {
+ "hint": "Draw his nose with a triangle using polygon - **type** `polygon 12, -16, -12, -16`",
+ "solution": "polygon 12, -16, -12, -16"
+ },
+ {
+ "hint": "Let's get ready to draw the mouth by moving down a bit further - **type** `move 0, 20`",
+ "solution": "move 0, 20"
+ },
+ {
+ "hint": "Set the `stroke` (the outline of a shape) to color `black` and size `3`",
+ "solution": "stroke black, 3"
+ },
+ {
+ "hint": "We don't want the mouth to be filled so set its color to null - **type** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "An arc is part of a cirlce, just like a smiling mouth - **type** `arc 15, 0.5, 2`",
+ "solution": "arc 15, 0.5, 2"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/bow_and_arrow.json b/lib/challenges/locales/ja/worlds/summercamp/bow_and_arrow.json
new file mode 100644
index 000000000..73a61d1f7
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/bow_and_arrow.json
@@ -0,0 +1,107 @@
+{
+ "id": "bow_and_arrow",
+ "title": "Bow and Arrow",
+ "short_title": "Bow and Arrow",
+ "icon_class": "challenge_bow",
+ "description": "Shooting a bow and arrow at a target is an art that requires lots of focus. This challenge is to draw a bow and try to hold it steady while your hand jitters...just like the other humans who have used the bow and arrow for hunting since the dawn of civilization over ten thousand years ago. The oldest known bow, found in Denmark, dates from 9000 BCE!",
+ "cover": "summercamp/day_15.png",
+ "completion_text": "Click Refresh and watch the target move while you try to hold your bow steady! Try getting rid of the moving background to make it easier to hit the target, converting your bow to a crossbow, or drawing an arrow in the bullseye.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "The weather is good today...there is no wind to blow our arrows off course. Set the background color to blue - **type** `background blue`.",
+ "solution": "background blue"
+ },
+ {
+ "hint": "We want the background to jitter, so we will start drawing it at a random point. **Type** `moveTo (random -20, 0), (random 240, 260)`.",
+ "solution": "moveTo (random -20, 0), (random 240, 260)"
+ },
+ {
+ "hint": "Our target is in a grassy field, so lets get our `color green` ready",
+ "solution": "color green"
+ },
+ {
+ "hint": "Draw a big `rectangle` that is `550` pixels high and `300` pixels wide for the grass",
+ "solution": "rectangle 550, 300"
+ },
+ {
+ "hint": "Now let's move the cursor to draw our target - **type** `move 275, 10`",
+ "solution": "move 275, 10"
+ },
+ {
+ "hint": "First we will draw the wooden legs supporting it - **type** `stroke brown, 15`",
+ "solution": "stroke brown, 15"
+ },
+ {
+ "hint": "Draw the left leg - **type** `line -80, 120`",
+ "solution": "line -80, 120"
+ },
+ {
+ "hint": "...and now draw the right leg - **type** `line 80, 120`",
+ "solution": "line 80, 120"
+ },
+ {
+ "hint": "We will draw a target with white and red rings - **type** `stroke white, 50`",
+ "solution": "stroke white, 50"
+ },
+ {
+ "hint": "Now make the inside of the circle red - **type** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "First, draw a circle with a radius of 80 pixels- **type** `circle 80`",
+ "solution": "circle 80"
+ },
+ {
+ "hint": "Second, draw a `circle` with a radius of `30` pixels",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Move the cursor to draw an arrowhead - **type** `moveTo 250, 220`",
+ "solution": "moveTo 250, 220"
+ },
+ {
+ "hint": "Our arrowhead will have a thin gray outline - **type** `stroke gray, 1`",
+ "solution": "stroke gray, 1"
+ },
+ {
+ "hint": "The flint arrowhead is going to have the `color dimgray`",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Draw the arrowhead as a diamond - **type** `polygon -10, 40, 0, 80, 10, 40`",
+ "solution": "polygon -10, 40, 0, 80, 10, 40"
+ },
+ {
+ "hint": "Next we will move to draw the shaft of the arrow - **type** `move 0, 40`",
+ "solution": "move 0, 40"
+ },
+ {
+ "hint": "The arrow will be made of a light brown wood - **type** `stroke lightbrown, 20`",
+ "solution": "stroke lightbrown, 20"
+ },
+ {
+ "hint": "Draw a line for the shaft - **type** `line 200, 360`",
+ "solution": "line 200, 360"
+ },
+ {
+ "hint": "The last thing we will draw is our bow. Move the cursor off the screen - **type** `moveTo 800, 375`",
+ "solution": "moveTo 800, 375"
+ },
+ {
+ "hint": "We are going to draw an arc for the bow so let's draw it with a thick piece of dark brown wood - **type** `stroke darkbrown, 40`",
+ "solution": "stroke darkbrown, 40"
+ },
+ {
+ "hint": "We don't want the arc to have any fill - **type** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "Draw the bow with a big arc centered far off the screen - **type** `arc 500, 0, 2`",
+ "solution": "arc 500, 0, 2"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/camera.json b/lib/challenges/locales/ja/worlds/summercamp/camera.json
new file mode 100644
index 000000000..0a53047ee
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/camera.json
@@ -0,0 +1,109 @@
+{
+ "id": "camera",
+ "title": "Take a pic!",
+ "short_title": "Camera",
+ "icon_class": "challenge_camera",
+ "description": "Back in the 1820s, early cameras would take several hours to actually capture a film! With annoyingly long exposure hours, photographing people with a nice smile was not really possible. Why? Try and hold a convincing smile while staying perfectly still for hours and you will get your answer. Today, the number of photographs clicked every two minutes is same as the number of photographs clicked by mankind in 1800s.",
+ "cover": "summercamp/day_10.png",
+ "completion_text": "Try drawing yourself taking the picture behind that beautiful camera. Perhaps a light for the flash as well?",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "Set the `background` color to `blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set the `stroke` to `0` for now, to avoid drawing the outline of the shapes",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to place the camera in the center - **type** `moveTo 100, 150`",
+ "solution": "moveTo 100, 150"
+ },
+ {
+ "hint": "Set the `color` of the camera to `dimgray`",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Now draw the body of the camera with a rectangle - **type** `rectangle 300, 200`",
+ "solution": "rectangle 300, 200"
+ },
+ {
+ "hint": "Move the cursor to draw the lense - **type** `move 150, 100`",
+ "solution": "move 150, 100"
+ },
+ {
+ "hint": "Set the `color` of the lens to `lightblue`",
+ "solution": "color lightblue"
+ },
+ {
+ "hint": "Set the outline of the lens with a `stroke` of `black` color and size `10`",
+ "solution": "stroke black, 10"
+ },
+ {
+ "hint": "The lens is a `circle` with size `50`",
+ "solution": "circle 50"
+ },
+ {
+ "hint": "Set the `color` to `lightgray` for the second half of the lens",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Draw an arc that matches the top half of the lens - **type** `arc 50, 2, 1`",
+ "solution": "arc 50, 2, 1"
+ },
+ {
+ "hint": "This camera needs a flash! Move the cursor to place it - **type** `move -30, -125`",
+ "solution": "move -30, -125"
+ },
+ {
+ "hint": "Draw the flash of the camera with a `rectangle` of size `60` by `20`",
+ "solution": "rectangle 60, 20"
+ },
+ {
+ "hint": "Set the `stroke` back to `0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the `color` of the flash to `lightblue`",
+ "solution": "color lightblue"
+ },
+ {
+ "hint": "Draw a polygon for the reflection on the flash - **type** `polygon 40, 0, 0, 20`",
+ "solution": "polygon 40, 0, 0, 20"
+ },
+ {
+ "hint": "Only the button to shoot the picture is left - **type** `move 100, 10`",
+ "solution": "move 100, 10"
+ },
+ {
+ "hint": "Set the `color` of the button to `red`.",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw the button of the camera with a `rectangle` of size `50` by `15`",
+ "solution": "rectangle 50, 15"
+ },
+ {
+ "hint": "One more detail! Move the cursor one last time - **type** `move 25, -20`",
+ "solution": "move 25, -20"
+ },
+ {
+ "hint": "Set the `color` to `yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Set the `font` to `'ComicSansMS'` and size `30`",
+ "solution": "font 'ComicSansMS', 30"
+ },
+ {
+ "hint": "Write some text - **type** `text 'Click!'`",
+ "solution": "text 'Click!'"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/camp_badge.json b/lib/challenges/locales/ja/worlds/summercamp/camp_badge.json
new file mode 100644
index 000000000..f682de7bc
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/camp_badge.json
@@ -0,0 +1,56 @@
+{
+ "id": "camp_badge",
+ "title": "Camp Badge",
+ "short_title": "Camp Badge",
+ "icon_class": "challenge_badge",
+ "description": "First Aid is the most popular merit badge. In fact, 6.9 million Scouts have earned it since its debut in 1911. Other popular ones are Environmental Science, Search and Rescue, and our favourites Programming and Game Design! There are some oddballs as well. For example Truck transportation: “Assume that you are going to ship by truck 500 pounds of goods (freight class 65) from your town to another town 500 miles away. Your shipment must arrive within three days. Explain in writing…”. And Nuclear Science “Obtain a sample of irradiated and non-irradiated foods. Prepare the two foods and compare their taste and texture.”",
+ "completion_text": "This badge will distinguish your camp from others. Surprise the world with an amazing creation.",
+ "cover": "summercamp/day_5.png",
+ "difficulty": 1,
+ "startAt": 4,
+ "start_date": "2015-10-07 06:00:00",
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Set the `background` color to `black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "Set the `color` of your badge to `lightblue`",
+ "solution": "color lightblue"
+ },
+ {
+ "hint": "Set the stroke for the first circle - **type** `stroke red, 20`",
+ "solution": "stroke red, 20"
+ },
+ {
+ "hint": "Draw a `circle` with a size of `200`",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Set the stroke for the second circle - **type** `stroke yellow, 20`",
+ "solution": "stroke yellow, 20"
+ },
+ {
+ "hint": "Draw a `circle` with a size of `190`",
+ "solution": "circle 190"
+ },
+ {
+ "hint": "Set the `stroke` for the third circle to `purple` color and size `20`",
+ "solution": "stroke purple, 20"
+ },
+ {
+ "hint": "Draw a `circle` with a size of `180`",
+ "solution": "circle 180"
+ },
+ {
+ "hint": "Add some decoration inside. Set the `color` to `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "An arc is part of a cirlce, draw one that matches the inner circle - **type** `arc 180, 1, 2`",
+ "solution": "arc 180, 1, 2"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/camp_sign.json b/lib/challenges/locales/ja/worlds/summercamp/camp_sign.json
new file mode 100644
index 000000000..7b952989f
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/camp_sign.json
@@ -0,0 +1,87 @@
+{
+ "id": "camp_sign",
+ "title": "Your Summer Camp sign",
+ "short_title": "Camp Sign",
+ "description": "Welcome to your first day! Before you set out from the lodge, your campsite sign needs a design. Be creative! Use a crazy color scheme, or show what you want to do at camp. \nIn the Pacific Northwest of the United States, indigenous peoples carved Totem Poles to tell the stories of their families and cultures. These beautiful sculptures are carved into trees and often feature characters and events from legends. You can search the internet for images of Totem Poles for inspiration.",
+ "icon_class": "challenge_campsign",
+ "cover": "summercamp/day_1.png",
+ "completion_text": "Well done! Now give your camp its own name and style. Change your camp name on line 18, or the color of the sign on line 3... why not add a nice background and some extra decorations? All those tool icons on the left can help you get started.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "It's a sunny day! Set the background color to blue - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Let's move the cursor over to the place on the screen where we want to start drawing our sign.\n\nPress ENTER to add the next instruction, and - **type** `moveTo 100, 150`",
+ "solution": "moveTo 100, 150"
+ },
+ {
+ "hint": "In Make Art we draw shapes using digital pens. Before we draw the next shape, we need to choose the pen we want to use to fill the inside of the shape. Let's select a brown pen by setting the color to lightbrown.\n\n In a new line **type** `color lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "When we draw a shape we can control how thick the line is that the pen leaves behind on the outside of the shape. This line is called the **stroke**.\n\n Press enter and then set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Now that we have our pen all set up, we can get started on the sign!\n\nDraw the main billboard with a rectangle - **type** `rectangle 300, 200`",
+ "solution": "rectangle 300, 200"
+ },
+ {
+ "hint": "Let's draw a shadow. Set the color to brown first - **type** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw a thin shadow with a rectangle - **type** `rectangle 300, 5`",
+ "solution": "rectangle 300, 5"
+ },
+ {
+ "hint": "That sign looks too clean. We need to draw some imperfections.\n\nMove the cursor to place one on the left side of the sign- **type** `moveTo 100, 220`",
+ "solution": "moveTo 100, 220"
+ },
+ {
+ "hint": "Our imperfection will be a triangle. First we need to set the `color` to `blue` to match the background.",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Set the stroke to be size 5 and brown (to match the sign). You can do both these things in one command - **type** `stroke brown, 5`",
+ "solution": "stroke brown, 5"
+ },
+ {
+ "hint": "Now we will use the `polygon` command to draw a triangular imperfection. To create it, we need to list out the positions of the two other corners - **type** `polygon 16, 10, 0, 11`",
+ "solution": "polygon 16, 10, 0, 11"
+ },
+ {
+ "hint": "The sign needs a post. Move the cursor to where we will draw it - **type** `moveTo 230, 350`",
+ "solution": "moveTo 230, 350"
+ },
+ {
+ "hint": "Set the color of the post to brown - **type** `color brown` ",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Set the stroke to 0, again - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Now that we have the colors sorted, draw the post with a rectangle - **type** `rectangle 40, 150`",
+ "solution": "rectangle 40, 150"
+ },
+ {
+ "hint": "We need some text in that sign. Move the cursor to place the text - **type** `moveTo 250, 270`",
+ "solution": "moveTo 250, 270"
+ },
+ {
+ "hint": "Next we need to choose our font. There are lots to choose from, like `Bariol`, `Georgia`, `Comic Sans MS`, `cursive`, and `Courier`.\n\nPick your favorite and type it in (inside of quote marks) like this - `font 'Bariol', 70`.",
+ "solution": "font 'Bariol', 70"
+ },
+ {
+ "hint": "Now write some silly text - **type** `text 'My Camp'`",
+ "solution": "text 'My Camp'"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/campfire.json b/lib/challenges/locales/ja/worlds/summercamp/campfire.json
new file mode 100644
index 000000000..3b311f9ec
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/campfire.json
@@ -0,0 +1,90 @@
+{
+ "id": "campfire",
+ "title": "Campfire",
+ "short_title": "Campfire",
+ "description": "Time to get a fire going. In the wilderness, this very well might be your only source of light, and heat. Burning at over a thousand degrees fahrenheit, this will be sure to keep you warm, and could keep you fed too! Meats, vegetables, and marshmallows can all be cooked over the open flame with the help of sharpened sticks. \nMost fires are built with wood logs, but yours is going to be built with code and a “loop.” As your fire grows upwards its colour changes into a deep shade of orange as the oxygen it is burning starts to cools down. Your code will produce hundreds of colours with just a few lines of code.",
+ "icon_class": "challenge_fire",
+ "completion_text": "Try adding a starry sky, more flames, a ring of rocks to make it safer or even marshmallows!",
+ "difficulty": 2,
+ "cover": "summercamp/day_6.png",
+ "startAt": 5,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "It's a dark night. Set the `background` color to `darkblue`",
+ "solution": "background darkblue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to place the floor - **type** `moveTo 0, 300`",
+ "solution": "moveTo 0, 300"
+ },
+ {
+ "hint": "Set the `color` to `darkgreen`",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Draw the grass using a rectangle - **type** `rectangle 500, 200`",
+ "solution": "rectangle 500, 200"
+ },
+ {
+ "hint": "Move the cursor to place the light casted by the fire - **type** `moveTo 250, 400`",
+ "solution": "moveTo 250, 400"
+ },
+ {
+ "hint": "Set the `color` to `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Draw the light using an ellipse - **type** `ellipse 200, 30`",
+ "solution": "ellipse 200, 30"
+ },
+ {
+ "hint": "We need some wood to feed the fire - **type** `moveTo 110, 390`",
+ "solution": "moveTo 110, 390"
+ },
+ {
+ "hint": "Set the stroke for the logs - **type** `stroke brown, 25`",
+ "solution": "stroke brown, 25"
+ },
+ {
+ "hint": "Good, now draw the first log - **type** `line 270, 30`",
+ "solution": "line 270, 30"
+ },
+ {
+ "hint": "Move the cursor to place the second log - **type** `moveTo 420, 390`",
+ "solution": "moveTo 420, 390"
+ },
+ {
+ "hint": "Draw the second log - **type** `line -290, 30`",
+ "solution": "line -290, 30"
+ },
+ {
+ "hint": "Excellent! We are now ready to light the fire - **type** `moveTo 180, 400`",
+ "solution": "moveTo 180, 400"
+ },
+ {
+ "hint": "Our fire will be formed by 150 lines! - **type** `for i in [ 1 .. 150 ]`",
+ "solution": "for i in [ 1 .. 150 ]"
+ },
+ {
+ "hint": "Set the thickness and color of each line - **type** `stroke 'rgba(255, ' + (i + 50) + ', 0, 0.3)', 10`",
+ "solution": " stroke 'rgba(255, ' + (i + 50) + ', 0, 0.3)', 10",
+ "validate": "__^ stroke 'rgba*\\(255, ' \\+ \\(i *\\+ *50\\) *\\+ *', *0, *0.3\\)', 10"
+ },
+ {
+ "hint": "Now draw the line - **type** `line 150 - i`",
+ "solution": " line 150 - i"
+ },
+ {
+ "hint": "Move the cursor for next line - **type** `move 0.5, -2`",
+ "solution": " move 0.5, -2"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/campsite.json b/lib/challenges/locales/ja/worlds/summercamp/campsite.json
new file mode 100644
index 000000000..abce584fa
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/campsite.json
@@ -0,0 +1,97 @@
+{
+ "id": "campsite",
+ "title": "Draw your Campsite",
+ "short_title": "Campsite",
+ "description": "Time to find a clearing to set up camp. The most important thing to look for when searching for an optimal campsite is flat ground. If possible, keep your campsite close to a water source. This will make cooking and cleanup much easier. Finally, make sure to be surrounded by trees, as they are a great source of kindling and will provide protection from the sun. \nYour friends will want to see what your campsite looks like, so once you have one, draw it! You can use code to set the scene for where you are staying for the next two weeks.",
+ "icon_class": "challenge_location",
+ "completion_text": "The camp site is looking great! Try adding more trees, flowers, rocks... perhaps a fence as well?",
+ "cover": "summercamp/day_2.png",
+ "difficulty": 1,
+ "startAt": 1,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "Today is a beautiful day - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor where the sun will be - **type** `moveTo 400, 50`",
+ "solution": "moveTo 400, 50"
+ },
+ {
+ "hint": "Set the `color` of the sun `yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Draw the sun with a circle - **type** `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "There is one cloud in the horizon. - **type** `moveTo 440, 80`",
+ "solution": "moveTo 440, 80"
+ },
+ {
+ "hint": "Set the `color` of the cloud `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw the cloud with an ellipse - **type** `ellipse 60, 20`",
+ "solution": "ellipse 60, 20"
+ },
+ {
+ "hint": "The lake has a beautiful `color` `aquamarine` this morning",
+ "solution": "color aquamarine"
+ },
+ {
+ "hint": "Move the cursor where the lake is - **type** `moveTo 0, 190`",
+ "solution": "moveTo 0, 190"
+ },
+ {
+ "hint": "Draw the lake using a simple rectangle - **type** `rectangle 500, 310`",
+ "solution": "rectangle 500, 310"
+ },
+ {
+ "hint": "Set the `color` to `green` for the grass",
+ "solution": "color green"
+ },
+ {
+ "hint": "Move the cursor before drawing the grass - **type** `moveTo 250, 700`",
+ "solution": "moveTo 250, 700"
+ },
+ {
+ "hint": "Draw the camp site with a circle - **type** `circle 500`",
+ "solution": "circle 500"
+ },
+ {
+ "hint": "Looking a bit empty, let's draw a tree - **type** `moveTo 120, 300`",
+ "solution": "moveTo 120, 300"
+ },
+ {
+ "hint": "Set the `color` of the trunk to `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw the trunk of the tree using a rectangle - **type** `rectangle 15, 100`",
+ "solution": "rectangle 15, 100"
+ },
+ {
+ "hint": "Now place the foliage of the tree - **type** `move 8, -120`",
+ "solution": "move 8, -120"
+ },
+ {
+ "hint": "Set the `color` to `darkgreen`",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Draw the foliage with a triangle using `polygon` - **type** `polygon -40, 160, 40, 160`",
+ "solution": "polygon -40, 160, 40, 160"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/cinema.json b/lib/challenges/locales/ja/worlds/summercamp/cinema.json
new file mode 100644
index 000000000..4393cb316
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/cinema.json
@@ -0,0 +1,117 @@
+{
+ "id": "cinema",
+ "title": "Relax watching a movie",
+ "short_title": "Cinema",
+ "icon_class": "challenge_cinema",
+ "description": "After playing a short movie presentation by the Skladanowsky brothers in 1895 which consisted of numerous very short 6-second films accompanied by a specially composed piece of music, the Berlin Wintergarten theatre in Mitte, Berlin, became known as the first ever movie theatre. Unfortunately the theatre was destroyed by bombs in 1944, during the Second World War.",
+ "cover": "summercamp/day_14.png",
+ "completion_text": "What good is a cinema without a movie playing? Go ahead and try drawing a scene from your favourite movie up on the big screen, and maybe a few friends to watch the film with you! Don't forget the popcorn!",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "It's dark in the cinema. Set the `background` color to `black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "Set the `stroke` to `0` in a new line",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "First, move the cursor to place the floor - **type** `moveTo 0, 300`",
+ "solution": "moveTo 0, 300"
+ },
+ {
+ "hint": "Set the floor `color` to `gray`",
+ "solution": "color gray"
+ },
+ {
+ "hint": "Draw the floor with a rectangle - **type** `rectangle 500, 200`",
+ "solution": "rectangle 500, 200"
+ },
+ {
+ "hint": "Now, move the cursor to place the screen - **type** `moveTo 50, 20`",
+ "solution": "moveTo 50, 20"
+ },
+ {
+ "hint": "Set the screen `color` to `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "The screen is a perfect `rectangle` of size `400` by `250`",
+ "solution": "rectangle 400, 250"
+ },
+ {
+ "hint": "Amazing! This theatre needs a curtain - **type** `moveTo 0`",
+ "solution": "moveTo 0"
+ },
+ {
+ "hint": "Set the curtain `color` to `red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a vertical curtain as a `rectangle` of size `30` by `330`",
+ "solution": "rectangle 30, 330"
+ },
+ {
+ "hint": "Now draw an horizontal curtain size `500` by `10` in the same way",
+ "solution": "rectangle 500, 10"
+ },
+ {
+ "hint": "Looking great! Place the curtain on the right - **type** `move 470`",
+ "solution": "move 470"
+ },
+ {
+ "hint": "Draw a vertical curtain, same as the one the left",
+ "solution": "rectangle 30, 330"
+ },
+ {
+ "hint": "This theatre needs some seats for our audience - **type** `moveTo 0, 360`",
+ "solution": "moveTo 0, 360"
+ },
+ {
+ "hint": "Set the outline of the seats with a `stroke` of `orangered` color and size `10`.",
+ "solution": "stroke orangered, 10"
+ },
+ {
+ "hint": "Let's draw 3 rows and 6 columns of seats with a loop - **type** `for x in [ 1 .. 3 ]`",
+ "solution": "for x in [ 1 .. 3 ]"
+ },
+ {
+ "hint": "Set the `color` of the seats to `red`",
+ "solution": " color red"
+ },
+ {
+ "hint": "Now draw a `rectangle` for the seats, size `500` by `40`",
+ "solution": " rectangle 500, 40"
+ },
+ {
+ "hint": "Move the cursor down to place the back of the seats - **type** `move 0, 50`",
+ "solution": " move 0, 50"
+ },
+ {
+ "hint": "Inside the loop, open a second loop for the back of the seats - **type** `for y in [ 1 .. 6 ]`",
+ "solution": " for y in [ 1 .. 6 ]"
+ },
+ {
+ "hint": "Set the `color` of the backseat to `darkred`",
+ "solution": " color darkred"
+ },
+ {
+ "hint": "Brilliant! Almost there. Now draw the back of the seat with an arc - **type** `arc 60, 0, 1`",
+ "solution": " arc 60, 0, 1"
+ },
+ {
+ "hint": "Now separate the seats `130` pixels to the right",
+ "solution": " move 130"
+ },
+ {
+ "hint": "Last, make sure new rows are positioned correctly - Press **Enter**, then **Backspace** and **type** `move -800` inside the **first** loop",
+ "solution": " move -800"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/compass.json b/lib/challenges/locales/ja/worlds/summercamp/compass.json
new file mode 100644
index 000000000..4732d0453
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/compass.json
@@ -0,0 +1,93 @@
+{
+ "id": "compass",
+ "title": "A compass is an essential tool",
+ "short_title": "Compass",
+ "icon_class": "challenge_compass",
+ "description": "Essentially a compass is a magnet, generally a magnetized needle. Since opposites attract the southern pole of the needle is attracted to the Earth's natural magnetic north pole. Did you know that you can create your own simply with a paperclip, cork and a magnet?",
+ "cover": "summercamp/day_9.png",
+ "completion_text": "Now try adding the rest of the 'compass rose' components: the other cardinal directions (E, S, W and even NE, NW, SE and SW). Why not a hand holding it?",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Select `lightgray` for the `background`",
+ "solution": "background lightgray"
+ },
+ {
+ "hint": "In a new line, set the outline of your compass with a `stroke` of `gold` color and size `20`",
+ "solution": "stroke gold, 20"
+ },
+ {
+ "hint": "Set the `color` of the compass to `gray`",
+ "solution": "color gray"
+ },
+ {
+ "hint": "Your compass is a `circle` with size `110`",
+ "solution": "circle 110"
+ },
+ {
+ "hint": "Set the `color` to `darkgray` for the second half of the compass",
+ "solution": "color darkgray"
+ },
+ {
+ "hint": "Set the `stroke` back to `0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Draw an arc that matches the top half of the compass - **type** `arc 105, 2, 1`",
+ "solution": "arc 105, 2, 1"
+ },
+ {
+ "hint": "We need to mark the north somehow - in a new line **type** `moveTo 250, 175`",
+ "solution": "moveTo 250, 175"
+ },
+ {
+ "hint": "Set the `color` to `gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Set the `font` to `'cursive'` and size `25`",
+ "solution": "font 'cursive', 25"
+ },
+ {
+ "hint": "Write an N to represent north - **type** `text 'N'`",
+ "solution": "text 'N'"
+ },
+ {
+ "hint": "Now we need a needle with 2 sides. Set the `color` to `red` for the first side",
+ "solution": "color red"
+ },
+ {
+ "hint": "Move the cursor to position the needle **type** `moveTo 230, 250`",
+ "solution": "moveTo 230, 250"
+ },
+ {
+ "hint": "Draw a triangle with a polygon - **type** `polygon 20, -90, 40, 0`",
+ "solution": "polygon 20, -90, 40, 0"
+ },
+ {
+ "hint": "Excellent! Set the `color` to `white` for the second side of the needle",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw the second part - **type** `polygon 20, 90, 40, 0`",
+ "solution": "polygon 20, 90, 40, 0"
+ },
+ {
+ "hint": "Finish it with one last detail. Set the `color` to `gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Move the cursor to the center of the compass **type** `move 20`",
+ "solution": "move 20"
+ },
+ {
+ "hint": "Draw a `circle` of size `20`",
+ "solution": "circle 20"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/fireworks.json b/lib/challenges/locales/ja/worlds/summercamp/fireworks.json
new file mode 100644
index 000000000..ae830ff5e
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/fireworks.json
@@ -0,0 +1,50 @@
+{
+ "id": "fireworks",
+ "title": "Fireworks",
+ "short_title": "Fireworks",
+ "icon_class": "challenge_fireworks",
+ "description": "Camp is almost all wrapped up, and to celebrate we have fireworks! Fireworks have been around for centuries and are believed to have been invented by the Chinese. We are almost ready to put on the show, but need a little bit of help designing the fireworks. Can you give it a shot?",
+ "cover": "summercamp/day_21.png",
+ "completion_text": "Well done! You made a function that can draw fireworks! Can you improve upon it? Draw them all over the screen, add in other creations and share it!",
+ "difficulty": 3,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "The sun is down and the moon is new, let’s make the sky dark with `background black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "Lets set the `stroke` to `red` for our firework",
+ "solution": "stroke red"
+ },
+ {
+ "hint": "We want to draw 60 lines radiating outward, so let’s use a for loop `for i in [0 ... 60]`",
+ "solution": "for i in [0 ... 60]"
+ },
+ {
+ "hint": "All of the lines in our firework should have different lengths! So for each line radiating out, let’s set its length with `length = random 1, 200`.",
+ "solution": " length = random 1, 200"
+ },
+ {
+ "hint": "For each loop, we are drawing a new line radiating out. So for every time we loop, the angle of the line should change. This uses some advanced math, but don’t fear. Just **type** `angle = (360 / 60 * i) * (Math.PI / 180)`.",
+ "solution": " angle = (360 / 60 * i) * (Math.PI / 180)",
+ "validate": "__^ angle *= *\\(360 */ *60 \\* i\\) *\\* *\\(Math[.]PI */ *180\\) *$"
+ },
+ {
+ "hint": "Using this new angle and the random length let’s calculate where the x coordinate for the end of the radiating line should be. **Type** `dx = 250 + Math.sin(angle) * length`.",
+ "solution": " dx = 250 + Math.sin(angle) * length",
+ "validate": "__^ dx *= *250 *\\+ *Math.sin\\(angle\\) *\\* *length"
+ },
+ {
+ "hint": "Now let’s calculate the y coordinate for the end of the radiating line. **Type** `dy = 250 + Math.cos(angle) * length`.",
+ "solution": " dy = 250 + Math.cos(angle) * length",
+ "validate": "__^ dy *= *250 *\\+ *Math.cos\\(angle\\) *\\* *length"
+ },
+ {
+ "hint": "Finally, with all the coordinates set we are ready to draw the line. **Type** `lineTo dx, dy` ",
+ "solution": " lineTo dx, dy"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/fishing.json b/lib/challenges/locales/ja/worlds/summercamp/fishing.json
new file mode 100644
index 000000000..1ddb8f591
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/fishing.json
@@ -0,0 +1,107 @@
+{
+ "id": "fishing",
+ "title": "Fishing",
+ "short_title": "Fishing",
+ "icon_class": "challenge_fishing",
+ "description": "It's time to go fishing! Inside the camp's lake there are trout, catfish, and perch swimming around looking for a snack. Grab your fishing pole and some worms, and head over to the water for todays adventure. ",
+ "cover": "summercamp/day_17.png",
+ "completion_text": "Great job! Every time you press a key the image redraws, so try holding down the space bar and watch the waves ripple. Also try adding a fish or making the bobber disappear.",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "It's a sunny day! Set the background color to lightblue - **type** `background lightblue`",
+ "solution": "background lightblue"
+ },
+ {
+ "hint": "We will start by drawing the waves. We want to draw without a fill - set the `color` to be `null`.",
+ "solution": "color null"
+ },
+ {
+ "hint": "Our waves will be dark blue - **type** `stroke darkblue, 8`",
+ "solution": "stroke darkblue, 8"
+ },
+ {
+ "hint": "Move to the place on the left where we will start - **type** `moveTo 10, 250`",
+ "solution": "moveTo 10, 250"
+ },
+ {
+ "hint": "We are going to draw waves on the surface by creating 25 waves inside of a loop - **type** `for i in [1 .. 25]`",
+ "solution": "for i in [1 .. 25]"
+ },
+ {
+ "hint": "Create a wave out of an arc with a radius of 20 pixels - **type** `arc 20, 0.8, 0.2`",
+ "solution": " arc 20, 0.8, 0.2"
+ },
+ {
+ "hint": "Move each wave a random amount to the left - **type** `move (random 24, 32)`",
+ "solution": " move (random 24, 32)"
+ },
+ {
+ "hint": "Press ENTER and then backspace to get rid of the indent. This exits the loop.\n\nNext move to the place where we will draw the rest of the water - **type** `moveTo 0, 270`.",
+ "solution": "moveTo 0, 270"
+ },
+ {
+ "hint": "Set the `color` of the water to be the same as the wave",
+ "solution": "color darkblue"
+ },
+ {
+ "hint": "Draw a big `rectangle` that is `500` pixels high and `250` pixels wide for the water",
+ "solution": "rectangle 500, 250"
+ },
+ {
+ "hint": "Let's get ready to add some little blue waves - **type** `stroke blue, 2`",
+ "solution": "stroke blue, 2"
+ },
+ {
+ "hint": "We are going to draw waves 100 times - **type** `for i in [1 .. 100]`",
+ "solution": "for i in [1 .. 100]"
+ },
+ {
+ "hint": "Move each wave to a random place on the surface of the water - **type** `moveTo (random 0, 500), (random 260, 500)`",
+ "solution": " moveTo (random 0, 500), (random 260, 500)"
+ },
+ {
+ "hint": "Now draw each ripple as a little arc - **type** `arc 10, 0.8, 0.2`",
+ "solution": " arc 10, 0.8, 0.2"
+ },
+ {
+ "hint": "Brilliant! When you press the spacebar, a new set of random numbers is selected and the ripples move!\n\nTo catch some fish we need a brown fishing pole - Exit the loop and **type** `stroke brown, 10`.",
+ "solution": "stroke brown, 10"
+ },
+ {
+ "hint": "Now move to the place where we will draw the end of the pole - **type** `moveTo 300, 100`",
+ "solution": "moveTo 300, 100"
+ },
+ {
+ "hint": "Draw a `line` that is `150` pixels wide and `400` pixels tall",
+ "solution": "line 150, 400"
+ },
+ {
+ "hint": "Next we need some fishing line - **type** `stroke lightgray, 1`",
+ "solution": "stroke lightgray, 1"
+ },
+ {
+ "hint": "Draw a line that goes to the left and down - **type** `line -100, 200`",
+ "solution": "line -100, 200"
+ },
+ {
+ "hint": "To see if a fish is biting we are going to draw a cork. Move to the end of the fishing line - **type** `move -100, (random 197, 202)`",
+ "solution": "move -100, (random 197, 202)"
+ },
+ {
+ "hint": "The cork doesn't have an outline - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the cork's `color` to `red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Now draw the cork floating in the water - `arc 8, 0, 1`",
+ "solution": "arc 8, 0, 1"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/flag.json b/lib/challenges/locales/ja/worlds/summercamp/flag.json
new file mode 100644
index 000000000..ea40cbe69
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/flag.json
@@ -0,0 +1,77 @@
+{
+ "id": "flag",
+ "title": "Create your Flag",
+ "short_title": "Flag",
+ "description": "Time to show your true colors. Your campsite is shaping up, and as the campsite’s leader you need to make a flag. We’ve got you started with the basics for a flag and flagpole, but it is up to you to to make it your own. \nYou can look to other flags for inspiration. Countries like France, Nigeria, Sweden, and Switzerland all have very simple color and shape designs, while Spain, Brazil, Kenya, and Saudi Arabia all feature complex designs. When you are done, share your flag to lay claim to your little bit of land!",
+ "icon_class": "challenge_flag",
+ "completion_text": "You have a white canvas in front of you, use it wisely. Create the most amazing flag the world has ever seen!",
+ "cover": "summercamp/day_4.png",
+ "difficulty": 1,
+ "startAt": 3,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "Draw the sky where the flag will flutter - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Let's move the cursor to place our flagpole in the correct place - **type** `moveTo 100, 100`",
+ "solution": "moveTo 100, 100"
+ },
+ {
+ "hint": "Set the `color` of the flagpole to `darkgray`",
+ "solution": "color darkgray"
+ },
+ {
+ "hint": "Draw a long, thin pole with a rectangle - **type** `rectangle 10, 400`",
+ "solution": "rectangle 10, 400"
+ },
+ {
+ "hint": "Move the cursor to place a ball on top-centre of the flagpole - **type** `move 5`",
+ "solution": "move 5"
+ },
+ {
+ "hint": "Set the `color` of the ornament to `gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Draw a circle with a size of 8 - in a new line **type** `circle 8`",
+ "solution": "circle 8"
+ },
+ {
+ "hint": "Now move the cursor to start drawing our flag - **type** `moveTo 115, 123`",
+ "solution": "moveTo 115, 123"
+ },
+ {
+ "hint": "Set the `color` of the flag to `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw the flag with a `rectangle` of size `261` and `171`",
+ "solution": "rectangle 261, 171"
+ },
+ {
+ "hint": "That flag needs something to hold of to - **type** `moveTo 100, 140`",
+ "solution": "moveTo 100, 140"
+ },
+ {
+ "hint": "Set the `color` of the holder to `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw the holder with a `20` by `5` `rectangle`",
+ "solution": "rectangle 20, 5"
+ },
+ {
+ "hint": "Now is your turn! Draw an amazing pattern on it - **type** `moveTo 120, 125`",
+ "solution": "moveTo 120, 125"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/flashlight.json b/lib/challenges/locales/ja/worlds/summercamp/flashlight.json
new file mode 100644
index 000000000..a5a6781ce
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/flashlight.json
@@ -0,0 +1,93 @@
+{
+ "id": "flashlight",
+ "title": "Lights on!",
+ "short_title": "Flashlight",
+ "icon_class": "challenge_flashlight",
+ "description": "Torches started life as ignited sticks; their flames would light the way for explorers and settlers. With the advent of electricity, and the invention of the dry cell battery in 1887, the road was paved to use incandescent bulbs as a replacement. While the technology inside flashlights has evolved, they are still as useful as ever. Meanwhile, the original torch has found a second coming in circus acts where they are thrown skywards in miraculous fashion.",
+ "cover": "summercamp/day_12.png",
+ "completion_text": "You are an adventurer in the dark with your torch. What might you have uncovered as its light passes over the room? Try drawing that fox that you’ve unearthed or that owl hooting in the trees.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "It's getting dark outside! Set the `background` color to `darkblue`",
+ "solution": "background darkblue"
+ },
+ {
+ "hint": "Set the `stroke` to `0`, we won't need it for this challenge",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to place our flashlight - **type** `moveTo 200, 200`",
+ "solution": "moveTo 200, 200"
+ },
+ {
+ "hint": "Set the `color` of the flashlight to `dimgray`",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Draw the head of the flashlight with a rectangle - **type** `rectangle 100, 50`",
+ "solution": "rectangle 100, 50"
+ },
+ {
+ "hint": "Now move the cursor down to draw the neck - **type** `move 0, 50`",
+ "solution": "move 0, 50"
+ },
+ {
+ "hint": "Set the `color` of this piece to `gray`",
+ "solution": "color gray"
+ },
+ {
+ "hint": "Draw a polygon - **type** `polygon 20, 40, 80, 40, 100, 0`",
+ "solution": "polygon 20, 40, 80, 40, 100, 0"
+ },
+ {
+ "hint": "Exciting! Now move the cursor down to draw the body - **type** `move 20, 40`",
+ "solution": "move 20, 40"
+ },
+ {
+ "hint": "Set the `color` of the body to `darkgray`",
+ "solution": "color darkgray"
+ },
+ {
+ "hint": "Draw the body of the flashlight with a `rectangle` `60` by `200`",
+ "solution": "rectangle 60, 200"
+ },
+ {
+ "hint": "The only thing missing now is a ON/OFF button. Set the `color` to `dimgray`",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Now move the cursor down to place the button - **type** `move 30, 50`",
+ "solution": "move 30, 50"
+ },
+ {
+ "hint": "Draw an ellipse where the button will be located - **type** `ellipse 10, 30`",
+ "solution": "ellipse 10, 30"
+ },
+ {
+ "hint": "Set the `color` of the button to `red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw the button with an ellipse - **type** `ellipse 10, 20`",
+ "solution": "ellipse 10, 20"
+ },
+ {
+ "hint": "You have turned on the light! Set the color of the beam - **type** `color \"rgba(255, 255, 0, 0.8)\"`",
+ "solution": "color \"rgba(255, 255, 0, 0.8)\""
+ },
+ {
+ "hint": "Place the beam in front of the flashlight - **type** `move -50, -140`",
+ "solution": "move -50, -140"
+ },
+ {
+ "hint": "Draw the beam of light with a polygon - **type** `polygon 100, 0, 180, -300, -80, -300`",
+ "solution": "polygon 100, 0, 180, -300, -80, -300"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/foraging.json b/lib/challenges/locales/ja/worlds/summercamp/foraging.json
new file mode 100644
index 000000000..2d2add721
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/foraging.json
@@ -0,0 +1,57 @@
+{
+ "id": "foraging",
+ "title": "Foraging",
+ "short_title": "Foraging",
+ "icon_class": "challenge_foraging",
+ "description": "The wild is full of plants and fauna to eat. From stinging nettles, to mushrooms, and a multitude of berries, any meal can be improved with naturally growing foods. Berries are common in many parts of the world, and at Camp Kano they run wild even though they are hard to find. You might need to use your coding skills to find them.",
+ "cover": "summercamp/day_19.png",
+ "completion_text": "But wait! Where are the berries you are meant to be foraging for? Use `color red`, `moveTo`, and `square 25` to add some extra berries to the scene!",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Set `stroke` to `0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "It’s a bright sunny day! Set `background` to `blue`.",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Our foraging scene needs a grass field to start with. Set our drawing color to `green`.",
+ "solution": "color green"
+ },
+ {
+ "hint": "We want our grass on the bottom half of the screen, so let’s move into position with `moveTo 0, 300`",
+ "solution": "moveTo 0, 300"
+ },
+ {
+ "hint": "The grass is a nice big `rectangle` of size `500, 200`",
+ "solution": "rectangle 500, 200"
+ },
+ {
+ "hint": "Now let’s add in a nice boxy tree to our foraging scene. Begin by moving into position at exactly `50, 100`.",
+ "solution": "moveTo 50, 100"
+ },
+ {
+ "hint": "Our leaves are drawn with `square 150`.",
+ "solution": "square 150"
+ },
+ {
+ "hint": "Next we need to draw the trunk. Move the cursor with `moveTo 100, 250`.",
+ "solution": "moveTo 100, 250"
+ },
+ {
+ "hint": "Our wood has a nice `darkbrown` color. Set that as the drawing color.",
+ "solution": "color darkbrown"
+ },
+ {
+ "hint": "The trunk is a `rectangle` with a width of `40` and a height of `150`",
+ "solution": "rectangle 40, 150"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/ghost.json b/lib/challenges/locales/ja/worlds/summercamp/ghost.json
new file mode 100644
index 000000000..7e6b445b3
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/ghost.json
@@ -0,0 +1,91 @@
+{
+ "id": "ghost",
+ "title": "Ghost gathering",
+ "short_title": "Ghost",
+ "description": "Boo! As night falls, the spirits come out to play. A recent poll reported 87% of Chinese office workers believed in ghosts, and according to another report, 25% of Britons say they have seen a ghost. It’s getting dark, so it is hard to see, but we think we found a ghost! Maybe you can find out if it is friendly or not with your creative coding powers.",
+ "icon_class": "challenge_ghost",
+ "completion_text": "Spooky! Can you make the ghost scarier? Maybe if it could say \"Boo!\"...",
+ "difficulty": 2,
+ "cover": "summercamp/day_7.png",
+ "startAt": 6,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Select a spooky color for the background - **type** `background darkpurple`",
+ "solution": "background darkpurple"
+ },
+ {
+ "hint": "Set the `stroke` to `0`, to avoid drawing lines",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the `color` to `white` for the ghost",
+ "solution": "color white"
+ },
+ {
+ "hint": "Now draw an ellipse for the ghost's body - **type** `ellipse 120, 140`",
+ "solution": "ellipse 120, 140"
+ },
+ {
+ "hint": "Set the `color` to `black` for the eyes",
+ "solution": "color black"
+ },
+ {
+ "hint": "Move the cursor to draw the first eye - **type** `move -40, -50`",
+ "solution": "move -40, -50"
+ },
+ {
+ "hint": "Draw the eye with an ellipse - **type** `ellipse 20, 30`",
+ "solution": "ellipse 20, 30"
+ },
+ {
+ "hint": "Set the `color` to `lightgray`",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Now draw a `circle` with a size of `5`",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Looking good! Now move the cursor to the right for the second eye - **type** `move 30`",
+ "solution": "move 30"
+ },
+ {
+ "hint": "Set the `color` back to `black` for the second eye",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw the second eye with an ellipse - **type** `ellipse 20, 30`",
+ "solution": "ellipse 20, 30"
+ },
+ {
+ "hint": "Set the `color` to `lightgray`",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Draw a `circle` with size `5`",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Almost there! Set the `color` to `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Now move the cursor to the bottom - **type** `move -80, 150`",
+ "solution": "move -80, 150"
+ },
+ {
+ "hint": "Let's open a loop - **type** `for i in [ 1 .. 5 ]`",
+ "solution": "for i in [ 1 .. 5 ]"
+ },
+ {
+ "hint": "Draw a `circle` with a size of `45`",
+ "solution": " circle 45"
+ },
+ {
+ "hint": "And move the cursor slightly to the right - **type** `move 45`",
+ "solution": " move 45"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/guitar.json b/lib/challenges/locales/ja/worlds/summercamp/guitar.json
new file mode 100644
index 000000000..a57697b83
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/guitar.json
@@ -0,0 +1,127 @@
+{
+ "id": "guitar",
+ "title": "Play a nice sweet song",
+ "short_title": "Guitar",
+ "icon_class": "challenge_guitar",
+ "description": "Did you know the oldest surviving guitar-like instrument is actually around 3,500 years old? It is a 3-string instrument with a plectrum suspended from the neck, it was owned by an Egyptian singer called Har-Mose and was buried alongside him - You can still see the instrument on display at the Archaeological Museum in Cairo, Egypt.",
+ "cover": "summercamp/day_13.png",
+ "completion_text": "What good is a guitar if we don't have a campfire to sing songs around? Try drawing a nice big campfire with some marshmallows for toasting! Maybe a big moon on the horizon too? Be wild, be creative!",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "It's a very nice night! Set the background color to darkblue - **type** `background darkblue`",
+ "solution": "background darkblue"
+ },
+ {
+ "hint": "Set the `stroke` to `0` in a new line.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set your guitar `color` to `lightbrown`.",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Move the cursor to place the guitar - **type** `moveTo 120, 250`",
+ "solution": "moveTo 120, 250"
+ },
+ {
+ "hint": "Draw the first part of the body with an ellipse - **type** `ellipse 60, 65`",
+ "solution": "ellipse 60, 65"
+ },
+ {
+ "hint": "Now for the second part of the body, `move` the cursor `70` pixels to the right.",
+ "solution": "move 70"
+ },
+ {
+ "hint": "Draw a `circle` of size `55` for the second part of the guitar.",
+ "solution": "circle 55"
+ },
+ {
+ "hint": "Every guitar needs a neck, move the cursor to place it - **type** `move 20, -10`",
+ "solution": "move 20, -10"
+ },
+ {
+ "hint": "Set the neck `color` to `brown`.",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw the neck of the guitar with a rectangle - **type** `rectangle 150, 20`",
+ "solution": "rectangle 150, 20"
+ },
+ {
+ "hint": "Now place the headstock - **type** `move 150, -5`",
+ "solution": "move 150, -5"
+ },
+ {
+ "hint": "Draw the headstock with a `rectangle` of size `40` by `30`.",
+ "solution": "rectangle 40, 30"
+ },
+ {
+ "hint": "Time to place the soundhole in the body - **type** `move -170, 15`",
+ "solution": "move -170, 15"
+ },
+ {
+ "hint": "Set the outline of the soundhole with a `stroke` of `brown` color and size `10`.",
+ "solution": "stroke brown, 10"
+ },
+ {
+ "hint": "Set the soundhole `color` to `black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw the hole with a `circle` of size `20`.",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "The bridge is the piece that fixes the strings to the body. Let's place it - **type** `move -50, -15`",
+ "solution": "move -50, -15"
+ },
+ {
+ "hint": "Set the outline of the bridge with a `stroke` of `red` color and size `10`.",
+ "solution": "stroke red, 10"
+ },
+ {
+ "hint": "Draw the bridge with a `rectangle` of size `2` by `35`.",
+ "solution": "rectangle 2, 35"
+ },
+ {
+ "hint": "That's a good looking guitar. Time for the strings - **type** `move 0, 9`",
+ "solution": "move 0, 9"
+ },
+ {
+ "hint": "Set the properties of the strings with a `stroke` of `white` color and size `1`.",
+ "solution": "stroke white, 1"
+ },
+ {
+ "hint": "Let's draw the 4 strings with a loop - **type** `for i in [ 1 .. 4 ]`",
+ "solution": "for i in [ 1 .. 4 ]"
+ },
+ {
+ "hint": "Draw a string using a line - **type** `line 250`",
+ "solution": " line 250"
+ },
+ {
+ "hint": "And move the cursor slightly down to place the next ones - **type** `move 0, 4`",
+ "solution": " move 0, 4"
+ },
+ {
+ "hint": "Press **Enter** and **Backspace** to set the `color` to `white` outside the loop",
+ "solution": "color white"
+ },
+ {
+ "hint": "You are ready to play music! Start a new loop - **type** `for i in [ 1 .. 10 ]`",
+ "solution": "for i in [ 1 .. 10 ]"
+ },
+ {
+ "hint": "Select a random location - **type** `moveTo (random 200, 400), (random 100, 400)`",
+ "solution": " moveTo (random 200, 400), (random 100, 400)"
+ },
+ {
+ "hint": "Come on, play some notes! - **copy and paste** `text '♪'`",
+ "solution": " text '♪'"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/hiking.json b/lib/challenges/locales/ja/worlds/summercamp/hiking.json
new file mode 100644
index 000000000..b12407269
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/hiking.json
@@ -0,0 +1,111 @@
+{
+ "id": "hiking",
+ "title": "Hiking Poster",
+ "short_title": "Hiking",
+ "icon_class": "challenge_hiking",
+ "description": "The beautiful Camp Kano mountain range is known for its deep blue colour. The ten mountains in the range always seem to be changing position though, which makes for a dangerous climb. Should campers attempt to climb it? Who knows! But management wants an advertising posts so get to it.",
+ "cover": "summercamp/day_18.png",
+ "completion_text": "Well done, you’ve made a beautiful poster! Now play around with the random values and the text. What else is the poster missing?",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Up above the clouds the sky is blue, **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Camp Kano’s mountains are a beautiful shade of dark blue. Set the drawing colour with `color darkblue`.",
+ "solution": "color darkblue"
+ },
+ {
+ "hint": "The mountains also have a nice thick border, give them `stroke white, 10`",
+ "solution": "stroke white, 10"
+ },
+ {
+ "hint": "Lets draw the ten mountains of Camp Kano with a loop! This will be much faster than drawing them individually. **Type** `for i in [ 0 ... 10 ]` to open the loop.",
+ "solution": "for i in [ 0 ... 10 ]"
+ },
+ {
+ "hint": "For every time the loop runs, we want a mountain’s peak to be drawn randomly across the screen. Let’s select an x value with `x = random 0, 500`",
+ "solution": " x = random 0, 500"
+ },
+ {
+ "hint": "However, for the y value, we only want each mountain’s peak to be drawn on the top part of the screen. **Type** `y = random 0, 200`",
+ "solution": " y = random 0, 200"
+ },
+ {
+ "hint": "Now with our x and y values set we can move the cursor to them. **Type** `moveTo x, y`",
+ "solution": " moveTo x, y"
+ },
+ {
+ "hint": "Draw a mountain for every loop with the polygon function. **Type** `polygon 400, 500, -400, 500`",
+ "solution": " polygon 400, 500, -400, 500"
+ },
+ {
+ "hint": "First, get out of the previous for loop’s indent by pressing `BACKSPACE`. Now let’s draw some clouds with `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Clouds are made of small droplets of water vapor, which is see-through! To get a see-through color we use the `opacity` function. **Type** `color (opacity white, .1)`",
+ "solution": "color (opacity white, .1)"
+ },
+ {
+ "hint": "Now let’s make a new loop to draw the cloud puffs. We’ll need a lot, so let’s make this a loop last 300 iterations. **Type** `for j in [ 0 ... 250 ]`.",
+ "solution": "for j in [ 0 ... 250 ]"
+ },
+ {
+ "hint": "We want cloud puffs sprinkled randomly across the screen, so lets choose an x value between 0 and 500 with`x = random 0, 500`",
+ "solution": " x = random 0, 500"
+ },
+ {
+ "hint": "However, for the y value, we only want the cloud puffs to be drawn on the bottom part of the screen. **Type** `y = random 300, 500`",
+ "solution": " y = random 300, 500"
+ },
+ {
+ "hint": "Now with our x and y values set we can move the cursor to them. **Type** `moveTo x, y`",
+ "solution": " moveTo x, y"
+ },
+ {
+ "hint": "Let’s draw the cloud puff—a big transparent `circle` of size `100`",
+ "solution": " circle 100"
+ },
+ {
+ "hint": "Press `BACKSPACE` once to get out of the indented line. Then lets move the cursor into place for some text with `moveTo 250, 350`",
+ "solution": "moveTo 250, 350"
+ },
+ {
+ "hint": "Set the drawing color to red - **type** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Our message should be strong and impactful, so lets set the font to bold with `bold true`",
+ "solution": "bold true"
+ },
+ {
+ "hint": "We also want to style our text with italics, do this with `italic true`",
+ "solution": "italic true"
+ },
+ {
+ "hint": "Finally lets select Bariol at point size 40 with `font 'Bariol', 40`",
+ "solution": "font 'Bariol', 40"
+ },
+ {
+ "hint": "Start your message off with `text 'Hike the Blue Mountains of'`",
+ "solution": "text 'Hike the Blue Mountains of'"
+ },
+ {
+ "hint": "Now for a new line with a big bold finish. **Type** `font 90`",
+ "solution": "font 90"
+ },
+ {
+ "hint": "Lets move the cursor down into place for the final line with `move 0, 90`",
+ "solution": "move 0, 90"
+ },
+ {
+ "hint": "The finishing touch: **type** `text 'Camp Kano!'`",
+ "solution": "text 'Camp Kano!'"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/index.json b/lib/challenges/locales/ja/worlds/summercamp/index.json
new file mode 100644
index 000000000..a54bd7049
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/index.json
@@ -0,0 +1,26 @@
+{
+
+ "challenges": [
+ "./camp_sign",
+ "./campsite",
+ "./tent",
+ "./flag",
+ "./camp_badge",
+ "./campfire",
+ "./ghost",
+ "./backpack",
+ "./compass",
+ "./camera",
+ "./bear",
+ "./flashlight",
+ "./guitar",
+ "./cinema",
+ "./bow_and_arrow",
+ "./table_tennis",
+ "./fishing",
+ "./hiking",
+ "./foraging",
+ "./tree",
+ "./fireworks"
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/table_tennis.json b/lib/challenges/locales/ja/worlds/summercamp/table_tennis.json
new file mode 100644
index 000000000..8e5a1fae0
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/table_tennis.json
@@ -0,0 +1,121 @@
+{
+ "id": "table_tennis",
+ "title": "Table Tennis",
+ "short_title": "Table Tennis",
+ "icon_class": "challenge_tennis",
+ "description": "Table Tennis is a game played with one or two people on each side of a table with the outline of a miniaturized tennis court drawn on it. Games are played to 11 or 21, and upon completion the victor’s paddle is thrown to the ground as they let out their battle cry.",
+ "cover": "summercamp/day_16.png",
+ "completion_text": "Well done! You made a beautiful 3D ping pong table. But there isn’t anyone playing or watching the game! Use your creativity and code to draw some fellow campers enjoying the game.",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Begin by setting your stroke to zero with `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "It's a sunny day! Set the background color to blue - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set your color to green for the grass. **Type** `color green`.",
+ "solution": "color green"
+ },
+ {
+ "hint": "Move your cursor into place with `moveTo 0, 350`",
+ "solution": "moveTo 0, 350"
+ },
+ {
+ "hint": "Cover the bottom part of the canvas with grass. Draw a `rectangle` of width `500` and height `150`.",
+ "solution": "rectangle 500, 150"
+ },
+ {
+ "hint": "Give your table a solid foundation with some wood legs. Set `color` to `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Move the cursor into position for the first leg. **Type** `moveTo 20, 310`",
+ "solution": "moveTo 20, 310"
+ },
+ {
+ "hint": "Now draw the first leg. Type `rectangle 15, 150`.",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "Now the second leg. **Type** `moveTo 465, 310`.",
+ "solution": "moveTo 465, 310"
+ },
+ {
+ "hint": "Now **draw** the second leg. Type `rectangle 15, 150`.",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "And the third leg. **Type** `moveTo 40, 250`.",
+ "solution": "moveTo 40, 250"
+ },
+ {
+ "hint": "Now draw the third leg the same size as the others",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "Finally, the fourth leg. **Type** `moveTo 445, 250`.",
+ "solution": "moveTo 445, 250"
+ },
+ {
+ "hint": "Now draw the fourth leg",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "Now to put the table on! Set the drawing `color` to `darkgreen`.",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Move your table top into place. Type `moveTo 10, 300`.",
+ "solution": "moveTo 10, 300"
+ },
+ {
+ "hint": "Your table has perspective, to draw it use `polygon 30, -60, 450, -60, 480, 0`",
+ "solution": "polygon 30, -60, 450, -60, 480, 0"
+ },
+ {
+ "hint": "For a cool 3D effect, lighten the drawing color with `color lighten darkgreen, 10`",
+ "solution": "color lighten darkgreen, 10"
+ },
+ {
+ "hint": "**Draw** the side of the table with `rectangle 480, 10`",
+ "solution": "rectangle 480, 10"
+ },
+ {
+ "hint": "For the net, set the drawing `color` to `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Move the cursor to **exactly** `moveTo 248, 220`",
+ "solution": "moveTo 248, 220"
+ },
+ {
+ "hint": "Draw the net: a `rectangle` with a width of `4`, and height of `80`",
+ "solution": "rectangle 4, 80"
+ },
+ {
+ "hint": "Give your scene a bouncing ball! Use random to decide what side of the table the ball should be on. **Type** `x = random 40, 460`.",
+ "solution": "x = random 40, 460"
+ },
+ {
+ "hint": "Now we’ll use random to decide how high the ball should be! **Type** `y = random 150, 250`.",
+ "solution": "y = random 150, 250"
+ },
+ {
+ "hint": "Move the cursor to the x and y variables you just made. **Type** `moveTo x, y`.",
+ "solution": "moveTo x, y"
+ },
+ {
+ "hint": "Finally, draw your ball with a circle with radius 7. Type `circle 7`.",
+ "solution": "circle 7"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/tent.json b/lib/challenges/locales/ja/worlds/summercamp/tent.json
new file mode 100644
index 000000000..e0552bc87
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/tent.json
@@ -0,0 +1,73 @@
+{
+ "id": "tent",
+ "title": "Pitch your Tent",
+ "short_title": "Tent",
+ "description": "No campsite is complete without a tent. We’ve got you settled with a simple fly tent, which is usually made with a tarpaulin, rope, and stakes. But with your creative powers you can transform it into whatever you want! \nPeople have lived in tents, teepees, yurts, and wigwams for thousands of years. What features can you add to your tent to keep yourself dry and warm? You can always get inspiration from browsing other creations on Kano World, and searching the Internet for other tent designs.",
+ "icon_class": "challenge_setcamp",
+ "completion_text": "Nice job! You learnt in the last challenge how to draw a tree, why not adding it to the scene? Is that a bird flying in the sky?",
+ "cover": "summercamp/day_3.png",
+ "difficulty": 1,
+ "startAt": 2,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Draw a sunny day - **type** `background lightblue`",
+ "solution": "background lightblue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor where the sun will be - **type** `moveTo 400, 50`",
+ "solution": "moveTo 400, 50"
+ },
+ {
+ "hint": "Set the `color` of the sun `yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Draw the sun with a circle - **type** `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Move the cursor to draw some grass - **type** `moveTo 0, 350`",
+ "solution": "moveTo 0, 350"
+ },
+ {
+ "hint": "Set the `color` of the grass `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Draw the grass using a rectangle - **type** `rectangle 500, 150`",
+ "solution": "rectangle 500, 150"
+ },
+ {
+ "hint": "Excellent! Now we are ready to draw the tent - **type** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Position the cursor on top of the grass - **type** `moveTo 100, 350`",
+ "solution": "moveTo 100, 350"
+ },
+ {
+ "hint": "Draw a triangle using `polygon` - **type** `polygon 150, -200, 300, 0`",
+ "solution": "polygon 150, -200, 300, 0"
+ },
+ {
+ "hint": "We need the entrace now. Set the `color` to `darkred`",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "Place the cursor on the tip of the tent - **type** `moveTo 250, 150`",
+ "solution": "moveTo 250, 150"
+ },
+ {
+ "hint": "Draw the entrance - **type** `polygon 30, 200, -30, 200`",
+ "solution": "polygon 30, 200, -30, 200"
+ }
+ ]
+}
diff --git a/lib/challenges/locales/ja/worlds/summercamp/tree.json b/lib/challenges/locales/ja/worlds/summercamp/tree.json
new file mode 100644
index 000000000..0e190b858
--- /dev/null
+++ b/lib/challenges/locales/ja/worlds/summercamp/tree.json
@@ -0,0 +1,17 @@
+{
+ "id": "tree",
+ "title": "Fractal Tree",
+ "short_title": "Tree",
+ "icon_class": "challenge_tree",
+ "description": "Time to play! Our counselors made you something to play with. A tree which can grow branches that expand outwards, and contract inwards. It can blossom fully, or shrivel to nothing. All with code. Make it yours.",
+ "cover": "summercamp/day_20.png",
+ "completion_text": "Play around with the blue numbers at the top of the code. What do they do? Why do they do that? Shape the tree to your liking. If things mess up, go back to the main menu and start again.",
+ "difficulty": 3,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "code": "# Play with these blue numbers\narmLength = 50\niterations = 9 # Might crash if you go over 15!\ndegreeChange = 20\nhasBlossoms = 3\nblossomSize = 70\nblossomOpacity = .02\n# ^^^ Play with these blue numbers ^^^\n\n###\nThis is a function that draws a tree\nbranch, when it completes, it calls itself\nover and over and over again until all the\ntree’s branches have been drawn!\n###\ndrawBranch = (x, y, branchesLeft, startAngle) ->\n if branchesLeft > 0 \n # Move to where the next branch should\n # be drawn:\n moveTo x, y\n \n # A branch is always blue, but the width\n # depends on how far from the base it is!\n stroke blue, branchesLeft \n \n # A bit of trigonometry here, it’s alright\n # if you don’t understand this! This \n # calculates coordinates for where the \n # branch should be drawn to, and where\n # the next branch should start.\n dx = Math.cos(startAngle) * armLength \n dy = -Math.sin(startAngle) * armLength \n \n # Draw a line to the new coordinates we\n # just calculated\n line dx, dy \n \n # This block of code only executes if\n # the current branch has blossoms. You\n # can change the hasBlossoms variable up\n # top!\n if branchesLeft <= hasBlossoms \n # Set the drawing color to a nice red\n color opacity \"rgb(247, 45, 99)\", blossomOpacity\n # Our blossoms shouldn’t have a stroke\n stroke 0 \n # Draw the blossom! These big blossoms\n # overlap over each other to create a\n # neat effect.\n circle blossomSize\n \n # Starts the next branch on the right\n drawBranch(x + dx, y + dy, branchesLeft - 1, startAngle - Math.PI / 180 * degreeChange) \n # Starts the next branch on the left\n drawBranch(x + dx, y + dy, branchesLeft - 1, startAngle + Math.PI / 180 * degreeChange)\n \n###\nFinally we call our function and see what\nhappens. Play with the numbers at the top of\nthe function and see how they change the tree\n###\ndrawBranch(stage.width * .5, stage.height, iterations, Math.PI / 2, length)",
+ "steps": []
+}
diff --git a/lib/challenges/medal.js b/lib/challenges/medal.js
deleted file mode 100644
index dae5cc8ef..000000000
--- a/lib/challenges/medal.js
+++ /dev/null
@@ -1,81 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'medal',
- title : 'Make a Medal',
- description : 'Code you\'re own winners medal!',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the stroke to red',
- 'color red',
- [
- [ 'stroke-color', { color: palette.red } ]
- ]
- ],
- [
- 'Set the stroke to size 90',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 90 } ]
- ]
- ],
- [
- 'Draw a line - **type** `line -100, -270`',
- 'line -100, -270',
- [
- [ 'line', { dx: -100, dy: -270 } ]
- ]
- ],
- [
- 'Draw a line - **type** `line 100, -270`',
- 'line 100, -270',
- [
- [ 'line', { dx: 100, dy: -270 } ]
- ]
- ],
- [
- 'Set the stroke to zero',
- 'stroke black, 5',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Set the color to gold - **type** `color gold`',
- 'color gold',
- [
- [ 'color', { color: palette.gold } ]
- ]
- ],
- [
- 'Draw a circle with a size of 170 ',
- 'circle 170',
- [
- [ 'ellipse', { rx: 170, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to white',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Now to write on the medal, set the font size - **type** `font 40`',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Write something on the medal - **type** `text \'Winner\'`',
- 'move 0, 15',
- [
- [ 'move-to', { dx: 0, dy: 15 } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/pizza.js b/lib/challenges/pizza.js
deleted file mode 100644
index 1be69a93a..000000000
--- a/lib/challenges/pizza.js
+++ /dev/null
@@ -1,76 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'pizza',
- title : 'Pizza',
- description : 'Code yourself a tasty pizza!',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the stroke to the color beige',
- 'stroke beige',
- [
- [ 'stroke-color', { color: palette.beige } ]
- ]
- ],
- [
- 'Set the stroke to 50',
- 'stroke 50',
- [
- [ 'stroke-width', { width: 50 } ]
- ]
- ],
- [
- 'Set the color to red',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Draw a circle with a size of 200',
- 'circle 200',
- [
- [ 'ellipse', { rx: 200, isCircle: true } ]
- ]
- ],
- [
- 'Looking tasty! Set the stroke back to 0',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Top it with a cheesy yellow circle of size 195',
- 'color yellow\ncircle 195',
- [
- [ 'color', { color: palette.yellow } ],
- [ 'ellipse', { rx: 195, isCircle: true } ]
- ]
- ],
- [
- 'It\'s not pizza without toppings - set the color to darkred',
- 'color darkred',
- [
- [ 'color', { color: palette.darkred } ]
- ]
- ],
- [
- 'Now `moveTo` `164, 290`',
- 'moveTo 164, 290',
- [
- [ 'move-to', { x: 164, y: 290 } ]
- ]
- ],
- [
- 'Cool, now draw a circle with a size of 20',
- 'circle 20',
- [
- [ 'ellipse', { rx: 20, isCircle: true } ]
- ]
- ]
-
- ])
-};
diff --git a/lib/challenges/planet.js b/lib/challenges/planet.js
deleted file mode 100644
index a1373d79c..000000000
--- a/lib/challenges/planet.js
+++ /dev/null
@@ -1,133 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'planet',
- title : 'Planet painter',
- description : 'Code your own planet!',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the background using a hex code - **type** `background \'#444444\'`',
- 'color \'#444444\'',
- [
- [ 'background', { color: '#444444' } ]
- ]
- ],
- [
- 'Set the stroke to 0, to avoid drawing lines',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Set the color to yellow',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Now let\'s open a loop - **type** `for i in [ 0 .. 32 ]`',
- 'for i in [ 0 .. 32 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '0..32' } ]
- ]
- ],
- [
- 'Now to set where each star will go - **type** `moveTo (random 1, 500), (random 1, 500)`',
- ' moveTo (random 1, 500), (random 1, 500)',
- function () {
- var i = 0,
- out = [];
-
- function validateMove(options) {
- return (
- options.x > 0 && options.x <= 500 &&
- options.y > 0 && options.y <= 500
- );
- }
-
- for (i = 0; i < 33; i += 1) {
- out.push([ 'move-to', validateMove ]);
- }
-
- return out;
- }
- ],
- [
- 'Draw each star as a circle with a size of 5',
- ' circle 5',
- function () {
- var i = 0,
- out = [];
-
- function validateMove(options) {
- return (
- options.x > 0 && options.x <= 500 &&
- options.y > 0 && options.y <= 500
- );
- }
-
- function validateStar(options) {
- return (
- options.isCircle &&
- options.rx === 5
- );
- }
-
- for (i = 0; i < 33; i += 1) {
- out.push([ 'move-to', validateMove ]);
- out.push([ 'ellipse', validateStar ]);
- }
-
- return out;
- },
- { override: true }
- ],
- [
- 'Now, make sure you\'re out of the for loop (not indented) and **type** `color purple`',
- 'color purple',
- [
- [ 'color', { color: palette.purple } ]
- ]
- ],
- [
- 'Now `moveTo 250, 250`',
- 'moveTo 250, 250',
- [
- [ 'move-to', { x: 250, y: 250 } ]
- ]
- ],
- [
- 'Now draw a circle with a size of 170',
- 'circle 170',
- [
- [ 'ellipse', { rx: 170, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to white',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Choose a thick, white stroke - in a new line, **type** `stroke white, 5`',
- 'stroke white, 5',
- [
- [ 'stroke-color', { color: palette.white } ],
- [ 'stroke-width', { width: 5 } ]
- ]
- ],
- [
- 'Now draw an ellipse - **type** `ellipse 220, 4`',
- 'ellipse 220, 4',
- [
- [ 'ellipse', { rx: 220, ry: 4 } ]
- ]
- ]
- ])
-};
\ No newline at end of file
diff --git a/lib/challenges/random.js b/lib/challenges/random.js
deleted file mode 100644
index 2d5b1ab54..000000000
--- a/lib/challenges/random.js
+++ /dev/null
@@ -1,80 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'random',
- title : 'Random!',
- description : 'Not sure where to put something - theres a function for that!',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Let\'s move the cursor somewhere random - **type** `moveTo (random 1, 500), (random 1, 500)`',
- 'moveTo random(1, 500), random(1, 500)',
- [
- [ 'move-to', function (options) {
- return (
- options.x > 0 && options.x <= 500 &&
- options.y > 0 && options.y <= 500
- );
- } ]
- ]
- ],
- [
- 'Set the color to red - **type** `color red`',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Now let\'s draw a circle in a random place - **type** `circle 200`',
- 'circle 200',
- [
- [ 'ellipse', { rx: 200, isCircle: true } ]
- ]
- ],
- [
- 'Set the drawing color to green',
- 'color green',
- [
- [ 'color', { color: palette.green } ]
- ]
- ],
- [
- 'Now draw a circle with size 150',
- 'circle 150',
- [
- [ 'ellipse', { rx: 150, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to yellow',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Now draw a circle with size 100',
- 'circle 100',
- [
- [ 'ellipse', { rx: 100, isCircle: true } ]
- ]
- ],
- [
- 'Set the color to blue',
- 'color blue',
- [
- [ 'color', { color: palette.blue } ]
- ]
- ],
- [
- 'Finish it of with a circle with a size of 50',
- 'circle 50',
- [
- [ 'ellipse', { rx: 50, isCircle: true } ]
- ]
- ],
-
- ])
-};
diff --git a/lib/challenges/scripts/change_cover.js b/lib/challenges/scripts/change_cover.js
new file mode 100644
index 000000000..83777cf98
--- /dev/null
+++ b/lib/challenges/scripts/change_cover.js
@@ -0,0 +1,36 @@
+"use strict";
+var fs = require('fs'),
+ challenges = [
+ 'japan',
+ 'sweden',
+ 'stare',
+ 'smiley',
+ 'baloon',
+ 'stickman',
+ 'shrinking',
+ 'random',
+ 'starry',
+ 'house',
+ // 'medal',
+ 'dots',
+ 'gradient',
+ 'planet',
+ 'pizza',
+ 'breakfast'
+ ],
+ baseDir = '../worlds/basic/';
+
+challenges.forEach(function (ch) {
+ var obj = require(baseDir + ch),
+ formattedJSON;
+ obj.cover = obj.id + '.png';
+ formattedJSON = JSON.stringify(obj, null, 4);
+ fs.writeFile(baseDir + ch + '.json', formattedJSON, function (err) {
+ if (err) {
+ throw err;
+ }
+ });
+ console.log(" --------- " + ch);
+ //console.log(formattedJSON);
+
+});
diff --git a/lib/challenges/scripts/sc_converter.js b/lib/challenges/scripts/sc_converter.js
new file mode 100644
index 000000000..e9139d661
--- /dev/null
+++ b/lib/challenges/scripts/sc_converter.js
@@ -0,0 +1,80 @@
+"use strict";
+/**
+ * Script used to refactor old summercamp challenges to new ones.
+ * @type {[type]}
+ */
+var fs = require('fs'),
+ slugify = require('slugify'),
+ challenges = [
+ 'day_one',
+ 'day_two',
+ 'day_three',
+ 'day_four',
+ 'day_five',
+ 'day_six',
+ 'day_seven',
+ 'day_eight',
+ 'day_nine',
+ 'day_ten',
+ 'day_eleven',
+ 'day_twelve',
+ 'day_thirteen',
+ 'day_fourteen',
+ 'day_fifteen',
+ 'day_sixteen',
+ 'day_seventeen',
+ 'day_eighteen',
+ 'day_nineteen',
+ 'day_twenty',
+ 'day_twentyone'
+ ],
+ baseDir = "../summer_camp/",
+ jsonBaseDir = "../worlds/summercamp/",
+ arr = [];
+
+console.log("Length " + challenges.length);
+challenges.forEach(function (fileName) {
+ var challenge = require(baseDir + fileName),
+ solution = (challenge.steps[challenge.steps.length - 1]) ? challenge.steps[challenge.steps.length - 1].solution : "",
+ solLength = solution.split('\n').length,
+ stepsLength = challenge.steps.length,
+ formattedJSON,
+ title,
+ slugSource = challenge.short_title || challenge.title;
+ //arr = [];
+
+ title = slugify(slugSource.toLowerCase(), "_");
+ /*challenge.id = title;
+ challenge.steps.forEach(function (step, idx) {
+
+ var sol = step.solution.split('\n');
+ step.solution = sol[idx];
+ });
+
+
+ console.log(" ------------ " + title);
+
+ if (solLength !== stepsLength) {
+ console.log("----- " + title + " requires more attention");
+ }
+
+ formattedJSON = JSON.stringify(challenge, null, 4);
+ fs.writeFile(jsonBaseDir + title + '.json', formattedJSON, function (err) {
+ if (err) {
+ throw err;
+ }
+ });
+*/
+ /*
+ challenge.steps.forEach(function(step) {
+ console.log(JSON.stringify(step, null, 4));
+ });*/
+
+ //console.log(JSON.stringify(challenge, null, 4));
+ arr.push("./" + title);
+
+
+});
+
+ console.log(JSON.stringify(arr, null, 4));
+
diff --git a/lib/challenges/scripts/scriptsconverter.js b/lib/challenges/scripts/scriptsconverter.js
new file mode 100644
index 000000000..1c4777da9
--- /dev/null
+++ b/lib/challenges/scripts/scriptsconverter.js
@@ -0,0 +1,75 @@
+"use strict";
+
+var fs = require('fs'),
+ challenges = [
+ 'japan',
+ 'sweden',
+ 'stare',
+ 'smiley',
+ 'baloon',
+ 'stickman',
+ 'shrinking',
+ 'random',
+ 'starry',
+ 'house',
+ // 'medal',
+ 'dots',
+ 'gradient',
+ 'planet',
+ 'pizza',
+ 'breakfast'
+ ],
+ baseDir = "../",
+ jsonBaseDir = "../descriptors/",
+ SUCCESS_MESSAGES = [
+ 'Nice work! That is one cool flag!', //Japan
+ 'Cool beans - enough of flags, let\'s try something else!', //Sweden
+ 'You Wizard! The next time I need spooky eyes I know who to call!', //Stare
+ 'Nice! Keep up the good work you face drawing genius!', //Smiley
+ 'Awesome balloon! Why not change the color before moving on?', //Blue balloon
+ 'Nice one! The canvas is 500 wide and 500 high, moving about on it is a skill!', //Stickman
+ 'Great! For loops let us repeat bits of code (it saves on all the typing)!', //Shrinking
+ 'Random functions give us a random number so we can put things in a surprise spot.', //random
+ 'Awesome! Press space and see what happens to the stars...', //Starry
+ 'You built an awesome house with code!', //House
+ 'Mathematical! Change the numbers and see what happens.', //Dots
+ 'Try changing the numbers in the rotate function and see what happens.', //Gradient
+ 'Astronomical! Change the colors and see what you can add to make your planet even cooler before hitting share!', //planet
+ 'Tasty! Finish it off with more toppings, let your imagination run wild and see what you can make, then share it with the world!', //Pizza
+ 'Well done!', //null
+ ];
+
+console.log("Length " + challenges.length + SUCCESS_MESSAGES.length);
+challenges.forEach(function (fileName, idx) {
+ var challenge = require(baseDir + fileName),
+ solution = challenge.steps[challenge.steps.length - 1].solution,
+ solLength = solution.split('\n').length,
+ stepsLength = challenge.steps.length,
+ formattedJSON;
+
+ challenge.completion_text = SUCCESS_MESSAGES[idx];
+ challenge.steps.forEach(function (step, idx) {
+ var sol = step.solution.split('\n');
+ step.solution = sol[idx];
+ step.validate = "^" + sol[idx] + "$";
+ });
+
+
+
+ if (solLength !== stepsLength) {
+ console.log("----- " + fileName + " requires more attention");
+ }
+
+ formattedJSON = JSON.stringify(challenge, null, 4);
+ fs.writeFile(jsonBaseDir + fileName + '.json', formattedJSON, function (err) {
+ if (err) {
+ throw err;
+ }
+ });
+
+
+ /*challenge.steps.forEach(function(step) {
+ console.log(JSON.stringify(step, null, 4));
+ });*/
+
+});
diff --git a/lib/challenges/scripts/solve.js b/lib/challenges/scripts/solve.js
new file mode 100644
index 000000000..cf4935c8a
--- /dev/null
+++ b/lib/challenges/scripts/solve.js
@@ -0,0 +1,18 @@
+/**
+ * Solves a challenge specified in the filename
+ */
+"use strict";
+function run() {
+
+ var fileName = process.argv[2],
+ challenge;
+ if (!fileName) {
+ throw "Missing name of the challenge use: \n node solve.js challengePath";
+ }
+ challenge = require(fileName);
+ challenge.steps.forEach(function (step) {
+ console.log(step.solution);
+ });
+}
+
+run();
diff --git a/lib/challenges/shrinking.js b/lib/challenges/shrinking.js
deleted file mode 100644
index 072987b31..000000000
--- a/lib/challenges/shrinking.js
+++ /dev/null
@@ -1,48 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'shrinking',
- title : 'Shrinking Circles',
- description : 'Ever shrinking circles with a for loop!',
- code : '',
- startAt : 1,
- steps : generate.fromSequence([
- [
- 'Set the stroke - **type** `stroke gray, 3`',
- 'stroke gray, 3',
- [
- [ 'stroke-color', { color: palette.gray } ],
- [ 'stroke-width', { width: 3 } ]
- ]
- ],
- [
- 'Set the color to see through - **type** `color null`',
- 'color null',
- [
- [ 'color', { color: null } ]
- ]
- ],
- [
- 'Let\'s draw 32 circles all at once - **type** `for i in [ 0 .. 32 ]`',
- 'for i in [ 0 .. 32 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '0..32' } ]
- ]
- ],
- [
- 'Awesome, now for the circles - inside the for loop **type** `circle 10 * i`',
- ' circle 10 * i',
- function () {
- var i = 0,
- out = [];
-
- for (i = 0; i < 33; i += 1) {
- out.push([ 'ellipse', { isCircle: true, rx: i * 10 } ]);
- }
-
- return out;
- }
- ]
- ])
-};
\ No newline at end of file
diff --git a/lib/challenges/smiley.js b/lib/challenges/smiley.js
deleted file mode 100644
index d5043dbd6..000000000
--- a/lib/challenges/smiley.js
+++ /dev/null
@@ -1,82 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'smiley',
- title : 'Your first face',
- description : 'Draw a face',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the drawing color to yellow - **type** `color yellow`',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Choose a thick black stroke - **type** `stroke black, 20`',
- 'stroke black, 20',
- [
- [ 'stroke-color', { color: palette.black } ],
- [ 'stroke-width', { width: 20 } ]
- ],
- ],
- [
- 'Draw a circle with a size of 200',
- 'circle 200',
- [
- [ 'ellipse', { rx: 200, isCircle: true } ]
- ]
- ],
- [
- 'Move left and up by 80 - **type** `move -80, -80`',
- 'move -80, -80',
- [
- [ 'move-to', { dx: -80, dy: -80 } ]
- ]
- ],
- [
- 'Set the drawing color to black',
- 'color black',
- [
- [ 'color', { color: palette.black} ]
- ]
- ],
- [
- 'Draw a circle with a radius of 20',
- 'circle 20',
- [
- [ 'ellipse', { rx: 20, isCircle: true } ]
- ]
- ],
- [
- 'Move right by 160 - **type** `move 160`',
- 'move 160',
- [
- [ 'move-to', { dx: 160, dy: 0 } ]
- ]
- ],
- [
- 'Draw another circle with a size of 20',
- 'circle 20',
- [
- [ 'ellipse', { rx: 20, isCircle: true } ]
- ]
- ],
- [
- 'Move to the center and down - In a new line, **type** `moveTo 250, 270`',
- 'moveTo 250, 270',
- [
- [ 'move-to', { x: 250, y: 270 } ]
- ]
- ],
- [
- 'An arc is part of a circle, we can draw one as the mouth - **type** `arc 100, 1, 2`',
- 'arc 100, 1, 2',
- [
- [ 'arc', { radius: 100, start: 1, end: 2 } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/snowman.js b/lib/challenges/snowman.js
deleted file mode 100644
index 36119f202..000000000
--- a/lib/challenges/snowman.js
+++ /dev/null
@@ -1,185 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'snowman',
- title : 'Snowman',
- description : 'Draw a snowman using circles and loops',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the `background` to `darkblue`',
- 'background darkblue',
- [
- [ 'background', { color: palette.darkblue } ]
- ]
- ],
- [
- 'Move down, ***type*** `move 0, 200`',
- 'move 0, 200',
- [
- [ 'move-to', { dx: 0, dy: 200 } ]
- ]
- ],
- [
- 'Set the `stroke` to `0`',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Set the `color` to `white`',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Draw a `circle` with a size of `200`',
- 'circle 200',
- [
- [ 'ellipse', { rx: 200, isCircle: true } ]
- ]
- ],
- [
- 'Move back up, ***type*** `move 0, -200`',
- 'move 0, -200',
- [
- [ 'move-to', { dx: 0, dy: -200 } ]
- ]
- ],
- [
- 'Set the `stroke` to `lightgray`, with a size of `4`',
- 'stroke lightgray, 4',
- [
- [ 'stroke-color', { color: palette.lightgray } ],
- [ 'stroke-width', { width: 4 } ]
- ]
- ],
- [
- 'Draw a `circle` with a size of `100`',
- 'circle 100',
- [
- [ 'ellipse', { rx: 100, isCircle: true } ]
- ]
- ],
- [
- 'Set the `stroke` to `0`',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Move left, ***type*** `move -60`',
- 'move 0, -60',
- [
- [ 'move-to', { dx: -60, dy: 0 } ]
- ]
- ],
- [
- 'Set the `color` to `black`',
- 'color black',
- [
- [ 'color', { color: palette.black } ]
- ]
- ],
- [
- 'Draw a `circle` with a size of `10`',
- 'circle 10',
- [
- [ 'ellipse', { rx: 10, isCircle: true } ]
- ]
- ],
- [
- 'Move right, ***type*** `move 120`',
- 'move 0, 120',
- [
- [ 'move-to', { dx: 120, dy: 0 } ]
- ]
- ],
- [
- 'Draw a `circle` with a size of `10`',
- 'circle 10',
- [
- [ 'ellipse', { rx: 10, isCircle: true } ]
- ]
- ],
- [
- 'Move left and down, ***type*** `move -60, 30`',
- 'move -60, 30',
- [
- [ 'move-to', { dx: -60, dy: 30 } ]
- ]
- ],
- [
- 'Set the `color` to `orange`',
- 'color orange',
- [
- [ 'color', { color: palette.orange } ]
- ]
- ],
- [
- 'Draw a `circle` with a size of `15`',
- 'circle 15',
- [
- [ 'ellipse', { rx: 15, isCircle: true } ]
- ]
- ],
- [
- 'Set the `color` to `white`',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Now open a loop - ***type*** `for i in [ 0 .. 50 ]`',
- '\nfor i in [ 0 .. 50 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '0..50' } ]
- ]
- ],
- [
- 'Move around the canvas - **type** `moveTo (random 1, 500), (random 1, 500)`',
- ' moveTo (random 1, 500), (random 1, 500)',
- function () {
- var i = 0,
- out = [];
-
- for (i = 0; i < 51; i += 1) {
- out.push([ 'move-to', function (options) {
- return (
- options.x > 0 && options.x <= 500 &&
- options.y > 0 && options.y <= 500
- );
- } ]);
- }
-
- return out;
- }
- ],
- [
- 'Draw the snow! Add `circle 5`',
- ' circle 5',
- function () {
- var i = 0,
- out = [];
-
- for (i = 0; i < 51; i += 1) {
- out.push([ 'move-to', function (options) {
- return (
- options.x > 0 && options.x <= 500 &&
- options.y > 0 && options.y <= 500
- );
- } ]);
- out.push([ 'ellipse', { rx: 5, isCircle: true }]);
- }
-
- return out;
- },
- { override: true }
- ]
- ])
-};
\ No newline at end of file
diff --git a/lib/challenges/stare.js b/lib/challenges/stare.js
deleted file mode 100644
index feffd32b2..000000000
--- a/lib/challenges/stare.js
+++ /dev/null
@@ -1,89 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'stare',
- title : 'Stare in the dark',
- description : 'Draw a pair of staring eyes in the dark',
- startAt : 2,
- steps : generate.fromSequence([
- [
-
- 'Set the background to black',
- 'background black',
- [
- [ 'background', { color: palette.black } ]
- ],
- ],
- [
- 'Move to the left by 80 - **type** `move -80`',
- 'move -80',
- [
- [ 'move-to', { dx: -80, dy: 0 } ]
- ]
- ],
- [
- 'Set the drawing color to white',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Draw an ellipse, it\'s like a stretched circle - **type** `ellipse 60, 40`',
- 'ellipse 60, 40',
- [
- [ 'ellipse', { rx: 60, ry: 40 } ]
- ]
- ],
- [
- 'Set the drawing color to black - **type** `color black`',
- 'color black',
- [
- [ 'color', { color: palette.black } ],
- ]
- ],
- [
- 'Draw a circle with a size of 10 - **type** `circle 10`',
- 'circle 10',
- [
- [ 'ellipse', { rx: 10, isCircle: true } ]
- ]
- ],
- [
- 'Move to the right by 160 - **type** `move 160`',
- 'move 160',
- [
- [ 'move-to', { dx: 160, dy: 0 } ]
- ]
- ],
- [
- 'Set the drawing color to white again',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Now draw another ellipse with width 60 and height 40 - **type** `ellipse 60, 40`',
- 'ellipse 60, 40',
- [
- [ 'ellipse', { rx: 60, ry: 40 } ]
- ]
- ],
- [
- 'Set the drawing color to black',
- 'color black',
- [
- [ 'color', { color: palette.black } ]
- ]
- ],
- [
- 'Finish it up by drawing a circle with a size of 10',
- 'circle 10',
- [
- [ 'ellipse', { rx: 10, isCircle: true } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/starry.js b/lib/challenges/starry.js
deleted file mode 100644
index d95b4c20f..000000000
--- a/lib/challenges/starry.js
+++ /dev/null
@@ -1,90 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'starry-sky',
- title : 'Starry sky',
- description : 'Code your own starry night sky using the random function!',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the background to darkblue',
- 'background darkblue',
- [
- [ 'background', { color: palette.darkblue } ]
- ]
- ],
- [
- 'Now set the stroke to 0',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'And set the drawing color to yellow',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Now let\'s open a loop - **type** `for i in [ 0 .. 32 ]`',
- 'for i in [ 0 .. 32 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '0..32' } ]
- ]
- ],
- [
- 'Now to set where each star will go - **type** `moveTo (random 1, 500), (random 1, 500)`',
- ' moveTo (random 1, 500), (random 1, 500)',
- function () {
- var i = 0,
- out = [];
-
- function validateMove(options) {
- return (
- options.x > 0 && options.x <= 500 &&
- options.y > 0 && options.y <= 500
- );
- }
-
- for (i = 0; i < 33; i += 1) {
- out.push([ 'move-to', validateMove ]);
- }
-
- return out;
- }
- ],
- [
- 'Finally draw each star as a circle with a size of 5',
- ' circle 5',
- function () {
- var i = 0,
- out = [];
-
- function validateMove(options) {
- return (
- options.x > 0 && options.x <= 500 &&
- options.y > 0 && options.y <= 500
- );
- }
-
- function validateStar(options) {
- return (
- options.isCircle &&
- options.rx === 5
- );
- }
-
- for (i = 0; i < 33; i += 1) {
- out.push([ 'move-to', validateMove ]);
- out.push([ 'ellipse', validateStar ]);
- }
-
- return out;
- },
- { override: true }
- ],
- ])
-};
diff --git a/lib/challenges/stickman.js b/lib/challenges/stickman.js
deleted file mode 100644
index cc4baa579..000000000
--- a/lib/challenges/stickman.js
+++ /dev/null
@@ -1,95 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'stickman',
- title : 'Stickman',
- description : 'Draw a stickman using circles and lines',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the `stroke` to `black` with a size of `10`',
- 'stroke black, 10',
- [
- [ 'stroke-color', { color: palette.black } ],
- [ 'stroke-width', { width: 10 } ]
- ]
- ],
- [
- 'Move up on the canvas, **type** `move 0, -50`',
- 'move 0, -50',
- [
- [ 'move-to', { dx: 0, dy: -50 } ]
- ]
- ],
- [
- 'Draw the stickman body - `line 0, 150`',
- 'line 0, 150',
- [
- [ 'line', { dx: 0, dy: 150 } ]
- ]
- ],
- [
- 'Draw the stickman left arm - `line -80, 120`',
- 'line -80, 120',
- [
- [ 'line', { dx: -80, dy: 120 } ]
- ]
- ],
- [
- 'Good, now with the right arm - `line 80, 120`',
- 'line 80, 120',
- [
- [ 'line', { dx: 80, dy: 120 } ]
- ]
- ],
- [
- 'Move down, **type** `move 0, 150`',
- 'move 0, 150',
- [
- [ 'move-to', { dx: 0, dy: 150 } ]
- ]
- ],
- [
- 'Draw the stickman left leg: `line -80, 120`',
- 'line -80, 120',
- [
- [ 'line', { dx: -80, dy: 120 } ]
- ]
- ],
- [
- 'Good, now with the right leg - `line 80, 120`',
- 'line 80, 120',
- [
- [ 'line', { dx: 80, dy: 120 } ]
- ]
- ],
- [
- 'Move to the top of the drawing, **type** `moveTo 250, 100`',
- 'moveTo 250, 100',
- [
- [ 'move-to', { x: 250, y: 100 } ]
- ]
- ],
- [
- 'Set the stroke back to 0',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Now to draw a many sided shape - **type** `polygon -60, 50, 0, 100 , 60, 50`',
- 'polygon -60, 50, 0, 100 , 60, 50',
- [
- [ 'polygon', { points: [
- { x: 0, y: 0 },
- { x: -60, y: 50 },
- { x: 0, y: 100 },
- { x: 60, y: 50 }
- ] } ]
- ]
- ],
-
- ])
-};
diff --git a/lib/challenges/summer_camp/day_eight.js b/lib/challenges/summer_camp/day_eight.js
deleted file mode 100644
index 912b6c574..000000000
--- a/lib/challenges/summer_camp/day_eight.js
+++ /dev/null
@@ -1,242 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_eight',
- title : 'Pack your things',
- short_title : 'Backpack',
- icon_class : 'challenge_backpack',
- description : 'Dick Kelty is the inventor of the aluminium frame backpack. Kelty got the idea for his backpack in 1951 when he and a friend were hiking in the Sierra Nevada. Working in his garage, and with $500 borrowed against his house, Kelty and his wife went into production; he cut and welded the aluminium frames, while she stitched the nylon. They sold the finished backpacks for $24 apiece.',
- img : '/assets/summercamp/ch_pics/day_8.png',
- completion_text: 'I have a big challenge for you: try drawing your character behind that backpack! Head, arms, legs… ',
- difficulty : 1,
- startAt : 0,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'It\'s a beautiful day to go for a walk in the forest - **type** `background blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Firstly, set the `stroke` to `0`, to avoid drawing the outlines of the shapes',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Move the cursor to place the floor - in a new line **type** `moveTo 0, 250`',
- 'moveTo 0, 250',
- [
- [ 'move-to', { x: 0, y: 250 } ]
- ]
- ],
- [
- 'Set the `color` of the grass to `green`',
- 'color green',
- [
- [ 'color', { color: palette.green } ]
- ]
- ],
- [
- 'Draw the floor with a rectangle - **type** `rectangle 500, 250`',
- 'rectangle 500, 250',
- [
- [ 'rectangle', { width: 500, height: 250 } ]
- ]
- ],
- [
- 'This looks like a nice place for a tree - **type** `moveTo 220`',
- 'moveTo 220',
- [
- [ 'move-to', { x: 220, y: 0 } ]
- ]
- ],
- [
- 'Set the `color` of the trunk to `lightbrown`',
- 'color lightbrown',
- [
- [ 'color', { color: palette.lightbrown } ]
- ]
- ],
- [
- 'Draw the trunk with a `rectangle` of size `50` by `400`',
- 'rectangle 50, 400',
- [
- [ 'rectangle', { width: 50, height: 400 } ]
- ]
- ],
- [
- 'Beautiful! Now it just needs some leaves - **type** `move 30, -120` to place them',
- 'move 30, -120',
- [
- [ 'move-to', { dx: 30, dy: -120 } ]
- ]
- ],
- [
- 'Set the `color` of the leaves to `darkgreen`',
- 'color darkgreen',
- [
- [ 'color', { color: palette.darkgreen } ]
- ]
- ],
- [
- 'Draw a `circle` to represent the leaves with size `200`',
- 'circle 200',
- [
- [ 'ellipse', { rx: 200, isCircle: true } ]
- ]
- ],
- [
- 'Looking great! Let\'s move the cursor to place our backpack - **type** `moveTo 150, 200`',
- 'moveTo 150, 200',
- [
- [ 'move-to', { x: 150, y: 200 } ]
- ]
- ],
- [
- 'Let\'s choose `color` `brown` for our backpack',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Draw the main part of the rucksack with a square - **type** `square 200`',
- 'square 200',
- [
- [ 'rectangle', { width: 200, height: 200 } ]
- ]
- ],
- [
- 'Now move the cursor to bottom part of our backpack - **type** `moveTo 250, 400`',
- 'moveTo 250, 400',
- [
- [ 'move-to', { x: 250, y: 400 } ]
- ]
- ],
- [
- 'Draw the bottom using an ellipse - **type** `ellipse 100, 30`',
- 'ellipse 100, 30',
- [
- [ 'ellipse', { rx: 100, ry: 30 } ]
- ]
- ],
- [
- 'We need to store the sleeping bag at the top - **type** `moveTo 250, 200`',
- 'moveTo 250, 200',
- [
- [ 'move-to', { x: 250, y: 200 } ]
- ]
- ],
- [
- 'Set the `color` of the sleeping bag to `darkred`',
- 'color darkred',
- [
- [ 'color', { color: palette.darkred } ]
- ]
- ],
- [
- 'Ready to draw the sleeping bag? Use an ellipse - **type** `ellipse 115, 40`',
- 'ellipse 115, 40',
- [
- [ 'ellipse', { rx: 115, ry: 40 } ]
- ]
- ],
- [
- 'We need something to secure your sleeping bag to your backpack - **type** `moveTo 200, 160`',
- 'moveTo 200, 160',
- [
- [ 'move-to', { x: 200, y: 160 } ]
- ]
- ],
- [
- 'The `orangered` seems like a nice `color` for the holders',
- 'color orangered',
- [
- [ 'color', { color: palette.orangered } ]
- ]
- ],
- [
- 'Draw the left one first - **type** `rectangle 15, 90`',
- 'rectangle 15, 90',
- [
- [ 'rectangle', { width: 15, height: 90 } ]
- ]
- ],
- [
- 'Position the cursor to the right to draw the next holder - **type** `moveTo 290, 160`',
- 'moveTo 290, 160',
- [
- [ 'move-to', { x: 290, y: 160 } ]
- ]
- ],
- [
- 'Use a `rectangle` again for the right holder, same as the previous one',
- 'rectangle 15, 90',
- [
- [ 'rectangle', { width: 15, height: 90 } ]
- ]
- ],
- [
- 'Excellent! We need more storage space. Move the cursor to the middle - **type** `moveTo 205, 320`',
- 'moveTo 205, 320',
- [
- [ 'move-to', { x: 205, y: 320 } ]
- ]
- ],
- [
- 'Use a `rectangle` for the outside pocket, `90` by `60` will suffice',
- 'rectangle 90, 60',
- [
- [ 'rectangle', { width: 90, height: 60 } ]
- ]
- ],
- [
- 'That pocket needs to be closed so bugs can\'t get in - **type** `moveTo 250, 320`',
- 'moveTo 250, 320',
- [
- [ 'move-to', { x: 250, y: 320 } ]
- ]
- ],
- [
- 'Set the `color` to `lightbrown`',
- 'color lightbrown',
- [
- [ 'color', { color: palette.lightbrown } ]
- ]
- ],
- [
- 'An `ellipse` of size `48` by `7` will help here',
- 'ellipse 48, 7',
- [
- [ 'ellipse', { rx: 48, ry: 7 } ]
- ]
- ],
- [
- 'Looking great! Let\’s place a button on that pocket - **type** `moveTo 250, 330`',
- 'moveTo 250, 330',
- [
- [ 'move-to', { x: 250, y: 330 } ]
- ]
- ],
- [
- 'Set the `color` to `brown`',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Draw a `circle` button of size `10`',
- 'circle 10',
- [
- [ 'ellipse', { rx: 10, isCircle: true } ]
- ]
- ],
- ])
-};
diff --git a/lib/challenges/summer_camp/day_eighteen.js b/lib/challenges/summer_camp/day_eighteen.js
deleted file mode 100644
index 975c5b89e..000000000
--- a/lib/challenges/summer_camp/day_eighteen.js
+++ /dev/null
@@ -1,295 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_eighteen',
- title : 'Hiking Poster',
- short_title : 'Hiking',
- icon_class : 'challenge_hiking',
- description : 'The beautiful Camp Kano mountain range is known for its deep blue colour. The ten mountains in the range always seem to be changing position though, which makes for a dangerous climb. Should campers attempt to climb it? Who knows! But management wants an advertising posts so get to it.',
- img : '/assets/summercamp/ch_pics/day_18.png',
- completion_text: 'Well done, you’ve made a beautiful poster! Now play around with the random values and the text. What else is the poster missing?',
- difficulty : 1,
- startAt : 0,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'Up above the clouds the sky is blue, **type** `background blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Camp Kano’s mountains are a beautiful shade of dark blue. Set the drawing colour with `color darkblue`.',
- 'color darkblue',
- [
- [ 'color', { color: palette.darkblue } ]
- ]
- ],
- [
- 'The mountains also have a nice thick border, give them `stroke 10, white`',
- 'stroke 10, white',
- [
- [ 'stroke-color', { color: palette.white } ],
- [ 'stroke-width', { width: 10 } ]
- ]
- ],
-
-
- [
- 'Lets draw the ten mountains of Camp Kano with a loop! This will be much faster than drawing them individually. **Type** `for i in [ 0 ... 10 ]` to open the loop.',
- 'for i in [ 0 ... 10 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '0...10' } ]
- ]
- ],
- [
- 'For every time the loop runs, we want a mountain’s peak to be drawn randomly across the screen. Let’s select an x value with `x = random 0, 500`',
- 'x = random 0, 500',
- function () { // None of the looping works with or without the
- var out = [];
- for (var i = 0; i < 10; i++) {
- out.push([ 'var', function (opts) {
- return opts.name === 'x' && opts.value === 'random0,500';
- }]);
- }
- return out;
- }
- ],
- [
- 'However, for the y value, we only want each mountain’s peak to be drawn on the top part of the screen. **Type** `y = random 0, 200`',
- 'y = random 0, 200',
- function () {
- var out = [];
- var i = 0;
- for (i = 0; i < 10; i++) {
- out.push([ 'var', function (opts) {
- return opts.name === 'x' && opts.value === 'random0,500';
- }]);
- out.push([ 'var', function (opts) {
- return opts.name === 'y' && opts.value === 'random0,200'
- }]);
- }
- return out;
- },
- {override: true}
- ],
- [
- 'Now with our x and y values set we can move the cursor to them. **Type** `moveTo x, y`',
- 'moveTo x, y',
- function () {
- var out = [];
- for (var i = 0; i < 10; i++) {
- out.push([ 'var', function (opts) {
- return opts.name === 'x' && opts.value === 'random0,500';
- }]);
- out.push([ 'var', function (opts) {
- return opts.name === 'y' && opts.value === 'random0,200'
- }]);
- out.push(
- ['move-to', function (opts) {
- return (opts.x >= 0 && opts.x <= 500) && (opts.y > 0 && opts.y <= 200)
- }]
- );
- }
- return out;
- },
- {override: true}
- ],
- [
- 'Draw a mountain for every loop with the polygon function. **Type** `polygon 400, 500, -400, 500`',
- 'polygon 400, 500, -400, 500',
- function () {
- var out = [];
- var i = 0;
-
- for (i = 0; i < 10; i++) {
- out.push([ 'var', function (opts) {
- return opts.name === 'x' && opts.value === 'random0,500';
- }]);
- out.push([ 'var', function (opts) {
- return opts.name === 'y' && opts.value === 'random0,200'
- }]);
- out.push(
- ['move-to', function (opts) {
- return (opts.x >= 0 && opts.x <= 500) && (opts.y > 0 && opts.y <= 200)
- }]
- );
- out.push(
- [ 'polygon', { points: [
- { x: 0, y: 0 },
- { x: 400, y: 500 },
- { x: -400, y: 500 }
- ]}]
- );
- }
- return out;
- },
- {override: true}
- ],
- [
- 'First, get out of the previous for loop’s indent by pressing `BACKSPACE`. Now let’s draw some clouds with `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Clouds are made of small droplets of water vapor, which is see-through! To get a see-through color we use the `opacity` function. **Type** `color (opacity white, .1)`',
- 'color(opacity white, .1)',
- [
- [ 'color', { color: 'rgba(255, 255, 255, 0.1)' } ]
- ]
- ],
- [
- 'Now let’s make a new loop to draw the cloud puffs. We’ll need a lot, so let’s make this a loop last 300 iterations. **Type** `for j in [ 0 ... 250 ]`.',
- 'for j in [ 0 ... 250 ]',
- [
- [ 'for-loop', { iterator: 'j', range: '0...250' } ]
- ]
- ],
- [
- 'We want cloud puffs sprinkled randomly across the screen, so lets choose an x value between 0 and 500 with`x = random 0, 500`',
- 'x = random 0, 500',
- function () {
- var out = [];
- var i=0;
- for (i = 0; i < 250; i++) {
- out.push([ 'var', function (opts) {
- return opts.name === 'x' && opts.value === 'random0,500';
- }]);
- }
- return out;
- }
- ],
- [
- 'However, for the y value, we only want the cloud puffs to be drawn on the bottom part of the screen. **Type** `y = random 300, 500`',
- 'y = random 300, 500',
- function () {
- var out = [];
- for (var i = 0; i < 250; i++) {
- out.push([ 'var', function (opts) {
- return opts.name === 'x' && opts.value === 'random0,500';
- }]);
- out.push([ 'var', function (opts) {
- return opts.name === 'y' && opts.value === 'random300,500';
- }]);
- }
- return out;
- },
- {override: true}
- ],
- [
- 'Now with our x and y values set we can move the cursor to them. **Type** `moveTo x, y`',
- 'moveTo x, y',
- function () {
- var out = [];
- for (var i = 0; i < 250; i++) {
- out.push([ 'var', function (opts) {
- return opts.name === 'x' && opts.value === 'random0,500';
- }]);
- out.push([ 'var', function (opts) {
- return opts.name === 'y' && opts.value === 'random300,500';
- }]);
- out.push(
- ['move-to', function (opts) {
- return (opts.x >= 0 && opts.x <= 500) && (opts.y >= 300 && opts.y <= 500);
- }]
- );
- }
- return out;
- },
- {override: true}
- ],
- [
- 'Let’s draw the cloud puff—a big transparent `circle` of size `100`',
- 'color blue',
- function () {
- var out = [];
- for (var i = 0; i < 250; i++) {
- out.push([ 'var', function (opts) {
- return opts.name === 'x' && opts.value === 'random0,500';
- }]);
- out.push([ 'var', function (opts) {
-
- return opts.name === 'y' && opts.value === 'random300,500';
- }]);
- out.push(
- ['move-to', function (opts) {
- return (opts.x >= 0 && opts.x <= 500) && (opts.y >= 300 && opts.y <= 500);
- }]
- );
- out.push([ 'ellipse', { rx: 100, isCircle: true } ]);
- }
- return out;
- },
- {override: true}
- ],
- [
- 'Press `BACKSPACE` once to get out of the indented line. Then lets move the cursor into place for some text with `moveTo 250, 350`',
- 'moveTo 250, 350',
- [
- [ 'move-to', { x: 250, y: 350 } ]
- ]
- ],
- [
- 'Set the drawing color to red - **type** `color red`',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Our message should be strong and impactful, so lets set the font to bold with `bold true`',
- 'bold true',
- [
- [ 'text-bold', {state: true} ]
- ]
- ],
- [
- 'We also want to style our text with italics, do this with `italic true`',
- 'bold true',
- [
- [ 'text-italic', { state: true } ]
- ]
- ],
- [
- 'Finally lets select Bariol at point size 40 with `font \'Bariol\', 40`',
- 'font \'Bariol\', 40',
- [
- [ 'font-family', { font: 'Bariol' } ],
- [ 'text-size', { size: '40' } ]
- ]
- ],
- [
- 'Start your message off with `text \'Hike the Blue Mountains of\'`',
- 'text \'Hike the Blue Mountains of\'',
- [
- [ 'text', { value: 'Hike the Blue Mountains of' } ]
- ]
- ],
- [
- 'Now for a new line with a big bold finish. **Type** `font 90`',
- 'font \'Bariol\', 90',
- [
- [ 'text-size', { size: '90' } ]
- ]
- ],
- [
- 'Lets move the cursor down into place for the final line with `move 0, 90`',
- 'move 0, 90',
- [
- [ 'move-to', { dx: 0, dy: 90 } ]
- ]
- ],
- [
- 'The finishing touch: **type** `text \'Camp Kano!\'`',
- 'text \'Camp Kano!\'',
- [
- [ 'text', { value: 'Camp Kano!' } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_eleven.js b/lib/challenges/summer_camp/day_eleven.js
deleted file mode 100644
index cd90c8771..000000000
--- a/lib/challenges/summer_camp/day_eleven.js
+++ /dev/null
@@ -1,156 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_eleven',
- title : 'Cheeky Bear',
- short_title : 'Bear',
- description : 'Bears are very smart and have been known to roll rocks into bear traps to set off the trap and then eat the bait in safety. These animals can run up to 40 miles per hour, fast enough to catch a running horse. Take into account that the fastest known human alive today is Usain Bolt, who can run at 27mph! And because bears can walk short distances on their hind legs, some Native Americans called them \“the beast that walks like a man.\”',
- icon_class : 'challenge_bear',
- img : '/assets/summercamp/ch_pics/day_11.png',
- completion_text: 'That cute bear needs a body, and a habitat. Use your code skills to impress other campers with your abilities! Remember that the icons on your left can help you achieve what you have in mind.',
- difficulty : 1,
- startAt : 0,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'The bear lives in the forest. Set the background color to green - **type** `background green`',
- 'background green',
- [
- [ 'background', { color: palette.green } ]
- ]
- ],
- [
- 'The bear has brown fur, let\'s start to draw his face by getting our paint ready - **type** `color brown`',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Draw the face with a circle - **type** `circle 100`',
- 'circle 100',
- [
- [ 'ellipse', { rx: 100, isCircle: true } ]
- ]
- ],
- [
- 'Now let\'s move the cursor to draw his left ear - **type** `move -80, -80`',
- 'move -80, -80',
- [
- [ 'move-to', { dx: -80, dy: -80 } ]
- ]
- ],
- [
- 'Draw the ear with a `circle` of size `30`',
- 'circle 30',
- [
- [ 'ellipse', { rx: 30, isCircle: true } ]
- ]
- ],
- [
- 'Move the cursor to the right to draw his other ear - **type** `move 160`',
- 'move 160',
- [
- [ 'move-to', { dx: 160 } ]
- ]
- ],
- [
- 'Draw another ear (just like the last one)',
- 'circle 30',
- [
- [ 'ellipse', { rx: 30, isCircle: true } ]
- ]
- ],
- [
- 'Now head over to where the left eye will go - **type** `move -110, 50`',
- 'move -110, 50',
- [
- [ 'move-to', { dx: -110, dy: 50 } ]
- ]
- ],
- [
- 'The rest of the bear\'s facial features will be `black`, so lets set the `color`',
- 'color black',
- [
- [ 'color', { color: palette.black } ]
- ]
- ],
- [
- 'Draw the first eye - **type** `circle 5`',
- 'circle 5',
- [
- [ 'ellipse', { rx: 5, isCircle: true } ]
- ]
- ],
- [
- 'Next move over to where the other eye goes - **type** `move 60`',
- 'move 60',
- [
- [ 'move-to', { dx: 60 } ]
- ]
- ],
- [
- 'Fill in the other eye just like the last one',
- 'circle 5',
- [
- [ 'ellipse', { rx: 5, isCircle: true } ]
- ]
- ],
- [
- 'Move to the center of the face for the bear\'s most distinctive feature - **type** `move -30, 50`',
- 'move -30, 50',
- [
- [ 'move-to', { dx: -30, dy: 50 } ]
- ]
- ],
- [
- 'Draw his nose with a triangle using polygon - **type** `polygon 12, -16, -12, -16`',
- 'polygon 12, -16, -12, -16',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 12, y: -16 },
- { x: -12, y: -16 }
- ] } ]
- ]
- ],
- [
- 'Let\'s get ready to draw the mouth by moving down a bit further - **type** `move 0, 20`',
- 'move 0, 20',
- [
- [ 'move-to', { dx: 0, dy: 20 } ]
- ]
- ],
- [
- 'Set the `stroke` (the outline of a shape) to size `3` and `black`',
- 'stroke black, 3',
- [
- [ 'stroke-color', { color: palette.black } ],
- [ 'stroke-width', { width: 3 } ]
- ]
- ],
- [
- 'We don\'t want the mouth to be filled so set its color to null - **type** `color null`',
- 'color null',
- [
- [ 'color', null ]
- ]
- ],
- [
- 'An arc is part of a cirlce, just like a smiling mouth - **type** `arc 15, 0.5, 2`',
- 'arc 15, 0.5, 2',
- [
- [ 'arc', { radius: 15, start: 0.5, end: 2 }]
- ]
- ]
- ])
-};
\ No newline at end of file
diff --git a/lib/challenges/summer_camp/day_fifteen.js b/lib/challenges/summer_camp/day_fifteen.js
deleted file mode 100644
index 1b7508c81..000000000
--- a/lib/challenges/summer_camp/day_fifteen.js
+++ /dev/null
@@ -1,194 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_fifteen',
- title : 'Bow and Arrow',
- short_title : 'Bow and Arrow',
- icon_class : 'challenge_bow',
- description : 'Shooting a bow and arrow at a target is an art that requires lots of focus. This challenge is to draw a bow and try to hold it steady while your hand jitters...just like the other humans who have used the bow and arrow for hunting since the dawn of civilization over ten thousand years ago. The oldest known bow, found in Denmark, dates from 9000 BCE!',
- img : '/assets/summercamp/ch_pics/day_15.png',
- completion_text: 'Click Refresh and watch the target move while you try to hold your bow steady! Try getting rid of the moving background to make it easier to hit the target, converting your bow to a crossbow, or drawing an arrow in the bullseye.',
- difficulty : 1,
- startAt : 0,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'The weather is good today...there is no wind to blow our arrows off course. Set the background color to blue - **type** `background blue`.',
- 'background lightblue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'We want the background to jitter, so we will start drawing it at a random point. **Type** `moveTo (random -20, 0), (random 240, 260)`.',
- 'moveTo (random -20, 0), (random 240, 260)',
- [
- [ 'move-to', function (options) {
- return options.x >= -20
- && options.x <= 0
- && options.y >= 240
- && options.y <= 260
- }]
- ]
- ],
- [
- 'Our target is in a grassy field, so let\s get our `color green` ready',
- 'color green',
- [
- [ 'color', { color: palette.green } ]
- ]
- ],
- [
- 'Draw a big `rectangle` that is `550` pixels high and `300` pixels wide for the grass',
- 'rectangle 550, 300',
- [
- [ 'rectangle', { width: 550, height: 300 } ]
- ]
- ],
- [
- 'Now let\'s move the cursor to draw our target - **type** `move 275, 10`',
- 'move 275, 10',
- [
- [ 'move-to', { dx: 275, dy: 10 } ]
- ]
- ],
- [
- 'First we will draw the wooden legs supporting it - **type** `stroke brown, 15`',
- 'stroke brown, 15',
- [
- [ 'stroke-color', { color: palette.brown } ],
- [ 'stroke-width', { width: 15 } ]
- ]
- ],
- [
- 'Draw the left leg - **type** `line -80, 120`...',
- 'line -80, 120',
- [
- [ 'line', { dx: -80, dy: 120 } ]
- ]
- ],
- [
- '...and now draw the right leg - **type** `line 80, 120`',
- 'line 80, 120',
- [
- [ 'line', { dx: 80, dy: 120 } ]
- ]
- ],
- [
- 'We will draw a target with white and red rings - **type** `stroke white, 50`',
- 'stroke white, 50',
- [
- [ 'stroke-color', { color: palette.white } ],
- [ 'stroke-width', { width: 50 } ]
- ]
- ],
- [
- 'Now make the inside of the circle red - **type** `color red`',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'First, draw a circle with a radius of 80 pixels- **type** `circle 80`',
- 'circle 80',
- [
- [ 'ellipse', { rx: 80, isCircle: true } ]
- ]
- ],
- [
- 'Second, draw a `circle` with a radius of `30` pixels',
- 'circle 30',
- [
- [ 'ellipse', { rx: 30, isCircle: true } ]
- ]
- ],
- [
- 'Move the cursor to draw an arrowhead - **type** `moveTo 250, 220`',
- 'moveTo 250, 220',
- [
- [ 'move-to', { x: 250, y: 220 } ]
- ]
- ],
- [
- 'Our arrowhead will have a thin gray outline - **type** `stroke gray, 1`',
- 'stroke gray, 1',
- [
- [ 'stroke-color', { color: palette.gray } ],
- [ 'stroke-width', { width: 1 } ]
- ]
- ],
- [
- 'The flint arrowhead is going to have the `color dimgray`',
- 'color dimgray',
- [
- [ 'color', { color: palette.dimgray } ]
- ]
- ],
- [
- 'Draw the arrowhead as a diamond - **type** `polygon -10, 40, 0, 80, 10, 40`',
- 'polygon -10, 40, 0, 80, 10, 40',
- [
- [ 'polygon', { points: [
- { x: 0, y: 0 },
- { x: -10, y: 40 },
- { x: 0, y: 80 },
- { x: 10, y: 40 }
- ] } ]
- ]
- ],
- [
- 'Next we will move to draw the shaft of the arrow - **type** `move 0, 40`',
- 'move 0, 40',
- [
- [ 'move-to', { dx: 0, dy: 40 } ]
- ]
- ],
- [
- 'The arrow will be made of a light brown wood - **type** `stroke lightbrown, 20`',
- 'stroke lightbrown, 20',
- [
- [ 'stroke-color', { color: palette.lightbrown } ],
- [ 'stroke-width', { width: 20 } ]
- ]
- ],
- [
- 'Draw a line for the shaft - **type** `line 200, 360`',
- 'line 200, 360',
- [
- [ 'line', { dx: 200, dy: 360 } ]
- ]
- ],
- [
- 'The last thing we will draw is our bow. Move the cursor off the screen - **type** `moveTo 800, 375`',
- 'moveTo 800, 375',
- [
- [ 'move-to', { x: 800, y: 375 } ]
- ]
- ],
- [
- 'We are going to draw an arc for the bow so let\'s draw it with a thick piece of dark brown wood - **type** `stroke darkbrown, 40`',
- 'stroke darkbrown, 40',
- [
- [ 'stroke-color', { color: palette.darkbrown } ],
- [ 'stroke-width', { width: 40 } ]
- ]
- ],
- [
- 'We don\'t want the arc to have any fill - **type** `color null`',
- 'color null',
- [
- [ 'color', null ]
- ]
- ],
- [
- 'Draw the bow with a big arc centered far off the screen - **type** `arc 500, 0, 2`',
- 'arc 500, 0, 2',
- [
- [ 'arc', { radius: 500, start: 0, end: 2 }]
- ]
- ],
- ])
-};
diff --git a/lib/challenges/summer_camp/day_five.js b/lib/challenges/summer_camp/day_five.js
deleted file mode 100644
index 51385e46a..000000000
--- a/lib/challenges/summer_camp/day_five.js
+++ /dev/null
@@ -1,91 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_five',
- title : 'Camp Badge',
- short_title : 'Camp Badge',
- icon_class : 'challenge_badge',
- description : 'First Aid is the most popular merit badge. In fact, 6.9 million Scouts have earned it since its debut in 1911. Other popular ones are Environmental Science, Search and Rescue, and our favourites Programming and Game Design! There are some oddballs as well. For example Truck transportation: \“Assume that you are going to ship by truck 500 pounds of goods (freight class 65) from your town to another town 500 miles away. Your shipment must arrive within three days. Explain in writing…\”. And Nuclear Science \“Obtain a sample of irradiated and non-irradiated foods. Prepare the two foods and compare their taste and texture.\”',
- completion_text: 'This badge will distinguish your camp from others. Surprise the world with an amazing creation.',
- img : '/assets/summercamp/ch_pics/day_5.png',
- difficulty : 1,
- startAt : 4,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'Set the `background` color to `black`',
- 'background black',
- [
- [ 'background', { color: palette.black } ]
- ]
- ],
- [
- 'Set the `color` of your badge to `lightblue`',
- 'color lightblue',
- [
- [ 'color', { color: palette.lightblue } ]
- ]
- ],
- [
- 'Set the stroke for the first circle - **type** `stroke red, 20`',
- 'stroke red, 20',
- [
- [ 'stroke-color', { color: palette.red } ],
- [ 'stroke-width', { width: 20 } ]
- ]
- ],
- [
- 'Draw a `circle` with a size of `200`',
- 'circle 200',
- [
- [ 'ellipse', { rx: 200, isCircle: true } ]
- ]
- ],
- [
- 'Set the stroke for the second circle - **type** `stroke yellow, 20`',
- 'stroke yellow, 20',
- [
- [ 'stroke-color', { color: palette.yellow } ],
- [ 'stroke-width', { width: 20 } ]
- ]
- ],
- [
- 'Draw a `circle` with a size of `190`',
- 'circle 190',
- [
- [ 'ellipse', { rx: 190, isCircle: true } ]
- ]
- ],
- [
- 'Set the `stroke` for the third circle to size `20` and `purple` color',
- 'stroke purple, 20',
- [
- [ 'stroke-color', { color: palette.purple } ],
- [ 'stroke-width', { width: 20 } ]
- ]
- ],
- [
- 'Draw a `circle` with a size of `180`',
- 'circle 180',
- [
- [ 'ellipse', { rx: 180, isCircle: true } ]
- ]
- ],
- [
- 'Add some decoration inside. Set the `color` to `green`',
- 'color green',
- [
- [ 'color', { color: palette.green } ]
- ]
- ],
- [
- 'An arc is part of a cirlce, draw one that matches the inner circle - **type** `arc 180, 1, 2`',
- 'arc 180, 1, 2',
- [
- [ 'arc', { radius: 180, start: 1, end: 2 } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_four.js b/lib/challenges/summer_camp/day_four.js
deleted file mode 100644
index ef162c965..000000000
--- a/lib/challenges/summer_camp/day_four.js
+++ /dev/null
@@ -1,123 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_four',
- title : 'Create your Flag',
- short_title : 'Flag',
- description : 'Time to show your true colors. Your campsite is shaping up, and as the campsite’s leader you need to make a flag. We’ve got you started with the basics for a flag and flagpole, but it is up to you to to make it your own. \nYou can look to other flags for inspiration. Countries like France, Nigeria, Sweden, and Switzerland all have very simple color and shape designs, while Spain, Brazil, Kenya, and Saudi Arabia all feature complex designs. When you are done, share your flag to lay claim to your little bit of land!',
- icon_class : 'challenge_flag',
- completion_text: 'You have a white canvas in front of you, use it wisely. Create the most amazing flag the world has ever seen!',
- img : '/assets/summercamp/ch_pics/day_4.png',
- difficulty : 1,
- startAt : 3,
- summerCamp : true,
- rewards : {'outfit': 1},
- steps : generate.fromSequence([
- [
- 'Draw the sky where the flag will flutter - **type** `background blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Let\'s move the cursor to place our flagpole in the correct place - **type** `moveTo 100, 100`',
- 'moveTo 100, 100',
- [
- [ 'move-to', { x: 100, y: 100 } ]
- ]
- ],
- [
- 'Set the `color` of the flagpole to `darkgray`',
- 'color darkgray',
- [
- [ 'color', { color: palette.darkgray } ]
- ]
- ],
- [
- 'Draw a long, thin pole with a rectangle - **type** `rectangle 10, 400`',
- 'rectangle 10, 400',
- [
- [ 'rectangle', { width: 10, height: 400 } ]
- ]
- ],
- [
- 'Move the cursor to place a ball on top-centre of the flagpole - **type** `move 5`',
- 'move 5',
- [
- [ 'move-to', { dx: 5, dy: 0 } ]
- ]
- ],
- [
- 'Set the `color` of the ornament to `gold`',
- 'color gold',
- [
- [ 'color', { color: palette.gold } ]
- ]
- ],
- [
- 'Draw a circle with a size of 8 - in a new line **type** `circle 8`',
- 'circle 8',
- [
- [ 'ellipse', { rx: 8, isCircle: true } ]
- ]
- ],
- [
- 'Now move the cursor to start drawing our flag - **type** `moveTo 115, 123`',
- 'moveTo 115, 123',
- [
- [ 'move-to', { x: 115, y: 123 } ]
- ]
- ],
- [
- 'Set the `color` of the flag to `white`',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Draw the flag with a `rectangle` of size `261` and `171`',
- 'rectangle 261, 171',
- [
- [ 'rectangle', { width: 261, height: 171 } ]
- ]
- ],
- [
- 'That flag needs something to hold of to - **type** `moveTo 100, 140`',
- 'moveTo 100, 140',
- [
- [ 'move-to', { x: 100, y: 140 } ]
- ]
- ],
- [
- 'Set the `color` of the holder to `brown`',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Draw the holder with a `20` by `5` `rectangle`',
- 'rectangle 20, 5',
- [
- [ 'rectangle', { width: 20, height: 5 } ]
- ]
- ],
- [
- 'Now is your turn! Draw an amazing pattern on it - **type** `moveTo 120, 125`',
- 'moveTo 120, 125',
- [
- [ 'move-to', { x: 120, y: 125 } ]
- ]
- ],
- ])
-};
diff --git a/lib/challenges/summer_camp/day_fourteen.js b/lib/challenges/summer_camp/day_fourteen.js
deleted file mode 100644
index b67afba51..000000000
--- a/lib/challenges/summer_camp/day_fourteen.js
+++ /dev/null
@@ -1,285 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_fourteen',
- title : 'Relax watching a movie',
- short_title : 'Cinema',
- icon_class : 'challenge_cinema',
- description : 'After playing a short movie presentation by the Skladanowsky brothers in 1895 which consisted of numerous very short 6-second films accompanied by a specially composed piece of music, the Berlin Wintergarten theatre in Mitte, Berlin, became known as the first ever movie theatre. Unfortunately the theatre was destroyed by bombs in 1944, during the Second World War.',
- img : '/assets/summercamp/ch_pics/day_14.png',
- completion_text: 'What good is a cinema without a movie playing? Go ahead and try drawing a scene from your favourite movie up on the big screen, and maybe a few friends to watch the film with you! Don\'t forget the popcorn!',
- difficulty : 2,
- startAt : 0,
- summerCamp : true,
- rewards : {'wallpaper': 1},
- steps : generate.fromSequence([
- [
- 'It\'s dark in the cinema. Set the `background` color to `black`',
- 'background black',
- [
- [ 'background', { color: palette.black } ]
- ]
- ],
- [
- 'Set the `stroke` to `0` in a new line',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'First, move the cursor to place the floor - **type** `moveTo 0, 300`',
- 'moveTo 0, 300',
- [
- [ 'move-to', { x: 0, y: 300 } ]
- ]
- ],
- [
- 'Set the floor `color` to `gray`',
- 'color gray',
- [
- [ 'color', { color: palette.gray } ]
- ]
- ],
- [
- 'Draw the floor with a rectangle - **type** `rectangle 500, 200`',
- 'rectangle 500, 200',
- [
- [ 'rectangle', { width: 500, height: 200 } ]
- ]
- ],
- [
- 'Now, move the cursor to place the screen - **type** `moveTo 50, 20`',
- 'moveTo 50, 20',
- [
- [ 'move-to', { x: 50, y: 20 } ]
- ]
- ],
- [
- 'Set the screen `color` to `white`',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'The screen is a perfect `rectangle` of size `400` by `250`',
- 'rectangle 400, 250',
- [
- [ 'rectangle', { width: 400, height: 250 } ]
- ]
- ],
- [
- 'Amazing! This theatre needs a curtain - **type** `moveTo 0`',
- 'moveTo 0',
- [
- [ 'move-to', { x: 0, y: 0 } ]
- ]
- ],
- [
- 'Set the curtain `color` to `red`',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Draw a vertical curtain as a `rectangle` of size `30` by `330`',
- 'rectangle 30, 330',
- [
- [ 'rectangle', { width: 30, height: 330 } ]
- ]
- ],
- [
- 'Now draw an horizontal curtain size `500` by `10` in the same way',
- 'rectangle 500, 10',
- [
- [ 'rectangle', { width: 500, height: 10 } ]
- ]
- ],
- [
- 'Looking great! Place the curtain on the right - **type** `move 470`',
- 'move 470',
- [
- [ 'move-to', { dx: 470, dy: 0 } ]
- ]
- ],
- [
- 'Draw a vertical curtain, same as the one the left',
- 'rectangle 30, 330',
- [
- [ 'rectangle', { width: 30, height: 330 } ]
- ]
- ],
- [
- 'This theatre needs some seats for our audience - **type** `moveTo 0, 360`',
- 'moveTo 0, 360',
- [
- [ 'move-to', { x: 0, y: 360 } ]
- ]
- ],
- [
- 'Set the outline of the seats with a `stroke` of `10` and `orangered` color.',
- 'stroke orangered, 10',
- [
- [ 'stroke-color', { color: palette.orangered } ],
- [ 'stroke-width', { 'width': 10 } ]
- ]
- ],
- [
- 'Let\'s draw 3 rows and 6 columns of seats with a loop - **type** `for x in [ 1 .. 3 ]`',
- 'for x in [ 1 .. 3 ]',
- [
- [ 'for-loop', { iterator: 'x', range: '1..3' } ]
- ]
- ],
- [
- 'Set the `color` of the seats to `red`',
- ' line 250',
- function () {
- var x = 0,
- out = [];
-
- for (x = 0; x < 3; x += 1) {
- out.push([ 'color', { color: palette.red } ]);
- }
- return out;
- }
- ],
- [
- 'Now draw a `rectangle` for the seats, size `500` by `40`',
- ' rectangle 500, 40',
- function () {
- var x = 0,
- out = [];
-
- for (x = 0; x < 3; x += 1) {
- out.push([ 'color', { color: palette.red } ]);
- out.push([ 'rectangle', { width: 500, height: 40 } ]);
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Move the cursor down to place the back of the seats - **type** `move 0, 50`',
- ' move 0, 50',
- function () {
- var x = 0,
- out = [];
-
- for (x = 0; x < 3; x += 1) {
- out.push([ 'color', { color: palette.red } ]);
- out.push([ 'rectangle', { width: 500, height: 40 } ]);
- out.push([ 'move-to', { dx: 0, dy: 50 } ]);
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Inside the loop, open a second loop for the back of the seats - **type** `for y in [ 1 .. 6 ]`',
- ' for y in [ 1 .. 6 ]',
- function () {
- var out = [],
- x;
-
- for (x = 0; x < 3; x += 1) {
- out.push([ 'color', { color: palette.red } ]);
- out.push([ 'rectangle', { width: 500, height: 40 } ]);
- out.push([ 'move-to', { dx: 0, dy: 50 } ]);
- out.push([ 'for-loop', { iterator: 'y', range: '1..6' } ]);
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Set the `color` of the backseat to `darkred`',
- ' color darkred',
- function () {
- var out = [],
- x;
-
- for (x = 0; x < 3; x += 1) {
- out.push([ 'color', { color: palette.red } ]);
- out.push([ 'rectangle', { width: 500, height: 40 } ]);
- out.push([ 'move-to', { dx: 0, dy: 50 } ]);
- out.push([ 'for-loop', { iterator: 'y', range: '1..6' } ]);
- for (y = 0; y < 6; y += 1) {
- out.push([ 'color', { color: palette.darkred } ]);
- }
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Brilliant! Almost there. Now draw the back of the seat with an arc - **type** `arc 60, 0, 1`',
- ' arc 60, 0, 1',
- function () {
- var out = [],
- x;
-
- for (x = 0; x < 3; x += 1) {
- out.push([ 'color', { color: palette.red } ]);
- out.push([ 'rectangle', { width: 500, height: 40 } ]);
- out.push([ 'move-to', { dx: 0, dy: 50 } ]);
- out.push([ 'for-loop', { iterator: 'y', range: '1..6' } ]);
- for (y = 0; y < 6; y += 1) {
- out.push([ 'color', { color: palette.darkred } ]);
- out.push([ 'arc', { radius: 60, start: 0, end: 1 }]);
- }
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Now separate the seats `130` pixels to the right',
- ' move 130',
- function () {
- var out = [],
- x;
-
- for (x = 0; x < 3; x += 1) {
- out.push([ 'color', { color: palette.red } ]);
- out.push([ 'rectangle', { width: 500, height: 40 } ]);
- out.push([ 'move-to', { dx: 0, dy: 50 } ]);
- out.push([ 'for-loop', { iterator: 'y', range: '1..6' } ]);
- for (y = 0; y < 6; y += 1) {
- out.push([ 'color', { color: palette.darkred } ]);
- out.push([ 'arc', { radius: 60, start: 0, end: 1 }]);
- out.push([ 'move-to', { dx: 130, dy: 0 } ]);
- }
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Last, make sure new rows are positioned correctly - Press **Enter**, then **Backspace** and **type** `move -800` inside the **first** loop',
- ' move -800',
- function () {
- var out = [],
- x;
-
- for (x = 0; x < 3; x += 1) {
- out.push([ 'color', { color: palette.red } ]);
- out.push([ 'rectangle', { width: 500, height: 40 } ]);
- out.push([ 'move-to', { dx: 0, dy: 50 } ]);
- out.push([ 'for-loop', { iterator: 'y', range: '1..6' } ]);
- for (y = 0; y < 6; y += 1) {
- out.push([ 'color', { color: palette.darkred } ]);
- out.push([ 'arc', { radius: 60, start: 0, end: 1 }]);
- out.push([ 'move-to', { dx: 130, dy: 0 } ]);
- }
- out.push([ 'move-to', { dx: -800, dy: 0 } ]);
- }
- return out;
- },
- { override: true }
- ],
- ])
-};
diff --git a/lib/challenges/summer_camp/day_nine.js b/lib/challenges/summer_camp/day_nine.js
deleted file mode 100644
index e5917037f..000000000
--- a/lib/challenges/summer_camp/day_nine.js
+++ /dev/null
@@ -1,161 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_nine',
- title : 'A compass is an essential tool',
- short_title : 'Compass',
- icon_class : 'challenge_compass',
- description : 'Essentially a compass is a magnet, generally a magnetized needle. Since opposites attract the southern pole of the needle is attracted to the Earth\'s natural magnetic north pole. Did you know that you can create your own simply with a paperclip, cork and a magnet?',
- img : '/assets/summercamp/ch_pics/day_9.png',
- completion_text: 'Now try adding the rest of the \'compass rose\' components: the other cardinal directions (E, S, W and even NE, NW, SE and SW). Why not a hand holding it?',
- difficulty : 1,
- startAt : 0,
- summerCamp : true,
- rewards : {'wallpaper': 1},
- steps : generate.fromSequence([
- [
- 'Select `lightgray` for the `background`',
- 'background lightgray',
- [
- [ 'background', { color: palette.lightgray } ]
- ]
- ],
- [
- 'In a new line, set the outline of your compass with a `stroke` of `20` and `gold` color',
- 'stroke gold, 20',
- [
- [ 'stroke-color', { color: palette.gold } ],
- [ 'stroke-width', { 'width': 20 } ]
- ]
- ],
- [
- 'Set the `color` of the compass to `gray`',
- 'color gray',
- [
- [ 'color', { color: palette.gray } ]
- ]
- ],
- [
- 'Your compass is a `circle` with size `110`',
- 'circle 110',
- [
- [ 'ellipse', { rx: 110, isCircle: true } ]
- ]
- ],
- [
- 'Set the `color` to `darkgray` for the second half of the compass',
- 'color darkgray',
- [
- [ 'color', { color: palette.darkgray } ]
- ]
- ],
- [
- 'Set the `stroke` back to `0`',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Draw an arc that matches the top half of the compass - **type** `arc 105, 2, 1`',
- 'arc 105, 2, 1',
- [
- [ 'arc', { radius: 105, start: 2, end: 1 } ]
- ]
- ],
- [
- 'We need to mark the north somehow - in a new line **type** `moveTo 250, 175`',
- 'moveTo 250, 175',
- [
- [ 'move-to', { x: 250, y: 175 } ]
- ]
- ],
- [
- 'Set the `color` to `gold`',
- 'color gold',
- [
- [ 'color', { color: palette.gold } ]
- ]
- ],
- [
- 'Set the `font` to `\'cursive\'` and size `25`',
- 'font \'cursive\', 25',
- [
- [ 'font-family', { font: 'cursive' } ],
- [ 'text-size', { size: '25' } ],
- ]
- ],
- [
- 'Write an N to represent north - **type** `text \'N\'`',
- 'text \'N\'',
- [
- [ 'text', { value: 'N' } ]
- ]
- ],
- [
- 'Now we need a needle with 2 sides. Set the `color` to `red` for the first side',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Move the cursor to position the needle **type** `moveTo 230, 250`',
- 'moveTo 230, 250',
- [
- [ 'move-to', { x: 230, y: 250 } ]
- ]
- ],
- [
- 'Draw a triangle with a polygon - **type** `polygon 20, -90, 40, 0`',
- 'polygon 20, -90, 40, 0',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 20, y: -90 },
- { x: 40, y: -0 }
- ] } ]
- ]
- ],
- [
- 'Excellent! Set the `color` to `white` for the second side of the needle',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Draw the second part - **type** `polygon 20, 90, 40, 0`',
- 'polygon 20, 90, 40, 0',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 20, y: 90 },
- { x: 40, y: -0 }
- ] } ]
- ]
- ],
- [
- 'Finish it with one last detail. Set the `color` to `gold`',
- 'color gold',
- [
- [ 'color', { color: palette.gold } ]
- ]
- ],
- [
- 'Move the cursor to the center of the compass **type** `move 20`',
- 'move 20',
- [
- [ 'move-to', { dx: 20, dy: 0 } ]
- ]
- ],
- [
- 'Draw a `circle` of size `20`',
- 'circle 20',
- [
- [ 'ellipse', { rx: 20, isCircle: true } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_nineteen.js b/lib/challenges/summer_camp/day_nineteen.js
deleted file mode 100644
index c5b251fa5..000000000
--- a/lib/challenges/summer_camp/day_nineteen.js
+++ /dev/null
@@ -1,91 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_nineteen',
- title : 'Foraging',
- short_title : 'Foraging',
- icon_class : 'challenge_foraging',
- description : 'The wild is full of plants and fauna to eat. From stinging nettles, to mushrooms, and a multitude of berries, any meal can be improved with naturally growing foods. Berries are common in many parts of the world, and at Camp Kano they run wild even though they are hard to find. You might need to use your coding skills to find them.',
- img : '/assets/summercamp/ch_pics/day_19.png',
- completion_text: 'But wait! Where are the berries you are meant to be foraging for? Use `color red`, `moveTo`, and `square 25` to add some extra berries to the scene!',
- difficulty : 1,
- startAt : 0,
- summerCamp : true,
- rewards : {'wallpaper': 1},
- steps : generate.fromSequence([
- [
- 'Set `stroke` to `0`',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'It’s a bright sunny day! Set `background` to `blue`.',
- 'background blue',
- [
- [ 'background', { color : palette.blue } ]
- ]
- ],
- [
- 'Our foraging scene needs a grass field to start with. Set our drawing color to `green`.',
- 'color green',
- [
- [ 'color', { color : palette.green } ]
- ]
- ],
- [
- 'We want our grass on the bottom half of the screen, so let’s move into position with `moveTo 0, 300`',
- 'moveTo 0, 300',
- [
- [ 'move-to', { x: 0, y: 300 } ]
- ]
-
- ],
- [
- 'The grass is a nice big `rectangle` of size `500, 200`',
- 'rectangle 500, 200',
- [
- [ 'rectangle', { width: 500, height: 200 } ]
- ]
- ],
- [
- 'Now let’s add in a nice boxy tree to our foraging scene. Begin by moving into position at exactly `50, 100`.',
- 'moveTo 50, 100',
- [
- [ 'move-to', { x: 50, y: 100 } ]
- ]
- ],
- [
- 'Our leaves are drawn with `square 150`.',
- 'square 150',
- [
- [ 'rectangle', { width: 150, isSquare: true } ]
- ]
- ],
- [
- 'Next we need to draw the trunk. Move the cursor with `moveTo 100, 250`.',
- 'moveTo 100, 250',
- [
- [ 'move-to', { x: 100, y: 250 } ]
- ]
- ],
- [
- 'Our wood has a nice `darkbrown` color. Set that as the drawing color.',
- 'color darkbrown',
- [
- [ 'color', { color: palette.darkbrown } ]
- ]
- ],
- [
- 'The trunk is a `rectangle` with a width of `40` and a height of `150`',
- 'rectangle 40, 150',
- [
- [ 'rectangle', { width: 40, height: 150 } ]
- ]
- ]
- ])
-};
-
-
diff --git a/lib/challenges/summer_camp/day_one.js b/lib/challenges/summer_camp/day_one.js
deleted file mode 100644
index acd90b2cd..000000000
--- a/lib/challenges/summer_camp/day_one.js
+++ /dev/null
@@ -1,162 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_one',
- title : 'Your Summer Camp sign',
- short_title : 'Camp Sign',
- description : 'Welcome to your first day! Before you set out from the lodge, your campsite sign needs a design. Be creative! Use a crazy color scheme, or show what you want to do at camp. \nIn the Pacific Northwest of the United States, indigenous peoples carved Totem Poles to tell the stories of their families and cultures. These beautiful sculptures are carved into trees and often feature characters and events from legends. You can search the internet for images of Totem Poles for inspiration.',
- icon_class : 'challenge_campsign',
- img : '/assets/summercamp/ch_pics/day_1.png',
- completion_text: 'Well done! Now give your camp its own name and style. Change your camp name on line 18, or the color of the sign on line 3... why not add a nice background and some extra decorations? All those tool icons on the left can help you get started.',
- difficulty : 1,
- startAt : 0,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'It\'s a sunny day! Set the background color to blue - **type** `background blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Let\'s move the cursor over to the place on the screen where we want to start drawing our sign.\n\nPress ENTER to add the next instruction, and - **type** `moveTo 100, 150`',
- 'moveTo 100, 150',
- [
- [ 'move-to', { x: 100, y: 150 } ]
- ]
- ],
- [
- 'In Make Art we draw shapes using digital pens. Before we draw the next shape, we need to choose the pen we want to use to fill the inside of the shape. Let\'s select a brown pen by setting the color to lightbrown.\n\n In a new line **type** `color lightbrown`',
- 'color lightbrown',
- [
- [ 'color', { color: palette.lightbrown } ]
- ]
- ],
- [
- 'When we draw a shape we can control how thick the line is that the pen leaves behind on the outside of the shape. This line is called the **stroke**.\n\n Press enter and then set the stroke to 0, to avoid drawing lines - **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', function(options){
- return !isNaN(options.width);
- }]
- ]
- ],
- [
- 'Now that we have our pen all set up, we can get started on the sign!\n\nDraw the main billboard with a rectangle - **type** `rectangle 300, 200`',
- 'rectangle 300, 200',
- [
- [ 'rectangle', { width: 300, height: 200 } ]
- ]
- ],
- [
- 'Let\'s draw a shadow. Set the color to brown first - **type** `color brown`',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Draw a thin shadow with a rectangle - **type** `rectangle 300, 5`',
- 'rectangle 300, 5',
- [
- [ 'rectangle', { width: 300, height: 5 } ]
- ]
- ],
- [
- 'That sign looks too clean. We need to draw some imperfections.\n\nMove the cursor to place one on the left side of the sign- **type** `moveTo 100, 220`',
- 'moveTo 100, 220',
- [
- [ 'move-to', { x: 100, y: 220 } ]
- ]
- ],
- [
- 'Our imperfection will be a triangle. First we need to set the `color` to `blue` to match the background.',
- 'color blue',
- [
- [ 'color', { color: palette.blue } ]
- ]
- ],
- [
- 'Set the stroke to be size 5 and brown (to match the sign). You can do both these things in one command - **type** `stroke brown, 5`',
- 'stroke brown, 5',
- [
- [ 'stroke-color', { color: palette.brown } ],
- [ 'stroke-width', { width: 5 } ]
- ]
- ],
-
- [
- 'Now we will use the `polygon` command to draw a triangular imperfection. To create it, we need to list out the positions of the two other corners - **type** `polygon 16, 10, 0, 11`',
- 'polygon 16, 10, 0, 11',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 16, y: 10 },
- { x: 0, y: 11 }
- ] } ]
- ]
- ],
- [
- 'The sign needs a post. Move the cursor to where we will draw it - **type** `moveTo 230, 350`',
- 'moveTo 230, 350',
- [
- [ 'move-to', { x: 230, y: 350 } ]
- ]
- ],
- [
- 'Set the color of the post to brown - **type** `color brown` ',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Set the stroke to 0, again - **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Now that we have the colors sorted, draw the post with a rectangle - **type** `rectangle 40, 150`',
- 'rectangle 40, 150',
- [
- [ 'rectangle', { width: 40, height: 150 } ]
- ]
- ],
- [
- 'We need some text in that sign. Move the cursor to place the text - **type** `moveTo 250, 270`',
- 'moveTo 250, 270',
- [
- [ 'move-to', { x: 250, y: 270 } ]
- ]
- ],
- [
- 'Next we need to choose our font. There are lots to choose from, like `Bariol`, `Georgia`, `Comic Sans MS`, `cursive`, and `Courier`.\n\nPick your favorite and type it in (inside of quote marks) like this - `font \'Bariol\', 70`.',
- 'font \'Bariol\', 70',
- [
- [ 'font-family', function(options){
- return (options.font === "Bariol") ||
- (options.font === "Georgia") ||
- (options.font === "Comic Sans MS") ||
- (options.font === "cursive") ||
- (options.font === "Courier");
- }],
- [ 'text-size', { size: '70' } ],
- ]
- ],
- [
- 'Now write some silly text - **type** `text \'My Camp\'`',
- 'text \'My Camp\'',
- [
- [ 'text', function(options){
- return (typeof(options.value)=="string" &&
- options.value.length > 0);
- }]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_seven.js b/lib/challenges/summer_camp/day_seven.js
deleted file mode 100644
index be1027e49..000000000
--- a/lib/challenges/summer_camp/day_seven.js
+++ /dev/null
@@ -1,166 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_seven',
- title : 'Ghost gathering',
- short_title : 'Ghost',
- description : 'Boo! As night falls, the spirits come out to play. A recent poll reported 87% of Chinese office workers believed in ghosts, and according to another report, 25% of Britons say they have seen a ghost. It’s getting dark, so it is hard to see, but we think we found a ghost! Maybe you can find out if it is friendly or not with your creative coding powers.',
- icon_class : 'challenge_ghost',
- completion_text: 'Spooky! Can you make the ghost scarier? Maybe if it could say \"Boo!\"...',
- difficulty : 2,
- img : '/assets/summercamp/ch_pics/day_7.png',
- startAt : 6,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'Select a spooky color for the background - **type** `background darkpurple`',
- 'background darkpurple',
- [
- [ 'background', { color: palette.darkpurple } ]
- ]
- ],
- [
- 'Set the `stroke` to `0`, to avoid drawing lines',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Set the `color` to `white` for the ghost',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Now draw an ellipse for the ghost\'s body - **type** `ellipse 120, 140`',
- 'ellipse 120, 140',
- [
- [ 'ellipse', { rx: 120, ry: 140 } ]
- ]
- ],
- [
- 'Set the `color` to `black` for the eyes',
- 'color black',
- [
- [ 'color', { color: palette.black } ]
- ]
- ],
- [
- 'Move the cursor to draw the first eye - **type** `move -40, -50`',
- 'move -40, -50',
- [
- [ 'move-to', { dx: -40, dy: -50 } ]
- ]
- ],
- [
- 'Draw the eye with an ellipse - **type** `ellipse 20, 30`',
- 'ellipse 20, 30',
- [
- [ 'ellipse', { rx: 20, ry: 30 } ]
- ]
- ],
- [
- 'Set the `color` to `lightgray`',
- 'color lightgray',
- [
- [ 'color', { color: palette.lightgray } ]
- ]
- ],
- [
- 'Now draw a `circle` with a size of `5`',
- 'circle 5',
- [
- [ 'ellipse', { rx: 5, isCircle: true } ]
- ]
- ],
- [
- 'Looking good! Now move the cursor to the right for the second eye - **type** `move 30`',
- 'move 30',
- [
- [ 'move-to', { dx: 30, dy: 0 } ]
- ]
- ],
- [
- 'Set the `color` back to `black` for the second eye',
- 'color black',
- [
- [ 'color', { color: palette.black } ]
- ]
- ],
- [
- 'Draw the second eye with an ellipse - **type** `ellipse 20, 30`',
- 'ellipse 20, 30',
- [
- [ 'ellipse', { rx: 20, ry: 30 } ]
- ]
- ],
- [
- 'Set the `color` to `lightgray`',
- 'color lightgray',
- [
- [ 'color', { color: palette.lightgray } ]
- ]
- ],
- [
- 'Draw a `circle` with size `5`',
- 'circle 5',
- [
- [ 'ellipse', { rx: 5, isCircle: true } ]
- ]
- ],
- [
- 'Almost there! Set the `color` to `white`',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Now move the cursor to the bottom - **type** `move -80, 150`',
- 'move -80, 150',
- [
- [ 'move-to', { dx: -80, dy: 150 } ]
- ]
- ],
- [
- 'Let\'s open a loop - **type** `for i in [ 1 .. 5 ]`',
- 'for i in [ 1 .. 5 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '1..5' } ]
- ]
- ],
- [
- 'Draw a `circle` with a size of `45`',
- ' circle 45',
- function () {
- var i = 0,
- out = [];
-
- for (i = 0; i < 5; i += 1) {
- out.push([ 'ellipse', { rx: 45, isCircle: true } ]);
- }
-
- return out;
- }
- ],
- [
- 'And move the cursor slightly to the right - **type** `move 45`',
- ' move 45',
- function () {
- var i = 0,
- out = [];
-
- for (i = 0; i < 5; i += 1) {
- out.push([ 'ellipse', { rx: 45, isCircle: true } ]);
- out.push([ 'move-to', { dx: 45, dy: 0 } ]);
- }
- return out;
- },
- { override: true }
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_seventeen.js b/lib/challenges/summer_camp/day_seventeen.js
deleted file mode 100644
index c6c5ee19e..000000000
--- a/lib/challenges/summer_camp/day_seventeen.js
+++ /dev/null
@@ -1,223 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_seventeen',
- title : 'Fishing',
- short_title : 'Fishing',
- icon_class : 'challenge_fishing',
- description : 'It\'s time to go fishing! Inside the camp\'s lake there are trout, catfish, and perch swimming around looking for a snack. Grab your fishing pole and some worms, and head over to the water for today\s adventure. ',
- img : '/assets/summercamp/ch_pics/day_17.png',
- completion_text: 'Great job! Every time you press a key the image redraws, so try holding down the space bar and watch the waves ripple. Also try adding a fish or making the bobber disappear.',
- difficulty : 2,
- startAt : 0,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'It\'s a sunny day! Set the background color to blue - **type** `background blue`',
- 'background lightblue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'We will start by drawing the waves. We want to draw without a fill - set the `color` to be `null`.',
- 'color null',
- [
- [ 'color', null ]
- ]
- ],
- [
- 'Our waves will be dark blue - **type** `stroke darkblue, 8`',
- 'stroke darkblue, 8',
- [
- [ 'stroke-color', { color: palette.darkblue } ],
- [ 'stroke-width', { width: 8 } ]
- ]
- ],
- [
- 'Move to the place on the left where we will start - **type** `moveTo 10, 250`',
- 'moveTo 10, 250',
- [
- [ 'move-to', { x: 10, y: 250 } ]
- ]
- ],
- [
- 'We are going to draw waves on the surface by creating 25 waves inside of a loop - **type** `for i in [1 .. 25]`',
- 'for i in [1 .. 25]',
- [
- [ 'for-loop', { iterator: 'i', range: '1..25' } ]
- ]
- ],
- [
- 'Create a wave out of an arc with a radius of 20 pixels - **type** `arc 20, 0.8, 0.2`',
- ' arc 20, 0.8, 0.2',
- function () {
- var out = [],
- i = 1;
- for (i = 1; i <= 25; i += 1) {
- out.push([ 'arc', { radius: 20, start: 0.8, end: 0.2 } ]);
- }
- return out;
- }
- ],
- [
- 'Move each wave a random amount to the left - **type** `move (random 24, 32)`',
- ' move (random 24, 32)',
- function () {
- var out = [],
- i = 1;
- for (i = 1; i <= 25; i += 1) {
- out.push([ 'arc', { radius: 20, start: 0.8, end: 0.2 } ]);
- out.push([ 'move-to', function (options) {
- return options.dx >= 24 && options.dx <= 32
- }])
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Press ENTER and then backspace to get rid of the indent. This exits the loop.\n\nNext move to the place where we will draw the rest of the water - **type** `moveTo 0, 270`.',
- 'moveTo 0, 270',
- [
- [ 'move-to', { x: 0, y: 270 } ]
- ]
- ],
- [
- 'Set the `color` of the water to be the same as the wave',
- 'color darkblue',
- [
- [ 'color', { color: palette.darkblue } ]
- ]
- ],
- [
- 'Draw a big `rectangle` that is `500` pixels high and `250` pixels wide for the water',
- 'rectangle 500, 250',
- [
- [ 'rectangle', { width: 500, height: 250 } ]
- ]
- ],
- [
- 'Let\'s get ready to add some little blue waves - **type** `stroke blue, 2`',
- 'stroke blue, 2',
- [
- [ 'stroke-color', { color: palette.blue } ],
- [ 'stroke-width', { width: 2 } ]
- ]
- ],
- [
- 'We are going to draw waves 100 times - **type** `for i in [1 .. 100]`',
- 'for i in [1 .. 100]',
- [
- [ 'for-loop', { iterator: 'i', range: '1..100' } ]
- ]
- ],
- [
- 'Move each wave to a random place on the surface of the water - **type** `moveTo (random 0, 500), (random 260, 500)`',
- ' moveTo (random 0, 500), (random 260, 500)',
- function () {
- var out = [],
- i = 1;
- for (i = 1; i <= 100; i += 1) {
- out.push([ 'move-to', function (options) {
- return options.x >= 0
- && options.x <= 500
- && options.y >= 260
- && options.y <= 500
- }]);
- }
- return out;
- }
- ],
- [
- 'Now draw each ripple as a little arc - **type** `arc 10, 0.8, 0.2`',
- ' arc 10, 0.8, 0.2',
- function () {
- var out = [],
- i = 1;
- for (i = 1; i <= 100; i += 1) {
- out.push([ 'move-to', function (options) {
- return options.x >= 0
- && options.x <= 500
- && options.y >= 260
- && options.y <= 500
- }]);
- out.push([ 'arc', { radius: 10, start: 0.8, end: 0.2 } ]);
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Brilliant! When you press the spacebar, a new set of random numbers is selected and the ripples move!\n\nTo catch some fish we need a brown fishing pole - Exit the loop and **type** `stroke brown, 10`.',
- 'stroke brown, 10',
- [
- [ 'stroke-color', { color: palette.brown } ],
- [ 'stroke-width', { width: 10 } ]
- ]
- ],
- [
- 'Now move to the place where we will draw the end of the pole - **type** `moveTo 300, 100`',
- 'moveTo 300, 100',
- [
- [ 'move-to', { x: 300, y: 100 } ]
- ]
- ],
- [
- 'Draw a `line` that is `150` pixels wide and `400` pixels tall',
- 'line 150, 400',
- [
- [ 'line', { dx: 150, dy: 400 } ]
- ]
- ],
- [
- 'Next we need some fishing line - **type** `stroke lightgray, 1`',
- 'stroke lightgray, 1',
- [
- [ 'stroke-color', { color: palette.lightgray } ],
- [ 'stroke-width', { width: 1 } ]
- ]
- ],
- [
- 'Draw a line that goes to the left and down - **type** `line -100, 200`',
- 'line -100, 200',
- [
- [ 'line', { dx: -100, dy: 200 } ]
- ]
- ],
- [
- 'To see if a fish is biting we are going to draw a cork. Move to the end of the fishing line - **type** `move -100, (random 197, 202)`',
- 'move -100, (random 197, 202)',
- [
- [ 'move-to', function(options){
- return options.dx == -100
- && options.dy >= 197
- && options.dy <= 202
- }]
- ]
- ],
- [
- 'The cork doesn\'t have an outline - **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Set the cork\'s `color` to `red`',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Now draw the cork floating in the water - `arc 8, 0, 1`',
- 'arc 8, 0, 1',
- [
- [ 'arc', { radius: 8, start: 0, end: 1 } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_six.js b/lib/challenges/summer_camp/day_six.js
deleted file mode 100644
index 7b22be961..000000000
--- a/lib/challenges/summer_camp/day_six.js
+++ /dev/null
@@ -1,168 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_six',
- title : 'Campfire',
- short_title : 'Campfire',
- description : 'Time to get a fire going. In the wilderness, this very well might be your only source of light, and heat. Burning at over a thousand degrees fahrenheit, this will be sure to keep you warm, and could keep you fed too! Meats, vegetables, and marshmallows can all be cooked over the open flame with the help of sharpened sticks. \nMost fires are built with wood logs, but yours is going to be built with code and a “loop.” As your fire grows upwards its colour changes into a deep shade of orange as the oxygen it is burning starts to cools down. Your code will produce hundreds of colours with just a few lines of code.',
- icon_class : 'challenge_fire',
- completion_text: 'Try adding a starry sky, more flames, a ring of rocks to make it safer or even marshmallows!',
- difficulty : 2,
- img : '/assets/summercamp/ch_pics/day_6.png',
- startAt : 5,
- summerCamp : true,
- rewards : {'wallpaper': 1},
- steps : generate.fromSequence([
- [
- 'It\'s a dark night. Set the `background` color to `darkblue`',
- 'background darkblue',
- [
- [ 'background', { color: palette.darkblue } ]
- ]
- ],
- [
- 'Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Move the cursor to place the floor - **type** `moveTo 0, 300`',
- 'moveTo 0, 300',
- [
- [ 'move-to', { x: 0, y: 300 } ]
- ]
- ],
- [
- 'Set the `color` to `darkgreen`',
- 'color darkgreen',
- [
- [ 'color', { color: palette.darkgreen } ]
- ]
- ],
- [
- 'Draw the grass using a rectangle - **type** `rectangle 500, 200`',
- 'rectangle 500, 200',
- [
- [ 'rectangle', { width: 500, height: 200 } ]
- ]
- ],
- [
- 'Move the cursor to place the light casted by the fire - **type** `moveTo 250, 400`',
- 'moveTo 250, 400',
- [
- [ 'move-to', { x: 250, y: 400 } ]
- ]
- ],
- [
- 'Set the `color` to `green`',
- 'color green',
- [
- [ 'color', { color: palette.green } ]
- ]
- ],
- [
- 'Draw the light using an ellipse - **type** `ellipse 200, 30`',
- 'ellipse 200, 30',
- [
- [ 'ellipse', { rx: 200, ry: 30 } ]
- ]
- ],
- [
- 'We need some wood to feed the fire - **type** `moveTo 110, 390`',
- 'moveTo 110, 390',
- [
- [ 'move-to', { x: 110, y: 390 } ]
- ]
- ],
- [
- 'Set the stroke for the logs - **type** `stroke brown, 25`',
- 'stroke brown, 25',
- [
- [ 'stroke-color', { color: palette.brown } ],
- [ 'stroke-width', { width: 25 } ]
- ]
- ],
- [
- 'Good, now draw the first log - **type** `line 270, 30`',
- 'line 270, 30',
- [
- [ 'line', { dx: 270, dy: 30 } ]
- ]
- ],
- [
- 'Move the cursor to place the second log - **type** `moveTo 420, 390`',
- 'moveTo 420, 390',
- [
- [ 'move-to', { x: 420, y: 390 } ]
- ]
- ],
- [
- 'Draw the second log - **type** `line -290, 30`',
- 'line -290, 30',
- [
- [ 'line', { dx: -290, dy: 30 } ]
- ]
- ],
- [
- 'Excellent! We are now ready to light the fire - **type** `moveTo 180, 400`',
- 'moveTo 180, 400',
- [
- [ 'move-to', { x: 180, y: 400 } ]
- ]
- ],
- [
- 'Our fire will be formed by 150 lines! - **type** `for i in [ 1 .. 150 ]`',
- 'for i in [ 1 .. 150 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '1..150' } ]
- ]
- ],
- [
- 'Set the thickness and color of each line - **type** `stroke \'rgba(255, \' + (i + 50) + \', 0, 0.3)\', 10`',
- ' stroke \'rgba(255, \' + (i + 50) + \', 0, 0.3)\', 10',
- function () {
- var out = [],
- i = 1;
- for (i = 1; i <= 150; i += 1) {
- out.push([ 'stroke-color', { color: 'rgba(255, ' + (i + 50) + ', 0, 0.3)' } ]);
- out.push([ 'stroke-width', { width: 10 } ]);
- }
- return out;
- }
- ],
- [
- 'Now draw the line - **type** `line 150 - i`',
- ' line 150 - i',
- function () {
- var out = [],
- i = 1;
- for (i = 1; i <= 150; i += 1) {
- out.push([ 'stroke-color', { color: 'rgba(255, ' + (i + 50) + ', 0, 0.3)' } ]);
- out.push([ 'stroke-width', { width: 10 } ]);
- out.push([ 'line', { dx: 150 - i, dy: 0 } ]);
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Move the cursor for next line - **type** `move 0.5, -2`',
- ' move 0.5, -2',
- function () {
- var out = [],
- i = 1;
- for (i = 1; i <= 150; i += 1) {
- out.push([ 'stroke-color', { color: 'rgba(255, ' + (i + 50) + ', 0, 0.3)' } ]);
- out.push([ 'stroke-width', { width: 10 } ]);
- out.push([ 'line', { dx: 150 - i, dy: 0 } ]);
- out.push([ 'move-to', { dx: 0.5, dy: -2 } ]);
- }
- return out;
- },
- { override: true }
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_sixteen.js b/lib/challenges/summer_camp/day_sixteen.js
deleted file mode 100644
index dd775f6ca..000000000
--- a/lib/challenges/summer_camp/day_sixteen.js
+++ /dev/null
@@ -1,211 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_sixteen',
- title : 'Table Tennis',
- short_title : 'Table Tennis',
- icon_class : 'challenge_tennis',
- description : 'Table Tennis is a game played with one or two people on each side of a table with the outline of a miniaturized tennis court drawn on it. Games are played to 11 or 21, and upon completion the victor’s paddle is thrown to the ground as they let out their battle cry.',
- img : '/assets/summercamp/ch_pics/day_16.png',
- completion_text: 'Well done! You made a beautiful 3D ping pong table. But there isn’t anyone playing or watching the game! Use your creativity and code to draw some fellow campers enjoying the game.',
- difficulty : 2,
- startAt : 0,
- summerCamp : true,
- rewards : {'wallpaper': 1},
- steps : generate.fromSequence([
- [
- 'Begin by setting your stroke to zero with `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'It\'s a sunny day! Set the background color to blue - **type** `background blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Set your color to green for the grass. **Type** `color green`.',
- 'color green',
- [
- [ 'color', { color: palette.green } ]
- ]
- ],
- [
- 'Move your cursor into place with `moveTo 0, 350`',
- 'moveTo 0, 350',
- [
- [ 'move-to', { x: 0, y: 350 } ]
- ]
- ],
- [
- 'Cover the bottom part of the canvas with grass. Draw a `rectangle` of width `500` and height `150`.',
- 'rectangle 500, 150',
- [
- [ 'rectangle', { width: 500, height: 150 } ]
- ]
- ],
- [
- 'Give your table a solid foundation with some wood legs. Set `color` to `brown`',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Move the cursor into position for the first leg. **Type** `moveTo 20, 310`',
- 'moveTo 20, 310',
- [
- [ 'move-to', { x: 20, y: 310 } ]
- ]
- ],
- [
- 'Now draw the first leg. Type `rectangle 15, 150`.',
- 'rectangle 15, 150',
- [
- [ 'rectangle', { width: 15, height: 150 } ]
- ]
- ],
- [
- 'Now the second leg. **Type** `moveTo 465, 310`.',
- 'moveTo 465, 310',
- [
- [ 'move-to', { x: 465, y: 310 } ]
- ]
- ],
- [
- 'Now **draw** the second leg. Type `rectangle 15, 150`.',
- 'rectangle 15, 150',
- [
- [ 'rectangle', { width: 15, height: 150 } ]
- ]
- ],
- [
- 'And the third leg. **Type** `moveTo 40, 250`.',
- 'moveTo 40, 250',
- [
- [ 'move-to', { x: 40, y: 250 } ]
- ]
- ],
- [
- 'Now draw the third leg the same size as the others',
- 'rectangle 15, 150',
- [
- [ 'rectangle', { width: 15, height: 150 } ]
- ]
- ],
- [
- 'Finally, the fourth leg. **Type** `moveTo 445, 250`.',
- 'moveTo 445, 250',
- [
- [ 'move-to', { x: 445, y: 250 } ]
- ]
- ],
- [
- 'Now draw the fourth leg',
- 'rectangle 15, 150',
- [
- [ 'rectangle', { width: 15, height: 150 } ]
- ]
- ],
- [
- 'Now to put the table on! Set the drawing `color` to `darkgreen`.',
- 'color darkgreen',
- [
- [ 'color', { color: palette.darkgreen } ]
- ]
- ],
- [
- 'Move your table top into place. Type `moveTo 10, 300`.',
- 'moveTo 10, 300',
- [
- [ 'move-to', { x: 10, y: 300 } ]
- ]
- ],
- [
- 'Your table has perspective, to draw it use `polygon 30, -60, 450, -60, 480, 0`',
- 'polygon 30, -60, 450, -60, 480, 0',
- [
- [ 'polygon', { points: [
- { x: 0, y: 0 },
- { x: 30, y: -60 },
- { x: 450, y: -60 },
- { x: 480, y: 0 }
- ] } ]
- ]
- ],
- [
- 'For a cool 3D effect, lighten the drawing color with `color lighten darkgreen, 10`',
- 'color lighten darkgreen, 10',
- [
- [ 'color', { color: 'rgba(111, 138, 81, 1)' } ]
- ]
- ],
- [
- '**Draw** the side of the table with `rectangle 480, 10`',
- 'rectangle 480, 10',
- [
- [ 'rectangle', { width: 480, height: 10 } ]
- ]
- ],
- [
- 'For the net, set the drawing `color` to `white`',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- '`move` the cursor to **exactly** `248, 220`',
- 'moveTo 248, 220',
- [
- [ 'move-to', { x: 248, y: 220 } ]
- ]
- ],
- [
- 'Draw the net: a `rectangle` with a width of `4`, and height of `80`',
- 'rectangle 4, 80',
- [
- [ 'rectangle', { width: 4, height: 80 } ]
- ]
- ],
- [
- 'Give your scene a bouncing ball! Use random to decide what side of the table the ball should be on. **Type** `x = random 40, 460`.',
- 'x = random 40, 460',
- [
- [ 'var', function (opts) {
- return opts.name === 'x' && opts.value === 'random40,460';
- }]
- ]
- ],
- [
- 'Now we’ll use random to decide how high the ball should be! **Type** `y = random 150, 250`.',
- 'y = random 150, 250',
- [
- ['var', function (opts) {
- return (opts.name === 'y' && opts.value === 'random150,250');
- }]
- ]
- ],
- [
- 'Move the cursor to the x and y variables you just made. **Type** `moveTo x, y`.',
- 'moveTo x, y',
- [
- [ 'move-to', function (opts) {
- return (opts.x >= 40 && opts.x <= 460) && (opts.y > 150 && opts.y <= 250)
- }]
- ]
- ],
- [
- 'Finally, draw your ball with a circle with radius 7. Type `circle 7`.',
- 'circle 7',
- [
- [ 'ellipse', { rx: 7, isCircle: true } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_ten.js b/lib/challenges/summer_camp/day_ten.js
deleted file mode 100644
index de7d91189..000000000
--- a/lib/challenges/summer_camp/day_ten.js
+++ /dev/null
@@ -1,185 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_ten',
- title : 'Take a pic!',
- short_title : 'Camera',
- icon_class : 'challenge_camera',
- description : 'Back in the 1820s, early cameras would take several hours to actually capture a film! With annoyingly long exposure hours, photographing people with a nice smile was not really possible. Why? Try and hold a convincing smile while staying perfectly still for hours and you will get your answer. Today, the number of photographs clicked every two minutes is same as the number of photographs clicked by mankind in 1800s.',
- img : '/assets/summercamp/ch_pics/day_10.png',
- completion_text: 'Try drawing yourself taking the picture behind that beautiful camera. Perhaps a light for the flash as well?',
- difficulty : 1,
- startAt : 0,
- summerCamp : true,
- rewards : {'outfit': 1},
- steps : generate.fromSequence([
- [
- 'Set the `background` color to `blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Set the `stroke` to `0` for now, to avoid drawing the outline of the shapes',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Move the cursor to place the camera in the center - **type** `moveTo 100, 150`',
- 'moveTo 100, 150',
- [
- [ 'move-to', { x: 100, y: 150 } ]
- ]
- ],
- [
- 'Set the `color` of the camera to `dimgray`',
- 'color dimgray',
- [
- [ 'color', { color: palette.dimgray } ]
- ]
- ],
- [
- 'Now draw the body of the camera with a rectangle - **type** `rectangle 300, 200`',
- 'rectangle 300, 200',
- [
- [ 'rectangle', { width: 300, height: 200 } ]
- ]
- ],
- [
- 'Move the cursor to draw the lense - **type** `move 150, 100`',
- 'move 150, 100',
- [
- [ 'move-to', { dx: 150, dy: 100 } ]
- ]
- ],
- [
- 'Set the `color` of the lens to `lightblue`',
- 'color lightblue',
- [
- [ 'color', { color: palette.lightblue } ]
- ]
- ],
- [
- 'Set the outline of the lens with a `stroke` of `10` and `black` color',
- 'stroke black, 10',
- [
- [ 'stroke-color', { color: palette.black } ],
- [ 'stroke-width', { 'width': 10 } ]
- ]
- ],
- [
- 'The lens is a `circle` with size `50`',
- 'circle 50',
- [
- [ 'ellipse', { rx: 50, isCircle: true } ]
- ]
- ],
- [
- 'Set the `color` to `lightgray` for the second half of the lens',
- 'color lightgray',
- [
- [ 'color', { color: palette.lightgray } ]
- ]
- ],
- [
- 'Draw an arc that matches the top half of the lens - **type** `arc 50, 2, 1`',
- 'arc 50, 2, 1',
- [
- [ 'arc', { radius: 50, start: 2, end: 1 } ]
- ]
- ],
- [
- 'This camera needs a flash! Move the cursor to place it - **type** `move -30, -125`',
- 'move -30, -125',
- [
- [ 'move-to', { dx: -30, dy: -125 } ]
- ]
- ],
- [
- 'Draw the flash of the camera with a `rectangle` of size `60` by `20`',
- 'rectangle 60, 20',
- [
- [ 'rectangle', { width: 60, height: 20 } ]
- ]
- ],
- [
- 'Set the `stroke` back to `0`.',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Set the `color` of the flash to `lightblue`',
- 'color lightblue',
- [
- [ 'color', { color: palette.lightblue } ]
- ]
- ],
- [
- 'Draw a polygon for the reflection on the flash - **type** `polygon 40, 0, 0, 20`',
- 'polygon 40, 0, 0, 20',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 40, y: 0 },
- { x: 0, y: 20 }
- ] } ]
- ]
- ],
- [
- 'Only the button to shoot the picture is left - **type** `move 100, 10`',
- 'move 100, 10',
- [
- [ 'move-to', { dx: 100, dy: 10 } ]
- ]
- ],
- [
- 'Set the `color` of the button to `red`.',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Draw the button of the camera with a `rectangle` of size `50` by `15`',
- 'rectangle 50, 15',
- [
- [ 'rectangle', { width: 50, height: 15 } ]
- ]
- ],
- [
- 'One more detail! Move the cursor one last time - **type** `move 25, -20`',
- 'move 25, -20',
- [
- [ 'move-to', { dx: 25, dy: -20 } ]
- ]
- ],
- [
- 'Set the `color` to `yellow`',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Set the `font` to `\'ComicSansMS\'` and size `30`',
- 'font \'ComicSansMS\', 30',
- [
- [ 'font-family', { font: 'ComicSansMS' } ],
- [ 'text-size', { size: '30' } ],
- ]
- ],
- [
- 'Write some text - **type** `text \'Click!\'`',
- 'text \'Click!\'',
- [
- [ 'text', { value: 'Click!' } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_thirteen.js b/lib/challenges/summer_camp/day_thirteen.js
deleted file mode 100644
index e04dde235..000000000
--- a/lib/challenges/summer_camp/day_thirteen.js
+++ /dev/null
@@ -1,257 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_thirteen',
- title : 'Play a nice sweet song',
- short_title : 'Guitar',
- icon_class : 'challenge_guitar',
- description : 'Did you know the oldest surviving guitar-like instrument is actually around 3,500 years old? It is a 3-string instrument with a plectrum suspended from the neck, it was owned by an Egyptian singer called Har-Mose and was buried alongside him - You can still see the instrument on display at the Archaeological Museum in Cairo, Egypt.',
- img : '/assets/summercamp/ch_pics/day_13.png',
- completion_text: 'What good is a guitar if we don\'t have a campfire to sing songs around? Try drawing a nice big campfire with some marshmallows for toasting! Maybe a big moon on the horizon too? Be wild, be creative!',
- difficulty : 2,
- startAt : 0,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'It\'s a very nice night! Set the background color to darkblue - **type** `background darkblue`',
- 'background darkblue',
- [
- [ 'background', { color: palette.darkblue } ]
- ]
- ],
- [
- 'Set the `stroke` to `0` in a new line.',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Set your guitar `color` to `lightbrown`.',
- 'color lightbrown',
- [
- [ 'color', { color: palette.lightbrown } ]
- ]
- ],
- [
- 'Move the cursor to place the guitar - **type** `moveTo 120, 250`',
- 'moveTo 120, 250',
- [
- [ 'move-to', { x: 120, y: 250 } ]
- ]
- ],
- [
- 'Draw the first part of the body with an ellipse - **type** `ellipse 60, 65`',
- 'ellipse 60, 65',
- [
- [ 'ellipse', { rx: 60, ry: 65 } ]
- ]
- ],
- [
- 'Now for the second part of the body, `move` the cursor `70` pixels to the right.',
- 'move 70',
- [
- [ 'move-to', { dx: 70, dy: 0 } ]
- ]
- ],
- [
- 'Draw a `circle` of size `55` for the second part of the guitar.',
- 'circle 55',
- [
- [ 'ellipse', { rx: 55, isCircle: true } ]
- ]
- ],
- [
- 'Every guitar needs a neck, move the cursor to place it - **type** `move 20, -10`',
- 'move 20, -10',
- [
- [ 'move-to', { dx: 20, dy: -10 } ]
- ]
- ],
- [
- 'Set the neck `color` to `brown`.',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Draw the neck of the guitar with a rectangle - **type** `rectangle 150, 20`',
- 'rectangle 150, 20',
- [
- [ 'rectangle', { width: 150, height: 20 } ]
- ]
- ],
- [
- 'Now place the headstock - **type** `move 150, -5`',
- 'move 150, -5',
- [
- [ 'move-to', { dx: 150, dy: -5 } ]
- ]
- ],
- [
- 'Draw the headstock with a `rectangle` of size `40` by `30`.',
- 'rectangle 40, 30',
- [
- [ 'rectangle', { width: 40, height: 30 } ]
- ]
- ],
- [
- 'Time to place the soundhole in the body - **type** `move -170, 15`',
- 'move -170, 15',
- [
- [ 'move-to', { dx: -170, dy: 15 } ]
- ]
- ],
- [
- 'Set the outline of the soundhole with a `stroke` of `10` and `brown` color.',
- 'stroke brown, 10',
- [
- [ 'stroke-color', { color: palette.brown } ],
- [ 'stroke-width', { 'width': 10 } ]
- ]
- ],
- [
- 'Set the soundhole `color` to `black`.',
- 'color black',
- [
- [ 'color', { color: palette.black } ]
- ]
- ],
- [
- 'Draw the hole with a `circle` of size `20`.',
- 'circle 20',
- [
- [ 'ellipse', { rx: 20, isCircle: true } ]
- ]
- ],
- [
- 'The bridge is the piece that fixes the strings to the body. Let\'s place it - **type** `move -50, -15`',
- 'move -50, -15',
- [
- [ 'move-to', { dx: -50, dy: -15 } ]
- ]
- ],
- [
- 'Set the outline of the bridge with a `stroke` of `10` and `red` color.',
- 'stroke red, 10',
- [
- [ 'stroke-color', { color: palette.red } ],
- [ 'stroke-width', { 'width': 10 } ]
- ]
- ],
- [
- 'Draw the bridge with a `rectangle` of size `2` by `35`.',
- 'rectangle 2, 35',
- [
- [ 'rectangle', { width: 2, height: 35 } ]
- ]
- ],
- [
- 'That\'s a good looking guitar. Time for the strings - **type** `move 0, 9`',
- 'move 0, 9',
- [
- [ 'move-to', { dx: 0, dy: 9 } ]
- ]
- ],
- [
- 'Set the properties of the strings with a `stroke` of `1` and `white` color.',
- 'stroke white, 1',
- [
- [ 'stroke-color', { color: palette.white } ],
- [ 'stroke-width', { 'width': 1 } ]
- ]
- ],
- [
- 'Let\'s draw the 4 strings with a loop - **type** `for i in [ 1 .. 4 ]`',
- 'for i in [ 1 .. 4 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '1..4' } ]
- ]
- ],
- [
- 'Draw a string using a line - **type** `line 250`',
- ' line 250',
- function () {
- var i = 0,
- out = [];
-
- for (i = 0; i < 4; i += 1) {
- out.push([ 'line', { dx: 250, dy: 0 } ]);
- }
- return out;
- }
- ],
- [
- 'And move the cursor slightly down to place the next ones - **type** `move 0, 4`',
- ' move 0, 4',
- function () {
- var i = 0,
- out = [];
-
- for (i = 0; i < 4; i += 1) {
- out.push([ 'line', { dx: 250, dy: 0 } ]);
- out.push([ 'move-to', { dx: 0, dy: 4 } ]);
- }
- return out;
- },
- { override: true }
- ],
- [
- 'Press **Enter** and **Backspace** to set the `color` to `white` outside the loop',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'You are ready to play music! Start a new loop - **type** `for i in [ 1 .. 10 ]`',
- 'for i in [ 1 .. 10 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '1..10' } ]
- ]
- ],
- [
- 'Select a random location - **type** `moveTo (random 200, 400), (random 100, 400)`',
- ' moveTo (random 200, 400), (random 100, 400)',
- function () {
- var i = 0,
- out = [];
-
- for (i = 0; i < 10; i += 1) {
- out.push([ 'move-to', function (options) {
- return (
- options.x >= 200 && options.x <= 400 &&
- options.y >= 100 && options.y <= 400
- );
- }
- ]);
- }
- return out;
- }
- ],
- [
- 'Come on, play some notes! - **copy and paste** `text \'♪\'`',
- ' text \'♪\'',
- function () {
- var i = 0,
- out = [];
-
- for (i = 0; i < 10; i += 1) {
- out.push([ 'move-to', function (options) {
- return (
- options.x >= 200 && options.x <= 400 &&
- options.y >= 100 && options.y <= 400
- );
- }
- ]);
- out.push([ 'text', { value: '♪' } ]);
- }
- return out;
- },
- { override: true }
- ],
- ])
-};
diff --git a/lib/challenges/summer_camp/day_three.js b/lib/challenges/summer_camp/day_three.js
deleted file mode 100644
index c690874f8..000000000
--- a/lib/challenges/summer_camp/day_three.js
+++ /dev/null
@@ -1,124 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_three',
- title : 'Pitch your Tent',
- short_title : 'Tent',
- description : 'No campsite is complete without a tent. We’ve got you settled with a simple fly tent, which is usually made with a tarpaulin, rope, and stakes. But with your creative powers you can transform it into whatever you want! \nPeople have lived in tents, teepees, yurts, and wigwams for thousands of years. What features can you add to your tent to keep yourself dry and warm? You can always get inspiration from browsing other creations on Kano World, and searching the Internet for other tent designs.',
- icon_class : 'challenge_setcamp',
- completion_text: 'Nice job! You learnt in the last challenge how to draw a tree, why not adding it to the scene? Is that a bird flying in the sky?',
- img : '/assets/summercamp/ch_pics/day_3.png',
- difficulty : 1,
- startAt : 2,
- summerCamp : true,
- rewards : {'wallpaper': 1},
- steps : generate.fromSequence([
- [
- 'Draw a sunny day - **type** `background lightblue`',
- 'background lightblue',
- [
- [ 'background', { color: palette.lightblue } ]
- ]
- ],
- [
- 'Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Move the cursor where the sun will be - **type** `moveTo 400, 50`',
- 'moveTo 400, 50',
- [
- [ 'move-to', { x: 400, y: 50 } ]
- ]
- ],
- [
- 'Set the `color` of the sun `yellow`',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Draw the sun with a circle - **type** `circle 40`',
- 'circle 40',
- [
- [ 'ellipse', { rx: 40, isCircle: true } ]
- ]
- ],
- [
- 'Move the cursor to draw some grass - **type** `moveTo 0, 350`',
- 'moveTo 0, 350',
- [
- [ 'move-to', { x: 0, y: 350 } ]
- ]
- ],
- [
- 'Set the `color` of the grass `green`',
- 'color green',
- [
- [ 'color', { color: palette.green } ]
- ]
- ],
- [
- 'Draw the grass using a rectangle - **type** `rectangle 500, 150`',
- 'rectangle 500, 150',
- [
- [ 'rectangle', { width: 500, height: 150 } ]
- ]
- ],
- [
- 'Excellent! Now we are ready to draw the tent - **type** `color red`',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Position the cursor on top of the grass - **type** `moveTo 100, 350`',
- 'moveTo 100, 350',
- [
- [ 'move-to', { x: 100, y: 350 } ]
- ]
- ],
- [
- 'Draw a triangle using `polygon` - **type** `polygon 150, -200, 300, 0`',
- 'polygon 150, -200, 300, 0',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 150, y: -200 },
- { x: 300, y: 0 }
- ] } ]
- ]
- ],
- [
- 'We need the entrace now. Set the `color` to `darkred`',
- 'color darkred',
- [
- [ 'color', { color: palette.darkred } ]
- ]
- ],
- [
- 'Place the cursor on the tip of the tent - **type** `moveTo 250, 150`',
- 'moveTo 250, 150',
- [
- [ 'move-to', { x: 250, y: 150 } ]
- ]
- ],
- [
- 'Draw the entrance - **type** `polygon 30, 200, -30, 200`',
- 'polygon 30, 200, -30, 200',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 30, y: 200 },
- { x: -30, y: 200 }
- ] } ]
- ]
- ],
- ])
-};
diff --git a/lib/challenges/summer_camp/day_twelve.js b/lib/challenges/summer_camp/day_twelve.js
deleted file mode 100644
index 603db8830..000000000
--- a/lib/challenges/summer_camp/day_twelve.js
+++ /dev/null
@@ -1,161 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_twelve',
- title : 'Lights on!',
- short_title : 'Flashlight',
- icon_class : 'challenge_flashlight',
- description : 'Torches started life as ignited sticks; their flames would light the way for explorers and settlers. With the advent of electricity, and the invention of the dry cell battery in 1887, the road was paved to use incandescent bulbs as a replacement. While the technology inside flashlights has evolved, they are still as useful as ever. Meanwhile, the original torch has found a second coming in circus acts where they are thrown skywards in miraculous fashion.',
- img : '/assets/summercamp/ch_pics/day_12.png',
- completion_text: 'You are an adventurer in the dark with your torch. What might you have uncovered as its light passes over the room? Try drawing that fox that you’ve unearthed or that owl hooting in the trees.',
- difficulty : 1,
- startAt : 0,
- summerCamp : true,
- rewards : {'outfit': 1},
- steps : generate.fromSequence([
- [
- 'It\'s getting dark outside! Set the `background` color to `darkblue`',
- 'background darkblue',
- [
- [ 'background', { color: palette.darkblue } ]
- ]
- ],
- [
- 'Set the `stroke` to `0`, we won\'t need it for this challenge',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Move the cursor to place our flashlight - **type** `moveTo 200, 200`',
- 'moveTo 200, 200',
- [
- [ 'move-to', { x: 200, y: 200 } ]
- ]
- ],
- [
- 'Set the `color` of the flashlight to `dimgray`',
- 'color dimgray',
- [
- [ 'color', { color: palette.dimgray } ]
- ]
- ],
- [
- 'Draw the head of the flashlight with a rectangle - **type** `rectangle 100, 50`',
- 'rectangle 100, 50',
- [
- [ 'rectangle', { width: 100, height: 50 } ]
- ]
- ],
- [
- 'Now move the cursor down to draw the neck - **type** `move 0, 50`',
- 'move 0, 50',
- [
- [ 'move-to', { dx: 0, dy: 50 } ]
- ]
- ],
- [
- 'Set the `color` of this piece to `gray`',
- 'color gray',
- [
- [ 'color', { color: palette.gray } ]
- ]
- ],
- [
- 'Draw a polygon - **type** `polygon 20, 40, 80, 40, 100, 0`',
- 'polygon 20, 40, 80, 40, 100, 0',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 20, y: 40 },
- { x: 80, y: 40 },
- { x: 100, y: 0 }
- ] } ]
- ]
- ],
- [
- 'Exciting! Now move the cursor down to draw the body - **type** `move 20, 40`',
- 'move 20, 40',
- [
- [ 'move-to', { dx: 20, dy: 40 } ]
- ]
- ],
- [
- 'Set the `color` of the body to `darkgray`',
- 'color darkgray',
- [
- [ 'color', { color: palette.darkgray } ]
- ]
- ],
- [
- 'Draw the body of the flashlight with a `rectangle` `60` by `200`',
- 'rectangle 60, 200',
- [
- [ 'rectangle', { width: 60, height: 200 } ]
- ]
- ],
- [
- 'The only thing missing now is a ON/OFF button. Set the `color` to `dimgray`',
- 'color dimgray',
- [
- [ 'color', { color: palette.dimgray } ]
- ]
- ],
- [
- 'Now move the cursor down to place the button - **type** `move 30, 50`',
- 'move 30, 50',
- [
- [ 'move-to', { dx: 30, dy: 50 } ]
- ]
- ],
- [
- 'Draw an ellipse where the button will be located - **type** `ellipse 10, 30`',
- 'ellipse 10, 30',
- [
- [ 'ellipse', { rx: 10, ry: 30 } ]
- ]
- ],
- [
- 'Set the `color` of the button to `red`',
- 'color red',
- [
- [ 'color', { color: palette.red } ]
- ]
- ],
- [
- 'Draw the button with an ellipse - **type** `ellipse 10, 20`',
- 'ellipse 10, 20',
- [
- [ 'ellipse', { rx: 10, ry: 20 } ]
- ]
- ],
- [
- 'You have turned on the light! Set the color of the beam - **type** `color \"rgba(255, 255, 0, 0.8)\"`',
- 'color \"rgba(255, 255, 0, 0.8)\"',
- [
- [ 'color', { color: 'rgba(255, 255, 0, 0.8)' } ]
- ]
- ],
- [
- 'Place the beam in front of the flashlight - **type** `move -50, -140`',
- 'move -50, -140',
- [
- [ 'move-to', { dx: -50, dy: -140 } ]
- ]
- ],
- [
- 'Draw the beam of light with a polygon - **type** `polygon 100, 0, 180, -300, -80, -300`',
- 'polygon 100, 0, 180, -300, -80, -300',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: 100, y: 0 },
- { x: 180, y: -300 },
- { x: -80, y: -300 }
- ] } ]
- ]
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_twenty.js b/lib/challenges/summer_camp/day_twenty.js
deleted file mode 100644
index 5aef47a14..000000000
--- a/lib/challenges/summer_camp/day_twenty.js
+++ /dev/null
@@ -1,19 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_twenty',
- title : 'Fractal Tree',
- short_title : 'Tree',
- icon_class : 'challenge_tree',
- description : 'Time to play! Our counselors made you something to play with. A tree which can grow branches that expand outwards, and contract inwards. It can blossom fully, or shrivel to nothing. All with code. Make it yours.',
- img : '/assets/summercamp/ch_pics/day_20.png',
- completion_text: 'Play around with the blue numbers at the top of the code. What do they do? Why do they do that? Shape the tree to your liking. If things mess up, go back to the main menu and start again.',
- difficulty : 3,
- startAt : 0,
- summerCamp : true,
- rewards : {'outfit': 1},
- code : '# Play with these blue numbers\narmLength = 50\niterations = 9 # Might crash if you go over 15!\ndegreeChange = 20\nhasBlossoms = 3\nblossomSize = 70\nblossomOpacity = .02\n# ^^^ Play with these blue numbers ^^^\n\n###\nThis is a function that draws a tree\nbranch, when it completes, it calls itself\nover and over and over again until all the\ntree’s branches have been drawn!\n###\ndrawBranch = (x, y, branchesLeft, startAngle) ->\n if branchesLeft > 0 \n # Move to where the next branch should\n # be drawn:\n moveTo x, y\n \n # A branch is always blue, but the width\n # depends on how far from the base it is!\n stroke blue, branchesLeft \n \n # A bit of trigonometry here, it’s alright\n # if you don’t understand this! This \n # calculates coordinates for where the \n # branch should be drawn to, and where\n # the next branch should start.\n dx = Math.cos(startAngle) * armLength \n dy = -Math.sin(startAngle) * armLength \n \n # Draw a line to the new coordinates we\n # just calculated\n line dx, dy \n \n # This block of code only executes if\n # the current branch has blossoms. You\n # can change the hasBlossoms variable up\n # top!\n if branchesLeft <= hasBlossoms \n # Set the drawing color to a nice red\n color opacity "rgb(247, 45, 99)", blossomOpacity\n # Our blossoms shouldn’t have a stroke\n stroke 0 \n # Draw the blossom! These big blossoms\n # overlap over each other to create a\n # neat effect.\n circle blossomSize\n \n # Starts the next branch on the right\n drawBranch(x + dx, y + dy, branchesLeft - 1, startAngle - Math.PI / 180 * degreeChange) \n # Starts the next branch on the left\n drawBranch(x + dx, y + dy, branchesLeft - 1, startAngle + Math.PI / 180 * degreeChange)\n \n###\nFinally we call our function and see what\nhappens. Play with the numbers at the top of\nthe function and see how they change the tree\n###\ndrawBranch(stage.width * .5, stage.height, iterations, Math.PI / 2, length)',
- steps : generate.fromSequence([
- ])
-};
diff --git a/lib/challenges/summer_camp/day_twentyone.js b/lib/challenges/summer_camp/day_twentyone.js
deleted file mode 100644
index 3d1965666..000000000
--- a/lib/challenges/summer_camp/day_twentyone.js
+++ /dev/null
@@ -1,143 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_twentyone',
- title : 'Fireworks',
- short_title : 'Fireworks',
- icon_class : 'challenge_fireworks',
- description : 'Camp is almost all wrapped up, and to celebrate we have fireworks! Fireworks have been around for centuries and are believed to have been invented by the Chinese. We are almost ready to put on the show, but need a little bit of help designing the fireworks. Can you give it a shot?',
- img : '/assets/summercamp/ch_pics/day_21.png',
- completion_text: 'Well done! You made a function that can draw fireworks! Can you improve upon it? Draw them all over the screen, add in other creations and share it!',
- difficulty : 3,
- startAt : 0,
- summerCamp : true,
- rewards : null,
- steps : generate.fromSequence([
- [
- 'The sun is down and the moon is new, let’s make the sky dark with `background black`',
- 'background black',
- [
- [ 'background', { color: palette.black } ]
- ]
- ],
- [
- 'Lets set the `stroke` to `red` for our firework',
- 'stroke red',
- [
- ['stroke-color', { color: palette.red } ]
- ]
- ],
- [
- 'We want to draw 60 lines radiating outward, so let’s use a for loop `for i in [ 0 ... 60 ]`',
- 'for i in [ 0 ... 60 ]',
- [
- [ 'for-loop', { iterator: 'i', range: '0...60' } ]
- ]
- ],
- [
- 'All of the lines in our firework should have different lengths! So for each line radiating out, let’s set its length with `length = random 1, 200`.',
- ' length = random 1, 200',
- function () {
- var out = [],
- i;
- for (i = 0; i < 60; i++) {
- out.push( [ 'var', function (opts) {
- return (opts.name === 'length' && opts.value === 'random1,200' );
- }]);
- }
- return out;
- }
- ],
- [
- 'For each loop, we are drawing a new line radiating out. So for every time we loop, the angle of the line should change. This uses some advanced math, but don’t fear. Just **type** `angle = (360 / 60 * i) * (Math.PI / 180)`.',
- ' angle = (360 / 60 * i) * (Math.PI / 180)',
- function () {
- var out = [],
- i;
- for (i = 0; i < 60; i++) {
- out.push( [ 'var', function (opts) {
- return (opts.name === 'length' && opts.value === 'random1,200' );
- }]);
- out.push( [ 'var', function (opts) {
- return (opts.name === 'angle' && opts.value === '(360/60*i)*(Math.PI/180)' );
- }]);
- }
- return out;
- },
- {"override": true}
- ],
- [
- 'Using this new angle and the random length let’s calculate where the x coordinate for the end of the radiating line should be. **Type** `dx = 250 + Math.sin(angle) * length`.',
- ' dx = 250 + Math.sin(angle) * length',
- function () {
- var out = [],
- i;
- for (i = 0; i < 60; i++) {
- out.push( [ 'var', function (opts) {
- return (opts.name === 'length' && opts.value === 'random1,200' );
- }]);
- out.push( [ 'var', function (opts) {
- return (opts.name === 'angle' && opts.value === '(360/60*i)*(Math.PI/180)' );
- }]);
- out.push( [ 'var', function (opts) {
- return (opts.name === 'dx' && opts.value === '250+Math.sin(angle)*length' );
- }]);
- }
- return out;
- },
- {"override": true}
- ],
- [
- 'Now let’s calculate the y coordinate for the end of the radiating line. **Type** `dy = 250 + Math.cos(angle) * length`.',
- ' dy = 250 + Math.cos(angle) * length',
- function () {
- var out = [],
- i;
- for (i = 0; i < 60; i++) {
- out.push( [ 'var', function (opts) {
- return (opts.name === 'length' && opts.value === 'random1,200' );
- }]);
- out.push( [ 'var', function (opts) {
- return (opts.name === 'angle' && opts.value === '(360/60*i)*(Math.PI/180)' );
- }]);
- out.push( [ 'var', function (opts) {
- return (opts.name === 'dx' && opts.value === '250+Math.sin(angle)*length' );
- }]);
- out.push( [ 'var', function (opts) {
- return (opts.name === 'dy' && opts.value === '250+Math.cos(angle)*length' );
- }]);
- }
- return out;
- },
- {"override": true}
- ],
- [
- 'Finally, with all the coordinates set we are ready to draw the line. **Type** `lineTo dx, dy` ',
- ' lineTo dx, dy',
- function () {
- var out = [],
- i;
- for (i = 0; i < 60; i++) {
- out.push( [ 'var', function (opts) {
- return (opts.name === 'length' && opts.value === 'random1,200' );
- }]);
- out.push( [ 'var', function (opts) {
- return (opts.name === 'angle' && opts.value === '(360/60*i)*(Math.PI/180)' );
- }]);
- out.push( [ 'var', function (opts) {
- return (opts.name === 'dx' && opts.value === '250+Math.sin(angle)*length' );
- }]);
- out.push( [ 'var', function (opts) {
- return (opts.name === 'dy' && opts.value === '250+Math.cos(angle)*length' );
- }]);
- out.push( [ 'line', function (opts) {
- return (typeof opts.dx === 'number') && (typeof opts.dy === 'number');
- }]);
- }
- return out;
- },
- {"override": true}
- ]
- ])
-};
diff --git a/lib/challenges/summer_camp/day_two.js b/lib/challenges/summer_camp/day_two.js
deleted file mode 100644
index 500393ba0..000000000
--- a/lib/challenges/summer_camp/day_two.js
+++ /dev/null
@@ -1,162 +0,0 @@
-var palette = require('../../language/modules/palette.json'),
- generate = require('./../util/generate');
-
-module.exports = {
- id : 'day_two',
- title : 'Draw your Campsite',
- short_title : 'Campsite',
- description : 'Time to find a clearing to set up camp. The most important thing to look for when searching for an optimal campsite is flat ground. If possible, keep your campsite close to a water source. This will make cooking and cleanup much easier. Finally, make sure to be surrounded by trees, as they are a great source of kindling and will provide protection from the sun. \nYour friends will want to see what your campsite looks like, so once you have one, draw it! You can use code to set the scene for where you are staying for the next two weeks.',
- icon_class : 'challenge_location',
- completion_text: 'The camp site is looking great! Try adding more trees, flowers, rocks... perhaps a fence as well?',
- img : '/assets/summercamp/ch_pics/day_2.png',
- difficulty : 1,
- startAt : 1,
- summerCamp : true,
- rewards : {'outfit': 1},
- steps : generate.fromSequence([
- [
- 'Today is a beautiful day - **type** `background blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { 'width': 0 } ]
- ]
- ],
- [
- 'Move the cursor where the sun will be - **type** `moveTo 400, 50`',
- 'moveTo 400, 50',
- [
- [ 'move-to', { x: 400, y: 50 } ]
- ]
- ],
- [
- 'Set the `color` of the sun `yellow`',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Draw the sun with a circle - **type** `circle 40`',
- 'circle 40',
- [
- [ 'ellipse', { rx: 40, isCircle: true } ]
- ]
- ],
- [
- 'There is one cloud in the horizon. - **type** `moveTo 440, 80`',
- 'moveTo 440, 80',
- [
- [ 'move-to', { x: 440, y: 80 } ]
- ]
- ],
- [
- 'Set the `color` of the cloud `white`',
- 'color white',
- [
- [ 'color', { color: palette.white } ]
- ]
- ],
- [
- 'Draw the cloud with an ellipse - **type** `ellipse 60, 20`',
- 'ellipse 60, 20',
- [
- [ 'ellipse', { rx: 60, ry: 20 } ]
- ]
- ],
- [
- 'The lake has a beautiful `color` `aquamarine` this morning',
- 'color aquamarine',
- [
- [ 'color', { color: palette.aquamarine } ]
- ]
- ],
- [
- 'Move the cursor where the lake is - **type** `moveTo 0, 190`',
- 'moveTo 0, 190',
- [
- [ 'move-to', { x: 0, y: 190 } ]
- ]
- ],
- [
- 'Draw the lake using a simple rectangle - **type** `rectangle 500, 310`',
- 'rectangle 500, 310',
- [
- [ 'rectangle', { width: 500, height: 310 } ]
- ]
- ],
- [
- 'Set the `color` to `green` for the grass',
- 'color green',
- [
- [ 'color', { color: palette.green } ]
- ]
- ],
- [
- 'Move the cursor before drawing the grass - **type** `moveTo 250, 700`',
- 'moveTo 250, 700',
- [
- [ 'move-to', { x: 250, y: 700 } ]
- ]
- ],
- [
- 'Draw the camp site with a circle - **type** `circle 500`',
- 'circle 500',
- [
- [ 'ellipse', { rx: 500, isCircle: true } ]
- ]
- ],
- [
- 'Looking a bit empty, let\'s draw a tree - **type** `moveTo 120, 300`',
- 'moveTo 120, 300',
- [
- [ 'move-to', { x: 120, y: 300 } ]
- ]
- ],
- [
- 'Set the `color` of the trunk to `brown`',
- 'color brown',
- [
- [ 'color', { color: palette.brown } ]
- ]
- ],
- [
- 'Draw the trunk of the tree using a rectangle - **type** `rectangle 15, 100`',
- 'rectangle 15, 100',
- [
- [ 'rectangle', { width: 15, height: 100 } ]
- ]
- ],
- [
- 'Now place the foliage of the tree - **type** `move 8, -120`',
- 'move 8, -120',
- [
- [ 'move-to', { dx: 8, dy: -120 } ]
- ]
- ],
- [
- 'Set the `color` to `darkgreen`',
- 'color darkgreen',
- [
- [ 'color', { color: palette.darkgreen } ]
- ]
- ],
- [
- 'Draw the foliage with a triangle using `polygon` - **type** `polygon -40, 160, 40, 160`',
- 'polygon -40, 160, 40, 160',
- [
- [ 'polygon', { points: [
- { x: 0, y: -0 },
- { x: -40, y: 160 },
- { x: 40, y: 160 }
- ] } ]
- ]
- ],
- ])
-};
diff --git a/lib/challenges/summer_camp/index.js b/lib/challenges/summer_camp/index.js
deleted file mode 100644
index a3e034b47..000000000
--- a/lib/challenges/summer_camp/index.js
+++ /dev/null
@@ -1,23 +0,0 @@
-module.exports = [
- require('./day_one'),
- require('./day_two'),
- require('./day_three'),
- require('./day_four'),
- require('./day_five'),
- require('./day_six'),
- require('./day_seven'),
- require('./day_eight'),
- require('./day_nine'),
- require('./day_ten'),
- require('./day_eleven'),
- require('./day_twelve'),
- require('./day_thirteen'),
- require('./day_fourteen'),
- require('./day_fifteen'),
- require('./day_sixteen'),
- require('./day_seventeen'),
- require('./day_eighteen'),
- require('./day_nineteen'),
- require('./day_twenty'),
- require('./day_twentyone')
- ];
\ No newline at end of file
diff --git a/lib/challenges/sweden.js b/lib/challenges/sweden.js
deleted file mode 100644
index 792d0f067..000000000
--- a/lib/challenges/sweden.js
+++ /dev/null
@@ -1,61 +0,0 @@
-var palette = require('../language/modules/palette.json'),
- generate = require('./util/generate');
-
-module.exports = {
- id : 'flag-sweden',
- title : 'Swedish flag',
- description : 'A flag challenge with two crossing rectangles!',
- code : '',
- startAt : 2,
- steps : generate.fromSequence([
- [
- 'Set the background to blue - **type** `background blue`',
- 'background blue',
- [
- [ 'background', { color: palette.blue } ]
- ]
- ],
- [
- 'Set the drawing color to yellow - **type** `color yellow`',
- 'color yellow',
- [
- [ 'color', { color: palette.yellow } ]
- ]
- ],
- [
- 'Set the stroke to 0, to avoid drawing outlines - in a new line, **type** `stroke 0`',
- 'stroke 0',
- [
- [ 'stroke-width', { width: 0 } ]
- ]
- ],
- [
- 'Move to the top and left - in a new line, **type** `moveTo 150`',
- 'moveTo 150',
- [
- [ 'move-to', { x: 150, y: 0 } ]
- ]
- ],
- [
- 'Draw your first rectangle - **type** `rectangle 50, 500`',
- 'rectangle 50, 500',
- [
- [ 'rectangle', { width: 50, height: 500 } ]
- ]
- ],
- [
- 'Move to the left and down - in a new line, **type** `moveTo 0, 150`',
- 'moveTo 0 , 150',
- [
- [ 'move-to', { x: 0, y: 150 } ]
- ]
- ],
- [
- 'Draw another rectangle with width 500 and height 50 - in a new line, **type** `rectangle 500, 50`',
- 'rectangle 500, 50',
- [
- [ 'rectangle', { width: 500, height: 50 } ]
- ]
- ]
- ])
-};
\ No newline at end of file
diff --git a/lib/challenges/util/validator.js b/lib/challenges/util/validator.js
new file mode 100644
index 000000000..c27af44f3
--- /dev/null
+++ b/lib/challenges/util/validator.js
@@ -0,0 +1,186 @@
+"use strict";
+
+var stringUtil = require('../../util/string');
+
+module.exports = function (steps, synonyms) {
+
+ var validateString = function (regex, str) {
+ var reg = new RegExp(regex);
+ return !!reg.exec(str); //just the boolean result
+ },
+ /**
+ * Replaces synonyms in a rule
+ * @param {string} rule The rule
+ * @return {string} The modified rule
+ */
+ addSynonims = function (rule) {
+ //add the synonims
+ if (typeof synonyms === "object") {
+ Object.keys(synonyms).forEach(function (baseWord) {
+ var syns = synonyms[baseWord],
+ newWord = '(' + baseWord; //synonyms for 1 word
+
+ if (rule.indexOf(baseWord) > -1) {
+ syns.forEach(function (key) {
+ newWord = newWord + '|' + key;
+ });
+ newWord = newWord + ')';
+ rule = rule.replace(baseWord, newWord);
+ }
+ });
+ }
+ return rule;
+ },
+ /**
+ * Completes the regex:
+ * - adding EOL and beginning of line
+ * - making whitespaces optional
+ * @param {[type]} rule [description]
+ * @return {[type]} [description]
+ */
+ completeRegex = function (rule) {
+
+ //remove all the spaces around ,*()[]=
+ rule = rule.replace(/ *([\,\(\)\[\]\=]|\.\.) */g, "$1");
+
+ //make all the spaces optional
+ rule = rule.replace(/([,\[\]=\/\<\>]|\.\.)/g, " ?$1 ?");
+
+ //escape brackets, dash
+ rule = rule.replace(/[\[\]\.\(\)]/g, "\\$&");
+
+ //escape math operators
+ rule = rule.replace(/[\+\-\*\/\%]/g, "\\$&");
+
+ //add the synonims
+ rule = addSynonims(rule);
+
+ rule = "^" + rule + " *$";
+
+ //NOTE: the regex comes out correct, but not particularly clean from here
+ return rule;
+ },
+
+ validateSingleRule = function (rule, line) {
+ var fn;
+ if (typeof rule === "string") {
+ if (rule.indexOf("@@") === 0) {
+ //if a rule starts with @@ what follows needs to be escaped
+ rule = rule.substr(2, rule.length - 1);
+ rule = stringUtil.escapeRegex(rule);
+ } else if (rule.indexOf("__") === 0) {
+ //If a rule is prefixed with __ it will be left untouched
+ rule = rule.substr(2, rule.length - 1);
+ } else {
+ rule = completeRegex(rule);
+ }
+ return validateString(rule, line);
+ } else if (typeof rule === "object") {
+ if (rule.type === "function") {
+ /*jshint evil: true*/
+ eval("fn = " + rule.fn);
+ /*jshint evil: false*/
+ return fn(line);
+ }
+ }
+ return false;
+ },
+ validator = {
+ /**
+ * Validates a step (possibly many rules and many lines)
+ * @param {Array/String} rules An array or a string containing the rules to be validated
+ * @param {object} lines An array of lines that should be matched against the steps
+ * @return {object} [description]
+ */
+ validateStep: function (rules, lines) {
+ var res = true,
+ rep = {valid: false};
+ if (rules instanceof Array) {
+ rules.forEach(function (rule, idx) {
+ var line = lines[idx];
+ res = res && validateSingleRule(rule, line);
+ });
+ } else {
+ res = validateSingleRule(rules, lines[0]);
+ }
+ rep.valid = res;
+ return rep;
+ },
+ /**
+ * Checks all the steps in the code.
+ * It returns a report in the following form:
+ * {
+ * lastValidStep: 12,
+ * report: {
+ * steps: [
+ * {valid: true, lines: [12]},
+ * {valid: true, lines: [13]},
+ * {valid: true, lines: [14]},
+ * {valid: true, lines: [16,17]},
+ * {valid: false, lines: [18,19]},
+ * {valid: false, lines: [20]}
+ * ]
+ * }
+ * }
+ * @param {string} code all the code written so far
+ * @param {all the steps} steps [description]
+ * @param {[type]} currentStep [description]
+ * @return {object} A report of the result
+ */
+ validate: function (code) {
+ var lines = code.split('\n'),
+ report = {lastValidStep: null, steps: [], complete: false},
+ currentLine = 0,
+ validSteps = 0,
+ validCode = true,
+ firstBrokenLine = null;
+
+
+ steps.forEach(function (step, stepIdx) {
+ var rules = step.validate || step.solution,
+ i,
+ currLines = [],
+ stepReport,
+ firstLineOfStep = currentLine;
+ //every step could be made of more than 1 line
+ if (rules instanceof Array) {
+ for (i = 0; i < rules.length; i++) {
+ currLines.push(lines[currentLine]);
+ currentLine++;
+ }
+ } else {
+ currLines.push(lines[currentLine]);
+ currentLine++;
+ }
+ //validate the rules and the lines we have
+ stepReport = validator.validateStep(rules, currLines);
+ stepReport.lines = [];
+
+ validCode = validCode && stepReport.valid;
+
+ if (firstBrokenLine === null && !validCode) {
+ firstBrokenLine = stepIdx;
+ }
+
+ if (stepReport.valid) {
+ report.lastValidStep = stepIdx;
+ validSteps++;
+
+ }
+ for (i = 0; i < currLines.length; i++) {
+ //TODO: this could possibly done cleaner
+ stepReport.lines.push(i + firstLineOfStep);
+ }
+ report.steps.push(stepReport);
+ });
+ report.complete = validSteps === steps.length;
+ report.firstBrokenLine = firstBrokenLine;
+ return report;
+ },
+ private: {
+ completeRegex: completeRegex
+ }
+ };
+ return validator;
+};
+
diff --git a/lib/challenges/worlds/basic/baloon.json b/lib/challenges/worlds/basic/baloon.json
new file mode 100644
index 000000000..2f396bafe
--- /dev/null
+++ b/lib/challenges/worlds/basic/baloon.json
@@ -0,0 +1,42 @@
+{
+ "id": "baloon",
+ "title": "Blue Balloon",
+ "cover": "blue-baloon.png",
+ "description": "Draw a balloon floating in the air using polygons!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Set the color to blue - **type** `color blue`",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Draw a circle with a size of 100",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Move down by 100 - **type** `move 0, 100`",
+ "solution": "move 0, 100"
+ },
+ {
+ "hint": "Draw the knot - **type** `polygon 15, 15, -15, 15`",
+ "solution": "polygon 15, 15, -15, 15"
+ },
+ {
+ "hint": "Move down a little more - **type** `move 0, 15`",
+ "solution": "move 0, 15"
+ },
+ {
+ "hint": "Choose a thick black stroke - **type** `stroke black, 5`",
+ "solution": "stroke black, 5"
+ },
+ {
+ "hint": "Draw the line of the balloon thread - **type** `line 0, 200`",
+ "solution": "line 0, 200"
+ }
+ ],
+ "completion_text": "Awesome balloon! Why not change the color before moving on?"
+}
diff --git a/lib/challenges/worlds/basic/breakfast.json b/lib/challenges/worlds/basic/breakfast.json
new file mode 100644
index 000000000..b42d3d54d
--- /dev/null
+++ b/lib/challenges/worlds/basic/breakfast.json
@@ -0,0 +1,197 @@
+{
+ "id": "breakfast",
+ "title": "Breakfast",
+ "description": "Draw a bacon and eggs breakfast!",
+ "startAt": 2,
+ "fatherDay": true,
+ "steps": [
+ {
+ "hint": "Set the background to blue - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a circle with a size of 240",
+ "solution": "\n#Plate\ncircle 240",
+ "validate": "circle 240"
+ },
+ {
+ "hint": "Set the color to `'#eee'` - **type** `color '#eee'`",
+ "solution": "color '#eee'"
+ },
+ {
+ "hint": "Draw a circle with a size of 200",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Move up and left - **type** `move -70, -70`",
+ "solution": "\n#Egg\nmove -70, -70",
+ "validate": "move -70,-70"
+ },
+ {
+ "hint": "Set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a circle with a size of 80",
+ "solution": "circle 80"
+ },
+ {
+ "hint": "Set the color to yellow",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Draw a circle with a size of 30",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Move up and right - **type** `move 90, -60`",
+ "solution": "\n#Bacon 1\nmove 90, -60",
+ "validate": "move 90,-60"
+ },
+ {
+ "hint": "Set the color to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 60 and 200 - **type** `rectangle 60, 200`",
+ "solution": "rectangle 60, 200"
+ },
+ {
+ "hint": "Move 30 to the right - **type** `move 30`",
+ "solution": "move 30"
+ },
+ {
+ "hint": "Set the color to `'#aa0000'` - **type** `color '#aa0000'`",
+ "solution": "color '#aa0000'"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 30 and 200 - **type** `rectangle 30, 200`",
+ "solution": "rectangle 30, 200"
+ },
+ {
+ "hint": "Move down and left - **type** `move -3, 10`",
+ "solution": "move -3, 10"
+ },
+ {
+ "hint": "Set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 6 and 180 - **type** `rectangle 6, 180`",
+ "solution": "rectangle 6, 180"
+ },
+ {
+ "hint": "Move down and right - **type** `move 40, 50`",
+ "solution": "\n#Bacon 2\nmove 40, 50",
+ "validate": "move 40, 50"
+ },
+ {
+ "hint": "Set the color to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 60 and 200 - **type** `rectangle 60, 200`",
+ "solution": "rectangle 60, 200"
+ },
+ {
+ "hint": "Move 30 to the right - **type** `move 30`",
+ "solution": "move 30"
+ },
+ {
+ "hint": "Set the color to `'#aa0000'` - **type** `color '#aa0000'`",
+ "solution": "color '#aa0000'"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 30 and 200 - **type** `rectangle 30, 200`",
+ "solution": "rectangle 30, 200"
+ },
+ {
+ "hint": "Move down and left - **type** `move -3, 10`",
+ "solution": "move -3, 10"
+ },
+ {
+ "hint": "Set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a rectangle with a size of 6 and 180 - **type** `rectangle 6, 180`",
+ "solution": "rectangle 6, 180"
+ },
+ {
+ "hint": "Move down and left - **type** `move -200, 150`",
+ "solution": "\n#Tomato 1\nmove -200, 150",
+ "validate": "move -200, 150"
+ },
+ {
+ "hint": "Set the color to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a circle with a size of 40",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Set the color to `'#cc4444'` - **type** `color '#cc4444'`",
+ "solution": "color '#cc4444'"
+ },
+ {
+ "hint": "Draw a circle with a size of 35",
+ "solution": "circle 35"
+ },
+ {
+ "hint": "Set the color to `'#aa4444'` - **type** `color '#aa4444'`",
+ "solution": "color '#aa4444'"
+ },
+ {
+ "hint": "Draw an ellipse with a size of 30 and 10",
+ "solution": "ellipse 30, 10"
+ },
+ {
+ "hint": "Draw an ellipse with a size of 10 and 30",
+ "solution": "ellipse 10, 30"
+ },
+ {
+ "hint": "Move down and right - **type** `move 70, 50`",
+ "solution": "\n#Tomato 2\nmove 70, 50",
+ "validate": "move 70, 50"
+ },
+ {
+ "hint": "Set the color to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a circle with a size of 40",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Set the color to `'#cc4444'` - **type** `color '#cc4444'`",
+ "solution": "color '#cc4444'"
+ },
+ {
+ "hint": "Draw a circle with a size of 35",
+ "solution": "circle 35"
+ },
+ {
+ "hint": "Set the color to `'#aa4444'` - **type** `color '#aa4444'`",
+ "solution": "color '#aa4444'"
+ },
+ {
+ "hint": "Draw an ellipse with a size of 30 and 10",
+ "solution": "ellipse 30, 10"
+ },
+ {
+ "hint": "Draw an ellipse with a size of 10 and 30",
+ "solution": "ellipse 10, 30"
+ }
+ ],
+ "completion_text": "Well done!",
+ "cover": "breakfast.png"
+}
\ No newline at end of file
diff --git a/lib/challenges/worlds/basic/index.json b/lib/challenges/worlds/basic/index.json
new file mode 100644
index 000000000..2369ad6ca
--- /dev/null
+++ b/lib/challenges/worlds/basic/index.json
@@ -0,0 +1,11 @@
+{
+ "challenges": [
+ "./sunnyday",
+ "./swissflag",
+ "./stare",
+ "./smiley",
+ "./baloon",
+ "./stickman"
+
+ ]
+}
diff --git a/lib/challenges/worlds/basic/smiley.json b/lib/challenges/worlds/basic/smiley.json
new file mode 100644
index 000000000..54b2960d8
--- /dev/null
+++ b/lib/challenges/worlds/basic/smiley.json
@@ -0,0 +1,50 @@
+{
+ "id": "smiley",
+ "title": "Your first face",
+ "description": "Draw a face",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Set the drawing color to yellow - **type** `color yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Choose a thick black stroke - **type** `stroke black, 20`",
+ "solution": "stroke black, 20"
+ },
+ {
+ "hint": "Draw a circle with a size of 200",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Move left and up by 80 - **type** `move -80, -80`",
+ "solution": "move -80, -80"
+ },
+ {
+ "hint": "Set the drawing color to black",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw a circle with a radius of 20",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Move right by 160 - **type** `move 160`",
+ "solution": "move 160"
+ },
+ {
+ "hint": "Draw another circle with a size of 20",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Move to the center and down - In a new line, **type** `moveTo 250, 270`",
+ "solution": "moveTo 250, 270"
+ },
+ {
+ "hint": "An arc is part of a circle, we can draw one as the mouth - **type** `arc 100, 1, 2`",
+ "solution": "arc 100, 1, 2"
+ }
+ ],
+ "completion_text": "Nice! Keep up the good work you face drawing genius!",
+ "cover": "smiley.png"
+}
diff --git a/lib/challenges/worlds/basic/stare.json b/lib/challenges/worlds/basic/stare.json
new file mode 100644
index 000000000..b5744bbf0
--- /dev/null
+++ b/lib/challenges/worlds/basic/stare.json
@@ -0,0 +1,55 @@
+{
+ "id": "stare",
+ "title": "Stare in the dark",
+ "description": "Draw a pair of staring eyes in the dark",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Set the background to black",
+ "solution": "background black"
+ },
+ {
+ "hint": "Move to the left by 80 - **type** `move -80`",
+ "solution": "move -80",
+ "validate": "move -80"
+ },
+ {
+ "hint": "Set the drawing color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw an ellipse, it's like a stretched circle - **type** `ellipse 60, 40`",
+ "solution": "ellipse 60, 40"
+ },
+ {
+ "hint": "Set the drawing color to black - **type** `color black`",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw a circle with a size of 10 - **type** `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Move to the right by 160 - **type** `move 160`",
+ "solution": "move 160"
+ },
+ {
+ "hint": "Set the drawing color to white again",
+ "solution": "color white"
+ },
+ {
+ "hint": "Now draw another ellipse with width 60 and height 40 - **type** `ellipse 60, 40`",
+ "solution": "ellipse 60, 40"
+ },
+ {
+ "hint": "Set the drawing color to black",
+ "solution": "color black"
+ },
+ {
+ "hint": "Finish it up by drawing a circle with a size of 10",
+ "solution": "circle 10"
+ }
+ ],
+ "completion_text": "You Wizard! The next time I need spooky eyes I know who to call!",
+ "cover": "stare.png"
+}
diff --git a/lib/challenges/worlds/basic/stickman.json b/lib/challenges/worlds/basic/stickman.json
new file mode 100644
index 000000000..8441783bb
--- /dev/null
+++ b/lib/challenges/worlds/basic/stickman.json
@@ -0,0 +1,54 @@
+{
+ "id": "stickman",
+ "title": "Stickman",
+ "description": "Draw a stickman using circles and lines",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Set the `stroke` to `black` with a size of `10`",
+ "solution": "stroke black, 10"
+ },
+ {
+ "hint": "Move up on the canvas, **type** `move 0, -50`",
+ "solution": "move 0, -50"
+ },
+ {
+ "hint": "Draw the stickman body - `line 0, 150`",
+ "solution": "line 0, 150"
+ },
+ {
+ "hint": "Draw the stickman left arm - `line -80, 120`",
+ "solution": "line -80, 120"
+ },
+ {
+ "hint": "Good, now with the right arm - `line 80, 120`",
+ "solution": "line 80, 120"
+ },
+ {
+ "hint": "Move down, **type** `move 0, 150`",
+ "solution": "move 0, 150"
+ },
+ {
+ "hint": "Draw the stickman left leg: `line -80, 120`",
+ "solution": "line -80, 120"
+ },
+ {
+ "hint": "Good, now with the right leg - `line 80, 120`",
+ "solution": "line 80, 120"
+ },
+ {
+ "hint": "Move to the top of the drawing, **type** `moveTo 250, 100`",
+ "solution": "moveTo 250, 100"
+ },
+ {
+ "hint": "Set the stroke back to 0",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Now to draw a many sided shape - **type** `polygon -60, 50, 0, 100, 60, 50`",
+ "solution": "polygon -60, 50, 0, 100, 60, 50"
+ }
+ ],
+ "completion_text": "Nice one! The canvas is 500 wide and 500 high, moving about on it is a skill!",
+ "cover": "stickman.png"
+}
diff --git a/lib/challenges/worlds/basic/sunnyday.json b/lib/challenges/worlds/basic/sunnyday.json
new file mode 100644
index 000000000..2f751fffe
--- /dev/null
+++ b/lib/challenges/worlds/basic/sunnyday.json
@@ -0,0 +1,23 @@
+{
+ "id": "sunnyday",
+ "title": "Sunny Day",
+ "description": "Learn the basics by coding a clear sunny day.",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "First we will fill the canvas with a nice sky blue - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Now we need to change the drawing color to a bright yellow - in a new line **type** `color yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Finally we draw the sun, we use the circle command followed by the size we want the circle to be - in a new line **type** `circle 150`",
+ "solution": "circle 150"
+ }
+ ],
+ "completion_text": "Well done! It's a bright and sunny day, now change the number next to circle and see what happens",
+ "cover": "sunny-day.png"
+}
diff --git a/lib/challenges/worlds/basic/swissflag.json b/lib/challenges/worlds/basic/swissflag.json
new file mode 100644
index 000000000..05dc69c33
--- /dev/null
+++ b/lib/challenges/worlds/basic/swissflag.json
@@ -0,0 +1,39 @@
+{
+ "id": "swissflag",
+ "title": "Swiss Flag",
+ "description": "Make the Swiss Flag with code",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Now for something a little more challenging. We can make the background a solid color by **typing** `background red`.",
+ "solution": "background red"
+ },
+ {
+ "hint": "We’re going to draw lines, so we need to set a nice big stroke for them. **Type** `stroke 66`",
+ "solution": "stroke 66"
+ },
+ {
+ "hint": "Now let’s set the stroke color to white - on the third line **type** `stroke white`",
+ "solution": "stroke white"
+ },
+ {
+ "hint": "Let’s draw our first line. We have already set that it will be white and 66 pixels wide. We will draw a line of length 100 by **typing** `line 100`.",
+ "solution": "line 100"
+ },
+ {
+ "hint": "What a short, stubby line. It needs a friend. We’ll make its friend 100 pixels long too, but this time in the opposite direction. **Type** `line -100`.",
+ "solution": "line -100"
+ },
+ {
+ "hint": "Much better, now we need lines going up and down. But how are we going to do that? The line function can actually take two numbers. The first number controls where the horizontal position is, the second controls where the vertical position is. **Type** `line 0, 100`",
+ "solution": "line 0, 100"
+ },
+ {
+ "hint": "The line goes down! To go the other way we need to type **type** `line 0, -100`.",
+ "solution": "line 0, -100"
+ }
+ ],
+ "completion_text": "What a great flag! The Swiss are known for their great minimalist designs.",
+ "cover": "swissflag.png"
+}
diff --git a/lib/challenges/worlds/medium/dots.json b/lib/challenges/worlds/medium/dots.json
new file mode 100644
index 000000000..6c6166abb
--- /dev/null
+++ b/lib/challenges/worlds/medium/dots.json
@@ -0,0 +1,39 @@
+{
+ "id": "dots",
+ "title": "Dots Pattern",
+ "description": "Create a fancy dots pattern using loops and circles!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "Set the background to red",
+ "solution": "background red"
+ },
+ {
+ "hint": "Set the stroke to 0",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the color to yellow",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Now open a loop - **type** `for x in [ 0 .. 25 ]`",
+ "solution": "for x in [ 0..25 ]"
+ },
+ {
+ "hint": "Inside this loop, open a second loop - **type** `for y in [ 0 .. 25 ]`",
+ "solution": " for y in [ 0..25 ]"
+ },
+ {
+ "hint": "Move around, **type** `moveTo x * 20, y * 20`",
+ "solution": " moveTo x * 20, y * 20"
+ },
+ {
+ "hint": "Now for the dots - **type** `circle 6`",
+ "solution": " circle 6"
+ }
+ ],
+ "completion_text": "Mathematical! Change the numbers and see what happens.",
+ "cover": "dots.png"
+}
diff --git a/lib/challenges/worlds/medium/gradient.json b/lib/challenges/worlds/medium/gradient.json
new file mode 100644
index 000000000..744be51f1
--- /dev/null
+++ b/lib/challenges/worlds/medium/gradient.json
@@ -0,0 +1,31 @@
+{
+ "id": "gradient",
+ "title": "Rainbow gradient",
+ "description": "Combine for loops and colors to make something magical!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "Start by opening a for loop - **type** `for x in [ 0 .. 25 ]`",
+ "solution": "for x in [ 0..25 ]"
+ },
+ {
+ "hint": "Now lets open a second for loop inside - **type** `for y in [ 0 .. 25 ]`",
+ "solution": " for y in [ 0..25 ]"
+ },
+ {
+ "hint": "Let's rotate through the color spectrum - **type** `color rotate red, 10 * x + 10 * y`",
+ "solution": " color rotate red, 10 * x + 10 * y"
+ },
+ {
+ "hint": "Now we need to move every time we draw - **type** `moveTo 20 * x, 20 * y`",
+ "solution": " moveTo 20 * x, 20 * y"
+ },
+ {
+ "hint": "Now for the shapes draw a square of size 20",
+ "solution": " square 20"
+ }
+ ],
+ "completion_text": "Try changing the numbers in the rotate function and see what happens.",
+ "cover": "gradient.png"
+}
diff --git a/lib/challenges/worlds/medium/house.json b/lib/challenges/worlds/medium/house.json
new file mode 100644
index 000000000..e3fc73ccb
--- /dev/null
+++ b/lib/challenges/worlds/medium/house.json
@@ -0,0 +1,87 @@
+{
+ "id": "house",
+ "title": "House",
+ "description": "Draw a cosy house!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "Let's start with the sky! Set the `background` to `blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Now set the stroke to 0",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move to the left and up, **type** `move -150, -50`",
+ "solution": "move -150, -50"
+ },
+ {
+ "hint": "Now set the color to beige",
+ "solution": "color beige"
+ },
+ {
+ "hint": "Now draw rectangle, **type** `rectangle 300, 200`",
+ "solution": "rectangle 300, 200"
+ },
+ {
+ "hint": "Move to the right and up, **type** `move 150, -100`",
+ "solution": "move 150, -100"
+ },
+ {
+ "hint": "Set the color to darkred for the roof",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "Now let's draw the roof, **type** `polygon 170, 100, -170, 100`",
+ "solution": "polygon 170, 100, -170, 100"
+ },
+ {
+ "hint": "Move to the left and down, **type** `move -250, 300`",
+ "solution": "move -250, 300"
+ },
+ {
+ "hint": "Now set the color to green for the grass",
+ "solution": "color green"
+ },
+ {
+ "hint": "Draw rectangle for the grass, width a size of 500, 100",
+ "solution": "rectangle 500, 100"
+ },
+ {
+ "hint": "Move to the right and up, **type** `move 220, -80`",
+ "solution": "move 220, -80"
+ },
+ {
+ "hint": "Now set the color to brown for the wooden door",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw rectangle for the door, with a size of 60, 80",
+ "solution": "rectangle 60, 80"
+ },
+ {
+ "hint": "Set the color to aqua for the windows",
+ "solution": "color aqua"
+ },
+ {
+ "hint": "Move to the left and up, **type** `move -80, -80`",
+ "solution": "move -80, -80"
+ },
+ {
+ "hint": "Draw square with a size of 50",
+ "solution": "square 50"
+ },
+ {
+ "hint": "Move to the right, **type** `move 170`",
+ "solution": "move 170"
+ },
+ {
+ "hint": "Draw square with a size of 50",
+ "solution": "square 50"
+ }
+ ],
+ "completion_text": "You built an awesome house with code!",
+ "cover": "house.png"
+}
diff --git a/lib/challenges/worlds/medium/index.json b/lib/challenges/worlds/medium/index.json
new file mode 100644
index 000000000..6d4e6623b
--- /dev/null
+++ b/lib/challenges/worlds/medium/index.json
@@ -0,0 +1,13 @@
+{
+ "challenges": [
+ "./shrinking",
+ "./random",
+ "./starry",
+ "./house",
+ "./dots",
+ "./gradient",
+ "./planet",
+ "./pizza"
+ ]
+}
+
diff --git a/lib/challenges/worlds/medium/pizza.json b/lib/challenges/worlds/medium/pizza.json
new file mode 100644
index 000000000..fef200afc
--- /dev/null
+++ b/lib/challenges/worlds/medium/pizza.json
@@ -0,0 +1,50 @@
+{
+ "id": "pizza",
+ "title": "Pizza",
+ "description": "Code yourself a tasty pizza!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Set the stroke to the color beige",
+ "solution": "stroke beige"
+ },
+ {
+ "hint": "Set the stroke to 50",
+ "solution": "stroke 50"
+ },
+ {
+ "hint": "Set the color to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a circle with a size of 200",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Looking tasty! Set the stroke back to 0",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Top it with a cheesy yellow circle of size 195",
+ "solution": "color yellow\ncircle 195",
+ "validate": [
+ "color yellow",
+ "circle 195"
+ ]
+ },
+ {
+ "hint": "It's not pizza without toppings - set the color to darkred",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "Now `moveTo 164, 290`",
+ "solution": "moveTo 164, 290"
+ },
+ {
+ "hint": "Cool, now draw a circle with a size of 20",
+ "solution": "circle 20"
+ }
+ ],
+ "completion_text": "Tasty! Finish it off with more toppings, let your imagination run wild and see what you can make, then share it with the world!",
+ "cover": "pizza.png"
+}
diff --git a/lib/challenges/worlds/medium/planet.json b/lib/challenges/worlds/medium/planet.json
new file mode 100644
index 000000000..bdc15596e
--- /dev/null
+++ b/lib/challenges/worlds/medium/planet.json
@@ -0,0 +1,60 @@
+{
+ "id": "planet",
+ "title": "Planet painter",
+ "description": "Code your own planet!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Set the background using a hex code - **type** `background '#444444'`",
+ "solution": "background '#444444'",
+ "validate": "background '\\#444444'"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the color to yellow",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Now let's open a loop - **type** `for i in [ 0 .. 32 ]`",
+ "solution": "for i in [ 0..32 ]"
+ },
+ {
+ "hint": "Now to set where each star will go - **type** `moveTo (random 1, 500), (random 1, 500)`",
+ "solution": " moveTo (random 1, 500), (random 1, 500)",
+ "validate": "__ moveTo ?\\( *random +1, +500 *\\), +\\( *random +1, +500 *\\)"
+ },
+ {
+ "hint": "Draw each star as a circle with a size of 5",
+ "solution": " circle 5"
+ },
+ {
+ "hint": "Now, make sure you're out of the for loop (not indented) and **type** `color purple`",
+ "solution": "color purple"
+ },
+ {
+ "hint": "Now `moveTo 250, 250`",
+ "solution": "moveTo 250, 250"
+ },
+ {
+ "hint": "Now draw a circle with a size of 170",
+ "solution": "circle 170"
+ },
+ {
+ "hint": "Set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Choose a thick, white stroke - in a new line, **type** `stroke white, 5`",
+ "solution": "stroke white, 5"
+ },
+ {
+ "hint": "Now draw an ellipse - **type** `ellipse 220, 4`",
+ "solution": "ellipse 220, 4"
+ }
+ ],
+ "completion_text": "Astronomical! Change the colors and see what you can add to make your planet even cooler before hitting share!",
+ "cover": "planet.png"
+}
diff --git a/lib/challenges/worlds/medium/random.json b/lib/challenges/worlds/medium/random.json
new file mode 100644
index 000000000..e9c8495d0
--- /dev/null
+++ b/lib/challenges/worlds/medium/random.json
@@ -0,0 +1,47 @@
+{
+ "id": "random",
+ "title": "Random!",
+ "description": "Not sure where to put something - theres a function for that!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Let's move the cursor somewhere random - **type** `moveTo (random 1, 500), (random 1, 500)`",
+ "solution": "moveTo (random 1, 500), (random 1, 500)",
+ "validate": "__moveTo ?\\( *random +1, +500 *\\), +\\( *random +1, +500 *\\)"
+ },
+ {
+ "hint": "Set the color to red - **type** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Now let's draw a circle in a random place - **type** `circle 200`",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Set the drawing color to green",
+ "solution": "color green"
+ },
+ {
+ "hint": "Now draw a circle with size 150",
+ "solution": "circle 150"
+ },
+ {
+ "hint": "Set the color to yellow",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Now draw a circle with size 100",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Set the color to blue",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Finish it of with a circle with a size of 50",
+ "solution": "circle 50"
+ }
+ ],
+ "completion_text": "Random functions give us a random number so we can put things in a surprise spot.",
+ "cover": "random.png"
+}
diff --git a/lib/challenges/worlds/medium/shrinking.json b/lib/challenges/worlds/medium/shrinking.json
new file mode 100644
index 000000000..15437942a
--- /dev/null
+++ b/lib/challenges/worlds/medium/shrinking.json
@@ -0,0 +1,27 @@
+{
+ "id": "shrinking",
+ "title": "Shrinking Circles",
+ "description": "Ever shrinking circles with a for loop!",
+ "code": "",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "Set the stroke - **type** `stroke gray, 3`",
+ "solution": "stroke gray, 3"
+ },
+ {
+ "hint": "Set the color to see through - **type** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "Let's draw 32 circles all at once - **type** `for i in [ 0 .. 32 ]`",
+ "solution": "for i in [ 0..32 ]"
+ },
+ {
+ "hint": "Awesome, now for the circles - inside the for loop **type** `circle 10 * i`",
+ "solution": " circle 10 * i"
+ }
+ ],
+ "completion_text": "Great! For loops let us repeat bits of code (it saves on all the typing)!",
+ "cover": "shrinking.png"
+}
diff --git a/lib/challenges/worlds/medium/starry.json b/lib/challenges/worlds/medium/starry.json
new file mode 100644
index 000000000..190fd5ec6
--- /dev/null
+++ b/lib/challenges/worlds/medium/starry.json
@@ -0,0 +1,37 @@
+{
+ "id": "starry",
+ "title": "Starry sky",
+ "description": "Code your own starry night sky using the random function!",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Set the background to darkblue",
+ "solution": "background darkblue",
+ "validate": "^background darkblue$"
+ },
+ {
+ "hint": "Now set the stroke to 0",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "And set the drawing color to yellow",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Now let's open a loop - **type** `for i in [ 0 .. 32 ]`",
+ "solution": "for i in [ 0..32 ]"
+ },
+ {
+ "hint": "Now to set where each star will go - **type** `moveTo (random 1, 500), (random 1, 500)`",
+ "solution": " moveTo (random 1, 500), (random 1, 500)",
+ "validate": "__ moveTo ?\\( *random +1, +500 *\\), +\\( *random +1, +500 *\\)"
+
+ },
+ {
+ "hint": "Finally draw each star as a circle with a size of 5",
+ "solution": " circle 5"
+ }
+ ],
+ "completion_text": "Awesome! Press space and see what happens to the stars...",
+ "cover": "starry-sky.png"
+}
diff --git a/lib/challenges/worlds/mischiefweek2015/cat.json b/lib/challenges/worlds/mischiefweek2015/cat.json
new file mode 100644
index 000000000..b7838444b
--- /dev/null
+++ b/lib/challenges/worlds/mischiefweek2015/cat.json
@@ -0,0 +1,139 @@
+{
+ "id": "cat",
+ "title": "Spooky Cat",
+ "cover": "mischiefweek2015/mw-002-cat.png",
+ "description": "Make a spooky cat with code",
+ "start_date": "2015-10-27T06:00:00",
+ "startAt": 2,
+ "completion_text": "Nice work - you made a spooky black cat! Try changing its color or even its tail! Don't forget to share your cat afterwards.",
+ "steps": [
+ {
+ "hint": "We can make the background a spooky black by **typing** `background black`.",
+ "solution": "background black"
+ },
+ {
+ "hint": "Set the stroke to zero so we have no outlines. **Type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Now let's set the color of the full moon. **Type** `color lightgray`",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Create a nice big circle for the moon. **Type** `circle 230`.",
+ "solution": "circle 230"
+ },
+ {
+ "hint": "Now move downwards so we can draw the grass on the ground. **Type** `moveTo 250, 500`.",
+ "solution": "moveTo 250, 500"
+ },
+ {
+ "hint": "It's night time, so we'll make the grass a dark green. **Type** `color darkgreen`.",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Draw the grassy hill with an ellipse so it's round. **Type** `ellipse 300, 100`",
+ "solution": "ellipse 300, 100"
+ },
+ {
+ "hint": "Now set the color of the cat! To make it black, **type** `color black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Move the cursor to draw the cat in the middle! **Type** `moveTo 250, 250`.",
+ "solution": "moveTo 250, 250"
+ },
+ {
+ "hint": "Let's draw the head with an ellipse. **Type** `ellipse 60, 40`.",
+ "solution": "ellipse 60, 40"
+ },
+ {
+ "hint": "Let's move to right and up to draw the first ear. **Type** `moveTo 280, 220`.",
+ "solution": "moveTo 280, 220"
+ },
+ {
+ "hint": "We'll use the polygon function to make a nice pointy ear! **Type** `polygon 0, -40, 28, 20, close`.",
+ "solution": "polygon 0, -40, 28, 20, close"
+ },
+ {
+ "hint": "Move to the left by 60 so we can draw the other ear! **Type** `move -60`.",
+ "solution": "move -60"
+ },
+ {
+ "hint": "This ear is a reflection of the other one! **Type** `polygon 0, -40, -28, 20, close`.",
+ "solution": "polygon 0, -40, -28, 20, close"
+ },
+ {
+ "hint": "Let's make the neck underneath. **Type** `moveTo 250, 310`.",
+ "solution": "moveTo 250, 310"
+ },
+ {
+ "hint": "Draw it using an ellipse! **Type** `ellipse 30, 50`.",
+ "solution": "ellipse 30, 50"
+ },
+ {
+ "hint": "Now lets make the body below. **Type** `moveTo 250, 360`.",
+ "solution": "moveTo 250, 360"
+ },
+ {
+ "hint": "We'll use a bigger ellipse this time round! **type** `ellipse 50, 60`.",
+ "solution": "ellipse 50, 60"
+ },
+ {
+ "hint": "Now we need to move down and right to make the tail! **Type** `moveTo 320, 380`.",
+ "solution": "moveTo 320, 380"
+ },
+ {
+ "hint": "Let's use a sneaky text trick to make a curvy tail. **Type** `font 150`.",
+ "solution": "font 150"
+ },
+ {
+ "hint": "We'll make the text bold to make a large. **Type** `bold true`.",
+ "solution": "bold true"
+ },
+ {
+ "hint": "Great! Now draw the tail by **typing** `text 'S'`.",
+ "solution": "text 'S'"
+ },
+ {
+ "hint": "Finally, we'll make the eyes. **Type** `moveTo 225, 250`.",
+ "solution": "moveTo 225, 250"
+ },
+ {
+ "hint": "Set the color to yellow. **Type** `color yellow`.",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "We'll draw it using an ellipse. **Type** `ellipse 14, 6`.",
+ "solution": "ellipse 14, 6"
+ },
+ {
+ "hint": "Now for the pupil! Set the color to black. **Type** `color black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Make a circle of radius 6. **Type** `circle 6`.",
+ "solution": "circle 6"
+ },
+ {
+ "hint": "Great! Now lets move right to do the other eye. **Type** `moveTo 275, 250`.",
+ "solution": "moveTo 275, 250"
+ },
+ {
+ "hint": "Set the color back to yellow. **Type** `color yellow`.",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Make the same ellipse as before. **Type** `ellipse 14,6`.",
+ "solution": "ellipse 14, 6"
+ },
+ {
+ "hint": "Set the color back to black. **type** `color black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Lastly create the second pupil. **Type** `circle 6`.",
+ "solution": "circle 6"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/mischiefweek2015/ghost.json b/lib/challenges/worlds/mischiefweek2015/ghost.json
new file mode 100644
index 000000000..456d04e4f
--- /dev/null
+++ b/lib/challenges/worlds/mischiefweek2015/ghost.json
@@ -0,0 +1,136 @@
+{
+ "id": "ghost",
+ "title": "Ghost",
+ "description": "Draw your very own ghost. Change the colors to win!",
+ "start_date": "2015-10-30T06:00:00",
+ "code": "ghostColor = '#809B79'\nfaceColor = '#634E42'\ntongueColor = '#7A5750'\n",
+ "startAt": 0,
+ "steps": [
+ {
+ "hint": "We need to set up some variables to color the ghost - **Type** `ghostColor = '#809B79'`",
+ "solution": "ghostColor = '#809B79'"
+ },
+ {
+ "hint": "Another variable for the face - **Type** `faceColor = '#634E42'`",
+ "solution": "faceColor = '#634E42'"
+ },
+ {
+ "hint": "Finally a variable for the tongue - **Type** `tongueColor = '#7A5750'`",
+ "solution": "tongueColor = '#7A5750'"
+ },
+ {
+ "hint": "We’ll use solid shapes, so set the stroke to zero with `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Our ghost leaves a trail behind that we need to draw first, so we'll set the draw color to something slightly darker with `color darken(ghostColor, 10)`",
+ "solution": "color darken(ghostColor, 10)"
+ },
+ {
+ "hint": "Move into position for the trail with `move 0, 60`",
+ "solution": "move 0, 60"
+ },
+ {
+ "hint": "And then draw the trail with `circle 70`",
+ "solution": "circle 70"
+ },
+ {
+ "hint": "Now we’ll draw the ghost’s body. Set the ghost’s color to our variable with `color ghostColor`",
+ "solution": "color ghostColor"
+ },
+ {
+ "hint": "Now move back to draw the body, move back up to the center with `move 0, -60`",
+ "solution": "move 0, -60"
+ },
+ {
+ "hint": "Draw the body with `circle 100`",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Now we’ll draw the mouth with the faceColor variable we defined. **Type** `color faceColor`.",
+ "solution": "color faceColor"
+ },
+ {
+ "hint": "Now move into position for the mouth with `move -40, 10`",
+ "solution": "move -40, 10"
+ },
+ {
+ "hint": "Draw the mouth with `rectangle 80, 35`",
+ "solution": "rectangle 80, 35"
+ },
+ {
+ "hint": "The teeth are a polygon. **Hint:** you can copy and paste this: `polygon 10, -10, 20, 0, 30, -10, 40, 0, 50, -10, 60, 0, 70, -10, 80, 0`",
+ "solution": "polygon 10, -10, 20, 0, 30, -10, 40, 0, 50, -10, 60, 0, 70, -10, 80, 0"
+ },
+ {
+ "hint": "Now for the tongue, move to the bottom of the mouth with `move 40, 35`",
+ "solution": "move 40, 35"
+ },
+ {
+ "hint": "Set the drawing color to our variable with `color tongueColor`",
+ "solution": "color tongueColor"
+ },
+ {
+ "hint": "The tongue is a half-circle of radius 25, which we can draw with `arc 25, 0, 1`",
+ "solution": "arc 25, 0, 1"
+ },
+ {
+ "hint": "For the eyes, set the drawing color with `color faceColor`",
+ "solution": "color faceColor"
+ },
+ {
+ "hint": "Move into position for the eyes with `move -40, -80`",
+ "solution": "move -40, -80"
+ },
+ {
+ "hint": "The eye is then drawn with `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Move for the second eye with `move 80, 0`",
+ "solution": "move 80, 0"
+ },
+ {
+ "hint": "Draw the second eye with `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "For our hands, were going to do something new: make a function. **Type** `hand = ->`",
+ "solution": "hand = ->"
+ },
+ {
+ "hint": "This indented block is where your function goes. Any time you call this function this entire block of code will run. Lets set the stroke for our hand with `stroke 10, darken(ghostColor, 10)`",
+ "solution": " stroke 10, darken(ghostColor, 10)"
+ },
+ {
+ "hint": "Then draw the first of the three fingers with `line 20, 30`",
+ "solution": " line 20, 30"
+ },
+ {
+ "hint": "Then the second with `line 0, 35`",
+ "solution": " line 0, 35"
+ },
+ {
+ "hint": "The final finger with `line -20, 30`",
+ "solution": " line -20, 30"
+ },
+ {
+ "hint": "Now to draw the hands: make sure you are out of the function block by pressing **BACKSPACE**. Then **type** `move 60, 100` for the first hand.",
+ "solution": "move 60, 100"
+ },
+ {
+ "hint": "Draw the hand at the current location with `hand()`",
+ "solution": "hand()"
+ },
+ {
+ "hint": "Move over to the left to draw the other hand with `move -200, 0`",
+ "solution": "move -200, 0"
+ },
+ {
+ "hint": "Draw the final hand with `hand()`",
+ "solution": "hand()"
+ }
+ ],
+ "completion_text": "Well done! You can change the color of your ghost by setting ghostColor, faceColor, and tongueColor to whatever you want.",
+ "cover": "mischiefweek2015/mw-005-ghost.png"
+}
diff --git a/lib/challenges/worlds/mischiefweek2015/hauntedhouse.json b/lib/challenges/worlds/mischiefweek2015/hauntedhouse.json
new file mode 100644
index 000000000..cca16f838
--- /dev/null
+++ b/lib/challenges/worlds/mischiefweek2015/hauntedhouse.json
@@ -0,0 +1,11 @@
+{
+ "id": "hauntedhouse",
+ "title": "Haunted House",
+ "description": "Dress the house up for Halloween and express your creativity.",
+ "start_date": "2015-11-01T06:00:00",
+ "completion_text": "It's Halloween Night! Change the colours at the top of the code to make this into a night scene then express your creativity by combining previous challenges with it or causing mischief, some ideas to get you started are in the comments.",
+ "startAt": 0,
+ "code": "#Colors, that need to be made spookier\nsky = aqua\nground = green\nsun = yellow\nbricks = brown\nroof = red\nframes = gray\nwindows = blue\ndoor = setBrightness(brown,-40)\n\n#Sky\nbackground sky\nstroke 0\nmoveTo 100, 100\ncolor sun\ncircle 60\n#Why not add some spooky clouds or bats\n\n#Ground\nmoveTo 250,530\ncolor ground\nellipse 500,150\n\n#House\nmoveTo 100,180\ncolor bricks\nrectangle 300,270\n\n#Windows\n#Cause some mischief, can you figure out how to throw eggs on all the windows in just 3 lines of code\ndrawWindow = (x,y) ->\n color setBrightness(frames,30)\n moveTo x,y\n rectangle 60,80\n color windows\n moveTo x+5,y+5\n rectangle 50,70\n color setBrightness(frames,30)\n moveTo x, y+37.5\n rectangle 60,5\n moveTo x+27.5, y\n rectangle 5,80\ndrawWindow(120,200)\ndrawWindow(220,200)\ndrawWindow(120,310)\ndrawWindow(320,200)\ndrawWindow(320,310)\n\n#Roof\ncolor roof\nmoveTo 100, 180\npolygon 150, -100, 300, 0\n#You could use the arc function to cover the roof in toilet paper \n\n\n#Door\ncolor setBrightness(frames,-10)\nmoveTo 215, 330\nrectangle 70,110\nmove 5,5\ncolor door\nrectangle 60,105\ncolor setBrightness(frames,-40) \nmove -15,105\nrectangle 90,20\nmove 65,-50\ncolor setBrightness(door,80)\ncircle 3\n#How about putting a pumpkin beside the door",
+ "steps": [],
+ "cover": "mischiefweek2015/mw-007-hauntedhouse.png"
+}
diff --git a/lib/challenges/worlds/mischiefweek2015/index.json b/lib/challenges/worlds/mischiefweek2015/index.json
new file mode 100644
index 000000000..2bbe6689e
--- /dev/null
+++ b/lib/challenges/worlds/mischiefweek2015/index.json
@@ -0,0 +1,11 @@
+{
+ "challenges": [
+ "./skull",
+ "./cat",
+ "./pumpkin",
+ "./potion",
+ "./ghost",
+ "./spiderweb",
+ "./hauntedhouse"
+ ]
+}
diff --git a/lib/challenges/worlds/mischiefweek2015/potion.json b/lib/challenges/worlds/mischiefweek2015/potion.json
new file mode 100644
index 000000000..cfb784232
--- /dev/null
+++ b/lib/challenges/worlds/mischiefweek2015/potion.json
@@ -0,0 +1,96 @@
+{
+ "id": "potion",
+ "title": "Potion Flask",
+ "description": "Brew a potion with code.",
+ "start_date": "2015-10-29T06:00:00",
+ "code": "",
+ "startAt": 2,
+ "completion_text": "Well done! You can change the color of your potion by setting potionColor to whatever you want. The clear glass you made with opacity(white, .4) will give it a cool effect, whatever color you choose.",
+ "cover": "mischiefweek2015/mw-004-potion.png",
+ "steps": [
+ {
+ "hint": "We want solid shapes for this challenge, so **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "For a dark look, lets set `background charcoal`",
+ "solution": "background charcoal"
+ },
+ {
+ "hint": "If we want to change our potion color we can set it to a variable. To do that **type** `potionColor = purple`",
+ "solution": "potionColor = purple"
+ },
+ {
+ "hint": "Let’s move into position with `move 0, 50`",
+ "solution": "move 0, 50"
+ },
+ {
+ "hint": "And set the drawing color with `color potionColor`",
+ "solution": "color potionColor"
+ },
+ {
+ "hint": "Finally, we can draw the potion with `circle 90`",
+ "solution": "circle 90"
+ },
+ {
+ "hint": "Our potion is going to go up the neck of the flask, and we will use a line. Set up the line style with `stroke potionColor, 40`",
+ "solution": "stroke potionColor, 40"
+ },
+ {
+ "hint": "And draw the line up from the middle with `line 0, -120`.",
+ "solution": "line 0, -120"
+ },
+ {
+ "hint": "Set the stroke back to zero for the flask’s glass. **Type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Let’s add in another circle to give our potion a natural look. Move to the right and up with `move 30, -30`.",
+ "solution": "move 30, -30"
+ },
+ {
+ "hint": "Set the drawing color to a shade slightly darker than the potion color with `color darken(potionColor, 10)`",
+ "solution": "color darken(potionColor, 10)"
+ },
+ {
+ "hint": "Finally, draw the circle with `circle 20`",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Move back to the center with `move -30, 30`",
+ "solution": "move -30, 30"
+ },
+ {
+ "hint": "Set the color for the glass flask with `color opacity(white, .4)`",
+ "solution": "color opacity(white, .4)"
+ },
+ {
+ "hint": "Then draw the flask with an arc. **Type** `arc 100, 1.4, 1.6`",
+ "solution": "arc 100, 1.4, 1.6"
+ },
+ {
+ "hint": "See how the arc draws almost a complete circle, but leaves a nice flat top for us? Our flask neck will meet it there, but first we need to draw a cork behind the glass. **Type** `move 0, -130`",
+ "solution": "move 0, -130"
+ },
+ {
+ "hint": "Set the drawing color for the cork with `color tan`",
+ "solution": "color tan"
+ },
+ {
+ "hint": "And draw the cork with `polygon 20, 0, 30, -40, -30, -40, -20, 0`",
+ "solution": "polygon 20, 0, 30, -40, -30, -40, -20, 0"
+ },
+ {
+ "hint": "Now for the neck, lets choose the see-through white color for our stroke. **Type** `stroke 60, opacity(white, .4)`",
+ "solution": "stroke 60, opacity(white, .4)"
+ },
+ {
+ "hint": "Move up just a little bit for the neck with `move 0, -25`",
+ "solution": "move 0, -25"
+ },
+ {
+ "hint": "Finally, draw the neck with `line 0, 60`",
+ "solution": "line 0, 60"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/mischiefweek2015/pumpkin.json b/lib/challenges/worlds/mischiefweek2015/pumpkin.json
new file mode 100644
index 000000000..16e88cd86
--- /dev/null
+++ b/lib/challenges/worlds/mischiefweek2015/pumpkin.json
@@ -0,0 +1,87 @@
+{
+ "id": "pumpkin",
+ "title": "Kan-o-lantern",
+ "cover": "mischiefweek2015/mw-003-pumpkin.png",
+ "description": "It wouldn't be Halloween without pumpkins, learn how to carve a spooky pumpkin then get creative by changing the face.",
+ "start_date": "2015-10-28T06:00:00",
+ "completion_text": "That's a nice Kan-o-lantern! But it needs to be lit to be truly spooky, can you figure out how to light the pumpkin by changing some colors, you could also try carving a spookier looking face. Show off your creativity then share your creation.",
+ "startAt": 0,
+ "steps": [
+ {
+ "hint": "Let’s set a spooky scene, make it night time - **type** `background charcoal`",
+ "solution": "background charcoal"
+ },
+ {
+ "hint": "The most distinctive thing about pumpkins is their bright orange skin, to set your fill color - **type** `color orange`",
+ "solution": "color orange"
+ },
+ {
+ "hint": "We're going to use strokes to build the bulbous shape of the pumpkin, use the darken function to build contrast against the skin - **type** `stroke darken(orange, 20), 30`",
+ "solution": "stroke darken(orange, 20), 30"
+ },
+ {
+ "hint": "Build your pumpkin slice by slice using ellipses - **type** `ellipse 180, 140`",
+ "solution": "ellipse 180, 140"
+ },
+ {
+ "hint": "Add another slice - **type** `ellipse 130, 140`",
+ "solution": "ellipse 130, 140"
+ },
+ {
+ "hint": "It's starting to take shape, add the final slice the same way - **type** `ellipse 50, 140`",
+ "solution": "ellipse 50, 140"
+ },
+ {
+ "hint": "Pumpkins are actually fruits, so we need to draw a green stem - **type** `color green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Turn off the orange stroke - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to the top of the pumpkin - **type** `move -20, -180`",
+ "solution": "move -20, -180"
+ },
+ {
+ "hint": "Draw the chunky stem of the pumpkin - **type** `square 40`",
+ "solution": "square 40"
+ },
+ {
+ "hint": "Now we have a fresh pumpkin, we can get to the fun part, carving your design - **type** `color black`",
+ "solution": "color black"
+ },
+ {
+ "hint": "We're going to give them a face, move the cursor to where the eye will be - **type** `moveTo 190, 275`",
+ "solution": "moveTo 190, 275"
+ },
+ {
+ "hint": "Cut a hole for the first eye - **type** `circle 15`",
+ "solution": "circle 15"
+ },
+ {
+ "hint": "Move the cursor to where the second eye will be - **type** `moveTo 310, 275`",
+ "solution": "moveTo 310, 275"
+ },
+ {
+ "hint": "Cut the second hole the same size as the first - **type** `circle 15`",
+ "solution": "circle 15"
+ },
+ {
+ "hint": "Next we need to give them a mouth - **type** `moveTo 250, 320`",
+ "solution": "moveTo 250, 320"
+ },
+ {
+ "hint": "We need to turn the fill colour off by setting it to null - **type** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "We'll use a thick black stroke for the mouth - **type** `stroke 15, black`",
+ "solution": "stroke 15, black"
+ },
+ {
+ "hint": "Finally we will use the arc command to give them a smile. - **type** `arc 40, 1, 2, true`",
+ "solution": "arc 40, 1, 2, true"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/mischiefweek2015/skull.json b/lib/challenges/worlds/mischiefweek2015/skull.json
new file mode 100644
index 000000000..ba27a9392
--- /dev/null
+++ b/lib/challenges/worlds/mischiefweek2015/skull.json
@@ -0,0 +1,75 @@
+{
+ "id": "skull",
+ "title": "Skull",
+ "cover": "mischiefweek2015/mw-001-skull.png",
+ "start_date": "2015-10-26T06:00:00",
+ "description": "",
+ "completion_text": "Amazing skull! You can dress it up with some background patterns or other facial accessories. When you are done, share your creation for a chance to win it on a t-shirt!",
+ "startAt": 0,
+ "steps": [
+ {
+ "hint": "First we need to set the spooky scene, make it night time - **type** `background charcoal`",
+ "solution": "background charcoal"
+ },
+ {
+ "hint": "We want to draw solid shapes with no stroke, so **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "We can move into position for the skull with `moveTo 250, 200`",
+ "solution": "moveTo 250, 200"
+ },
+ {
+ "hint": "Our skull should be white, so set the drawing color with `color white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "The skull is made with an ellipse—a kind of squashed circle. Make this with `ellipse 140, 120`",
+ "solution": "ellipse 140, 120"
+ },
+ {
+ "hint": "It's starting to take shape, add the final slice the same way - **type** `move -60, 90`",
+ "solution": "move -60, 90"
+ },
+ {
+ "hint": "The mouth of the skull is a square we will draw with `square 120`",
+ "solution": "square 120"
+ },
+ {
+ "hint": "Lets move up and over for the eyes - **type** `move 10, -50`",
+ "solution": "move 10, -50"
+ },
+ {
+ "hint": "Our skull’s empty eyes peer into your soul. Set their color to charcoal with `color charcoal`",
+ "solution": "color charcoal"
+ },
+ {
+ "hint": "The eye is a circle. Draw it with `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Move on over for the other eye. **Type** `move 100`",
+ "solution": "move 100"
+ },
+ {
+ "hint": "Now draw the other eye with `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Move on over for the nose with `move -50, 60`",
+ "solution": "move -50, 60"
+ },
+ {
+ "hint": "Then draw the nose with `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "Finally, for the mouth move down by **typing** `move -40, 60`",
+ "solution": "move -40, 60"
+ },
+ {
+ "hint": "Finally, you draw the mouth with `rectangle 80, 20`",
+ "solution": "rectangle 80, 20"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/mischiefweek2015/spiderweb.json b/lib/challenges/worlds/mischiefweek2015/spiderweb.json
new file mode 100644
index 000000000..7d3befdb4
--- /dev/null
+++ b/lib/challenges/worlds/mischiefweek2015/spiderweb.json
@@ -0,0 +1,12 @@
+{
+ "id": "spiderweb",
+ "title": "Spider Web",
+ "description": "Play around with a spider web!",
+ "completion_text": "Happy Halloween! Today’s challenge is for you to play around with the code here. Can you make a different-colored web? Or a bigger spider? Thanks to community member @NOPx90 who coded the spiderweb!",
+ "cover": "mischiefweek2015/mw-006-spiderweb.png",
+ "start_date": "2015-10-31T06:00:00",
+ "startAt": 0,
+ "code": "# PLAY WITH THESE\nradius = 370\nframes = 30\nbridges = 10\ndegrees = 360\nrotation = 0\n\nbodySize = 80\nlegSpread = 90\nlegLength = 100\neyeSize = 4\neyeSpacing = 12\n\n\n# This defines the spiderweb\nclass SpiderWeb\n constructor: (@x, @y, @radius, @frames, @bridges, @degrees, @rotation) ->\n @x = @x ? 250\n @y = @y ? 250\n @radius = @radius ? 250\n @frames = @frames ? 16\n @bridges = @bridges ? 20\n @degrees = @degrees ? 360\n @rotation = @rotation ? 0\n @frameAngle = @degrees / @frames\n @draw()\n drawFrame: () ->\n moveTo @x , @y\n for i in [ 0 .. @frames ]\n if i * @frameAngle != 360\n radians = (i * @frameAngle + @rotation)*(Math.PI / 180)\n x = Math.cos(radians)\n y = -Math.sin(radians)\n line (x*@radius) , (y*@radius)\n drawBridge: () ->\n r = @radius\n for web in [ 1 .. @bridges ]\n r /= 1.2\n for i in [ 0 ... @frames ]\n # Starting postition\n r1 = (i * @frameAngle + @rotation)*(Math.PI / 180)\n x1 = Math.cos(r1)\n y1 = -Math.sin(r1)\n \n # End position\n r2 = (i * @frameAngle + @frameAngle + @rotation)*(Math.PI / 180)\n x2 = Math.cos(r2)\n y2 = -Math.sin(r2)\n moveTo x1 * r + @x , y1 * r + @y\n lineTo x2 * r + @x , y2 * r + @y\n draw: ( ) ->\n this.drawFrame()\n this.drawBridge()\n\n# Here is the drawing\nbackground setSaturation(darkpurple,-25)\nstroke white\nweb = new SpiderWeb(250,250, radius, frames, bridges, degrees, rotation)\n# Spider Body\nstroke white, 10\nmoveTo 250, 0\nline 0, 200\nmove 0, 200\ncolor black\nstroke 0\nellipse bodySize * 0.8, bodySize\n# Spider Legs\nstroke black, 5\ncolor null\npairOfLegs = (flipHori,flipVert) ->\n spread = legSpread * flipHori\n length = legLength * flipVert\n polygon spread, length, spread * 0.7, length * 2\n polygon spread * 1.3, length * 0.4, spread * 1.7, length * 1.5\npairOfLegs(1,1)\npairOfLegs(-1,1)\npairOfLegs(1,-1)\npairOfLegs(-1,-1)\n\n#Spider Head\nstroke 0\ncolor black\nmove 0, bodySize\nellipse bodySize * 0.5, bodySize * 0.4\ncolor orangered\nmove eyeSpacing * -1.5, eyeSpacing / -2\nfor [1 .. 4]\n circle eyeSize\n move 0, eyeSpacing\n circle eyeSize\n move eyeSpacing, eyeSpacing * -1\n",
+ "steps": [
+ ]
+}
diff --git a/lib/challenges/worlds/ozwaldboateng/ancientcodes.json b/lib/challenges/worlds/ozwaldboateng/ancientcodes.json
new file mode 100644
index 000000000..b15f1ee9b
--- /dev/null
+++ b/lib/challenges/worlds/ozwaldboateng/ancientcodes.json
@@ -0,0 +1,55 @@
+{
+ "id": "ancientcodes",
+ "title": "Ancient Codes",
+ "description": "Learn how to take simple shapes and compose them into a repeatable pattern",
+ "code": "houndstooth = ->\n stroke 0\n rectangle 20, 10\n rectangle -10, 20\n rectangle 10, -20\n rectangle -20, -10\n\n",
+ "startAt": 8,
+ "steps": [
+ {
+ "hint": "",
+ "solution": "houndstooth = ->"
+ },
+ {
+ "hint": "",
+ "solution": " stroke 0"
+ },
+ {
+ "hint": "",
+ "solution": " rectangle 20, 10"
+ },
+ {
+ "hint": "",
+ "solution": " rectangle -10, 20"
+ },
+ {
+ "hint": "",
+ "solution": " rectangle 10, -20"
+ },
+ {
+ "hint": "",
+ "solution": " rectangle -20, -10"
+ },
+ {
+ "hint": "In this challenge we’re going to take our houndstooth shape and turn it into a more complex pattern. We’ve put your houndstooth code into a reusable function, which will save us the trouble of writing all those commands hundreds of times. We’ll put them in a pattern with a purple background. Type `background rgb(110, 60, 158)`",
+ "solution": "background rgb(110, 60, 158)"
+ },
+ {
+ "hint": "We will use a loop to travel across the canvas with `for x in [0 .. 500] by 40`",
+ "solution": "for x in [0 .. 500] by 40"
+ },
+ {
+ "hint": "Next let’s use another loop to travel down the canvas. Type `for y in [20 .. 500] by 40`.",
+ "solution": " for y in [20 .. 500] by 40"
+ },
+ {
+ "hint": "Let’s move into position with `moveTo x, y`",
+ "solution": " moveTo x, y"
+ },
+ {
+ "hint": "And now we use the special word we created above with your code by typing `houndstooth()`",
+ "solution": " houndstooth()"
+ }
+ ],
+ "completion_text": "Well done! The houndstooth is a popular pattern that gets its distinctive looks from the loom it is woven on. Looms have been used for over eight thousand years, and the intricate designs produced by them were created with some of the earliest coding languages. In fact the digital information encoded in punch cards were first used by 19th century looms before computers borrowed them.",
+ "cover": "ozwaldboateng/ancientcodes.png"
+}
diff --git a/lib/challenges/worlds/ozwaldboateng/bespokedesign.json b/lib/challenges/worlds/ozwaldboateng/bespokedesign.json
new file mode 100644
index 000000000..d83e27e36
--- /dev/null
+++ b/lib/challenges/worlds/ozwaldboateng/bespokedesign.json
@@ -0,0 +1,46 @@
+{
+ "id": "bespokedesign",
+ "title": "Bespoke Design",
+ "description": "Bring out your own personality with a twist on this subtle tweed pattern",
+ "code": "",
+ "steps": [
+ {
+ "hint": "This is an opportunity for you to bring out your own personality with a twist on the classic herringbone zigzag pattern. We’ll make it a bright Boateng orange by typing `stroke orange, 2`",
+ "solution": "stroke orange, 2"
+ },
+ {
+ "hint": "Move into position with `move -50, -50`",
+ "solution": "move -50, -50"
+ },
+ {
+ "hint": "To loop down the canvas, type `for i in [0 ... 10]`",
+ "solution": "for i in [0 ... 10]"
+ },
+ {
+ "hint": "Our zigzag’s direction switches between up and down, to keep track we’ll use a variable. Type `direction = 20`",
+ "solution": " direction = 20"
+ },
+ {
+ "hint": "Next, we’ll make another loop to travel across the canvas. Type `for j in [0 ... 10]`",
+ "solution": " for j in [0 ... 10]"
+ },
+ {
+ "hint": "Draw the line with `line 10, direction`",
+ "solution": " line 10, direction"
+ },
+ {
+ "hint": "Then we’ll move the drawing cursor into position at the end of the line with `move 10, direction`",
+ "solution": " move 10, direction"
+ },
+ {
+ "hint": "We’ll change direction of our zigzag by multiplying it by negative one. Type `direction *= -1`",
+ "solution": " direction *= -1"
+ },
+ {
+ "hint": "Exit the second loop by pressing `BACKSPACE` once. Next, type `move -100, 10`, making sure you only have one tab before it.",
+ "solution": " move -100, 10"
+ }
+ ],
+ "completion_text": "This herringbone pattern has been worn by many thousands of men and women throughout history. Ozwald Boateng uses it every now and then, but still manages to create a suit that becomes a unique expression of the wearer’s personality. Being true to yourself doesn’t mean you have to do something nobody else is doing, sometimes it means taking what you know and making it yours. Play around with the colors here—find something that feels right for you.",
+ "cover": "ozwaldboateng/bespokedesign.png"
+}
diff --git a/lib/challenges/worlds/ozwaldboateng/color.json b/lib/challenges/worlds/ozwaldboateng/color.json
new file mode 100644
index 000000000..d36844687
--- /dev/null
+++ b/lib/challenges/worlds/ozwaldboateng/color.json
@@ -0,0 +1,27 @@
+{
+ "id": "color",
+ "title": "Color",
+ "description": "Learn how Ozwald Boateng chooses combinations to evoke feelings",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Welcome to the world of London fashion designer Ozwald Boateng. His iconic suits have been worn by the likes of Will Smith and Richard Branson, and are well known for their vibrant, nontraditional colors. We’ll explore how Boateng uses color with a bit of an experiment. Set the drawing color to blue by typing `color rgb(102, 211, 231)`.",
+ "solution": "color rgb(102, 211, 231)"
+ },
+ {
+ "hint": "Great, now we’ll draw a shape using this bright blue. First we need to do some housekeeping by turning off stroke by typing `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Now draw the circle by typing `circle 150`",
+ "solution": "circle 150"
+ },
+ {
+ "hint": "This blue evokes a certain feeling when set against a white background. But watch, as you set it against another color, the same blue circle will change its appearance. Type `background rgb(109, 147, 248)`",
+ "solution": "background rgb(109, 147, 248)"
+ }
+ ],
+ "completion_text": "Amazing! See how adding a different background color completely changes the feeling of the blue in the circle? It isn’t as overpowering as it first appeared. Ozwald Boateng uses tricks like this to create harmony and dissonance with his use of color. See if you can change the colors of the background and circle to bring out different emotions.",
+ "cover": "ozwaldboateng/color.png"
+}
diff --git a/lib/challenges/worlds/ozwaldboateng/cut.json b/lib/challenges/worlds/ozwaldboateng/cut.json
new file mode 100644
index 000000000..16656ffba
--- /dev/null
+++ b/lib/challenges/worlds/ozwaldboateng/cut.json
@@ -0,0 +1,43 @@
+{
+ "id": "cut",
+ "title": "Cut",
+ "description": "see how a simple technique, nested looping, can be used to create very powerful illusions",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Boateng’s suits play tricks on the eyes, making the wearer appear both taller and slimmer. We’ll use simple shapes to create a similar illusion. Start by typing `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "We’ll set the background to a big beautiful red by typing `background rgb(208, 89, 109)`",
+ "solution": "background rgb(208, 89, 109)"
+ },
+ {
+ "hint": "And contrast it by setting the drawing color for our shapes to blue with `color rgb(102, 211, 231)`",
+ "solution": "color rgb(102, 211, 231)"
+ },
+ {
+ "hint": "Next, we start a loop that will scan left to right across the canvas, drawing shapes. Type `for x in [50 .. 450] by 20`.",
+ "solution": "for x in [50 .. 450] by 20"
+ },
+ {
+ "hint": "Each shape is going to start small on the left, grow slightly bigger in the middle, and become small again on the right. For this we will use what computer artists call a “shaping function” and store its value in the variable “size”. Type `size = Math.pow(x - 250, 2) / -4000 + 10`.",
+ "solution": " size = Math.pow(x - 250, 2) / -4000 + 10"
+ },
+ {
+ "hint": "We’ll make another loop, this time to run top to bottom with `for y in [50 .. 450] by 20`.",
+ "solution": " for y in [50 .. 450] by 20"
+ },
+ {
+ "hint": "Now for each iteration of the loops we want to move the drawing cursor into position by typing `moveTo x, y`",
+ "solution": " moveTo x, y"
+ },
+ {
+ "hint": "And finally, draw the squares using the size variable we created above with `square size`",
+ "solution": " square size"
+ }
+ ],
+ "completion_text": "Beautiful! Using only rectangles, we can achieve an image that transcends the screen it sits on. Boateng’s masterful understanding of the human body helps him cut a slim silhouette, combining simple shapes into a moving masterpiece, similar to what you have done here.",
+ "cover": "ozwaldboateng/cut.png"
+}
diff --git a/lib/challenges/worlds/ozwaldboateng/index.json b/lib/challenges/worlds/ozwaldboateng/index.json
new file mode 100644
index 000000000..f7dc1ad95
--- /dev/null
+++ b/lib/challenges/worlds/ozwaldboateng/index.json
@@ -0,0 +1,12 @@
+{
+ "challenges": [
+ "./color",
+ "./material",
+ "./cut",
+ "./pattern",
+ "./ancientcodes",
+ "./inspiration",
+ "./bespokedesign",
+ "./masterchallenge"
+ ]
+}
diff --git a/lib/challenges/worlds/ozwaldboateng/inspiration.json b/lib/challenges/worlds/ozwaldboateng/inspiration.json
new file mode 100644
index 000000000..5a6ed7296
--- /dev/null
+++ b/lib/challenges/worlds/ozwaldboateng/inspiration.json
@@ -0,0 +1,39 @@
+{
+ "id": "inspiration",
+ "title": "Inspiration",
+ "description": "",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "The artist Bridget Riley, spent years copying famous paintings to learn about their use of shape and color before creating her minimalist masterpieces. This composition has a nice ivory background. Type `background ivory`.",
+ "solution": "background ivory"
+ },
+ {
+ "hint": "We’ll want to see only the outlines of the circles. We can do this by typing `color transparent`",
+ "solution": "color transparent"
+ },
+ {
+ "hint": "Next we’ll use two loops to draw our circles. The first one goes across the canvas in increments of fifty. Type `for x in [0 .. 500] by 50`",
+ "solution": "for x in [0 .. 500] by 50"
+ },
+ {
+ "hint": "The second loop goes down the canvas. Type `for y in [0 .. 500] by 50`",
+ "solution": " for y in [0 .. 500] by 50"
+ },
+ {
+ "hint": "We only want to draw circles sometimes. Which we will do by using an “if statement”. Type `if random(0, 10) > 2`. Read this as “if the computer guesses a random number between 0 and 10, and it is bigger than two, draw a circle.”",
+ "solution": " if random(0, 10) > 2"
+ },
+ {
+ "hint": "Next let’s move into position with `moveTo x, y`",
+ "solution": " moveTo x, y"
+ },
+ {
+ "hint": "Finally we can draw the circle with `circle 50`",
+ "solution": " circle 50"
+ }
+ ],
+ "completion_text": "You have to know the rules first if you want to break them. Ozwald Boateng grew up wearing suits he sewed himself. As he grew older his designs became more distinctive, he started breaking traditions and creating new ones, just like how Bridget Riley did with her paintings of circles. Today, Boateng’s suits are sold in a store on Savile Row, the centuries-old home of British tailoring where the suit was invented.",
+ "cover": "ozwaldboateng/inspiration.png"
+}
diff --git a/lib/challenges/worlds/ozwaldboateng/masterchallenge.json b/lib/challenges/worlds/ozwaldboateng/masterchallenge.json
new file mode 100644
index 000000000..039c6e9be
--- /dev/null
+++ b/lib/challenges/worlds/ozwaldboateng/masterchallenge.json
@@ -0,0 +1,9 @@
+{
+ "id": "masterchallenge",
+ "title": "Master Challenge",
+ "description" : "To create something new you must look into the future",
+ "code": "cross = ->\n stroke 0\n color yellow\n for x in [0 ... 100] by 20\n move 0, x\n square 20\n move 0, 80 - 2 * x\n square 20\n move 20, -(80 - x)\n\nmypattern = ->\n move 50, 50\n ### ▼ Your Code here ▼ ###\n \n ### ▲ Your Code here ▲ ###\n\nbackground black\nfor x in [0 ... 500] by 100\n for y in [0 ... 500] by 100\n moveTo x, y\n if (x / 100 + y / 100) % 2 is 1\n cross()\n else\n mypattern()\nstroke 2, white\nfor x in [100 ... 500] by 100\n moveTo x, 0\n line 0, 500\nfor y in [100 ... 500] by 100\n moveTo 0, y\n line 500, 0",
+ "steps": [],
+ "completion_text": "“If you can’t visualise it, you’ll never see it.” — Ozwald Boateng. You may look backwards to search for inspiration, but to create something new you must look into the future. This is a traditional Kente pattern from Ghana, where Ozwald Boateng’s parents moved to London from. Finish the design by typing your own code on to line 14. If you want, go back to the previous zig-zag challenge to copy that code and paste it here. It works perfectly. Good luck!",
+ "cover": "ozwaldboateng/masterchallenge.png"
+}
diff --git a/lib/challenges/worlds/ozwaldboateng/material.json b/lib/challenges/worlds/ozwaldboateng/material.json
new file mode 100644
index 000000000..b133040ca
--- /dev/null
+++ b/lib/challenges/worlds/ozwaldboateng/material.json
@@ -0,0 +1,27 @@
+{
+ "id": "material",
+ "title": "Material",
+ "description": "learn composition and how simple layering can be used to create beautiful shapes",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Like fashion designers who choose fabrics based on how they stretch and feel, computer artists have to carefully choose and combine shapes. Type `rectangle 100, 50`.",
+ "solution": "rectangle 100, 50"
+ },
+ {
+ "hint": "When arranged by a talented maker, simple shapes can magically transform into complex creations. Type `rectangle -50, 100`.",
+ "solution": "rectangle -50, 100"
+ },
+ {
+ "hint": "This challenge repeats the same rectangular shape four times to create the houndstooth, a centuries-old weaving pattern. Type `rectangle 50, -100`.",
+ "solution": "rectangle 50, -100"
+ },
+ {
+ "hint": "For the final piece, type `rectangle -100, -50`.",
+ "solution": "rectangle -100, -50"
+ }
+ ],
+ "completion_text": "Great job! Ozwald Boateng is famous for his use of mohair in his suits. Like wool it is warm in the winter and cool in the summer, but it is uniquely soft and silky, perfect for the modern man. The properties of the fabric become a part of the suit, but the arrangement and cut of the fabric create the final look.",
+ "cover": "ozwaldboateng/material.png"
+}
diff --git a/lib/challenges/worlds/ozwaldboateng/pattern.json b/lib/challenges/worlds/ozwaldboateng/pattern.json
new file mode 100644
index 000000000..9d23654f5
--- /dev/null
+++ b/lib/challenges/worlds/ozwaldboateng/pattern.json
@@ -0,0 +1,35 @@
+{
+ "id": "pattern",
+ "title": "Pattern",
+ "description": "Learn about color interactions and how shapes can affect our perception of color",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Patterns can create entirely new spaces of color. We’ll create a pattern based off of Ozwald Boateng’s limited edition ties from this season. Let’s get started by typing `background black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "We’ll set the stroke color with `stroke 30, red`",
+ "solution": "stroke 30, red"
+ },
+ {
+ "hint": "And to give us just the strokes with no fill, we will type `color transparent`",
+ "solution": "color transparent"
+ },
+ {
+ "hint": "Our pattern is created by circles inside of circles, which we’ll draw using a loop that radiates outwards. Type `for r in [0 .. 350] by 50`.",
+ "solution": "for r in [0 .. 350] by 50"
+ },
+ {
+ "hint": "Our second loop will go all around the circle, letting us draw in our pattern. Type `for i in [0 ... 2] by .1`",
+ "solution": " for i in [0 ... 2] by .1"
+ },
+ {
+ "hint": "Finally, complete the pattern by drawing the arcs with `arc r, i, i - .05`",
+ "solution": " arc r, i, i - .05"
+ }
+ ],
+ "completion_text": "Great job! This hypnotic circular pattern uses solid black and red, but the simultaneous contrast of both change our perception of both, making our brain think it sees colors where our eyes actually see sharp edges! Ozwald Boateng has ties in many different colors, try remixing the code for your own unique color combination.",
+ "cover": "ozwaldboateng/pattern.png"
+}
diff --git a/lib/challenges/worlds/pixelhack/chest.json b/lib/challenges/worlds/pixelhack/chest.json
new file mode 100644
index 000000000..60635a1ec
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/chest.json
@@ -0,0 +1,104 @@
+{
+ "id": "chest",
+ "title": "Loot Chest",
+ "description": "A mysterious chest found with your code",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Role Playing Games (RPGs) became one of the most popular genres of games in the late 80s. One of the most common objects in these games is the loot chest, let's start by setting a moody scene with a background **type** `background darkslategray`",
+ "solution": "background darkslategray"
+ },
+ {
+ "hint": "Next we're going to turn the stroke off **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Position the cursor to the top left corner of where the chest will be - **type** `move -150,-100`",
+ "solution": "move -150,-100"
+ },
+ {
+ "hint": "Set the color to gold to give the impression of riches inside **type** `color gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "We're going to layer this up to keep the number of shapes we have to draw to a minimum, first we draw the golden frame draw a `rectangle` 300 by 200",
+ "solution": "rectangle 300,200"
+ },
+ {
+ "hint": "Next we are going to add a highlight to the gold **type** `color lightyellow`",
+ "solution": "color lightyellow"
+ },
+ {
+ "hint": "With 8-bit art, simple details add a lot to an object, add a 25 by 100 `rectangle` to give the gold a nice sheen",
+ "solution": "rectangle 25,100"
+ },
+ {
+ "hint": "Move your cursor to get ready to draw the wooden part of the chest **type** `move 25,0`",
+ "solution": "move 25,0"
+ },
+ {
+ "hint": "Change the fill color to `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "We use a trick of overlaying brown over the gold so the gold looks like a frame holding the wooden panels together **type** `rectangle 250,175`",
+ "solution": "rectangle 250,175"
+ },
+ {
+ "hint": "Move the cursor into place to split the wooden part in half with a lid **type** `move 0,60`",
+ "solution": "move 0,60"
+ },
+ {
+ "hint": "Switch your `color` back to gold",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Draw the seam with a `rectangle` 250 by 25",
+ "solution": "rectangle 250,25"
+ },
+ {
+ "hint": "Next we'll continue our highlight effect **type** `color lightyellow`",
+ "solution": "color lightyellow"
+ },
+ {
+ "hint": "Continue the highlight effect by drawing a `square` 25 big",
+ "solution": "square 25"
+ },
+ {
+ "hint": "Finally we're going to finish with a clasp, move to the middle of the chest **type** `move 75,-25`",
+ "solution": "move 75,-25"
+ },
+ {
+ "hint": "Set the `color` to gold so it matches the frame",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Draw a `rectangle` 100 by 75",
+ "solution": "rectangle 100,75"
+ },
+ {
+ "hint": "Move in to draw the actual clasp **type** `move 25,25`",
+ "solution": "move 25,25"
+ },
+ {
+ "hint": "Set the `color` to darkbrown",
+ "solution": "color darkbrown"
+ },
+ {
+ "hint": "Finally draw a `rectangle` 50 by 75",
+ "solution": "rectangle 50,75"
+ }
+ ],
+ "completion_text": "That chest looks great, I wonder what loot is hiding within!",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "chest-nether.png",
+ "chest-sunken.png",
+ "chest-open.png"
+ ]
+ },
+ "cover": "pixel-chest.png",
+ "guide": "#### New Words\n**square** size | square 25\n\nThis is just a shorthand version of the rectangle \n\n#### What you'll make\n1. Role Playing Games (RPGs) became one of the most popular genres of games in the late 80s. One of the most common objects in these games is the loot chest, let's start by setting a moody scene with a background **type** `background darkslategray`\n2. Next we're going to turn the stroke off **type** `stroke 0`\n3. Position the cursor to the top left corner of where the chest will be **type** `move -150,-100`\n4. Set the color to gold to give the impression of riches inside **type** `color gold`\n5. We're going to layer this up to keep the number of shapes we have to draw to a minimum, first we draw the golden frame draw a `rectangle` 300 by 200\n6. Next we are going to add a highlight to the gold **type** `color lightyellow`\n7. With 8-bit art, simple details add a lot to an object, add a 25 by 100 `rectangle` to give the gold a nice sheen\n8. Move your cursor to get ready to draw the wooden part of the chest **type** `move 25,0`\n9. Change the fill color to `color brown`\n10. We use a trick of overlaying brown over the gold so the gold looks like a frame holding the wooden panels together **type** `rectangle 250,175`\n11. Move the cursor into place to split the wooden part in half with a lid **type** `move 0,60`\n12. Switch your `color` back to gold\n13. Draw the seam with a `rectangle` 250 by 25\n14. Next we'll continue our highlight effect **type** `color lightyellow`\n15. Continue the highlight effect by drawing a `square` 25 big\n16. Finally we're going to finish with a clasp, move to the middle of the chest **type** `move 75,-25`\n17. Set the `color` to gold so it matches the frame\n18. Draw a `rectangle` 100 by 75\n19. Move in to draw the actual clasp **type** `move 25,25`\n20. Set the `color` to darkbrown\n21. Finally draw a `rectangle` 50 by 75\n\n#### What you'll hack\nBy changing the colours and adding a few shapes you can build an environment around your chest. For a more complex challenge change your code to open the chest and code some loot within.\n\n#### Briefing \nThe loot chest has made appearances in too many video games to list. Possibly most prominently in games from the Legend of Zelda, where they would hold everything from small gifts of money to powerful new tools and weapons. What is in this loot chest? That’s up to you. When you’re done with the chest, open it up and put in your own creation as the loot.\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/colorfrenzy.json b/lib/challenges/worlds/pixelhack/colorfrenzy.json
new file mode 100644
index 000000000..888290bb3
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/colorfrenzy.json
@@ -0,0 +1,43 @@
+{
+ "id": "colorfrenzy",
+ "title": "Color Frenzy",
+ "description": "Use a loop to draw an 8-bit pattern.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We’ll just use solid shapes with no stroke. So **type** `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "For this one lets use two loops, one to go horizontally across the screen, and one to go down vertically. **Type** `for x in [ 0 .. 20 ]`",
+ "solution": "for x in [ 0 .. 20 ]"
+ },
+ {
+ "hint": "The second loop should be `for y in [ 0 .. 20 ]`",
+ "solution": " for y in [ 0 .. 20 ]"
+ },
+ {
+ "hint": "Set the color with `color rotate red, x + y * 10`",
+ "solution": " color rotate red, x + y * 10"
+ },
+ {
+ "hint": "Move into position with `moveTo x * 25, y * 25`",
+ "solution": " moveTo x * 25, y * 25"
+ },
+ {
+ "hint": "Finally, lets add squares. **Type** `square 25`",
+ "solution": " square 25"
+ }
+ ],
+ "completion_text": "Well done! The rotate function cycles through colours creating a rainbow effect.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "color-quint.png",
+ "color-multi.png",
+ "color-random.png"
+ ]
+ },
+ "cover": "pixel-colorfrenzy.png",
+ "guide": "#### What you'll make\n1. We’ll just use solid shapes with no stroke. So **type** `stroke 0`.\n2. For this one lets use two loops, one to go horizontally across the screen, and one to go down vertically. **Type** `for x in [ 0 .. 20 ]`\n3. The second loop should be `for y in [ 0 .. 20 ]`\n4. Set the color with `color rotate red, x * 10`\n5. Move into position with `moveTo x * 25, y * 25`\n6. Finally, lets add some randomness to the mix. **Type** `square random 20, 25`\n\n#### What you’ll hack\nBecause you are travelling across both the x and the y axes, you can have some pretty crazy color combinations. Using math you can make the rainbows repeat in interesting ways, and then change up how you draw on the screen using a random function. There are loads of variations, see what you can find!\n\n#### Briefing\nWe used one loop in the previous exercises to repeat actions, and then change one thing about the loop. In this challenge, we are going to be looping twice. The first loop is going to loop across the x coordinates—then the second loop is going to loop across the y coordinates. Because we have two loops, we are able to control color going in two different directions, and draw squares in two different directions. These “nested” loops will allow us to cover the canvas with color.\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/diamondblock.json b/lib/challenges/worlds/pixelhack/diamondblock.json
new file mode 100644
index 000000000..dc2633c4d
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/diamondblock.json
@@ -0,0 +1,72 @@
+{
+ "id": "diamondblock",
+ "title": "8-bit Diamond Block",
+ "description": "Make a diamond block with loops.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Set stroke to 0.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "We’ll use two loops for the stone. **Type** `for x in [0 ... 16]`",
+ "solution": "for x in [0 ... 16]"
+ },
+ {
+ "hint": "For y use `for y in [0 ... 16]`",
+ "solution": " for y in [0 ... 16]"
+ },
+ {
+ "hint": "We’ll use a randomly chosen shade of gray to make the stone. **Type** `color darken gray, random -6, 9`",
+ "solution": " color darken gray, random -6, 9",
+ "validate": " color darken gray, random -6, 9"
+ },
+ {
+ "hint": "Move into position for each square with `moveTo x * 31.25, y * 31.25`",
+ "solution": " moveTo x * 31.25, y * 31.25"
+ },
+ {
+ "hint": "Finally, draw the stone square with `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ },
+ {
+ "hint": "Get out of the loop by bringing your cursor to the beginning of the line with **BACKSPACE**. We’ll start a new pair of loops to draw the diamond. **Type** `for x in [2 ... 14]`",
+ "solution": "for x in [2 ... 14]"
+ },
+ {
+ "hint": "This time we are starting at 2 and ending at 14 because we only want the diamond to appear in the middle of the block. **Type** `for y in [2 ... 14]`",
+ "solution": " for y in [2 ... 14]"
+ },
+ {
+ "hint": "The diamond should appear randomly, this if statement will only randomly draw the diamond. **Type** `if 1 == random 0, 4`",
+ "solution": " if 1 == random 0, 4",
+ "validate": " if 1 == random 0, 4"
+ },
+ {
+ "hint": "Set the diamond color with `color lighten lightblue, random 0, 40`",
+ "solution": " color lighten lightblue, random 0, 40",
+ "validate": " color lighten lightblue, random 0, 40"
+ },
+ {
+ "hint": "Move into position for each square with `moveTo x * 31.25, y * 31.25`",
+ "solution": " moveTo x * 31.25, y * 31.25"
+ },
+ {
+ "hint": "Finally, draw the diamond square with `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ }
+ ],
+ "completion_text": "You’ve hacked the pixels and won! What’s next? More challenges and 8-bit art projects here at Kano if you are interested. Congratulations and well done!",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "diamond-obsidian.png",
+ "diamond-rainbow.png",
+ "diamond-lotsof.png"
+ ]
+ },
+ "cover": "pixel-diamondblock.png",
+ "guide": "#### What you'll make\n1. Set stroke to 0.\n2. We’ll use two loops for the stone. **Type** `for x in [0 ... 16]`\n3. For y use `for y in [0 ... 16]`\n4. We’ll use a randomly chosen shade of gray to make the stone. **Type** `color darken gray, random -6, 9`\n5. Move into position for each square with `moveTo x * 31.25, y * 31.25`\n6. Finally, draw the stone square with `square 32`\n7. Get out of the loop by bringing your cursor to the beginning of the line with **BACKSPACE**. We’ll start a new pair of loops to draw the diamond. **Type** `for x in [2 ... 14]`\n8. This time we are starting at 2 and ending at 14 because we only want the diamond to appear in the middle of the block. **Type** `for y in [2 ... 14]`\n9. The diamond should appear randomly, this if statement will only randomly draw the diamond. **Type** `if 1 == random 0, 4`\n10. Set the diamond color with `color lighten lightblue, random 0, 40`\n11. Move into position for each square with `moveTo x * 31.25, y * 31.25`\n12. Finally, draw the diamond square with `square 32`\n\n#### What you'll hack\nTo make this Minecraft Block your own all you need to change are two variables: the colour of the top and bottom parts. Make a strawberry, a water block, or anything else by just changing these two lines. For added complexity, make a Mycelium block with some randomness and another for loop. If you really want to have a challenge, try incorporating what you learned in the previous lesson and use the x and y values to add some extra color rotation to the dirt.\n\n#### Briefing\nThe grand finale: diamonds. This is the most precious and sought after Minecraft block. You’ll use nested for loops again to draw the stone, and then another pair of nested for loops to draw the stone, and then another nested for loop to draw the diamond. But as the diamond should be sprinkled throughout the stone we’ll use an if statement and use a random number to determine whether or not we should draw the square. Additionally, we only want to draw the diamond on the inside of the block, so our diamond-drawing loop will run from 2 to 14, rather than 0 to 16.\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/forloop.json b/lib/challenges/worlds/pixelhack/forloop.json
new file mode 100644
index 000000000..28798c88a
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/forloop.json
@@ -0,0 +1,95 @@
+{
+ "id": "forloop",
+ "title": "For Loop",
+ "description": "Use a for loop to build a snake with ease.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We're going to recreate the classic game Snake using the power of For Loops. First let's set a dirt background - **type** `background tan`",
+ "solution": "background tan"
+ },
+ {
+ "hint": "Turn off the stroke **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "First we position the head of the snake **type** `moveTo 80,250`",
+ "solution": "moveTo 80,250"
+ },
+ {
+ "hint": "Set a snake-like color **type** `color olivedrab`",
+ "solution": "color olivedrab"
+ },
+ {
+ "hint": "Draw a `circle` 30 pixels big for the head",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "This is where we will set the loop up, using the for function, the first number is where to start counting from and the second number is what to count to. So 1 .. 7 makes the computer count to 7 - **type** `for [1 .. 7]`",
+ "solution": "for [1 .. 7]"
+ },
+ {
+ "hint": "Everything within this indent will be repeated 7 times, we need a move function to position each part of the body - **type** `move 50,0`",
+ "solution": " move 50,0"
+ },
+ {
+ "hint": "We set the body color - **type** `color olivedrab`",
+ "solution": " color olivedrab"
+ },
+ {
+ "hint": "Once we add a circle it'll be clear what is going on with the code being repeated several times - **type** `circle 30`",
+ "solution": " circle 30"
+ },
+ {
+ "hint": "Let's add a square so the body looks more segmented, notice how we're now drawing 7 shapes for every line of code - **type** `square 30`",
+ "solution": " square 30"
+ },
+ {
+ "hint": "We're going to add another detail to the tail so set the `color` to darkgreen",
+ "solution": " color darkgreen"
+ },
+ {
+ "hint": "Draw another circle, the code is still indented this will be repeated 7 times - **type** `circle 20`",
+ "solution": " circle 20"
+ },
+ {
+ "hint": "Finally draw a `square` 20 pixels big",
+ "solution": " square 20"
+ },
+ {
+ "hint": "First make sure you are no longer in the for loop by hitting backspace so we're no longer indented. - **type** `moveTo 70,240`",
+ "solution": "moveTo 70,240"
+ },
+ {
+ "hint": "Change the `color` to black",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw an `ellipse` 10 pixels wide and 5 tall.",
+ "solution": "ellipse 10,5"
+ },
+ {
+ "hint": "Finally we'll give it a tongue so it can hiss like a snake should - **type** `color salmon`",
+ "solution": "color salmon"
+ },
+ {
+ "hint": "Position it on the face - **type** `move -15, 20`",
+ "solution": "move -15, 20"
+ },
+ {
+ "hint": "We'll draw a rectangle with a negative width to stretch it out from the mouth - **type** `rectangle -20,4`",
+ "solution": "rectangle -20,4"
+ }
+ ],
+ "completion_text": "Now you have seen how we can create more art with less lines of code by using loops in clever ways, why not try changing the 7 in the for loop and see what happenes",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "snake-tiny.png",
+ "snake-apple.png",
+ "snake-corner.png"
+ ]
+ },
+ "cover": "pixel-forloop.png",
+ "guide": "#### New words\n**for i in [rangeMinimum ... rangeMax]**| for in [0 .. 10]\n\nThe for loop executes once for every number in the range provided. 10 times for the range [0 … 10] and 20 times for the range [0 … 20]. The loop executes any code that is indented four spaces appearing after it.\n\n#### What you'll make\n1. We're going to recreate the classic game Snake using the power of For Loops. First let's set a dirt background - **type** `background tan`\n2. Turn off the stroke **type** `stroke 0`\n3. First we position the head of the snake **type** `moveTo 80,250`\n4. Set a snake-like color **type** `color olivedrab`\n5. Draw a `circle` 30 pixels big for the head\n6. This is where we will set the loop up, using the for function, the first number is where to start counting from and the second number is what to count to. So 1 .. 7 makes the computer count to 7 - **type** `for [1 .. 7]`\n7. Everything within this indent will be repeated 7 times, we need a move function to position each part of the body - **type** `move 50,0`\n8. We set the body color - **type** `color olivedrab`\n9. Once we add a circle it'll be clear what is going on with the code being repeated several times - **type** `circle 30`\n10. Let's add a square so the body looks more segmented, notice how we're now drawing 7 shapes for every line of code - **type** `square 30`\n11. We're going to add another detail to the tail so set the `color` to darkgreen\n12. Draw another circle, the code is still indented this will be repeated 7 times - **type** `circle 20`\n13. Finally draw a `square` 20 pixels big\n14. First make sure you are no longer in the for loop by hitting backspace so we're no longer indented. - **type** `moveTo 70,240`\n15. Change the `color` to black\n16. Draw an `ellipse` 10 pixels wide and 5 tall.\n17. Finally we'll give it a tongue so it can hiss like a snake should - **type** `color salmon`\n18. Position it on the face - **type** `move -15, 20`\n19. We'll draw a rectangle with a negative width to stretch it out from the mouth - **type** `rectangle -20,4`\n\n#### What you’ll hack\nThe beauty of the loop is you can change your code once, and every single shape you draw will update. This is another reason to use loops, you can make big changes without rewriting big amounts of code.\n\n#### Briefing\nHow many times have you written the same commands over and over again in the last few challenges? For this snake challenge, each part of the snake is made up of the same circle shape. Instead of writing the same circle code over and over, you can have the computer do it for you!\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/gradient.json b/lib/challenges/worlds/pixelhack/gradient.json
new file mode 100644
index 000000000..3ed86e9e3
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/gradient.json
@@ -0,0 +1,53 @@
+{
+ "id": "gradient",
+ "title": "8-bit sunset",
+ "description": "Use a loop to draw an 8-bit sunset.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We’ll just use solid shapes with no stroke. So **type** `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "To better see the effect of our test, text to bold with `bold true`",
+ "solution": "bold true"
+ },
+ {
+ "hint": "We’ll use a loop to draw the sunset. This time going from 0 to 10. **Type** `for y in [ 0 .. 10 ]`",
+ "solution": "for y in [ 0 .. 10 ]"
+ },
+ {
+ "hint": "To understand how this loop works, we’ll draw the loop value out. Move into position with `moveTo 250, y * 50`",
+ "solution": " moveTo 250, y * 50"
+ },
+ {
+ "hint": "Let’s inspect what our loop is doing. You can use the text function with plain text, but you can also pass it a variable. **Type** `text y` to see what the y values are across the screen.",
+ "solution": " text y",
+ "validate": " text y"
+ },
+ {
+ "hint": "The y variable is increasing every time we go through the loop. It goes from 0 (which is off the screen) to 10. To draw our sunset we need to just move to the left edge. **Type** `moveTo 0, y * 50`",
+ "solution": " moveTo 0, y * 50"
+ },
+ {
+ "hint": "We’ll use a special function to darken the color every loop. **Type** `color darken blue, y * 3`.",
+ "solution": " color darken blue, y * 3"
+ },
+ {
+ "hint": "Now for the sky, this will draw over your numbers. **Type** `rectangle 500, 50`.",
+ "solution": " rectangle 500, 50",
+ "validate": " rectangle 500, 50"
+ }
+ ],
+ "completion_text": "Beautiful! See how the blue gets darker as it goes down the screen. The y variable was being passed to the darken function. As the y value increased, the darken function made the blue color get darker.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "grad-yello.png",
+ "grad-sword.png",
+ "grad-mage.png"
+ ]
+ },
+ "cover": "pixel-gradient.png",
+ "guide": "#### New words\n**bold** state | bold true\n\nCan set whether text is drawn as bold or regular. True is bold, false is regular.\n\n**text** string | text “hello world”\n\nDraws text centered on the drawing cursor. The properties of the text are controlled by the color, font, bold, and italic functions.\n\n\n#### What you'll make\n1. We’ll just use solid shapes with no stroke. So **type** `stroke 0`.\n2. To better see the effect of our test, text to bold with `bold true`\n3. We’ll use a loop to draw the sunset. This time going from 0 to 10. **Type** `for y in [ 0 .. 10 ]`\n4. To understand how this loop works, we’ll draw the loop value out. Move into position with `moveTo 250, y * 50`\n5. Let’s inspect what our loop is doing. You can use the text function with plain text, but you can also pass it a variable. **Type** `text y` to see what the y values are across the screen.\n6. The y variable is increasing every time we go through the loop. It goes from 0 (which is off the screen) to 10. To draw our sunset we need to just move to the left edge. **Type** `moveTo 0, y * 50`\n7. We’ll use a special function to darken the color every loop. **Type** `color darken blue, y * 3`.\n8. Now for the sky, this will draw over your numbers. **Type** `rectangle 500, 50`.\n\n\n#### What you’ll hack\nNow that you’ve got a few pixel art creations under your belt, why not bring them in here and give them the full background treatment they deserve!\n\n#### Briefing\nWith a loop you can tell a computer to do something over and over again. With the `for` loop, you can ask how many times the loop has happened, and depending on what loop it is vary what it is the loop is doing.\n\nIn this sunset, we want to draw rectangular blocks of the same size, and we want to move down a certain distance every time, but this time we need a way of telling the computer that they will have a different color every time. How do we do this? With variables. The way you can ask how many times the loop has happened in a `for` loop is with the word you start the loop with. When we say `for y in [ 0 .. 10]`, we are making the word `y` equal to how many times the loop has executed. It is incremented for us, so any time we use the variable `y`, it will tell the program how many times it has looped.\n\nBecause it increments by one every time, we can use it to travel across color shades by making a color darker in step as our y variable gets bigger. So at the beginning of the loop we have a small y and a regular color, and at the end we have a big y and a dark color.\n\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/grassblock.json b/lib/challenges/worlds/pixelhack/grassblock.json
new file mode 100644
index 000000000..98830f060
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/grassblock.json
@@ -0,0 +1,60 @@
+{
+ "id": "grassblock",
+ "title": "8-bit Grass Block",
+ "description": "Make a block you can bring into Minecraft.",
+ "startAt": 1,
+ "steps": [
+ {
+ "hint": "We’ll just use solid shapes with no stroke. So **type** `stroke 0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "We’ll have to use two loops here. **Type** `for x in [0 ... 16]`",
+ "solution": "for x in [0 ... 16]"
+ },
+ {
+ "hint": "For y use `for y in [0 ... 16]`",
+ "solution": " for y in [0 ... 16]",
+ "validate": " for y in \\[0 ... 16\\]"
+ },
+ {
+ "hint": "We’ll use a randomly chosen shade of brown to make the dirt. **Type** `color darken brown, random 0, 25`",
+ "solution": " color darken brown, random 0, 25",
+ "validate": " color darken brown, random 0, 25"
+ },
+ {
+ "hint": "We’ll need to move into position for each square with `moveTo x * 31.25, y * 31.25`",
+ "solution": " moveTo x * 31.25, y * 31.25"
+ },
+ {
+ "hint": "Finally, draw the dirt square with `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ },
+ {
+ "hint": "The grass should only appear on the top of the block, so we’ll decide if the block should have a green bit drawn over it with `if 4 > y + random 0, 3`",
+ "solution": " if 4 > y + random 0, 3"
+ },
+ {
+ "hint": "Set the green color with the darken function. **Type** `color darken green, random 0, 25`",
+ "solution": " color darken green, random 0, 25",
+ "validate": " color darken green, random 0, 25"
+ },
+ {
+ "hint": "Finally, draw the square with `square 32`",
+ "solution": " square 32",
+ "validate": " square 32"
+ }
+ ],
+ "completion_text": "Nice! Now you can change the brown and green colors to get an interesting effect. Can you make a block that looks like a strawberry?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "grass-overgrown.png",
+ "grass-strawb.png",
+ "grass-myce.png"
+ ]
+ },
+ "cover": "pixel-grassblock.png",
+ "guide": "#### What you'll make\n1. We’ll just use solid shapes with no stroke. So **type** `stroke 0`.\n2. We’ll have to use two loops here. **Type** `for x in [0 ... 16]`\n3. For y use `for y in [0 ... 16]`\n4. We’ll use a randomly chosen shade of brown to make the dirt. **Type** `color darken brown, random 0, 25`\n5. We’ll need to move into position for each square with `moveTo x * 31.25, y * 31.25`\n6. Finally, draw the dirt square with `square 32`\n7. The grass should only appear on the top of the block, so we’ll decide if the block should have a green bit drawn over it with `if 4 > y + random 0, 3`\n8. Set the green color with the darken function. **Type** `color darken green, random 0, 25`\n9. Finally, draw the square with `square 32`\n\n#### What you’ll hack\nTo make this Minecraft Block your own all you need to change are two variables: the colour of the top and bottom parts. Make a strawberry, a water block, or anything else by just changing these two lines. For added complexity, make a Mycelium block with some randomness and another for loop. If you really want to have a challenge, try incorporating what you learned in the previous lesson and use the x and y values to add some extra color rotation to the dirt.\n\n#### Briefing \nOne of the most basic and commonly found blocks in minecraft is the grass block. Just sixteen blocks across and down, it is a very simple image, but we’re going to use randomness to give it a bit of texture. Two different layers will be used: one for the grass, and one for the dirt. \n\nIn each layer we will use the nested for loops to draw across and down the screen, and we’ll only change one thing about each: the color.\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/index.json b/lib/challenges/worlds/pixelhack/index.json
new file mode 100644
index 000000000..2552d1e17
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/index.json
@@ -0,0 +1,17 @@
+{
+ "challenges": [
+ "./pong",
+ "./ship",
+ "./tetris",
+ "./chest",
+ "./variables",
+ "./sword",
+ "./steve",
+ "./mage",
+ "./forloop",
+ "./gradient",
+ "./colorfrenzy",
+ "./grassblock",
+ "./diamondblock"
+ ]
+}
diff --git a/lib/challenges/worlds/pixelhack/mage.json b/lib/challenges/worlds/pixelhack/mage.json
new file mode 100644
index 000000000..4486614f5
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/mage.json
@@ -0,0 +1,208 @@
+{
+ "id": "mage",
+ "title": "RPG Mage",
+ "description": "Draw an 8-bit Aqua Mage",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We're going to make a Mage character for an RPG game so first we need to set the scene, we'll use the darken function to change the color teal to a more suitable shade of dark blue - **type** `background darken teal,15 `",
+ "solution": "background darken teal,15"
+ },
+ {
+ "hint": "We're making pixel art so we'll turn the stroke off - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Let's set the color to saddlebrown for her hair - **type** `color saddlebrown`",
+ "solution": "color saddlebrown"
+ },
+ {
+ "hint": "We'll use the moveTo function to move the cursor to a specific canvas coordinate - **type** `moveTo 120,80`",
+ "solution": "moveTo 120,80"
+ },
+ {
+ "hint": "We're going to draw the hair first so we can layer the rest of the character over it to create depth - **type** `rectangle 320,400`",
+ "solution": "rectangle 320,400"
+ },
+ {
+ "hint": "We're going to be drawing with blocks 20 by 20 pixels big - **type** `move 20,-20`",
+ "solution": "move 20,-20"
+ },
+ {
+ "hint": "Stack up another layer of hair - **type** `rectangle 280,20`",
+ "solution": "rectangle 280,20"
+ },
+ {
+ "hint": "Move upwards again to create a rounded effect - **type** `move 20,-20`",
+ "solution": "move 20,-20"
+ },
+ {
+ "hint": "Draw the final row of hair in - **type** `rectangle 240,20`",
+ "solution": "rectangle 240,20"
+ },
+ {
+ "hint": "Next we'll draw the face, - **type** `color sandybrown`",
+ "solution": "color sandybrown"
+ },
+ {
+ "hint": "Move the cursor to the top corner of where the face will be - **type** `move 0,120`",
+ "solution": "move 0,120"
+ },
+ {
+ "hint": "Draw the face with a `rectangle` that is 240 by 140",
+ "solution": "rectangle 240,140"
+ },
+ {
+ "hint": "The face is a little too square so move the cursor up to the forehead - **type** `move 80,-40`",
+ "solution": "move 80,-40"
+ },
+ {
+ "hint": "Draw another rectangle to create a fringe - **type** `rectangle 80,40`",
+ "solution": "rectangle 80,40"
+ },
+ {
+ "hint": "Move the cursor down the face - **type** `move -40,180`",
+ "solution": "move -40,180"
+ },
+ {
+ "hint": "Finish the face off by drawing a chin - **type** `rectangle 160,20`",
+ "solution": "rectangle 160,20"
+ },
+ {
+ "hint": "Next we'll draw some robes, our mage specialises in water magic so we'll make them blue - **type** `color paleturquoise`",
+ "solution": "color paleturquoise"
+ },
+ {
+ "hint": "Position the cursor for drawing the sleeves - **type** `move -100,20`",
+ "solution": "move -100,20"
+ },
+ {
+ "hint": "Draw a `rectangle` 360 wide and 80 tall for the robe sleeves",
+ "solution": "rectangle 360,80"
+ },
+ {
+ "hint": "Position the cursor to draw the body of the robe - **type** `move 60,80`",
+ "solution": "move 60,80"
+ },
+ {
+ "hint": "Draw a `rectangle` 240 wide and 100 tall for the robe body",
+ "solution": "rectangle 240,100"
+ },
+ {
+ "hint": "Next we'll draw a seam on the robe, change the `color` to teal",
+ "solution": "color teal"
+ },
+ {
+ "hint": "Move the cursor to the middle of the robe - **type** `move 120,-80`",
+ "solution": "move 120,-80"
+ },
+ {
+ "hint": "Draw a thin rectangle for the seam - **type** `rectangle 40,180`",
+ "solution": "rectangle 40,180"
+ },
+ {
+ "hint": "Next up we'll draw the arms - **type** `move 140,40`",
+ "solution": "move 140,40"
+ },
+ {
+ "hint": "Change the `color` back to sandybrown",
+ "solution": "color sandybrown"
+ },
+ {
+ "hint": "Draw the arm with a `rectangle` 40 by 140",
+ "solution": "rectangle 40,140"
+ },
+ {
+ "hint": "Move to the the other side of the body - **type** `move -380,40`",
+ "solution": "move -380,40"
+ },
+ {
+ "hint": "This arm will be disconnected at first but don't worry, we haven't finished yet - **type** `rectangle 40,100`",
+ "solution": "rectangle 40,100"
+ },
+ {
+ "hint": "Every mage needs a staff to cast magic, so we'll draw that next - **type** `color darkmagenta`",
+ "solution": "color darkmagenta"
+ },
+ {
+ "hint": "Move to the top of the staff - **type** `move 60,-120`",
+ "solution": "move 60,-120"
+ },
+ {
+ "hint": "Draw the staff over the arm so it looks like it's being held - **type** `rectangle -40,220`",
+ "solution": "rectangle -40,220"
+ },
+ {
+ "hint": "As we said at the start our character uses water magic so set the color to aquamarine - **type** `color aquamarine`",
+ "solution": "color aquamarine"
+ },
+ {
+ "hint": "Draw an orb at the top of the staff, if we use negative numbers then shapes are drawn backwards - **type** `square -80`",
+ "solution": "square -80"
+ },
+ {
+ "hint": "We're also going to draw a magic circlet, move to the forehead - **type** `move 60,-200`",
+ "solution": "move 60,-200"
+ },
+ {
+ "hint": "Draw a `rectangle` that is 240 by 20",
+ "solution": "rectangle 240,20"
+ },
+ {
+ "hint": "Shift down a row on our grid - **type** `move -20,20`",
+ "solution": "move -20,20"
+ },
+ {
+ "hint": "Finish off the circlet - **type** `rectangle 280,20`",
+ "solution": "rectangle 280,20"
+ },
+ {
+ "hint": "We just need to finish off the face so position the cursor over the eyes - **type** `move 60,60`",
+ "solution": "move 60,60"
+ },
+ {
+ "hint": "Set the `color` to black",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw the eyes long and thin - **type** `rectangle 40,80`",
+ "solution": "rectangle 40,80"
+ },
+ {
+ "hint": "Move across for the other eye - **type** `move 120,0`",
+ "solution": "move 120,0"
+ },
+ {
+ "hint": "Draw the other eye - **type** `rectangle 40,80`",
+ "solution": "rectangle 40,80"
+ },
+ {
+ "hint": "One last detail, we're going to make the eyes shiny - **type** `color white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a smaller rectangle within the pupil - **type** `rectangle 20,40`",
+ "solution": "rectangle 20,40"
+ },
+ {
+ "hint": "Move to the other eye - **type** `move -120,0`",
+ "solution": "move -120,0"
+ },
+ {
+ "hint": "Finish off with one last rectangle - **type** `rectangle 20,40`",
+ "solution": "rectangle 20,40"
+ }
+ ],
+ "completion_text": "Well done! Your mage is ready to raid some dungeons and cast some spells. Why not change the colours of the outfit to red for fire magic.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "mage-fire.png",
+ "mage-light.png",
+ "mage-warrior.png"
+ ]
+ },
+ "cover": "pixel-mage.png",
+ "guide": "#### What you'll make\n1. We're going to make a Mage character for an RPG game so first we need to set the scene, we'll use the darken function to change the color teal to a more suitable shade of dark blue - **type** `background darken teal,15 `\n2. We're making pixel art so we'll turn the stroke off - **type** `stroke 0`\n3. Let's set the color to saddlebrown for her hair - **type** `color saddlebrown`\n4. We'll use the moveTo function to move the cursor to a specific canvas coordinate - **type** `moveTo 120,80`\n5. We're going to draw the hair first so we can layer the rest of the character over it to create depth - **type** `rectangle 320,400`\n6. We're going to be drawing with blocks 20 by 20 pixels big - **type** `move 20,-20`\n7. Stack up another layer of hair - **type** `rectangle 280,20`\n8. Move upwards again to create a rounded effect - **type** `move 20,-20`\n9. Draw the final row of hair in - **type** `rectangle 240,20`\n10. Next we'll draw the face, - **type** `color sandybrown`\n11. Move the cursor to the top corner of where the face will be - **type** `move 0,120`\n12. Draw the face with a `rectangle` that is 240 by 140\n13. The face is a little too square so move the cursor up to the forehead - **type** `move 80,-40`\n14. Draw another rectangle to create a fringe - **type** `rectangle 80,40`\n15. Move the cursor down the face - **type** `move -40,180`\n16. Finish the face off by drawing a chin - **type** `rectangle 160,20`\n17. Next we'll draw some robes, our mage specialises in water magic so we'll make them blue - **type** `color paleturquoise`\n18. Position the cursor for drawing the sleeves - **type** `move -100,20`\n19. Draw a `rectangle` 360 wide and 80 tall for the robe sleeves\n20. Position the cursor to draw the body of the robe - **type** `move 60,80`\n21. Draw a `rectangle` 240 wide and 100 tall for the robe body\n22. Next we'll draw a seam on the robe, change the `color` to teal\n23. Move the cursor to the middle of the robe - **type** `move 120,-80`\n24. Draw a thin rectangle for the seam - **type** `rectangle 40,180`\n25. Next up we'll draw the arms - **type** `move 140,40`\n26. Change the `color` back to sandybrown\n27. Draw the arm with a `rectangle` 40 by 140\n28. Move to the the other side of the body - **type** `move -380,40`\n29. This arm will be disconnected at first but don't worry, we haven't finished yet - **type** `rectangle 40,100`\n30. Every mage needs a staff to cast magic, so we'll draw that next - **type** `color darkmagenta`\n31. Move to the top of the staff - **type** `move 60,-120`\n32. Draw the staff over the arm so it looks like it's being held - **type** `rectangle -40,220`\n33. As we said at the start our character uses water magic so set the color to aquamarine - **type** `color aquamarine`\n34. Draw an orb at the top of the staff, if we use negative numbers then shapes are drawn backwards - **type** `square -80`\n35. We're also going to draw a magic circlet, move to the forehead - **type** `move 60,-200`\n36. Draw a `rectangle` that is 240 by 20\n37. Shift down a row on our grid - **type** `move -20,20`\n38. Finish off the circlet - **type** `rectangle 280,20`\n39. We just need to finish off the face so position the cursor over the eyes - **type** `move 60,60`\n40. Set the `color` to black\n41. Draw the eyes long and thin - **type** `rectangle 40,80`\n42. Move across for the other eye - **type** `move 120,0`\n43. Draw the other eye - **type** `rectangle 40,80`\n44. One last detail, we're going to make the eyes shiny - **type** `color white`\n45. Draw a smaller rectangle within the pupil - **type** `rectangle 20,40`\n46. Move to the other eye - **type** `move -120,0`\n47. Finish off with one last rectangle - **type** `rectangle 20,40`\n\n#### What you’ll hack\nThis is a much more intricate drawing, so there are many more things to change. You can go through the colours making up the image and change them to a new palette, and add in new objects. Take a look at the warrior and the lightning mage as ways of powering up your warrior with new powers, armour and weapons.\n\n#### Briefing\nThe simple shapes you have been putting together into simple designs can be refined and brought into more complex creations. This RPG Mage is a much more intricate design, but uses just the same tools to create them.\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/pong.json b/lib/challenges/worlds/pixelhack/pong.json
new file mode 100644
index 000000000..e69fbd42e
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/pong.json
@@ -0,0 +1,40 @@
+{
+ "id": "pong",
+ "title": "Pong",
+ "description": "Code a Pong match",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Pong (1972) was one of the first home game consoles. It is an incredibly basic table tennis game. And what do we need to play table tennis? First, a ball. **type** `circle 15`",
+ "solution": "circle 15"
+ },
+ {
+ "hint": "Next we need to draw some paddles. We use the moveTo function to position objects. The first number after MoveTo tells you where the object goes the object goes horizontally (left and right). The second number tells you where the object goes vertically (up and down). **type** `moveTo 30,100`",
+ "solution": "moveTo 30,100"
+ },
+ {
+ "hint": "We'll draw the paddles using the rectangle function, for the first paddle **type** `rectangle 20, 100`",
+ "solution": "rectangle 20, 100"
+ },
+ {
+ "hint": "It's no fun to play Pong alone so we need an opponent **type** `moveTo 450,300`",
+ "solution": "moveTo 450,300"
+ },
+ {
+ "hint": "Finally, let’s draw the opponent's paddle, the first number is the width and the second the height. **type** `rectangle 20, 100`",
+ "solution": "rectangle 20, 100"
+ }
+ ],
+ "completion_text": "Well done! You've recreated one of the first videogames, pretty boring to look at right? Why not try clicking on the 100 after the first moveTo command and using the slider to reposition the paddle.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "pong-white.png",
+ "pong-movecolor.png",
+ "pong-movepaddles.png"
+ ]
+ },
+ "cover": "pixel-pong.png",
+ "guide": "#### New words\n**circle** radius | circle 15\n\nDraws a circle at the current position. The bigger the radius number, the bigger the circle: radius is the distance from the center of a circle to its edge.\n\n**moveTo** x, y | moveTo 30, 100\n\nMoves the drawing position to the new (x, y) place. The first value is the x value, which controls how far across horizontally the x part of the coordinate should go. The second value is the y coordinate, which controls how far down vertically the y coordinate should go.\n\n**rectangle** width, height | rectangle 20, 100\n\nDraws a rectangle at the current position. The width and height independently control how wide and tall the rectangle is.\n\n**color** name | color red\n\nChanges the drawing color. All following shapes and text will be drawn with the fill color set to this color until you change it again. To set the drawing color to transparent, type `color null`\n\n**background** colorName | background white\n\nChanges the background color.\n\n#### What you'll make\n1. Pong (1972) was one of the first home game consoles, being an incredibly basic table tennis game the first thing we need is a ball. **Type** `circle 15`\n2. Next we need to draw some paddles, use the moveTo function to position them. The first number is the horizontal position and the second number the vertical. **Type** `moveTo 30, 100`\n3. We'll draw the paddles using the rectangle function, for the first paddle. **Type** `rectangle 20, 100`\n4. It's no fun to play Pong alone so we need an opponent. **Type** `moveTo 450, 300`\n5. Finally, let’s draw the opponent's paddle, the first number is the width and the second the height. **Type** `rectangle 20, 100`\n\n#### What you’ll hack\nThere is one other essential tool every coding artist is going to need to know: color. The default starting color is black, and every time a shape is drawn in Make Art the drawing tool checks to see what color should be drawn. So because you haven’t specified a color yet, it will draw everything black.\n\nYou can change the color to red, for instance, by typing color red after the first `moveTo` command and before `rectangle 20, 100`to change the color of both paddles. Note: this will only change any shapes drawn after this command, so typing it at the bottom of your code won’t do anything. \n\nThe background color can be changed with background charcoal. You can put this anywhere in your code, and for most creations you’ll only call it once. It is best practice to put it at the top of the your code.\n\nFinally, you can move your paddles up and down by changing the second number next to each moveTo function. You can play with them by clicking on the number and using the slider to change the number.\n\n#### Briefing\nIn 1972, Al Alcorn got handed a \"warm-up exercise\" for his new job at game company Atari, and came up with Pong: two paddles, one ball, and soon after, lines of people around the block to play it. Pong went on to become an international sensation, with millions of games sold. It was so famous, in fact that dozens of knock-offs were made by competitors who tried to ride the coattails of Atari’s success. What made the game so simple and universal was what made it so easy to copy. \n\nYour first task as a hacker is to make your very own Pong game. One of the secrets to being a good hacker is remixing: taking ideas and code from other places and putting them together into something new. You can take the original idea of Pong and then hack it together into your own game! You’ll set up a basic screen with paddles and balls. Then it is up to you to hack it into something new! \n"
+}
diff --git a/lib/challenges/worlds/pixelhack/ship.json b/lib/challenges/worlds/pixelhack/ship.json
new file mode 100644
index 000000000..3e74d8152
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/ship.json
@@ -0,0 +1,68 @@
+{
+ "id": "ship",
+ "title": "Asteroids Ship",
+ "description": "Code a vector spaceship using lines",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Asteroids (1979) was one of the first major arcade games. It is set in space, so we're going to need to set the scene. **type** `background black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "The graphics in Asteroids were simple line based graphics called vectors, we still use a more complex version of this concept in computer graphics today. First we need to set our line color and width **type** `stroke white,5`",
+ "solution": "stroke white,5"
+ },
+ {
+ "hint": "Next we use the move function to set the starting point for our first line. **type** `move 0, -70`",
+ "solution": "move 0, -70"
+ },
+ {
+ "hint": "'Function' is the word we use to define what we are drawing. Let’s draw a line that starts where we just moved the cursor and ends at the bottom left. We type where we want the line to end after the function, so **type** `line -50,150` Try changing the Y value from 150 to -150 watch what happens. The line goes up, above where we started at -50. Let’s move it back down to 150!",
+ "solution": "line -50,150"
+ },
+ {
+ "hint": "To move our cursor to the start of the next line we feed the previous coordinates into the move function. **type** `move -50,150`",
+ "solution": "move -50,150"
+ },
+ {
+ "hint": "We draw a line inwards and upwards to form the thruster of the ship - **type** `line 50,-25`",
+ "solution": "line 50,-25"
+ },
+ {
+ "hint": "Again moving to the same coordinates for a continuous line **type** `move 50,-25`",
+ "solution": "move 50,-25"
+ },
+ {
+ "hint": "We replicate the shape for the right side of the ship by reversing the second coordinate to draw downwards instead of up **type** `line 50,25`",
+ "solution": "line 50,25"
+ },
+ {
+ "hint": "Follow through with the line again **type** `move 50,25`",
+ "solution": "move 50,25"
+ },
+ {
+ "hint": "Our ship starts to take shape. To make a line that goes from the end of the one we just drew to connect back up at the top **type** `line -50,-150`",
+ "solution": "line -50,-150"
+ },
+ {
+ "hint": "Finally we move back to our starting point to add a final touch **type** `move -50,-150`",
+ "solution": "move -50,-150"
+ },
+ {
+ "hint": "We draw a line down the centre to help the simple shape look more like a ship, early videogames had only a fraction of computing power we are used to today, so graphics had to be basic. **type** `line 0,125`",
+ "solution": "line 0,125"
+ }
+ ],
+ "completion_text": "It might not look like much but simple graphics like this were where videogames we know today began and games like this lay the foundations for the 3D graphics we're used to today.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "ship-golden.png",
+ "ship-thrust.png",
+ "ship-sleek.png"
+ ]
+ },
+ "cover": "pixel-ship.png",
+ "guide": "#### New words\n**move** x, y | move 50, 25\n\nMoves the drawing cursor from the current position. The difference from moveTo is that this is relative, not absolute. `move 50, 25` will move the cursor 50 to the right, and 25 down from the current position, as opposed to from the top-left corner. The first value is the x value, which controls how far across horizontally the x part of the coordinate should go. The second value is the y coordinate, which controls how far down vertically the y coordinate should go. Negative values will send it in the opposite direction.\n\n**line** x, y | line 50, 25\n\nDraws a line from the current position to a relative position. The x, y coordinates control the point relative to the cursor where the line will be drawn to.\n\n**stroke** color, width | stroke white, 5\n\nSets the drawing stroke. All following shapes and text will be drawn with the stroke color and width set to this until you change it again. This function is smart and can accept arguments in any order, or only one at a time.\n\n#### What you'll make\n1. Asteroids (1979) was one of the first major arcade games. It was set in space so we're going to need to set the scene **type** `background black`\n2. The graphics in Asteroids were simple line based graphics called vectors, we still use a more complex version of this concept in computer graphics today. First we need to set our line color and width **type** `stroke white,5`\n3. Next we use the move function to set the starting point for our first line. **type** `move 0, -70`\n4. We use the line function to draw a line relative to our starting position, we use a negative number first to move to the left and a positive number second to move downward **type** `line -50,150`\n5. To move our cursor to the start of the next line we feed the previous coordinates into the move function. **type** `move -50,150`\n6. We draw a line inwards and upwards to form the thruster of the ship **type** `line 50,-25`\n7. Again moving to the same coordinates for a continuous line **type** `move 50,-25`\n8. We replicate the shape for the right side of the ship by reversing the second coordinate to draw downwards instead of up **type** `line 50,25`\n9. Follow through with the line again **type** `move 50,25`\n10. Our ship starts to take shape **type** `line -50,-150`\n11. Finally we move back to our starting point to add a final touch **type** `move -50,-150`\n12. We draw a line down the centre to help the simple shape look more like a ship, early videogames had only a fraction of computing power we are used to today, so graphics had to be basic. **type** `line 0,125`\n\n#### What you’ll hack\nIt might not look like much but graphics like this are where the videogames we know today began. Games like this lay the foundations for the 3D graphics we're used to today. You can turn your spaceship into a variety of different things by customising the stroke color and width. \n\nFor something more advanced, try drawing more lines.\n\n#### Briefing\nAsteroids (1979) was one of the first major arcade games. Limited by primitive drawing hardware, the game designers could only draw using simple line based graphics called vectors, we still use a more complex version of this concept in computer graphics today.\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/steve.json b/lib/challenges/worlds/pixelhack/steve.json
new file mode 100644
index 000000000..ed0339cc0
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/steve.json
@@ -0,0 +1,127 @@
+{
+ "id": "steve",
+ "title": "Steve",
+ "description": "Make Steve's face with simple shapes.",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We are going to just be using solid squares and rectangles, so lets turn our stroke off with `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the background to lightbrown",
+ "solution": "background lightbrown"
+ },
+ {
+ "hint": "Set the drawing color for the hair with `color brown`.",
+ "solution": "color brown"
+ },
+ {
+ "hint": "The hair starts in the top-left corner, so **type** `moveTo 0, 0`.",
+ "solution": "moveTo 0, 0"
+ },
+ {
+ "hint": "We’ll draw a rectangle, which takes a width and height parameter. **Type** `rectangle 500, 150`",
+ "solution": "rectangle 500, 150"
+ },
+ {
+ "hint": "Set the drawing color to lightbrown**type** `color lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Move into position for the forehead, **type** `moveTo 100, 100`",
+ "solution": "moveTo 100, 100"
+ },
+ {
+ "hint": "Now to draw the forehead, **type** `rectangle 300, 50`",
+ "solution": "rectangle 300, 50"
+ },
+ {
+ "hint": "For the eyes, set the color to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Now let’s move with `moveTo 100, 200`",
+ "solution": "moveTo 100, 200"
+ },
+ {
+ "hint": "Draw the eye with `square 50`",
+ "solution": "square 50"
+ },
+ {
+ "hint": "For the other eye, **type** `moveTo 350, 200`",
+ "solution": "moveTo 350, 200"
+ },
+ {
+ "hint": "And another square of size 50",
+ "solution": "square 50"
+ },
+ {
+ "hint": "For the iris we’ll use the color purple.",
+ "solution": "color purple"
+ },
+ {
+ "hint": "Move to `150, 200`.",
+ "solution": "moveTo 150, 200"
+ },
+ {
+ "hint": "Draw a square of size 50.",
+ "solution": "square 50"
+ },
+ {
+ "hint": "Move to `300, 200`.",
+ "solution": "moveTo 300, 200"
+ },
+ {
+ "hint": "And draw the final iris with a square of size 50.",
+ "solution": "square 50"
+ },
+ {
+ "hint": "We’ll draw the nose next. **Type** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Move into position with `moveTo 200, 250`.",
+ "solution": "moveTo 200, 250"
+ },
+ {
+ "hint": "Draw out a rectangle of width `100` and height `50`.",
+ "solution": "rectangle 100, 50"
+ },
+ {
+ "hint": "For the mouth set the drawing color to `saddlebrown`.",
+ "solution": "color saddlebrown"
+ },
+ {
+ "hint": "Move to `150, 300`",
+ "solution": "moveTo 150, 300"
+ },
+ {
+ "hint": "The mouth should be wide rectangle. **Type** `200, 100`.",
+ "solution": "rectangle 200, 100"
+ },
+ {
+ "hint": "To make the mouth just right we need to cut out a bit of the top. Set the drawing color to the same color as the skin. **Type** `color lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Move to `200, 300`.",
+ "solution": "moveTo 200, 300"
+ },
+ {
+ "hint": "Finish up with `rectangle 100, 50` ",
+ "solution": "rectangle 100, 50"
+ }
+ ],
+ "completion_text": "Well done! Our character is looking good. What parts can you change to make the character look more like you?",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "Steve-blue.png",
+ "Steve-beard.png",
+ "Steve-Alex.png"
+ ]
+ },
+ "cover": "pixel-steve.png",
+ "guide": "#### What you'll make\n1. We are going to just be using solid squares and rectangles, so lets turn our stroke off with `stroke 0`\n2. Set the background to lightbrown\n3. Set the drawing color for the hair with `color brown`.\n4. The hair starts in the top-left corner, so **type** `moveTo 0, 0`.\n5. We’ll draw a rectangle, which takes a width and height parameter. **Type** `rectangle 500, 150`\n6. Set the drawing color to lightbrown**type** `color lightbrown`\n7. Move into position for the forehead, **type** `moveTo 100, 100`\n8. Now to draw the forehead, **type** `rectangle 300, 50`\n9. For the eyes, set the color to white\n10. Now let’s move with `moveTo 100, 200`\n11. Draw the eye with `square 50`\n12. For the other eye, **type** `moveTo 350, 200`\n13. And another square of size 50\n14. For the iris we’ll use the color purple.\n15. Move to `150, 200`.\n16. Draw a square of size 50.\n17. Move to `300, 200`.\n18. And draw the final iris with a square of size 50.\n19. We’ll draw the nose next. **Type** `color brown`\n20. Move into position with `moveTo 200, 250`.\n21. Draw out a rectangle of width `100` and height `50`.\n22. For the mouth set the drawing color to `saddlebrown`.\n23. Move to `150, 300`\n24. The mouth should be wide rectangle. **Type** `200, 100`.\n25. To make the mouth just right we need to cut out a bit of the top. Set the drawing color to the same color as the skin. **Type** `color lightbrown`\n26. Move to `200, 300`.\n27. Finish up with `rectangle 100, 50` \n\n#### What you’ll hack\nThe shapes here are simple, but that is what makes it so easy to remix and hack! Change the colors of the skin, eyes, or hair, or add in your own facial features to win!\n\n#### Briefing\nSteve is the hero of Minecraft. Not very much is known about Steve: who he is, where he came from, and whether he is even human. But he is the face of the most sensational game and building system of the decade. This simple composition with just squares and rectangles is brought to life with beautiful colors.\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/sword.json b/lib/challenges/worlds/pixelhack/sword.json
new file mode 100644
index 000000000..bc5491c80
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/sword.json
@@ -0,0 +1,96 @@
+{
+ "id": "sword",
+ "title": "Diamond Sword",
+ "description": "Code a sword forged from pure diamonds",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "We're going to make a sword out of diamond so first we will set up some variables to represent the colors **type** `sword = aquamarine`",
+ "solution": "sword = aquamarine"
+ },
+ {
+ "hint": "We set up enough variable to represent the darker areas of the sword, we can use the darken function to alter the color of our existing sword color. **type** `darksword = darken sword,40`",
+ "solution": "darksword = darken sword,40"
+ },
+ {
+ "hint": "We start by setting the `background` color to slategray",
+ "solution": "background slategray"
+ },
+ {
+ "hint": "Next we set our fill color to the variable we set up before **type** `color sword`",
+ "solution": "color sword"
+ },
+ {
+ "hint": "and set our stroke color to the darker version of our sword color **type** `stroke darksword,20`",
+ "solution": "stroke darksword,20"
+ },
+ {
+ "hint": "Move the cursor to where the top of the blade will be **type** `move -20,-180`",
+ "solution": "move -20,-180"
+ },
+ {
+ "hint": "Draw the blade of the sword **type** `rectangle 40,230`",
+ "solution": "rectangle 40,230"
+ },
+ {
+ "hint": "It's already starting to take shape, let's create the handle **type** `move 10,250`",
+ "solution": "move 10,250"
+ },
+ {
+ "hint": "The grip isn't going to be made of diamond so set the `stroke` color to darkbrown",
+ "solution": "stroke darkbrown"
+ },
+ {
+ "hint": "and the `color` to brown",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw a `rectangle` 20 by 100 for the grip",
+ "solution": "rectangle 20,100"
+ },
+ {
+ "hint": "We're going to create the crossguard next **type** `move -70,-10`",
+ "solution": "move -70,-10"
+ },
+ {
+ "hint": "Change the `stroke` color back to our darksword variable",
+ "solution": "stroke darksword"
+ },
+ {
+ "hint": "We're going to use another color function: lighten to slightly change our diamond color **type** `color lighten darksword,10`",
+ "solution": "color lighten darksword,10"
+ },
+ {
+ "hint": "Next draw a `rectangle` 160 by 20",
+ "solution": "rectangle 160,20"
+ },
+ {
+ "hint": "Notice how the lighten function altered the diamond color. **type** `move 80, -240`",
+ "solution": "move 80, -240"
+ },
+ {
+ "hint": "Finally we're going to add a highlight detail to finish the sword **type** `color white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Turn the stroke off **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Draw a `rectangle` 10 by 230",
+ "solution": "rectangle 10, 230"
+ }
+ ],
+ "completion_text": "That sword looks exquisite, perfect for fighting your way out of some caves. Try changing the color on the first line, see how it changes all the other colors on the blade because of the variables.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "sword-golden.png",
+ "sword-shovel.png",
+ "sword-pickaxe.png"
+ ]
+ },
+ "cover": "pixel-sword.png",
+ "guide": "#### New words\n**darken**(color, amount) | darken(blue, 40)\n\nReturns a lightened color by the amount you give it. The amount can be between 0 and 100.\n\n**lighten**(color, amount) | lighten(blue, 40)\n\nReturns a lightened color by the amount you give it. The amount can be between 0 and 100.\n\n\n#### What you'll make\n1. We're going to make a sword out of diamond so first we will set up some variables to represent the colors **type** `sword = aquamarine`\n2. We set up enough variable to represent the darker areas of the sword, we can use the darken function to alter the color of our existing sword color. **type** `darksword = darken sword,40`\n3. We start by setting the `background` color to slategray\n4. Next we set our fill color to the variable we set up before **type** `color sword`\n5. and set our stroke color to the darker version of our sword color **type** `stroke darksword,20`\n6. Move the cursor to where the top of the blade will be **type** `move -20,-180`\n7. Draw the blade of the sword **type** `rectangle 40,230`\n8. It's already starting to take shape, let's create the handle **type** `move 10,250`\n9. The grip isn't going to be made of diamond so set the `stroke` color to darkbrown\n10. and the `color` to brown\n11. Draw a `rectangle` 20 by 100 for the grip\n12. We're going to create the crossguard next **type** `move -70,-10`\n13. Change the `stroke` color back to our darksword variable\n14. We're going to use another color function: lighten to slightly change our diamond color **type** `color lighten darksword,10`\n15. Next draw a `rectangle` 160 by 20\n16. Notice how the lighten function altered the diamond color. **type** `move 80, -240`\n17. Finally we're going to add a highlight detail to finish the sword **type** `color white`\n18. Turn the stroke off **type** `stroke 0`\n19. Draw a `rectangle` 10 by 230\n\n#### What you’ll hack\nBecause all of the colors we used are just shades of our sword color, by changing one variable all the darker and lighter shades will change as well! We can make a diamond sword into a gold one by just changing the sword color.\n\nUsing the same code and style, what other Minecraft-inspired creations can you make?\n\n#### Briefing\nThe diamond sword is the most powerful weapon in the Minecraft players toolbelt. It is also the most difficult to make given the rarity of diamond. All of the different types of swords in Minecraft (diamond, gold, steel, stone…) follow a simple pattern in their design. The only thing that separates them visually is their color. For the diamond sword we’ll make a word called \"sword\" which is set to the color \"aquamarine\". To select different shades for the hilt and outlines, we’ll use the color with special color functions."
+}
diff --git a/lib/challenges/worlds/pixelhack/tetris.json b/lib/challenges/worlds/pixelhack/tetris.json
new file mode 100644
index 000000000..bb9cfbc9f
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/tetris.json
@@ -0,0 +1,96 @@
+{
+ "id": "tetris",
+ "title": "Tetris",
+ "description": "Stack some Tetris blocks with code",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "Tetris (1984) was the first videogame to be exported from the USSR to the US, going on to sell 170 million copies. We're going to make some Tetris blocks, first turn the stroke off **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Next move your cursor to the start of the first block **type** `moveTo 150,200`",
+ "solution": "moveTo 150,200"
+ },
+ {
+ "hint": "We're going to need some colors to tell the blocks apart, change the fill color of your shapes with the color function **type** `color crimson`",
+ "solution": "color crimson"
+ },
+ {
+ "hint": "First we're going to draw an L shaped block **type** `rectangle 50,150`",
+ "solution": "rectangle 50,150"
+ },
+ {
+ "hint": "Our blocks are all going to be to a 50x50 grid, so you'll notice all our draw and move commands are multiples of 50 **type** `move 0,100`",
+ "solution": "move 0,100"
+ },
+ {
+ "hint": "Finish off the block with a 100,50 `rectangle`",
+ "solution": "rectangle 100,50"
+ },
+ {
+ "hint": "Let's move to start the next block **type** `move 50,-100`",
+ "solution": "move 50,-100"
+ },
+ {
+ "hint": "We'll change the color to tell them apart, color functions affect all shapes drawn below them in the code **type** `color seagreen`",
+ "solution": "color seagreen"
+ },
+ {
+ "hint": "Next we'll be drawing the S shaped block **type** `rectangle 50,100`",
+ "solution": "rectangle 50,100"
+ },
+ {
+ "hint": "Move one gridspace across and one down **type** `move 50,50`",
+ "solution": "move 50,50"
+ },
+ {
+ "hint": "To make an S shape we create another `rectangle` of 50 by 100",
+ "solution": "rectangle 50,100"
+ },
+ {
+ "hint": "On to the next block **type** `move 50,-100`",
+ "solution": "move 50,-100"
+ },
+ {
+ "hint": "We change the color again **type** `color indigo`",
+ "solution": "color indigo"
+ },
+ {
+ "hint": "Next we will draw a long and thin block, draw a `rectangle` 50 by 200",
+ "solution": "rectangle 50,200"
+ },
+ {
+ "hint": "One final block to complete the square **type** `move -150,0`",
+ "solution": "move -150,0"
+ },
+ {
+ "hint": "Let's make the `color` gold to stand out",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Another L block will fit nicely so draw the stem with **type** `rectangle 150,50`",
+ "solution": "rectangle 150,50"
+ },
+ {
+ "hint": "Move into place to draw the foot **type** `move 100,0`",
+ "solution": "move 100,0"
+ },
+ {
+ "hint": "Finally fill in the foot **type** `rectangle 50,100`",
+ "solution": "rectangle 50,100"
+ }
+ ],
+ "completion_text": "Well done you created a nice stack of tetrominoes, why not try changing the color of some of the blocks or drawing a few more to fill the screen.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "tetris-gameboy.png",
+ "tetris-3D.png",
+ "tetris-row.png"
+ ]
+ },
+ "cover": "pixel-tetris.png",
+ "guide": "#### New words\n**color** name | color red\n\nChanges the drawing color. All following shapes and text will be drawn with the fill color set to this color until you change it again. To set the drawing color to transparent, type `color null`\n\n#### What you'll make\n1. Tetris (1984) was the first videogame to be exported from the USSR to the US, going on to sell 170 million copies. We're going to make some Tetris blocks, first turn the stroke off **type** `stroke 0`\n2. Next move your cursor to the start of the first block **type** `moveTo 150,200`\n3. We're going to need some colors to tell the blocks apart, change the fill color of your shapes with the color function **type** `color crimson`\n4. First we're going to draw an L shaped block **type** `rectangle 50,150`\n5. Our blocks are all going to be to a 50x50 grid, so you'll notice all our draw and move commands are multiples of 50 **type** `move 0,100`\n6. Finish off the block with a 100,50 `rectangle`\n7. Let's move to start the next block **type** `move 50,-100`\n8. We'll change the color to tell them apart, color functions affect all shapes drawn below them in the code **type** `color seagreen`\n9. Next we'll be drawing the S shaped block **type** `rectangle 50,100`\n10. Move one gridspace across and one down **type** `move 50,50`\n11. To make an S shape we create another `rectangle` of 50 by 100\n12. On to the next block **type** `move 50,-100`\n13. We change the color again **type** `color indigo`\n14. Next we will draw a long and thin block, draw a `rectangle` 50 by 200\n15. One final block to complete the square **type** `move -150,0`\n16. Let's make the `color` gold to stand out\n17. Another L block will fit nicely so draw the stem with **type** `rectangle 150,50`\n18. Move into place to draw the foot **type** `move 100,0`\n19. Finally fill in the foot **type** `rectangle 50,100`\n\n#### What you’ll hack\nNow you’ve made a cube, give each tetrimino whatever color you want, and add in more.\n\n#### Briefing \nTetris (1984) was the first videogame to be exported from the USSR to the US, going on to sell 170 million copies. We're going to make some Tetris blocks, which are known as tetriminos. Each tetrimino is made up of four squares that connect together, and fit together in interesting ways. In the game, tetriminos fall down endlessly and the goal is to make as many of them fit together without empty spaces.\n"
+}
diff --git a/lib/challenges/worlds/pixelhack/variables.json b/lib/challenges/worlds/pixelhack/variables.json
new file mode 100644
index 000000000..8ff8ef2bb
--- /dev/null
+++ b/lib/challenges/worlds/pixelhack/variables.json
@@ -0,0 +1,92 @@
+{
+ "id": "variables",
+ "title": "Variables",
+ "description": "Use variables to create a maze dwelling ghost",
+ "code": "",
+ "startAt": 2,
+ "steps": [
+ {
+ "hint": "This classic ghost character first appeared in Pac-Man (1980), because it's a ghost we're going to need a spooky background **type** `background navy`",
+ "solution": "background navy"
+ },
+ {
+ "hint": "Next turn the stroke off by typing `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the `color` to red",
+ "solution": "color red"
+ },
+ {
+ "hint": "Here we're introducing something new, we're going to set the size of the ghost using a variable, variables are a way of repeating the same value in your code **type** `ghostsize = 100`",
+ "solution": "ghostsize = 100"
+ },
+ {
+ "hint": "Move the cursor up to where the ghosts head will start **type** `move 0,-50`",
+ "solution": "move 0,-50"
+ },
+ {
+ "hint": "We're going to use that variable we set earlier to draw the head, because we set the variable to 100 this command will create a circle 100 pixels big **type** `circle ghostsize`",
+ "solution": "circle ghostsize"
+ },
+ {
+ "hint": "We're going to use the variable again, this time with the move command, by placing a minus symbol in front of it this command becomes the same as moving -100,0 **type** `move -ghostsize,0`",
+ "solution": "move -ghostsize,0"
+ },
+ {
+ "hint": "Time to draw the rest of the ghost's body, because the body is relative to the head size we will use the variable again **type** `square ghostsize * 2`",
+ "solution": "square ghostsize * 2"
+ },
+ {
+ "hint": "We're going to give it some eyes so we need to use the variable again to make sure they're positioned correctly on the head **type** `move ghostsize / 2`",
+ "solution": "move ghostsize / 2"
+ },
+ {
+ "hint": "Set the `color` to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Start the eyes off with a `circle` 20 pixels big",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Now set the iris color **type** `color blue`",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Finally draw the iris **type** `circle 10`",
+ "solution": "circle 10"
+ },
+ {
+ "hint": "We're gonna use the variable one last time to position the other eye correctly **type** `move ghostsize,0`",
+ "solution": "move ghostsize,0"
+ },
+ {
+ "hint": "Repeat the process for the second eye, set the `color` to white",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw a `circle` 20 pixels big",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "Set the `color` to blue",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Finally draw a `circle` 10 pixels big",
+ "solution": "circle 10"
+ }
+ ],
+ "completion_text": "Nice! Now because you have used a variable, you can click on the ghostsize number and use the slider to change the value. Notice how the eyes stay the same size because they are hardcoded numbers and everything else scales together.",
+ "gallery": {
+ "cover_path": "/assets/challenges/images/pixelremixes/",
+ "remixes": [
+ "ghost-big.png",
+ "ghost-tired.png",
+ "ghost-scared.png"
+ ]
+ },
+ "cover": "pixel-variables.png",
+ "guide": "#### New words\n**variable** = value | ghostsize = 100\n\nCreates a word that is assigned a value and can be used elsewhere.\n\n#### What you'll make\n1. This classic ghost character first appeared in Pac-Man (1980), because it's a ghost we're going to need a spooky background **type** `background navy`\n2. Next turn the stroke off by typing `stroke 0`\n3. Set the `color` to red\n4. Here we're introducing something new, we're going to set the size of the ghost using a variable, variables are a way of repeating the same value in your code **type** `ghostsize = 100`\n5. Move the cursor up to where the ghosts head will start **type** `move 0,-50`\n6. We're going to use that variable we set earlier to draw the head, because we set the variable to 100 this command will create a circle 100 pixels big **type** `circle ghostsize`\n7. We're going to use the variable again, this time with the move command, by placing a minus symbol in front of it this command becomes the same as moving -100,0 **type** `move -ghostsize,0`\n8. Time to draw the rest of the ghost's body, because the body is relative to the head size we will use the variable again **type** `square ghostsize * 2`\n9. We're going to give it some eyes so we need to use the variable again to make sure they're positioned correctly on the head **type** `move ghostsize / 2`\n10. Set the `color` to white\n11. Start the eyes off with a `circle` 20 pixels big\n12. Now set the iris color **type** `color blue`\n13. Finally draw the iris **type** `circle 10`\n14. We're gonna use the variable one last time to position the other eye correctly **type** `move ghostsize,0`\n15. Repeat the process for the second eye, set the `color` to white\n16. Draw a `circle` 20 pixels big\n17. Set the `color` to blue\n18. Finally draw a `circle` 10 pixels big\n\n#### What you’ll hack\nBecause you used a variable for all the different versions of the ghost’s body, you can change it once to see how the ghost’s body resizes! If you notice, the eyes position change with the ghostsize variable, but the eyes’ size don’t. Could you set up another variable that controls the size of the eyes?\n\n#### Briefing\nWhen you write a computer program you are telling a computer to do a series of tasks. And when you run the program, the computer reads all of the tasks and does them. So far we have written programs that will run the same way every single time you do the program, but what if we want the program to be different every time we run the program? If we wanted to vary the way in which the program works every time it was run, we need to use variables.\n\nA variable is a word that takes on the meaning of something else. And when you use it somewhere else it will pass on the word it means. In this challenge you will base the size of many of the ghost’s parts off of a variable. Changing the variable with a slider will then affect all of the different individual shapes sizes, giving you the power to make the whole ghost bigger or smaller at once!\n"
+}
diff --git a/lib/challenges/worlds/summercamp/backpack.json b/lib/challenges/worlds/summercamp/backpack.json
new file mode 100644
index 000000000..fdb455022
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/backpack.json
@@ -0,0 +1,143 @@
+{
+ "id": "backpack",
+ "title": "Pack your things",
+ "short_title": "Backpack",
+ "icon_class": "challenge_backpack",
+ "description": "Dick Kelty is the inventor of the aluminium frame backpack. Kelty got the idea for his backpack in 1951 when he and a friend were hiking in the Sierra Nevada. Working in his garage, and with $500 borrowed against his house, Kelty and his wife went into production; he cut and welded the aluminium frames, while she stitched the nylon. They sold the finished backpacks for $24 apiece.",
+ "cover": "summercamp/day_8.png",
+ "completion_text": "I have a big challenge for you: try drawing your character behind that backpack! Head, arms, legs… ",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "It's a beautiful day to go for a walk in the forest - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Firstly, set the `stroke` to `0`, to avoid drawing the outlines of the shapes",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to place the floor - in a new line **type** `moveTo 0, 250`",
+ "solution": "moveTo 0, 250"
+ },
+ {
+ "hint": "Set the `color` of the grass to `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Draw the floor with a rectangle - **type** `rectangle 500, 250`",
+ "solution": "rectangle 500, 250"
+ },
+ {
+ "hint": "This looks like a nice place for a tree - **type** `moveTo 220`",
+ "solution": "moveTo 220"
+ },
+ {
+ "hint": "Set the `color` of the trunk to `lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Draw the trunk with a `rectangle` of size `50` by `400`",
+ "solution": "rectangle 50, 400"
+ },
+ {
+ "hint": "Beautiful! Now it just needs some leaves - **type** `move 30, -120` to place them",
+ "solution": "move 30, -120"
+ },
+ {
+ "hint": "Set the `color` of the leaves to `darkgreen`",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Draw a `circle` to represent the leaves with size `200`",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Looking great! Let's move the cursor to place our backpack - **type** `moveTo 150, 200`",
+ "solution": "moveTo 150, 200"
+ },
+ {
+ "hint": "Let's choose `color` `brown` for our backpack",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw the main part of the rucksack with a square - **type** `square 200`",
+ "solution": "square 200"
+ },
+ {
+ "hint": "Now move the cursor to bottom part of our backpack - **type** `moveTo 250, 400`",
+ "solution": "moveTo 250, 400"
+ },
+ {
+ "hint": "Draw the bottom using an ellipse - **type** `ellipse 100, 30`",
+ "solution": "ellipse 100, 30"
+ },
+ {
+ "hint": "We need to store the sleeping bag at the top - **type** `moveTo 250, 200`",
+ "solution": "moveTo 250, 200"
+ },
+ {
+ "hint": "Set the `color` of the sleeping bag to `darkred`",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "Ready to draw the sleeping bag? Use an ellipse - **type** `ellipse 115, 40`",
+ "solution": "ellipse 115, 40"
+ },
+ {
+ "hint": "We need something to secure your sleeping bag to your backpack - **type** `moveTo 200, 160`",
+ "solution": "moveTo 200, 160"
+ },
+ {
+ "hint": "The `orangered` seems like a nice `color` for the holders",
+ "solution": "color orangered"
+ },
+ {
+ "hint": "Draw the left one first - **type** `rectangle 15, 90`",
+ "solution": "rectangle 15, 90"
+ },
+ {
+ "hint": "Position the cursor to the right to draw the next holder - **type** `moveTo 290, 160`",
+ "solution": "moveTo 290, 160"
+ },
+ {
+ "hint": "Use a `rectangle` again for the right holder, same as the previous one",
+ "solution": "rectangle 15, 90"
+ },
+ {
+ "hint": "Excellent! We need more storage space. Move the cursor to the middle - **type** `moveTo 205, 320`",
+ "solution": "moveTo 205, 320"
+ },
+ {
+ "hint": "Use a `rectangle` for the outside pocket, `90` by `60` will suffice",
+ "solution": "rectangle 90, 60"
+ },
+ {
+ "hint": "That pocket needs to be closed so bugs can't get in - **type** `moveTo 250, 320`",
+ "solution": "moveTo 250, 320"
+ },
+ {
+ "hint": "Set the `color` to `lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "An `ellipse` of size `48` by `7` will help here",
+ "solution": "ellipse 48, 7"
+ },
+ {
+ "hint": "Looking great! Let’s place a button on that pocket - **type** `moveTo 250, 330`",
+ "solution": "moveTo 250, 330"
+ },
+ {
+ "hint": "Set the `color` to `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw a `circle` button of size `10`",
+ "solution": "circle 10"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/bear.json b/lib/challenges/worlds/summercamp/bear.json
new file mode 100644
index 000000000..3282e8b5c
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/bear.json
@@ -0,0 +1,91 @@
+{
+ "id": "bear",
+ "title": "Cheeky Bear",
+ "short_title": "Bear",
+ "description": "Bears are very smart and have been known to roll rocks into bear traps to set off the trap and then eat the bait in safety. These animals can run up to 40 miles per hour, fast enough to catch a running horse. Take into account that the fastest known human alive today is Usain Bolt, who can run at 27mph! And because bears can walk short distances on their hind legs, some Native Americans called them “the beast that walks like a man.”",
+ "icon_class": "challenge_bear",
+ "cover": "summercamp/day_11.png",
+ "completion_text": "That cute bear needs a body, and a habitat. Use your code skills to impress other campers with your abilities! Remember that the icons on your left can help you achieve what you have in mind.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "The bear lives in the forest. Set the background color to green - **type** `background green`",
+ "solution": "background green"
+ },
+ {
+ "hint": "The bear has brown fur, let's start to draw his face by getting our paint ready - **type** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Draw the face with a circle - **type** `circle 100`",
+ "solution": "circle 100"
+ },
+ {
+ "hint": "Now let's move the cursor to draw his left ear - **type** `move -80, -80`",
+ "solution": "move -80, -80"
+ },
+ {
+ "hint": "Draw the ear with a `circle` of size `30`",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Move the cursor to the right to draw his other ear - **type** `move 160`",
+ "solution": "move 160"
+ },
+ {
+ "hint": "Draw another ear (just like the last one)",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Now head over to where the left eye will go - **type** `move -110, 50`",
+ "solution": "move -110, 50"
+ },
+ {
+ "hint": "The rest of the bear's facial features will be `black`, so lets set the `color`",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw the first eye - **type** `circle 5`",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Next move over to where the other eye goes - **type** `move 60`",
+ "solution": "move 60"
+ },
+ {
+ "hint": "Fill in the other eye just like the last one",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Move to the center of the face for the bear's most distinctive feature - **type** `move -30, 50`",
+ "solution": "move -30, 50"
+ },
+ {
+ "hint": "Draw his nose with a triangle using polygon - **type** `polygon 12, -16, -12, -16`",
+ "solution": "polygon 12, -16, -12, -16"
+ },
+ {
+ "hint": "Let's get ready to draw the mouth by moving down a bit further - **type** `move 0, 20`",
+ "solution": "move 0, 20"
+ },
+ {
+ "hint": "Set the `stroke` (the outline of a shape) to color `black` and size `3`",
+ "solution": "stroke black, 3"
+ },
+ {
+ "hint": "We don't want the mouth to be filled so set its color to null - **type** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "An arc is part of a cirlce, just like a smiling mouth - **type** `arc 15, 0.5, 2`",
+ "solution": "arc 15, 0.5, 2"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/bow_and_arrow.json b/lib/challenges/worlds/summercamp/bow_and_arrow.json
new file mode 100644
index 000000000..c3f571e91
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/bow_and_arrow.json
@@ -0,0 +1,108 @@
+{
+ "id": "bow_and_arrow",
+ "title": "Bow and Arrow",
+ "short_title": "Bow and Arrow",
+ "icon_class": "challenge_bow",
+ "description": "Shooting a bow and arrow at a target is an art that requires lots of focus. This challenge is to draw a bow and try to hold it steady while your hand jitters...just like the other humans who have used the bow and arrow for hunting since the dawn of civilization over ten thousand years ago. The oldest known bow, found in Denmark, dates from 9000 BCE!",
+ "cover": "summercamp/day_15.png",
+ "completion_text": "Click Refresh and watch the target move while you try to hold your bow steady! Try getting rid of the moving background to make it easier to hit the target, converting your bow to a crossbow, or drawing an arrow in the bullseye.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "The weather is good today...there is no wind to blow our arrows off course. Set the background color to blue - **type** `background blue`.",
+ "solution": "background blue"
+ },
+ {
+ "hint": "We want the background to jitter, so we will start drawing it at a random point. **Type** `moveTo (random -20, 0), (random 240, 260)`.",
+ "solution": "moveTo (random -20, 0), (random 240, 260)",
+ "validate": "__moveTo ?\\( *random +-20, +0 *\\), +\\( *random +240, +260 *\\)"
+ },
+ {
+ "hint": "Our target is in a grassy field, so lets get our `color green` ready",
+ "solution": "color green"
+ },
+ {
+ "hint": "Draw a big `rectangle` that is `550` pixels high and `300` pixels wide for the grass",
+ "solution": "rectangle 550, 300"
+ },
+ {
+ "hint": "Now let's move the cursor to draw our target - **type** `move 275, 10`",
+ "solution": "move 275, 10"
+ },
+ {
+ "hint": "First we will draw the wooden legs supporting it - **type** `stroke brown, 15`",
+ "solution": "stroke brown, 15"
+ },
+ {
+ "hint": "Draw the left leg - **type** `line -80, 120`",
+ "solution": "line -80, 120"
+ },
+ {
+ "hint": "...and now draw the right leg - **type** `line 80, 120`",
+ "solution": "line 80, 120"
+ },
+ {
+ "hint": "We will draw a target with white and red rings - **type** `stroke white, 50`",
+ "solution": "stroke white, 50"
+ },
+ {
+ "hint": "Now make the inside of the circle red - **type** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "First, draw a circle with a radius of 80 pixels- **type** `circle 80`",
+ "solution": "circle 80"
+ },
+ {
+ "hint": "Second, draw a `circle` with a radius of `30` pixels",
+ "solution": "circle 30"
+ },
+ {
+ "hint": "Move the cursor to draw an arrowhead - **type** `moveTo 250, 220`",
+ "solution": "moveTo 250, 220"
+ },
+ {
+ "hint": "Our arrowhead will have a thin gray outline - **type** `stroke gray, 1`",
+ "solution": "stroke gray, 1"
+ },
+ {
+ "hint": "The flint arrowhead is going to have the `color dimgray`",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Draw the arrowhead as a diamond - **type** `polygon -10, 40, 0, 80, 10, 40`",
+ "solution": "polygon -10, 40, 0, 80, 10, 40"
+ },
+ {
+ "hint": "Next we will move to draw the shaft of the arrow - **type** `move 0, 40`",
+ "solution": "move 0, 40"
+ },
+ {
+ "hint": "The arrow will be made of a light brown wood - **type** `stroke lightbrown, 20`",
+ "solution": "stroke lightbrown, 20"
+ },
+ {
+ "hint": "Draw a line for the shaft - **type** `line 200, 360`",
+ "solution": "line 200, 360"
+ },
+ {
+ "hint": "The last thing we will draw is our bow. Move the cursor off the screen - **type** `moveTo 800, 375`",
+ "solution": "moveTo 800, 375"
+ },
+ {
+ "hint": "We are going to draw an arc for the bow so let's draw it with a thick piece of dark brown wood - **type** `stroke darkbrown, 40`",
+ "solution": "stroke darkbrown, 40"
+ },
+ {
+ "hint": "We don't want the arc to have any fill - **type** `color null`",
+ "solution": "color null"
+ },
+ {
+ "hint": "Draw the bow with a big arc centered far off the screen - **type** `arc 500, 0, 2`",
+ "solution": "arc 500, 0, 2"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/camera.json b/lib/challenges/worlds/summercamp/camera.json
new file mode 100644
index 000000000..0a53047ee
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/camera.json
@@ -0,0 +1,109 @@
+{
+ "id": "camera",
+ "title": "Take a pic!",
+ "short_title": "Camera",
+ "icon_class": "challenge_camera",
+ "description": "Back in the 1820s, early cameras would take several hours to actually capture a film! With annoyingly long exposure hours, photographing people with a nice smile was not really possible. Why? Try and hold a convincing smile while staying perfectly still for hours and you will get your answer. Today, the number of photographs clicked every two minutes is same as the number of photographs clicked by mankind in 1800s.",
+ "cover": "summercamp/day_10.png",
+ "completion_text": "Try drawing yourself taking the picture behind that beautiful camera. Perhaps a light for the flash as well?",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "Set the `background` color to `blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set the `stroke` to `0` for now, to avoid drawing the outline of the shapes",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to place the camera in the center - **type** `moveTo 100, 150`",
+ "solution": "moveTo 100, 150"
+ },
+ {
+ "hint": "Set the `color` of the camera to `dimgray`",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Now draw the body of the camera with a rectangle - **type** `rectangle 300, 200`",
+ "solution": "rectangle 300, 200"
+ },
+ {
+ "hint": "Move the cursor to draw the lense - **type** `move 150, 100`",
+ "solution": "move 150, 100"
+ },
+ {
+ "hint": "Set the `color` of the lens to `lightblue`",
+ "solution": "color lightblue"
+ },
+ {
+ "hint": "Set the outline of the lens with a `stroke` of `black` color and size `10`",
+ "solution": "stroke black, 10"
+ },
+ {
+ "hint": "The lens is a `circle` with size `50`",
+ "solution": "circle 50"
+ },
+ {
+ "hint": "Set the `color` to `lightgray` for the second half of the lens",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Draw an arc that matches the top half of the lens - **type** `arc 50, 2, 1`",
+ "solution": "arc 50, 2, 1"
+ },
+ {
+ "hint": "This camera needs a flash! Move the cursor to place it - **type** `move -30, -125`",
+ "solution": "move -30, -125"
+ },
+ {
+ "hint": "Draw the flash of the camera with a `rectangle` of size `60` by `20`",
+ "solution": "rectangle 60, 20"
+ },
+ {
+ "hint": "Set the `stroke` back to `0`.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the `color` of the flash to `lightblue`",
+ "solution": "color lightblue"
+ },
+ {
+ "hint": "Draw a polygon for the reflection on the flash - **type** `polygon 40, 0, 0, 20`",
+ "solution": "polygon 40, 0, 0, 20"
+ },
+ {
+ "hint": "Only the button to shoot the picture is left - **type** `move 100, 10`",
+ "solution": "move 100, 10"
+ },
+ {
+ "hint": "Set the `color` of the button to `red`.",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw the button of the camera with a `rectangle` of size `50` by `15`",
+ "solution": "rectangle 50, 15"
+ },
+ {
+ "hint": "One more detail! Move the cursor one last time - **type** `move 25, -20`",
+ "solution": "move 25, -20"
+ },
+ {
+ "hint": "Set the `color` to `yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Set the `font` to `'ComicSansMS'` and size `30`",
+ "solution": "font 'ComicSansMS', 30"
+ },
+ {
+ "hint": "Write some text - **type** `text 'Click!'`",
+ "solution": "text 'Click!'"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/camp_badge.json b/lib/challenges/worlds/summercamp/camp_badge.json
new file mode 100644
index 000000000..f682de7bc
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/camp_badge.json
@@ -0,0 +1,56 @@
+{
+ "id": "camp_badge",
+ "title": "Camp Badge",
+ "short_title": "Camp Badge",
+ "icon_class": "challenge_badge",
+ "description": "First Aid is the most popular merit badge. In fact, 6.9 million Scouts have earned it since its debut in 1911. Other popular ones are Environmental Science, Search and Rescue, and our favourites Programming and Game Design! There are some oddballs as well. For example Truck transportation: “Assume that you are going to ship by truck 500 pounds of goods (freight class 65) from your town to another town 500 miles away. Your shipment must arrive within three days. Explain in writing…”. And Nuclear Science “Obtain a sample of irradiated and non-irradiated foods. Prepare the two foods and compare their taste and texture.”",
+ "completion_text": "This badge will distinguish your camp from others. Surprise the world with an amazing creation.",
+ "cover": "summercamp/day_5.png",
+ "difficulty": 1,
+ "startAt": 4,
+ "start_date": "2015-10-07 06:00:00",
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Set the `background` color to `black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "Set the `color` of your badge to `lightblue`",
+ "solution": "color lightblue"
+ },
+ {
+ "hint": "Set the stroke for the first circle - **type** `stroke red, 20`",
+ "solution": "stroke red, 20"
+ },
+ {
+ "hint": "Draw a `circle` with a size of `200`",
+ "solution": "circle 200"
+ },
+ {
+ "hint": "Set the stroke for the second circle - **type** `stroke yellow, 20`",
+ "solution": "stroke yellow, 20"
+ },
+ {
+ "hint": "Draw a `circle` with a size of `190`",
+ "solution": "circle 190"
+ },
+ {
+ "hint": "Set the `stroke` for the third circle to `purple` color and size `20`",
+ "solution": "stroke purple, 20"
+ },
+ {
+ "hint": "Draw a `circle` with a size of `180`",
+ "solution": "circle 180"
+ },
+ {
+ "hint": "Add some decoration inside. Set the `color` to `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "An arc is part of a cirlce, draw one that matches the inner circle - **type** `arc 180, 1, 2`",
+ "solution": "arc 180, 1, 2"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/camp_sign.json b/lib/challenges/worlds/summercamp/camp_sign.json
new file mode 100644
index 000000000..7b952989f
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/camp_sign.json
@@ -0,0 +1,87 @@
+{
+ "id": "camp_sign",
+ "title": "Your Summer Camp sign",
+ "short_title": "Camp Sign",
+ "description": "Welcome to your first day! Before you set out from the lodge, your campsite sign needs a design. Be creative! Use a crazy color scheme, or show what you want to do at camp. \nIn the Pacific Northwest of the United States, indigenous peoples carved Totem Poles to tell the stories of their families and cultures. These beautiful sculptures are carved into trees and often feature characters and events from legends. You can search the internet for images of Totem Poles for inspiration.",
+ "icon_class": "challenge_campsign",
+ "cover": "summercamp/day_1.png",
+ "completion_text": "Well done! Now give your camp its own name and style. Change your camp name on line 18, or the color of the sign on line 3... why not add a nice background and some extra decorations? All those tool icons on the left can help you get started.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "It's a sunny day! Set the background color to blue - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Let's move the cursor over to the place on the screen where we want to start drawing our sign.\n\nPress ENTER to add the next instruction, and - **type** `moveTo 100, 150`",
+ "solution": "moveTo 100, 150"
+ },
+ {
+ "hint": "In Make Art we draw shapes using digital pens. Before we draw the next shape, we need to choose the pen we want to use to fill the inside of the shape. Let's select a brown pen by setting the color to lightbrown.\n\n In a new line **type** `color lightbrown`",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "When we draw a shape we can control how thick the line is that the pen leaves behind on the outside of the shape. This line is called the **stroke**.\n\n Press enter and then set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Now that we have our pen all set up, we can get started on the sign!\n\nDraw the main billboard with a rectangle - **type** `rectangle 300, 200`",
+ "solution": "rectangle 300, 200"
+ },
+ {
+ "hint": "Let's draw a shadow. Set the color to brown first - **type** `color brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw a thin shadow with a rectangle - **type** `rectangle 300, 5`",
+ "solution": "rectangle 300, 5"
+ },
+ {
+ "hint": "That sign looks too clean. We need to draw some imperfections.\n\nMove the cursor to place one on the left side of the sign- **type** `moveTo 100, 220`",
+ "solution": "moveTo 100, 220"
+ },
+ {
+ "hint": "Our imperfection will be a triangle. First we need to set the `color` to `blue` to match the background.",
+ "solution": "color blue"
+ },
+ {
+ "hint": "Set the stroke to be size 5 and brown (to match the sign). You can do both these things in one command - **type** `stroke brown, 5`",
+ "solution": "stroke brown, 5"
+ },
+ {
+ "hint": "Now we will use the `polygon` command to draw a triangular imperfection. To create it, we need to list out the positions of the two other corners - **type** `polygon 16, 10, 0, 11`",
+ "solution": "polygon 16, 10, 0, 11"
+ },
+ {
+ "hint": "The sign needs a post. Move the cursor to where we will draw it - **type** `moveTo 230, 350`",
+ "solution": "moveTo 230, 350"
+ },
+ {
+ "hint": "Set the color of the post to brown - **type** `color brown` ",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Set the stroke to 0, again - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Now that we have the colors sorted, draw the post with a rectangle - **type** `rectangle 40, 150`",
+ "solution": "rectangle 40, 150"
+ },
+ {
+ "hint": "We need some text in that sign. Move the cursor to place the text - **type** `moveTo 250, 270`",
+ "solution": "moveTo 250, 270"
+ },
+ {
+ "hint": "Next we need to choose our font. There are lots to choose from, like `Bariol`, `Georgia`, `Comic Sans MS`, `cursive`, and `Courier`.\n\nPick your favorite and type it in (inside of quote marks) like this - `font 'Bariol', 70`.",
+ "solution": "font 'Bariol', 70"
+ },
+ {
+ "hint": "Now write some silly text - **type** `text 'My Camp'`",
+ "solution": "text 'My Camp'"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/campfire.json b/lib/challenges/worlds/summercamp/campfire.json
new file mode 100644
index 000000000..3b311f9ec
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/campfire.json
@@ -0,0 +1,90 @@
+{
+ "id": "campfire",
+ "title": "Campfire",
+ "short_title": "Campfire",
+ "description": "Time to get a fire going. In the wilderness, this very well might be your only source of light, and heat. Burning at over a thousand degrees fahrenheit, this will be sure to keep you warm, and could keep you fed too! Meats, vegetables, and marshmallows can all be cooked over the open flame with the help of sharpened sticks. \nMost fires are built with wood logs, but yours is going to be built with code and a “loop.” As your fire grows upwards its colour changes into a deep shade of orange as the oxygen it is burning starts to cools down. Your code will produce hundreds of colours with just a few lines of code.",
+ "icon_class": "challenge_fire",
+ "completion_text": "Try adding a starry sky, more flames, a ring of rocks to make it safer or even marshmallows!",
+ "difficulty": 2,
+ "cover": "summercamp/day_6.png",
+ "startAt": 5,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "It's a dark night. Set the `background` color to `darkblue`",
+ "solution": "background darkblue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to place the floor - **type** `moveTo 0, 300`",
+ "solution": "moveTo 0, 300"
+ },
+ {
+ "hint": "Set the `color` to `darkgreen`",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Draw the grass using a rectangle - **type** `rectangle 500, 200`",
+ "solution": "rectangle 500, 200"
+ },
+ {
+ "hint": "Move the cursor to place the light casted by the fire - **type** `moveTo 250, 400`",
+ "solution": "moveTo 250, 400"
+ },
+ {
+ "hint": "Set the `color` to `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Draw the light using an ellipse - **type** `ellipse 200, 30`",
+ "solution": "ellipse 200, 30"
+ },
+ {
+ "hint": "We need some wood to feed the fire - **type** `moveTo 110, 390`",
+ "solution": "moveTo 110, 390"
+ },
+ {
+ "hint": "Set the stroke for the logs - **type** `stroke brown, 25`",
+ "solution": "stroke brown, 25"
+ },
+ {
+ "hint": "Good, now draw the first log - **type** `line 270, 30`",
+ "solution": "line 270, 30"
+ },
+ {
+ "hint": "Move the cursor to place the second log - **type** `moveTo 420, 390`",
+ "solution": "moveTo 420, 390"
+ },
+ {
+ "hint": "Draw the second log - **type** `line -290, 30`",
+ "solution": "line -290, 30"
+ },
+ {
+ "hint": "Excellent! We are now ready to light the fire - **type** `moveTo 180, 400`",
+ "solution": "moveTo 180, 400"
+ },
+ {
+ "hint": "Our fire will be formed by 150 lines! - **type** `for i in [ 1 .. 150 ]`",
+ "solution": "for i in [ 1 .. 150 ]"
+ },
+ {
+ "hint": "Set the thickness and color of each line - **type** `stroke 'rgba(255, ' + (i + 50) + ', 0, 0.3)', 10`",
+ "solution": " stroke 'rgba(255, ' + (i + 50) + ', 0, 0.3)', 10",
+ "validate": "__^ stroke 'rgba*\\(255, ' \\+ \\(i *\\+ *50\\) *\\+ *', *0, *0.3\\)', 10"
+ },
+ {
+ "hint": "Now draw the line - **type** `line 150 - i`",
+ "solution": " line 150 - i"
+ },
+ {
+ "hint": "Move the cursor for next line - **type** `move 0.5, -2`",
+ "solution": " move 0.5, -2"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/campsite.json b/lib/challenges/worlds/summercamp/campsite.json
new file mode 100644
index 000000000..abce584fa
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/campsite.json
@@ -0,0 +1,97 @@
+{
+ "id": "campsite",
+ "title": "Draw your Campsite",
+ "short_title": "Campsite",
+ "description": "Time to find a clearing to set up camp. The most important thing to look for when searching for an optimal campsite is flat ground. If possible, keep your campsite close to a water source. This will make cooking and cleanup much easier. Finally, make sure to be surrounded by trees, as they are a great source of kindling and will provide protection from the sun. \nYour friends will want to see what your campsite looks like, so once you have one, draw it! You can use code to set the scene for where you are staying for the next two weeks.",
+ "icon_class": "challenge_location",
+ "completion_text": "The camp site is looking great! Try adding more trees, flowers, rocks... perhaps a fence as well?",
+ "cover": "summercamp/day_2.png",
+ "difficulty": 1,
+ "startAt": 1,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "Today is a beautiful day - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor where the sun will be - **type** `moveTo 400, 50`",
+ "solution": "moveTo 400, 50"
+ },
+ {
+ "hint": "Set the `color` of the sun `yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Draw the sun with a circle - **type** `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "There is one cloud in the horizon. - **type** `moveTo 440, 80`",
+ "solution": "moveTo 440, 80"
+ },
+ {
+ "hint": "Set the `color` of the cloud `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw the cloud with an ellipse - **type** `ellipse 60, 20`",
+ "solution": "ellipse 60, 20"
+ },
+ {
+ "hint": "The lake has a beautiful `color` `aquamarine` this morning",
+ "solution": "color aquamarine"
+ },
+ {
+ "hint": "Move the cursor where the lake is - **type** `moveTo 0, 190`",
+ "solution": "moveTo 0, 190"
+ },
+ {
+ "hint": "Draw the lake using a simple rectangle - **type** `rectangle 500, 310`",
+ "solution": "rectangle 500, 310"
+ },
+ {
+ "hint": "Set the `color` to `green` for the grass",
+ "solution": "color green"
+ },
+ {
+ "hint": "Move the cursor before drawing the grass - **type** `moveTo 250, 700`",
+ "solution": "moveTo 250, 700"
+ },
+ {
+ "hint": "Draw the camp site with a circle - **type** `circle 500`",
+ "solution": "circle 500"
+ },
+ {
+ "hint": "Looking a bit empty, let's draw a tree - **type** `moveTo 120, 300`",
+ "solution": "moveTo 120, 300"
+ },
+ {
+ "hint": "Set the `color` of the trunk to `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw the trunk of the tree using a rectangle - **type** `rectangle 15, 100`",
+ "solution": "rectangle 15, 100"
+ },
+ {
+ "hint": "Now place the foliage of the tree - **type** `move 8, -120`",
+ "solution": "move 8, -120"
+ },
+ {
+ "hint": "Set the `color` to `darkgreen`",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Draw the foliage with a triangle using `polygon` - **type** `polygon -40, 160, 40, 160`",
+ "solution": "polygon -40, 160, 40, 160"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/cinema.json b/lib/challenges/worlds/summercamp/cinema.json
new file mode 100644
index 000000000..4393cb316
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/cinema.json
@@ -0,0 +1,117 @@
+{
+ "id": "cinema",
+ "title": "Relax watching a movie",
+ "short_title": "Cinema",
+ "icon_class": "challenge_cinema",
+ "description": "After playing a short movie presentation by the Skladanowsky brothers in 1895 which consisted of numerous very short 6-second films accompanied by a specially composed piece of music, the Berlin Wintergarten theatre in Mitte, Berlin, became known as the first ever movie theatre. Unfortunately the theatre was destroyed by bombs in 1944, during the Second World War.",
+ "cover": "summercamp/day_14.png",
+ "completion_text": "What good is a cinema without a movie playing? Go ahead and try drawing a scene from your favourite movie up on the big screen, and maybe a few friends to watch the film with you! Don't forget the popcorn!",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "It's dark in the cinema. Set the `background` color to `black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "Set the `stroke` to `0` in a new line",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "First, move the cursor to place the floor - **type** `moveTo 0, 300`",
+ "solution": "moveTo 0, 300"
+ },
+ {
+ "hint": "Set the floor `color` to `gray`",
+ "solution": "color gray"
+ },
+ {
+ "hint": "Draw the floor with a rectangle - **type** `rectangle 500, 200`",
+ "solution": "rectangle 500, 200"
+ },
+ {
+ "hint": "Now, move the cursor to place the screen - **type** `moveTo 50, 20`",
+ "solution": "moveTo 50, 20"
+ },
+ {
+ "hint": "Set the screen `color` to `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "The screen is a perfect `rectangle` of size `400` by `250`",
+ "solution": "rectangle 400, 250"
+ },
+ {
+ "hint": "Amazing! This theatre needs a curtain - **type** `moveTo 0`",
+ "solution": "moveTo 0"
+ },
+ {
+ "hint": "Set the curtain `color` to `red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw a vertical curtain as a `rectangle` of size `30` by `330`",
+ "solution": "rectangle 30, 330"
+ },
+ {
+ "hint": "Now draw an horizontal curtain size `500` by `10` in the same way",
+ "solution": "rectangle 500, 10"
+ },
+ {
+ "hint": "Looking great! Place the curtain on the right - **type** `move 470`",
+ "solution": "move 470"
+ },
+ {
+ "hint": "Draw a vertical curtain, same as the one the left",
+ "solution": "rectangle 30, 330"
+ },
+ {
+ "hint": "This theatre needs some seats for our audience - **type** `moveTo 0, 360`",
+ "solution": "moveTo 0, 360"
+ },
+ {
+ "hint": "Set the outline of the seats with a `stroke` of `orangered` color and size `10`.",
+ "solution": "stroke orangered, 10"
+ },
+ {
+ "hint": "Let's draw 3 rows and 6 columns of seats with a loop - **type** `for x in [ 1 .. 3 ]`",
+ "solution": "for x in [ 1 .. 3 ]"
+ },
+ {
+ "hint": "Set the `color` of the seats to `red`",
+ "solution": " color red"
+ },
+ {
+ "hint": "Now draw a `rectangle` for the seats, size `500` by `40`",
+ "solution": " rectangle 500, 40"
+ },
+ {
+ "hint": "Move the cursor down to place the back of the seats - **type** `move 0, 50`",
+ "solution": " move 0, 50"
+ },
+ {
+ "hint": "Inside the loop, open a second loop for the back of the seats - **type** `for y in [ 1 .. 6 ]`",
+ "solution": " for y in [ 1 .. 6 ]"
+ },
+ {
+ "hint": "Set the `color` of the backseat to `darkred`",
+ "solution": " color darkred"
+ },
+ {
+ "hint": "Brilliant! Almost there. Now draw the back of the seat with an arc - **type** `arc 60, 0, 1`",
+ "solution": " arc 60, 0, 1"
+ },
+ {
+ "hint": "Now separate the seats `130` pixels to the right",
+ "solution": " move 130"
+ },
+ {
+ "hint": "Last, make sure new rows are positioned correctly - Press **Enter**, then **Backspace** and **type** `move -800` inside the **first** loop",
+ "solution": " move -800"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/compass.json b/lib/challenges/worlds/summercamp/compass.json
new file mode 100644
index 000000000..4732d0453
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/compass.json
@@ -0,0 +1,93 @@
+{
+ "id": "compass",
+ "title": "A compass is an essential tool",
+ "short_title": "Compass",
+ "icon_class": "challenge_compass",
+ "description": "Essentially a compass is a magnet, generally a magnetized needle. Since opposites attract the southern pole of the needle is attracted to the Earth's natural magnetic north pole. Did you know that you can create your own simply with a paperclip, cork and a magnet?",
+ "cover": "summercamp/day_9.png",
+ "completion_text": "Now try adding the rest of the 'compass rose' components: the other cardinal directions (E, S, W and even NE, NW, SE and SW). Why not a hand holding it?",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Select `lightgray` for the `background`",
+ "solution": "background lightgray"
+ },
+ {
+ "hint": "In a new line, set the outline of your compass with a `stroke` of `gold` color and size `20`",
+ "solution": "stroke gold, 20"
+ },
+ {
+ "hint": "Set the `color` of the compass to `gray`",
+ "solution": "color gray"
+ },
+ {
+ "hint": "Your compass is a `circle` with size `110`",
+ "solution": "circle 110"
+ },
+ {
+ "hint": "Set the `color` to `darkgray` for the second half of the compass",
+ "solution": "color darkgray"
+ },
+ {
+ "hint": "Set the `stroke` back to `0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Draw an arc that matches the top half of the compass - **type** `arc 105, 2, 1`",
+ "solution": "arc 105, 2, 1"
+ },
+ {
+ "hint": "We need to mark the north somehow - in a new line **type** `moveTo 250, 175`",
+ "solution": "moveTo 250, 175"
+ },
+ {
+ "hint": "Set the `color` to `gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Set the `font` to `'cursive'` and size `25`",
+ "solution": "font 'cursive', 25"
+ },
+ {
+ "hint": "Write an N to represent north - **type** `text 'N'`",
+ "solution": "text 'N'"
+ },
+ {
+ "hint": "Now we need a needle with 2 sides. Set the `color` to `red` for the first side",
+ "solution": "color red"
+ },
+ {
+ "hint": "Move the cursor to position the needle **type** `moveTo 230, 250`",
+ "solution": "moveTo 230, 250"
+ },
+ {
+ "hint": "Draw a triangle with a polygon - **type** `polygon 20, -90, 40, 0`",
+ "solution": "polygon 20, -90, 40, 0"
+ },
+ {
+ "hint": "Excellent! Set the `color` to `white` for the second side of the needle",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw the second part - **type** `polygon 20, 90, 40, 0`",
+ "solution": "polygon 20, 90, 40, 0"
+ },
+ {
+ "hint": "Finish it with one last detail. Set the `color` to `gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Move the cursor to the center of the compass **type** `move 20`",
+ "solution": "move 20"
+ },
+ {
+ "hint": "Draw a `circle` of size `20`",
+ "solution": "circle 20"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/fireworks.json b/lib/challenges/worlds/summercamp/fireworks.json
new file mode 100644
index 000000000..ae830ff5e
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/fireworks.json
@@ -0,0 +1,50 @@
+{
+ "id": "fireworks",
+ "title": "Fireworks",
+ "short_title": "Fireworks",
+ "icon_class": "challenge_fireworks",
+ "description": "Camp is almost all wrapped up, and to celebrate we have fireworks! Fireworks have been around for centuries and are believed to have been invented by the Chinese. We are almost ready to put on the show, but need a little bit of help designing the fireworks. Can you give it a shot?",
+ "cover": "summercamp/day_21.png",
+ "completion_text": "Well done! You made a function that can draw fireworks! Can you improve upon it? Draw them all over the screen, add in other creations and share it!",
+ "difficulty": 3,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "The sun is down and the moon is new, let’s make the sky dark with `background black`",
+ "solution": "background black"
+ },
+ {
+ "hint": "Lets set the `stroke` to `red` for our firework",
+ "solution": "stroke red"
+ },
+ {
+ "hint": "We want to draw 60 lines radiating outward, so let’s use a for loop `for i in [0 ... 60]`",
+ "solution": "for i in [0 ... 60]"
+ },
+ {
+ "hint": "All of the lines in our firework should have different lengths! So for each line radiating out, let’s set its length with `length = random 1, 200`.",
+ "solution": " length = random 1, 200"
+ },
+ {
+ "hint": "For each loop, we are drawing a new line radiating out. So for every time we loop, the angle of the line should change. This uses some advanced math, but don’t fear. Just **type** `angle = (360 / 60 * i) * (Math.PI / 180)`.",
+ "solution": " angle = (360 / 60 * i) * (Math.PI / 180)",
+ "validate": "__^ angle *= *\\(360 */ *60 \\* i\\) *\\* *\\(Math[.]PI */ *180\\) *$"
+ },
+ {
+ "hint": "Using this new angle and the random length let’s calculate where the x coordinate for the end of the radiating line should be. **Type** `dx = 250 + Math.sin(angle) * length`.",
+ "solution": " dx = 250 + Math.sin(angle) * length",
+ "validate": "__^ dx *= *250 *\\+ *Math.sin\\(angle\\) *\\* *length"
+ },
+ {
+ "hint": "Now let’s calculate the y coordinate for the end of the radiating line. **Type** `dy = 250 + Math.cos(angle) * length`.",
+ "solution": " dy = 250 + Math.cos(angle) * length",
+ "validate": "__^ dy *= *250 *\\+ *Math.cos\\(angle\\) *\\* *length"
+ },
+ {
+ "hint": "Finally, with all the coordinates set we are ready to draw the line. **Type** `lineTo dx, dy` ",
+ "solution": " lineTo dx, dy"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/fishing.json b/lib/challenges/worlds/summercamp/fishing.json
new file mode 100644
index 000000000..3b85db20f
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/fishing.json
@@ -0,0 +1,110 @@
+{
+ "id": "fishing",
+ "title": "Fishing",
+ "short_title": "Fishing",
+ "icon_class": "challenge_fishing",
+ "description": "It's time to go fishing! Inside the camp's lake there are trout, catfish, and perch swimming around looking for a snack. Grab your fishing pole and some worms, and head over to the water for todays adventure. ",
+ "cover": "summercamp/day_17.png",
+ "completion_text": "Great job! Every time you press a key the image redraws, so try holding down the space bar and watch the waves ripple. Also try adding a fish or making the bobber disappear.",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "It's a sunny day! Set the background color to lightblue - **type** `background lightblue`",
+ "solution": "background lightblue"
+ },
+ {
+ "hint": "We will start by drawing the waves. We want to draw without a fill - set the `color` to be `null`.",
+ "solution": "color null"
+ },
+ {
+ "hint": "Our waves will be dark blue - **type** `stroke darkblue, 8`",
+ "solution": "stroke darkblue, 8"
+ },
+ {
+ "hint": "Move to the place on the left where we will start - **type** `moveTo 10, 250`",
+ "solution": "moveTo 10, 250"
+ },
+ {
+ "hint": "We are going to draw waves on the surface by creating 25 waves inside of a loop - **type** `for i in [1 .. 25]`",
+ "solution": "for i in [1 .. 25]"
+ },
+ {
+ "hint": "Create a wave out of an arc with a radius of 20 pixels - **type** `arc 20, 0.8, 0.2`",
+ "solution": " arc 20, 0.8, 0.2"
+ },
+ {
+ "hint": "Move each wave a random amount to the left - **type** `move (random 24, 32)`",
+ "solution": " move (random 24, 32)",
+ "validate": "__ move \\(random 24, +32\\)"
+ },
+ {
+ "hint": "Press ENTER and then backspace to get rid of the indent. This exits the loop.\n\nNext move to the place where we will draw the rest of the water - **type** `moveTo 0, 270`.",
+ "solution": "moveTo 0, 270"
+ },
+ {
+ "hint": "Set the `color` of the water to be the same as the wave",
+ "solution": "color darkblue"
+ },
+ {
+ "hint": "Draw a big `rectangle` that is `500` pixels high and `250` pixels wide for the water",
+ "solution": "rectangle 500, 250"
+ },
+ {
+ "hint": "Let's get ready to add some little blue waves - **type** `stroke blue, 2`",
+ "solution": "stroke blue, 2"
+ },
+ {
+ "hint": "We are going to draw waves 100 times - **type** `for i in [1 .. 100]`",
+ "solution": "for i in [1 .. 100]"
+ },
+ {
+ "hint": "Move each wave to a random place on the surface of the water - **type** `moveTo (random 0, 500), (random 260, 500)`",
+ "solution": " moveTo (random 0, 500), (random 260, 500)",
+ "validate": "__ moveTo ?\\( *random +0, +500 *\\), +\\( *random +260, +500 *\\)"
+ },
+ {
+ "hint": "Now draw each ripple as a little arc - **type** `arc 10, 0.8, 0.2`",
+ "solution": " arc 10, 0.8, 0.2"
+ },
+ {
+ "hint": "Brilliant! When you press the spacebar, a new set of random numbers is selected and the ripples move!\n\nTo catch some fish we need a brown fishing pole - Exit the loop and **type** `stroke brown, 10`.",
+ "solution": "stroke brown, 10"
+ },
+ {
+ "hint": "Now move to the place where we will draw the end of the pole - **type** `moveTo 300, 100`",
+ "solution": "moveTo 300, 100"
+ },
+ {
+ "hint": "Draw a `line` that is `150` pixels wide and `400` pixels tall",
+ "solution": "line 150, 400"
+ },
+ {
+ "hint": "Next we need some fishing line - **type** `stroke lightgray, 1`",
+ "solution": "stroke lightgray, 1"
+ },
+ {
+ "hint": "Draw a line that goes to the left and down - **type** `line -100, 200`",
+ "solution": "line -100, 200"
+ },
+ {
+ "hint": "To see if a fish is biting we are going to draw a cork. Move to the end of the fishing line - **type** `move -100, (random 197, 202)`",
+ "solution": "move -100, (random 197, 202)",
+ "valudate": "__move -100, \\(random 197, 202\\)"
+ },
+ {
+ "hint": "The cork doesn't have an outline - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the cork's `color` to `red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Now draw the cork floating in the water - `arc 8, 0, 1`",
+ "solution": "arc 8, 0, 1"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/flag.json b/lib/challenges/worlds/summercamp/flag.json
new file mode 100644
index 000000000..ea40cbe69
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/flag.json
@@ -0,0 +1,77 @@
+{
+ "id": "flag",
+ "title": "Create your Flag",
+ "short_title": "Flag",
+ "description": "Time to show your true colors. Your campsite is shaping up, and as the campsite’s leader you need to make a flag. We’ve got you started with the basics for a flag and flagpole, but it is up to you to to make it your own. \nYou can look to other flags for inspiration. Countries like France, Nigeria, Sweden, and Switzerland all have very simple color and shape designs, while Spain, Brazil, Kenya, and Saudi Arabia all feature complex designs. When you are done, share your flag to lay claim to your little bit of land!",
+ "icon_class": "challenge_flag",
+ "completion_text": "You have a white canvas in front of you, use it wisely. Create the most amazing flag the world has ever seen!",
+ "cover": "summercamp/day_4.png",
+ "difficulty": 1,
+ "startAt": 3,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "Draw the sky where the flag will flutter - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Let's move the cursor to place our flagpole in the correct place - **type** `moveTo 100, 100`",
+ "solution": "moveTo 100, 100"
+ },
+ {
+ "hint": "Set the `color` of the flagpole to `darkgray`",
+ "solution": "color darkgray"
+ },
+ {
+ "hint": "Draw a long, thin pole with a rectangle - **type** `rectangle 10, 400`",
+ "solution": "rectangle 10, 400"
+ },
+ {
+ "hint": "Move the cursor to place a ball on top-centre of the flagpole - **type** `move 5`",
+ "solution": "move 5"
+ },
+ {
+ "hint": "Set the `color` of the ornament to `gold`",
+ "solution": "color gold"
+ },
+ {
+ "hint": "Draw a circle with a size of 8 - in a new line **type** `circle 8`",
+ "solution": "circle 8"
+ },
+ {
+ "hint": "Now move the cursor to start drawing our flag - **type** `moveTo 115, 123`",
+ "solution": "moveTo 115, 123"
+ },
+ {
+ "hint": "Set the `color` of the flag to `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Draw the flag with a `rectangle` of size `261` and `171`",
+ "solution": "rectangle 261, 171"
+ },
+ {
+ "hint": "That flag needs something to hold of to - **type** `moveTo 100, 140`",
+ "solution": "moveTo 100, 140"
+ },
+ {
+ "hint": "Set the `color` of the holder to `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw the holder with a `20` by `5` `rectangle`",
+ "solution": "rectangle 20, 5"
+ },
+ {
+ "hint": "Now is your turn! Draw an amazing pattern on it - **type** `moveTo 120, 125`",
+ "solution": "moveTo 120, 125"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/flashlight.json b/lib/challenges/worlds/summercamp/flashlight.json
new file mode 100644
index 000000000..a5a6781ce
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/flashlight.json
@@ -0,0 +1,93 @@
+{
+ "id": "flashlight",
+ "title": "Lights on!",
+ "short_title": "Flashlight",
+ "icon_class": "challenge_flashlight",
+ "description": "Torches started life as ignited sticks; their flames would light the way for explorers and settlers. With the advent of electricity, and the invention of the dry cell battery in 1887, the road was paved to use incandescent bulbs as a replacement. While the technology inside flashlights has evolved, they are still as useful as ever. Meanwhile, the original torch has found a second coming in circus acts where they are thrown skywards in miraculous fashion.",
+ "cover": "summercamp/day_12.png",
+ "completion_text": "You are an adventurer in the dark with your torch. What might you have uncovered as its light passes over the room? Try drawing that fox that you’ve unearthed or that owl hooting in the trees.",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "steps": [
+ {
+ "hint": "It's getting dark outside! Set the `background` color to `darkblue`",
+ "solution": "background darkblue"
+ },
+ {
+ "hint": "Set the `stroke` to `0`, we won't need it for this challenge",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor to place our flashlight - **type** `moveTo 200, 200`",
+ "solution": "moveTo 200, 200"
+ },
+ {
+ "hint": "Set the `color` of the flashlight to `dimgray`",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Draw the head of the flashlight with a rectangle - **type** `rectangle 100, 50`",
+ "solution": "rectangle 100, 50"
+ },
+ {
+ "hint": "Now move the cursor down to draw the neck - **type** `move 0, 50`",
+ "solution": "move 0, 50"
+ },
+ {
+ "hint": "Set the `color` of this piece to `gray`",
+ "solution": "color gray"
+ },
+ {
+ "hint": "Draw a polygon - **type** `polygon 20, 40, 80, 40, 100, 0`",
+ "solution": "polygon 20, 40, 80, 40, 100, 0"
+ },
+ {
+ "hint": "Exciting! Now move the cursor down to draw the body - **type** `move 20, 40`",
+ "solution": "move 20, 40"
+ },
+ {
+ "hint": "Set the `color` of the body to `darkgray`",
+ "solution": "color darkgray"
+ },
+ {
+ "hint": "Draw the body of the flashlight with a `rectangle` `60` by `200`",
+ "solution": "rectangle 60, 200"
+ },
+ {
+ "hint": "The only thing missing now is a ON/OFF button. Set the `color` to `dimgray`",
+ "solution": "color dimgray"
+ },
+ {
+ "hint": "Now move the cursor down to place the button - **type** `move 30, 50`",
+ "solution": "move 30, 50"
+ },
+ {
+ "hint": "Draw an ellipse where the button will be located - **type** `ellipse 10, 30`",
+ "solution": "ellipse 10, 30"
+ },
+ {
+ "hint": "Set the `color` of the button to `red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Draw the button with an ellipse - **type** `ellipse 10, 20`",
+ "solution": "ellipse 10, 20"
+ },
+ {
+ "hint": "You have turned on the light! Set the color of the beam - **type** `color \"rgba(255, 255, 0, 0.8)\"`",
+ "solution": "color \"rgba(255, 255, 0, 0.8)\""
+ },
+ {
+ "hint": "Place the beam in front of the flashlight - **type** `move -50, -140`",
+ "solution": "move -50, -140"
+ },
+ {
+ "hint": "Draw the beam of light with a polygon - **type** `polygon 100, 0, 180, -300, -80, -300`",
+ "solution": "polygon 100, 0, 180, -300, -80, -300"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/foraging.json b/lib/challenges/worlds/summercamp/foraging.json
new file mode 100644
index 000000000..2d2add721
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/foraging.json
@@ -0,0 +1,57 @@
+{
+ "id": "foraging",
+ "title": "Foraging",
+ "short_title": "Foraging",
+ "icon_class": "challenge_foraging",
+ "description": "The wild is full of plants and fauna to eat. From stinging nettles, to mushrooms, and a multitude of berries, any meal can be improved with naturally growing foods. Berries are common in many parts of the world, and at Camp Kano they run wild even though they are hard to find. You might need to use your coding skills to find them.",
+ "cover": "summercamp/day_19.png",
+ "completion_text": "But wait! Where are the berries you are meant to be foraging for? Use `color red`, `moveTo`, and `square 25` to add some extra berries to the scene!",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Set `stroke` to `0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "It’s a bright sunny day! Set `background` to `blue`.",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Our foraging scene needs a grass field to start with. Set our drawing color to `green`.",
+ "solution": "color green"
+ },
+ {
+ "hint": "We want our grass on the bottom half of the screen, so let’s move into position with `moveTo 0, 300`",
+ "solution": "moveTo 0, 300"
+ },
+ {
+ "hint": "The grass is a nice big `rectangle` of size `500, 200`",
+ "solution": "rectangle 500, 200"
+ },
+ {
+ "hint": "Now let’s add in a nice boxy tree to our foraging scene. Begin by moving into position at exactly `50, 100`.",
+ "solution": "moveTo 50, 100"
+ },
+ {
+ "hint": "Our leaves are drawn with `square 150`.",
+ "solution": "square 150"
+ },
+ {
+ "hint": "Next we need to draw the trunk. Move the cursor with `moveTo 100, 250`.",
+ "solution": "moveTo 100, 250"
+ },
+ {
+ "hint": "Our wood has a nice `darkbrown` color. Set that as the drawing color.",
+ "solution": "color darkbrown"
+ },
+ {
+ "hint": "The trunk is a `rectangle` with a width of `40` and a height of `150`",
+ "solution": "rectangle 40, 150"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/ghost.json b/lib/challenges/worlds/summercamp/ghost.json
new file mode 100644
index 000000000..7e6b445b3
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/ghost.json
@@ -0,0 +1,91 @@
+{
+ "id": "ghost",
+ "title": "Ghost gathering",
+ "short_title": "Ghost",
+ "description": "Boo! As night falls, the spirits come out to play. A recent poll reported 87% of Chinese office workers believed in ghosts, and according to another report, 25% of Britons say they have seen a ghost. It’s getting dark, so it is hard to see, but we think we found a ghost! Maybe you can find out if it is friendly or not with your creative coding powers.",
+ "icon_class": "challenge_ghost",
+ "completion_text": "Spooky! Can you make the ghost scarier? Maybe if it could say \"Boo!\"...",
+ "difficulty": 2,
+ "cover": "summercamp/day_7.png",
+ "startAt": 6,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Select a spooky color for the background - **type** `background darkpurple`",
+ "solution": "background darkpurple"
+ },
+ {
+ "hint": "Set the `stroke` to `0`, to avoid drawing lines",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set the `color` to `white` for the ghost",
+ "solution": "color white"
+ },
+ {
+ "hint": "Now draw an ellipse for the ghost's body - **type** `ellipse 120, 140`",
+ "solution": "ellipse 120, 140"
+ },
+ {
+ "hint": "Set the `color` to `black` for the eyes",
+ "solution": "color black"
+ },
+ {
+ "hint": "Move the cursor to draw the first eye - **type** `move -40, -50`",
+ "solution": "move -40, -50"
+ },
+ {
+ "hint": "Draw the eye with an ellipse - **type** `ellipse 20, 30`",
+ "solution": "ellipse 20, 30"
+ },
+ {
+ "hint": "Set the `color` to `lightgray`",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Now draw a `circle` with a size of `5`",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Looking good! Now move the cursor to the right for the second eye - **type** `move 30`",
+ "solution": "move 30"
+ },
+ {
+ "hint": "Set the `color` back to `black` for the second eye",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw the second eye with an ellipse - **type** `ellipse 20, 30`",
+ "solution": "ellipse 20, 30"
+ },
+ {
+ "hint": "Set the `color` to `lightgray`",
+ "solution": "color lightgray"
+ },
+ {
+ "hint": "Draw a `circle` with size `5`",
+ "solution": "circle 5"
+ },
+ {
+ "hint": "Almost there! Set the `color` to `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Now move the cursor to the bottom - **type** `move -80, 150`",
+ "solution": "move -80, 150"
+ },
+ {
+ "hint": "Let's open a loop - **type** `for i in [ 1 .. 5 ]`",
+ "solution": "for i in [ 1 .. 5 ]"
+ },
+ {
+ "hint": "Draw a `circle` with a size of `45`",
+ "solution": " circle 45"
+ },
+ {
+ "hint": "And move the cursor slightly to the right - **type** `move 45`",
+ "solution": " move 45"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/guitar.json b/lib/challenges/worlds/summercamp/guitar.json
new file mode 100644
index 000000000..c6fa84b89
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/guitar.json
@@ -0,0 +1,128 @@
+{
+ "id": "guitar",
+ "title": "Play a nice sweet song",
+ "short_title": "Guitar",
+ "icon_class": "challenge_guitar",
+ "description": "Did you know the oldest surviving guitar-like instrument is actually around 3,500 years old? It is a 3-string instrument with a plectrum suspended from the neck, it was owned by an Egyptian singer called Har-Mose and was buried alongside him - You can still see the instrument on display at the Archaeological Museum in Cairo, Egypt.",
+ "cover": "summercamp/day_13.png",
+ "completion_text": "What good is a guitar if we don't have a campfire to sing songs around? Try drawing a nice big campfire with some marshmallows for toasting! Maybe a big moon on the horizon too? Be wild, be creative!",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "It's a very nice night! Set the background color to darkblue - **type** `background darkblue`",
+ "solution": "background darkblue"
+ },
+ {
+ "hint": "Set the `stroke` to `0` in a new line.",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Set your guitar `color` to `lightbrown`.",
+ "solution": "color lightbrown"
+ },
+ {
+ "hint": "Move the cursor to place the guitar - **type** `moveTo 120, 250`",
+ "solution": "moveTo 120, 250"
+ },
+ {
+ "hint": "Draw the first part of the body with an ellipse - **type** `ellipse 60, 65`",
+ "solution": "ellipse 60, 65"
+ },
+ {
+ "hint": "Now for the second part of the body, `move` the cursor `70` pixels to the right.",
+ "solution": "move 70"
+ },
+ {
+ "hint": "Draw a `circle` of size `55` for the second part of the guitar.",
+ "solution": "circle 55"
+ },
+ {
+ "hint": "Every guitar needs a neck, move the cursor to place it - **type** `move 20, -10`",
+ "solution": "move 20, -10"
+ },
+ {
+ "hint": "Set the neck `color` to `brown`.",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Draw the neck of the guitar with a rectangle - **type** `rectangle 150, 20`",
+ "solution": "rectangle 150, 20"
+ },
+ {
+ "hint": "Now place the headstock - **type** `move 150, -5`",
+ "solution": "move 150, -5"
+ },
+ {
+ "hint": "Draw the headstock with a `rectangle` of size `40` by `30`.",
+ "solution": "rectangle 40, 30"
+ },
+ {
+ "hint": "Time to place the soundhole in the body - **type** `move -170, 15`",
+ "solution": "move -170, 15"
+ },
+ {
+ "hint": "Set the outline of the soundhole with a `stroke` of `brown` color and size `10`.",
+ "solution": "stroke brown, 10"
+ },
+ {
+ "hint": "Set the soundhole `color` to `black`.",
+ "solution": "color black"
+ },
+ {
+ "hint": "Draw the hole with a `circle` of size `20`.",
+ "solution": "circle 20"
+ },
+ {
+ "hint": "The bridge is the piece that fixes the strings to the body. Let's place it - **type** `move -50, -15`",
+ "solution": "move -50, -15"
+ },
+ {
+ "hint": "Set the outline of the bridge with a `stroke` of `red` color and size `10`.",
+ "solution": "stroke red, 10"
+ },
+ {
+ "hint": "Draw the bridge with a `rectangle` of size `2` by `35`.",
+ "solution": "rectangle 2, 35"
+ },
+ {
+ "hint": "That's a good looking guitar. Time for the strings - **type** `move 0, 9`",
+ "solution": "move 0, 9"
+ },
+ {
+ "hint": "Set the properties of the strings with a `stroke` of `white` color and size `1`.",
+ "solution": "stroke white, 1"
+ },
+ {
+ "hint": "Let's draw the 4 strings with a loop - **type** `for i in [ 1 .. 4 ]`",
+ "solution": "for i in [ 1 .. 4 ]"
+ },
+ {
+ "hint": "Draw a string using a line - **type** `line 250`",
+ "solution": " line 250"
+ },
+ {
+ "hint": "And move the cursor slightly down to place the next ones - **type** `move 0, 4`",
+ "solution": " move 0, 4"
+ },
+ {
+ "hint": "Press **Enter** and **Backspace** to set the `color` to `white` outside the loop",
+ "solution": "color white"
+ },
+ {
+ "hint": "You are ready to play music! Start a new loop - **type** `for i in [ 1 .. 10 ]`",
+ "solution": "for i in [ 1 .. 10 ]"
+ },
+ {
+ "hint": "Select a random location - **type** `moveTo (random 200, 400), (random 100, 400)`",
+ "solution": " moveTo (random 200, 400), (random 100, 400)",
+ "validate": "__ moveTo ?\\( *random +200, +400 *\\), +\\( *random +100, +400 *\\)"
+ },
+ {
+ "hint": "Come on, play some notes! - **copy and paste** `text '♪'`",
+ "solution": " text '♪'"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/hiking.json b/lib/challenges/worlds/summercamp/hiking.json
new file mode 100644
index 000000000..44c30e2ec
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/hiking.json
@@ -0,0 +1,112 @@
+{
+ "id": "hiking",
+ "title": "Hiking Poster",
+ "short_title": "Hiking",
+ "icon_class": "challenge_hiking",
+ "description": "The beautiful Camp Kano mountain range is known for its deep blue colour. The ten mountains in the range always seem to be changing position though, which makes for a dangerous climb. Should campers attempt to climb it? Who knows! But management wants an advertising posts so get to it.",
+ "cover": "summercamp/day_18.png",
+ "completion_text": "Well done, you’ve made a beautiful poster! Now play around with the random values and the text. What else is the poster missing?",
+ "difficulty": 1,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": null,
+ "steps": [
+ {
+ "hint": "Up above the clouds the sky is blue, **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Camp Kano’s mountains are a beautiful shade of dark blue. Set the drawing colour with `color darkblue`.",
+ "solution": "color darkblue"
+ },
+ {
+ "hint": "The mountains also have a nice thick border, give them `stroke white, 10`",
+ "solution": "stroke white, 10"
+ },
+ {
+ "hint": "Lets draw the ten mountains of Camp Kano with a loop! This will be much faster than drawing them individually. **Type** `for i in [ 0 ... 10 ]` to open the loop.",
+ "solution": "for i in [ 0 ... 10 ]"
+ },
+ {
+ "hint": "For every time the loop runs, we want a mountain’s peak to be drawn randomly across the screen. Let’s select an x value with `x = random 0, 500`",
+ "solution": " x = random 0, 500"
+ },
+ {
+ "hint": "However, for the y value, we only want each mountain’s peak to be drawn on the top part of the screen. **Type** `y = random 0, 200`",
+ "solution": " y = random 0, 200"
+ },
+ {
+ "hint": "Now with our x and y values set we can move the cursor to them. **Type** `moveTo x, y`",
+ "solution": " moveTo x, y"
+ },
+ {
+ "hint": "Draw a mountain for every loop with the polygon function. **Type** `polygon 400, 500, -400, 500`",
+ "solution": " polygon 400, 500, -400, 500"
+ },
+ {
+ "hint": "First, get out of the previous for loop’s indent by pressing `BACKSPACE`. Now let’s draw some clouds with `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Clouds are made of small droplets of water vapor, which is see-through! To get a see-through color we use the `opacity` function. **Type** `color (opacity white, .1)`",
+ "solution": "color (opacity white, .1)",
+ "validate": "__color \\(opacity white, .1\\)"
+ },
+ {
+ "hint": "Now let’s make a new loop to draw the cloud puffs. We’ll need a lot, so let’s make this a loop last 300 iterations. **Type** `for j in [ 0 ... 250 ]`.",
+ "solution": "for j in [ 0 ... 250 ]"
+ },
+ {
+ "hint": "We want cloud puffs sprinkled randomly across the screen, so lets choose an x value between 0 and 500 with`x = random 0, 500`",
+ "solution": " x = random 0, 500"
+ },
+ {
+ "hint": "However, for the y value, we only want the cloud puffs to be drawn on the bottom part of the screen. **Type** `y = random 300, 500`",
+ "solution": " y = random 300, 500"
+ },
+ {
+ "hint": "Now with our x and y values set we can move the cursor to them. **Type** `moveTo x, y`",
+ "solution": " moveTo x, y"
+ },
+ {
+ "hint": "Let’s draw the cloud puff—a big transparent `circle` of size `100`",
+ "solution": " circle 100"
+ },
+ {
+ "hint": "Press `BACKSPACE` once to get out of the indented line. Then lets move the cursor into place for some text with `moveTo 250, 350`",
+ "solution": "moveTo 250, 350"
+ },
+ {
+ "hint": "Set the drawing color to red - **type** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Our message should be strong and impactful, so lets set the font to bold with `bold true`",
+ "solution": "bold true"
+ },
+ {
+ "hint": "We also want to style our text with italics, do this with `italic true`",
+ "solution": "italic true"
+ },
+ {
+ "hint": "Finally lets select Bariol at point size 40 with `font 'Bariol', 40`",
+ "solution": "font 'Bariol', 40"
+ },
+ {
+ "hint": "Start your message off with `text 'Hike the Blue Mountains of'`",
+ "solution": "text 'Hike the Blue Mountains of'"
+ },
+ {
+ "hint": "Now for a new line with a big bold finish. **Type** `font 90`",
+ "solution": "font 90"
+ },
+ {
+ "hint": "Lets move the cursor down into place for the final line with `move 0, 90`",
+ "solution": "move 0, 90"
+ },
+ {
+ "hint": "The finishing touch: **type** `text 'Camp Kano!'`",
+ "solution": "text 'Camp Kano!'"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/index.json b/lib/challenges/worlds/summercamp/index.json
new file mode 100644
index 000000000..a54bd7049
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/index.json
@@ -0,0 +1,26 @@
+{
+
+ "challenges": [
+ "./camp_sign",
+ "./campsite",
+ "./tent",
+ "./flag",
+ "./camp_badge",
+ "./campfire",
+ "./ghost",
+ "./backpack",
+ "./compass",
+ "./camera",
+ "./bear",
+ "./flashlight",
+ "./guitar",
+ "./cinema",
+ "./bow_and_arrow",
+ "./table_tennis",
+ "./fishing",
+ "./hiking",
+ "./foraging",
+ "./tree",
+ "./fireworks"
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/table_tennis.json b/lib/challenges/worlds/summercamp/table_tennis.json
new file mode 100644
index 000000000..8e5a1fae0
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/table_tennis.json
@@ -0,0 +1,121 @@
+{
+ "id": "table_tennis",
+ "title": "Table Tennis",
+ "short_title": "Table Tennis",
+ "icon_class": "challenge_tennis",
+ "description": "Table Tennis is a game played with one or two people on each side of a table with the outline of a miniaturized tennis court drawn on it. Games are played to 11 or 21, and upon completion the victor’s paddle is thrown to the ground as they let out their battle cry.",
+ "cover": "summercamp/day_16.png",
+ "completion_text": "Well done! You made a beautiful 3D ping pong table. But there isn’t anyone playing or watching the game! Use your creativity and code to draw some fellow campers enjoying the game.",
+ "difficulty": 2,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Begin by setting your stroke to zero with `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "It's a sunny day! Set the background color to blue - **type** `background blue`",
+ "solution": "background blue"
+ },
+ {
+ "hint": "Set your color to green for the grass. **Type** `color green`.",
+ "solution": "color green"
+ },
+ {
+ "hint": "Move your cursor into place with `moveTo 0, 350`",
+ "solution": "moveTo 0, 350"
+ },
+ {
+ "hint": "Cover the bottom part of the canvas with grass. Draw a `rectangle` of width `500` and height `150`.",
+ "solution": "rectangle 500, 150"
+ },
+ {
+ "hint": "Give your table a solid foundation with some wood legs. Set `color` to `brown`",
+ "solution": "color brown"
+ },
+ {
+ "hint": "Move the cursor into position for the first leg. **Type** `moveTo 20, 310`",
+ "solution": "moveTo 20, 310"
+ },
+ {
+ "hint": "Now draw the first leg. Type `rectangle 15, 150`.",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "Now the second leg. **Type** `moveTo 465, 310`.",
+ "solution": "moveTo 465, 310"
+ },
+ {
+ "hint": "Now **draw** the second leg. Type `rectangle 15, 150`.",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "And the third leg. **Type** `moveTo 40, 250`.",
+ "solution": "moveTo 40, 250"
+ },
+ {
+ "hint": "Now draw the third leg the same size as the others",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "Finally, the fourth leg. **Type** `moveTo 445, 250`.",
+ "solution": "moveTo 445, 250"
+ },
+ {
+ "hint": "Now draw the fourth leg",
+ "solution": "rectangle 15, 150"
+ },
+ {
+ "hint": "Now to put the table on! Set the drawing `color` to `darkgreen`.",
+ "solution": "color darkgreen"
+ },
+ {
+ "hint": "Move your table top into place. Type `moveTo 10, 300`.",
+ "solution": "moveTo 10, 300"
+ },
+ {
+ "hint": "Your table has perspective, to draw it use `polygon 30, -60, 450, -60, 480, 0`",
+ "solution": "polygon 30, -60, 450, -60, 480, 0"
+ },
+ {
+ "hint": "For a cool 3D effect, lighten the drawing color with `color lighten darkgreen, 10`",
+ "solution": "color lighten darkgreen, 10"
+ },
+ {
+ "hint": "**Draw** the side of the table with `rectangle 480, 10`",
+ "solution": "rectangle 480, 10"
+ },
+ {
+ "hint": "For the net, set the drawing `color` to `white`",
+ "solution": "color white"
+ },
+ {
+ "hint": "Move the cursor to **exactly** `moveTo 248, 220`",
+ "solution": "moveTo 248, 220"
+ },
+ {
+ "hint": "Draw the net: a `rectangle` with a width of `4`, and height of `80`",
+ "solution": "rectangle 4, 80"
+ },
+ {
+ "hint": "Give your scene a bouncing ball! Use random to decide what side of the table the ball should be on. **Type** `x = random 40, 460`.",
+ "solution": "x = random 40, 460"
+ },
+ {
+ "hint": "Now we’ll use random to decide how high the ball should be! **Type** `y = random 150, 250`.",
+ "solution": "y = random 150, 250"
+ },
+ {
+ "hint": "Move the cursor to the x and y variables you just made. **Type** `moveTo x, y`.",
+ "solution": "moveTo x, y"
+ },
+ {
+ "hint": "Finally, draw your ball with a circle with radius 7. Type `circle 7`.",
+ "solution": "circle 7"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/tent.json b/lib/challenges/worlds/summercamp/tent.json
new file mode 100644
index 000000000..e0552bc87
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/tent.json
@@ -0,0 +1,73 @@
+{
+ "id": "tent",
+ "title": "Pitch your Tent",
+ "short_title": "Tent",
+ "description": "No campsite is complete without a tent. We’ve got you settled with a simple fly tent, which is usually made with a tarpaulin, rope, and stakes. But with your creative powers you can transform it into whatever you want! \nPeople have lived in tents, teepees, yurts, and wigwams for thousands of years. What features can you add to your tent to keep yourself dry and warm? You can always get inspiration from browsing other creations on Kano World, and searching the Internet for other tent designs.",
+ "icon_class": "challenge_setcamp",
+ "completion_text": "Nice job! You learnt in the last challenge how to draw a tree, why not adding it to the scene? Is that a bird flying in the sky?",
+ "cover": "summercamp/day_3.png",
+ "difficulty": 1,
+ "startAt": 2,
+ "summerCamp": true,
+ "rewards": {
+ "wallpaper": 1
+ },
+ "steps": [
+ {
+ "hint": "Draw a sunny day - **type** `background lightblue`",
+ "solution": "background lightblue"
+ },
+ {
+ "hint": "Set the stroke to 0, to avoid drawing lines - **type** `stroke 0`",
+ "solution": "stroke 0"
+ },
+ {
+ "hint": "Move the cursor where the sun will be - **type** `moveTo 400, 50`",
+ "solution": "moveTo 400, 50"
+ },
+ {
+ "hint": "Set the `color` of the sun `yellow`",
+ "solution": "color yellow"
+ },
+ {
+ "hint": "Draw the sun with a circle - **type** `circle 40`",
+ "solution": "circle 40"
+ },
+ {
+ "hint": "Move the cursor to draw some grass - **type** `moveTo 0, 350`",
+ "solution": "moveTo 0, 350"
+ },
+ {
+ "hint": "Set the `color` of the grass `green`",
+ "solution": "color green"
+ },
+ {
+ "hint": "Draw the grass using a rectangle - **type** `rectangle 500, 150`",
+ "solution": "rectangle 500, 150"
+ },
+ {
+ "hint": "Excellent! Now we are ready to draw the tent - **type** `color red`",
+ "solution": "color red"
+ },
+ {
+ "hint": "Position the cursor on top of the grass - **type** `moveTo 100, 350`",
+ "solution": "moveTo 100, 350"
+ },
+ {
+ "hint": "Draw a triangle using `polygon` - **type** `polygon 150, -200, 300, 0`",
+ "solution": "polygon 150, -200, 300, 0"
+ },
+ {
+ "hint": "We need the entrace now. Set the `color` to `darkred`",
+ "solution": "color darkred"
+ },
+ {
+ "hint": "Place the cursor on the tip of the tent - **type** `moveTo 250, 150`",
+ "solution": "moveTo 250, 150"
+ },
+ {
+ "hint": "Draw the entrance - **type** `polygon 30, 200, -30, 200`",
+ "solution": "polygon 30, 200, -30, 200"
+ }
+ ]
+}
diff --git a/lib/challenges/worlds/summercamp/tree.json b/lib/challenges/worlds/summercamp/tree.json
new file mode 100644
index 000000000..0e190b858
--- /dev/null
+++ b/lib/challenges/worlds/summercamp/tree.json
@@ -0,0 +1,17 @@
+{
+ "id": "tree",
+ "title": "Fractal Tree",
+ "short_title": "Tree",
+ "icon_class": "challenge_tree",
+ "description": "Time to play! Our counselors made you something to play with. A tree which can grow branches that expand outwards, and contract inwards. It can blossom fully, or shrivel to nothing. All with code. Make it yours.",
+ "cover": "summercamp/day_20.png",
+ "completion_text": "Play around with the blue numbers at the top of the code. What do they do? Why do they do that? Shape the tree to your liking. If things mess up, go back to the main menu and start again.",
+ "difficulty": 3,
+ "startAt": 0,
+ "summerCamp": true,
+ "rewards": {
+ "outfit": 1
+ },
+ "code": "# Play with these blue numbers\narmLength = 50\niterations = 9 # Might crash if you go over 15!\ndegreeChange = 20\nhasBlossoms = 3\nblossomSize = 70\nblossomOpacity = .02\n# ^^^ Play with these blue numbers ^^^\n\n###\nThis is a function that draws a tree\nbranch, when it completes, it calls itself\nover and over and over again until all the\ntree’s branches have been drawn!\n###\ndrawBranch = (x, y, branchesLeft, startAngle) ->\n if branchesLeft > 0 \n # Move to where the next branch should\n # be drawn:\n moveTo x, y\n \n # A branch is always blue, but the width\n # depends on how far from the base it is!\n stroke blue, branchesLeft \n \n # A bit of trigonometry here, it’s alright\n # if you don’t understand this! This \n # calculates coordinates for where the \n # branch should be drawn to, and where\n # the next branch should start.\n dx = Math.cos(startAngle) * armLength \n dy = -Math.sin(startAngle) * armLength \n \n # Draw a line to the new coordinates we\n # just calculated\n line dx, dy \n \n # This block of code only executes if\n # the current branch has blossoms. You\n # can change the hasBlossoms variable up\n # top!\n if branchesLeft <= hasBlossoms \n # Set the drawing color to a nice red\n color opacity \"rgb(247, 45, 99)\", blossomOpacity\n # Our blossoms shouldn’t have a stroke\n stroke 0 \n # Draw the blossom! These big blossoms\n # overlap over each other to create a\n # neat effect.\n circle blossomSize\n \n # Starts the next branch on the right\n drawBranch(x + dx, y + dy, branchesLeft - 1, startAngle - Math.PI / 180 * degreeChange) \n # Starts the next branch on the left\n drawBranch(x + dx, y + dy, branchesLeft - 1, startAngle + Math.PI / 180 * degreeChange)\n \n###\nFinally we call our function and see what\nhappens. Play with the numbers at the top of\nthe function and see how they change the tree\n###\ndrawBranch(stage.width * .5, stage.height, iterations, Math.PI / 2, length)",
+ "steps": []
+}
diff --git a/lib/controller/auth-form.js b/lib/controller/auth-form.js
new file mode 100644
index 000000000..b529af58b
--- /dev/null
+++ b/lib/controller/auth-form.js
@@ -0,0 +1,45 @@
+'use strict';
+/*
+ * Login Form Controller
+ *
+ * Opens up the login form.
+ */
+
+var app = require('../app'),
+ tracking = require('../core/tracking');
+
+app.controller('AuthController', ['$scope', '$rootScope', '$element', 'API', 'AUTH', function ($scope, $rootScope, $element, api, auth) {
+ var loginForm = $element.find('kano-login-form')[0],
+ signupForm = $element.find('kano-signup-form')[0];
+
+ $scope.mode = 'login';
+ $scope.toggleMode = function () {
+ $scope.mode = $scope.mode === 'login' ? 'signup' : 'login';
+ };
+
+ $scope.onSuccess = function (ev) {
+ auth.login(ev.details, function (user) {
+ $rootScope.updateUser(user);
+ $rootScope.$apply(function () {
+ $rootScope.auth.closeModal();
+ });
+ });
+ };
+
+ $scope.onSignupSuccess = function (ev) {
+ tracking.dispatchTrackingEvent('signedUpToKanoWorld');
+ $rootScope.updateUser(ev.details.user);
+ };
+
+ $scope.close = function () {
+ $rootScope.auth.closeModal();
+ };
+
+ loginForm.addEventListener('success', $scope.onSuccess);
+ signupForm.addEventListener('success', $scope.onSignupSuccess);
+ signupForm.addEventListener('success', $scope.onSuccess);
+
+ loginForm.addEventListener('signup-click', $scope.$apply.bind($scope, $scope.toggleMode));
+ signupForm.addEventListener('login-click', $scope.$apply.bind($scope, $scope.toggleMode));
+
+}]);
diff --git a/lib/controller/challenge.js b/lib/controller/challenge.js
index 1fdeb7501..fbfcc1d22 100644
--- a/lib/controller/challenge.js
+++ b/lib/controller/challenge.js
@@ -1,109 +1,314 @@
+"use strict";
var app = require('../app'),
api,
- fileUtil = require('../util/file'),
- challenges = require('../challenges/index'),
- challengeAssert = require('../challenges/util/assert'),
+ notify = require('../api/notify'),
language = require('../language/index'),
session = require('../language/session'),
sound = require('../core/sound'),
- analytics = require('../core/analytics');
-
+ tracking = require('../core/tracking'),
+ getValidator = require('../challenges/util/validator'),
+ DEFAULT_SUCCESS_MESSAGE = 'Well done!', //null
+ HINT_HIGHLIGHT_DELAY = 10000,
+ VALIDATE_DELAY,
+ config,
+ timer;
/*
* Challenge Controller
*
* Controller for export modal
*/
-var SUCCESS_MESSAGES = [
- 'Well done!', //null
- 'Nice work! That is one cool flag!', //Japan
- 'Cool beans - enough of flags, let\'s try something else!', //Sweden
- 'You Wizard! The next time I need spooky eyes I know who to call!', //Stare
- 'Nice! Keep up the good work you face drawing genius!', //Smiley
- 'Awesome balloon! Why not change the color before moving on?', //Blue balloon
- 'Nice one! The canvas is 500 wide and 500 high, moving about on it is a skill!', //Stickman
- 'Great! For loops let us repeat bits of code (it saves on all the typing)!', //Shrinking
- 'Random functions give us a random number so we can put things in a surprise spot.', //random
- 'Awesome! Press space and see what happens to the stars...', //Starry
- 'You built an awesome house with code!', //House
- 'Mathematical! Change the numbers and see what happens.', //Dots
- 'Try changing the numbers in the rotate function and see what happens.', //Gradient
- 'Astronomical! Change the colors and see what you can add to make your planet even cooler before hitting share!', //planet
- 'Tasty! Finish it off with more toppings, let your imagination run wild and see what you can make, then share it with the world!', //Pizza
- 'Well done!', //null
- ],
- HINT_HIGHLIGHT_DELAY = 10000,
- VALIDATE_DELAY;
+app.controller('ChallengeController', function ($scope, $routeParams, $window, $timeout, $rootScope, $location, contentService, emailService, socialService) {
+ var win = angular.element($window),
+ hintTimer,
+ worldId;
-app.controller('ChallengeController', function ($scope, $routeParams, $window, $timeout, $rootScope) {
- api = $rootScope.api;
- VALIDATE_DELAY = $rootScope.cfg.OFFLINE ? 1020 : 20;
+ config = $rootScope.cfg;
- var win = angular.element($window),
- hintTimer;
-
- $scope.id = $routeParams.id ? parseInt($routeParams.id, 10) : 1;
- $scope.lastChallengeVisited.set($scope.id);
- $scope.content = challenges[$scope.id - 1];
- $scope.challenge = { code: $scope.content.code };
- $scope.fatherDay = $scope.content.fatherDay;
- $scope.hasNext = challenges.length > $scope.id;
- setStep(0);
- $scope.started = true;
- $scope.animationClass = '';
- analytics.track('Started Challenge ' + $scope.id, {
- category: 'Started Challenge'
- });
+ /**
+ * Returns true if the code compiles
+ * @param {string} code The coffeescript code
+ * @return {boolean} True if the code compiles
+ */
+ function isValidCode(code) {
+ var err,
+ // Create a canvas element
+ canvas = document.createElement('canvas'),
+ ctx;
+ canvas.width = 500;
+ canvas.height = 400;
+
+ // Get the fake drawing context
+ ctx = canvas.getContext('2d');
+ err = language.run(code, {ctx: ctx});
+ canvas = undefined;
+ return (typeof err === 'undefined');
+ }
- $scope.$watch('step', function (step) {
- $scope.hint = $scope.content.steps[step] ? $scope.content.steps[step].hint : null;
- $scope.solution = $scope.getSolution();
- animateAndPlaySound(step);
- });
- /*
+
+ /**
+ * Saves code in the local storage
+ * @param {string} code a string containing the code
+ */
+ function saveCode(code) {
+ localStorage["code_" + $scope.worldId + "_" + $scope.id] = code;
+ }
+
+
+
+ /**
+ * Returns the next challenge in the world if it exists
+ * @return {object} The next Challenge in the world
+ */
+ function getNextChallenge() {
+ var position = $scope.index,
+ challenges = $rootScope.selectedWorld.challenges;
+ return challenges[position];
+ }
+
+
+
+ /**
+ * Asynchronously loads the challenge
+ */
+ function loadChallenge() {
+ contentService.challenge.get($scope.worldId, $scope.id).then(function (challenge) {
+ var lsCode;
+
+ if ($scope.isChallengeLocked(challenge)) {
+ $location.path('/challenges/' + $rootScope.selectedWorld.id);
+ }
+
+ lsCode = localStorage["code_" + $scope.worldId + "_" + $scope.id];
+
+ $scope.lastChallengeVisited.set($scope.worldId, $scope.index); //???
+
+ $scope.content = challenge;
+ $scope.index = challenge.index;
+ $scope.challenge = { code: lsCode || $scope.content.code };
+ $scope.validator = getValidator($scope.content.steps, config.languageSynonyms);
+
+ $scope.next = getNextChallenge();
+
+ setStep(0);
+ $scope.started = true;
+ $scope.animationClass = '';
+
+ $scope.$watch('step', function (step) {
+ $scope.hint = $scope.content.steps[step] ? $scope.content.steps[step].hint : null;
+ $scope.solution = $scope.getSolution();
+ animateAndPlaySound(step);
+ });
+ });
+ }
+
+
+
+ /**
* Initialise controller
*
* @return void
*/
function init() {
+ $scope.nextModal = false;
+ $scope.shareModal = false;
+ $scope.mailTab = false;
+ $scope.loading = false;
+ $scope.unlockedWorld = null;
+ $rootScope.exportModal = false;
+ $rootScope.successful = false;
$scope.closeChallengeComplete();
+ socialService.init();
+ socialService.twitter.share(function (res) {
+ if (res) {
+ $scope.closeShareModal();
+ if (!$scope.nextModal) {
+ $scope.openNextModal();
+ }
+ $scope.$apply();
+ }
+ });
+ api = $rootScope.api;
+ VALIDATE_DELAY = $rootScope.cfg.OFFLINE ? 1020 : 20;
+
+ $scope.id = $routeParams.id;
+ $scope.worldId = $routeParams.world;
+
+ $scope.offline = $rootScope.cfg.OFFLINE;
+
+ if (!$rootScope.worlds) {
+ contentService.world.getAll().then(function (data) {
+ $rootScope.worlds = data;
+ }, function (err) {
+ console.err("Couldn't load worlds " + err);
+ });
+ }
+
+ if (!$rootScope.selectedWorld) {
+ //You're hitting the address directly, and we need to load the world's information
+ contentService.world.get("worlds/" + $scope.worldId).then(function (data) {
+ $scope.challenges = data.challenges;
+ $rootScope.selectedWorld = data;
+ worldId = $rootScope.selectedWorld.id;
+ $rootScope.$broadcast('world-loaded', data);
+ loadChallenge();
+ });
+ } else {
+ loadChallenge();
+ worldId = $rootScope.selectedWorld.id;
+
+ }
- if ($scope.fatherDay && localStorage.fatherDayCode) {
- $scope.challenge.code = localStorage.fatherDayCode;
- $scope.completed = true;
+ if (!$rootScope.inWorldProgress) {
+ $rootScope.inWorldProgress = contentService.progress.get($scope.worldId);
}
}
- /*
- * Share father's day card
- *
- * @return void
+ $rootScope.$watch('successful', function (val) {
+ if (val) {
+ $scope.closeExportModal();
+ if ($scope.next) {
+ $scope.next.locked = $scope.isChallengeLocked($scope.next) ? true : false;
+ }
+ $scope.openShareModal();
+ }
+ });
+
+ $rootScope.openNextModal = function () {
+ if (!$scope.next) {
+ $scope.openFinishedGame();
+ } else {
+ $scope.nextModal = true;
+ }
+ };
+
+ $scope.closeNextModal = function () {
+ $scope.nextModal = false;
+ };
+
+ $scope.openShareModal = function () {
+ if ($rootScope.creation) {
+ $scope.shareModal = true;
+ } else if ($rootScope.successful && !$rootScope.creation) {
+ $scope.openNextModal();
+ }
+ };
+
+ $scope.closeShareModal = function () {
+ $scope.shareModal = false;
+ $scope.closeMailTab();
+ };
+
+ $scope.openExportModal = function () {
+ $rootScope.exportModal = true;
+ };
+
+ $scope.closeExportModal = function () {
+ $rootScope.exportModal = false;
+ };
+
+ $scope.openMailTab = function () {
+ $scope.mailTab = true;
+ };
+
+ $scope.closeMailTab = function () {
+ $scope.mailTab = false;
+ };
+
+ $scope.skipSocialSharing = function () {
+ $scope.closeShareModal();
+ $scope.openNextModal();
+ };
+
+ /**
+ * Goes to the next challenge depending on the world locking strategy
+ * @return {[type]} [description]
*/
- $scope.shareFatherDay = function () {
- var canvas = document.querySelector('canvas'),
- image = canvas.toDataURL('image/png'),
- cover = fileUtil.dataURItoBlob(image),
- attachment = new Blob([ $scope.challenge.code ], { type: 'text/plain' });
-
- cover.filename = 'cover.png';
- attachment.filename = 'code.draw';
-
- api.online.share.post({
- app : 'kano-draw',
- title : 'Father\'s Day Card',
- files : {
- cover : cover,
- attachment : attachment
+ $scope.goToNext = function () {
+ if ($rootScope.selectedWorld.share_strategy && !$rootScope.creation) {
+ $scope.openExportModal();
+ } else {
+ $scope.openNextModal();
+ }
+ };
+
+ $scope.twitterShare = function (creation) {
+ var url = socialService.twitter.build(creation);
+ window.open(url, '_blank', 'height=500,width=500');
+ tracking.dispatchTrackingEvent('worldExternalShare');
+ };
+
+ $scope.facebookShare = function (creation) {
+ var options = {
+ title : creation.title + ' on Make Art',
+ url : creation.url,
+ picture : creation.cover_url,
+ caption : 'Shared by ' + creation.username + ' through ' + creation.world,
+ text : creation.description
+ };
+
+ socialService.facebook.share(options, function (err, res) {
+ if (err) {
+ return err;
}
- })
- .then(function (res) {
- var itemId = res.body.item.id;
- location.href = 'http://fathersday.kano.me/card/' + itemId + '/edit';
+ if (res) {
+ tracking.dispatchTrackingEvent('worldExternalShare');
+ $scope.closeShareModal();
+ if (!$scope.nextModal) {
+ $scope.openNextModal();
+ }
+ $scope.$apply();
+ }
});
};
+ $scope.sendMail = function (creation) {
+ var emailObj;
+ if (!creation.email) {
+ return false;
+ } else {
+ $scope.loading = true;
+ if (emailService.validate(creation.email)) {
+ creation.user_email = $rootScope.user.email;
+ creation.type = 'art-share';
+ emailObj = emailService.buildObject(creation);
+
+ emailService.send(emailObj, function (response) {
+ if (response.status !== 200) {
+ $scope.loading = false;
+ return notify.failure();
+ }
+ tracking.dispatchTrackingEvent('worldExternalShare');
+ $scope.loading = false;
+ emailService.reset(creation);
+ $scope.closeMailTab();
+ $scope.closeShareModal();
+
+ if (!$scope.nextModal) {
+ $scope.openNextModal();
+ }
+
+ $scope.$apply();
+
+ return notify.success();
+ }, function (error) {
+ if (error) {
+ emailService.reset(creation);
+ $scope.closeMailTab();
+ $scope.loading = false;
+ return notify.failure();
+ }
+ });
+ } else {
+ return notify.failure();
+ }
+ }
+ };
+
+ $scope.isChallengeLocked = contentService.challenge.isLocked;
+
/*
* Get back to first step
*
@@ -120,7 +325,9 @@ app.controller('ChallengeController', function ($scope, $routeParams, $window, $
* @return void
*/
$scope.toggleSolution = function () {
- if (hintTimer) { $timeout.cancel(hintTimer); }
+ if (hintTimer) {
+ $timeout.cancel(hintTimer);
+ }
$scope.highlightHelp = false;
$scope.showSolution = !$scope.showSolution;
};
@@ -131,49 +338,48 @@ app.controller('ChallengeController', function ($scope, $routeParams, $window, $
* @return void
*/
$scope.validate = function () {
+ if ($rootScope.cfg.OFFLINE) {
+ clearTimeout(timer);
+ }
+
// Next tick...
- setTimeout(function () {
+ timer = setTimeout(function () {
var code = language.strip($scope.challenge.code),
step = 0,
steps = $scope.content.steps,
- history = session.steps || [],
- assertObj = challengeAssert(code, history),
- validateStep, i, finished;
+ finished,
+ report;
- for (i = $scope.step; i < steps.length; i += 1) {
- validateStep = steps[i].validate;
+ report = $scope.validator.validate(code, steps);
- if (validateStep.call(assertObj, code, history)) {
- step = i + 1;
- }
+ $scope.challengeReport = report;
+
+ step = (report.lastValidStep !== null) ? report.lastValidStep + 1 : 0;
+
+ if (report.complete || (step >= steps.length && isValidCode(code))) {
+ //we consider complete the code of a user who reached the last step and broke previous ones
+ //as long as the code compiles
+ finished = true;
}
if (step > $scope.step) {
+ //we have a new valid step
setStep(step);
- }
-
- if ($scope.step > steps.length - 1) {
- finished = true;
+ saveCode(code);
}
if (session.steps && finished && !$scope.completed) {
- analytics.track('Completed Challenge ' + $scope.id, {
- category: 'Completed Challenge'
- });
+ tracking.dispatchTrackingEvent('worldTutorialCompleted');
+ tracking.trackUserProgress($scope.id);
$scope.completed = true;
- if ($scope.fatherDay) {
- localStorage.fatherDayComplete = true;
- localStorage.fatherDayCode = $scope.challenge.code;
- } else {
- $rootScope.updateProgress($scope.id + 1);
- }
-
- $rootScope.updateProgress($scope.id + 1);
+ $rootScope.updateProgress(worldId, $scope.index + 1);
api.progress.trackLinesOfCode($scope.challenge.code.split('\n').length);
+
}
$scope.$apply();
+
}, VALIDATE_DELAY);
};
@@ -184,13 +390,16 @@ app.controller('ChallengeController', function ($scope, $routeParams, $window, $
* @return void
*/
function setStep(index) {
- if (hintTimer) { $timeout.cancel(hintTimer); }
+ if (hintTimer) {
+ $timeout.cancel(hintTimer);
+ }
$scope.highlightHelp = false;
$scope.step = index;
$scope.showSolution = false;
- hintTimer = $timeout(function() {
+
+ hintTimer = $timeout(function () {
$scope.highlightHelp = true;
}, HINT_HIGHLIGHT_DELAY);
}
@@ -201,11 +410,20 @@ app.controller('ChallengeController', function ($scope, $routeParams, $window, $
* @return {String}
*/
$scope.getSolution = function () {
- var content = $scope.content.steps[$scope.step];
+ var steps = $scope.content.steps,
+ i,
+ solution = "";
- if (!content) { return null; }
- return content.solution;
+ if (!steps) {
+ return null;
+ }
+ for (i = 0; i <= $scope.step; i++) {
+ if (steps[i]) {
+ solution += steps[i].solution + "\n";
+ }
+ }
+ return solution;
};
/*
@@ -216,12 +434,12 @@ app.controller('ChallengeController', function ($scope, $routeParams, $window, $
$scope.successMessage = function () {
//If we are executing draw on Pi we want to show the xp gained
var xpGain = parseInt($scope.xpGain, 10),
- successMsg = SUCCESS_MESSAGES[$scope.id % SUCCESS_MESSAGES.length],
+ successMsg = $scope.content.completion_text || DEFAULT_SUCCESS_MESSAGE,
xpMessage = xpGain ? ' You earned ' + $scope.xpGain + 'xp!' : '',
onlineMessage = successMsg,
offlineMessage = onlineMessage + xpMessage;
- return $rootScope.cfg.OFFLINE ? offlineMessage : onlineMessage;
+ return $rootScope.cfg.OFFLINE ? offlineMessage : onlineMessage;
};
/*
@@ -286,7 +504,22 @@ app.controller('ChallengeController', function ($scope, $routeParams, $window, $
*
* @return void
*/
- $scope.openFinishedGame = function() {
+ $scope.openFinishedGame = function () {
+ var arr = [],
+ progressGroups = $rootScope.progress.groups;
+
+ $rootScope.worlds.forEach(function (world) {
+ // Check for worlds that are not locked and not completed
+ if (!contentService.world.isLocked(world) && !contentService.world.isCompleted(world)) {
+ if (!progressGroups.hasOwnProperty(world.id)) {
+ // If the user has no progress for that world, it is new & unlocked
+ arr.push(world);
+ }
+ }
+ });
+
+ // Return the first world in the array as the unlocked world
+ $scope.unlockedWorld = arr.length ? arr[0] : null;
$scope.gameCompleteOpen = true;
};
@@ -295,7 +528,7 @@ app.controller('ChallengeController', function ($scope, $routeParams, $window, $
*
* @return void
*/
- $scope.closeFinishedGame = function() {
+ $scope.closeFinishedGame = function () {
$scope.challengeCompleteOpen = false;
};
diff --git a/lib/controller/challenges.js b/lib/controller/challenges.js
index 1bc71455f..f3247f8d5 100644
--- a/lib/controller/challenges.js
+++ b/lib/controller/challenges.js
@@ -1,7 +1,6 @@
+"use strict";
var api,
- app = require('../app'),
- challenges = require('../challenges/index'),
- analytics = require('../core/analytics');
+ app = require('../app');
/*
* Challenges Controller
@@ -9,17 +8,16 @@ var api,
* Controller for challenges selection screen
*/
-app.controller('ChallengesController', function ($scope, $rootScope) {
- api = $rootScope.api;
-
- // Assign index to each challenge
- challenges.forEach(function (challenge, i) {
- challenge.index = i;
- });
+app.controller('ChallengesController', function ($scope, $rootScope, $routeParams, $location, $http, contentService) {
+ var config = window.CONFIG,
+ domain = window.location.hostname,
+ domainCfg = (config.DOMAIN_CFG) ? config.DOMAIN_CFG[domain] : null,
+ routeWorldId = $routeParams.world,
+ routeWorld;
- $scope.challenges = challenges.filter(function (challenge) {
- return !challenge.fatherDay;
- });
+ api = $rootScope.api;
+ $scope.formData = {};
+ $rootScope.inWorldProgress = { challengeNo: 1 };
init();
@@ -29,24 +27,88 @@ app.controller('ChallengesController', function ($scope, $rootScope) {
* @return void
*/
function init() {
- api.online.share.list({}, { app_name: 'kano-draw', limit: 19 })
- .then(function (res) {
- // Next tick..
- setTimeout(function () {
- $scope.shares = res.body.entries;
- $scope.$apply();
- });
+ $scope.certModal = false;
+ contentService.world.getAll().then(function gotWorlds(data) {
+ $rootScope.worlds = data;
+ if (routeWorldId) {
+ //a world has been selected through the URL
+ $rootScope.worlds.forEach(function (world) {
+ if (world.id === routeWorldId) {
+ routeWorld = world;
+ }
+ });
+ }
+ if (routeWorld && !$scope.isWorldLocked(routeWorld)) {
+ $scope.loadWorld(routeWorld);
+ } else {
+ //don't load a world automatically
+ $location.path('/challenges');
+ $scope.unloadWorld();
+ }
+ }, function errorWorlds(err) {
+ //we might want in the future to load something offline
+ console.err("Couldn't load worlds " + err);
+
});
+
+
+ if (!(domainCfg && domainCfg.hideShares)) {
+ api.online.share.list({}, { app_name: 'kano-draw', limit: 19 })
+ .then(function (res) {
+ // Next tick..
+ setTimeout(function () {
+ $scope.shares = res.body.entries;
+ $scope.$apply();
+ });
+ });
+ }
}
+ /**
+ * Loads all the information for a world
+ * @param {object} world the object that contains the world information
+ */
+ $scope.loadWorld = function (world) {
+ var worldPath = "worlds/" + world.id;
+ if (!$scope.isWorldLocked(world)) {
+ contentService.world.get(worldPath).then(function (data) {
+ $scope.challenges = data.challenges;
+ $rootScope.selectedWorld = data;
+ $rootScope.selectedWorldClass = $rootScope.selectedWorld.css_class;
+ $rootScope.inWorldProgress = contentService.progress.get($rootScope.selectedWorld.id);
+ $rootScope.$broadcast('world-loaded', data);
+ });
+ }
+ };
+
+ $scope.unloadWorld = function () {
+ $rootScope.selectedWorld = undefined;
+ $rootScope.selectedWorldClass = undefined;
+ $rootScope.inWorldProgress = {challengeNo: 1};
+ $location.path('/challenges');
+ };
+
/*
- * Returns true if challenge is disabled
+ * Select a challenge and open challenge modal
*
- * @param {Number} index
- * @return {Boolean}
+ * @param {Object} challenge
+ * @return void
+ */
+ $scope.selectChallenge = function (challenge) {
+ if ($scope.isLocked(challenge)) {
+ return;
+ }
+
+ $scope.selectedChallenge = challenge;
+ };
+
+ /*
+ * Deselect a challenge and close challenge modal
+ *
+ * @return void
*/
- $scope.isCompleted = function (index) {
- return index + 1 < $rootScope.progress.challengeNo;
+ $scope.deselectedChallenge = function () {
+ $scope.selectedChallenge = null;
};
/*
@@ -62,56 +124,137 @@ app.controller('ChallengesController', function ($scope, $rootScope) {
};
/*
- * Returns true if challenge is locked
- *
- * @param {Number} index
- * @return {Boolean}
+ * Accept user email for Kano updates
+ */
+ $scope.submit = function () {
+ var data;
+ if (!validateEmail($scope.formData.email)) {
+ $scope.success = '';
+ $scope.error = 'Please enter a valid email address!';
+ return;
+ }
+ $scope.error = '';
+ data = $rootScope.selectedWorld.updateForm.field + '=' + $scope.formData.email;
+
+ postEmail(data, formNotification, formNotification);
+ };
+
+ function formNotification() {
+ $scope.error = '';
+ $scope.success = 'Thank you for signing up!';
+ $scope.formData.email = '';
+ setTimeout(function () {
+ $scope.success = '';
+ $scope.$apply();
+ }, 5000);
+ }
+
+ function postEmail(data, successCB, errorCB) {
+ var req = {
+ method : 'POST',
+ url : $rootScope.selectedWorld.updateForm.url,
+ headers : {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ data : data
+ };
+
+ $http(req).then(successCB, errorCB);
+ }
+
+ function validateEmail(email) {
+ var regExp = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
+ return regExp.test(email);
+ }
+
+
+ /**
+ * Returns the URL for a certificate for a specific user
+ * @return {string} a url
*/
- $scope.isLocked = function (index) {
- return index >= $rootScope.progress.challengeNo;
+ $scope.getCertificateUrl = function () {
+ var url = 'http://certificate.kano.me/' + $rootScope.selectedWorld.id + '/?name=';
+ if ($rootScope.user) {
+ url += $rootScope.user.username;
+ }
+
+ if (true && $scope.isCertAchieved($rootScope.selectedWorld)) {
+ return url;
+ }
+
+ return '#0';
+ };
+
+ $scope.checkCertOnPi = function (e) {
+ if ($rootScope.cfg.OFFLINE) {
+ e.preventDefault();
+ $scope.toggleCertModal();
+ }
+ };
+
+ $scope.toggleCertModal = function () {
+ if ($scope.certModal) {
+ $scope.certModal = false;
+ } else {
+ $scope.certModal = true;
+ }
};
+ /**
+ * Returns true if the world is visible.
+ * @param {object} world The world
+ * @return {Boolean} true if the world is completed
+ */
+ $scope.isWorldInIndex = contentService.world.isVisible;
+
+ /**
+ * Returns true if the world is completed.
+ * @param {object} world The world
+ * @return {Boolean} true if the world is completed
+ */
+ $scope.isWorldCompleted = contentService.world.isCompleted;
+
+ /**
+ *Returns true if a world is locked;
+ */
+ $scope.isWorldLocked = contentService.world.isLocked;
+
/*
- * Returns true if challenge is last unlocked
+ * Returns true if world is current.
+ * @param {object} world
+ * @return {Boolean}
+ */
+ $scope.isWorldCurrent = contentService.world.isCurrent;
+
+ /*
+ * Returns true if challenge is locked
*
- * @param {Number} index
+ * @param {object} the challenge item
* @return {Boolean}
*/
- $scope.isCurrent = function (index) {
- return index + 1 === $rootScope.progress.challengeNo;
- };
+ $scope.isLocked = contentService.challenge.isLocked;
/*
- * Returns true if all challenges have been unlocked
+ * Returns true if challenge is disabled
*
* @param {Number} index
* @return {Boolean}
*/
- $scope.isLastUnlocked = function (index) {
- return index === $rootScope.progress.challengeNo;
- };
+ $scope.isCompleted = contentService.challenge.isCompleted;
/*
- * Select a challenge and open challenge modal
+ * Returns true if challenge is last unlocked
*
- * @param {Object} challenge
- * @return void
+ * @param {Number} index
+ * @return {Boolean}
*/
- $scope.selectChallenge = function (challenge) {
- if ($scope.isLocked(challenge.index)) { return; }
-
- analytics.track('Viewed Challenge ' + (challenge.index + 1), {
- category : 'Viewed Challenge'
- });
- $scope.selectedChallenge = challenge;
- };
+ $scope.isCurrent = contentService.challenge.isCurrent;
/*
- * Deselect a challenge and close challenge modal
+ * Returns true if a certificate is achieved
*
- * @return void
+ * @param {Object} world
+ * @return {Boolean}
*/
- $scope.deselectedChallenge = function () {
- $scope.selectedChallenge = null;
- };
+ $scope.isCertAchieved = contentService.world.isCertAchieved;
});
diff --git a/lib/controller/feedback.js b/lib/controller/feedback.js
new file mode 100644
index 000000000..0d564a22c
--- /dev/null
+++ b/lib/controller/feedback.js
@@ -0,0 +1,87 @@
+'use strict';
+/*
+ * Feedback Screen Controller
+ *
+ * Opens up when on main route
+ */
+
+var api,
+ app = require('../app'),
+ _ = require('lodash'),
+ ERROR_MSG = 'We are sorry but an error occured.';
+
+app.controller('FeedbackController', function ($scope, $rootScope, $routeParams) {
+ api = $rootScope.api;
+ $scope.loading = false;
+ $scope.questions = null;
+ $scope.error = '';
+ $scope.feedback = {
+ rating: 'rating-1'
+ };
+
+ if ($routeParams.world) {
+ $scope.feedback.world = $routeParams.world;
+ }
+
+ if ($routeParams.id) {
+ $scope.feedback.challenge = $routeParams.id;
+ }
+
+ api.questions.list({})
+ .then(function (res) {
+ if (res.body.questions) {
+ $scope.questions = _.filter(res.body.questions, { tags: ['feedback', 'make-art'] });
+ $scope.$apply();
+ }
+ });
+
+ $scope.sendFeedback = function () {
+ var answer,
+ answers = [],
+ answerKeys = Object.keys($scope.feedback);
+
+ $scope.loading = true;
+
+ _.each(answerKeys, function (key) {
+ answer = _.filter($scope.questions, { tags: [key] });
+ answer = {
+ question_id : answer[0].id,
+ tags : answer[0].tags,
+ text : $scope.feedback[key]
+ };
+ answers.push(answer);
+ });
+
+ if (!answers.length) {
+ $scope.error = ERROR_MSG;
+ return;
+ }
+
+ api.questions.respond({
+ username: $rootScope.user ? $rootScope.user.username : $rootScope.cfg.UNKNOWN_USER,
+ email: $rootScope.user ? $rootScope.user.email : $rootScope.cfg.UNKNOWN_USER + '@kano.me',
+ answers: answers
+ })
+ .then(function () {
+ handleClick();
+ }, function () {
+ $scope.loading = false;
+ $scope.error = ERROR_MSG;
+ });
+ };
+
+ function handleClick() {
+ $scope.feedback.thought = '';
+ document.querySelector('.form-message').classList.remove('hide');
+ document.querySelector('.form-container').classList.add('hide');
+ document.querySelector('.feedback-buttons').classList.add('hide');
+
+ setTimeout(function () {
+ $rootScope.feedback.closeModal();
+ }, 2500);
+ }
+
+ $scope.closeModal = function () {
+ $rootScope.feedback.closeModal();
+ };
+});
diff --git a/lib/controller/main.js b/lib/controller/main.js
index 0b5e30c7a..b4c995d42 100644
--- a/lib/controller/main.js
+++ b/lib/controller/main.js
@@ -6,6 +6,11 @@
var app = require('../app');
-app.controller('MainController', function () {
- // ...
-});
+app.controller('MainController', ['$scope', '$rootScope', '$location', function ($scope, $rootScope, $location) {
+ var path = $location.path(),
+ isRoot = path === '/' || path === '/challenges';
+ if (isRoot) {
+ $location.path('/challenges');
+ $rootScope.splash.open();
+ }
+}]);
diff --git a/lib/controller/promo-popup.js b/lib/controller/promo-popup.js
index d8dc878e7..1eb59c570 100644
--- a/lib/controller/promo-popup.js
+++ b/lib/controller/promo-popup.js
@@ -38,41 +38,66 @@ app.controller('PromoPopupController', function ($scope, $rootScope) {
'RPI/A' : 'pi-1',
'RPI/A+' : 'pi-1'
},
+ init,
+ loadedProfile,
+ loadedWorld;
-
+ $scope.isPopupOpen = false; //this drives the opening of the promo popup
/**
* Initialises the promo popup
*/
init = function () {
-
var lsItemName = 'promoPopupOpen',
promoPopupOpen = localStorage.getItem(lsItemName),
day = new Date(),
ts = day.getYear() + "-" + day.getMonth() + "-" + day.getDate(),
profile = ($rootScope.user) ? $rootScope.user.profile : undefined,
pi_v,
- cfgKey = "none"; //the user doesn't have a kit;
- if (profile) {
- pi_v = (profile.kano_kit) ? profile.kano_kit.pi_v : undefined;
- cfgKey = (keyMapping[pi_v]) ? keyMapping[pi_v] : cfgKey;
- }
- $scope.cfg = cfg[cfgKey];
+ cfgKey = "none", //the user doesn't have a kit;
+ worldId,
+ promoIndex,
+ challengeIndex;
+
+ loadedWorld = !!$rootScope.selectedWorld;
+
+ if (loadedWorld && loadedProfile) {
+ worldId = $rootScope.selectedWorld.id,
+ promoIndex = $rootScope.selectedWorld.sales_popup_after,
+ challengeIndex = $rootScope.progress.groups[worldId] ? $rootScope.progress.groups[worldId].challengeNo : 1;
+
+ if (profile) {
+ pi_v = (profile.kano_kit) ? profile.kano_kit.pi_v : undefined;
+ cfgKey = (keyMapping[pi_v]) ? keyMapping[pi_v] : cfgKey;
+ }
+ $scope.cfg = cfg[cfgKey];
- // it opens only once a day
- if (false && promoPopupOpen !== ts) {
- $scope.isPopupOpen = true;
- localStorage.setItem(lsItemName, ts);
+ // it opens only once a day
+ if (promoPopupOpen !== ts && challengeIndex > promoIndex) {
+ $scope.isPopupOpen = true;
+ localStorage.setItem(lsItemName, ts);
+ }
}
};
- $scope.isPopupOpen = false; //this drives the opening of the promo popup
- $rootScope.$on('user-profile-loaded', init);
+ $rootScope.$on('user-profile-loaded', function () {
+ loadedProfile = true;
+ init();
+ });
+
+ $rootScope.$watch('selectedWorld', function () {
+ if (!loadedWorld && $rootScope.selectedWorld) {
+ loadedWorld = true;
+ init();
+ }
+ });
+
setTimeout(function () {
//We've waited long enough!
if (!$rootScope.user) {
+ loadedProfile = true;
init();
}
}, 1000);
diff --git a/lib/controller/share.js b/lib/controller/share.js
index adba005a7..628a30765 100644
--- a/lib/controller/share.js
+++ b/lib/controller/share.js
@@ -14,7 +14,7 @@ app.controller('ShareController', function ($scope, $routeParams, $http, $locati
$scope.id = $routeParams.id || null;
// Get share by id given in route
- api.share.get.byId({ id: $scope.id })
+ api.online.share.get.byId({ id: $scope.id })
.then(function (res) {
$scope.item = res.body.item;
@@ -54,4 +54,4 @@ app.controller('ShareController', function ($scope, $routeParams, $http, $locati
$scope.$apply();
}, 1);
};
-});
\ No newline at end of file
+});
diff --git a/lib/controller/splash.js b/lib/controller/splash.js
index ec7e63d9b..82eb54919 100644
--- a/lib/controller/splash.js
+++ b/lib/controller/splash.js
@@ -6,6 +6,12 @@
var app = require('../app');
-app.controller('SplashController', function ($scope, $location) {
- $scope.isSplashOpen = $location.path() === '/';
-});
\ No newline at end of file
+app.controller('SplashController', ['$scope', '$rootScope', '$location', function ($scope, $rootScope, $location) {
+ var path = $location.path(),
+ isRoot = path === '/' || path === '/challenges';
+ if (isRoot) {
+ $rootScope.splash.open();
+ } else {
+ $rootScope.splash.close();
+ }
+}]);
diff --git a/lib/controller/summer-camp-challenge.js b/lib/controller/summer-camp-challenge.js
deleted file mode 100644
index be91ee98b..000000000
--- a/lib/controller/summer-camp-challenge.js
+++ /dev/null
@@ -1,313 +0,0 @@
-var app = require('../app'),
- api,
- fileUtil = require('../util/file'),
- challenges = require('../challenges/summer_camp/index'),
- challengeAssert = require('../challenges/util/assert'),
- language = require('../language/index'),
- session = require('../language/session'),
- sound = require('../core/sound'),
- analytics = require('../core/analytics'),
- moment = require('moment');
-
-/*
- * Challenge Controller
- *
- * Controller for export modal
- */
-
-var DEF_SUCCESS_MESSAGE = 'Well done!',
- HINT_HIGHLIGHT_DELAY = 10000,
- VALIDATE_DELAY,
- LOCAL_STORAGE_CODE_PREFIX = 'summerChallengeCode';
-
-app.controller('SummerCampChallengeController', function ($scope, $routeParams, $window, $timeout, $rootScope, $location) {
- api = $rootScope.api;
- VALIDATE_DELAY = $rootScope.cfg.OFFLINE ? 1020 : 20;
-
- var win = angular.element($window),
- hintTimer,
- summerCampGroup = $rootScope.progress.groups.summercamp,
- progressNo = summerCampGroup ? summerCampGroup.challengeNo : 1,
- now = moment().date(),
- startDate = moment('10-08-2015 06:00:00', 'DD-MM-YYYY HH:mm:ss').date(),
- diff = now - startDate;
-
- $scope.id = $routeParams.id ? parseInt($routeParams.id, 10) : 1;
- $rootScope.id = $scope.id;
- $rootScope.progessNo = progressNo;
-
- // Check if the difference in the route params and the current day of summercamp is greater than 1
- // Check if the id in the route params is either greater than or equal to the progress challenge number.
- // If both are true, then redirect the user to the summercamp calendar
- // if (!config.TEST_MODE && ($scope.id - diff) > 1 && $scope.id >= progressNo) {
- // $location.path('/summercamp');
- // }
- // else {
- // $scope.lastChallengeVisited.set($scope.id, 'summercamp');
- // }
-
- $scope.lastChallengeVisited.set($scope.id, 'summercamp');
- $scope.content = challenges[$scope.id - 1];
- $scope.challenge = { code: $scope.content.code };
- $scope.hasNext = challenges.length > $scope.id;
- $scope.openModal = null;
-
- setStep(0);
- $scope.started = true;
- $scope.animationClass = '';
- analytics.track('Started Challenge ' + $scope.id, {
- category: 'Started Summer Camp Challenge'
- });
-
-
- $scope.$watch('step', function (step) {
- $scope.hint = $scope.content.steps[step] ? $scope.content.steps[step].hint : null;
- $scope.solution = $scope.getSolution();
- animateAndPlaySound(step);
- });
-
- $rootScope.$watch('successful', function (msg) {
- if (msg) {
- if (!$rootScope.progessNo || $rootScope.id >= $rootScope.progessNo) {
- $rootScope.updateProgress($rootScope.id + 1, 'summercamp');
- }
- $rootScope.successful = false;
- $location.path('/summercamp');
- }
- });
-
- /*
- * Initialise controller
- *
- * @return void
- */
- function init() {
- $scope.challenge.code = $scope.challenge.code || retrieveCode($scope.id - 1);
- $scope.validate(true);
- $scope.closeChallengeComplete();
- $scope.completed = $scope.id < $rootScope.progressNo;
- }
-
- /*
- * Get back to first step
- *
- * @return void
- */
- $scope.restart = function () {
- var localStorageKey = LOCAL_STORAGE_CODE_PREFIX + ($scope.id - 1);
- if (localStorage[localStorageKey]) {
- localStorage.removeItem(localStorageKey);
- }
- $scope.step = 0;
- $scope.completed = false;
- };
-
-
- /*
- * Show / Hide solution
- *
- * @return void
- */
- $scope.toggleSolution = function () {
- if (hintTimer) { $timeout.cancel(hintTimer); }
- $scope.highlightHelp = false;
- $scope.showSolution = !$scope.showSolution;
- };
-
- /**
- *
- * Validate code to determine success of challenge
- * @param {boolean} noTracking Doesn't track completion if true (needed when loading from local storage)
- * @return void
- */
- $scope.validate = function (noTracking) {
- // Next tick...
- setTimeout(function () {
- var code = language.strip($scope.challenge.code),
- step = 0,
- steps = $scope.content.steps,
- history = session.steps || [],
- assertObj = challengeAssert(code, history),
- validateStep, i, finished;
- storeCode($scope.id - 1 );
- for (i = $scope.step; i < steps.length; i += 1) {
- validateStep = steps[i].validate;
-
- if (validateStep.call(assertObj, code, history)) {
- step = i + 1;
- }
- }
-
- if (step > $scope.step) {
- setStep(step);
- }
-
- if ($scope.step > steps.length - 1) {
- finished = true;
- }
-
- if (session.steps && finished && !$scope.completed) {
- $scope.completed = true;
-
- if (!noTracking) {
- analytics.track('Completed Challenge ' + $scope.id, {
- category: 'Completed Summer Camp Challenge'
- });
-
- api.progress.trackLinesOfCode($scope.challenge.code.split('\n').length);
- }
-
- if ($scope.challengeNo === 21) {
- $scope.openFeedback();
- }
- }
-
- $scope.$apply();
- }, VALIDATE_DELAY);
- };
-
- $rootScope.shareModal = false;
-
-
- function storeCode (index) {
- var key = LOCAL_STORAGE_CODE_PREFIX + index;
- if (localStorage[key]) {
- localStorage.removeItem(key);
- }
- localStorage[key] = $scope.challenge.code;
- }
-
- function retrieveCode (index) {
- var key = LOCAL_STORAGE_CODE_PREFIX + index;
- return localStorage[key] || '';
- }
-
- //opens a modal
- $scope.setOpenModal = function () {
- $rootScope.shareModal = true;
- };
-
- /*
- * Set current challenge step
- *
- * @param {Number} index
- * @return void
- */
- function setStep(index) {
- if (hintTimer) { $timeout.cancel(hintTimer); }
-
- $scope.highlightHelp = false;
-
- $scope.step = index;
- $scope.showSolution = false;
- hintTimer = $timeout(function() {
- $scope.highlightHelp = true;
- }, HINT_HIGHLIGHT_DELAY);
- }
-
- /*
- * Get current step solution
- *
- * @return {String}
- */
- $scope.getSolution = function () {
- var content = $scope.content.steps[$scope.step];
-
- if (!content) { return null; }
-
- return content.solution;
- };
-
- /*
- * Show success message
- *
- * @return void
- */
- $scope.successMessage = function () {
- //If we are executing draw on Pi we want to show the xp gained
- var xpGain = parseInt($scope.xpGain, 10),
- successMsg = $scope.content.completion_text || DEF_SUCCESS_MESSAGE,
- xpMessage = xpGain ? ' You earned ' + $scope.xpGain + 'xp!' : '',
- onlineMessage = successMsg,
- offlineMessage = onlineMessage + xpMessage;
-
- return $rootScope.cfg.OFFLINE ? offlineMessage : onlineMessage;
- };
-
- /*
- * Start challenge after reading the intro
- *
- * @return void
- */
- $scope.start = function () {
- $scope.started = true;
- };
-
- /*
- * Show complete challenge panel
- *
- * @return void
- */
- $scope.challengeComplete = function () {
- $scope.isChallengeCompleteOpen = true;
- };
-
- /*
- * Hide complete challenge panel
- *
- * @return void
- */
- $scope.closeChallengeComplete = function () {
- $scope.isChallengeCompleteOpen = false;
- };
-
- // Listen for key press
- win.bind('keydown', function (e) {
- if (e.keyCode === 27) { // ESC
- $scope.$apply();
- }
- });
-
- /*
- * Animate progress circle and play sound
- *
- * @return void
- */
- function animateAndPlaySound(step) {
- if (step) {
- $scope.animationClass = 'animate-pulse';
-
- if (step < $scope.content.steps.length) {
- sound.play('pop');
- } else {
- sound.play('success');
- }
-
- $timeout(resetAnimation, 500);
- }
-
- function resetAnimation() {
- $scope.animationClass = '';
- }
- }
-
- /*
- * Close game completion modal
- *
- * @return void
- */
- $scope.openFinishedGame = function() {
- $scope.gameCompleteOpen = true;
- };
-
- /*
- * Close game completion modal
- *
- * @return void
- */
- $scope.closeFinishedGame = function() {
- $scope.challengeCompleteOpen = false;
- };
-
- init();
-});
diff --git a/lib/controller/summer-camp-leaderboard.js b/lib/controller/summer-camp-leaderboard.js
deleted file mode 100644
index 2ccb24bf8..000000000
--- a/lib/controller/summer-camp-leaderboard.js
+++ /dev/null
@@ -1,29 +0,0 @@
-var app = require('../app'),
- api;
-
-app.controller('SummerCampLeaderboardController', function ($scope, $rootScope) {
- api = $rootScope.api;
- // setTimeout needed because $rootScope.user not populated in time
- setTimeout(function() {
- var leaderboardFn = api.summercamp.getLeaderboard,
- username;
-
- if ($rootScope.user) {
- username = $rootScope.user.username;
- leaderboardFn = api.summercamp.getUserLeaderboard;
- }
-
- leaderboardFn({
- username: username
- })
- .then(function (res) {
- $scope.leaderboard_entries = res.body;
- $scope.$apply();
- }, function (res) {
- $scope.showError(res.body);
- })
- .catch(function (err) {
- throw err;
- });
- }, 500);
-});
diff --git a/lib/controller/summer-camp.js b/lib/controller/summer-camp.js
deleted file mode 100644
index eca83a6e3..000000000
--- a/lib/controller/summer-camp.js
+++ /dev/null
@@ -1,397 +0,0 @@
-var app = require('../app'),
- challenges = require('../challenges/summer_camp/index'),
- api,
- social = require('../core/social'),
- moment = require('moment'),
- notify = require('../api/notify'),
- countDownInterval,
- startDate = moment('10-08-2015 06:00:00', 'DD-MM-YYYY HH:mm:ss'),
- analytics = require('../core/analytics');
-
-/*
- * Summer Challenges Controller
- *
- * Controller for summer challenges selection screen
- */
-
-app.controller('SummerCampController', function ($interval, $timeout, $scope, $rootScope, $location, emailService) {
- api = $rootScope.api;
-
- if ($rootScope.cfg.TEST_MODE) {
- startDate = moment('01-07-2015 06:00:00', 'DD-MM-YYYY HH:mm:ss');
- }
-
- $scope.feedbackOpen = false;
-
- $scope.openFeedback = function(){
- $scope.feedbackOpen = true;
- $scope.$apply();
- };
-
- $scope.closeFeedback = function(){
- $scope.feedbackOpen = false;
- $scope.$apply();
- };
-
- // Assign index to each challenge
- challenges.forEach(function (challenge, i) {
- challenge.index = i;
- });
-
- $scope.summerCampChallenges = challenges;
- $scope.selectedSummerChallenge = null;
- $scope.isFutureChallenge = false;
- $scope.mailPreview = false;
- $rootScope.progress.groups['summercamp'] = $rootScope.progress.groups['summercamp'] || { 'challengeNo' : 1 };
-
- if ($rootScope.progress.groups['summercamp'].challengeNo === $scope.summerCampChallenges.length + 1) {
- $scope.openFeedback();
- }
-
- // Add cover image of shares to respective summer camp challenges
- // Declare variables to be used
- var i, uid, shares, challengeNo;
-
- // Check if there's a uuid variable in the localStorage
- if (localStorage.uuid) {
- uid = localStorage.uuid;
-
- // Get about 50 shares by a user based on the user's id
- api.online.share.list({}, {limit: 50, user_id: uid})
- .then(function(data) {
- // Assign the entries from the returned object into the shares variable
- shares = data.body.entries;
-
- // loop through the objects in the shares
- for (i = 0; i < shares.length; i++) {
- // check for every share that has a summerCamp campaign code
- if (shares[i].campaign_ref.code === 'summerCamp') {
- // Reduce the challengeNo in the share campaign_ref by 1 and assign it to the challengeNo variable
- challengeNo = shares[i].campaign_ref.challengeNo - 1;
- // Find the respective summercamp challenge and assign a cover_url, world_url, shareTitle, shareDescription & username to the challenge
- $scope.summerCampChallenges[challengeNo].cover_url = shares[i].cover_url;
- $scope.summerCampChallenges[challengeNo].world_url = 'http://world.kano.me/shared/' + shares[i].id;
- $scope.summerCampChallenges[challengeNo].shareTitle = shares[i].title;
- $scope.summerCampChallenges[challengeNo].shareDescription = shares[i].description;
- $scope.summerCampChallenges[challengeNo].username = shares[i].user.username;
- }
- }
- });
- }
-
- // Initialise the facebook SDK
- social.init();
-
- // Call the facebook share function
- $scope.facebookShare = function (challenge) {
- social.share.facebook({
- title : challenge.shareTitle + ' on Make Art',
- url : challenge.world_url,
- picture : challenge.cover_url,
- caption : 'Shared by ' + challenge.username + ' through Summer Camp',
- text : challenge.shareDescription
- }, function (err, res) {
- if (err) { return err; }
- });
- };
-
- $scope.openPreview = function () {
- $scope.mailPreview = true;
- };
-
- $scope.closePreview = function () {
- $scope.mailPreview = false;
- };
-
- // Call the send function from the email service
- $scope.sendMail = function (challengeObj) {
- var emailObj;
- if (!challengeObj.name || !challengeObj.message || !challengeObj.email) {
- return false;
- } else {
- if (emailService.validate(challengeObj.email)) {
- challengeObj.user_email = $rootScope.user.email;
- emailObj = emailService.buildObject(challengeObj);
-
- emailService.send(emailObj, function (response) {
- if (response.status === 200) {
- emailService.reset(challengeObj);
- $scope.mailPreview = false;
-
- return notify.success();
- }
- }, function (error) {
- if (error) {
- emailService.reset(challengeObj);
- $scope.mailPreview = false;
-
- return notify.failure();
- }
- });
- } else {
- return notify.failure();
- }
- }
- };
-
- /*
- * Returns true if challenge is disabled
- *
- * @param {Number} index
- * @return {Boolean}
- */
- $scope.isCompleted = function (index) {
- return index + 1 < $rootScope.progress.groups['summercamp'].challengeNo;
- };
-
- /**
- * Return an additional class for the background in the calendar
- * @param {number} the day
- * @return {string}
- */
- $scope.additionalBgClass = function (day) {
- var additionalClass = (challenges[day] ? challenges[day].icon_class: null);
- if (!$scope.isCompleted(day) && (additionalClass)) {
- return additionalClass;
- }
- if ($scope.isCompleted(day)) {
- return '';
- }
- return 'unknown';
- };
-
- /*
- * Returns true if challenge is locked
- *
- * @param {Number} index
- * @return {Boolean}
- */
- $scope.isLocked = function (index) {
-
- if (config.TEST_MODE) {
- return false;
- }
-
- return index >= $rootScope.progress.groups['summercamp'].challengeNo;
- };
-
- /*
- * Returns true if challenge is last unlocked
- *
- * @param {Number} index
- * @return {Boolean}
- */
- $scope.isCurrent = function (index) {
- return index + 1 === $rootScope.progress.groups['summercamp'].challengeNo;
- };
-
- /*
- * Returns true if all challenges have been unlocked
- *
- * @param {Number} index
- * @return {Boolean}
- */
- $scope.isLastUnlocked = function (index) {
- return index === $rootScope.progress.groups['summercamp'].challengeNo;
- };
-
- /*
- * Select a challenge and open challenge modal
- *
- * @param {Object} day
- * @return void
- */
- $scope.selectChallenge = function (day) {
- if ($scope.isLocked(day)) { return; }
- var selectedChallenge = day;
- $scope.selectedSummerChallengeDay = startDate.date() + day;
- $scope.selectedSummerChallenge = selectedChallenge;
- $scope.selectedSummerChallengeObj = challenges[selectedChallenge];
- $scope.isFutureChallenge = isFutureChallenge(day);
- analytics.track('Viewed Challenge ' + day, {
- category : 'Viewed Summer Camp Challenge'
- });
- countDownToNext();
- };
-
- /*
- * Open a challenge by double-clicking and check if it's unlocked and not a future
- * challenge.
- *
- * @param {Number} day
- * @return void
- */
- $scope.openChallenge = function (day) {
- if (!$scope.isLocked(day) && !isFutureChallenge(day)) {
- day = day + 1;
- $location.path('/summercamp/challenge/' + day);
- }
- };
-
- // Initialize the selected day variable with -1
- $scope.selectedDay = -1;
-
- /* Set a day to be active when a challenge is selected in the calendar
- * Also check if the day is not locked and not a future challenge
- *
- * @param {Number} index
- * @return void
- */
- $scope.setActive = function(index) {
- if (!$scope.isLocked(index) && !isFutureChallenge(index)) {
- $scope.selectedDay = index;
- }
- };
-
- /* Get the active challenge day on the calendar
- * and use that to apply the proper css class
- *
- * @param {Number} index
- * @return {Boolean}
- */
- $scope.getActive = function(index) {
- return $scope.selectedDay === index;
- };
-
- /*
- * Deselect a challenge and close challenge modal
- *
- * @return void
- */
- $scope.deselectedChallenge = function () {
- $scope.selectedSummerChallenge = null;
- };
-
- /*
- * Returns an AP containing max elements
- *
- * @param {Number} max
- * @return {Array}
- */
- $scope.range = function (max) {
- var ar = [];
-
- for(var i = 0; i < max; i++) {
- ar.push(i);
- }
- return ar;
- };
-
- // Close register modal
- $scope.closeModal = function () {
- $scope.register = false;
- };
-
- // Create calendar dates
- $scope.weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
-
- $scope.loading = true;
-
- $scope.currentDay = function(index) {
- return index >= $rootScope.progress.groups['summercamp'].challengeNo && checkTime(index);
- };
-
- // This is just used to highlight the day name on the calendar
- $scope.isCurrentDayIndex = function(index) {
- var now = new Date();
- return index === (now.getDay() || 7) - 1;
- }
-
- /*
- * Returns true if challenge is a future challenge
- *
- * @param {Number} day
- * @return {Boolean}
- */
- function isFutureChallenge(day) {
- return (day > currentDayIndex()) ? true : false;
- };
-
- function currentDayIndex() {
- var today = moment(),
- diff = today.diff(startDate),
- remainder = diff / 1000;
-
- return parseInt(remainder / 86400, 10);
- }
-
- function countDown() {
- var today = moment();
- var diff = startDate.diff(today);
- var remainder = diff / 1000;
-
- $scope.days = parseInt(remainder / 86400, 10);
- remainder = remainder % 86400;
-
- $scope.hours = parseInt(remainder / 3600, 10);
- $scope.hours = (($scope.hours < 10) ? '0' : '') + $scope.hours;
- remainder = remainder % 3600;
-
- $scope.minutes = parseInt(remainder / 60, 10);
- $scope.minutes = (($scope.minutes < 10) ? '0' : '') + $scope.minutes;
-
- $scope.seconds = parseInt(remainder % 60, 10);
- $scope.seconds = (($scope.seconds < 10) ? '0' : '') + $scope.seconds;
-
- $scope.loading = false;
- if (diff < 1) {
- if (countDownInterval) {
- $interval.cancel(countDownInterval);
- }
- $location.path('/summercamp');
- }
- }
-
- function countDownToNext() {
- var day = $scope.selectedSummerChallenge + 1;
- var nextStartDate = moment(startDate);
- var now = moment();
- var nextDay = moment().add(1, 'day');
- var timeDiff;
-
- $scope.timeToNext = $scope.timeToNext || {};
- //debugger;
- nextStartDate.add(day, 'days');
-
- //console.log(day, nextDay, nextStartDate, now);
- if (nextDay.isBefore(nextStartDate)) {
- //if we're just looking at the next
- timeDiff = moment(nextStartDate.diff(now));
- //debugger;
-
- $scope.timeToNext.h = timeDiff.hours();
- $scope.timeToNext.m = timeDiff.minutes();
- $scope.timeToNext.s = timeDiff.seconds();
- //$scope.timeToNext = timeDiffObj;
- $timeout(countDownToNext, 500);
- } else {
- $scope.timeToNext = null;
-
- }
- }
-
- function checkTime(index) {
- var today = moment();
- if(today.isAfter(startDate)) {
- var dayDiff = today.date() - startDate.date();
- if (index <= dayDiff) {
-
- $interval(function () {
- $scope.currentDay(index + 1);
- }, today.diff(startDate));
- return true;
- }
- }
- else {
- return false;
- }
- }
-
- if (!$rootScope.cfg.TEST_MODE) {
- countDownInterval = $interval(countDown, 1000);
- }
-
- // We have our indexes screwed somewhere. I shouldn't have to do -1 here
- $scope.selectChallenge($rootScope.progress.groups['summercamp'].challengeNo - 1);
- $scope.setActive($rootScope.progress.groups['summercamp'].challengeNo - 1);
-});
diff --git a/lib/core/analytics.js b/lib/core/analytics.js
deleted file mode 100644
index 7e81d811a..000000000
--- a/lib/core/analytics.js
+++ /dev/null
@@ -1,71 +0,0 @@
-var config = window.CONFIG,
- analytics = window.analytics;
-
-/*
- * Analytics module
- *
- * Wrapper to Segment.io analytics
- * More documentation at: https://segment.com/docs/libraries/analytics.js
- */
-
-var enabled = config.SEGMENTIO_ID && !config.OFFLINE && analytics;
-
-/*
- * Initialise Segment.io if `SEGMENTIO_ID` is present in config, start
- * tracking page views
- *
- * @return void
- */
-exports.init = function () {
- if (!enabled) { return; }
-
- window.analytics.load(config.SEGMENTIO_ID);
-};
-
-/*
- * Track current page view
- *
- * @param {String} pageId
- * @return void
- */
-exports.page = function (pageId) {
- if (!enabled) { return; }
-
- window.analytics.page(pageId);
-};
-
-/*
- * Tie a user to the tracked data
- *
- *
- * @param {String} [userId] - The database ID for the user. If as yet
- * unknown, you can omit and just record traits
- * @param {Object} [traits] - Dictionary of known traits of the user, e.g.
- * email or name
- * @param {Object} [options] - Dictionary to enable or disable specific
- * integrations for the call
- * @param {Function} [callback] - Callback called after a short timeout
- * @return void
- */
-exports.identify = function (userId, traits, options, callback) {
- if (!enabled) { return; }
-
- window.analytics.identify(userId, traits, options, callback);
-};
-
-/*
- * Record actions that a user performs
- *
- * @param {String} event - Tracked event name, e.g. 'Added to Cart'
- * @param {Object} [properties] - Dictionary of event properties, e.g. price,
- * productType, etc.
- * @param {Object} [options] - Dictionary to enable or disable specific
- * integrations for the call
- * @param {Function} [callback] - Callback called after a short timeout
- * @return void
- */
-exports.track = function (event, properties, options, callback) {
- if (!enabled) { return; }
-
- window.analytics.track(event, properties, options, callback);
-};
diff --git a/lib/core/auth.js b/lib/core/auth.js
index 9b6e9dccf..cbc93214f 100644
--- a/lib/core/auth.js
+++ b/lib/core/auth.js
@@ -5,7 +5,8 @@
*/
var api,
- user = null;
+ user = null,
+ tracking = require('./tracking');
/*
* Get user object if logged in
@@ -37,17 +38,30 @@ function auth (config) {
// Call async API method to checked if there's a logged in user
api.auth.init(function (err, loggedUser) {
if (loggedUser) { user = loggedUser; }
- if (callback) { callback(); }
+ if (callback) { callback(loggedUser); }
});
}
return {
init : init,
- login : api.auth.login,
- logout : api.auth.logout,
+ login : function (session, callback) {
+ user = session.user;
+ tracking.dispatchTrackingEvent('loggedInToKanoWorld');
+ tracking.trackVisitType('Logged in');
+ api.auth.saveLogin(session.token, function () {
+ if (callback) { callback(user); }
+ });
+ },
+ logout : function (callback) {
+ user = null;
+ api.auth.logout(false);
+ tracking.dispatchTrackingEvent('loggedOutOfKanoWorld');
+ tracking.trackVisitType('Logged out');
+ if (callback) { callback(); }
+ },
getState : getState,
getUser : getUser
};
}
-module.exports = auth;
\ No newline at end of file
+module.exports = auth;
diff --git a/lib/core/config.js b/lib/core/config.js
index 70fe849be..a250e2cab 100644
--- a/lib/core/config.js
+++ b/lib/core/config.js
@@ -4,6 +4,8 @@
* Central config inclusive of environment sensitive variable parsed
* from rendered page
*/
+"use strict";
+var langSynonyms = require('../language/synonyms');
module.exports = {
"default": {
@@ -13,16 +15,39 @@ module.exports = {
SEGMENTIO_ID : window.CONFIG ? window.CONFIG.SEGMENTIO_ID : null,
TEST_MODE : window.CONFIG ? window.CONFIG.TEST_MODE : false,
FACEBOOK_APP_ID : window.CONFIG ? window.CONFIG.FACEBOOK_APP_ID : false,
- MAILSERVER : window.CONFIG ? window.CONFIG.MAILSERVER : false,
API_URL : window.CONFIG ? window.CONFIG.API_URL : false,
- WORLD_URL : window.CONFIG ? window.CONFIG.WORLD_URL : false
+ WORLD_URL : window.CONFIG ? window.CONFIG.WORLD_URL : false,
+ CHALLENGES_URL : window.CONFIG ? window.CONFIG.CHALLENGES_URL : false,
+ UNKNOWN_USER : window.CONFIG ? window.CONFIG.UNKNOWN_USER : null,
+ DOMAIN_CFG: {
+ "hourofcode.kano.me": {
+ 'mapToWorld': 'pixelhack',
+ 'hideShares': true
+ },
+ "csedweek.kano.me": {
+ 'mapToWorld': 'pixelhack',
+ 'hideShares': true
+ }
+ },
+ DEFAULT_AVATAR : 'https://s3-eu-west-1.amazonaws.com/world.kano.me/users/avatars/563890fec4d6960800c721f7/avatar-circle.png',
+ languageSynonyms: langSynonyms
},
"development": {
FACEBOOK_APP_ID : '832712683515218',
- MAILSERVER : 'http://localhost:7000/summercamp/send',
WORLD_URL : 'http://localhost:5000',
- API_URL : 'http://localhost:1234'
+ API_URL : 'http://localhost:1234',
+ UNKNOWN_USER : 'tanc'
},
- "staging": {},
- "production": {}
-};
\ No newline at end of file
+ "staging": {
+ FACEBOOK_APP_ID : '832712683515218',
+ API_URL : 'https://api-staging.kano.me',
+ WORLD_URL : 'https://world-staging.kano.me',
+ UNKNOWN_USER : 'tanc'},
+ "production": {
+ FACEBOOK_APP_ID : '832712683515218',
+ API_URL : 'https://api.kano.me',
+ WORLD_URL : 'http://world.kano.me',
+ UNKNOWN_USER : 'tanc'
+ }
+
+};
diff --git a/lib/core/social.js b/lib/core/facebook.js
similarity index 85%
rename from lib/core/social.js
rename to lib/core/facebook.js
index c507dbf8c..9baf39a0a 100644
--- a/lib/core/social.js
+++ b/lib/core/facebook.js
@@ -1,18 +1,15 @@
+"use strict";
/*
- * Social module
- *
- * Centralised module to handle social sharing / integration / SDKs
+ * Facebook module
*/
-var share = {};
-
/*
* Share content with given options on Facebook
*
* @param {Object} options
* @param {Function} callback
*/
-share.facebook = function (options, callback) {
+function share(options, callback) {
window.FB.ui({
method : 'feed',
name : options.title || 'Kano World',
@@ -28,7 +25,7 @@ share.facebook = function (options, callback) {
return callback(res, !res ? null : null);
});
-};
+}
/*
* Add http protocol to URL if missing
@@ -55,8 +52,8 @@ function init() {
* I really didn't wanna pollute the main HTML file with this stuff..
*/
function initFacebook() {
- var facebookRoot = document.createElement('div');
- var appId = window.CONFIG.FACEBOOK_APP_ID;
+ var facebookRoot = document.createElement('div'),
+ appId = window.CONFIG.FACEBOOK_APP_ID;
facebookRoot.setAttribute('id', 'fb-root');
document.body.appendChild(facebookRoot);
@@ -73,7 +70,9 @@ function initFacebook() {
(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
- if (d.getElementById(id)) { return; }
+ if (d.getElementById(id)) {
+ return;
+ }
js = d.createElement(s);
js.id = id;
@@ -85,4 +84,4 @@ function initFacebook() {
module.exports = {
init : init,
share : share
-};
\ No newline at end of file
+};
diff --git a/lib/core/sound.js b/lib/core/sound.js
index 6379eba26..73cba1441 100644
--- a/lib/core/sound.js
+++ b/lib/core/sound.js
@@ -1,5 +1,4 @@
-var play = require('play-audio'),
- api,
+var api,
config = window.CONFIG;
var cfg = {
@@ -17,12 +16,18 @@ api = require('../api')(cfg);
* @return void
*/
exports.play = function (name) {
+ var soundPath = 'assets/sounds/' + name + '.mp3',
+ audio;
//Pi is not compatible with HTML5 audio object
if (config.OFFLINE) {
- api.sound.playSound('assets/sounds/' + name + '.mp3');
+ api.sound.playSound(soundPath);
} else {
- play('/assets/sounds/' + name + '.mp3').play();
+ audio = document.createElement('audio');
+
+ audio.src = '/' + soundPath;
+ audio.load();
+ audio.play();
}
-};
\ No newline at end of file
+};
diff --git a/lib/core/tracking.js b/lib/core/tracking.js
new file mode 100644
index 000000000..7a0c7f37e
--- /dev/null
+++ b/lib/core/tracking.js
@@ -0,0 +1,181 @@
+/*
+ * Tracking module
+ * Currently we are using Google Analytics with Google Tag Manager
+*/
+
+var state = {
+ account : false,
+ userProgress : {},
+ userType : null,
+ visitType : null
+}
+
+/**
+ * Check whether the user has ever had an account
+ */
+function checkAccount () {
+ var account = state.account || checkVisitType() === 'Logged in' || isPi();
+ if (!account) {
+ var storedAccount = localStorage.getItem('KW_ACCOUNT');
+ account = storedAccount ? JSON.parse(storedAccount) : false;
+ };
+ state.account = account;
+ return account;
+}
+
+/**
+ * Function for checking number of challenges completed this session
+ */
+function checkUserProgress () {
+ var userProgress = state.userProgress
+ if (!userProgress.completedStories) {
+ var storedProgress = sessionStorage.getItem('KW_PROGRESS');
+ if (storedProgress) {
+ userProgress = JSON.parse(storedProgress);
+ } else {
+ userProgress = {
+ completedStories: []
+ }
+ sessionStorage.setItem('KW_PROGRESS', JSON.stringify(userProgress));
+ }
+ }
+ return userProgress;
+}
+
+/**
+ * Check whether the user is using a Kano Kit
+ */
+function checkUserType () {
+ var userType = state.userType;
+ if (!userType) {
+ userType = isPi() ? 'paid' : 'free';
+ }
+ state.userType = userType;
+ return userType;
+}
+
+/**
+ * Check whether the user is currently logged in
+ */
+function checkVisitType () {
+ var visitType = state.visitType;
+ if (!visitType) {
+ var storedToken = localStorage.getItem('KW_TOKEN') || null;
+ visitType = storedToken ? 'Logged in' : 'Logged out';
+ state.visitType = visitType;
+ }
+ return visitType;
+}
+
+/**
+ * Wrapper for dispatching events to GTM datalayer
+ *
+ * @param {Object || String} trackingEvent
+ */
+function dispatchTrackingEvent(trackingEvent) {
+ var payload = trackingEvent;
+ if (trackingEvent !== Object(trackingEvent)) {
+ payload = {
+ event: trackingEvent
+ };
+ }
+ window.dataLayer = window.dataLayer || [];
+ window.dataLayer.push(payload);
+}
+
+/**
+ * Function to dispatch virtualPageViews with all
+ * the required information
+ */
+function dispatchVirtualPageView() {
+ var payload = {
+ account : checkAccount(),
+ challengesCompleted : checkUserProgress().completedStories.length,
+ event : 'virtualPageView',
+ userType : checkUserType(),
+ virtualPageTitle : document.title,
+ virtualPageURL : window.location.pathname,
+ visitType : checkVisitType()
+ };
+ this.dispatchTrackingEvent(payload);
+}
+
+/*
+ * Initialise tracking
+ *
+ * @return void
+ */
+function init () {
+ checkUserType();
+ checkVisitType();
+ checkAccount();
+
+ return;
+};
+
+/**
+ * Generic function to check whether the user is using a Kano Kit
+ *
+ * note: not currently fullproof
+ */
+
+function isPi () {
+ var userAgent = window.navigator.userAgent;
+ return userAgent.indexOf('armv6l') !== -1 || userAgent.indexOf('armv7l') !== -1;
+}
+
+/**
+ * Update the account based on the user logging in, if there
+ * wasn't a previous account
+ */
+function trackAccount () {
+ if (!state.account) {
+ state.account = true;
+ dispatchTrackingEvent({
+ account: true
+ });
+ localStorage.setItem('KW_ACCOUNT', true);
+ }
+}
+
+/**
+ * Function for update number of challenges completed this session
+ *
+ * @param {String} challenge
+ */
+function trackUserProgress (challenge) {
+ var userProgress = checkUserProgress();
+ if (challenge && userProgress.completedStories.indexOf(challenge) === -1) {
+ userProgress.completedStories.push(challenge);
+ }
+ dispatchTrackingEvent({
+ challengesCompleted: userProgress.completedStories.length
+ });
+ sessionStorage.setItem('KW_PROGRESS', JSON.stringify(userProgress));
+}
+
+/**
+ * Update the visitType based in user logging in or out and dispatch
+ * the updated tracking event
+ *
+ * @param {String} type
+ */
+function trackVisitType (type) {
+ if (state.visitType !== type) {
+ dispatchTrackingEvent({
+ visitType: type
+ });
+ state.visitType = type;
+ }
+ if (type === 'Logged in') {
+ trackAccount();
+ }
+}
+
+module.exports = {
+ dispatchTrackingEvent : dispatchTrackingEvent,
+ dispatchVirtualPageView : dispatchVirtualPageView,
+ init : init,
+ trackUserProgress : trackUserProgress,
+ trackVisitType : trackVisitType
+};
diff --git a/lib/core/twitter.js b/lib/core/twitter.js
new file mode 100644
index 000000000..080ecabe2
--- /dev/null
+++ b/lib/core/twitter.js
@@ -0,0 +1,39 @@
+"use strict";
+function init() {
+ window.twttr = (function (d, s, id) {
+ var js,
+ fjs = d.getElementsByTagName(s)[0],
+ t = window.twttr || {};
+
+ if (d.getElementById(id)) {
+ return t;
+ }
+ js = d.createElement(s);
+ js.id = id;
+ js.src = "https://platform.twitter.com/widgets.js";
+ fjs.parentNode.insertBefore(js, fjs);
+
+ t._e = [];
+ t.ready = function (f) {
+ t._e.push(f);
+ };
+
+ return t;
+ }(document, "script", "twitter-wjs"));
+}
+
+function share(callback) {
+ window.twttr.ready(function (twttr) {
+ twttr.events.bind('tweet', function (res) {
+ if (!res) {
+ return;
+ }
+ callback(res);
+ });
+ });
+}
+
+module.exports = {
+ init: init,
+ share: share
+};
diff --git a/lib/directive/auth.js b/lib/directive/auth.js
new file mode 100644
index 000000000..90621e805
--- /dev/null
+++ b/lib/directive/auth.js
@@ -0,0 +1,19 @@
+"use strict";
+var i18n = require('../i18n'),
+ app = require('../app');
+
+/*
+ * Auth directive
+ *
+ * Diplay alogin/signup form an authenticate the user
+ */
+app.directive('authForm', function () {
+ return {
+ restrict : 'E',
+ templateUrl : i18n.getHtmlLocalePath() + '/directive/auth.html',
+ scope : {
+ mode: '='
+ },
+ controller: 'AuthController'
+ };
+});
diff --git a/lib/directive/display.js b/lib/directive/display.js
index 2650941eb..5754731d6 100644
--- a/lib/directive/display.js
+++ b/lib/directive/display.js
@@ -1,10 +1,13 @@
-var app = require('../app'),
+"use strict";
+var i18n = require('../i18n'),
+ app = require('../app'),
session = require('../language/session'),
- config = require('../core/config'),
fileUtil = require('../util/file'),
- td = require('throttle-debounce');
-
-var THROTTLE_MS = config.OFFLINE ? 1000 : 1;
+ td = require('throttle-debounce'),
+ firstRender = true,
+ THROTTLE_MS,
+ VALIDATE_DELAY,
+ config;
/*
* Display directive
@@ -12,13 +15,16 @@ var THROTTLE_MS = config.OFFLINE ? 1000 : 1;
* Handles live updatingm execution of code and rendering coming from its model
*/
-var firstRender = true;
-app.directive('display', function ($window, $timeout) {
+app.directive('display', function ($window, $timeout, $rootScope) {
+ config = $rootScope.cfg;
+ THROTTLE_MS = config.OFFLINE ? 1000 : 1;
+ VALIDATE_DELAY = config.OFFLINE ? 1000 : 1;
+
return {
require : '^workspace',
restrict : 'E',
- templateUrl : '/directive/display.html',
+ templateUrl : i18n.getHtmlLocalePath() + '/directive/display.html',
scope : {
source : '=',
mode : '=',
@@ -27,7 +33,7 @@ app.directive('display', function ($window, $timeout) {
},
link : function (scope, element, attrs, workspaceCtl) {
- var win = angular.element($window),
+ var timer, win = angular.element($window),
loadInput = element.find('input')[0];
// Attach workspace to scope
@@ -109,24 +115,30 @@ app.directive('display', function ($window, $timeout) {
settings = scope,
err;
- // Reset error, evaluate code and catch new potentia;errors
- scope.error = null;
- err = language.run(scope.source, settings);
- scope.cursorPosition = language.cursorPosition || scope.getCenter();
- scope.displayCoordsPos();
-
- if (err) {
- // Drawing failed
- scope.drawingFailed = true;
+ if (config.OFFLINE) {
+ clearTimeout(timer);
+ }
- if ('loc' in err) {
- displayEditorError(err);
+ timer = setTimeout(function () {
+ // Reset error, evaluate code and catch new potentia;errors
+ scope.error = null;
+ err = language.run(scope.source, settings);
+ scope.cursorPosition = language.cursorPosition || scope.getCenter();
+ scope.displayCoordsPos();
+
+ if (err) {
+ // Drawing failed
+ scope.drawingFailed = true;
+
+ if ('loc' in err) {
+ displayEditorError(err);
+ }
+ } else {
+ // Drawing succeded
+ scope.drawingFailed = false;
+ workspaceCtl.scope.error = null;
}
- } else {
- // Drawing succeded
- scope.drawingFailed = false;
- workspaceCtl.scope.error = null;
- }
+ }, VALIDATE_DELAY);
});
/*
@@ -179,13 +191,15 @@ app.directive('display', function ($window, $timeout) {
});
/*
- * Set coordinates to display
+ * Set coordinates to display, round decimal places to 2.
*
* @param {Number} x
* @param {Number} y
* @return void
*/
function setCoordsPosition(x, y) {
+ x = x % 1 === 0 ? x : Number(x.toFixed(2));
+ y = y % 1 === 0 ? y : Number(y.toFixed(2));
scope.coordsPosition = { x: x, y: y };
}
@@ -236,7 +250,7 @@ app.directive('display', function ($window, $timeout) {
var file = loadInput.files[0],
reader = new FileReader();
- reader.onload = function(evt) {
+ reader.onload = function (evt) {
var fileData = evt.target.result;
scope.setCode(fileData);
@@ -250,12 +264,13 @@ app.directive('display', function ($window, $timeout) {
*
* @return void
*/
- scope.save = function() {
+ scope.save = function () {
+ var blob;
if (config.OFFLINE) {
return scope.setOpenModal('save');
}
- var blob = new Blob([ scope.source ], { type: 'text/plain' });
+ blob = new Blob([scope.source], { type: 'text/plain' });
fileUtil.downloadBlob(blob, 'creation.draw');
};
@@ -263,4 +278,4 @@ app.directive('display', function ($window, $timeout) {
init();
}
};
-});
\ No newline at end of file
+});
diff --git a/lib/directive/editor-input.js b/lib/directive/editor-input.js
index 44155d3de..fa7922ebe 100644
--- a/lib/directive/editor-input.js
+++ b/lib/directive/editor-input.js
@@ -1,4 +1,5 @@
-var app = require('../app'),
+var i18n = require('../i18n'),
+ app = require('../app'),
palette = require('../language/modules/palette.json'),
docs = require('../../content/docs.json'),
equal = require('deep-equal'),
@@ -44,7 +45,7 @@ app.directive('editor', function ($rootScope, $controller, $compile) {
return {
require : '^workspace',
restrict : 'E',
- templateUrl : '/directive/editor.html',
+ templateUrl : i18n.getHtmlLocalePath() + '/directive/editor.html',
scope : {
ngModel : '=',
editable : '=',
@@ -605,4 +606,4 @@ function getAutoCompleteList() {
});
return out;
-}
\ No newline at end of file
+}
diff --git a/lib/directive/editor.js b/lib/directive/editor.js
index f7653bcfd..8222b1d91 100644
--- a/lib/directive/editor.js
+++ b/lib/directive/editor.js
@@ -1,6 +1,9 @@
-var app = require('../app'),
+"use strict";
+
+var i18n = require('../i18n'),
+ app = require('../app'),
palette = require('../language/modules/palette.json'),
- config = require('../core/config'),
+ config,
docs = require('../../content/docs.json'),
equal = require('deep-equal'),
domUtil = require('../util/dom'),
@@ -8,22 +11,15 @@ var app = require('../app'),
tokenHandlers = {
number : require('../editor/token-handler/NumberHandler'),
color : require('../editor/token-handler/ColorHandler')
- };
-
-/*
- * Disable number handler on Pi version
- */
-if (config.OFFLINE) {
- delete tokenHandlers.number;
-}
+ },
/*
* Editor directive
*
- * Initialises, configures and binds ACE editor and binds its value to a model
+ * Initialises, configures and binds ACE editor and binds its value to a model
*/
-var EDITOR_DEFAULTS = {
+ EDITOR_DEFAULTS = {
mode : 'ace/mode/coffee',
theme : 'ace/theme/dawn',
wrapMode : false,
@@ -45,14 +41,23 @@ var EDITOR_DEFAULTS = {
* @return void
*/
function initialiseEditor() {
- langTools.setCompleters([ { getCompletions: getAutoComplete } ]);
+ langTools.setCompleters([{ getCompletions: getAutoComplete }]);
}
-app.directive('editor', function ($rootScope, $controller, $compile) {
+app.directive('editor', function ($rootScope, $controller, $compile, $routeParams, contentService) {
+ config = $rootScope.cfg;
+ docs = docs[i18n.getLanguage() || 'en'];
+ /*
+ * Disable number handler on Pi version
+ */
+ if (config.OFFLINE) {
+ delete tokenHandlers.number;
+ }
+
return {
require : '?workspace',
restrict : 'E',
- templateUrl : '/directive/editor.html',
+ templateUrl : i18n.getHtmlLocalePath() + '/directive/editor.html',
scope : {
ngModel : '=',
editable : '=',
@@ -87,6 +92,20 @@ app.directive('editor', function ($rootScope, $controller, $compile) {
scope.ngModel = scope.ngModel || editorElement.innerHTML;
// Select first docs category
scope.selectDocsCategory(scope.docs[0]);
+ // Set guide to an empty string
+ scope.guide = '';
+ // Set challengeId & worldId from the route params
+ scope.challengeId = $routeParams.id;
+ scope.worldId = $routeParams.world;
+ // Get the guide from a challenge
+ if (scope.worldId && scope.challengeId) {
+ scope.teachers_guide = $rootScope.cfg.OFFLINE ? '' : $rootScope.selectedWorld.teachers_guide;
+ contentService.challenge.get(scope.worldId, scope.challengeId).then(function (challenge) {
+ if (challenge.guide) {
+ scope.guide = $rootScope.cfg.OFFLINE ? '' : challenge.guide;
+ }
+ });
+ }
// Initialise docs tab scrollable pane
initScrollablePane();
// Initialise ACE editor
@@ -371,11 +390,12 @@ app.directive('editor', function ($rootScope, $controller, $compile) {
}
function onCursorMove() {
- if (preventMove || !scope.editable) { return; }
-
var range = engine.getSelectionRange(),
token;
+ if (preventMove || !scope.editable) { return; }
+
+
// Return if selection is active
if (!equal(range.start, range.end)) { return; }
@@ -558,6 +578,9 @@ app.directive('editor', function ($rootScope, $controller, $compile) {
*/
function onMouseDownAnywhere(e) {
lastFocused = e.target;
+ if (tokenHandler && !domUtil.isIncludedBy(lastFocused, editorElement) && !domUtil.isIncludedBy(lastFocused, tokenHandler.el)) {
+ closeTokenHandler();
+ }
}
/*
diff --git a/lib/directive/export-modal.js b/lib/directive/export-modal.js
index 89da4deb5..3e0750cfb 100644
--- a/lib/directive/export-modal.js
+++ b/lib/directive/export-modal.js
@@ -1,7 +1,9 @@
+"use strict";
var api,
- app = require('../app');
-
-var WALLPAPER_RATIOS = {
+ i18n = require('../i18n'),
+ app = require('../app'),
+ tracking = require ('../core/tracking'),
+ WALLPAPER_RATIOS = {
'1024': {
width : 1024,
height : 768
@@ -22,10 +24,10 @@ var WALLPAPER_RATIOS = {
* Share modal directive under display
*/
-app.directive('exportModal', function ($rootScope, $location, $routeParams) {
+app.directive('exportModal', function ($rootScope, $routeParams) {
return {
restrict : 'E',
- templateUrl : '/directive/export-modal.html',
+ templateUrl : i18n.getHtmlLocalePath() + '/directive/export-modal.html',
scope : {
display : '=',
action : '='
@@ -38,7 +40,11 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
// Setup scope
scope.open = false;
scope.meta = {};
+ scope.loading = false;
+ scope.loggedIn = $rootScope.loggedIn ? true : false,
+ scope.optional = ($rootScope.selectedWorld ? ($rootScope.selectedWorld.share_strategy === 'optional') : '');
$rootScope.successful = false;
+ $rootScope.creation = null;
// Initialise
init();
@@ -47,11 +53,6 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
function init() {
var _shareCache;
- var location = $location.url();
- if (location.match(/summercamp/)) {
- scope.category = true;
- }
-
// Next tick..
setTimeout(function () {
if (scope.display.workspace && scope.display.workspace.scope._shareCache) {
@@ -62,12 +63,19 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
});
}
- $rootScope.$watch('shareModal', function (val) {
+ $rootScope.$watch('exportModal', function (val) {
if (val) {
- open();
+ if (scope.display.mode === 'challenge') {
+ scope.display.setOpenModal('share');
+ }
}
});
+ scope.skipSharing = function () {
+ scope.close();
+ $rootScope.openNextModal();
+ };
+
/*
* Submit form
*
@@ -78,6 +86,9 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
return showError('Please choose a title');
} else if (scope.meta.title.length > 100) {
return showError('Title is too long');
+ } else if (scope.meta.description &&
+ scope.meta.description.length > 500) {
+ return showError('Description is too long');
}
resetError();
@@ -91,6 +102,7 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
*/
function performAction() {
if (scope.action === 'share') {
+ scope.loading = true;
share();
} else if (scope.action === 'save') {
save();
@@ -135,10 +147,10 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
*
* @return void
*/
- scope.close = function() {
+ scope.close = function () {
scope.open = false;
- $rootScope.shareModal = false;
scope.display.openModal = null;
+ $rootScope.exportModal = false;
};
/*
@@ -157,7 +169,7 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
*/
function bind() {
- // Open or close on parent display modal state change
+ // Open or close on parent display modal state change
scope.display.$watch('openModal', function (modalId) {
if (modalId === scope.action && !scope.open) {
open();
@@ -188,40 +200,54 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
* @return void
*/
function share() {
- var title = scope.meta.title || '',
+ var creation,
+ options,
+ title = scope.meta.title || '',
code = scope.display.source,
description = scope.meta.description || '',
- challengeNo = $routeParams.id;
+ challengeId = $routeParams.id,
+ world = $routeParams.world;
if (!$rootScope.loggedIn) {
// Store current details and prompt login if not currently logged in
- var options = {
+ options = {
title : title,
code : code,
- description : description
+ description : description,
+ challengeId : challengeId
};
- if (scope.category) {
- options.challengeNo = challengeNo;
- }
-
localStorage._shareCache = JSON.stringify(options);
- api.auth.login();
+ $rootScope.auth.openModal();
+ scope.loading = false;
return;
}
api.progress.trackLinesOfCode(code.split('\n').length);
- if (scope.category) {
- api.challengeIO.share(title, code, description, scope.imageURL, challengeNo);
- $rootScope.successful = true;
- }
- else {
- api.challengeIO.share(title, code, description, scope.imageURL);
- }
-
- scope.close();
+ api.challengeIO.share(title, code, description, scope.imageURL, world, challengeId, function (data) {
+ if (data.body === '') {
+ scope.loading = false;
+ $rootScope.successful = true;
+ scope.close();
+ } else if (data.body.item) {
+ scope.loading = false;
+ creation = data.body.item;
+ creation.world = $rootScope.selectedWorld ? $rootScope.selectedWorld.name : null;
+ creation.username = $rootScope.user.username;
+ creation.url = $rootScope.cfg.WORLD_URL + "/shared/" + creation.slug;
+ $rootScope.creation = creation;
+ $rootScope.successful = true;
+ scope.close();
+ } else if (data.body && !data.body.item) {
+ scope.loading = false;
+ $rootScope.successful = true;
+ scope.close();
+ }
+ tracking.dispatchTrackingEvent('worldInternalShare');
+ $rootScope.$apply();
+ });
}
/*
@@ -245,7 +271,7 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
*
* @return void
*/
- scope.exportWallpaper = function() {
+ scope.exportWallpaper = function () {
var wallpapers = getWallpapers();
api.challengeIO.saveWallpaper(
@@ -263,9 +289,10 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
* @return void
*/
function getWallpapers() {
- var canvas = scope.display.canvas;
-
- var original = {
+ var ratio,
+ setCanvas,
+ canvas = scope.display.canvas,
+ original = {
width : canvas.width,
height : canvas.height
},
@@ -273,7 +300,7 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
tempCtx = tempCanvas.getContext('2d'),
wallpapers = {};
- var setCanvas = function(size) {
+ setCanvas = function (size) {
var scaleFactor = Math.min(
size.width / original.width,
size.height / original.height
@@ -296,7 +323,7 @@ app.directive('exportModal', function ($rootScope, $location, $routeParams) {
tempCtx.drawImage(canvas, offset.x, offset.y);
};
- for (var ratio in WALLPAPER_RATIOS) {
+ for (ratio in WALLPAPER_RATIOS) {
if (WALLPAPER_RATIOS.hasOwnProperty(ratio)) {
setCanvas(WALLPAPER_RATIOS[ratio]);
wallpapers[ratio] = tempCanvas.toDataURL('image/png');
diff --git a/lib/directive/progress-circle.js b/lib/directive/progress-circle.js
index 5ae688911..752b4eb10 100644
--- a/lib/directive/progress-circle.js
+++ b/lib/directive/progress-circle.js
@@ -1,4 +1,5 @@
-var app = require('../app');
+var i18n = require('../i18n'),
+ app = require('../app');
var XMLNS = 'http://www.w3.org/2000/svg',
RADIUS = 30,
@@ -13,7 +14,7 @@ var XMLNS = 'http://www.w3.org/2000/svg',
app.directive('progressCircle', function () {
return {
restrict : 'E',
- templateUrl : '/directive/progress-circle.html',
+ templateUrl : i18n.getHtmlLocalePath() + '/directive/progress-circle.html',
scope : {
value : '=',
max : '='
diff --git a/lib/directive/social.js b/lib/directive/social.js
new file mode 100644
index 000000000..07a0799aa
--- /dev/null
+++ b/lib/directive/social.js
@@ -0,0 +1,105 @@
+"use strict";
+var i18n = require('../i18n'),
+ app = require('../app'),
+ tracking = require('../core/tracking'),
+ notify = require('../api/notify');
+
+/*
+ * Social sharing directive
+ */
+
+app.directive('socialSharing', function ($rootScope, socialService, emailService) {
+ return {
+ restrict: 'E',
+ templateUrl : i18n.getHtmlLocalePath() + '/directive/social.html',
+ scope: {
+ type: '='
+ },
+ link : function (scope) {
+ var cover_url = 'http://art.kano.me/assets/challenges/images/' + $rootScope.selectedWorld.cover + '@2x.png';
+ function init() {
+ socialService.init();
+ socialService.twitter.share(function (res) {
+ if (!res) {
+ return;
+ }
+ tracking.dispatchTrackingEvent('worldExternalShare');
+ return;
+ });
+ }
+
+ init();
+
+ scope.loading = false;
+ scope.mailModal = false;
+ scope.buildURL = socialService.twitter.build;
+ scope.mailForm = {
+ title: $rootScope.selectedWorld.name,
+ type: 'world-invite',
+ description: $rootScope.selectedWorld.socialText ? $rootScope.selectedWorld.socialText.email : null,
+ cover_url: cover_url,
+ url: 'http://art.kano.me/' + $rootScope.selectedWorld.id
+ };
+
+ scope.open = function () {
+ scope.mailModal = true;
+ };
+ scope.close = function () {
+ scope.mailModal = false;
+ };
+ scope.facebookShare = function () {
+ var options = {
+ title : $rootScope.selectedWorld.name + ' on Make Art',
+ url : 'http://art.kano.me/challenges/' + $rootScope.selectedWorld.id,
+ picture : cover_url,
+ caption : 'Shared by ' + $rootScope.user.username + ' via ' + $rootScope.selectedWorld.name,
+ text : $rootScope.selectedWorld.socialText.facebook
+ };
+
+ socialService.facebook.share(options, function (err, res) {
+ if (!res || err) {
+ return err;
+ }
+ tracking.dispatchTrackingEvent('worldExternalShare');
+ });
+ };
+
+ scope.sendMail = function () {
+ var emailObj;
+ if (!scope.mailForm.email || !scope.mailForm.description) {
+ return;
+ } else {
+ if (!emailService.validate(scope.mailForm.email)) {
+ scope.error = 'Invalid email address';
+ return;
+ }
+ scope.error = '';
+ scope.loading = true;
+ scope.mailForm.user_email = $rootScope.user.email;
+ scope.mailForm.username = $rootScope.user.username;
+ emailObj = emailService.buildObject(scope.mailForm);
+
+ emailService.send(emailObj, function (res) {
+ if (res.status !== 200) {
+ scope.loading = false;
+ scope.$apply();
+ return notify.failure(res);
+ }
+ emailService.reset(scope.mailForm);
+ tracking.dispatchTrackingEvent('worldExternalShare');
+
+ scope.loading = false;
+ scope.close();
+ scope.$apply();
+
+ return notify.success();
+ }, function (error) {
+ scope.loading = false;
+ scope.$apply();
+ return notify.failure(error);
+ });
+ }
+ };
+ }
+ };
+});
diff --git a/lib/editor/token-handler/BaseHandler.js b/lib/editor/token-handler/BaseHandler.js
index c41ee5aad..d92a5999b 100644
--- a/lib/editor/token-handler/BaseHandler.js
+++ b/lib/editor/token-handler/BaseHandler.js
@@ -1,3 +1,4 @@
+"use strict";
/*
* BaseHandler class
*
@@ -10,7 +11,8 @@
* token.
*/
-var SPACING = 15;
+var SPACING = 15,
+ _ = require('lodash');
/*
* BaseHandler constructor
@@ -81,13 +83,17 @@ BaseHandler.prototype.getTemplate = function () {
BaseHandler.prototype.resize = function () {
// Next tick..
setTimeout(function () {
- if (!this.el || !this.token.el) { return; }
+ var bbox, tokenBbox, x, y;
+
+ if (!this.el || !this.token.el) {
+ return;
+ }
this.el.style.display = 'block';
- var bbox = this.el.getBoundingClientRect(),
- tokenBbox = this.token.el.getBoundingClientRect(),
- x = tokenBbox.left + tokenBbox.width / 2 - bbox.width / 2 + document.body.scrollLeft,
- y = tokenBbox.top - bbox.height - SPACING + document.body.scrollTop;
+ bbox = this.el.getBoundingClientRect();
+ tokenBbox = this.token.el.getBoundingClientRect();
+ x = tokenBbox.left + tokenBbox.width / 2 - bbox.width / 2 + document.body.scrollLeft;
+ y = tokenBbox.top - bbox.height - SPACING + document.body.scrollTop;
this.el.style.left = Math.floor(x) + 'px';
this.el.style.top = Math.floor(y) + 'px';
@@ -109,7 +115,15 @@ BaseHandler.prototype.close = function () {
* @return {Object}
*/
BaseHandler.prototype.getTokenElement = function () {
- var lineElement = this.getLineElement();
+ var lineElement = this.getLineElement(),
+ token = this.token;
+
+ if (lineElement.childNodes[this.token.index].nodeName === '#text') {
+ this.token.index = _.findIndex(lineElement.childNodes, function (el, index) {
+ return (el.innerText === token.value) && (index >= token.index);
+ });
+ }
+
return lineElement.childNodes[this.token.index];
};
@@ -124,14 +138,14 @@ BaseHandler.prototype.getLineElement = function () {
* @return [{Object}]
*/
BaseHandler.prototype.getAllTokens = function () {
- var lines = this.session.getValue().split('\n'),
+ var i, lines = this.session.getValue().split('\n'),
tokens = [];
- for (var i = 0; i < lines.length; i += 1) {
+ for (i = 0; i < lines.length; i += 1) {
tokens = tokens.concat(this.session.getTokens(i));
}
return tokens;
};
-module.exports = BaseHandler;
\ No newline at end of file
+module.exports = BaseHandler;
diff --git a/lib/i18n.js b/lib/i18n.js
new file mode 100644
index 000000000..bc6e21dc4
--- /dev/null
+++ b/lib/i18n.js
@@ -0,0 +1,54 @@
+var url = require('url');
+
+var SUPPORTED_LOCALES = [
+ 'ja',
+ 'en',
+ 'es',
+];
+
+function getLanguage() {
+ var parsedUrl = url.parse(window.location.href, true);
+
+ if (parsedUrl.query['lang'] !== undefined) {
+ var lang = parsedUrl.query['lang'];
+ if (SUPPORTED_LOCALES.indexOf(lang) > -1) {
+ return lang;
+ }
+ // not in supported languages so fall back to normal detection
+ }
+
+ var language = window.navigator.languages ? window.navigator.languages[0] :
+ window.navigator.userLanguage || window.navigator.language || 'en',
+ p = language.indexOf('-');
+
+ // check if full locale is supported
+ if (SUPPORTED_LOCALES.indexOf(language) > -1) {
+ return language;
+ }
+
+ language = language.toLowerCase();
+
+ // otherwise check that if just language is supported
+ language = (p > -1) ? language.substr(0, p) : language;
+ if (SUPPORTED_LOCALES.indexOf(language) > -1) {
+ return language;
+ }
+ // language is not supported so default to 'en'
+ return 'en';
+}
+
+function getChallengeLocalePath () {
+ var language = getLanguage();
+ return language === 'en' ? '' : '/locales/' + language;
+}
+
+function getHtmlLocalePath () {
+ var language = getLanguage();
+ return '/locales/' + language;
+}
+
+module.exports = {
+ getLanguage: getLanguage,
+ getHtmlLocalePath: getHtmlLocalePath,
+ getChallengeLocalePath: getChallengeLocalePath
+};
diff --git a/lib/index.js b/lib/index.js
index 0d0957eb3..f14c8f1c2 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,3 +1,4 @@
+"use strict";
/*
* Main Index module
*
@@ -14,6 +15,8 @@ require('./routes');
// Service
require('./service/email');
+require('./service/content');
+require('./service/social');
// Controllers
require('./controller/main');
@@ -27,10 +30,14 @@ require('./controller/share');
require('./controller/local-launch');
require('./controller/examples');
require('./controller/promo-popup');
+require('./controller/feedback');
+require('./controller/auth-form');
// Directives
require('./directive/workspace');
require('./directive/editor');
require('./directive/display');
require('./directive/progress-circle');
-require('./directive/export-modal');
\ No newline at end of file
+require('./directive/export-modal');
+require('./directive/auth');
+require('./directive/social');
diff --git a/lib/language/index.js b/lib/language/index.js
index 40cde6bcc..1b945556d 100644
--- a/lib/language/index.js
+++ b/lib/language/index.js
@@ -5,14 +5,10 @@
*/
var coffee = require('coffee-script'),
- config = require('../core/config'),
+ config,
session = require('./session'),
- modules = require('./modules/index');
-
-var ERROR_LOC_REGEX = /\s+at Object\.eval \((.+):(\d+):(\d+)\)/,
- FORLOOP_REGEX = /^\s*for\s+([a-zA-Z0-9_\\@\\$])+\s+in\s*\s*\[([a-zA-Z0-9_\s\-\*\+\.\\]+)\]\s*$/,
- FUNC_REGEX = /^([A-Za-z0-9_]+)(\(.*\))/,
- VAR_DEC_REGEX = /^([A-Za-z0-9_]+)=(.+)/;
+ modules = require('./modules/index'),
+ ERROR_LOC_REGEX = /\s+at Object\.eval \((.+):(\d+):(\d+)\)/;
/*
* Reset current drawing session
@@ -21,6 +17,7 @@ var ERROR_LOC_REGEX = /\s+at Object\.eval \((.+):(\d+):(\d+)\)/,
* @return void
*/
function resetSession(settings) {
+ "use strict";
session.ctx = settings.ctx;
session.width = settings.width;
session.height = settings.height;
@@ -40,6 +37,7 @@ function resetSession(settings) {
function run(code, settings) {
var compiled;
+ config = window.CONFIG;
code = preCompile(code || '');
// Reset session
@@ -85,22 +83,13 @@ function run(code, settings) {
}
/*
- * Precompile step - Add forLoopRecorder functions and empty blocks after loops
+ * Precompile step - Cleans the code up
*
* @param {String} code
* @return {String}
*/
function preCompile(code) {
- var lines,
- i,
- line,
- forMatch,
- varMatch,
- funcMatch,
- recLine,
- voidLine,
- funcName,
- indent;
+ "use strict";
code = code
// Only use fat arrow (Block access to Window through `this`)
.replace(/->/g, '=>')
@@ -109,62 +98,7 @@ function preCompile(code) {
return start + '->';
});
-
- //this array will contain those functions which are customly defined by the user
- session.declaredFunctions = [];
-
- lines = code.split('\n');
-
- //if it's an if
- for (i = 0; i < lines.length; i += 1) {
- line = lines[i];
-
- forMatch = line.match(FORLOOP_REGEX);
- varMatch = line.replace(/ /g, '').match(VAR_DEC_REGEX);
- funcMatch = line.replace(/ /g, '').match(FUNC_REGEX);
-
- if (forMatch) {
- indent = getIndentation(line);
-
- recLine = 'recordForLoop("' + forMatch[1] + '", "' + forMatch[2] + '")';
- recLine = setIndentation(recLine, indent);
- voidLine = setIndentation('" "', indent + 1);
-
- lines[i] = [ recLine, lines[i], voidLine ].join('\n');
- } else if (varMatch) {
- if (varMatch[2].indexOf('=>') > -1) {
- //it's a function declaration
- session.declaredFunctions.push(varMatch[1]);
- }
- indent = getIndentation(line);
- recLine = 'recordVar("'+ varMatch[1] +'", "' + varMatch[2].replace(/\"/g, '\\\"') +'")';
- recLine = setIndentation(recLine, indent);
-
- lines[i] = [recLine, lines[i]].join('\n');
- } else if (funcMatch) {
- //we're hitting this branch only if it's a function call not part of a variable assignment
- funcName = funcMatch[1];
- if (session.declaredFunctions.indexOf(funcName) > -1) {
- indent = getIndentation(line);
- recLine = 'recordFunc("'+ funcMatch[1] +'", "' + funcMatch[2].replace(/\"/g, '\\\"') +'")';
- recLine = setIndentation(recLine, indent);
- lines[i] = [recLine, lines[i]].join('\n');
-
- }
-
- }
-
- }
-
- return lines.join('\n');
-}
-
-function setIndentation(str, tabs) {
- return new Array(tabs + 1).join(' ') + str;
-}
-
-function getIndentation(str) {
- return Math.floor(str.match(/^\s*[!\s]*/)[0].replace(/\t/g, ' ').length / 4);
+ return code;
}
/*
@@ -174,8 +108,11 @@ function getIndentation(str) {
* @return [{Number}]
*/
function getErrorLocation(err) {
+ "use strict";
var trace,
- traceTop;
+ traceTop,
+ x,
+ y;
trace = err.stack.split('\n').map(function (line) {
return line.match(ERROR_LOC_REGEX);
@@ -186,10 +123,10 @@ function getErrorLocation(err) {
if (trace.length > 0) {
traceTop = trace[0];
- var x = parseInt(traceTop[2], 10) -1,
- y = parseInt(traceTop[3], 10) - 1;
+ x = parseInt(traceTop[2], 10) - 1;
+ y = parseInt(traceTop[3], 10) - 1;
- return [ x, y ];
+ return [x, y];
} else {
return null;
}
@@ -202,19 +139,32 @@ function getErrorLocation(err) {
* @return {String}
*/
function strip(code) {
+ "use strict";
if (code) {
code = code
// Remove multi-line comments
.replace(/###((.|\n)*)###/gm, '')
// Remove inline comments
- .replace(/(#.*(\n|$))/g, '')
+ .replace(/[^' ](#.*)|^[ ]*(#.*)($|\n)/g, '')
// Remove spaces
.replace(/^\s*[\r\n]/gm, '');
// Trim spaces on edges
return code.trim();
}
- return code;
+ return code;
+}
+
+
+function checkValidity(code) {
+ "use strict";
+ var compiled;
+ try {
+ compiled = coffee.compile(code || '', { sourceMap: true });
+ return true;
+ } catch (err) {
+ return false;
+ }
}
/*
@@ -256,5 +206,7 @@ function evalInContext(code) {
module.exports = {
run : run,
- strip : strip
+ strip : strip,
+ checkValidity: checkValidity,
+ evalInContext: evalInContext
};
diff --git a/lib/language/modules/colors.js b/lib/language/modules/colors.js
index b90eeb2c9..ddaac1219 100644
--- a/lib/language/modules/colors.js
+++ b/lib/language/modules/colors.js
@@ -136,6 +136,31 @@ function rotate(color, amount) {
return Color(color).rotate(amount).rgbaString();
}
+/*
+ * Rotate color hue by given amount
+ *
+ * @param {Number} red
+ * @param {Number} green
+ * @param {Number} blue
+ * @return {String}
+ */
+function rgb(red, green, blue) {
+ return "rgb(" + red + ", " + green + ", " + blue + ")";
+}
+
+/*
+ * Rotate alpha color hue by given amount
+ *
+ * @param {Number} red
+ * @param {Number} green
+ * @param {Number} blue
+ * @param {Number} alpha
+ * @return {String}
+ */
+function rgba(red, green, blue, alpha) {
+ return "rgba(" + red + ", " + green + ", " + blue + ", " + alpha + ")";
+}
+
/*
* Get color opacity or set it to value if it's passed
*
@@ -233,9 +258,11 @@ module.exports = {
hue : hue,
setTransparency : setTransparency,
rotate : rotate,
+ rgb : rgb,
+ rgba : rgba,
opacity : opacity,
opacize : opacize,
transparentize : transparentize,
setBrightness : setBrightness,
setSaturation : setSaturation
-};
\ No newline at end of file
+};
diff --git a/lib/language/modules/palette.json b/lib/language/modules/palette.json
index 22a40a1dd..2cf513c92 100644
--- a/lib/language/modules/palette.json
+++ b/lib/language/modules/palette.json
@@ -13,6 +13,7 @@
"brown" : "#A36A42",
"burlywood" : "#DEB887",
"cadetblue" : "#5F9EA0",
+ "charcoal" : "#333333",
"chartreuse" : "#7FFF00",
"chocolate" : "#D2691E",
"coral" : "#FF7F50",
@@ -136,6 +137,7 @@
"steelblue" : "#4682B4",
"tan" : "#D2B48C",
"teal" : "#008080",
+ "transparent" : "rgba(0,0,0,0)",
"thistle" : "#D8BFD8",
"tomato" : "#FF6347",
"turquoise" : "#40E0D0",
diff --git a/lib/language/modules/security.js b/lib/language/modules/security.js
index 8d6fec684..54caa0b6a 100644
--- a/lib/language/modules/security.js
+++ b/lib/language/modules/security.js
@@ -6,10 +6,15 @@
var OVERRIDES = [
'window',
+ 'top',
'Window',
+ 'Document',
'document',
'console',
'alert',
+ 'addEventListener',
+ 'removeEventListener',
+ 'releaseEvents',
'localStorage',
'KW_SDK',
'angular',
@@ -17,8 +22,20 @@ var OVERRIDES = [
'$',
'open',
'location',
+ 'parent',
+ 'postMessage',
+ 'print',
+ 'profile',
+ 'prompt',
'requestAnimationFrame',
'cancelAnimationFrame',
+ 'scroll',
+ 'scrollBy',
+ 'scrollTo',
+ 'scrollX',
+ 'scrollY',
+ 'session',
+ 'sessionStorage',
'setTimeout',
'setInterval'
];
@@ -28,4 +45,4 @@ module.exports = {};
// Build exports object
OVERRIDES.forEach(function (key) {
module.exports[key] = null;
-});
\ No newline at end of file
+});
diff --git a/lib/language/synonyms.json b/lib/language/synonyms.json
new file mode 100644
index 000000000..6c779a99f
--- /dev/null
+++ b/lib/language/synonyms.json
@@ -0,0 +1,4 @@
+{
+ "color": ["colour"],
+ "grey": ["gray"]
+}
diff --git a/lib/routes.js b/lib/routes.js
index 529a615d3..40fc886b7 100644
--- a/lib/routes.js
+++ b/lib/routes.js
@@ -1,4 +1,5 @@
-var app = require('./app');
+var i18n = require('./i18n'),
+ app = require('./app');
/*
* Routes module
@@ -9,39 +10,44 @@ var app = require('./app');
app.config(function ($routeProvider) {
$routeProvider
- // Challenge view (Defaults to first challenge)
- .when('/challenge', {
- templateUrl : '/challenge.html',
- controller : 'ChallengeController'
+ .when('/', {
+ template: '',
+ controller : 'MainController'
})
// Challenges selection view
.when('/challenges', {
- templateUrl : '/challenges.html',
+ templateUrl : i18n.getHtmlLocalePath() + '/challenges.html',
controller : 'ChallengesController'
})
// Challenge view (With challenge ID)
- .when('/challenge/:id', {
- templateUrl : '/challenge.html',
+ .when('/challenge/:world/:id', {
+ templateUrl : i18n.getHtmlLocalePath() + '/challenge.html',
controller : 'ChallengeController'
})
+ // Challenge view (With only worldID)
+ .when('/challenges/:world/', {
+ templateUrl : i18n.getHtmlLocalePath() + '/challenges.html',
+ controller : 'ChallengesController'
+ })
+
// Playground view
.when('/playground', {
- templateUrl : '/playground.html',
+ templateUrl : i18n.getHtmlLocalePath() + '/playground.html',
controller : 'PlaygroundController'
})
// Load share item by ID view
.when('/share/:id', {
- templateUrl : '/share.html',
+ templateUrl : i18n.getHtmlLocalePath() + '/share.html',
controller : 'ShareController'
})
// Launch a local share
.when('/localLoad/:path*', {
- templateUrl : '/share.html',
+ templateUrl : i18n.getHtmlLocalePath() + '/share.html',
controller : 'LocalLaunchController'
})
diff --git a/lib/service/content.js b/lib/service/content.js
new file mode 100644
index 000000000..49afa39f2
--- /dev/null
+++ b/lib/service/content.js
@@ -0,0 +1,177 @@
+"use strict";
+var app = require("../app"),
+ i18n = require("../i18n"),
+ _ = require('lodash'),
+ config = window.CONFIG;
+
+app.factory('contentService', function ($http, $q, $rootScope) {
+ function getWorlds () {
+ var defer = $q.defer(),
+ url = config.CHALLENGES_URL + i18n.getChallengeLocalePath() + "/index.json";
+ $http({
+ method: 'GET',
+ url: url
+ }).then(function successCallback(response) {
+ defer.resolve(response.data.worlds);
+ }, function errorCallback(response) {
+ defer.reject(response);
+ });
+ return defer.promise;
+ }
+
+ function getWorld(contentUrl) {
+ var defer = $q.defer(),
+ url = config.CHALLENGES_URL + i18n.getChallengeLocalePath() + "/" + contentUrl + "/index.json";
+ $http({
+ method: 'GET',
+ url: url
+ }).then(function successCallback(response) {
+
+ defer.resolve(response.data);
+
+ }, function errorCallback(response) {
+
+ defer.reject(response);
+
+ });
+ return defer.promise;
+ }
+
+ function getChallenge(world, challenge_id) {
+ var defer = $q.defer(),
+ url = config.CHALLENGES_URL + i18n.getChallengeLocalePath() + "/worlds/" + world + "/" + challenge_id + ".json";
+ $http({
+ method: 'GET',
+ url: url
+ }).then(function successCallback(response) {
+ defer.resolve(response.data);
+ }, function errorCallback(response) {
+ defer.reject(response);
+ });
+ return defer.promise;
+ }
+
+ function isChallengeLocked(challenge) {
+ var index = challenge.index,
+ now = new Date(),
+ start_date = challenge.start_date;
+
+ if (start_date) {
+ start_date = new Date(start_date);
+ if (start_date > now) {
+ return true;
+ }
+ }
+
+ return index > $rootScope.inWorldProgress.challengeNo;
+ }
+
+ function isChallengeCurrent(challenge) {
+ return challenge.index === $rootScope.inWorldProgress.challengeNo;
+ }
+
+ function isChallengeCompleted(challenge) {
+ return challenge.index < $rootScope.inWorldProgress.challengeNo;
+ }
+
+ function isWorldLocked(world) {
+ var now = new Date(),
+ date_status,
+ start_date;
+
+ if (world.start_date) {
+ start_date = new Date(world.start_date);
+ date_status = start_date >= now;
+
+ if (date_status) {
+ return true;
+ }
+ }
+
+ if (world.dependency) {
+ return !areDependenciesCompleted(world);
+ }
+
+ return false;
+ }
+
+ function isWorldCurrent(world) {
+ return (world.type === "normal") && isWorldLocked(world) && !isWorldCompleted(world);
+ }
+
+ function isWorldCompleted(world) {
+ var progress = getProgressGroup(world.id);
+ if (progress.challengeNo > world.challenges_num) {
+ return true;
+ }
+ return false;
+ }
+
+ function areDependenciesCompleted(world) {
+ var completed = true;
+ if (world.dependency) {
+ world.dependency.forEach(function (worldId) {
+ completed = completed && isWorldCompleted(getWorldById(worldId));
+ });
+ }
+ return completed;
+ }
+
+ function getWorldById(id) {
+ var worldObj = _.filter($rootScope.worlds, function (world) {
+ return world.id === id ? world : null;
+ });
+ return worldObj[0];
+ }
+
+ function getProgressGroup(worldId) {
+ var progressGroup = $rootScope.progress.groups[worldId];
+
+ if (!progressGroup) {
+ progressGroup = {challengeNo: 1};
+ $rootScope.progress.groups[worldId] = progressGroup;
+ }
+
+ return progressGroup;
+ }
+
+ function isWorldVisibile(world) {
+ return world.visibility !== 'hidden';
+ }
+
+ function isCertificateAchieved(world) {
+ var progress;
+
+ if (world) {
+ progress = getProgressGroup(world.id);
+
+ if (world.certificate_after && progress.challengeNo > world.certificate_after) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ return {
+ challenge: {
+ get : getChallenge,
+ isLocked : isChallengeLocked,
+ isCurrent : isChallengeCurrent,
+ isCompleted : isChallengeCompleted
+ },
+ world: {
+ get : getWorld,
+ getById : getWorldById,
+ getAll : getWorlds,
+ isLocked : isWorldLocked,
+ isCurrent : isWorldCurrent,
+ isCompleted : isWorldCompleted,
+ isVisible : isWorldVisibile,
+ isCertAchieved: isCertificateAchieved
+ },
+ progress: {
+ get : getProgressGroup
+ }
+ };
+});
diff --git a/lib/service/email.js b/lib/service/email.js
index 50b965927..4a5a60217 100644
--- a/lib/service/email.js
+++ b/lib/service/email.js
@@ -1,48 +1,41 @@
+"use strict";
var app = require('../app');
-app.factory('emailService', function ($http, $rootScope){
- function resetFields (item) {
- item.name = '';
- item.message = '';
- item.email = '';
- }
+app.factory('emailService', function ($rootScope) {
+ function resetFields(item) {
+ item.email = '';
+ }
- function buildEmailObject (item) {
- return {
- name : item.name ? item.name : item.username,
- message : item.message,
- title : item.shareTitle ? item.shareTitle : item.short_title,
- world_url : item.world_url,
- cover_url : item.cover_url ? item.cover_url : 'http://art.kano.me/' + item.img,
- user_email : item.user_email,
- email : item.email
- };
- }
+ function buildEmailObject(item) {
+ return {
+ type : item.type,
+ options : {
+ from_name : item.username,
+ from_email : item.user_email,
+ to_name : item.to_name || null,
+ to_email : item.email,
+ message : item.description || null,
+ title : item.title || null,
+ url : item.url || null,
+ cover_url : item.cover_url || null
+ }
+ };
+ }
- function emailer (item, successCB, errorCB) {
- var host = $rootScope.cfg.MAILSERVER;
+ function emailer(item, successCB, errorCB) {
+ $rootScope.api.mailer(item)
+ .then(successCB, errorCB);
+ }
- var req = {
- method : 'POST',
- url : host,
- headers : {
- 'Content-Type': 'application/json'
- },
- data : JSON.stringify(item)
- };
+ function validateEmail(email) {
+ var regExp = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
+ return regExp.test(email);
+ }
- $http(req).then(successCB, errorCB);
- }
-
- function validateEmail(email) {
- var regExp = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
- return regExp.test(email);
- }
-
- return {
- reset : resetFields,
- buildObject : buildEmailObject,
- send : emailer,
- validate : validateEmail
- };
-});
\ No newline at end of file
+ return {
+ reset : resetFields,
+ buildObject : buildEmailObject,
+ send : emailer,
+ validate : validateEmail
+ };
+});
diff --git a/lib/service/social.js b/lib/service/social.js
new file mode 100644
index 000000000..c8c3a3638
--- /dev/null
+++ b/lib/service/social.js
@@ -0,0 +1,29 @@
+"use strict";
+var app = require('../app'),
+ facebook = require('../core/facebook'),
+ twitter = require('../core/twitter');
+
+app.factory('socialService', function ($rootScope) {
+ function init() {
+ facebook.init();
+ twitter.init();
+ }
+
+ function buildURL(creation) {
+ var text;
+ if (creation) {
+ text = "For #" + creation.world.replace(/ /g, '') + " with @TeamKano I created " + creation.title + " on Make Art.\n" + creation.url;
+ } else {
+ text = $rootScope.selectedWorld.socialText.twitter;
+ }
+ return encodeURI("https://twitter.com/intent/tweet?text=" + text).replace(/#/g, '%23');
+ }
+
+ twitter.build = buildURL;
+
+ return {
+ init: init,
+ facebook: facebook,
+ twitter: twitter
+ };
+});
diff --git a/lib/util/string.js b/lib/util/string.js
index 442f95e34..052489bd1 100644
--- a/lib/util/string.js
+++ b/lib/util/string.js
@@ -1,3 +1,5 @@
+"use strict";
+
/*
* String utilities
*
@@ -13,4 +15,13 @@
*/
exports.splice = function (str, index, count, add) {
return str.slice(0, index) + (add || '') + str.slice(index + count);
-};
\ No newline at end of file
+};
+
+/**
+ * Escapes a string to be used as RegExp
+ * @param {string} value The string
+ * @return {string} The escaped string
+ */
+exports.escapeRegex = function (value) {
+ return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
+};
diff --git a/locales/en/challenge.json b/locales/en/challenge.json
new file mode 100644
index 000000000..db0f47962
--- /dev/null
+++ b/locales/en/challenge.json
@@ -0,0 +1,23 @@
+{
+ "challenge_title": "Challenge {{ id }}:",
+ "start": "Start",
+ "hint": "Hint",
+ "next": "Next",
+ "done": "Done",
+ "can_you_hack_this_challenge": "Can you hack this challenge?",
+ "make_it_into_a_fathers_day_card": "Make it into a Father's day card!",
+ "now_sign_up_to_kano_world_to_share_your_creation": "Now sign-up to Kano World to share your creation and send a card to your dad!",
+ "share_your_fathers_day_card": "Share your Father's day card!",
+ "now_you_can_share_the_card_on_kano_world": "Now you can share the card on Kano World and share it to your dad!",
+ "challenges_completed": "Challenges completed",
+ "now_make_something_new": "Now make something new!",
+ "new_world_unlocked_play_now": "New world unlocked! Play Now - ",
+ "menu": "Menu",
+ "playground": "Playground",
+ "you_did_it_go_back_for_more_coding_fun": "You did it, go back for more coding fun!",
+ "hour_of_code": "Hour Of Code",
+ "solution": "Solution",
+ "success_online": "{{ content.completion_text || 'Well done!' }}",
+ "success_offline": "{{ content.completion_text || 'Well done!' }} You've earned {{ xpGain }} XP!"
+}
+
diff --git a/locales/en/challenges.json b/locales/en/challenges.json
new file mode 100644
index 000000000..b1d430d3b
--- /dev/null
+++ b/locales/en/challenges.json
@@ -0,0 +1,12 @@
+{
+ "challenges": "Challenges",
+ "get_kano_updates": "Get Kano Updates",
+ "sign_up": "Sign Up",
+ "playground": "Playground",
+ "certificate": "Certificate",
+ "teachers_guide": "Teacher's Guide",
+ "latest_shares": "Latest Shares",
+ "browse_more": "Browse more",
+ "by": "by"
+}
+
diff --git a/locales/en/directive.json b/locales/en/directive.json
new file mode 100644
index 000000000..9b1352f7c
--- /dev/null
+++ b/locales/en/directive.json
@@ -0,0 +1,29 @@
+{
+ "save_to_gallery": "save to gallery",
+ "save_drawing": "save drawing",
+ "title": "Title",
+ "description": "Description",
+ "save": "Save",
+ "skip": "Skip",
+ "save_wallpaper": "Save Wallpaper",
+ "login_to_your_account": "Login to Your Account",
+ "create_your_account": "Create your account",
+ "share": "Share",
+ "save": "Save",
+ "reset": "Reset",
+ "load": "Load",
+ "toggle_autocomplete": "Toggle autocomplete",
+ "autocomplete": "Autocomplete",
+ "toggle_fullscreen": "Toggle fullscreen",
+ "fullscreen": "Fullscreen",
+ "download_teachers_guide": "Download teacher's guide",
+ "click_plus_to_add_a_shape_1": "Click",
+ "click_plus_to_add_a_shape_2": "to add a shape to your code",
+ "download_teachers_guide": "Download teacher's guide",
+ "guide": "Guide",
+ "recipients_email_address": "Recipient\\'s email address",
+ "your_message": "Your message",
+ "error": "{{ error.type | upperfirst }} Error",
+ "error_line": "on line {{ error.row + 1 || 0 }}",
+ "error_text": "{{ error.text || 0 }}"
+}
diff --git a/locales/en/partial.json b/locales/en/partial.json
new file mode 100644
index 000000000..09e4e3cff
--- /dev/null
+++ b/locales/en/partial.json
@@ -0,0 +1,35 @@
+{
+ "pixel_hack": "Pixel Hack:",
+ "code_your_way_through_the_history": "code your way through the history of video game art.",
+ "welcome_to_pixel_hack": "Welcome to Pixel Hack",
+ "start_your_coding_adventure": "Start your coding adventure by drawing Pong and other retro games. Move on to master 8-bit art for your own Minecraft characters and more. Show us how you can hack our challenges. Earn your Pixel Master Badge.",
+ "happy_hacking": "Happy Hacking!",
+ "claim_your_pixel_hack_certificate": "Claim your Pixel Hack certificate.",
+ "visit_this_address_from_a_computer": "Visit this address from a computer with a connected printer to create your certificate.",
+ "discover_kano": "Discover Kano",
+ "login": "Login",
+ "signup": "Sign up",
+ "logout": "Logout",
+ "feedback": "Feedback",
+ "whats_up": "What\\'s up?",
+ "thank_you": "Thank You!",
+ "send": "Send",
+ "load": "Load",
+ "load_from_where": "Load from where",
+ "your_kano": "Your Kano",
+ "internet": "Internet",
+ "share_by_email": "Share by email",
+ "enter_an_email_address": "Enter an email address",
+ "i_just_made_this_with_code": "I just made this with code! Want to try your own? It's easy, fun and for everyone",
+ "your_name": "Your name...",
+ "next_up": "Next up:",
+ "back_to_world": "'Back to ' + selectedWorld.name",
+ "come_back_tomorrow_to_continue": "Come back tomorrow to continue this challenge",
+ "share_on_facebook": "Share on Facebook",
+ "share_on_twitter": "Share on Twitter",
+ "email_address": "Email address",
+ "share_with_friends": "Share with friends:",
+ "skip_sharing": "Skip sharing",
+ "start":"Start",
+ "draw_with_code":"Draw With Code"
+}
diff --git a/locales/es/challenge.json b/locales/es/challenge.json
new file mode 100644
index 000000000..32b0ccf54
--- /dev/null
+++ b/locales/es/challenge.json
@@ -0,0 +1,22 @@
+{
+ "new_world_unlocked_play_now": "¡Nuevo mundo desbloqueado! Jugar Ahora - ",
+ "now_sign_up_to_kano_world_to_share_your_creation": "¡Ahora entra al Mundo Kano para compartir tus creaciones y enviar una tarjeta a tu padre!",
+ "hint": "Pista",
+ "share_your_fathers_day_card": "¡Comparte tu tarjeta del Día del Padre!",
+ "menu": "Menú",
+ "now_you_can_share_the_card_on_kano_world": "¡Ahora puedes compartir tu tarjeta en Mundo Kano y también con tu padre!",
+ "solution": "Solución",
+ "next": "Siguiente",
+ "challenges_completed": "Desafíos completos",
+ "start": "Empezar",
+ "hour_of_code": "Hora de programar",
+ "challenge_title": "Desafío {{ id }}:",
+ "done": "Hecho",
+ "playground": "Playground",
+ "now_make_something_new": "¡Ahora crea algo nuevo!",
+ "can_you_hack_this_challenge": "¿Puedes hackear este desafío?",
+ "you_did_it_go_back_for_more_coding_fun": "¡Lo lograste, vuelve atrás para programar más!",
+ "make_it_into_a_fathers_day_card": "¡Crea una tarjeta para el Día del Padre!",
+ "success_online": "{{ content.completion_text || 'Buen trabajo!' }}",
+ "success_offline": "{{ content.completion_text || 'Buen trabajo!' }} ¡Ganaste {{ xpGain }} PX!"
+}
diff --git a/locales/es/challenges.json b/locales/es/challenges.json
new file mode 100644
index 000000000..60626e1a8
--- /dev/null
+++ b/locales/es/challenges.json
@@ -0,0 +1,11 @@
+{
+ "browse_more": "Buscar más",
+ "certificate": "Certificado",
+ "get_kano_updates": "Obtener actualizaciones de Kano",
+ "teachers_guide": "Guía docente",
+ "challenges": "Desafíos",
+ "playground": "Playground",
+ "latest_shares": "Últimos compartidos",
+ "sign_up": "Ingresar",
+ "by": "por"
+}
diff --git a/locales/es/directive.json b/locales/es/directive.json
new file mode 100644
index 000000000..5a8d31db0
--- /dev/null
+++ b/locales/es/directive.json
@@ -0,0 +1,27 @@
+{
+ "load": "Cargar",
+ "recipients_email_address": "Correo electrónico del destinatario",
+ "your_message": "Tu mensaje",
+ "skip": "Saltear",
+ "share": "Compartir",
+ "save_drawing": "guardar dibujo",
+ "toggle_autocomplete": "Activar autocompletar",
+ "fullscreen": "Pantalla completa",
+ "title": "Título",
+ "download_teachers_guide": "Descargar guía docente",
+ "save_to_gallery": "guardar en galería",
+ "save_wallpaper": "Guardar fondo de pantalla",
+ "login_to_your_account": "Ingresa a tu cuenta",
+ "create_your_account": "Crea tu cuenta",
+ "save": "Guardar",
+ "description": "Descripción",
+ "click_plus_to_add_a_shape_1": "Haz clic",
+ "toggle_fullscreen": "Activar pantalla completa",
+ "reset": "Reiniciar",
+ "click_plus_to_add_a_shape_2": "para agregar una figura",
+ "autocomplete": "Autocompletar",
+ "guide": "Guía",
+ "error": "Error de Sintaxis",
+ "error_line": "en la línea {{ error.row + 1 || 0 }}",
+ "error_text": ""
+}
\ No newline at end of file
diff --git a/locales/es/partial.json b/locales/es/partial.json
new file mode 100644
index 000000000..087f8938a
--- /dev/null
+++ b/locales/es/partial.json
@@ -0,0 +1,35 @@
+{
+ "load": "Cargar",
+ "feedback": "Opinión y comentarios",
+ "load_from_where": "Cargar desde aquí",
+ "send": "Enviar",
+ "i_just_made_this_with_code": "¡Acabo de programar esto! ¿Quieres intentarlo? Es fácil y divertido para cualquiera",
+ "discover_kano": "Descubre Kano",
+ "code_your_way_through_the_history": "programa el camino históricamente recorrido por el arte en videojuegos.",
+ "email_address": "Correo electrónico",
+ "start_your_coding_adventure": "Comienza tu aventura en programación dibujando Ping Pong y otros juegos retro. Avanza a arte en 8-bits para tus propias creaciones de personajes de Minecraft y más. Muéstranos cómo hackear nuestros desafíos. Gana la insignia Pixel Master.",
+ "your_kano": "Tu Kano",
+ "whats_up": "¿Qué hay de nuevo?",
+ "welcome_to_pixel_hack": "Bienvenido a Pixel Hack",
+ "come_back_tomorrow_to_continue": "Vuelve mañana para continuar con este desafío",
+ "claim_your_pixel_hack_certificate": "Reclama tu certificado de Pixel Hack.",
+ "pixel_hack": "Pixel Hack:",
+ "happy_hacking": "¡Suerte Hackeando!",
+ "internet": "Internet",
+ "next_up": "Siguiente:",
+ "skip_sharing": "Saltear compartir",
+ "logout": "Salir",
+ "share_on_facebook": "Compartir en Facebook",
+ "share_with_friends": "Compartir con amigos:",
+ "visit_this_address_from_a_computer": "Visita esta página desde una computadora que se encuentre conectada a una impresora para crear tu certificado.",
+ "share_on_twitter": "Compartir en Twitter",
+ "share_by_email": "Compartir vía correo electrónico",
+ "enter_an_email_address": "Ingresa un correo electrónico",
+ "your_name": "Tu nombre...",
+ "login": "Ingresar",
+ "signup": "Regístrate",
+ "back_to_world": "'Atrás' + selectedWorld.name",
+ "thank_you": "¡Gracias!",
+ "start":"Comienza",
+ "draw_with_code":"Dibujar Con Código"
+}
diff --git a/locales/ja/challenge.json b/locales/ja/challenge.json
new file mode 100644
index 000000000..25cfe7dbd
--- /dev/null
+++ b/locales/ja/challenge.json
@@ -0,0 +1,20 @@
+{
+ "challenge_title": "{{ id }}チャレンジ:",
+ "start": "スタート",
+ "hint": "ヒント",
+ "next": "次へ",
+ "done": "終わり",
+ "can_you_hack_this_challenge": "これもチャレンジしてみるかい?",
+ "make_it_into_a_fathers_day_card": "父の日のカードにしよう!",
+ "now_sign_up_to_kano_world_to_share_your_creation": "さあ、Kanoワールドに登路し、作品を共有し、お父さんへカードを送ろう!",
+ "share_your_fathers_day_card": "父の日のカードを共有しよう!",
+ "now_you_can_share_the_card_on_kano_world": "よっし、Kanoワールドでカードを共有し、お父さんへ送られるよ!",
+ "challenges_completed": "チャレンジ完了!",
+ "now_make_something_new": "さあ、新しいものを作ろう!",
+ "new_world_unlocked_play_now": "新しいワールドに入れるよ! 早速遊ぼうー",
+ "menu": "メニュー",
+ "playground": "遊び場",
+ "you_did_it_go_back_for_more_coding_fun": "やったぜ! 戻って、コードでもっと遊ぼう!",
+ "hour_of_code": "ハウアー・オブ・コード",
+ "solution": "正解は…"
+}
diff --git a/locales/ja/challenges.json b/locales/ja/challenges.json
new file mode 100644
index 000000000..1f537ad39
--- /dev/null
+++ b/locales/ja/challenges.json
@@ -0,0 +1,12 @@
+{
+ "challenges": "チャレンジ一覧",
+ "get_kano_updates": "Kanoお知らせを受け取る",
+ "sign_up": "登録",
+ "playground": "遊び場",
+ "certificate": "証明書",
+ "teachers_guide": "講師用のガイド",
+ "latest_shares": "最新の共有",
+ "browse_more": "もっと見る",
+ "by": "by"
+}
+
diff --git a/locales/ja/directive.json b/locales/ja/directive.json
new file mode 100644
index 000000000..0f4193cf9
--- /dev/null
+++ b/locales/ja/directive.json
@@ -0,0 +1,29 @@
+{
+ "save_to_gallery": "ギャラリーへ保存",
+ "save_drawing": "描画を保存",
+ "title": "タイトル",
+ "description": "説明",
+ "save": "保存",
+ "skip": "スキップ",
+ "save_wallpaper": "壁紙を保存",
+ "login_to_your_account": "アカウントへログイン",
+ "create_your_account": "アカウントを作成",
+ "share": "共有",
+ "save": "保存",
+ "reset": "リセット",
+ "load": "読み込む",
+ "toggle_autocomplete": "オートコンプリート切り替え",
+ "autocomplete": "オートコンプリート",
+ "toggle_fullscreen": "全画面切り替え",
+ "fullscreen": "全画面",
+ "download_teachers_guide": "講師用ガイドをダウンロード",
+ "click_plus_to_add_a_shape_1": "形をコードに挿入するには、",
+ "click_plus_to_add_a_shape_2": "を押してみて",
+ "download_teachers_guide": "教師用のガイドをダウンロード",
+ "guide": "ガイド",
+ "recipients_email_address": "宛先のメールアドレス",
+ "your_message": "メッセージ",
+ "error": "{{ error.type | upperfirst }} Error",
+ "error_line": "on line {{ error.row + 1 || 0 }}",
+ "error_text": "{{ error.text || 0 }}"
+}
diff --git a/locales/ja/partial.json b/locales/ja/partial.json
new file mode 100644
index 000000000..77df88ccc
--- /dev/null
+++ b/locales/ja/partial.json
@@ -0,0 +1,35 @@
+{
+ "pixel_hack": "ピクセルハック:",
+ "code_your_way_through_the_history": "ビデオゲームのアート歴史をコードで歩んでみよう。",
+ "welcome_to_pixel_hack": "ピクセルハックへようこそ",
+ "start_your_coding_adventure": "ポングやその他のレトロなゲームからコードの冒険を始めよう。次に8ビットアートをマスターし、自分のマインクラフトキャラクターなど作ろう。乗り越えたチャレンジを皆に見せて、ピクセルマスターのバッジをゲットしよう。",
+ "happy_hacking": "ハッキングを楽しめ!",
+ "claim_your_pixel_hack_certificate": "ピクセルハック証明書を求めよう。",
+ "visit_this_address_from_a_computer": "証明書を作るには、プリンターに繋がっているパソコンから次のリンクにアクセスしよう。",
+ "discover_kano": "Kanoを探検する",
+ "login": "ログイン",
+ "signup": "サインアップ",
+ "logout": "ログアウト",
+ "feedback": "ご意見",
+ "whats_up": "思ったこと",
+ "thank_you": "ありがとう!",
+ "send": "送信",
+ "load": "読み込む",
+ "load_from_where": "どこから読み込む",
+ "your_kano": "あなたのKanoから",
+ "internet": "インターネットから",
+ "share_by_email": "eメールで共有",
+ "enter_an_email_address": "メールアドレスを入力",
+ "i_just_made_this_with_code": "さっき、これをコードで作ったよ!あなたもやってみませんか?簡単で皆で遊べるよ。",
+ "your_name": "お名前…",
+ "next_up": "次は:",
+ "back_to_world": "selectedWorld.name + 'へ戻る'",
+ "come_back_tomorrow_to_continue": "明日戻ってきて、このチャレンジを続けよう。",
+ "share_on_facebook": "Facebookで共有する",
+ "share_on_twitter": "Twitterで共有する",
+ "email_address": "メールアドレス",
+ "share_with_friends": "友達と共有する:",
+ "skip_sharing": "共有しない",
+ "start":"Start",
+ "draw_with_code":"Draw With Code"
+}
diff --git a/package.json b/package.json
index 91ca2135d..11c0b1f04 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "draw",
- "version": "0.0.0-2",
+ "version": "0.1.0",
"description": "App to learn programming using a basic CoffeeScript drawing API",
"main": "index.js",
"scripts": {
@@ -8,9 +8,10 @@
"watch": "gulp watch",
"build": "gulp",
"bundle": "./bundle",
- "postinstall": "node node_modules/.bin/gulp default",
+ "postinstall": "node node_modules/.bin/bower i && node_modules/.bin/gulp default",
"jshint": "jshint lib",
- "jscs": "jscs lib"
+ "jscs": "jscs lib",
+ "test": "./node_modules/.bin/mocha test/**/*"
},
"author": "Tancredi Trugenberger",
"repository": {
@@ -20,30 +21,39 @@
"license": "MIT2",
"dependencies": {
"api-service": "1.0.0",
+ "bower": "^1.8.0",
"cli-color": "~0.2.x",
"coffee-script": "~1.7.x",
"color": "^0.7.3",
"deep-equal": "^1.0.0",
+ "del": "1.1.1",
"express": "^4.10.5",
"griddy": "0.0.x",
- "gulp": "~3.5.x",
+ "gulp": "^3.9.x",
"gulp-browserify": "~0.4.x",
+ "gulp-callback": "0.0.3",
+ "gulp-html-i18n": "0.3.4",
"gulp-jade": "~0.4.x",
"gulp-livereload": "1.2.0",
+ "gulp-ng-annotate": "^2.0.0",
"gulp-rename": "~0.2.x",
"gulp-stylus": "^2.0.1",
+ "gulp-uglify": "^2.0.0",
"http-server": "~0.6.x",
- "kano-world-sdk": ">1.0.0",
+ "kano-world-sdk": "3.0.14",
"lodash": "~2.4.x",
"marked": "~0.3.x",
"moment": "^2.10.6",
"nib": "~1.0.x",
"partialify": "^3.1.1",
- "play-audio": "^0.1.0",
"throttle-debounce": "^0.1.1",
"tiny-lr": "0.0.9"
},
+ "devDependencies": {
+ "chai": "3.2.x",
+ "mocha": "2.3.x"
+ },
"engines": {
- "node": "0.12.x"
+ "node": "6.10.x"
}
}
diff --git a/po/lang-docs-po-to-json b/po/lang-docs-po-to-json
new file mode 100755
index 000000000..0b3eba135
--- /dev/null
+++ b/po/lang-docs-po-to-json
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+#
+# lang-docs-po-to-json
+#
+# Convert docs po file to content/docs.json
+#
+# Copyright (C) 2016 Kano Computing Ltd.
+# License: http://www.gnu.org/licenses/gpl-2.0.txt GNU GPLv2
+#
+# Tool to convert res/locales/en/lang.lua file to its corresponding lang.po file
+# in a given locale and language. The lang.po file will be empty and ready to be
+# given to translators.
+#
+# Dependencies:
+#
+# 1. python module: polib
+# pip install polib
+#
+# 2. python module: docopt
+# pip install docopt
+
+
+"""
+Usage:
+ lang-po-to-json
+
+
+Options:
+ -h, --help show this message
+
+Values:
+ locale the folder name under res/locales/
+
+Examples:
+ ./lang-po-to-json es_AR
+
+"""
+
+
+import io
+import os
+import sys
+import json
+import datetime
+import copy
+
+import polib
+import docopt
+
+CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
+
+# constants
+LOCALES_DIR = os.path.join(CURRENT_DIR, '../locales')
+DOCS_FILENAME = os.path.join(CURRENT_DIR, '../content/docs.json')
+POT_FILENAME = os.path.join(LOCALES_DIR, 'messages-docs.pot')
+DEFAULT_LOCALE = 'en'
+
+
+def convert_locale_to_browser(locale):
+ return locale.replace('_', '-')
+
+
+def get_translation(po_file, msgid):
+ entry = po_file.find(msgid)
+ if not entry:
+ print(u'Could not find translation for "{}"'.format(msgid))
+ return msgid
+ return entry.msgstr
+
+def translate_entry(po_file, entry):
+ # translate entry
+ entry['label'] = get_translation(po_file, entry['label'])
+ commands = []
+ for command in entry['commands']:
+ command['description'] = get_translation(po_file, command['description'])
+ args = []
+ for arg in command['args']:
+ args.append([
+ arg[0],
+ arg[1],
+ get_translation(po_file, arg[2]),
+ arg[3],
+ ])
+
+ command['args'] = args
+ commands.append(command)
+ entry['commands'] = commands
+
+ return entry
+
+
+def main(args):
+ locale = args['']
+ date = datetime.date.today()
+
+ LOCALE_PO_FILENAME = os.path.join(LOCALES_DIR, '{}-docs.po'.format(locale))
+ # get po file
+ if not os.path.exists(LOCALE_PO_FILENAME):
+ print('Error: Cannot find po file for locale {}'.format(locale))
+ sys.exit(1)
+
+ po_file = polib.pofile(LOCALE_PO_FILENAME)
+
+ with open(DOCS_FILENAME, 'r') as json_data_file:
+ json_data = json.load(json_data_file, encoding='utf-8')
+
+ translated_entries = []
+ for entry in json_data[DEFAULT_LOCALE]:
+ # translate each entry
+ translated_entries.append(
+ translate_entry(po_file, copy.deepcopy(entry))
+ )
+
+ json_data[convert_locale_to_browser(locale)] = translated_entries
+ with io.open(DOCS_FILENAME, 'w', encoding='utf8') as docs_file:
+ data = json.dumps(json_data, ensure_ascii=False, indent=4)
+ docs_file.write(unicode(data))
+
+if __name__ == '__main__':
+ args = docopt.docopt(__doc__)
+ sys.exit(main(args))
diff --git a/po/lang-extract-doc-strings b/po/lang-extract-doc-strings
new file mode 100755
index 000000000..a00d24728
--- /dev/null
+++ b/po/lang-extract-doc-strings
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+#
+# lang-extract-doc-strings
+#
+# Extract all translatable strings from docs into a pot file
+#
+# Copyright (C) 2016 Kano Computing Ltd.
+# License: http://www.gnu.org/licenses/gpl-2.0.txt GNU GPLv2
+#
+# Tool to convert res/locales/en/lang.lua file to its corresponding lang.po file
+# in a given locale and language. The lang.po file will be empty and ready to be
+# given to translators.
+#
+# Dependencies:
+#
+# 1. python module: polib
+# pip install polib
+#
+# 2. python module: docopt
+# pip install docopt
+
+
+"""
+Usage:
+ lang-extract-strings
+
+Options:
+ -h, --help show this message
+"""
+
+
+import os
+import sys
+import json
+import datetime
+
+import polib
+import docopt
+
+CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
+
+# constants
+HEADER = '''Messages for make-art.
+Copyright (C) {} Kano Computing Ltd.
+This file is distributed under the same license as the make-art package.
+ , {}
+'''
+LOCALES_DIR = os.path.join(CURRENT_DIR, '../locales')
+DOCS_FILENAME = os.path.join(CURRENT_DIR, '../content/docs.json')
+POT_FILENAME = os.path.join(LOCALES_DIR, 'messages-docs.pot')
+DEFAULT_LOCALE = 'en'
+
+
+def extract_strings(docs_data):
+ types = docs_data[DEFAULT_LOCALE]
+ strings = []
+
+ for index, _type in enumerate(types):
+ if 'label' in _type:
+ strings.append((
+ _type['label'],
+ 'en[{}]'.format(index),
+ ))
+
+ for command_index, command in enumerate(_type['commands']):
+ if 'description' in command:
+ strings.append((
+ command['description'],
+ 'en[{}].commands[{}]'.format(index, command_index),
+ ))
+
+ for args_index, arg in enumerate(command['args']):
+ strings.append((
+ arg[2],
+ 'en[{}].commands[{}].args[{}]'.format(index, command_index, args_index),
+ ))
+
+ return strings
+
+
+def main(args):
+ date = datetime.date.today()
+
+ # convert the json to a pot file
+ new_po_file = polib.POFile(check_for_duplicates=True)
+ new_po_file.metadata = {
+ 'Project-Id-Version': '1.0',
+ 'Report-Msgid-Bugs-To': 'dev@kano.me',
+ 'POT-Creation-Date': '{} 14:00+0100'.format(date),
+ 'PO-Revision-Date': '{} 14:00+0100'.format(date),
+ 'Last-Translator': '',
+ 'Language-Team': '',
+ 'Language': '',
+ 'MIME-Version': '1.0',
+ 'Content-Type': 'text/plain; charset=utf-8',
+ 'Content-Transfer-Encoding': '8bit',
+ }
+ new_po_file.header = HEADER.format(date.year, date.year)
+
+ # Get all strings
+ with open(DOCS_FILENAME) as json_data_file:
+ json_data = json.load(json_data_file)
+
+ doc_strings = extract_strings(json_data)
+
+ for string, location in doc_strings:
+ entry = polib.POEntry(
+ msgid=string,
+ msgstr=u'',
+ occurrences=[(location, 0)]
+ )
+ try:
+ new_po_file.append(entry)
+ except ValueError:
+ # string already exists
+ existing_entry = new_po_file.find(string)
+ existing_entry.occurrences.append((location, 0))
+
+ # write the po file
+ new_po_file.save(POT_FILENAME)
+
+
+if __name__ == '__main__':
+ args = docopt.docopt(__doc__)
+ sys.exit(main(args))
diff --git a/po/lang-extract-strings b/po/lang-extract-strings
new file mode 100755
index 000000000..557952f94
--- /dev/null
+++ b/po/lang-extract-strings
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+#
+# lang-extract-strings
+#
+# Extract all translatable strings to a pot file
+#
+# Copyright (C) 2016 Kano Computing Ltd.
+# License: http://www.gnu.org/licenses/gpl-2.0.txt GNU GPLv2
+#
+# Tool to convert res/locales/en/lang.lua file to its corresponding lang.po file
+# in a given locale and language. The lang.po file will be empty and ready to be
+# given to translators.
+#
+# Dependencies:
+#
+# 1. python module: polib
+# pip install polib
+#
+# 2. python module: docopt
+# pip install docopt
+
+
+"""
+Usage:
+ lang-extract-strings
+
+Options:
+ -h, --help show this message
+"""
+
+
+import os
+import sys
+import json
+import datetime
+
+import polib
+import docopt
+
+CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
+
+# constants
+HEADER = '''Messages for make-art.
+Copyright (C) {} Kano Computing Ltd.
+This file is distributed under the same license as the make-art package.
+ , {}
+'''
+LOCALES_DIR = os.path.join(CURRENT_DIR, '../locales')
+POT_FILENAME = os.path.join(LOCALES_DIR, 'messages.pot')
+
+
+def create_entries(filename, data):
+ entries = []
+ for key in data:
+ entry = polib.POEntry(
+ msgid=data[key],
+ msgstr=u'',
+ occurrences=[(
+ '{}.{}'.format(
+ os.path.splitext(filename)[0],
+ key,
+ ),
+ 0
+ )]
+ )
+ entries.append(entry)
+
+ return entries
+
+
+def main(args):
+ date = datetime.date.today()
+
+ EN_LOCALE_DIR = os.path.join(LOCALES_DIR, 'en')
+
+ # convert the json to a pot file
+ new_po_file = polib.POFile(check_for_duplicates=True)
+ new_po_file.metadata = {
+ 'Project-Id-Version': '1.0',
+ 'Report-Msgid-Bugs-To': 'dev@kano.me',
+ 'POT-Creation-Date': '{} 14:00+0100'.format(date),
+ 'PO-Revision-Date': '{} 14:00+0100'.format(date),
+ 'Last-Translator': '',
+ 'Language-Team': '',
+ 'Language': '',
+ 'MIME-Version': '1.0',
+ 'Content-Type': 'text/plain; charset=utf-8',
+ 'Content-Transfer-Encoding': '8bit',
+ }
+ new_po_file.header = HEADER.format(date.year, date.year)
+
+ # Get all strings
+ all_strings = {}
+ for f in os.listdir(EN_LOCALE_DIR):
+ if os.path.isfile(os.path.join(EN_LOCALE_DIR, f)) and f.endswith('.json'):
+ with open(os.path.join(EN_LOCALE_DIR, f)) as json_data_file:
+ json_data = json.load(json_data_file)
+
+ for entry in create_entries(f, json_data):
+ try:
+ new_po_file.append(entry)
+ except ValueError:
+ existing_entry = new_po_file.find(entry.msgid)
+ existing_entry.occurrences.append(entry.occurrences[0])
+
+ # write the po file
+ new_po_file.save(POT_FILENAME)
+
+
+if __name__ == '__main__':
+ args = docopt.docopt(__doc__)
+ sys.exit(main(args))
diff --git a/po/lang-po-to-json b/po/lang-po-to-json
new file mode 100755
index 000000000..0462c1291
--- /dev/null
+++ b/po/lang-po-to-json
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+#
+# lang-po-to-json
+#
+# Convert po to json files
+#
+# Copyright (C) 2016 Kano Computing Ltd.
+# License: http://www.gnu.org/licenses/gpl-2.0.txt GNU GPLv2
+#
+# Tool to convert res/locales/en/lang.lua file to its corresponding lang.po file
+# in a given locale and language. The lang.po file will be empty and ready to be
+# given to translators.
+#
+# Dependencies:
+#
+# 1. python module: polib
+# pip install polib
+#
+# 2. python module: docopt
+# pip install docopt
+
+
+"""
+Usage:
+ lang-po-to-json
+
+
+Options:
+ -h, --help show this message
+
+Values:
+ locale the folder name under res/locales/
+
+Examples:
+ ./lang-po-to-json es_AR
+
+"""
+
+
+import io
+import os
+import sys
+import json
+import datetime
+
+import polib
+import docopt
+
+CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
+
+# constants
+LOCALES_DIR = os.path.join(CURRENT_DIR, '../locales')
+DEFAULT_LOCALE = os.path.join(LOCALES_DIR, 'en')
+
+
+def convert_locale_to_browser(locale):
+ return locale.replace('_', '-')
+
+
+def translate_data(po_file, data):
+ translated_data = {}
+ # Find entry in po file
+ for key in sorted(data):
+ try:
+ entry = next(e for e in po_file if e.occurrences[0][0].split('.')[1] == key)
+ if not entry.msgstr:
+ print('Warning: String "{}" has not been translated'.format(entry.msgid))
+ translated_data[key] = data[key]
+ else:
+ translated_data[key] = entry.msgstr
+ except StopIteration:
+ # Entry has not been translated
+ print('Warning: Key `{}` cannot be found in po file'.format(key))
+ translated_data[key] = data[key]
+
+ return translated_data
+
+
+def main(args):
+ locale = args['']
+ date = datetime.date.today()
+
+ LOCALE_PO_FILENAME = os.path.join(LOCALES_DIR, '{}.po'.format(locale))
+ # get po file
+ if not os.path.exists(LOCALE_PO_FILENAME):
+ print('Error: Cannot find po file for locale {}'.format(locale))
+ sys.exit(1)
+
+ po_file = polib.pofile(LOCALE_PO_FILENAME)
+
+ NEW_LOCALE_DIR = os.path.join(LOCALES_DIR, convert_locale_to_browser(locale))
+
+ # create locale folder
+ os.system('mkdir -p {}'.format(NEW_LOCALE_DIR))
+
+ for f in os.listdir(DEFAULT_LOCALE):
+ if os.path.isfile(os.path.join(DEFAULT_LOCALE, f)) and f.endswith('.json'):
+ with open(os.path.join(DEFAULT_LOCALE, f)) as json_data_file:
+ json_data = json.load(json_data_file)
+
+ translated_data = translate_data(po_file, json_data)
+
+ # save translated file
+ with io.open(os.path.join(NEW_LOCALE_DIR, f), 'w', encoding='utf8') as locale_file:
+ data = json.dumps(translated_data, ensure_ascii=False, indent=4)
+ locale_file.write(unicode(data))
+
+
+if __name__ == '__main__':
+ args = docopt.docopt(__doc__)
+ sys.exit(main(args))
diff --git a/resources/icon-set/Read Me.txt b/resources/icon-set/Read Me.txt
index cd97d24a0..8491652f8 100755
--- a/resources/icon-set/Read Me.txt
+++ b/resources/icon-set/Read Me.txt
@@ -1,5 +1,7 @@
Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures.
+To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/#docs/local-fonts
+
You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects.
-You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu > Manage Projects) to retrieve your icon selection.
+You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection.
diff --git a/resources/icon-set/demo-files/demo.css b/resources/icon-set/demo-files/demo.css
deleted file mode 100755
index 982f938d3..000000000
--- a/resources/icon-set/demo-files/demo.css
+++ /dev/null
@@ -1,153 +0,0 @@
-body {
- padding: 0;
- margin: 0;
- font-family: sans-serif;
- font-size: 1em;
- line-height: 1.5;
- color: #555;
- background: #fff;
-}
-h1 {
- font-size: 1.5em;
- font-weight: normal;
-}
-small {
- font-size: .66666667em;
-}
-a {
- color: #e74c3c;
- text-decoration: none;
-}
-a:hover, a:focus {
- box-shadow: 0 1px #e74c3c;
-}
-.bshadow0, input {
- box-shadow: inset 0 -2px #e7e7e7;
-}
-input:hover {
- box-shadow: inset 0 -2px #ccc;
-}
-input, fieldset {
- font-size: 1em;
- margin: 0;
- padding: 0;
- border: 0;
-}
-input {
- color: inherit;
- line-height: 1.5;
- height: 1.5em;
- padding: .25em 0;
-}
-input:focus {
- outline: none;
- box-shadow: inset 0 -2px #449fdb;
-}
-.glyph {
- font-size: 16px;
- width: 15em;
- padding-bottom: 1em;
- margin-right: 4em;
- margin-bottom: 1em;
- float: left;
- overflow: hidden;
-}
-.liga {
- width: 80%;
- width: calc(100% - 2.5em);
-}
-.talign-right {
- text-align: right;
-}
-.talign-center {
- text-align: center;
-}
-.bgc1 {
- background: #f1f1f1;
-}
-.fgc1 {
- color: #999;
-}
-.fgc0 {
- color: #000;
-}
-p {
- margin-top: 1em;
- margin-bottom: 1em;
-}
-.mvm {
- margin-top: .75em;
- margin-bottom: .75em;
-}
-.mtn {
- margin-top: 0;
-}
-.mtl, .mal {
- margin-top: 1.5em;
-}
-.mbl, .mal {
- margin-bottom: 1.5em;
-}
-.mal, .mhl {
- margin-left: 1.5em;
- margin-right: 1.5em;
-}
-.mhmm {
- margin-left: 1em;
- margin-right: 1em;
-}
-.mls {
- margin-left: .25em;
-}
-.ptl {
- padding-top: 1.5em;
-}
-.pbs, .pvs {
- padding-bottom: .25em;
-}
-.pvs, .pts {
- padding-top: .25em;
-}
-.unit {
- float: left;
-}
-.unitRight {
- float: right;
-}
-.size1of2 {
- width: 50%;
-}
-.size1of1 {
- width: 100%;
-}
-.clearfix:before, .clearfix:after {
- content: " ";
- display: table;
-}
-.clearfix:after {
- clear: both;
-}
-.hidden-true {
- display: none;
-}
-.textbox0 {
- width: 3em;
- background: #f1f1f1;
- padding: .25em .5em;
- line-height: 1.5;
- height: 1.5em;
-}
-#testDrive {
- display: block;
- padding-top: 24px;
- line-height: 1.5;
-}
-.fs0 {
- font-size: 16px;
-}
-.fs1 {
- font-size: 32px;
-}
-.fs2 {
- font-size: 32px;
-}
diff --git a/resources/icon-set/demo-files/demo.js b/resources/icon-set/demo-files/demo.js
deleted file mode 100755
index e72f449b7..000000000
--- a/resources/icon-set/demo-files/demo.js
+++ /dev/null
@@ -1,30 +0,0 @@
-if (!('boxShadow' in document.body.style)) {
- document.body.setAttribute('class', 'noBoxShadow');
-}
-
-document.body.addEventListener("click", function(e) {
- var target = e.target;
- if (target.tagName === "INPUT" &&
- target.getAttribute('class').indexOf('liga') === -1) {
- target.select();
- }
-});
-
-(function() {
- var fontSize = document.getElementById('fontSize'),
- testDrive = document.getElementById('testDrive'),
- testText = document.getElementById('testText');
- function updateTest() {
- testDrive.innerHTML = testText.value || String.fromCharCode(160);
- if (window.icomoonLiga) {
- window.icomoonLiga(testDrive);
- }
- }
- function updateSize() {
- testDrive.style.fontSize = fontSize.value + 'px';
- }
- fontSize.addEventListener('change', updateSize, false);
- testText.addEventListener('input', updateTest, false);
- testText.addEventListener('change', updateTest, false);
- updateSize();
-}());
diff --git a/resources/icon-set/demo.html b/resources/icon-set/demo.html
deleted file mode 100755
index 7fdb6cf79..000000000
--- a/resources/icon-set/demo.html
+++ /dev/null
@@ -1,634 +0,0 @@
-
-
-
-
- IcoMoon Demo
-
-
-
-
-
-