Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace loop: with while nextFrame(): in python #2752

Closed
ChristianKleineidam opened this issue May 12, 2015 · 14 comments
Closed

Replace loop: with while nextFrame(): in python #2752

ChristianKleineidam opened this issue May 12, 2015 · 14 comments

Comments

@ChristianKleineidam
Copy link

The fact that python itself has no loop comment makes it very confusing for beginners when they learn in CodeCombat about loop: and then try to use loop: in real cpython.

The fact that loop: uses backround magic to wait for the next frame is very nonobvious to a beginner. A beginner will expect that while True: does the same thing as loop:.

The solution would be to add a new nextFrame() function that makes the magic explicit:

def nextFrame():
    self.wait(0.066) # seems to be the current frame length
    return True

Instead of loop: the standard could be while nextFrame():.

Why is this a problem? In January someone ask for help to debug an issue that comes from this error:
http://discourse.codecombat.com/t/python-while-loop-runs-infinitely/2407 nobody helped the poor guy. The error is non-obvious enough that forums users don't spot it.

User who are expecting normal python ask themselves whether this is really python when they see loop instead of while and for:
http://discourse.codecombat.com/t/python-coding-question/3853/4

@Vlevo
Copy link
Contributor

Vlevo commented May 13, 2015

Your first forum-example is trumped up. His problem would not have changed by adding any sort of loop around his code, and he specifically states this.

"While nextFrame()" would require the explanation of "frames" to happen in the Kithgard Dungeon, long before a knowledge of such things would be of any coding benefit. (as well as conditionals, etc. see below for when these currently appear)

"A user expecting normal python" [prior knowledge] is not the target audience of CodeCombat...
Having said that . . . For the languages at CoCo lucky enough to have been blessed with a coco-yielding-loop, maybe there should be an explanation that "loop" is not a normal language construct but used at code combat to simplify the learning of coding. I doubt the eight level a beginner has encounted, and three levels before the introduction of variables is a good place to introduce conditionals (currently the fourth Forest level, or twelve levels later) and full on loops (currently somewhere in the desert)... (a comment in the "Haunted Kithmaze" level where "loop" is first used would be, at least, a good start in covering this issue (loop not a normal language construct).)

@ChristianKleineidam
Copy link
Author

Let's look at the first linked example:

e = self.findNearest(self.findEnemies())
while e.health > 0:
   self.attack(e)

There are two ways to get that code correct:

e = self.findNearest(self.findEnemies())
loop:
    if e.health > 0:
       self.attack(e)
    else:
        break

or

e = self.findNearest(self.findEnemies())
while e.health > 0 and nextFrame():
   self.attack(e)

When testing around I sometimes found that self.attack() needs nearly no time to execute and can lead to a infinitive loop. It doesn't seem to do this always.

"While nextFrame()" would require the explanation of "frames" to happen in the Kithgard Dungeon,

No, the level could just tell the user that he has to write while nextFrame():. It doesn't have to explain the details. It doesn't have to explain anything about why that line works. It doesn't even have to allow the user to use any other while statement as long as he has only 'Programmaticon I'.

Apart from that the loop; magic violates the zen of python which says: Explicit is better than implicit.

PS: Python is just discussing to add a async for loop (https://www.python.org/dev/peps/pep-0492/).

@Vlevo
Copy link
Contributor

Vlevo commented May 14, 2015

Maybe you should try reading the post and not pulling code out of context to support your own theories:


The following code results in an infinite loop, even though the selected enemy is clearly dead.


The fix for his problem is NOT ANYTHING TO DO WITH THE LOOP, but fixing of the updating of the loss of enemy health.

So that is most of your post.

attack(something) --shouldn't-- be part of an infinite loop, ever . . . --shouldn't-- . . . temporary glitches not withstanding.

Well, if you want to not explain things you just introduced then there is no need to change "loop" or explain that it isn't "real" python................. (but you are asking for it by "while nextFrame()" ?? what does that mean? at least "loop" does what it says and says what it does. It loops, If you want to complain about black-boxes then don't ever try to introduce python "for" loops, with out lots of explanation.

This is not a "Teach pythonic python" site. This is a teach PROGRAMMING site, you choose the language to learn PROGRAMMING by using. You keep asking for the site to teach idiomatic python, PERIOD.

@nwinter
Copy link
Contributor

nwinter commented May 14, 2015

I agree that adding a self.nextFrame() method would be more Pythonic, and certainly better for more advanced programmers. The reason we added loop:, though, was based on difficulties raw beginners were having with super basic syntax and infinite loops, and although it does have some big downsides, I still think it's worth it for how much easier it is to do loops early on.

Maybe if we were going to do another campaign that moved a bit faster for older players, we would test out a while self.nextFrame(): approach instead. For now I think the next step is to better explain somewhere around where loops are introduced that they are syntactic sugar that's not part of Python.

@UltCombo
Copy link
Contributor

I also get slightly bothered that Python is the default programming language but the lessons are not taught in a Pythonic way.
Don't get me wrong, CodeCombat is awesome and I find the "structured" way of programming pretty efficient for teaching as well -- it just strikes me as odd that the default programming language was not from the broad C-family.

Either way, seeing as the Python-to-JS compiler currently used in CodeCombat seems rather limited, it most likely isn't suitable for teaching the Pythonic way. I'm also not sure whether it wouldn't be too complicated for beginners, although I've heard good stories about students learning the Pythonic way before being exposed to C-family languages.

Oh, sorry if I derailed the thread too much. 😛

Back on topic, loop: introduces quite a degree of magic that is not really part of the programming logic, but while self.nextFrame(): may be too confusing for beginners.
I agree on explaining that loop: is syntactic sugar intended to ease the learning is the best approach for the time being.

@nwinter
Copy link
Contributor

nwinter commented Oct 11, 2015

We are just going to make while True: act like the current simple loop does, with the automatic yielding of one frame if no blocking action is taken. Any other while condition won't trigger this yielding behavior. So it's still magic, but it works the way the player would expect (unless the player is super advanced) and prevents bad infinite loop errors. We'll work on our autocomplete and error hints to repair damaged while True: loop syntax as we see what happens with it becoming harder to type.

@ChristianKleineidam
Copy link
Author

I think hidden magic of that type is very confusing. A player who's code works when he writes while True: but where it doesn't work with while 1==1: will be highly confused.

@nwinter
Copy link
Contributor

nwinter commented Oct 12, 2015

So we'll handle that with responsive error messages or something. It's honestly not nearly as likely to come up as either 1) someone thinking that loop is not real Python/JS or 2) someone getting confused by the concepts involved in while self.nextFrame().

