I'm writing a program of self-learning cars driving to a point with pygame
and cython
. I think that the problem is somehow caused by cython
compiler and not pygame
, because there is nothing connected with pygame
in that method. However, when I cythonize the .pyd file, no error is given. Also if I change the code by any lines, for example, add a line before line 99, where the error is, the error line will not move by one, it will stay in 99 line. So, the error is somehow connected with the line number and not code. How is that possible?
Here is the class and the method I got an error in:
cdef class Car:
cdef:
double speed, car_angle, wheel_angle
Point position
Polygon shape
def __init__(self, position=(0.0, 0.0), speed=0.5, car_angle=0.0, wheel_angle=0.0):
self.position = Point(*position)
self.shape = Polygon(self.position, CAR_SHAPE[0], CAR_SHAPE[1], self.car_angle)
self.speed = speed
self.car_angle = car_angle # from -pi to pi
self.wheel_angle = wheel_angle # from -MAX_WHEEL_ANGLE to MAX_WHEEL_ANGLE
An error:
Traceback (most recent call last):
File "main.pyx", line 83, in main.Population.__init__
self.cars = [Car(position=CAR_START_POS, car_angle=random.random() * 2 - 1) for _ in range(amount)]
File "main.pyx", line 99, in main.Car.__init__
self.car_angle = car_angle # from -pi to pi
AttributeError: 'main.Car' object has no attribute 'shape'
Exception ignored in: 'main.main'
Traceback (most recent call last):
File "main.pyx", line 83, in main.Population.__init__
self.cars = [Car(position=CAR_START_POS, car_angle=random.random() * 2 - 1) for _ in range(amount)]
File "main.pyx", line 99, in main.Car.__init__
self.car_angle = car_angle # from -pi to pi
AttributeError: 'main.Car' object has no attribute 'shape'
Edit: As you can see the error is in line 99, but line 99 is
self.car_angle = car_angle # from -pi to pi
and it has nothing to do with attribute shape.
I have got all defines for the class Car
before the init function. And attribute shape is also in there. Then Why does it say, that there are no such attribute?
I'm using python 3.8, cython 0.29.21 Here is the whole code, if you need:
import random
from math import sin, cos, pi, sqrt
import numpy as np
import pygame
pygame.init()
cdef:
tuple BG_COLOR = (10, 10, 20)
tuple SIZE = (800, 600)
double MAX_WHEEL_ANGLE = 0.872665
tuple CAR_COLOR = (200, 200, 200)
tuple CAR_SHAPE = (23, 10)
tuple CAR_START_POS = (SIZE[0] / 2, SIZE[1])
clock = pygame.time.Clock()
font = pygame.font.SysFont("Arial", 18)
screen = pygame.display.set_mode(SIZE, pygame.NOFRAME)
def update_fps():
cdef:
str fps = str(int(clock.get_fps()))
fps_text = font.render(fps, True, pygame.Color("coral"))
return fps_text
cdef class Point():
cdef double x, y
cdef shift_x, shift_y
def __init__(self, double x, double y):
self.x = x
self.y = y
def __str__(self):
return self.x, self.y
cdef tuple to_array(self):
return (self.x, self.y)
cdef void rotate(self, Point center, double sine, double cosine):
self.x = cosine * (self.x - center.x) + sine * (self.y - center.y) + center.x
self.y = -sine * (self.x - center.x) + cosine * (self.y - center.y) + center.y
cdef class Polygon():
cdef:
Point center, p1, p2, p3, p4
double length, width, radius, angle, sine, cosine
def __init__(self, center: Point, length: double, width: double, angle: double):
self.center = center
self.length = length
self.width = width
self.radius = sqrt(width ** 2 + length ** 2)
self.p1 = Point(center.x - width / 2, center.y + length / 2)
self.p2 = Point(center.x + width / 2, center.y + length / 2)
self.p3 = Point(center.x + width / 2, center.y - length / 2)
self.p4 = Point(center.x - width / 2, center.y - length / 2)
self.angle = angle
sine, cosine = sin(pi - self.angle), cos(pi - self.angle)
self.p1.x, self.p1.y = self.shift_x(self.p1.x, self.p1.y, sine, cosine), self.shift_y(self.p1.x, self.p1.y, sine, cosine)
self.p2.x, self.p2.y = self.shift_x(self.p2.x, self.p2.y, sine, cosine), self.shift_y(self.p2.x, self.p2.y, sine, cosine)
self.p3.x, self.p3.y = self.shift_x(self.p3.x, self.p3.y, sine, cosine), self.shift_y(self.p3.x, self.p3.y, sine, cosine)
self.p4.x, self.p4.y = self.shift_x(self.p4.x, self.p4.y, sine, cosine), self.shift_y(self.p4.x, self.p4.y, sine, cosine)
cdef shift_x(self, double x, double y, sine, cosine):
return cosine * (x - self.center.x) + sine * (y - self.center.y) + self.center.x
cdef shift_y(self, double x, double y, sine, cosine):
return -sine * (x - self.center.x) + cosine * (y - self.center.y) + self.center.y
def __str__(self):
return (self.p1, self.p2, self.p3, self.p4)
cdef tuple to_array(self):
return self.p1.to_array(), self.p2.to_array(), self.p3.to_array(), self.p4.to_array()
cdef class Population:
cdef list cars
def __init__(self, amount: int):
self.cars = [Car(position=CAR_START_POS, car_angle=random.random() * 2 - 1) for _ in range(amount)]
cdef void go(self):
for car in self.cars:
car.go()
cdef class Car:
cdef:
double speed, car_angle, wheel_angle
Point position
Polygon shape
def __init__(self, position=(0.0, 0.0), speed=0.5, car_angle=0.0, wheel_angle=0.0):
self.position = Point(*position)
self.shape = Polygon(self.position, CAR_SHAPE[0], CAR_SHAPE[1], self.car_angle)
self.speed = speed
self.car_angle = car_angle # from -pi to pi
self.wheel_angle = wheel_angle # from -MAX_WHEEL_ANGLE to MAX_WHEEL_ANGLE
cdef void go(self):
self.position.y -= np.cos(self.car_angle) * self.speed
self.position.x += np.sin(self.car_angle) * self.speed
self.car_angle += self.wheel_angle
self.draw()
cdef void draw(self):
pygame.draw.polygon(screen, CAR_COLOR, self.shape.to_array(), 3)
pygame.draw.circle(screen, (255, 0, 0), (self.position.x, self.position.y), 1)
cdef class DNA:
pass
cpdef void main():
cdef bint close_program = False
cdef Population cars = Population(1000)
while not close_program:
screen.fill(BG_COLOR)
cars.go()
screen.blit(update_fps(), (10, 0))
clock.tick(60)
pygame.display.flip()
if __name__ == '__main__':
main()
EDIT2: most likely the problem is with python and cython versions. The .pyd
file it creates is main.cp39-win_amd64
, so it means that cython creates a file for python 3.9 and I use 3.8, but tried to go from 3.8 to 3.9. Unfortunately, I couldn't just move all downloaded libraries from 3.8 to 3.9, so, kept using the old version. If you know how to move all the libraries, help me. Otherwise, I have to somehow say cython to create files for python 3.8. And I also don't know how to do that.
So, there are 2 possible solutions:
The problem was with cython and python versions. Now I'm doing this project on python 3.9. I downloaded all libraries I will need for this project, but it would be a lot better if someone would help me with moving all python 3.8 libraries to 3.9 without manually downloading them.