6

I am trying to train a NEAT algorithm to play a simple game called 'curvefever'. I was able to create a pygame version of curvefever and now I want to train the AI to play it. Therefore, the AI has to learn to avoid obstacles: borders surrounding the game and tracks that each player leaves behind, like in Snake.

At the moment I am doing this in the following way:

  • Each player has a set of 'sensors' reaching forward that detect if and how far away an obstacle is.
  • Each 'sensor' is a straight line consisting of several pygame rectangles.
  • For each sensor it will detect if a collision with one of the obstacle rectangles occurred and calculate the distance of the collision to the player.
  • Which sensor detected the collision and the distance of the collision is the information that goes to the neural network.

The problem is that this is very slow! Running 'python -m cProfile -s cumtime ai.py' I figured that it is the detection of obstacles that is slowing the script down, taking up about 50% of the total runtime.

Please see some code below how I create the lines of sight:

posx = x-position of player

posy = y-position of player

dir = direction the player is going

dangle = is the degree-spacing between lines of sight

angle = total range (in degrees) of lines of sight

sketch illustrating the sensors

def create_lines_of_sight(posx, posy, dir, dangle, angle, length):
    dirs = [xdir for xdir in np.ceil(np.arange(dir-angle,dir+angle,dangle))]

    d_posx = np.cos(np.deg2rad(dir))
    d_posy = np.sin(np.deg2rad(dir))

    return list(map(functools.partial(f_lrects,posx,posy,length), dirs))


def create_rects(posx, posy, d_posx, d_posy, i):
    return f_rect(posx+i*d_posx,posy+i*d_posy,1,1,0,curvefever.WHITE)

f_create_rect = create_rects

def create_line_of_rects(posx, posy, length,dir):
    l = pygame.sprite.Group()
    ladd = l.add

    d_posx = np.cos(np.deg2rad(dir))
    d_posy = np.sin(np.deg2rad(dir))
    i = [i for i in range(2,length,8)]
    ladd(map(functools.partial(f_create_rect,posx,posy,d_posx,d_posy),i))
    return l

f_lrects = create_line_of_rects

All obstacles are rectangles defined as:

class Rect(pygame.sprite.Sprite):
    def __init__(self,x,y,width,height,dir,color):
        super().__init__()
        self.image = pygame.Surface([width, height])
        self.image.fill(color)
        self.rect = self.image.get_rect()

        self.rect.centerx = x
        self.rect.centery = y

and are saved in a sprite group.

What I tried

I tried adding a map command to get rid of the for loop, that did not speed it up much.

I tried adding the function names to remove the function lookup, I read this makes it faster, but it didn't.

I tried detecting an obstacle using 'Bresenham's Line Algorithm' and checking if an obstacle (x,y) position overlaps with the line of sight. Although this was a faster it did not work as it often missed obstacles. This happened because the line of sight did not exactly match the obstacle centre (rectx,recty) although it did overlap with the rectangle itself.

What do other people use to detect obstacles (maybe in pygame)? Any tips on how I can make this faster or more efficient are very welcome.

Thank you very much for your help!

9
  • So the obstacles are the trails, how are you storing them? Are they a bunch of points? Or is this more like snake where it is in a grid and you can only move up,down,left,right? Commented Apr 14, 2020 at 7:17
  • The obstacles are all rectangles and saved as a sprite.group: class Rect(pygame.sprite.Sprite): def __init__(self,x,y,width,height,dir,color): super().__init__() self.image = pygame.Surface([width, height]) self.image.fill(color) self.rect = self.image.get_rect() self.rect.centerx = x self.rect.centery = y Commented Apr 14, 2020 at 7:27
  • and you can move in any direction Commented Apr 14, 2020 at 7:30
  • What is dangle, angle? Are these angles related to the "sensors"? Are these angles constant? Commented Apr 14, 2020 at 7:39
  • 1
    Yes, dangle and angle are related to sensors. I added a picture that I hope adds some clarification and both are constants Commented Apr 14, 2020 at 7:48

1 Answer 1

0

I've been working on a similar project. In the end I've used the pygame.Rect.clipline() and pygame.Vector2.distance_to() methods of pygame:

def intersect_rect(self,other) -> tuple[float,float]:
        cl=other.clipline(self.a.x,self.a.y,self.b.x,self.b.y)
        if cl:
            return cl[0] if self.a.distance_to(cl[0]) <self.a.distance_to(cl[1]) else cl[1]
        else:
            return

self and other are both of a class, that inherited form the pygame.Rect class. self.a and self.b are two pygame.Vector2 objects. Where self.a is in the origin of the player and self.b the LoS. This resulted in a speedup of 100x, compared to a pure python function.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.