I'm trying to write a code that will display a shape from tetris on a board. I keep getting this error and don't know how to fix it:
TypeError: unbound method can_move() must be called with O_shape instance as first argument (got Board instance instead)
Here is my code:
class Board():
def __init__(self, win, width, height):
self.width = width
self.height = height
# create a canvas to draw the tetris shapes on
self.canvas = CanvasFrame(win, self.width * Block.BLOCK_SIZE + Block.OUTLINE_WIDTH,
self.height * Block.BLOCK_SIZE + Block.OUTLINE_WIDTH)
self.canvas.setBackground('light gray')
# create an empty dictionary
# currently we have no shapes on the board
self.grid = {}
def draw_shape(self, shape):
''' Parameters: shape - type: Shape
Return value: type: bool
draws the shape on the board if there is space for it
and returns True, otherwise it returns False
'''
if shape.can_move(self, 0, 0):
shape.draw(self.canvas)
return True
return False
class Tetris():
SHAPES = [I_shape, J_shape, L_shape, O_shape, S_shape, T_shape, Z_shape]
DIRECTION = {'Left':(-1, 0), 'Right':(1, 0), 'Down':(0, 1)}
BOARD_WIDTH = 10
BOARD_HEIGHT = 20
def __init__(self, win):
self.board = Board(win, self.BOARD_WIDTH, self.BOARD_HEIGHT)
self.win = win
self.delay = 1000 #ms
# sets up the keyboard events
# when a key is called the method key_pressed will be called
self.win.bind_all('<Key>', self.key_pressed)
# set the current shape to a random new shape
self.current_shape = self.create_new_shape()
# Draw the current_shape oan the board
self.board.draw_shape(self.current_shape)
def create_new_shape(self):
''' Return value: type: Shape
Create a random new shape that is centered
at y = 0 and x = int(self.BOARD_WIDTH/2)
return the shape
'''
# YOUR CODE HERE
y = 0
x = int(self.BOARD_WIDTH/2)
the_shape = random.choice(self.SHAPES)
return the_shape
you didn't post the code for the relevant method (can_move()).
besides, the error message is self explanatory, it expects a parameter of type O_shape but you're calling the method and passing it a Board instead.
def draw_shape(self, shape):
''' Parameters: shape - type: Shape
Return value: type: bool
draws the shape on the board if there is space for it
and returns True, otherwise it returns False
'''
if shape.can_move(self, 0, 0): # <-- you probably meant to call shape.can_move(0,0)
shape.draw(self.canvas)
return True
return False
methods that are bound to a class have the instance passed as the first parameter implicitly.
You forgot to instantiate your shape classes.
SHAPES = [I_shape(), J_shape(), L_shape(), O_shape(), S_shape(), T_shape(), Z_shape()]
But you should only have a single Shape class that takes arguments to turn them into the various shapes.
Related
I'm just beginning working with object-oriented programming. I have created some classes and am trying to complete the rectangle class. Any and all help is appreciated.
I'm confused about when you need to refer to self and when you can just create variables. For example, in defining the length of the rectangle, I don't know if I should call the variable self.length or just length, and I haven't been able to find any rectangle classes defined in this way.
import math
class Point (object):
# constructor
def __init__ (self, x = 0, y = 0):
self.x = x
self.y = y
# get distance
def dist (self, other):
return math.hypot (self.x - other.x, self.y - other.y)
# get a string representation of a Point object
def __str__ (self):
return '(' + str(self.x) + ", " + str(self.y) + ")"
# test for equality
def __eq__ (self, other):
tol = 1.0e-16
return ((abs (self.x - other.x) < tol) and (abs(self.y - other.y) < tol))
class Circle (object):
# constructor
def __init__ (self, radius = 1, x = 0, y = 0):
self.radius = radius
self.center = Point (x, y)
# compute cirumference
def circumference (self):
return 2.0 * math.pi * self.radius
# compute area
def area (self):
return math.pi * self.radius * self.radius
# determine if point is strictly inside circle
def point_inside (self, p):
return (self.center.dist(p) < self.radius)
# determine if a circle is strictly inside this circle
def circle_inside (self, c):
distance = self.center.dist (c.center)
return (distance + c.radius) < self.radius
# determine if a circle c intersects this circle (non-zero area of overlap)
def does_intersect (self, c):
# string representation of a circle
def __str__ (self):
# test for equality of radius
def __eq__ (self, other):
tol = 1.0e-16
class Rectangle (object):
# constructor
def __init__ (self, ul_x = 0, ul_y = 1, lr_x = 1, lr_y = 0):
if ((ul_x < lr_x) and (ul_y > lr_y)):
self.ul = Point (ul_x, ul_y)
self.lr = Point (lr_x, lr_y)
else:
self.ul = Point (0, 1)
self.lr = Point (1, 0)
# determine length of Rectangle
def length (self):
# determine width of Rectangle
def width (self):
# determine the perimeter
def perimeter (self):
# determine the area
def area (self):
# determine if a point is strictly inside the Rectangle
def point_inside (self, p)
# determine if another Rectangle is inside this Rectangle
def rectangle_inside (self, r):
# determine if two Rectangles overlap
def does_intersect (self, other):
# determine the smallest rectangle that circumscribes a circle
def rect_circumscribe (self, c):
# give string representation of a rectangle
def __str__ (self):
# determine if two rectangles have the same length and width
def __eq__ (self, other):
Basically, setting a value to self.length gives you the ability to access this value from other functions inside the class and from outside of the class. If you set a value to length, you are able to access this variable only in the current function inside the class.
Just a start, try to continue yourself:
class Rectangle (object):
# constructor
def __init__ (self, ul_x = 0, ul_y = 1, lr_x = 1, lr_y = 0):
# Called if you say: my_rectancle = Rectangle (-10, 10, 10, -10)
# Puts parameters in fields of your newly created object of class Rectancle
self.ul_x = ul_x
self.ul_y = ul_y
self.lr_x = lr_x
self.lr_y = lr_y
# compute cirumference
def circumference (self):
return 2 * (self.ur_x - self.lr_x) + 2 * (self.ul_y - self.lr_y)
# compute area
def area (self):
return (self.ur_x - self.lr_x) * (self.ul_y - self.lr_y)
[EDIT]
With regard to the additional code in your comment, it's quite close to what it should be. With some corrections:
# determine length of Rectangle
def length (self):
return self.ul_y - self.lr_y
# determine width of Rectangle
def width (self):
return self.lr_x - self.ul_x
# self. has been added, since e.g. lr_x is not a parameter
# of the width function, but a field of the object you make
# by instantiation: 'rectangle = Rectangle (10, 20, 100, 200)'
# After that, self refers to the object 'rectangle' you created,
# which has class 'Rectangle'.
#
# Note that a class is a type.
# You can have a type 'Dog'.
# Dog 'fluffy' is an instance of that class, so a particular dog.
# In the methods (functions) of 'Dog', 'self' refers to the particular
# dog you're working with.
# determine the perimeter
def perimeter (self):
return 2 * self.width () + 2 * self.length ()
# Note the () after width and length.
# They're needed because width and length are
# function calls (they DO something) rather than fields (data)
# You could also have stored width and length into fields,
# just like the constructor did with ul_x, ul_y, lr_x and lr_y,
# storing them in self.ul_x, self.ul_y etc.
# Then the braces wouldn't have been needed.
# But also out some superfluous braces here
# Multiplication goes before addition anyhow
# And return returns everything after it, no braces needed.
# determine the area
def area (self):
return self.width () * self.length ()
So, how far did you get now?
..trying to define a method within my Point Class that checks interaction with objects of my Rectangle class on interior or boundary using type based dispatch. I tried the code below, but yields: AttributeError: 'set' object has no attribute 'intersects'.
Also, seeking a way to clearly set what intersects at boundary vs. interior. Please advise.
class Point(object):
def __init__(self, x, y, height=0):
self.x = float(x)
self.y = float(y)
self.height = float(height)
def intersects(self, other):
if isinstance(other, Point):
s1=set([self.x, self.y])
s2=set([other.x, other.y])
if s1.intersection(s2):
return True
else:
return False
elif isinstance(other, Rectangle):
s1=set([self.x, self.y])
s2=set(other.pt_ll, other.pt_ur)
if s1.intersection(s2):
return True
else:
return False
class Rectangle(object):
def __init__(self, pt_ll, pt_ur):
"""Constructor.
Takes the lower left and upper right point defining the Rectangle.
"""
self.ll = pt_ll
self.lr = Point(pt_ur.x, pt_ll.y)
self.ur = pt_ur
self.ul = Point(pt_ll.x, pt_ur.y)
these are my calling statements:
pt0 = (.5, .5)
r=Rectangle(Point(0, 0),Point(10, 10))
s1 = set([pt0])
s2 = set([r])
print s1.intersects(s2)
it would be intersection() s1.intersection(s2), you are using a set not a Point object:
s1 = set([pt0]) # <- creates a python set
To use your intersects method you need Point objects:
p = Point(3,5) # <- creates a Point object that has intersects method
p2 = Point(3,5)
print(p.intersects(p2))
So using your example, you need to access the Point objects using the attributes of the Rectangle class:
r = Rectangle(Point(0, 0),Point(10, 10))
print(r.lr.intersects(r.ul)) # <- the Rectangle attributes lr and ul are Point objects because you passed in Point's when you initialised the instance r
You can simplify the assignments in Rectangle:
class Rectangle(object):
def __init__(self, pt_ll, pt_ur):
"""Constructor.
Takes the lower left and upper right point defining the Rectangle.
"""
self.lr = Point(pt_ur.x, pt_ll.y)
self.ul = Point(pt_ll.x, pt_ur.y)
You can also just use set literals:
s1 = {self.x, self.y}
s2 = {other.x, other.y}
I am new to python and I am trying to write class RectangleCollection. class Rectangle is given, and I need to write class RectangleCollection part.
class RectangleCollection has one list instance variable, rectangles, that should initially refer to an empty list.
get_same_area_rects takes a number as a parameter and returns a list of all Rectangles from the rectangles list that have that area.
class Rectangle:
""" A rectangle with a width and height. """
def __init__(self, w, h):
""" (Rectangle, number, number)
Create a new rectangle of width w and height h.
>>> r = Rectangle(1, 2)
>>> r.width
1
>>> r.height
2
"""
self.width = w
self.height = h
def area(self):
""" (Rectangle) -> number
Return the area of this rectangle.
>>> r = Rectangle(10, 20)
>>> r.area()
200
"""
return self.width * self.height
These are what I have done :
class RectangleCollection:
def __init__(self):
""" (RectangleCollection) -> NoneType
>>> rc = RectangleCollection()
>>> rc.rectangles
[]
"""
self.rectangles = []
def get_same_area_rects(self, number):
"""
>>>rc = RectangleCollection()
>>>r1 = Rectangle(10, 20)
>>>r2 = Rectangle(15, 20)
>>> r3 = Rectangle(20, 10)
>>>rc.rectangles.extend([r1, r2, r3])
>>>res = rc.get_same_area_rects(200)
>>>res == [r1, r3]
True
"""
self.number = number
a = self.rectangles.expend(self.area())
if number == self.rectangles.area():
return True
return False
but for get_same_area_rects part, I always get False..
I have no idea what I did wrong. Please help
i think it is because rc.rectangles gives me r1,r2,r3 addresses, not the areas. I should get [200,300,200] but I get the addresses. I think this is why I always get False.. How can I fix this problem?
How about use filter function to only take rectangles whose area is number
def get_same_area_rects(self, number):
return filter(lambda rect: rect.area() == number, self.rectangles)
CSC108 right? This function within class is not asking you to return True of False, it is asking you to call this function to get a list of rectangles that their area is 200
You have a typographic error in your code. It should be:
extend not expend as follows:
a = self.rectangles.extend(self.area())
if number == self.rectangles.extend(self.area()):
return True
Or simply:
a = self.rectangles.extend(self.area())
if number == a:
return True
You have to create a temporary list and then loop over the rectangles. This is because since we have to return a list which has the same area, we would need to use the rectangle.area() to compare if they are true or not and then add into the list.
def get_same_area_rects(self, number):
temp_list = []
for rectangle in self.rectangles:
if number == rectangle.area():
temp_list.append(rectangle)
return temp_list
hope it helps :)
I have this class (in a file named "occ_grid.py"):
class Grid:
def __init__(self, width, height, occupancy_value):
self.width = width
self.height = height
self.cells = []
# initialize grid to all specified occupancy value
for row in range(0, self.height):
self.cells.append([])
for col in range(0, self.width):
self.cells[row].append(occupancy_value)
def set_cell(self, point, value):
self.cells[point.y][point.x] = value
def get_cell(self, point):
return self.cells[point.y][point.x]
And I have another class/method(s) (in the file "worldmodel.py"):
class WorldModel:
def __init__(self, num_rows, num_cols, background):
self.background = occ_grid.Grid(num_cols, num_rows, background)
self.num_rows = num_rows
self.num_cols = num_cols
self.occupancy = occ_grid.Grid(num_cols, num_rows, None)
self.entities = []
self.action_queue = ordered_list.OrderedList()
def is_occupied(self, pt):
return (self.within_bounds(pt) and
occ_grid.get_cell(self.occupancy, pt) != None)
Notice how "def is_occupied" uses the method "get_cell". The thing is, "get_cell" is in a different file and it's a method in a different class. I thought about creating a new "grid" object, but I'm confused on where in my code I should create this object.
You're pretty close. It's just:
self.occupancy.get_cell(pt)
You could write:
occ_grid.Grid.get_cell(self.occupancy, pt)
and that would be equivalent -- But, that'd be pretty unidiomatic.
In order to use Grid at all from the file worldmodel.py, you need to import the Grid class.
You can do this in two possible ways.
import occ_grid. If you do this, you will have to access Grid with occ_grid.Grid
from occ_grid import Grid. Then you can use it with just Grid.
mgilson's answer is very clear about how to use it after you import it.
I am using the numpy module to retrieve the position of the maximum value in a 2d array. But this 2d array consists of MyObjects. Now I get the error:
TypeError: unorderable types: int() > MyObject()
I tried to override the int function with this code:
def int(self):
return self.score
But this does not solve my problem.
Do I have to convert my 2d array of MyObjects into a 2d array of integers, do I have to extend the Integer object (if this is possible in python) or can I override this int() function in another way?
[EDIT]
The full object:
class MyObject:
def __init__(self, x, y, score, direction, match):
self.x = x
self.y = y
self.score = score
self.direction = direction
self.match = match
def __str__(self):
return str(self.score)
def int(self):
return self.score
The way I call this object:
def traceBack(self):
self.matrix = np.array(self.matrix)
maxIndex = self.matrix.argmax()
print(self.matrix.unravel_index(maxIndex))
Try to use
...
def __int__(self):
return self.score
...
test = MyObject(0, 0, 10, 0, 0)
print 10+int(test)
# Will output: 20
in your MyObject class definition.
The max function takes a key that is applied on the elements. that's where you put score
Typically :
a = max(my_list, key=score)