Some thoughts about (and purely about) the coding aspect of CodeCombat


#1

Not sure if it’s the right place for this post…
A few things that pops into my mind:

  • Python syntax - It would be nice to support something like this that makes the codes shorter:
[hero.attack(enemy) for enemy in hero.findEnemies()]
# or 
if enemy = hero.findNearestEnemy():
    hero.attack(enemy)

UPDATE: Through a quick lot into the github repos I know CC’s python parser depends on a third party library called “filbert”, so it’s not trivial to add any python syntax support. In fact in their repo there are 50+ pending issues. So forget about the above please :slight_smile:
UPDATE 2: https://github.com/pybee/batavia this looks pretty good, aiming at python 3.4 support, still in alpha, but worthwhile having a “python3” in “aether” just like you did with java.

  • Shared code (applies to all the supported languages) - I personally had to repeat a lot of code in different levels, it’ll be nice if I can put some code somewhere to be shared by levels, for example:
def heroAttacksEnemy():
    # some strategies including casting something, bashing, cleaving and attacking
    # could be very useful for most repeatable / dual levels
    pass

def minionSomething():
    # some strategies to decide what to minion according to golds and needs
    pass

def attackNearestEnemyIfAny(max_range_in_metres=9999):
    enemy = hero.findNearest(hero.findEnemies())
    if enemy and hero.distanceTo(enemy) < max_range_in_metres:
        heroAttacksEnemy(enemy)

def findEnemyWithLeastHealth():
    # blah blah blah
    pass

I understand the point of CodeCombat is mainly helping people learn programing (I learned coffiescript through the game as well) and maybe it’s intentionally done this way so that users see the code again and again. But I still think separating responsibility, intention revealing naming and dependency injection are also very important aspects in software development, and people will learn to separate the codes to different files in a very early stage of learning a language.

I know there’s a lot to concern, like security, gaps between different languages etc. , and I haven’t looked into the github repo too much to know how possible it is to achieve that. So just throwing my two cents and see if you guys got any ideas.
UPDATE: Had a quick read on “aether” test suites, basically everything is thrown to “aether.transpile”, so I guess user’s custom code injection wouldn’t be too hard?

Cheers,

Daniel


#2

Excellent feedback, thanks for posting! :smile:

I believe list comprehensions are already supported, aren’t they? I just tried an even more elaborated piece:

while True:
    [hero.attack(enemy) for enemy in hero.findEnemies() if hero.isPathClear(hero.pos, enemy.pos)]

And it worked nicely.

As for assignment expressions inside conditional statements, it would be a really nice feature to have, indeed.

As for modules and splitting code into multiple files, I believe this has been discussed a few times in the past and is in the development team’s radar. Though, there are several intricacies that need to be resolved in order to have this feature come to fruition. For example, how to make the editor support multiple files without adding too much clutter to the interface? How and when should the module import/export syntax be taught? Also remember that each programming language has different semantics regarding how modules work, as in what can be exported and how to do it, as well as how to access a given module’s exports from another module.

Note that Filbert is not so much “third party” as it is maintained by Matt, co-founder of CodeCombat, as well as other CodeCombat developers.

You mean, “code injection” as in with the intent to hack/cheat the game? Aether does heavy transformations to the user code and runs it in relatively high isolation, forbidding access to practically all dangerous things. For instance, you cannot access the global object (window) nor any of the built-in’s constructors nor eval or any of its “aliases”—so no funny things like setTimeout or setInterval passing a string value or new Function(). Also, users are not able to perform cheats like freezing/crashing the game with an infinite loop when losing in a multiplayer game, as the user code’s game simulation runs inside a web worker. If the user code ends up in an infinite loop or error, the engine may kill the web worker making the player’s hero stop dead in their tracks with an error sign while the game and the opponent’s hero keep running just fine.

So, I’m just saying Aether is more robust than it seems. :slight_smile:
Also, if you are curious about this part, I suggest checking out Esper.js, CodeCombat’s new interpreter. It solves several issues with the old transpilation/execution process and improves security and accuracy. It interprets abstract syntax trees outputted by CodeCombat’s programming language parsers (e.g. Filbert), providing far more powerful instrumentation and control over the user code.


#3

If you want to teach people write unreadable and hard to maintain code :wink:


#4

Thanks @UltCombo , Esper.js looks cool :slight_smile:


#5

@Bryukh assignment inside conditional statements could be very useful, depends on how / when you use them :slight_smile:


#6

Examples?

I don’t assignments in “if” because if I see it in code, then I need read very careful and specially “=” or “==”. I prefer readable and obvious code that is easy to read for your collegues.


#7

@Bryukh The code sample in the opening post is a perfect example, in my opinion. For instance, instead of writing:

enemy = hero.findNearestEnemy()
if enemy:
    hero.attack(enemy)

One may write a shorter and more concise version:

if enemy = hero.findNearestEnemy():
    hero.attack(enemy)

Of course, beginners may not fully understand how assignment expressions work—more specifically, that assignment expressions evaluate to the assigned value—and the intricacies with operator precedence and confusion with the equality operators may throw them off as well. Whether this is “better” is very subjective and depends on the specific use case and the team’s code style preferences.

