Suscríbete vía RSS Síguenos en Twitter

Pygame VIII: Inteligencia artificial

adrigm | 14/02/2010 | 15 Comentarios

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.

Entradas relacionadas

15 Comentarios

1
Escrito por Josep Valls el 16 febrero 2010 a las 14:59 pm

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

2
Escrito por admin el 16 febrero 2010 a las 15:42 pm

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/

3
Escrito por Paco el 18 febrero 2010 a las 11:50 am

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…

4
Escrito por admin el 18 febrero 2010 a las 20:29 pm

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.

5
Escrito por Pau el 24 abril 2010 a las 11:40 am

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?

6
Escrito por admin el 24 abril 2010 a las 11:45 am

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.

7
Escrito por Olimpiodoro el 26 junio 2010 a las 22:54 pm

Una cosa, el juego es jugable pero, ¿es ganable? Me refiero, es imposible que la CPU se equivoque. ¿Cómo ganar el juego?

8
Escrito por admin el 27 junio 2010 a las 4:38 am

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.

9
Escrito por Andrés el 20 agosto 2010 a las 18:18 pm

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] * time

luego las colisiones las agregamos en bola:

bola.actualizar(time, [pala_jug, pala_cpu])

y podemos añadir colisiones a veintemil y un elementos:

bola.actualizar(time, [pala_jug, pala_cpu, cosa, que, se, me, ocurra, en, el, array])

¿¿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

10
Escrito por Andrés el 20 agosto 2010 a las 18:19 pm

nota sobre el comentario anterior.

La indentación no me la tomó. deberán imaginarla

11
Escrito por adrigm el 20 agosto 2010 a las 18:27 pm

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?

12
Escrito por Andrés el 22 agosto 2010 a las 19:52 pm

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.

13
Escrito por adrigm el 22 agosto 2010 a las 21:23 pm

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.

14
Escrito por Jogui el 3 septiembre 2010 a las 13:14 pm

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?

15
Escrito por adrigm el 3 septiembre 2010 a las 13:51 pm

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.

Deja un comentario

Su comentario: