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

Do not pull!! quick and dirty proof of concept asteroids with eval'ed user script #19

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

bwhmather
Copy link

I'm sorry for dumping such an ugly patch. It's for demonstration only.

Basic outline of what's going on (it's probably more clear from looking at the Ship class in turtlepower.asteroids):

  • The student script is just a string which gets evaluated once by the turtle at startup.
  • The turtle passes in a proxy to itself with overpowered methods (sort of) hidden and whatever utility functions might be needed.
  • Running the user script only once and forcing them to manually bind the update callback means that state can be saved in a closure. We could probably clear all callbacks when replacing the script.
  • Some things (like the turtle.distance method) don't really make sense without self and there needs to be a better way of deciding what methods to expose (@expose + @expose\n@staticmethod decorators?)

That's about it. What does everyone think?

@bloodearnest
Copy link
Member

Havn't had chance to look at this yet, but commenting to let you know there's a google group at https://groups.google.com/forum/#!forum/turtle-power-python if you wanna sign up for more discussion - most of the other sprinters have

@bwhmather
Copy link
Author

Excellent. Will move over there once I'm done with work

'turn_towards', 'towards', 'forward',
'xcor', 'ycor', 'distance',
'on_update',
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so the idea is to only expose limited apis?

Not sure I get this. The student is supposed to write the ship method. This example is just how I wrote it in an hour or so, there's other ways to do it. True, the teacher might choose to provide a rocket class or similar, but I'd want them to be able to build it all from scratch. E.g. first, an asteroid turtle, then a ship turtle, then a rocket turtle. We won't know in advance what methods they will write.

Also, the turn_towards, xcor, ycor, etc methods are part of the stdlib turtle interface, not specific to this game, and I would want them on every single turtle, not per world. It's up for the student to decide what they need to do.

Although, I do think update() is a bit better that callback() as a name.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a string just above called script which contains the students
code. The proxy object just limits the api so that you can keep things
competitive! There is no reason why the next level can't be making your
own game.
On 26 Sep 2013 20:35, "Simon Davy" [email protected] wrote:

In turtlepower/asteroids.py:

@@ -52,8 +93,23 @@ def setup(self):
self.setheading(90)
self.dead = False
self.rocket = None

  •    self.__range = 200
    
  •    self.state = "shooting"
    
  •    self.range = 200
    
  •    self._update_callback = None
    
  •    ship = Proxy(self, {
    
  •        'fire', 'range',
    
  •        'turn_towards', 'towards', 'forward',
    
  •        'xcor', 'ycor', 'distance',
    
  •        'on_update',
    
  •    })
    

Ok, so the idea is to only expose limited apis?

Not sure I get this. The student is supposed to write the ship method.
This example is just how I wrote it in an hour or so, there's other ways to
do it. True, the teacher might choose to provide a rocket class or similar,
but I'd want them to be able to build it all from scratch. E.g. first, an
asteroid turtle, then a ship turtle, then a rocket turtle. We won't know in
advance what methods they will write.

Also, the turn_towards, xcor, ycor, etc methods are part of the stdlib
turtle interface, not specific to this game, and I would want them on every
single turtle, not per world. It's up for the student to decide what they
need to do.

Although, I do think update() is a bit better that callback() as a name.


Reply to this email directly or view it on GitHubhttps://github.com//pull/19/files#r6611819
.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I saw the string - I was commenting ion the 'fire' method you provided.

Regards keeping it competitive, I think it may be confusing that the current implementation in turtlepower is incorrect. setposition/setx/sety do not teleport you in the stdlib module - they animate your path to that coord. That is TurtlePower will do too (very soon, once we start using NinjaTurtle as out turtle impl). So there is no way to teleport, at least - that's an artifact of current implementation that is incorrect.

Regards, other forms of cheating - that would be enforce by change the methods of the base turtle type for that world. e.g. for a grid world with only 90 deg changes allowed, it would coerce them to the nearest 90.

e.g.

class GridTurtle(PowerTurtle):
    def left(self, angle=90):
        angle = angle % 360
        if angle is None or angle <=90
            angle = 90
        elif angle <= 180:
            angle = 180
        elif angle <= 270:
            angle = 270
        else:
            return
        super(GridTurtle).left(angle)

@bloodearnest
Copy link
Member

So here's sort of what I'd imaging it'd look like in my head.

There'd be one file/script per turtle, a rough example of the asteroid

Asteroid script

# initialisation code
set_type('asteroid')
hideturtle()
setposition(...random...) # probably have helper for this.
setheading(...random...)
color('brown')
shape('circle')
speed(...random..)

radius = ...random...
shapesize(radius)

ship = world.get_first_turtle(type='ship')
alive = True

def explode(rocket):
    world.remove_turtle(rocket)
    alive = False
    exit()

def update():
    if distance(ship) < radius:  # more advanced api: if collided_with(ship):
        ship.die()
    rockets = world.get_turtles(type='rocket')
    for rocket in rockets:
        if distance(rocket) < radius:
            explode()
    if alive:
        forward()

In the above, all symbols save 'world' are just the bound methods of the appropriate PowerTurtle instance. The only new api's used that are not part of the stdlib turtle interface are 'exit' and 'set_type'

This script would be loaded as a function body with preloaded locals() (or func_closure/closure) to include the turtle's bare methods, pre-bound. The update and explode method definitions could be updated if changed, although the init code probably shouldn't be, perhaps.

If we assume a gui, we could even split these up into two separate windows/tabs - "Setup" for the init code and explode func, and "Update" for the update function code. That would give us more freedom about how we dynamically load/reload them.

@bwhmather
Copy link
Author

I think we are both arguing for the same thing here which is probably good.

I'm just using exec on line 109 to run the user script with the globals dict containing some utility functions and a reference to the turtle with game breaking methods hidden. This acheives basically the same thing as copying the script into a function body. The student code is currently loaded from the hard coded script string but could be loaded from a file or window.

I've left some of the initalisation code in Ship.setup as I don't think it makes sense for the ai code to be able to change the appearance of the ship.

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

Successfully merging this pull request may close these issues.

2 participants