如何通过寻路游戏避免即时“游戏结束”?

时间:2018-06-15 06:38:45

标签: python-3.x pygame

我一直在努力处理我在网络上找到的代码,并且修改了它。我的目标是通过两种方式找到通往特定点的路径,以便在特朗安游戏中使用“AI”赢得人类用户。但无论你做什么,我都发现你立即死了!经过几天的PyGame库搜索线索或试图看看会发生什么,我仍然无法弄清楚出了什么问题。

算法是这样的:

pathTarget = new Queue();
fillPath(pathTarget);
// use pathTarget to move with `move()`

它要么用A *搜索还是BFS填充它,但我认为它会瞬间遍历所有节点,尽管我找不到bug。如果你能指出我正确的方向,我会很感激吗?代码是here in a friend's repo因为我直到今天才知道github是什么。提前谢谢。

编辑:根据要求,代码也在这里:

import pygame, time, collections, random, heapq


pygame.init()
BLACK = (0, 0, 0)  # colores para usar en la ventana
P1_COLOUR = (0, 255, 255)  # color de traza del jugador 1
P2_COLOUR = (255, 0, 255)  # color de traza del jugador 2
moves = {'up':(0, -2), 'down':(0, 2), 'left':(0, -2), 'right':(0, 2)} # diccionario utilitario

# distancia manhattan entre dos nodos
def manhattan(nodeL, nodeR):
    return abs(nodeL[0] - nodeR[0]) + abs(nodeL[1] - nodeR[1])

class Queue: # Clase para acceder facilmente a collections.deque
    def __init__(self):
        self.elements = collections.deque() # nuevo deque (double ended queue)

    def empty(self):
        return len(self.elements) == 0 # checar si está vacío

    def push(self, x): # encolar elemento x
        self.elements.append(x)

    def pop(self): # remover y devolver el elemento en el frente
        return self.elements.popleft()

    def last(self): # ver el último nodo en la fila
        node = self.elements.pop() # lo sacamos
        self.elements.append(node) # lo metemos de vuelta
        return node # lo devolvemos

# Clase Cola de Prioridad (usa heap de Python)
class PriorityQueue:
    # Constructor
    def __init__(self):
        self.elements = []

    # Checa si esta vacía
    def empty(self):
        return len(self.elements) == 0

    # Inserta elemento item con prioridad priority
    def push(self, item, priority):
        heapq.heappush(self.elements, (priority, item))

    # regresa el elemento prioritario
    def pop(self):
        return heapq.heappop(self.elements)[1]