For someone like me, who has limited time and usually tries to solve every level as fast as possible with minimum effort, I really miss the ability to type fewer lines of code. Here’s another case which saves more than just one line:

coin = hero.findNearestItem()
if coin:
    # ...
else:
    enemy = hero.findNearestEnemy()
    if enemy:
        # ...

Instead, we can do:

if coin = hero.findNearestItem():
    # ...
elif enemy = hero.findNearestEnemy():
    # ...

This saves an indentation level (great readability and brevity boost :exclamation:) while keeping the same logic, that is, the hero.findNearestEnemy() method is only executed when no items are found, which provides better performance than moving it to before the conditionals.

In any case, I guess you will agree that developers need to learn how to deal with bad code sooner or later. :stuck_out_tongue:


#8

Sorry, but I’m totally disagree.

Btw, do you know that in python assignments in “if” are not available?


#9

That’s a good article about it http://effbot.org/pyfaq/why-can-t-i-use-an-assignment-in-an-expression.htm and I’m totally agree with the author

The reason for not allowing assignment in Python expressions is a common, hard-to-find bug in those other languages, caused by this construct:
if (x = 0) {
…error handling…
}
else {
…code that only works for nonzero x…
}


#10

I see. It is also worth noting that as the author mentions, it is a common C idiom. And this idiom has survived through several of the broad C-family languages, such as Java, PHP and JavaScript.

I somewhat disagree with it being a “hard-to-find bug”, most linters and compilers can be configured to warn about these constructs if you don’t like them. There’s even de-facto conventions to communicate the intent and silence such warnings by wrapping the assignment expression in an otherwise unnecessary pair of parens, e.g. if ((test = 1)) {} (valid C / JavaScript / Java).

When I saw the error message when CodeCombat tried to parse this syntax (“Possibly a bug with advanced Python feature parsing”), I assumed this was just a missing feature in the Filbert parser. Now I’ve done a more throughout research and I see that this is actually not valid Python, as you’ve said. :blush:

So, I agree that CodeCombat should not implement non-standard language features.

As for whether this is a good or bad language design feature, let’s leave it at the viewer’s discretion. This is just part of the endless debate about simplicity vs completeness. Usually it boils down to simplicity making the code easier to read, while completeness makes it easier to write. But things are never so simple. I believe shorter and conciser syntax can lead to code that is easier to read for the experienced eye. Let’s leave this as too subjective of a topic for discussion. :smiley:


#11

I’ve been waiting for the real HOLY WAR. How could you! :cry:

P.S.: just a joke. I don’t like holywars.


#12

Let’s meet again in the next thread about tabs vs spaces. :stuck_out_tongue_winking_eye:


#13

“Silicon Valley”?

btw, I prefer spaces, but I’m using “tab” button for it.


#14

I saw that episode, and yes, it is kinda crazy to indent using the space bar. I think 99% people use the tab key for indentation. Now whether they use “hard tabs” (actual tabs characters) or “soft tabs” (tab key as a macro for spaces) seem half and half among developers.

If you write mainly Python, you can follow PEP8 and indent with 4 spaces. Now when the programming language does not have such established standards for indentation, I’m more of a tab guy (mainly for the reasons listed in Lea Verou’s Why tabs are clearly superior article).

People often complain that tabs are rendered inconsistently in different software, but IMO the major point is exactly that developers can configure their text editors to render tabs with the width that they want, so there’s no need to discuss whether indentation levels should use 2 or 4 spaces and thus there are no conflicting opinions about it; everyone is happy working in the same code base using the indentation size of their own preference.

Oh gawd, guess I’m just fuelling a holy war and starting to go a bit off-topic, my bad. :smile:

Of course, indentation is always a personal preference and hardly any productive result can come out of such discussion, so let’s try to avoid it. :slight_smile:


#15

Yeah, I think it was the weird case in that episode.

I became a pythonist before JS and I’m used to use spaces and follow pep8.


#16

Yeah it’s a typical idiom in many languages. In java if you want to read an InputStream and write to an OutputStream, you’ll probably do something like:

void process(InputStream in, OutputStream out, int bufferSize) throws IOException {
    int bytesRead = 0;
    byte[] buffer = new byte[bufferSize];
    while ( (bytesRead = in.read(buffer, 0, bufferSize) ) > 0 ) {
        out.write(buffer, 0, bytesRead);
    }
}

Alternatively:

void process(InputStream in, OutputStream out, int bufferSize) throws IOException {
    byte[] buffer = new byte[bufferSize];
    while (true) {
        int bytesRead = in.read(buffer, 0, bufferSize);
        if (bytesRead > 0) {
            out.write(buffer, 0, bytesRead);
        } else {
            break;
        }
    }
}

I don’t know about your preference, but I’d definitely go with the first one.
Apart from it being subjective, you can think it as a trade off - reduces indents and lines of codes, but less trivial to read comparing to the normal way.
My preference is: I’ll try to use it whereever it’s obvious to read, and try to avoid it when the line gets too long.