9
\$\begingroup\$

The code creates a very simple/easy RPG game, with 2 classes Jedi and Orc. The data is visualized using turtle. Each class has a method of attack (lightsaber_attack for Jedi), which has an argument that must be either a Jedi or Orc instance. The .health attribute of the attacked one will be reduced by .power of the attacker. If .health is not positive, then the image of the character will disappear. By design, each character can attack itself.

Simulation

luke.lightsaber_attack( orc_1 )
luke.lightsaber_attack( orc_2 )
orc_1.attack( luke )
orc_2.attack( orc_2 )

Questions

  • How can I make the code to be easily understood by teenagers? (in a tutorial)
  • How can I make it more compact?
  • Are there any missing important features of Python's OOP that are important to be explained to students? (other than super and inheritance)

Image Links

Full code

import turtle
import time

jedi_gif = "/home/asus/Arief_tempo/images/random/jedi.gif"
orc_gif = "orc.gif"
darkorc_gif = "darkorc.gif"
damaged_gif = "damaged.gif" 

turtle.register_shape( jedi_gif )
turtle.register_shape( orc_gif )
turtle.register_shape( darkorc_gif )
turtle.register_shape( damaged_gif )

class JediLuke:
    def __init__(self):
        self.power = 300
        self.health = 300

        self.img = turtle.Turtle( shape = jedi_gif )
        self.damaged_img = turtle.Turtle( shape = damaged_gif, visible = False )

        self.img.penup()
        self.damaged_img.penup()

    def lightsaber_attack(self, enemy):
        self.img.setpos(enemy.img.pos()[0], enemy.img.pos()[1])
        enemy.damaged_img.showturtle()
        enemy.health += - self.power
        time.sleep(1)

        enemy.damaged_img.hideturtle()
        if enemy.health < 0:
            enemy.img.hideturtle()

        self.img.setpos(200, 0)

    def change_pos(self, pos):
        self.img.setpos(pos[0], pos[1])
        self.damaged_img.setpos(pos[0], pos[1] + 150)


class Orc:
    def __init__(self, health, gif_image):
        self.power = 100
        self.health = health

        self.img = turtle.Turtle( shape = gif_image )
        self.damaged_img = turtle.Turtle( shape = damaged_gif, visible = False )

        self.img.penup()
        self.damaged_img.penup()

    def attack(self, enemy):
        current_pos = self.img.pos()
        self.img.setpos(enemy.img.pos()[0], enemy.img.pos()[1])
        enemy.damaged_img.showturtle()
        enemy.health += - self.power

        time.sleep(1)

        enemy.damaged_img.hideturtle()
        if enemy.health < 0:
            enemy.img.hideturtle()

        self.img.setpos(current_pos[0], current_pos[1])

    def change_pos(self, pos):
        self.img.setpos(pos[0], pos[1])
        self.damaged_img.setpos(pos[0], pos[1] + 150)


luke = JediLuke()
luke.change_pos( [200, 0] )

orc_1 = Orc( health = 400 , gif_image = orc_gif)
orc_1.change_pos( [-200, 100] )

orc_2 = Orc( health = 200, gif_image = darkorc_gif )
orc_2.change_pos( [-200, -100] )
\$\endgroup\$
  • 5
    \$\begingroup\$ As an aside: Luke Skywalker attacking orcs is a jarring mix-up of universes. \$\endgroup\$ – Reinderien Dec 28 '18 at 15:45
  • 3
    \$\begingroup\$ I upvoted @Reinderien 's comment, but I also +1 this question because Luke Skywalker. Fighting Orcs. \$\endgroup\$ – bruglesco Dec 28 '18 at 16:10
  • 5
    \$\begingroup\$ Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers. \$\endgroup\$ – Mast Dec 28 '18 at 17:22
  • \$\begingroup\$ @Mast noted.... \$\endgroup\$ – Arief Anbiya Jan 2 at 13:37
  • \$\begingroup\$ why name the attacks differently? \$\endgroup\$ – Maarten Fabré Jan 3 at 9:48
9
\$\begingroup\$
jedi_gif = "/home/asus/Arief_tempo/images/random/jedi.gif"

It's unclear why this image has an absolute path but no others do. They should probably all be relative, as the other three are.

Especially if this is for a tutorial, you need to add docstrings to all of your functions.

This:

self.img.setpos(enemy.img.pos()[0], enemy.img.pos()[1])

can use argument expansion, i.e.:

self.img.setpos(*enemy.img.pos())

That pattern can be used elsewhere you're indexing into the position.

This:

enemy.health += - self.power

should be

enemy.health -= self.power
\$\endgroup\$
  • \$\begingroup\$ Thanks. Why need docstrings? the functions will be explained in class. If I use f(*pos) then I need to explain further about function to the kids (which makes the subject a bit more tedious to them), but using f(pos[0], pos[1]) would be more obvious. \$\endgroup\$ – Arief Anbiya Dec 29 '18 at 13:01
  • \$\begingroup\$ "Explaining it [presumably verbally] in class" is not good enough. The code should document itself. You should be able to hand a copy to a programmer who has not attended your class and have some reasonable expectation that they'll understand what's going on. \$\endgroup\$ – Reinderien Dec 29 '18 at 16:59
5
\$\begingroup\$

Just a note of something that was particularly jarring when viewing your code; for stylistic reasons, you shouldn't have spaces on either side of the arguments:

turtle.register_shape( jedi_gif )

Instead you want:

turtle.register_shape(jedi_gif)

This is covered in PEP 8 in the section Whitespace in Expressions and Statements. It's good to follow PEP 8 because it makes it easier for others (and in the long run, yourself) to read your code:

\$\endgroup\$
2
\$\begingroup\$

One guiding principle in programming is to write DRY code, Don't Repeat Yourself. Your JediLuke class and your Orc class are almost the same. Since you are already teaching about classes, you should also teach about inheritance (maybe later, but eventually).

import turtle
import time

class Entity:
    def __init__(self, power, health, img, damaged_img, *position):
        self.power = power
        self.health = health
        self.img = turtle.Turtle(shape=img)
        self.damaged_img = turtle.Turtle(shape=damaged_img, visible=False)

        self.img.penup()
        self.damaged_img.penup()
        self.set_position(*position)

    def attack(self, enemy):
        """Attack an enemy"""
        current_pos = self.img.pos()
        self.img.setpos(*enemy.img.pos())
        enemy.damaged(self.power)
        self.img.setpos(*current_pos)

    def damaged(self, power):
        """Take damage from `power`"""
        self.damaged_img.showturtle()
        self.health -= power
        time.sleep(1)
        self.damaged_img.hideturtle()
        if self.health <= 0:
            self.img.hideturtle()

    def set_position(self, pos):
        self.img.setpos(*pos)
        self.damaged_img.setpos(pos[0], pos[1] + 150)


class Jedi(Entity):
    def lightsaber_attack(self, enemy):
        super().attack(enemy)

    attack = None  # to ensure it cannot be called...


class Orc(Entity):
    pass


if __name__ == "__main__":

    jedi_gif = "jedi.gif"
    orc_gif = "orc.gif"
    darkorc_gif = "darkorc.gif"
    damaged_gif = "damaged.gif" 

    turtle.register_shape(jedi_gif)
    turtle.register_shape(orc_gif)
    turtle.register_shape(darkorc_gif)
    turtle.register_shape(damaged_gif)

    luke = Jedi(200, 0)
    orc_1 = Orc(400, orc_gif, -200, 100)       
    orc_2 = Orc(200, darkorc_gif, -200, -100)

This also has the calling code under an if __name__ == "__main__" guard to allow importing from this script and the whitespace fixed according to PEP8.

\$\endgroup\$
0
\$\begingroup\$

I just wanted to add use os path for cross compatibility with other os's. Since mac uses a "/", and windows "\"

otherwise if you copy your code to windows and run the script you will get an error saying it's not a valid directory

use

from os import path

path.join("Directory1", "Directory2", "filename.gif") 
# Equal to "Directory1/Directory2/filename.gif
# or "Directory1\Directory2\filename.gif
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.