Search code examples
pythonpython-3.xturtle-graphicspython-turtle

colliding condition of snake game using python not working


I tried to make a snake game in python using the turtle module. Overall, the game is working fine but the condition to show a game over when snake hits its own body is not working.

Following is the function I made to check collision of both types (with boundary and with itself):

def check_collision(self):
    #to check collision with boundary
    if 298 > self.head.xcor() > -298 and 298 > self.head.ycor() > -298:  

        #to check collision with its body  (s=list containing all parts of snake's body)
        for i in range(self.c):       #c = len(s) - 2
            if self.head.distance(self.s[i]) > 28:   
                f = 1
            else:
                f = 0
    else:
        f = 0

    #returning true or false to continue or terminate the game(false=terminate, true=continue)
    if f == 0:
        return False
    else:
        return True

This is working when the snake hits the boundary but not working when the snake hits its own body.

I'm expecting for the game to show game over when snake hits its own body, but the game continues.


Solution

  • This isn't exactly a complete, reproducible example, but you probably want something like:

    class Snake:
        collision_distance = 28
        screen_size = 298
    
        # ...
        def in_bounds(self):
            screen_size = self.screen_size
            return (
                -screen_size < self.head.xcor() < screen_size and
                -screen_size < self.head.ycor() < screen_size
            )
    
        def overlapping_tail(self):
            dist = self.collision_distance
            return any(self.head.distance(x) < dist for x in self.s[:-2])
    
        def alive(self):
            return self.in_bounds() and not self.overlapping_tail()
    
    
    # usage:
    if not snake.alive():
        end_game()
    

    any() is shorthand here for:

    for x in self.s[:-2]:
        if self.head.distance(x) < dist:
            return True
    return False
    

    Notes/suggestions:

    • Avoid the "single return" pattern. When you want to return, return immediately instead of introducing more mutable variables and control flow. Single return adds complexity for no reason and appears to be the cause of your bug. Specifically, you set f = 0 once you detect a collision, then risk flipping it back to 1 once another segment doesn't collide. If you must keep the single-return pattern, use break to exit the loop as soon as you detect a colliding segment.
    • Avoid using ints as bools. This hasn't even been necessary in C for the past 30-odd years, so it's truly archaic and confusing programming style.
    • Whenever you add a comment like #to check collision with boundary, break it out into a function check_collision_with_boundary and remove the comment.
    • Use print() to debug your code and observe the control flow and variables.
    • Avoid self.c. If it represents len(self.s) - 2, make it len(self.s) - 2 everywhere because len() is plenty fast and contains the most up-to-date information. With self.c, you have to remember to update it, and more state means a more complex program, which means more bugs.
    • self.s and self.c should use clearer variable names, like self.tail and self.tail_count or something like that. f should be valid (or removed complete in favor of immediate returns).
    • Avoid if x: return True; else: return False. Simply return x! (or return bool(x) if it's not a bool, and you want to return a bool based on its truthiness)
    • Instead of 142 > some_number > 42, prefer 42 < some_number < 142. Having the larger numbers first seems counter to how I usually see this written.
    • Break out literal values/constants to the class or initializer so they're easy to identify and change without having to hunt through the source code.