# Clase jugador
class Player:
    # Constructor
    def __init__(self, direction, isHuman=False):
        self.direction = direction # direccion como una tupla
        self.isHuman = isHuman # si es humano o AI
        if self.isHuman: # Si es humano lo construimos diferente
            self.x = 50 # con cierta posición
            self.y = height // 2
            self.colour = P1_COLOUR # Y cierto color
        else: # de otro modo es IA y va en otro lado
            self.x = width - 50
            self.y = height // 2
            self.colour = P2_COLOUR # Con otro color
            self.xTarget = 50 # además de valores especiales para seguir su objetivo y crear un camino
            self.yTarget = height // 2
            self.pathTarget = Queue() # como el camino a seguir para derrotar al humano
            self.difficulty = 2 # random.randint(1, 2) # dificultad a usar
        self.speed = 1  # velocidad del jugador
        self.boost = False  # si tiene nitro
        self.start_boost = time.time()  # para controlar la medida del nitro
        self.boosts = 3 # cantidad de nitros
        self.rect = pygame.Rect(self.x - 1, self.y - 1, 2, 2)  # rectangulo del jugador

    # metodo para dibujar
    def __draw__(self):
        self.rect = pygame.Rect(self.x - 1, self.y - 1, 2, 2)  # redefine el rectangulo
        pygame.draw.rect(screen, self.colour, self.rect, 0)  # dibuja al jugador en la pantalla

    # actualizar objetivo y camino, IA nada mas
    def updateTarget(self):
        self.xTarget, self.yTarget = p2.x, p2.y # actualiza los objetivos
        # Este punto se supone que es un buen punto para "matar" al enemigo
        if p2.direction == moves['left']:
            self.yTarget -= 2
        elif p2.direction == moves['right']:
            self.yTarget += 2
        elif p2.direction == moves['up']:
            self.xTarget += 2
        elif p2.direction == moves['down']:
            self.xTarget -= 2
        # Uso de la IA buscando caminos
        if not self.pathTarget.empty():
            self.pathTarget.push((self.xTarget, self.yTarget))
        else:
            if self.difficulty == 1:
                self.bfs()
            else:
                self.aSearch()

    # metodo para revisar la direccion de un nodo respecto a otro
    def checkDir(self, target):
        if self.x < target[0]:
            return moves['right']
        if self.x > target[0]:
            return moves['left']
        if self.y < target[1]:
            return moves['down']
        if self.y > target[1]:
            return moves['up']

    # metodo mover para evitar el privado '__move__', como una 'API' para la IA
    def move(self):
        if self.isHuman: # Si es humano que siga normal
            past = (self.x, self.y) 
            self.__move__() # nos movemos normal
            usedBoard[past[0]][past[1]] = True
        else: # si es IA
            if not self.pathTarget.empty(): # mientras haya direcciones a seguir en el camino
                self.direction = self.checkDir(self.pathTarget.pop()) # sigamoslas

            self.updateTarget() # actualiza las direcciones y la cola
            # importante! primero calculamos la direccion y luego marcamos el nodo como usado
            past = (self.x, self.y) 
            self.__move__() # nos movemos normal
            usedBoard[past[0]][past[1]] = True

    # Movimiento
    def __move__(self):
        if not self.boost:  # Si no hay nitro
            self.x += self.direction[0]
            self.y += self.direction[1]
        else: # si lo hay
            self.x += self.direction[0] * 2
            self.y += self.direction[1] * 2

    # Nitro
    def __boost__(self): 
        if self.boosts > 0: # si tenemos nitro
            self.boosts -= 1 # quitamos
            self.boost = True # activamos
            self.start_boost = time.time() # y a correr! (0.5s)

    # calcular los vecinos de un nodo dado un tablero prueba
    def computeNeighbors(self, coords, testBoard):
        neighbors = [] # vacía por las dudas
        # Si x > 0 y está vacío el tablero, vamos a la izquierda
        if coords[0] > 1:
            if not usedBoard[coords[0] - 2][coords[1]] and not testBoard[coords[0] - 2][coords[1]]:
                neighbors.append( (coords[0] - 2, coords[1]) )
        # Si y > 62 y está vacío el tablero, vamos arriba
        if coords[1] > 1:
            if not usedBoard[coords[0]][coords[1] - 2] and not testBoard[coords[0]][coords[1] - 2]:
                neighbors.append( (coords[0], coords[1] - 2) )
        # Si x < width y está vacío el tablero, vamos a la derecha
        if coords[0] < width - 4:
            if not usedBoard[coords[0] + 2][coords[1]] and not testBoard[coords[0] + 2][coords[1]]:
                neighbors.append( (coords[0] + 2, coords[1]) )
        # Si y < height y está vacío el tablero, vamos abajo
        if coords[1] < height - 4 - offset:
            if not usedBoard[coords[0]][coords[1] + 2] and not testBoard[coords[0]][coords[1] + 2]:
                neighbors.append( (coords[0], coords[1] + 2) )
        return neighbors

    # Busqueda de camino con A*
    def aSearch(self):
        testBoard = usedBoard # tablero de prueba para inundar
        neighbors = PriorityQueue() # Creamos un heap o cola de prioridad
        neighbors.push((self.x, self.y), 0) # metemos nuestro nodo inicial
        pathGenerated = {} # Camino creado
        costUpTo = {} # Costo generado
        pathGenerated[(self.x, self.y)] = None # Inicializamos el camino
        costUpTo[(self.x, self.y)] = 0 # y los costos
        while not neighbors.empty(): # Mientras haya vecinos
            current = neighbors.pop() # obtenemos el mejor vecino
            testBoard[current[0]][current[1]] = True # lo marcamos usado
            if current == (self.xTarget, self.yTarget): # si es el objetivo nos salimos
                break
            frontier = self.computeNeighbors(current, testBoard) # Si no, le sacamos los vecinos
            if frontier: # si no está vacía
                for node in frontier: # Y para cada uno
                    newCost = costUpTo[current] + 1 # Costo de viajar al siguiente es 1
                    if node not in costUpTo or newCost < costUpTo[node]: # si el nodo no ha sido visitado o el costo es menor
                        costUpTo[node] = newCost # vamos a recorrerlo y añadirlo
                        priority = newCost + manhattan(node, (self.xTarget, self.yTarget)) # creamos su prioridad
                        neighbors.push(node, priority) # lo metemos en el heap
                        pathGenerated[node] = current # y en el camino

        for key in pathGenerated: # metemos el camino
            self.pathTarget.push(key) # para que lo visitemos


    # Busqueda de camino con BFS
    def bfs(self): # calcula el camino a seguir
        testBoard = usedBoard # creamos el tablero de prueba para inundar
        neighbors = Queue() # cola de nodos a visitar
        neighbors.push((self.x, self.y)) # metemos el nodo actual
        while not neighbors.empty(): # mientras haya nodos por inundar
            current = neighbors.pop() # obtenemos el nodo de la cola
            self.pathTarget.push(current)
            # Si llegamos al objetivo, terminar y salir
            if current[0] == self.xTarget and current[1] == self.yTarget:
                break
            testBoard[current[0]][current[1]] = True # Marcar como usado
            frontier = self.computeNeighbors(current, testBoard) # Obtener vecinos del nodo actual
            if frontier: # actualizar la cola
                for node in frontier:
                    neighbors.push(node)

# para crear un nuevo juego
def new_game():
    new_p1 = Player((-2, 0)) # IA de dificultad aleatoria
    new_p2 = Player((2, 0), True) # Jugador
    return new_p1, new_p2


