1

so i am creating a snake game and i am having trouble with detecting it self colliding, what i need to understand is this: my snake is a tuple

snake = [(s_pos, s_pos),(s_pos + size, s_pos),(s_pos + size * 2, s_pos)]

like this and when it grows it create another thing on the list, like the code up there goes until snake[3], i know how to detect the collision with the first 3 parts of the snake, but when it goes up it says index out of range, here is the collision code

def collision (c1,c2):
    return (c1[0] == c2[0]) and (c1[1] == c2[1])

if collision(snake[0], snake[1]):
        print("self collision")

is there a way to be like

if collision(snake[0], snake[>1]):
        print("self collision")

without the index out of range?

when the snake grows it creates the snake[4] and i dont know how to create a code where i can detect the collision, sorry if im not being clear, the code that makes the snake grow: snake.append((0,0))

all my code(consider im really new to coding):

import pygame, random as rand, os

from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((1020, 585))
pygame.display.set_caption('2snakes!')
#files location
current_path = os.path.dirname(__file__)
data_path = os.path.join(current_path, 'data')
icon = pygame.image.load(os.path.join(data_path, 'icon.png'))
pygame.display.set_icon(icon)

#variables
direction = 'RIGHT'
direction2 = 'RIGHT'
change_to = direction
change2_to = direction2
fps = 15


#snake
size = 15
s_pos = 60
snake = [(s_pos, s_pos),(s_pos + size, s_pos),(s_pos + size * 2, s_pos)]
s_skin = pygame.Surface((size, size))
s_skin.fill((82,128,208))
#snake2
size2 = 15
s2_pos = 90
snake2 = [(s2_pos, s2_pos),(s2_pos + size2, s2_pos),(s2_pos + size2 * 2, s2_pos)]
s2_skin = pygame.Surface((size2, size2))
s2_skin.fill((208,128,82))
#apple
apple = pygame.Surface((size, size))
apple_pos = (165, 150)
#collission
def collision (c1,c2):
    return (c1[0] == c2[0]) and (c1[1] == c2[1])

def selfColliding(snake: list):
        for i in snake:
            if snake.count(i) > 1:
                return True
        return False

def gameOver():
    #pygame.quit()
    print("gameover")

def selfColliding(snake: list):
    return True if snake.count(snake[0]) > 1 else False

while True:
    #fps
    pygame.time.Clock().tick(fps)
    #basic stuff
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
        #input
        elif event.type == pygame.KEYDOWN:
            #snake
            if event.key == ord('w'):
                change_to = 'UP'
            if event.key == ord('s'):
                change_to = 'DOWN'
            if event.key == ord('a'):
                change_to = 'LEFT'
            if event.key == ord('d'):
                change_to = 'RIGHT'           
            #snake2
            if event.key == pygame.K_UP:
                change2_to = 'UP'
            if event.key == pygame.K_DOWN:
                change2_to = 'DOWN'
            if event.key == pygame.K_LEFT:
                change2_to = 'LEFT'
            if event.key == pygame.K_RIGHT:
                change2_to = 'RIGHT'
            #quit game
            if event.key == pygame.K_ESCAPE:
                pygame.event.post(pygame.event.Event(pygame.QUIT))
    #noice smooth snake movement
        #snake
    if change_to == 'UP' and direction != 'DOWN':
        direction = 'UP'
    if change_to == 'DOWN' and direction != 'UP':
        direction = 'DOWN'
    if change_to == 'LEFT' and direction != 'RIGHT':
        direction = 'LEFT'
    if change_to == 'RIGHT' and direction != 'LEFT':
        direction = 'RIGHT'
        #snake2
    if change2_to == 'UP' and direction2 != 'DOWN':
        direction2 = 'UP'
    if change2_to == 'DOWN' and direction2 != 'UP':
        direction2 = 'DOWN'
    if change2_to == 'LEFT' and direction2 != 'RIGHT':
        direction2 = 'LEFT'
    if change2_to == 'RIGHT' and direction2 != 'LEFT':
        direction2 = 'RIGHT'
    #movement
        #snake
    if direction == 'DOWN':
        snake[0] = (snake[0][0], snake[0][1] + size)
    if direction == 'UP':
        snake[0] = (snake[0][0], snake[0][1] - size)
    if direction == 'LEFT':
        snake[0] = (snake[0][0] - size, snake[0][1])
    if direction == 'RIGHT':
        snake[0] = (snake[0][0] + size, snake[0][1])
        #snake2
    if direction2 == 'DOWN':
        snake2[0] = (snake2[0][0], snake2[0][1] + size2)
    if direction2 == 'UP':
        snake2[0] = (snake2[0][0], snake2[0][1] - size2)
    if direction2 == 'LEFT':
        snake2[0] = (snake2[0][0] - size2, snake2[0][1])
    if direction2 == 'RIGHT':
        snake2[0] = (snake2[0][0] + size2, snake2[0][1])
    #snake apple collision
    if collision(snake[0], apple_pos):
        snake.append((0,0))
    #snake2 apple collision
    if collision(snake2[0], apple_pos):
        snake2.append((0,0))
    #snake wall collisison
    if snake[0][0] < 0 or snake[0][1] < 0:
        gameOver()
    elif snake[0][0] > 1020 or snake[0][1] > 585:
        gameOver()
    #snake2 wall collisison
    #if snake2[0][0] < 0 or snake2[0][1] < 0:
        #gameOver()
    #elif snake2[0][0] > 1020 or snake2[0][1] > 585:
        #gameOver()

    #all blocks follow first
        #snake
    for i in range(len(snake) -1, 0, -1):
        snake[i] = (snake[i-1][0], snake[i-1][1])
        #snake2
    for i in range(len(snake2) -1, 0, -1):
        snake2[i] = (snake2[i-1][0], snake2[i-1][1])

    if selfColliding:
        print("self colliding")
    

    #rendering
    screen.fill((0,0,0))
    apple.fill((255,0,0))
    screen.blit(apple,apple_pos)
    for pos in snake:
        screen.blit(s_skin,pos)
    for pos2 in snake2:
        screen.blit(s2_skin,pos2)
    pygame.display.update()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
zac
  • 139
  • 7
  • 1
    Can you please show the code where the index out of range exception occurs rather than some pseudocode? – Rabbid76 Oct 25 '20 at 18:56
  • ok sorry, `def collision (c1,c2): return (c1[0] == c2[0]) and (c1[1] == c2[1])` `if collision(snake[0], snake[4]): print("self collision")` then the error is list index out of range – zac Oct 25 '20 at 18:58
  • yes i know, but when the snake grows it creates the `snake[4]` and i dont know how to create a code where i can detect the collision, sorry if im not being clear, the code that makes the snake grow: `snake.append((0,0))` – zac Oct 25 '20 at 19:01
  • ok, sorry, i'll do it – zac Oct 25 '20 at 19:03

3 Answers3

1

If you want to test that the first element in a list is identical to any other element in the list, you can do the following:

if snake[0] in snake[1:]:
    print("self collision")
    print(snake[0], "is in", snake[1:])

The 1st element seems to be contained twice in your lists. See How do I chain the movement of a snake's body? and apply the solution to your code:

direction = 'RIGHT'
direction2 = 'RIGHT'

# [...]

snake = [(s_pos + size * 2, s_pos),(s_pos + size, s_pos),(s_pos, s_pos)]
snake2 = [(s2_pos + size2 * 2, s2_pos),(s2_pos + size2, s2_pos),(s2_pos, s2_pos)]

# [...]

while True:
   # [...]

    new_pos = None
    if direction == 'DOWN':
        new_pos = (snake[0][0], snake[0][1] + size)
    if direction == 'UP':
        new_pos = (snake[0][0], snake[0][1] - size)
    if direction == 'LEFT':
        new_pos = (snake[0][0] - size, snake[0][1])
    if direction == 'RIGHT':
        new_pos = (snake[0][0] + size, snake[0][1])
    if new_pos:
        snake = [new_pos] + snake
        del snake[-1]
    
    new_pos2 = None
    if direction2 == 'DOWN':
        new_pos2 = (snake2[0][0], snake2[0][1] + size2)
    if direction2 == 'UP':
        new_pos2 = (snake2[0][0], snake2[0][1] - size2)
    if direction2 == 'LEFT':
        new_pos2 = (snake2[0][0] - size2, snake2[0][1])
    if direction2 == 'RIGHT':
        new_pos2 = (snake2[0][0] + size2, snake2[0][1])
    if new_pos2:
        snake2 = [new_pos2] + snake2
        del snake2[-1]
    
    #snake apple collision
    if collision(snake[0], apple_pos):
        snake.append((-20,-20))
    #snake2 apple collision
    if collision(snake2[0], apple_pos):
        snake2.append((-20,-20))
    #snake wall collisison
    if snake[0][0] < 0 or snake[0][1] < 0:
        gameOver()
    elif snake[0][0] > 1020 or snake[0][1] > 585:
        gameOver()
        
    if snake[0] in snake[1:]:
        print("self collision")
        print(snake2[0], "is in", snake2[1:])
    if snake2[0] in snake2[1:]:
        print("self collision")
        print(snake2[0], "is in", snake2[1:])
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • this returns true every frame – zac Oct 25 '20 at 19:14
  • 1
    @zac Do `print(snake[0], "is in", snake[1:])` when a collision is detected. So that you can understand your mistake. – Rabbid76 Oct 25 '20 at 19:18
  • @zac the 1st element seems to be contained twice in your lists. – Rabbid76 Oct 25 '20 at 19:30
  • wow, this is like a problem? i discovered the code to detect auto collision, it is: `if snake[0] in snake[1:][1:]: print("self collision") print(snake[0], "is in", snake[1:])` – zac Oct 25 '20 at 19:35
  • @zac I've extended the answer. – Rabbid76 Oct 25 '20 at 19:44
  • 1
    so i found the "bug", this line: ```python for i in range(len(snake) -1, 0, -1): snake[i] = (snake[i-1][0], snake[i-1][1]) #snake2 for i in range(len(snake2) -1, 0, -1): snake2[i] = (snake2[i-1][0], snake2[i-1][1]) ``` the tutorial i found online used this line for arranjing the snake blocks, but with your code is unnecessary, thanks – zac Oct 25 '20 at 20:53
0

A possible solution to check if any tuple exists multiple times in the list:

def selfColliding(snake: list):
    for i in snake:
        if snake.count(i) > 1:
            return True
    return False
            

Or as pointed out by @Rabbid76 if you only want to check if the head collides:

def selfColliding(snake: list):
    return True if snake.count(snake[0]) > 1 else False
Lcj
  • 371
  • 3
  • 10
  • ok so im really new to coding, just a question, the def should be outside the running class? and inside it i do an `if selfcollidind: game()` – zac Oct 25 '20 at 19:13
0

I discovered(in reality i didnt discovered, i just used @rabbid76 code and i tried some changes and it worked!) how to do it:

 if snake[0] in snake[1:][1:]:
        print("self collision")
        print(snake[0], "is in", snake[1:])

i used @Rabbid76 code with a minor change and it works!

zac
  • 139
  • 7