rhdang commited on
Commit
e89f54b
1 Parent(s): 3c3e82d

Upload 9 files

Browse files
snake-pathfinding-AI/README.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Snake Game with Pathfinding
2
+
3
+ This project implements the Snake game using Python and Pygame library. The Snake moves around the screen, eating apples to grow longer. The game ends if the Snake collides with itself or with the screen's boundaries.
4
+
5
+ ## Features
6
+ - Snake movement: The Snake moves around the screen based on a predefined pathfinding algorithm (BFS).
7
+ - Apple spawning: Apples spawn randomly on the screen for the Snake to eat.
8
+ - Collision detection: Detects when the Snake collides with itself or the screen's boundaries.
9
+ - Pathfinding: Implements Breadth-First Search (BFS) algorithm to find the shortest path from the Snake to the nearest Apple.
10
+
11
+ ## Installation
12
+
13
+ 1. Clone the repository:
14
+ ```bash
15
+ git clone https://github.com/your-username/snake-game.git
16
+ 2. Install dependencies:
17
+ ```bash
18
+ pip install pygame
19
+ 4. Run the game:
20
+ ```bash
21
+ python main.py
22
+
23
+ ## Usage
24
+ - The snake will automatically find the shortest path to the nearest apple using the BFS algorithm.
25
+
26
+ ## Tests
27
+ - To run unit tests:
28
+ - ```bash
29
+ python -m unittest test_snake.py
snake-pathfinding-AI/__pycache__/pathfinding.cpython-312.pyc ADDED
Binary file (2.62 kB). View file
 
snake-pathfinding-AI/__pycache__/settings.cpython-312.pyc ADDED
Binary file (866 Bytes). View file
 
snake-pathfinding-AI/__pycache__/snake.cpython-312.pyc ADDED
Binary file (3.94 kB). View file
 
snake-pathfinding-AI/main.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pygame
2
+ from snake import Apple, Snake
3
+ from pathfinding import BFS
4
+ from settings import *
5
+
6
+ def drawGrid():
7
+ for x in range(0, WIDTH, BLOCK_SIZE):
8
+ for y in range(0, HEIGHT, BLOCK_SIZE):
9
+ rect = pygame.Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)
10
+ pygame.draw.rect(SCREEN, GRID_CLR, rect, 1)
11
+
12
+ def handle_events():
13
+ for event in pygame.event.get():
14
+ if event.type == pygame.QUIT:
15
+ return False
16
+ return True
17
+
18
+ def update_snake_direction(snake, path):
19
+ ''' Extract moves from the path and update snake's direction '''
20
+ if path:
21
+ next_x, next_y = path[0], path[1]
22
+ if next_x > 0 and snake.pos != [-1, 0]:
23
+ snake.pos = [1, 0]
24
+ elif next_x < 0 and snake.pos != [1, 0]:
25
+ snake.pos = [-1, 0]
26
+ elif next_y > 0 and snake.pos != [0, 1]:
27
+ snake.pos = [0, 1]
28
+ elif next_y < 0 and snake.pos != [0, -1]:
29
+ snake.pos = [0, -1]
30
+
31
+ # Remove the current move pair from the path
32
+ path = path[2:]
33
+ return path
34
+
35
+
36
+ def main():
37
+ pygame.init()
38
+ running = True
39
+ clock = pygame.time.Clock()
40
+ font = pygame.font.SysFont('timesnewroman', BLOCK_SIZE * 2)
41
+ score = font.render("1", True, "white")
42
+ score_rect = score.get_rect(center = (WIDTH / 2, HEIGHT / 20)) # Position score at the top center
43
+
44
+ drawGrid()
45
+
46
+ snake = Snake()
47
+ apple = Apple()
48
+
49
+ while running:
50
+ running = handle_events()
51
+
52
+ path = BFS(snake, apple, snake.pos)
53
+ path = update_snake_direction(snake, path)
54
+
55
+ snake.drawSnake()
56
+ SCREEN.fill(SURFACE_CLR)
57
+ drawGrid()
58
+ apple.checkSpawn(snake.body)
59
+ apple.drawApple()
60
+
61
+ score = font.render(f"{len(snake.body)}", True, GRID_CLR)
62
+
63
+ pygame.draw.rect(SCREEN, SNAKE_CLR, snake.head)
64
+ for square in snake.body:
65
+ pygame.draw.rect(SCREEN, SNAKE_CLR, square)
66
+
67
+ SCREEN.blit(score, score_rect)
68
+
69
+ # Grow the snake
70
+ if len(snake.body) < 1:
71
+ if(snake.head.x == apple.x and snake.head.y == apple.y):
72
+ snake.body.append(pygame.Rect(snake.x, snake.y, BLOCK_SIZE, BLOCK_SIZE))
73
+ else:
74
+ # Differentiates apple from the snake's body
75
+ if(snake.head.x == apple.x and snake.head.y == apple.y):
76
+ snake.body.append(pygame.Rect(square.x, square.y, BLOCK_SIZE, BLOCK_SIZE))
77
+
78
+
79
+ pygame.display.update()
80
+ clock.tick(FPS)
81
+
82
+ pygame.quit()
83
+
84
+ if __name__ == "__main__":
85
+ main()
snake-pathfinding-AI/pathfinding.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from settings import *
2
+ from queue import Queue
3
+
4
+ def get_neighbours(position):
5
+ '''
6
+ Get neighbouring positions from the current position
7
+
8
+ Args:
9
+ - position: Tuple representing the current position (x, y)
10
+ Returns:
11
+ - List of neighbouring positions
12
+ '''
13
+
14
+ neighbours = [[position[0] + BLOCK_SIZE, position[1]],
15
+ [position[0] - BLOCK_SIZE, position[1]],
16
+ [position[0], position[1] + BLOCK_SIZE],
17
+ [position[0], position[1] - BLOCK_SIZE]]
18
+
19
+ neighbours_within_grid = []
20
+ for pos in neighbours:
21
+ if pos in GRID:
22
+ neighbours_within_grid.append(pos)
23
+
24
+ return neighbours_within_grid
25
+
26
+ def BFS(snake, apple, current_direction):
27
+ '''
28
+ Perform Breadth-First Search (BFS) to find the shortest path from the snake's head to the apple
29
+
30
+ Args:
31
+ - snake: Snake object
32
+ - apple: Apple object
33
+ - current direction: Tuple representing current direction of the snake (x, y)
34
+ Returns:
35
+ - List representing the sequence of moves to the apple'''
36
+
37
+ queue = Queue()
38
+ head_pos = (snake.head.x, snake.head.y)
39
+ apple_pos = (apple.apple.x, apple.apple.y)
40
+ queue.put((head_pos, [])) # Initialize queue with snake's head position and empty path
41
+
42
+ visited = set()
43
+ visited.add(head_pos)
44
+
45
+ while not queue.empty():
46
+ current_pos, path = queue.get()
47
+
48
+ # Return path if apple is found
49
+ if current_pos == apple_pos:
50
+ return path # Sequence of moves towards the apple
51
+
52
+ # Explore neighbours
53
+ neighbours = get_neighbours(current_pos)
54
+ for neighbour in neighbours:
55
+ if neighbour not in snake.body:
56
+ direction = [neighbour[0] - current_pos[0], neighbour[1] - current_pos[1]]
57
+ if direction != [-current_direction[0], -current_direction[1]]:
58
+ if tuple(neighbour) not in visited:
59
+ visited.add(tuple(neighbour))
60
+ new_path = path + direction
61
+ queue.put((tuple(neighbour), new_path))
62
+ return [] # If no path is found
snake-pathfinding-AI/settings.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pygame
2
+
3
+ WIDTH = 800
4
+ HEIGHT = 800
5
+ BLOCK_SIZE = 50
6
+ ROWS = WIDTH // BLOCK_SIZE
7
+ GAP_SIZE = 2
8
+
9
+ # Set up display
10
+ SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
11
+ pygame.display.set_caption("Snake")
12
+
13
+ SURFACE_CLR = (0, 0, 0)
14
+ GRID_CLR = (255, 255, 255)
15
+ SNAKE_CLR = (0, 255, 0)
16
+ APPLE_CLR = (255, 0, 0)
17
+
18
+ FPS = 10
19
+ INITIAL_SNAKE_LEN = 1
20
+
21
+ GRID = [[x,y] for x in range(0, WIDTH, BLOCK_SIZE) for y in range(0, HEIGHT, BLOCK_SIZE)]
snake-pathfinding-AI/snake.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from settings import *
3
+
4
+ class Snake:
5
+ def __init__(self):
6
+ # Initialize the snake at the center of the screen
7
+ self.x, self.y = 0, 0
8
+ self.pos = [1, 0] # [x, y] direction, initially set to right
9
+ self.head = pygame.Rect(self.x, self.y, BLOCK_SIZE, BLOCK_SIZE)
10
+ self.body = [] # List to keep track of snake's body segments
11
+ self.dead = False
12
+
13
+ def drawSnake(self):
14
+ # Check if snake has gone out of bounds
15
+ if self.head.x not in range(0, WIDTH) or self.head.y not in range(0, HEIGHT):
16
+ self.dead = True
17
+ print('game over')
18
+ # Check if snake has hit itself
19
+ for square in self.body:
20
+ if self.head.x == square.x and self.head.y == square.y:
21
+ self.dead = True
22
+ print('game over')
23
+
24
+ self.body.append(self.head)
25
+
26
+ # Move body segments to follow head
27
+ for i in range(len(self.body) - 1):
28
+ self.body[i].x, self.body[i].y = self.body[i + 1].x, self.body[i + 1].y
29
+
30
+ # Update head position based on current direction
31
+ self.head.x += self.pos[0] * BLOCK_SIZE
32
+ self.head.y += self.pos[1] * BLOCK_SIZE
33
+ self.body.remove(self.head)
34
+
35
+ class Apple:
36
+ def __init__(self):
37
+ self.spawn()
38
+
39
+ def spawn(self):
40
+ ''' Spawn a new apple ont he screen at a random position '''
41
+ # Randomly spawn apple
42
+ self.x = random.randrange(0, WIDTH, BLOCK_SIZE)
43
+ self.y = random.randrange(0, HEIGHT, BLOCK_SIZE)
44
+ self.apple = pygame.Rect(self.x, self.y, BLOCK_SIZE, BLOCK_SIZE)
45
+
46
+ def drawApple(self):
47
+ pygame.draw.rect(SCREEN, APPLE_CLR, self.apple)
48
+
49
+ def checkSpawn(self, snake: list):
50
+ ''' Check if the apple collides with the snake. If so, respawn the apple. '''
51
+ for square in snake:
52
+ if self.apple.colliderect(square):
53
+ self.spawn() # Respawn apple if it collides with snake
snake-pathfinding-AI/test_snake.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ import pygame
3
+ from snake import Snake, Apple # Make sure the Snake and Apple classes are imported correctly
4
+ from settings import *
5
+
6
+ class TestSnakeGame(unittest.TestCase):
7
+
8
+ def setUp(self):
9
+ pygame.init()
10
+ self.snake = Snake()
11
+ self.apple = Apple()
12
+
13
+ def tearDown(self):
14
+ pygame.quit()
15
+
16
+ def test_initial_state(self):
17
+ # Test initial snake position
18
+ self.assertEqual(self.snake.head.x, 0)
19
+ self.assertEqual(self.snake.head.y, 0)
20
+ # Test initial snake length
21
+ self.assertEqual(len(self.snake.body), 0)
22
+
23
+ def test_movement(self):
24
+ # Test snake movement to the right
25
+ self.snake.pos = [1, 0]
26
+ self.snake.drawSnake()
27
+ self.assertEqual(self.snake.head.x, BLOCK_SIZE)
28
+ self.assertEqual(self.snake.head.y, 0)
29
+
30
+ # Test snake movement left
31
+ self.snake.pos = [-1, 0]
32
+ self.snake.drawSnake()
33
+ self.assertEqual(self.snake.head.x, 0)
34
+ self.assertEqual(self.snake.head.y, 0)
35
+
36
+ # Test snake movement downwards
37
+ self.snake.pos = [0, -1]
38
+ self.snake.drawSnake()
39
+ self.assertEqual(self.snake.head.x, 0)
40
+ self.assertEqual(self.snake.head.y, -50)
41
+
42
+ # Test snake movement upwards
43
+ self.snake.pos = [0, 1]
44
+ self.snake.drawSnake()
45
+ self.assertEqual(self.snake.head.x, 0)
46
+ self.assertEqual(self.snake.head.y, 0)
47
+
48
+ def test_apple_spawn(self):
49
+ # Test apple spawning within the grid
50
+ self.apple.spawn()
51
+ self.assertTrue(0 <= self.apple.apple.x < WIDTH)
52
+ self.assertTrue(0 <= self.apple.apple.y < HEIGHT)
53
+ self.assertEqual(self.apple.apple.width, BLOCK_SIZE)
54
+ self.assertEqual(self.apple.apple.height, BLOCK_SIZE)
55
+
56
+ def test_eating_apple(self):
57
+ # Place apple directly in front of the snake
58
+ self.apple.apple.x = self.snake.head.x + BLOCK_SIZE
59
+ self.apple.apple.y = self.snake.head.y
60
+ self.snake.pos = [1, 0]
61
+ self.snake.drawSnake()
62
+
63
+ # Simulate snake eating the apple
64
+ if self.snake.head.colliderect(self.apple.apple):
65
+ self.snake.body.append(pygame.Rect(self.snake.head.x, self.snake.head.y, BLOCK_SIZE, BLOCK_SIZE))
66
+ self.apple.spawn()
67
+
68
+ self.assertEqual(len(self.snake.body), 1)
69
+ self.assertNotEqual((self.apple.apple.x, self.apple.apple.y), (self.snake.head.x, self.snake.head.y))
70
+
71
+ def test_collision_with_wall(self):
72
+ # Move snake to the right until it hits the wall
73
+ self.snake.pos = [-1, 0]
74
+ self.snake.drawSnake()
75
+ self.snake.pos = [-1, 0]
76
+ self.snake.drawSnake()
77
+ self.assertTrue(self.snake.dead)
78
+
79
+ def test_collision_with_self(self):
80
+ # Create a situation where the snake will collide with itself
81
+ self.snake.body = [
82
+ pygame.Rect(WIDTH / 2, HEIGHT / 2, BLOCK_SIZE, BLOCK_SIZE),
83
+ pygame.Rect(WIDTH / 2 - BLOCK_SIZE, HEIGHT / 2, BLOCK_SIZE, BLOCK_SIZE),
84
+ pygame.Rect(WIDTH / 2 - 2 * BLOCK_SIZE, HEIGHT / 2, BLOCK_SIZE, BLOCK_SIZE)
85
+ ]
86
+ self.snake.pos = [1, 0]
87
+ self.snake.drawSnake() # Move right
88
+ self.snake.pos = [0, -1]
89
+ self.snake.drawSnake() # Move up
90
+ self.snake.pos = [-1, 0]
91
+ self.snake.drawSnake() # Move left (collide with body)
92
+ self.assertTrue(self.snake.dead)
93
+
94
+ if __name__ == '__main__':
95
+ unittest.main()