width, height = 600, 660  # dimensiones de la ventana
offset = height - width  # espacio vertical extra
usedBoard = [[False] * width for _ in range(height-offset)] # para uso con la BFS y la A*
screen = pygame.display.set_mode((width, height))  # crea la ventana
pygame.display.set_caption("Tron")  # coloca el titulo del juego

font = pygame.font.Font(None, 72) # cambiamos el tamaño de la fuente

clock = pygame.time.Clock()  # para regular los FPS
check_time = time.time()  # para revisar colisiones en tiempo y forma

objects = list()  # lista de todosl os jugadores
path = list()  # lista de todos los caminos ya recorridos
p1 = Player((-2, 0))  # crea IA
p2 = Player((2, 0), True) # crea al jugador
objects.append(p1) # metemos la IA
path.append((p1.rect, '1')) # metemos su objeto en el camino
objects.append(p2) # ahora ocn el jugador
path.append((p2.rect, '2'))

player_score = [0, 0]  # puntuaciones

wall_rects = [pygame.Rect([0, offset, 15, height]) , pygame.Rect([0, offset, width, 15]),\
            pygame.Rect([width - 15, offset, 15, height]),\
            pygame.Rect([0, height - 15, width, 15])]  # los muros de fuera de la ventana
# variables de uso
done = False
new = False

while not done:
    for event in pygame.event.get():  # obtener todos los eventos ocurridos
        if event.type == pygame.QUIT:  # cerrar el juego
            done = True
        elif event.type == pygame.KEYDOWN:  # tecla presionada
            # Movimientos del jugador
            # >Flechas para moverse
            # >Nitro con shift derecho

            # Esta parte se puede activar para 2 jugadores, solo se descomenta y juega con WASD y TAB
            # if event.key == pygame.K_w and objects[0].direction != (0, 2):
            #     objects[0].direction = (0, -2)
            # elif event.key == pygame.K_s and objects[0].direction != (0, -2):
            #     objects[0].direction = (0, 2)
            # elif event.key == pygame.K_a and objects[0].direction != (2, 0):
            #     objects[0].direction = (-2, 0)
            # elif event.key == pygame.K_d and objects[0].direction != (-2, 0):
            #     objects[0].direction = (2, 0)
            # elif event.key == pygame.K_TAB:
            #     objects[0].__boost__()

            # Aquí comienzan los movimientos del jugador
            if event.key == pygame.K_UP and objects[1].direction != (0, 2):
                objects[1].direction = (0, -2)
            elif event.key == pygame.K_DOWN and objects[1].direction != (0, -2):
                objects[1].direction = (0, 2)
            elif event.key == pygame.K_LEFT and objects[1].direction != (2, 0):
                objects[1].direction = (-2, 0)
            elif event.key == pygame.K_RIGHT and objects[1].direction != (-2, 0):
                objects[1].direction = (2, 0)
            elif event.key == pygame.K_RSHIFT:
                objects[1].__boost__()

    screen.fill(BLACK)  # limpia la pantalla

    for r in wall_rects: # para cada rectangulo
        pygame.draw.rect(screen, (42, 42, 42), r, 0)  # dibuja los muros

    for obj in objects:
        if time.time() - obj.start_boost >= 0.5:  # limita el nitro a 0.5s
            obj.boost = False

        # Revisar colisiones
        if (obj.rect, '1') in path or (obj.rect, '2') in path or obj.rect.collidelist(wall_rects) > -1:  # choco con el camino o muro
            # previene chocar consigo mismo
            if (time.time() - check_time) >= 0.1:
                check_time = time.time()
                #puntuaciones
                if obj.colour == P1_COLOUR:
                    player_score[1] += 1
                else:
                    player_score[0] += 1
                usedBoard = [[False] * width for _ in range(height-offset)] # para uso con la BFS y la A*
                new = True
                new_p1, new_p2 = new_game()
                objects = [new_p1, new_p2]
                path = [(p1.rect, '1'), (p2.rect, '2')]
                break
        else:  # sin atravesar
            path.append((obj.rect, '1')) if obj.colour == P1_COLOUR else path.append((obj.rect, '2'))

        obj.__draw__()
        obj.move()

    for r in path:
        if new is True:  # vacía el camino, evita glitches
            path = []
            new = False
            break
        if r[1] == '1':
            pygame.draw.rect(screen, P1_COLOUR, r[0], 0)
        else:
            pygame.draw.rect(screen, P2_COLOUR, r[0], 0)

    # muestra las puntuaciones
    score_text = font.render('{0} : {1}'.format(player_score[0], player_score[1]), 1, (255, 153, 51))
    score_text_pos = score_text.get_rect()
    score_text_pos.centerx = width // 2
    score_text_pos.centery = offset // 2
    screen.blit(score_text, score_text_pos)

    pygame.display.flip()  # voltea el display
    clock.tick(60)  # regula los FPS

pygame.quit()

0 个答案:

没有答案