Pygame VIII: Inteligencia artificial
En el siguiente tema no introduciremos ningún concepto nuevo de Pygame, pero si usaremos un poco de todo lo usado en los anteriores tutoriales para crear la pala que controlará el ordenador, se podría decir que es una Inteligencia Artificial muy básica.
Creando la pala de la CPU
Esto se hace igual que como creamos la pala_jug o la bola, ponemos la siguiente línea en la función principal debajo de pala_jug.
pala_cpu = Pala(WIDTH - 30)
En su momento pala_jug le pasamos como parámetro el valor 30 que quería decir que el centerx estaba a 30 píxeles de el borde izquierdo, ahora le pasamos WIDTH – 30, es decir a 30 píxeles del borde derecho.
También debemos añadir la línea:
screen.blit(pala_cpu.image, pala_cpu.rect)
En el bucle principal, debajo de los demas blit, como siempre. Con esto ya tenemos nuestro Sprite puesto en pantalla.
Ahora vamos a hacer que detecte también las colisiones, para ello volvemos a actualizar el método actualizar de la clase Bola:
def actualizar(self, time, pala_jug, pala_cpu): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left <= 0 or self.rect.right >= WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top <= 0 or self.rect.bottom >= HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time if pygame.sprite.collide_rect(self, pala_jug): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if pygame.sprite.collide_rect(self, pala_cpu): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time
Como podemos ver añadimos otro párametro al método pala_cpu que servirá para añadirle el Sprite pala_cpu como hicimos con pala_jug y añadimos las tres últimas líneas que son idénticas a las anteriores salvo que ahora para pala_cpu.
Recordad pasarle el parametro nuevo a la línea que llama a la función actualizar de la bola en el bucle del juego. Así:
ball.update(time, pala_jug, pala_cpu)
Dotando de Inteligencia Artificial a la Pala
Bueno ahora viene lo interesante, lograr que la pala se mueva para golpear la bola, esto se hace definiendo otro método en la clase Pala al método lo llamaré ia.
def ia(self, time, ball): if ball.speed[0] >= 0 and ball.rect.centerx >= WIDTH/2: if self.rect.centery < ball.rect.centery: self.rect.centery += self.speed * time if self.rect.centery > ball.rect.centery: self.rect.centery -= self.speed * time
En la línea 1 vemos que recibe como siempre self y time y aparte recibe ball que es la bola, es necesario pues el método necesita conocer donde está la bola.
En la línea 2 comprobamos que ball.speed[0] >= 0, es decir, que la velocidad en el eje x de la pelota sea positiva, es decir, que la pelota se este moviendo hacia la derecha (hacia la pala de la cpu) y tambien comprueba que ball.rect.centerx >= WIDTH/2 es decir que el centro x de la pelota sea mayor o igual que el centro del tablero, es decir, que la pelota este en el campo de la cpu.
Por tanto la línea 2 es un condicional que comprueba que la pelota vaya hacia donde está la pala de la cpu y que este en su campo, sino, que no se mueva. Esto se hace para que la CPU no sea invencible y no llegue a todas las pelotas.
La línea 3 comprueba si el centery de la pelota es menor que el centery de la bola, es decir si la pala está más arriba que que la pelota en cullo caso, ejecuta la línea 4 que mueve la pala de la cpu hacia abajo.
Las líneas 5 y 6 hacen lo mismo, pero a la inversa como se ve a simple vista.
Ahora solo nos queda llamar a la ia junto con las llamadas actualizar de la bola y mover de la pala_cpu.
pala_cpu.ia(time, bola)
El código nos queda de la siguiente manera:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Módulos
import sys, pygame
from pygame.locals import *
# Constantes
WIDTH = 640
HEIGHT = 480
# Clases
# ---------------------------------------------------------------------
class Bola(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = load_image("images/ball.png", True)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.centery = HEIGHT / 2
self.speed = [0.5, -0.5]
def actualizar(self, time, pala_jug, pala_cpu):
self.rect.centerx += self.speed[0] * time
self.rect.centery += self.speed[1] * time
if self.rect.left <= 0 or self.rect.right >= WIDTH:
self.speed[0] = -self.speed[0]
self.rect.centerx += self.speed[0] * time
if self.rect.top <= 0 or self.rect.bottom >= HEIGHT:
self.speed[1] = -self.speed[1]
self.rect.centery += self.speed[1] * time
if pygame.sprite.collide_rect(self, pala_jug):
self.speed[0] = -self.speed[0]
self.rect.centerx += self.speed[0] * time
if pygame.sprite.collide_rect(self, pala_cpu):
self.speed[0] = -self.speed[0]
self.rect.centerx += self.speed[0] * time
class Pala(pygame.sprite.Sprite):
def __init__(self, x):
pygame.sprite.Sprite.__init__(self)
self.image = load_image("images/pala.png")
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = HEIGHT / 2
self.speed = 0.5
def mover(self, time, keys):
if self.rect.top >= 0:
if keys[K_UP]:
self.rect.centery -= self.speed * time
if self.rect.bottom <= HEIGHT:
if keys[K_DOWN]:
self.rect.centery += self.speed * time
def ia(self, time, ball):
if ball.speed[0] >= 0 and ball.rect.centerx >= WIDTH/2:
if self.rect.centery < ball.rect.centery:
self.rect.centery += self.speed * time
if self.rect.centery > ball.rect.centery:
self.rect.centery -= self.speed * time
# ---------------------------------------------------------------------
# Funciones
# ---------------------------------------------------------------------
def load_image(filename, transparent=False):
try: image = pygame.image.load(filename)
except pygame.error, message:
raise SystemExit, message
image = image.convert()
if transparent:
color = image.get_at((0,0))
image.set_colorkey(color, RLEACCEL)
return image
# ---------------------------------------------------------------------
def main():
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pruebas Pygame")
background_image = load_image('images/fondo_pong.png')
bola = Bola()
pala_jug = Pala(30)
pala_cpu = Pala(WIDTH - 30)
clock = pygame.time.Clock()
while True:
time = clock.tick(60)
keys = pygame.key.get_pressed()
for eventos in pygame.event.get():
if eventos.type == QUIT:
sys.exit(0)
bola.actualizar(time, pala_jug, pala_cpu)
pala_jug.mover(time, keys)
pala_cpu.ia(time, bola)
screen.blit(background_image, (0, 0))
screen.blit(bola.image, bola.rect)
screen.blit(pala_jug.image, pala_jug.rect)
screen.blit(pala_cpu.image, pala_cpu.rect)
pygame.display.flip()
return 0
if __name__ == '__main__':
pygame.init()
main()
Ahora el juego es jugable, pero no tiene ni sistema de puntuación, ni sonidos ni nada de nada. Lo solucionaremos en los próximos tutoriales.
15 Comentarios
Estaba buscando información sobre agentes en python y topé con tu artículo.
No se debe mezclar el concepto de inteligencia (artificial) con el de comportamiento (complejo).
http://es.wikipedia.org/wiki/Inteligencia_artificial
http://es.wikipedia.org/wiki/Comportamiento
Josep Valls, ciertamente esto no es IA, pero como es artículo va dedicado a la creación de videojuegos no era cuestión de entrar en materia.
Aunque en realidad no está bien claro lo que se entiende por inteligencia artificial, te dejo un artículo que escribí hace tiempo con hasta 8 definiciones del concepto IA.
http://razonartificial.com/2010/01/que-es-inteligencia-artificial/
No se si te lo habran dicho en otros post…pero imagenes del juego, en cada uno de los apartados, segun lo vas haciendo seria bastante interesante, porque vas viendo la creacion del juego poco a poco. Ademas, creo que ganarias muchisimas mas visitas y nuevos desarrolladores.
Paco.
PD: Una imagen vale mas que mil palabras…
Paco, le tendré en cuenta, pero como de verdad se aprende es probando tu el código, modificándolo y experimentando por ti mismo. Es por eso que no considero necesario poner capturas.
Además al final de cada tutorial pongo todo el código para que con solo ejecutarlo lo veas funcionar.
Muchas gracias por el tutorial!! No por el idioma ya que la verdad es que me he leído ya muchos en inglés (aunque ayuda que este en español) sino por las explicaciones de las cosas. Por ejemplo, en ningun tutorial en inglés de los que me he leído he encontrado la explicación de qué era self.rect.image y en la documentación de pygame tampoco quedaba bastante claro (http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Sprite).
En cambio tu, aunque brevemente, haces una sencilla explicación de lo que es y para lo que sirve y qué parametros más se pueden encontrar cosa que me ha permitido más fluidez para modificar cosas sin tener que copiarlas de otros códigos.
Por cierto, donde podría encontrar algo más de información sobre los rects?
Pau, en la documentación traducida de Pygame que hizo loserjuegos hay bastante sobre los rects. Te dejo un enlace: http://www.losersjuegos.com.ar/traducciones/pygame/rect
De todas maneras cualquier duda sobre ellos comenta y tratare de resolverla.
Una cosa, el juego es jugable pero, ¿es ganable? Me refiero, es imposible que la CPU se equivoque. ¿Cómo ganar el juego?
Olimpiodoro, Hay varias técnicas para bajar la dificultad, haciendo que la reacción de la pala sea por ejemplo cuando este casi llegando a su lado, o simplemente disminuyendo la velocidad de su pala o subiendo la de la pelota.
Hay varias cosas para hacer una I.A. mucho más realista, pero no entran en este tutorial en el que se pretende enseñar la base de Pygame y no técnicas de I.A.
Para ahorrar un par de líneas de código te dejo mi función de actualizar
def actualizar(self, time, colisiones): self.rect.centerx += self.speed[0] * time self.rect.centery += self.speed[1] * time if self.rect.left = WIDTH: self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * time if self.rect.top = HEIGHT: self.speed[1] = -self.speed[1] self.rect.centery += self.speed[1] * time for elemento in colisiones: if pygame.sprite.collide_rect(self, elemento): self.speed[0] = -self.speed[0] self.rect.centerx += self.speed[0] * timeluego las colisiones las agregamos en bola:
y podemos añadir colisiones a veintemil y un elementos:
¿¿pero una pregunta puedo crear colisiones con límites que yo defina y que no se dibujen en la pantalla??.
PD: El mejor tutorial de pygame que he encontrado. Muy
nota sobre el comentario anterior.
La indentación no me la tomó. deberán imaginarla
Ya está arreglado tu comentario, utiliza las etiquetas ["python]["/python"] (sin comillas) para poner código.
En cuanto a la pregunta no entiendo bien, ¿te refieres a los rects, a que no sean cuadrados?
Gracias por la aclaración.
Respecto de la pregunta, me explico. Si puedo definir un cuadrado de cuatro puntos arbitrarios ej.: (20,20)(20,40)(40,40)(40,20)
Y crear una colisión con esta figura. O dicho de otro modo, que colisione con rects definidos arbitrariamente y no tomados de un archivo.
Tú el rect asociado a un sprite puedes definirlo del tamaño que quieres. Puedes acceder a sus variables con rect.x, rect.left, rect.center, etc. Cada una modifica las anteriores.
Adrigm, el método de la clase pala de la ia, todos los balls no habrían de ser traducidos por bola ( vi que ya lo comentabas en otro post del tutorial). Supongo que ese metodo no funcionaria si no se traduce no?
Puedes traducirlo perfectamente a bola, como ya he comentado el juego inicialmente lo tenía en inglés y traduje las variables cuando hice el tutorial, pero algunas se me pasaron.