Programar Juegos Arcade
con Python y PygameChapter 4: Adivinanzas con Números Aleatorios y Bucles
El último paso antes de comenzar con los gráficos, es aprender cómo iterar a través de una sección de código. La mayoría de los juegos 'iteran'. Repiten el mismo código una y otra vez. Por ejemplo, el siguiente juego de adivinar un número, itera cada vez que el usuario hace un intento:
Hey! Estoy pensando en un número al azar entre 1 y 100. ---- Intento 1 Adivina en qué número estoy pensando: 50 Demasiado alto. --- Intento 2 Adivina en qué número estoy pensando: 25 Demasiado alto. --- Intento 3 Adivina en qué número estoy pensando: 17 Muy alto. --- Attempt 4 Adivina en qué número estoy pensando: 9 Muy bajo. ---- Intento 5 Adivina en qué número estoy pensando: 14 Muy alto. ---- Intento 6 Adivina en qué número estoy pensando: 12 Muy alto. ---- Intento 7 Adivina en qué número estoy pensando: 10 Muy bajo. Ups! Te has quedado sin opciones. El número era el 11.
¿Pero espera, qué tiene que ver todo esto con gráficos y videojuegos? Pues un montón. Cada frame (fotograma) que el juego muestra, es uno, de una serie a través del bucle. Probablemente estés habituado a las estadísticas de frame-per-second (FPS) que muestran los juegos. El FPS representa el número de veces que el ordenador actualiza la pantalla por cada segundo. A mayor tasa de refresco, más fluido es el juego. De todas formas, una tasa de FPS superior a 60, es más rápida de lo que la mayoría de monitores puede actualizar, por lo que no tiene mucho sentido sobrepasar ese valor. La Figura 4.1 muestra el juego Eve Online y una gráfica donde se ve cuántos fotogramas por segundo (frames per second) es capaz de resolver el ordenador.
El bucle de estos juegos funciona como el diagrama de flujo de la Figura 4.2. A pesar de la complejidad de los modernos videojuegos, el interior de este bucle es parecido al programa calculadora que hicimos en el Capítulo 1; Obtener una entrada del usuario -> Realizar cálculos -> Expulsar un resultado. En un videojuego intentamos repetir esto hasta 60 veces por segundo.
Es muy posible que existan bucles dentro de otros bucles. Un auténtico 'rizar el rizo'. Observa el bloque 'Dibujar Todo' de la Figura 4.2. Establece el código del bucle que itera repetidamente para dibujar cada objeto del juego. Este bucle está contenido dentro de uno mayor que dibuja cada fotograma del juego, tal como el de la Figura 4.3.
Existen dos grandes clases de bucles en Python; los bucles for y los bucles while. Si lo que quieres es repetir algo cierto número de veces, utiliza un bucle for. Y si lo que quieres es repetir algo hasta que se cumpla cierta condición (por ejemplo, cuando el usuario hace click sobre el botón de cierre), entonces usa un bucle while.
Por ejemplo, podemos usar un bucle for para imprimir las notas de todos los estudiantes, ya que el ordenador sabe cuántos estudiantes hay. Sin embargo, haría falta un bucle while para comprobar cuándo el usuario pulsa sobre el botón del ratón, ya que el ordenador desconoce cuánto tiempo tiene que esperar hasta que eso suceda.
4.1 Bucles For
El siguiente ejemplo de bucle for ejecuta la función print cinco veces. Sería muy fácil ejecutarlo 100 o 1,000,000 veces con tan solo cambiar el 5 por el valor que necesitemos repetir el bucle. Observa las similitudes entre cómo escribimos el bucle for y cómo la sentencia if. Ambas finalizan con dos puntos (:), y ambas usan indentación para especificar qué líneas se ven afectadas por la sentencia.
for i in range(5): print ("No volveré a mascar chicle en clase.") |
Salida:
No volveré a mascar chicle en clase. No volveré a mascar chicle en clase. No volveré a mascar chicle en clase. No volveré a mascar chicle en clase. No volveré a mascar chicle en clase. |
La i en la línea 1 es la variable que lleva la cuenta de las veces que hemos iterado el programa. Es una variable nueva y la podemos llamar con cualquier nombre legal. Los Programadores suelen usar i como nombre de variable, ya que la 'i' abrevia la palabra incremento. Esta variable nos ayuda a saber cuándo debe parar el bucle.
La función range(rango) controla cuántas veces el código del bucle debe ejecutarse. En este caso, cinco veces.
El siguiente ejemplo imprimirá 'Por favor,' cinco veces, y '¿Puedo ir al centro comercial?' solo una. '¿Puedo ir al centro comercial?' no ha sido indentada por lo que no es parte del bucle for y no se imprimirá hasta que el bucle for finalice.
for i in range(5): print ("Por favor,") print ("¿Puedo ir al centro comercial?") |
Salida:
Por favor, Por favor, Por favor, Por favor, Por favor, ¿Puedo ir al centro comercial? |
El siguiente código toma el ejemplo anterior e indenta la línea 3. Este cambio provocará que el programa imprima 'Por favor,' y '¿Puedo ir al centro comercial?' cinco veces. Como ahora la sentencia ha sido indentada, '¿Puedo ir al centro comercial?' es ahora parte del bucle for y la repetirá cinco veces, al igual que 'Por favor,'.
for i in range(5): print ("Por favor,") print ("¿Puedo ir al centro comercial?") |
Salida:
Por favor, ¿Puedo ir al centro comercial? Por favor, ¿Puedo ir al centro comercial? Por favor, ¿Puedo ir al centro comercial? Por favor, ¿Puedo ir al centro comercial? Por favor, ¿Puedo ir al centro comercial? |
El código siguiente imprimirá los números del 0 al 9. Observa que el bucle empieza en 0 y no incluye al 10. Es natural asumir que range(10) incluiría al 10, pero se detiene justo antes de él.
for i in range(10): print(i) |
Salida:
0 1 2 3 4 5 6 7 8 9 |
Un programa no está obligado a usar una variable llamada i, podría ser cualquier otra cosa. Por ejemplo, una programadora podría usar linea_numero si ella estuviera procesando un archivo de texto.
Si un programador quisiera ir desde el 1 hasta el 10, en lugar del 0 al 9, existen un par de formas para conseguirlo. La primera es enviar a range dos números en lugar de uno solo. El primer número es el valor inicial, y el segundo sería el que está justo después del valor final.
Se necesita cierta práctica para acostumbrarse a la idea de que el bucle for incluirá al primer número pero no al segundo. El siguiente ejemplo especifica un rango de (1,11), donde se imprimen los números del 1 al 10. El número inicial 1 es incluido, pero no el final, 11.
for i in range(1,11): print(i) |
Salida:
1 2 3 4 5 6 7 8 9 10 |
Otra forma de imprimir los números del 1 al 10 es seguir usando range(10) y hacer que la variable i vaya de 0 a 9, pero justo antes de imprimirla, el programador le añada 1. Cualquiera de los dos métodos son igual de válidos.
# Imprime los números del 1 al 10. for i in range(10): print(i + 1)
4.1.1 Contando por Números Distintos al Uno
Si el programa necesita contar por 2 o cualquier otro incremento, la cosa es bastante fácil. Tal como en los ejemplos anteriores, hay dos formas de hacerlo. La más fácil es añadir un tercer número a la función range que le diga que cuente por 2. La segunda forma es hacer que cuente por uno, pero multiplicando la variable por 2. El siguiente código muestra los dos métodos.
# Dos formas de imprimir los números pares del 2 al 10 for i in range(2,12,2): print(i) for i in range(5): print ((i + 1) * 2) |
Salida:
2 4 6 8 10 2 4 6 8 10 |
También es posible hacer cuentas hacia atrás, es decir hacia cero, dándole a la función range un incremento negativo. En el siguiente ejemplo, empezamos en 10 y descendemos hacia cero, pero sin llegar a él, usando incrementos de -1. La parte más ardua al crear esta clase de bucles es que accidentalmente confundamos los números del principio y del final. El programa empieza con el número mayor, por lo que éste debe ir hacia el principio. Habitualmente, los bucles for empiezan a contar a partir del valor más pequeño que aparece dentro de la función range.
for i in range(10, 0, -1): print(i) |
Salida:
10 9 8 7 6 5 4 3 2 1 |
Si los números sobre los cuales el programa debe iterar no forman un patrón sencillo, es posible extraerlos de la lista. (Las listas se tratan con más detalle en el Capítulo 7. Esto es tan solo un avance de lo que podemos hacer.)
for i in [2,6,4,2,4,6,7,4]: print(i)
Esto imprimirá:
2 6 4 2 4 6 7 4
4.1.2 Bucles Anidados
Intenta predecir qué imprimirá el siguiente código. Luego escríbelo y observa el resultado.
# ¿Qué es lo que esto imprime? ¿Por qué? for i in range(3): print ("a") for j in range(3): print ("b")
El siguiente bloque es casi idéntico al anterior. Esta vez, el segundo bucle for ha sido indentado una vez, de forma que ahora está anidado dentro del primer bucle for. Esto cambia significativamente su comportamiento. Ejecútalo y observa la diferencia.
# ¿Qué es lo que esto imprime? ¿Por qué? for i in range(3): print("a") for j in range(3): print("b") print("Hecho")
No te voy a contar lo que este código imprime, ve a un ordenador y averígualo.
4.1.3 Mantener un Total Acumulado
Una operación común a muchos bucles es la de mantener un total acumulado en tiempo de ejecución. Este “total acumulado” es usado con frecuencia en este libro. Por ejemplo, para obtener el total de un marcador, el total de las transacciones bancarias de una persona, buscar un total para luego hallar un promedio, etc. Probablemente quieras señalar esta parte del libro ya que nos referiremos a ella muchas veces. En el ejemplo siguiente, el usuario ingresa 5 números y el programa calcula la suma total de los 5.
total = 0 for i in range(5): nuevo_numero = int(input("Introduce un número: " )) total += nuevo_numero print("El total es: ",total)
Observa que en la primera línea se crea una variable total, y se le asigna un valor inicial de cero. Es fácil olvidarse el crear e inicializar la variable a cero. Sin él, el código producirá un error al llegar a la línea 4. No sabe cómo añadir nuevo_numero a total, ya que total no tiene ningún valor aún.
Un error común es usar i para total en lugar de nuevo_numero. Recuerda que estamos almacenando un total para los valores introducidos por el usuario y no un total acumulado que lleve la cuenta de las veces que se ejecuta el bucle.
Si hablamos de llevar la cuenta del bucle en cada instante, podemos usar ese valor para algunas operaciones matemáticas. Por ejemplo:
$s=\sum\limits_{n=1}^{100}n$
Si no estás familiarizado con este tipo de fórmulas, ésta es una bonita manera de comenzar:
$s=1+2+3+4+5 \ldots 98+99+100$
Este código suma todos los números del 1 al 100. Demuestra un patrón común en el que se mantiene un total acumulado dentro del bucle. Además, usa otra variable, suma, para llevar la cuenta de la suma total.
# ¿Cuál es el valor de suma? suma = 0 for i in range(1,101): suma = suma + i print(suma)
Esta es una versión diferente. Toma 5 números del usuario y cuenta las veces que éste introduce un cero:
total = 0 for i in range(5): nuevo_numero = int(input( "Introduce un número: ")) if nuevo_numero == 0: total += 1 print("Has introducido ",total," ceros")
Un programador que comprenda los bucles anidados for y los totales acumulados, debería ser capaz de predecir el resultado para el código siguiente.
# ¿Cuál es el valor de a? a = 0 for i in range(10): a=a+1 print(a) # ¿Cuál es el valor de a? a = 0 for i in range(10): a = a + 1 for j in range(10): a = a + 1 print(a) # ¿Cuál es el valor de a? a = 0 for i in range(10): a = a + 1 for j in range(10): a = a + 1 print(a)
No pases de esta sección demasiado deprisa. Dale una oportunidad y predice la salida para el código anterior. Cuando lo tengas, cópialo en un programa Python, lo ejecutas y mira si estabas en lo cierto. Si no fuera así, pregúntate el por qué no.
4.2 Ejemplos de Bucles for
Este ejemplo trata de los bucles for más comunes y nos muestra cómo funcionan.
# Sample Python/Pygame Programs # Simpson College Computer Science # http://programarcadegames.com/ # http://simpson.edu/computer-science/ # Imprime 'Hola' 10 veces for i in range(10): print("Hola") # Imprime 'Hola' 5 veces y 'Allí' una for i in range(5): print("Hola") print("Allí") # Imprime 'Hola' 'Allí' 5 veces for i in range(5): print("Hola") print("Allí") # Imprime los números del 0 al 9 for i in range(10): print(i) # Dos formas de imprimir los números del 1 al 10 for i in range(1, 11): print(i) for i in range(10): print(i + 1) # Dos formas de imprimir los números del 2 al 10 for i in range(2, 12, 2): print(i) for i in range(5): print((i + 1) * 2) # Cuenta atrás desde 10 hasta 1 (el cero no) for i in range(10, 0, -1): print(i) # Imprime números desde una lista for i in [2, 6, 4, 2, 4, 6, 7, 4]: print(i) # ¿Qué es lo que imprime éste código? ¿Por qué? for i in range(3): print("a") for j in range(3): print("b") # ¿Cuál es el valor de a? a = 0 for i in range(10): a = a + 1 print(a) # ¿Cuál es el valor de a? a = 0 for i in range(10): a = a + 1 for j in range(10): a = a + 1 print(a) # ¿Cuál es el valor de a? a = 0 for i in range(10): a = a + 1 for j in range(10): a = a + 1 print(a) # ¿Cuál es el valor de la suma? sum = 0 for i in range(1, 101): sum = sum + i
4.3 Bucles While
Un bucle for se usa cuando el programa sabe que necesita repetir un bloque de código durante un cierto número de veces. Un bucle while se usa cuando el programa debe iterarse hasta que se alcanza una determinada condición.
Aunque bastante extraño, un bucle while puede usarse para sustituir a cualquier bucle for. Se puede utilizar para iterar una variable incremental hasta que alcance cierto valor. Entonces, ¿por qué usar bucles for cuando el while puede hacer todo?. Porque el bucle for es mucho más simple de usar y codificar. Un bucle for como el siguiente:
for i in range(10): print(i)
...puede hacerse cono un bucle while que tenga este aspecto:
i = 0 while i < 10: print(i) i = i + 1
En la línea 1 del bucle while se establece una variable “centinela” (en realidad, contador) que se usará para contar las veces que el bucle ha sido ejecutado. Esto sucede automáticamente con el bucle for , eliminando la necesidad de ese centinela. En la línea 2 empieza realmente el bucle while con un formato similar a una sentencia if. Si se mantiene la condición inicial, el código del bucle se seguirá repitiendo. La línea 4 sirve para incrementar el contador inicial del bucle. Esto sucede automáticamente con el bucle 'for', eliminando esta necesidad (una línea menos de código). Tal como se puede observar, el bucle for es más compacto y fácil de leer que un bucle while. De otra forma, los programas lo harían todo con un bucle while.
Un error común es confundir ambos bucles. El siguiente ejemplo muestra a un programador que no ha sido capaz de resolver la confusión entre un bucle for y otro while:
while range(10): print(i)
La función range solo funciona con los bucles for. No vayas a usarla con un bucle while!
4.3.1 Uso de Operadores de Incremento
Los operadores incrementales se usan frecuentemente con los bucles while. Es posible abreviar el código:
i = i + 1
Con esto:
i += 1
En el bucle while se vería así:
i = 0 while i < 10: print(i) i += 1
Esto se puede hacer con la resta y el producto también. Por ejemplo:
i *= 2
Es lo mismo que:
i = i * 2
Mira a ver si puedes adivinar que imprimirá esto:
i = 1 while i <= 2 ** 32: print(i) i *= 2
4.3.2 Iterando Hasta Que El Usuario Decide Salir
Una operación muy habitual es iterar hasta que el usuario realiza una petición de salida:
salir = "n" while salir == "n": salir = input ("¿Quieres salir? ")
Pueden existir diferentes formas de usar un bucle para salir. Usar una variable Boolena que dispare el evento, es una forma de manejarlo. Este es un ejemplo:
hecho = False while not hecho: salir = input ("¿Quieres salir? ") if salir == "y" : hecho = True; ataque = input ("Es que tu elfo ha atacado al dragón? ") if ataque == "y": print ("Pésima opción, estás muerto.") hecho = True;
Esto no es perfecto, ya que si ella pide salir del juego, el código volverá a preguntar si quiere atacar al dragón. ¿Cómo podríamos arreglarlo?
Este es un ejemplo del uso del bucle while donde el código se repite hasta que el valor se acerca lo suficiente a cero:
valor = 0 incremento = 0.5 while valor < 0.999: valor += incremento incremento *= 0.5 print(valor)
4.3.3 Problemas Habituales Con Los Bucles while
La programadora quiere hacer una cuenta atrás desde 10. ¿Qué es lo que está mal y cómo se puede arreglar?
i = 10 while i == 0: print (i) i -= 1
¿Qué es lo que está mal en este código que pretende contar hasta 10? ¿Qué pasará si se ejecuta? ¿Cómo se puede arreglar?
i = 1 while i < 10: print (i)
4.4 Ejemplo de Bucles while
El siguiente programa trata de los diferentes usos de los que hemos hablado sobre el bucle while.
# Sample Python/Pygame Programs # Simpson College Computer Science # http://programarcadegames.com/ # http://simpson.edu/computer-science/ # Podemos emplear un bucle while allí donde, también, podríamos usar un bucle for: i = 0 while i < 10: print(i) i = i + 1 # Es lo mismo que: for i in range(10): print(i) # Es posible simplificar el código: # i=i+1 # Con lo siguiente: # i += 1 # Esto lo podemos hacer también con la resta y la multiplicación. i = 0 while i < 10: print(i) i += 1 # ¿Qué imprimiremos aquí? i = 1 while i <= 2 ** 32: print(i) i *= 2 # Una tarea muy habitual, es iterar hasta que el usuario realiza una petición de salida. salir = "n" while salir == "n": salir = input ("¿Quieres salir? ") # Existen diversas formas para salir de un bucle. Un operador booleano que dispare el evento es una # forma de conseguirlo. hecho = False while not hecho: salir = input ("¿Quieres salir? ") if salir == "s": hecho = True; ataca = input ("¿El elfo atacó al dragón? ") if ataca == "s": print("Mala elección, estás muerto.") hecho = True; valor = 0 incremento = 0.5 while valor < 0.999: valor += incremento incremento *= 0.5 print(valor) # -- Problemas habituales con los bucles while -- # El programador quiere hacer una cuenta atrás empezando en 10. # ¿Qué es lo que está mal, y cómo lo podemos arreglar? i = 10 while i == 0: print (i) i -= 1 # ¿Qué es lo que está mal en este bucle que intenta contar hasta 10? # ¿Qué sucederá cuando lo ejecutemos? i = 1 while i < 10: print (i)
4.5 Números Aleatorios
Los números aleatorios son usados intensivamente en computación para programas que incorporan juegos o simulaciones.
4.5.1 La Función randrange
Python, por defecto, no sabe cómo construir números aleatorios. Python necesita importar una biblioteca de códigos que permitan crear este tipo de números. Es decir, para usar números aleatorios, lo primero que debería aparecer encima de cualquier línea de código del programa es una instrucción import:
import random
Tal como hicimos con pygame, es importante no crear ningún archivo que lleve el mismo nombre que la biblioteca importada. La creación de un archivo llamado random.py provocaría que Python empezara por importarlo en primer lugar, en vez de hacerlo con la biblioteca que crea los números aleatorios.
Luego de esto, podemos crear números aleatorios con la función randrange. Por ejemplo, este código genera números aleatorios entre 0 y 49. Por defecto, el límite inferior es 0.
mi_numero = random.randrange(50)
El siguiente ejemplo genera números aleatorios entre 100 y 200. Tal como pasa con la función range, el segundo parámetro especifica el límite superior, el cual no está incluido. Por lo tanto, si quieres números aleatorios hasta 200, incluido, especifica 201.
mi_numero = random.randrange(100,201)
¿Pero qué sucede si lo que quieres es un objeto aleatorio en lugar de un número? Eso necesita una lista. No trataremos en detalle las listas hasta el Capítulo 7, pero para tener una idea de cómo sería elegir al azar un objeto de una lista, veamos lo siguiente:
mi_lista = ["piedra", "papel", "tijera"] random_index = random.randrange(3) print(mi_lista[random_index])
4.5.2 La Función random
Todo el código anterior genera números enteros. Si necesitamos un número real, el programador puede usar la función random.
El siguiente código genera un número aleatorio entre 0 y 1, como por ejemplo 0.4355991106620656.
mi_numero = random.random()
Con algo de matemáticas básicas, este número puede ser adaptado. Por ejemplo, el código siguiente genera un número aleatorio real entre 10 y 15:
mi_numero = random.random() * 5 + 10
4.6 Repaso
4.6.1 Test
4.6.2 Ejercicios
Haz click para ir a los Ejercicios.
4.6.3 Taller
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin