updated agent navigation
This commit is contained in:
73
agent.py
73
agent.py
@@ -0,0 +1,73 @@
|
|||||||
|
import random
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from maze import Cell
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Choice:
|
||||||
|
score: int
|
||||||
|
cell: Cell
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
self.score += other
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
self.score -= other
|
||||||
|
|
||||||
|
|
||||||
|
class Agent:
|
||||||
|
def __init__(self, buffer, grid_size):
|
||||||
|
self.location = Cell(0, 0)
|
||||||
|
self.last_visited = Cell(0, 0)
|
||||||
|
self.buffer = buffer
|
||||||
|
self.grid_size = grid_size
|
||||||
|
self.first_step = True
|
||||||
|
self.auto_finish = 1
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.location = Cell(0, 0)
|
||||||
|
self.first_step = True
|
||||||
|
self.last_visited = Cell(0, 0)
|
||||||
|
|
||||||
|
def get_x_position(self):
|
||||||
|
return (self.location.x * self.grid_size) + self.buffer + self.grid_size / 1.9
|
||||||
|
|
||||||
|
def get_y_position(self):
|
||||||
|
return (self.location.y * self.grid_size) + self.buffer + self.grid_size / 1.9
|
||||||
|
|
||||||
|
def move(self, maze):
|
||||||
|
if self.first_step:
|
||||||
|
self.first_step = False
|
||||||
|
return
|
||||||
|
# Random Walk
|
||||||
|
# self.location = random.choice(maze.maze[self.location])
|
||||||
|
# Direct to end
|
||||||
|
# _, self.location = min(sorted([(maze.distance_map[x], x) for x in maze.maze[self.location]]))
|
||||||
|
# no backstep.
|
||||||
|
# self.last_visited = self.location
|
||||||
|
|
||||||
|
# Go to end when close.
|
||||||
|
if maze.distance_map[self.location] <= self.auto_finish:
|
||||||
|
_, self.location = min(sorted([(maze.distance_map[x], x) for x in maze.maze[self.location]]))
|
||||||
|
else:
|
||||||
|
choices = maze.maze[self.location].copy()
|
||||||
|
try:
|
||||||
|
choices.remove(self.last_visited)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not choices:
|
||||||
|
self.last_visited = self.location
|
||||||
|
self.location = maze.maze[self.last_visited][0]
|
||||||
|
else:
|
||||||
|
if smart_choices := set(choices).difference(maze.dead_ends):
|
||||||
|
|
||||||
|
self.last_visited = self.location
|
||||||
|
self.location = random.choice(list(smart_choices))
|
||||||
|
else:
|
||||||
|
self.last_visited = self.location
|
||||||
|
self.location = random.choice(choices)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
84
game.py
84
game.py
@@ -3,7 +3,8 @@ from itertools import product
|
|||||||
|
|
||||||
import pygame
|
import pygame
|
||||||
from pygame.locals import *
|
from pygame.locals import *
|
||||||
from maze import Maze
|
from maze import Maze, Cell
|
||||||
|
from agent import Agent
|
||||||
|
|
||||||
pygame.init()
|
pygame.init()
|
||||||
vec = pygame.math.Vector2 # 2 for two dimensional
|
vec = pygame.math.Vector2 # 2 for two dimensional
|
||||||
@@ -19,22 +20,28 @@ displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
|
|||||||
pygame.display.set_caption("Game")
|
pygame.display.set_caption("Game")
|
||||||
|
|
||||||
SCREEN_BUFFER = 10
|
SCREEN_BUFFER = 10
|
||||||
MAZE_SIZE = 15
|
MAZE_SIZE = 10
|
||||||
GRID_SIZE = 25
|
GRID_SIZE = 25
|
||||||
|
|
||||||
|
|
||||||
class Player(pygame.sprite.Sprite):
|
class VisitedSpot(pygame.sprite.Sprite):
|
||||||
def __init__(self):
|
def __init__(self, cell: Cell):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.surf = pygame.Surface((30, 30))
|
self.surf = pygame.Surface((GRID_SIZE, GRID_SIZE))
|
||||||
self.surf.fill((0, 255, 0))
|
self.surf.fill((0, 122, 000))
|
||||||
self.rect = self.surf.get_rect(center=(10, 420))
|
self.rect = self.surf.get_rect(topleft=(cell.x * GRID_SIZE + SCREEN_BUFFER, cell.y * GRID_SIZE + SCREEN_BUFFER))
|
||||||
|
|
||||||
|
class DeadEndSpot(pygame.sprite.Sprite):
|
||||||
|
def __init__(self, cell: Cell):
|
||||||
|
super().__init__()
|
||||||
|
self.surf = pygame.Surface((GRID_SIZE, GRID_SIZE))
|
||||||
|
self.surf.fill((122, 0, 000))
|
||||||
|
self.rect = self.surf.get_rect(topleft=(cell.x * GRID_SIZE + SCREEN_BUFFER, cell.y * GRID_SIZE + SCREEN_BUFFER))
|
||||||
|
|
||||||
class MazeWall(pygame.sprite.Sprite):
|
class MazeWall(pygame.sprite.Sprite):
|
||||||
def __init__(self, wall):
|
def __init__(self, wall):
|
||||||
wall_width = 2
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
wall_width = 2
|
||||||
start_cell, end_cell = wall
|
start_cell, end_cell = wall
|
||||||
x, y = start_cell
|
x, y = start_cell
|
||||||
vertical = start_cell[1] == end_cell[1]
|
vertical = start_cell[1] == end_cell[1]
|
||||||
@@ -48,17 +55,23 @@ class MazeWall(pygame.sprite.Sprite):
|
|||||||
else:
|
else:
|
||||||
self.rect = self.surf.get_rect(midleft=(x * GRID_SIZE + SCREEN_BUFFER, (y + 1) * GRID_SIZE + SCREEN_BUFFER))
|
self.rect = self.surf.get_rect(midleft=(x * GRID_SIZE + SCREEN_BUFFER, (y + 1) * GRID_SIZE + SCREEN_BUFFER))
|
||||||
|
|
||||||
|
agents = list()
|
||||||
|
agents.append(Agent(SCREEN_BUFFER, GRID_SIZE))
|
||||||
|
# agents.append(Agent(SCREEN_BUFFER, GRID_SIZE))
|
||||||
|
# agents.append(Agent(SCREEN_BUFFER, GRID_SIZE))
|
||||||
|
# agents.append(Agent(SCREEN_BUFFER, GRID_SIZE))
|
||||||
|
|
||||||
|
|
||||||
all_sprites = pygame.sprite.Group()
|
all_sprites = pygame.sprite.Group()
|
||||||
|
visited_cells_sprites = pygame.sprite.Group()
|
||||||
|
dead_end_sprites = pygame.sprite.Group()
|
||||||
maze_sprites = pygame.sprite.Group()
|
maze_sprites = pygame.sprite.Group()
|
||||||
|
|
||||||
maze = Maze(MAZE_SIZE, MAZE_SIZE)
|
MOVEEVENT = pygame.USEREVENT+1
|
||||||
|
RESETMAZE = pygame.USEREVENT+2
|
||||||
for wall in maze.walls:
|
reset_maze_event = pygame.event.Event(RESETMAZE, message="Reset Maze")
|
||||||
wall_sprite = MazeWall(wall)
|
pygame.event.post(reset_maze_event)
|
||||||
maze_sprites.add(wall_sprite)
|
pygame.time.set_timer(MOVEEVENT, 1000)
|
||||||
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@@ -66,18 +79,47 @@ while True:
|
|||||||
if event.type == QUIT:
|
if event.type == QUIT:
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
if event.type == MOVEEVENT:
|
||||||
|
for agent in agents:
|
||||||
|
agent.move(maze)
|
||||||
|
if agent.location not in maze.visited:
|
||||||
|
maze.visited.add(agent.location)
|
||||||
|
visited_cells_sprites.add(VisitedSpot(agent.location))
|
||||||
|
if agent.location == max(maze.cells):
|
||||||
|
pygame.event.post(reset_maze_event)
|
||||||
|
if maze.check_dead_end(agent.location):
|
||||||
|
dead_end_sprites.add(DeadEndSpot(agent.location))
|
||||||
|
if event.type == RESETMAZE:
|
||||||
|
for agent in agents:
|
||||||
|
agent.reset()
|
||||||
|
maze = Maze(MAZE_SIZE, MAZE_SIZE)
|
||||||
|
maze_sprites.empty()
|
||||||
|
visited_cells_sprites.empty()
|
||||||
|
dead_end_sprites.empty()
|
||||||
|
for wall in maze.walls:
|
||||||
|
wall_sprite = MazeWall(wall)
|
||||||
|
maze_sprites.add(wall_sprite)
|
||||||
# wipe buffer
|
# wipe buffer
|
||||||
displaysurface.fill((0, 0, 0))
|
displaysurface.fill((0, 0, 0))
|
||||||
# draw border
|
|
||||||
pygame.draw.rect(displaysurface, (255, 0, 0),
|
|
||||||
pygame.Rect((SCREEN_BUFFER, SCREEN_BUFFER, MAZE_SIZE * GRID_SIZE, MAZE_SIZE * GRID_SIZE)), 2)
|
|
||||||
pygame.draw.circle(displaysurface, (0, 255, 0), (25, 25), GRID_SIZE / 3)
|
|
||||||
|
|
||||||
# for entity in all_sprites:
|
from maze import Cell
|
||||||
# displaysurface.blit(entity.surf, entity.rect)
|
location = Cell(0, 5)
|
||||||
|
|
||||||
|
for entity in visited_cells_sprites:
|
||||||
|
displaysurface.blit(entity.surf, entity.rect)
|
||||||
|
|
||||||
|
for entity in dead_end_sprites:
|
||||||
|
displaysurface.blit(entity.surf, entity.rect)
|
||||||
|
|
||||||
|
for agent in agents:
|
||||||
|
pygame.draw.circle(displaysurface, (0, 255, 0), (agent.get_x_position(), agent.get_y_position()), GRID_SIZE / 3)
|
||||||
|
|
||||||
for entity in maze_sprites:
|
for entity in maze_sprites:
|
||||||
displaysurface.blit(entity.surf, entity.rect)
|
displaysurface.blit(entity.surf, entity.rect)
|
||||||
|
|
||||||
|
# draw border
|
||||||
|
pygame.draw.rect(displaysurface, (255, 0, 0),
|
||||||
|
pygame.Rect((SCREEN_BUFFER, SCREEN_BUFFER, MAZE_SIZE * GRID_SIZE, MAZE_SIZE * GRID_SIZE)), 2)
|
||||||
|
|
||||||
pygame.display.update()
|
pygame.display.update()
|
||||||
FramePerSec.tick(FPS)
|
FramePerSec.tick(FPS)
|
||||||
64
maze.py
64
maze.py
@@ -16,16 +16,51 @@ class Wall(NamedTuple):
|
|||||||
|
|
||||||
class Maze:
|
class Maze:
|
||||||
def __init__(self, width: int, height: int):
|
def __init__(self, width: int, height: int):
|
||||||
|
self.distance_map = dict()
|
||||||
self.cells = set(Cell(*c) for c in product(range(width), range(height)))
|
self.cells = set(Cell(*c) for c in product(range(width), range(height)))
|
||||||
self.maze = dict()
|
self.maze = dict()
|
||||||
for cell in self.cells:
|
for cell in self.cells:
|
||||||
self.maze[cell] = self.get_neighbours(cell)
|
self.maze[cell] = self.get_neighbours(cell)
|
||||||
self.walls = self.get_walls()
|
self.walls = self.get_walls()
|
||||||
self.generate_maze()
|
self.generate_maze()
|
||||||
|
self.remove_blocked_links()
|
||||||
|
self.build_map()
|
||||||
|
self.visited = set()
|
||||||
|
self.dead_ends = dict()
|
||||||
|
self.dead_end_limit = 5
|
||||||
|
|
||||||
def get_neighbours(self, cell: Cell) -> Set[Cell]:
|
def check_dead_end(self, cell: Cell) -> bool:
|
||||||
|
# return true is new dead end
|
||||||
|
if cell in self.dead_ends:
|
||||||
|
return False
|
||||||
|
if len(self.maze[cell]) == 1:
|
||||||
|
self.dead_ends[cell] = 0
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def build_map(self):
|
||||||
|
distance = 0
|
||||||
|
first_cell = max(self.cells)
|
||||||
|
visited = set()
|
||||||
|
visited.add(first_cell)
|
||||||
|
unvisited = self.cells.copy()
|
||||||
|
unvisited.remove(first_cell)
|
||||||
|
self.distance_map[first_cell] = 0
|
||||||
|
current_group = self.maze[first_cell]
|
||||||
|
|
||||||
|
while unvisited:
|
||||||
|
next_group = set()
|
||||||
|
distance += 1
|
||||||
|
for node in current_group:
|
||||||
|
self.distance_map[node] = distance
|
||||||
|
next_group.update(self.maze[node])
|
||||||
|
visited.add(node)
|
||||||
|
unvisited.remove(node)
|
||||||
|
current_group = next_group.difference(visited)
|
||||||
|
|
||||||
|
def get_neighbours(self, cell: Cell) -> List[Cell]:
|
||||||
diffs = ((0, 1), (0, -1), (1, 0), (-1, 0))
|
diffs = ((0, 1), (0, -1), (1, 0), (-1, 0))
|
||||||
return set(Cell(cell.x + x, cell.y + y) for x, y in diffs if Cell(cell.x + x, cell.y + y) in self.cells)
|
return list(Cell(cell.x + x, cell.y + y) for x, y in diffs if Cell(cell.x + x, cell.y + y) in self.cells)
|
||||||
|
|
||||||
def get_walls(self) -> Set[Wall]:
|
def get_walls(self) -> Set[Wall]:
|
||||||
walls = set()
|
walls = set()
|
||||||
@@ -33,6 +68,12 @@ class Maze:
|
|||||||
walls.update(set(Wall(*sorted([cell, c])) for c in self.maze[cell]))
|
walls.update(set(Wall(*sorted([cell, c])) for c in self.maze[cell]))
|
||||||
return walls
|
return walls
|
||||||
|
|
||||||
|
def remove_blocked_links(self):
|
||||||
|
for wall in self.walls:
|
||||||
|
cell1, cell2 = wall
|
||||||
|
self.maze[cell1].remove(cell2)
|
||||||
|
self.maze[cell2].remove(cell1)
|
||||||
|
|
||||||
def generate_maze(self) -> None:
|
def generate_maze(self) -> None:
|
||||||
visited = set()
|
visited = set()
|
||||||
unvisited = list(self.cells)
|
unvisited = list(self.cells)
|
||||||
@@ -50,25 +91,6 @@ class Maze:
|
|||||||
pass
|
pass
|
||||||
node = random_neighbour
|
node = random_neighbour
|
||||||
|
|
||||||
def generate_prim_maze(self) -> None:
|
|
||||||
visited = set()
|
|
||||||
node = random.choice(list(self.cells))
|
|
||||||
visited.add(node)
|
|
||||||
removed_walls = set()
|
|
||||||
wall_list = [w for w in self.walls if node in w]
|
|
||||||
while wall_list:
|
|
||||||
random_wall = random.choice(wall_list)
|
|
||||||
wall_list.remove(random_wall)
|
|
||||||
cell1, cell2 = random_wall
|
|
||||||
if (cell1 in visited) != (cell2 in visited):
|
|
||||||
removed_walls.add(random_wall)
|
|
||||||
self.walls.remove(random_wall)
|
|
||||||
visited.add(cell1)
|
|
||||||
visited.add(cell2)
|
|
||||||
wall_list.extend(
|
|
||||||
set(w for w in self.walls if (cell1 in w) or (cell2 in w)).difference(removed_walls)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
maze = Maze(3, 3)
|
maze = Maze(3, 3)
|
||||||
|
|||||||
Reference in New Issue
Block a user