loop is already nominally equivalent to while True, while 1, while 1==1, etc., but I've never seen anyone just decide to use one of those instead inside CodeCombat.

@UltCombo
Copy link
Contributor

@ChristianKleineidam I believe @nwinter means that any loop that does not perform any yielding would implicit yield at the end. E.g. for and do-while would also be affected. Is my assumption correct?

@nwinter
Copy link
Contributor

nwinter commented Oct 12, 2015

No, that would mess up a lot of code, like when you looping over a list/array of coins to find the most valuable one–your code would suddenly take one extra frame per coin. This is evil black magic to only apply when it's while True:, which is very unlikely to happen in an inner loop that's not supposed to take an action. (I couldn't think of a reasonable example of when you would want to do this in CodeCombat.)

@UltCombo
Copy link
Contributor

I see, makes sense. Though, in that case, you would most likely want to add the magical yield only to while True: loops that do not contain a break statement.

People moving the break condition to inside the loop is quite common. E.g.:

n = 0
while True:
    n++
    # do something
    if n > 3: break

@nwinter
Copy link
Contributor

nwinter commented Oct 12, 2015

Ah yeah, that's a good extension of the heuristic! I think that anyone who knows how to use break will either 1) just write the loop as while n <= 3: here instead or 2) have some more complicated condition that depends on what happens in the world leading to the break–in which case there is probably a blocking action in there anyway.

Really, the exercise would be to come up with breaking code that would actually be applied in a CodeCombat level–not necessarily the best way to do something, but one that at least sort of makes sense to some player doing it. Eventually I had come up with this crazy example:

def commandFollowers():
    # Iterate through friends between 1 and 2 times to either defend self or find a healer and defend it.
    friends = self.findFriends()
    friendIndex = 0
    healer = None
    timesIterated = 0
    while True:  # needs to not yield
        friend = friends[friendIndex]
        friendIndex += 1
        if not healer and friend.type is 'paladin' and friend.canCast('heal', self):
            healer = friend
            self.command(friend, "cast", "heal", self)
        elif friend is healer or friendIndex is len(friends):
            if timesIterated or not healer:
                break
            else:
                friendIndex = 0
                timesIterated = 1
        elif healer:
            self.command(friend, "defend", healer)
        else:
            self.command(friend, "defend", self)

while True:  # used to be simple loop, needs to yield
    self.commandFollowers()  # doesn't block

But that's pretty out there...

@UltCombo
Copy link
Contributor

Yes, complex conditions that need to be tested inside the loop represent an use case.

I believe the more common use case would be simply (ab)use break.

I think that anyone who knows how to use break will [...] just write the loop

You're assuming that everyone will write code in the most conventional and optimally readable way. Given that the vast majority of CodeCombat players are beginner programmers, I believe that would not be the case.

It is not uncommon for a learner to get addicted to certain language constructs right after discovering them (e.g. moving the break condition to a break statement). Of course, most often this is not good practice, but it takes a while (no pun intended) until they master these constructs and know where and when to apply them correctly.

And yes, if I recall correctly, I used to be a break abuser at some point. 😸

@nwinter
Copy link
Contributor

nwinter commented Nov 27, 2015

We've finished doing this for the Course levels, intended to be used in classrooms, and will update the rest of the individual game levels sometime around when we change the way we do videos, most likely.

@nwinter nwinter closed this as completed May 31, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants