Procedural Generation Using Classes Pygame - python

I'm in the process of creating a basic game in pygame at the moment, and one part of that is the procedural generation of new areas as you go off the screen. As a test, I'm looking to generate an object once per area by defining its variables, and then save that area's object within the class for if you come back to it later. Here's what I have at the moment:
#area_gen is set to "true" if you move to a new area
#swidth and sheight are set to the size of the screen
#x_area and y_area are defined as you change areas, acting as sector coordinates
#Red is defined in globals
class areas:
def __init__(self, coords):
self.coordinates = coords
self.generated = False
def gen_objects(self):
if not self.generated:
self.objs = []
obj_type = "test object"
center_x = random.randrange(105, swidth-25)
center_y = random.randrange(25, swidth - 175)
self.objs.append([obj_type, center_x, center_y])
self.generated = True
#Within The Game Loop
if area_gen == "true":
coords = str(str(x_area) + " " + str(y_area))
area = areas(coords)
area.gen_objects()
for thing in area.objs:
if thing[0] == "test object":
pygame.draw.rect(screen, Red, (thing[1], thing[2], 250, 250))
#Bottom of the Game Loop
area_gen = "false"
What I thought the self.generated variable would do was stop the new object generation if one already existed, but that doesn't seem to be working. The square still generates at a new location even if the area has already been visited. My knowledge on classes is relatively limited, so I'm a bit stuck as to where to go from here.

Pass area_gen into the constructor for the areas class, and set self.generated = area_gen. Does this work? I can't see enough of your code to know, to be honest.

Related

Pygame/Python: Can't create deepcopy of surfaces for room loading system (tl;dr at bottom)

tl;dr at the bottom. Most of this post is to give context to my problem. If that isn't needed, my main question is down below.
I am currently working on a pygame project (action adventure game) and I currently am working on stuff relating to object persistence and room transitioning. Basically, I want to create a system where I have a dictionary that contains all of the information about a particular room including the images it uses and the objects that are in that room.
My game has a dictionary that contains lists of the current objects that are updating and doing stuff in my game. Let's call this instances. Anytime I add an object to instances, it will appear in game. With that in mind, lets consider how loading the objects in a particular room actually works.
The way my system works is that I have a variable that is called room which contains a string that represents what room I am currently in. I have another dictionary that contains all of the objects within a room. Lets call this room_dict. room_dict would have the keys "objects":[obj1,obj2]. So based on the current value of room, it can access certain objects (i.e,room_dict[room]["objects"] would return a list of all of the objects in the current room).
Now that I've explained the basics of how it works, I have a method that actually knows when I have triggered a room transition (or rather, when the value of room is changed). When this happens, all of the objects existing in the room (that I was just in) are cleared from the instances dictionary. All of the objects from room_dict[room]["objects"] are added to instances so that they now appear in the game. Makes sense so far, right?
The main problem with this is that when objects in the instances dictionary (objects that are currently loaded) are updating, the objects that are in room_dict[room]["objects"] are also updated as well. This means that if I change the position of an enemy in one room and then leave the room and return, the object will be created at that position instead of the original position. So I tried doing instances[list_of_enemies].append(copy.copy(enemy_object)) to add a copy of the object as well. This still didn't work though, and when I tried doing a copy.deepcopy(), the interpreter said that it was unable to serialize the object because one of its attibutes was a pygame.Surface object.
So in other words my main issue is that I want to make a copy of an object that contains a pygame.Surface as its attribute that doesn't reference the original object at all. How would I go about making a deepcopy with an object that has a pygame.Surface type attribute?
tl;dr: I want to make a copy of an object that has an attribute that is a pygame.Surface object but copy.deepcopy() doesn't work. Is there any other way to copy without referencing the original object?
EDIT: Forgot to mention that the project is rather hefty, so it would be quite difficult to give code for context. I personally don't think it is needed, but I thought I'd put this out anyways. Thanks everyone!
One way to solve this is to create your objects from your json file everytime you need a new copy instead of creating the objects beforehand.
Or you could change your copy method: implement __deepcopy__ and/or __copy__ and copy the attributes of your objects without the image attribute, maybe just create a new instance.
Simple example:
import pygame
from copy import deepcopy
from itertools import cycle
# an implementation of something in our game
class Cloud(pygame.sprite.Sprite):
def __init__(self, pos, speed):
super().__init__()
self.pos = pos
self.speed = speed
self.image = pygame.Surface((50, 20))
self.image.set_colorkey((11, 12, 13))
self.image.fill((11, 12, 13))
pygame.draw.ellipse(self.image, 'white', self.image.get_rect())
self.rect = self.image.get_rect(topleft=self.pos)
def update(self):
super().update()
self.rect.move_ip(self.speed, 0)
if not pygame.display.get_surface().get_rect().colliderect(self.rect):
self.rect.right = 0
def __deepcopy__(self, memo):
# just create a new instance
return Cloud(self.pos, self.speed)
# the definition of our game world
game_data = {
'WORLD_A': {
'color': 'lightblue',
'objects': pygame.sprite.Group(Cloud((50, 50), 1))
},
'WORLD_B': {
'color': 'red',
'objects': pygame.sprite.Group(Cloud((100, 100), 2), Cloud((80, 30), 3))
},
}
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
keys = cycle(game_data.keys())
# happy deepcopying
current = deepcopy(game_data[next(keys)])
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
quit()
if e.type == pygame.KEYDOWN:
# happy deepcopying
current = deepcopy(game_data[next(keys)])
screen.fill(current['color'])
current['objects'].update()
current['objects'].draw(screen)
pygame.display.flip()
clock.tick(30)
Another solution is to lazy load the images and look them up only when needed so you don't need to copy them. Here's a simple example:
... see example above ...
# load/create all images once and store them in a dict
def create_cloud_image():
image = pygame.Surface((50, 20))
image.set_colorkey((11, 12, 13))
image.fill((11, 12, 13))
pygame.draw.ellipse(image, 'white', image.get_rect())
return image
images = {
'cloud': create_cloud_image()
}
# a simple sprite that lazy loads its image
class CopyableActor(pygame.sprite.Sprite):
def __init__(self, image_key, pos):
super().__init__()
self.pos = pos
self.image_key = image_key
def init_image(self):
self.image = images['cloud']
self.rect = self.image.get_rect(topleft=self.pos)
def update(self):
if not hasattr(self, 'image'):
self.init_image()
# an implementation of something in our game
class Cloud(CopyableActor):
def __init__(self, pos, speed):
super().__init__('cloud', pos)
self.speed = speed
def update(self):
super().update()
self.rect.move_ip(self.speed, 0)
if not pygame.display.get_surface().get_rect().colliderect(self.rect):
self.rect.right = 0
... see example above ...

Issues with mask multiple of the same identical mob in pygame

as discussed in the title I am having issues with masking identical images.
#initalising the masks
Invader1= pygame.image.load('Space_invaders_character_1_1.png').convert_alpha()
Invader1= pygame.transform.scale(Invader11, (40,30))
Invader1_mask = pygame.mask.from_surface(Invader11)
Invader1_mask= Invader11_mask.scale((70,40))
Invader2= pygame.image.load('Space_invaders_character_2_1.png').convert_alpha()
Invader2= pygame.transform.scale(Invader21, (40,30))
Invader2_mask = pygame.mask.from_surface(Invader21)
Invader2_mask= Invader11_mask.scale((70,40))
Invader3= pygame.image.load('Space_invaders_character_3_1.png').convert_alpha()
Invader3= pygame.transform.scale(Invader31, (40,30))
Invader3_mask = pygame.mask.from_surface(Invader31)
Invader3_mask= Invader11_mask.scale((70,40))
#drawing characters
def drawEnemies (invX,invY):
for num in range (1,11):
invX = invX + 50
gameDisplay.blit(Invader32, (invX,invY))
gameDisplay.blit(Invader32, (invX,invY-50))
gameDisplay.blit(Invader22, (invX,invY-100))
gameDisplay.blit(Invader22, (invX,invY-150))
gameDisplay.blit(Invader12, (invX, invY -200))
while lives > 0:
offset = (bulletX -invX, bulletY - invY)
result = Invader11_mask.overlap(bullet_mask, offset)
Of course this isn't all my code, however, I hope you see what I am attempting to do. In essence I am attempting to loop to create a specific Invader (yes from space invaders), however, the masks are either not being created with the other invaders or aren't moving. Can someone please help me?
Thanks.
The meaningful answer to your problem is to stop what your doing right now and start using the Sprite and Group classes together with the collide_mask function.
You don't want to create several global variables for each thingy in your game. You want instances of classes (you usually use Sprite), and add them to a list (usually a Group).
So, create a class for your invaders that inherits from Sprite and give them a mask attribue, something like this:
class Invader(pygame.spriteSprite):
def __init__(self, image, pos):
super().__init__()
self.image = image
self.rect = image.get_rect(topleft=pos)
self.mask = pygame.mask.from_surface(image)
def update(self):
pass # handle movement
Create a Group for your bullets and one for your invaders, then you can check the collision with:
pygame.sprite.groupcollide(bullets, invaders, True, True, pygame.sprite.collide_mask)

Python Turtle color change

im currently playing around with pythons turtle module and im trying to make a grid of opaque square shapes, say 30x30 that can change color based on some property (doesn't matter what property) my question is, is there anyway to change the color of a shape once its already been drawn down on the canvas?
Ive tried adding all the square shapes to an array, both stamps and polygons, but it seems impossible to change the color of any of them once they have been drawn.
i know stamp doesn't work because its like a footprint of where the turtle was but is there any method at all that allows for this with polygons or some other method im not aware of?
I didn't add any snippets of code because its a pretty basic question and can be used for many things.
Yes, you can do it. The key to this, and many complicated turtle problems, is using stamps. They are individually, or collectively, removable. And since they take on the shape of the turtle itself, they can be images or arbitrary polygons of any size or color you wish:
from turtle import Turtle, Screen
from random import randrange, choice
from collections import namedtuple
from math import ceil
GRID = 15 # GRID by GRID of squares
SIZE = 30 # each square is SIZE by SIZE
INCREASE = 1.5 # how much to lighten the square's color
WHITE = [255, 255, 255] # color at which we stop changing square
DELAY = 100 # time between calls to change() in milliseconds
DARK = 32 # range (ceil(INCREASE) .. DARK - 1) of dark colors
def change():
block = choice(blocks)
blocks.remove(block)
color = [min(int(primary * INCREASE), WHITE[i]) for i, primary in enumerate(block.color)] # lighten color
turtle.color(color)
turtle.setposition(block.position)
turtle.clearstamp(block.stamp)
stamp = turtle.stamp()
if color != WHITE:
blocks.append(Block(block.position, color, stamp)) # not white yet so keep changing this block
if blocks: # stop all changes if/when all blocks turn white
screen.ontimer(change, DELAY)
HALF_SIZE = SIZE // 2
screen = Screen()
screen.colormode(WHITE[0])
screen.register_shape("block", ((HALF_SIZE, -HALF_SIZE), (HALF_SIZE, HALF_SIZE), (-HALF_SIZE, HALF_SIZE), (-HALF_SIZE, -HALF_SIZE)))
screen.tracer(GRID ** 2) # ala #PyNuts
turtle = Turtle(shape="block", visible=False)
turtle.speed("fastest")
turtle.up()
Block = namedtuple('Block', ['position', 'color', 'stamp'])
blocks = list()
HALF_GRID = GRID // 2
for x in range(-HALF_GRID, HALF_GRID):
for y in range(-HALF_GRID, HALF_GRID):
turtle.goto(x * SIZE, y * SIZE)
color = [randrange(ceil(INCREASE), DARK) for primary in WHITE]
turtle.color(color)
blocks.append(Block(turtle.position(), color, turtle.stamp()))
screen.ontimer(change, DELAY)
screen.exitonclick()
The idea of the Turtle is that images, once drawn, become pixels, commited to the canvas - even though the Turtle commands themselves are "vector" commands, when one is talking about "vectorial" vs "raster" graphics.
That means the Turtle drawing context can't know about what is already drawn "by itself" - there is no way for you to reference an already drawn shape, or polygon and change its properties. (update: but for the stamps - the Turtle object does record information about those - thanks #cdlane)
What you'd have to do,in this case, is to annotate on your code data about any shapes you'd like to further modify - and them redraw them in someway, when you need it. In other words: you develop code for a vector graphics model -which allows you to do that.
However, you should note, that the tkinter Canvas widget, on top of which the Turtle runs (at least, I believe), is itself a Vector model - so it might be more appropriate for what you have in mind. Its documentation and way of working may be hard to grasp, though.
So, going back to "having your own Vector graphics implementation", you could take advantage of Python Object Orientation and data model to build "history-turtle" - which could record a log and replay blocks of commands within the same position context (but allowing for different color, width, etc, ... settings).
It is a bitty tricker done than said, but then, here is a sample one:
from turtle import Turtle
"""
Python Turtle with logging and replay capabilities
Author: João S. O. Bueno <gwidion#gmail.com>
License: LGPL 3.0+
This implements HistoryTurtle - a subclass of
Python's turtle.Turtle wich features a simple
command history and new `replay` and `log`
methods -
`turtle.replay(<start>, <end>)` will reissue
the commands logged in those positions of the history,
but with the current Pen settings.
The optional kwarg `position_independent` can be passed
as `True` to `replay` so that the turtle's position
and heading are not restored to the ones recorded
along with the command.
https://gist.github.com/jsbueno/cb413e985747392c460f39cc138436bc
"""
class Command:
__slots__ = ( "method_name", "args", "kwargs", "pos", "heading")
def __init__(self, method_name, args=(),
kwargs=None, pos=None, heading=None):
self.method_name = method_name
self.args = args
self.kwargs = kwargs or {}
self.pos = pos
self.heading = heading
def __repr__(self):
return "<{0}> - {1}".format(self.pos, self.method_name)
class Instrumented:
def __init__(self, turtle, method):
self.turtle = turtle
self.method = method
def __call__(self, *args, **kwargs):
command = Command(self.method.__name__, args, kwargs,
self.pos(), self.heading())
result = self.method(*args, **kwargs)
if (self.method.__name__ not in self.turtle.methods_to_skip or
not kwargs.pop('skip_self', True)):
self.turtle.history.append(command)
return result
def pos(self):
return self.turtle._execute(Command('pos'), True)
def heading(self):
return self.turtle._execute(Command('heading'), True)
class HistoryTurtle(Turtle):
methods_to_skip = ('replay', '_execute', 'log')
def __init__(self, *args, **kw):
self._inited = False
super().__init__(*args, **kw)
self.history = []
self._replaying = [False]
self._inited = True
def __getattribute__(self, attr):
result = super().__getattribute__(attr)
if (not callable(result) or
attr.startswith('_') or
not self._inited or
self._replaying[-1]):
return result
return Instrumented(self, result)
def replay(self, start, end=None, *,
position_independent=False,
skip_self=True,
restore=True
):
results = []
self._replaying.append(True)
if restore:
pos, heading, state = self.pos(), self.heading(), self.pen()['pendown']
for command in self.history[start:end]:
results.append(
self._execute(command, position_independent))
if restore:
self.setpos(pos)
self.setheading(heading)
(self.pendown() if state else self.penup())
self._replaying.pop()
return results
def log(self, start=0, end=None):
for i, command in enumerate(self.history[start:end], start):
print(i, command)
def _execute(self, command, position_independent):
""" Execute a method without triggering the log
"""
# Warning: not thread-safe:
self._replaying.append(True)
method = getattr(self, command.method_name)
if not position_independent:
state = self.pen()['pendown']
self.setpos(command.pos)
self.setheading(command.heading)
(self.pendown() if state else self.penup())
result = method(*command.args, **command.kwargs)
self._replaying.pop()
return result

Is this an optimal way of updating sprites in pygame?

Cutting through all the unrelated parts this is how I implemented it:
done=False
while(done==False)
#here goes some code for listening to the events
if (some condition fulfilled) then
screen.blit(background, [0,0])
screen.blit(some_sprite,[x,y])
elif (more conditions)
do something else
else
do even more stuff
pygame.display.flip()
Without the background update within this conditional statement this sprite doesn't get deleted, of course, os I get multiple copies on the screen. I have a strong suspicion this is by far not the optimal way of handling the situation, because blitting the image that doesn't change every time I need to do something else seems like a waste of resources.
I would appreciate any suggestions
Here's what I would recommend, based on personal experience. I built a tile-based game and over-engineered it a bit. My final solution looked a bit like this:
class Graphic(object):
def __init__(*some_args):
self.owner = which_object_owns_this_graphic
self.priority = some_int
# if more than one graphic are stacked on each other, which one to display?
self.surface = surface_to_draw_on
self.graphic = sprite_to_draw
#property
def x(self): return self.owner.x
#property
def y(self): return self.owner.y
def draw(self):
self.surface.blit(self.graphic, (self.x, self.y))
class Tile(object):
def __init__(*some_args):
self.x = x
self.y = y
self.graphic = some_default_Graphic_with_priority_minusone
self.contains = [self]
# list of objects that live here right now
#property
def topmost(self):
"""returns the graphic of the object with highest priority that is contained here"""
global_update_list = []
# anytime a tile is moved into or out of, place it here
Then in my event loop:
for tile in global_update_list:
tile.topmost.draw()
global_update_list = []
That prevented me from having to redraw the screen every time something moved, and I could just redraw the tile it moved out of and the tile it moved into.

Python - Classes and OOP Basics

I do not fully understand classes. I have read the python documentation and several other tutorials. I get the basic gist of it but don't understand the nuance. For instance in my code here:
class whiteroom():
""" Pick a door: red, blue, green, or black. """
do = raw_input("> ")
if "red" in do:
print "You entered the red room."
elif "blue" in do:
print "You entered the blue room."
elif "green" in do:
print "You entered the green room."
elif "black" in do:
print "You entered the black room."
else:
print "You sit patiently but slowly begin to stave. You're running out of time."
return whiteroom()
game = whiteroom()
game
(original codepad)
I would like to return the class whiteroom. Which is, either not possible, or not being done correctly. If you could clear up how to return a class or how to "link" two classes together so that whiteroom repeats on the else and the other rooms (which would be classes) are returned when called that would be awesome.
Also I'm super shaky on __init__ and am still not really sure what its purpose is. Everyone keeps telling me that it "initializes", which I'm sure it does, but that doesn't seem to be helping my brain out.
Functions are very different from classes. It looks like you took a function and just changed the def to class. I guess that mostly works in your case, but it's not how classes are supposed to go.
Classes contain functions (methods) and data. For example, you have a ball:
class Ball(object):
# __init__ is a special method called whenever you try to make
# an instance of a class. As you heard, it initializes the object.
# Here, we'll initialize some of the data.
def __init__(self):
# Let's add some data to the [instance of the] class.
self.position = (100, 100)
self.velocity = (0, 0)
# We can also add our own functions. When our ball bounces,
# its vertical velocity will be negated. (no gravity here!)
def bounce(self):
self.velocity = (self.velocity[0], -self.velocity[1])
Now we have a Ball class. How can we use it?
>>> ball1 = Ball()
>>> ball1
<Ball object at ...>
It doesn't look very useful. The data is where it could be useful:
>>> ball1.position
(100, 100)
>>> ball1.velocity
(0, 0)
>>> ball1.position = (200, 100)
>>> ball1.position
(200, 100)
Alright, cool, but what's the advantage over a global variable? If you have another Ball instance, it will remain independent:
>>> ball2 = Ball()
>>> ball2.velocity = (5, 10)
>>> ball2.position
(100, 100)
>>> ball2.velocity
(5, 10)
And ball1 remains independent:
>>> ball1.velocity
(0, 0)
Now what about that bounce method (function in a class) we defined?
>>> ball2.bounce()
>>> ball2.velocity
(5, -10)
The bounce method caused it to modify the velocity data of itself. Again, ball1 was not touched:
>>> ball1.velocity
Application
A ball is neat and all, but most people aren't simulating that. You're making a game. Let's think of what kinds of things we have:
A room is the most obvious thing we could have.
So let's make a room. Rooms have names, so we'll have some data to store that:
class Room(object):
# Note that we're taking an argument besides self, here.
def __init__(self, name):
self.name = name # Set the room's name to the name we got.
And let's make an instance of it:
>>> white_room = Room("White Room")
>>> white_room.name
'White Room'
Spiffy. This turns out not to be all that useful if you want different rooms to have different functionality, though, so let's make a subclass. A subclass inherits all functionality from its superclass, but you can add more functionality or override the superclass's functionality.
Let's think about what we want to do with rooms:
We want to interact with rooms.
And how do we do that?
The user types in a line of text that gets responded to.
How it's responded do depends on the room, so let's make the room handle that with a method called interact:
class WhiteRoom(Room): # A white room is a kind of room.
def __init__(self):
# All white rooms have names of 'White Room'.
self.name = 'White Room'
def interact(self, line):
if 'test' in line:
print "'Test' to you, too!"
Now let's try interacting with it:
>>> white_room = WhiteRoom() # WhiteRoom's __init__ doesn't take an argument (even though its superclass's __init__ does; we overrode the superclass's __init__)
>>> white_room.interact('test')
'Test' to you, too!
Your original example featured moving between rooms. Let's use a global variable called current_room to track which room we're in.1 Let's also make a red room.
1. There's better options besides global variables here, but I'm going to use one for simplicity.
class RedRoom(Room): # A red room is also a kind of room.
def __init__(self):
self.name = 'Red Room'
def interact(self, line):
global current_room, white_room
if 'white' in line:
# We could create a new WhiteRoom, but then it
# would lose its data (if it had any) after moving
# out of it and into it again.
current_room = white_room
Now let's try that:
>>> red_room = RedRoom()
>>> current_room = red_room
>>> current_room.name
'Red Room'
>>> current_room.interact('go to white room')
>>> current_room.name
'White Room'
Exercise for the reader: Add code to WhiteRoom's interact that allows you to go back to the red room.
Now that we have everything working, let's put it all together. With our new name data on all rooms, we can also show the current room in the prompt!
def play_game():
global current_room
while True:
line = raw_input(current_room.name + '> ')
current_room.interact(line)
You might also want to make a function to reset the game:
def reset_game():
global current_room, white_room, red_room
white_room = WhiteRoom()
red_room = RedRoom()
current_room = white_room
Put all of the class definitions and these functions into a file and you can play it at the prompt like this (assuming they're in mygame.py):
>>> import mygame
>>> mygame.reset_game()
>>> mygame.play_game()
White Room> test
'Test' to you, too!
White Room> go to red room
Red Room> go to white room
White Room>
To be able to play the game just by running the Python script, you can add this at the bottom:
def main():
reset_game()
play_game()
if __name__ == '__main__': # If we're running as a script...
main()
And that's a basic introduction to classes and how to apply it to your situation.
I'm sure you've heard all this before, but I'll give it a go.
Classes are a way to group up a bunch of function and variables into a single object. When you get all the way down to it, this is simply a way of organizing everything into groups that make sense. There are benefits down the road for making things easier to understand, debug, extend, or maintain, but basically its just a way to make something more defined in your mental model.
Your code looks like you are trying to write your entire program inside an 'object' (really, you just have an incorrectly written function).
Consider this instead.
Think of your mental model of rooms which have doors to them and whiteboards in them. Doors have a color. Also, whiteboards can have some text written on them. We'll leave it there to be simple.
To me, this suggests 3 different objects -- a door object that has a string for color, a whiteboard object that has a string for the text, and a room object that has a door and a whiteboard.
Consider the following code:
class Door(object):
def __init__(self, color):
self.color = color
class Whiteboard(object):
def __init__(self, default_text=''):
self.text = ''
self.write_text(default_text)
def write_text(self, text):
self.text += text
def erase(self):
self.text = ''
class Room(object):
def __init__(self, doorcolor, whiteboardtext=''):
self.whiteboard = Whiteboard(whiteboardtext)
self.door = Door(doorcolor)
# make a room with a red door and no text on the whiteboard
room1 = Room('red')
# make a room with a blue door and 'yeah, whiteboard' on the whiteboard
room2 = Room('blue', 'yeah, whiteboard')
# make a room with a green door
room3 = Room('green')
# now I can play around with my 'rooms' and they keep track of everything internally
print 'room 1 door color: ' + room1.door.color
print 'room 2 door color: ' + room2.door.color
# all my rooms have a door and a whiteboard, but each one is different and self contained. For example
# if I write on room 1's whiteboard, it doesn't change anything about room 3s
print 'room1 whiteboard: ' + room1.whiteboard.text
print 'room2 whiteboard: ' + room2.whiteboard.text
print 'room3 whiteboard: ' + room3.whiteboard.text
print '-- changeing room 1 whiteboard text --'
room1.whiteboard.write_text('oop is really helpful')
print 'room1 whiteboard: ' + room1.whiteboard.text
print 'room2 whiteboard: ' + room2.whiteboard.text
print 'room3 whiteboard: ' + room3.whiteboard.text
The init function is what gets called when you 'initialize' a new instance of your class. In the example I am making 3 Room objects which each create a Door and Whiteboard object internally. The parameters I pass into the constructor Room(parameter1, parameter2) get passed to the init functions - you can see I'm using this to set the door color and optionally some text on the whiteboard. Also notice that the variables that 'belong' to the objects are referenced with self - this reference is what gets passed in as the first parameter to all class functions (and becomes more important later when you are extending classes and other more advanced things).
You're really far off.
Sorry to say it, but this is just barely salvageable.
From what I can tell you want something like a room class, for instance:
class Room(object):
''' A generic room '''
def __init__(self):
self.choices = None
self.enter()
def enter(self):
''' Enter the room, to be filled out in subclass '''
pass
def print_choices(self):
'''You are stuck bro'''
print "You are stuck bro"
Then you can make a specific room like the whiteroom like so:
class Whiteroom(Room):
''' A white room '''
def __init__(self):
self.choices = ["red", "blue", "green", "black"]
self.enter()
def enter(self):
print "You sit patiently, but slowly begin to starve. You're running out of time."
def print_choices(self):
print "You can choose from the following rooms:"
print self.choices
class Blackroom(Room):
''' A black room '''
def enter(self):
print "It's really dark in here. You're out of time."
class Redroom(Room):
''' A red room '''
def __init__(self):
self.choices = ["black", "blue", "green", "white"]
self.enter()
def enter(self):
print "It's getting hot in here. So take off all your clothes."
def print_choices(self):
print "You can choose from the following rooms:"
print self.choices
class Blueroom(Room):
''' A blue room '''
def __init__(self):
self.choices = ["black", "red", "green", "white"]
self.enter()
def enter(self):
print "It's nice and cool in here. Stay awhile if you want."
def print_choices(self):
print "You can choose from the following rooms:"
print self.choices
class Greenroom(Room):
''' A green room '''
def __init__(self):
self.choices = ["black", "red", "blue", "white"]
self.enter()
def enter(self):
print "You won."
Then you'd have do this to run the game:
print "Type 'quit' to quit"
print "Type 'choices' to see what your choices are"
current_room = Whiteroom()
done = False
while (not done):
entry = raw_input("> ")
if entry == "quit":
done = True
if "choices" in entry:
current_room.print_choices()
if current_room.choices:
if entry in current_room.choices:
if "white" in entry:
current_room = Whiteroom()
if "black" in entry:
current_room = Blackroom()
if "red" in entry:
current_room = Redroom()
if "green" in entry:
current_room = Greenroom()
done = True
if "blue" in entry:
current_room = Blueroom()
That's my best attempt at turning your snippet into an actual game, using classes.
In OOP you have types and objects. Types are abstractions (I would call it idea of) and concrete objects. When you define a class via the syntax class Idea: you identify a type and you can give that type attributes and abilities.
For example:
class Idea:
populare = None
#staticmethod
def change_the_world(atrribute):
return f'a new {attribute} world'
Why is it great and not just a coding style? Just because, in our complex world there are many ideas and even more of the idea of those ideas and so it appears in complex applications. You can have the idea itself as a basement, a mix of other ideas, as a layer in multiple levels of ideas or a hybrid of all that.
As an example:
class Idea:
populare = None
#staticmethod
def change_the_world(atrribute):
return f'a new {attribute} world'
class Freedom(Idea):
populare = True
#classmethod
def desire(cls):
return cls.change_the_world('free')
How powerful this is? You will notice when you think about built-in-types and what all you have done or could do with them. How easy it gets to implement design pattern and to built a whole world that manifests your point of view. But enough of types, there are a concrete objects. They are manifested types with individual identities, so called Entity's.
As example:
class Color:
def __init__(self, name, value):
self.name = None
self.value = None
def less_red(self):
red_portion = self.value[0]
new_value = red_portion-1
self.value = (new_value, self.value[1], self.value[2])
return self.value
my_red = Color('My Red', (255, 0, 0))
Entities can be imagine as manifested idea, that can be identified by themselves. That why the we usually write self as the first parameter in the __init__ it is a hint that this pointer is a reference to itself, not the class but the entity you just initialized.
class Circle():
pi = 3.14
def __init__(self,radius=1):
self.radius = radius
self.area = radius*radius*self.pi
def get_circrumference(self):
return self.radius* self.pi*2
result:
Circle(23)
my_circle = Circle(30)
my_circle.pi =
3.14
my_circle.radius =
30
my_circle.get_circrumference() =
118.4
my_circle.area =
2826.0
help(Circle) =
Help on class Circle in module main:
class Circle(builtins.object)
| Circle(radius=1)
|
| Methods defined here:
|
| init(self, radius=1)
| Initialize self. See help(type(self)) for accurate signature.
|
| get_circrumference(self)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| dict
| dictionary for instance variables (if defined)
|
| weakref
| list of weak references to the object (if defined)
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| pi = 3.14
Here's a simple implementation of cartesian points using class:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, move_x, move_y):
self.x += move_x
self.y += move_y
def scale(self, factor):
self.x *= factor
self.y *= factor
def mod(self):
return (self.x ** 2 + self.y ** 2) ** (1 / 2)
A = Point(3, 4)
print(A.x, A.y) -> 3 4
print(A.mod()) -> 5
A.move(2, 8) #(5, 12)
A.scale(2) #(10, 26)

Categories

Resources