InventarConPython 3a Es
InventarConPython 3a Es
de computadora con
Python
3 edicin
ii
http://inventwithpython.com/es
Para Caro, con ms amor que el que nunca supe que tena.
iv
http://inventwithpython.com/es
vi
http://inventwithpython.com/es
TABLA DE CONTENIDO
Instalando Python............................................................................................................................. 1
Descargar e Instalar Python ......................................................................................................... 2
Iniciando IDLE ............................................................................................................................ 3
Cmo Usar este Libro .................................................................................................................. 4
Buscando Ayuda Online .............................................................................................................. 5
Resumen ................................................................................................................................... 6
La Consola Interactiva ..................................................................................................................... 7
Operaciones Matemticas Sencillas ............................................................................................. 7
Almacenamiento de Valores en Variables ................................................................................. 10
Escribiendo Programas .................................................................................................................. 15
Cadenas ...................................................................................................................................... 15
Concatenacin de cadenas ......................................................................................................... 16
Escribir Programas en el Editor de Archivos de IDLE .............................................................. 16
Hola Mundo!............................................................................................................................. 17
Guardando el programa.............................................................................................................. 18
Abriendo tus Programas Guardados .......................................................................................... 19
Cmo Funciona el Programa Hola Mundo ............................................................................. 21
Nombres de Variables ................................................................................................................ 23
Adivina el Nmero ......................................................................................................................... 25
Muestra de ejecucin de Adivina el Nmero ......................................................................... 25
Cdigo Fuente de Adivina el Nmero ....................................................................................... 26
Sentencias import ................................................................................................................... 27
La Funcin random.randint() .......................................................................................... 28
Bucles......................................................................................................................................... 30
Bloques ...................................................................................................................................... 30
El Tipo de Datos Booleano ........................................................................................................ 31
viii
http://inventwithpython.com/es
Paso a Paso................................................................................................................................. 68
Encuentra el Bug ........................................................................................................................ 71
Puntos de Quiebre ...................................................................................................................... 74
Ejemplos de Puntos Quiebre ...................................................................................................... 75
Diagramas de Flujo ........................................................................................................................ 78
Cmo jugar al Ahorcado ............................................................................................................ 78
Prueba de ejecucin del Ahorcado ............................................................................................. 78
Arte ASCII ................................................................................................................................. 80
Diseo de un Programa mediante Diagramas de Flujo .............................................................. 80
Crear el Diagrama de Flujo ........................................................................................................ 82
El Ahorcado ................................................................................................................................... 91
Cdigo Fuente de El Ahorcado .................................................................................................. 91
Cadenas Multi-Lnea .................................................................................................................. 95
Variables Constantes .................................................................................................................. 96
Listas .......................................................................................................................................... 96
Mtodos ................................................................................................................................... 101
Los mtodos de cadena lower() y upper()...................................................................... 102
Los mtodos de lista reverse() y append() ................................................................... 103
El mtodo de lista split() ................................................................................................... 104
Las Funciones range() y list() ...................................................................................... 106
Los bucles for ........................................................................................................................ 107
Cortes ....................................................................................................................................... 109
Sentencias elif ("Else If") .................................................................................................... 113
Extendiendo Ahorcado................................................................................................................. 121
Diccionarios ............................................................................................................................. 122
La Funcin random.choice() .......................................................................................... 125
Asignacin Mltiple................................................................................................................. 127
Ta Te Ti ....................................................................................................................................... 129
Prueba de Ejecucin de Ta Te Ti ............................................................................................. 129
http://inventwithpython.com/es
Captulo 1
INSTALANDO PYTHON
Temas Tratados En Este Captulo:
Descargar e instalar el intrprete de Python
Cmo usar este libro
La pgina web de este libro en http://inventwithpython.com/es
Hola! Este libro te ensear a programar creando videojuegos. Una vez que aprendas cmo
funcionan los juegos en este libro, sers capaz de crear tus propios juegos. Todo lo que necesitas
es una computadora, un software llamado el intrprete de Python, y este libro. El intrprete de
Python es libre para descargar de Internet.
Cuando era nio, un libro como este me ense cmo escribir mis primeros programas y juegos.
Era divertido y fcil. Ahora, siendo un adulto, sigo divirtindome programando y me pagan por
hacerlo. Pero incluso si no te conviertes en un programador cuando crezcas, programar es una
habilidad divertida y til para tener.
Las computadoras son mquinas increbles, y aprender a programarlas no es tan difcil como la
gente cree. Si puedes leer este libro, puedes programar una computadora. Un programa de
computadora es un conjunto de instrucciones que la computadora puede entender, igual que un
libro de cuentos es un conjunto de oraciones que el lector entiende. Ya que los videojuegos no
son ms que programas de computadora, tambin estn compuestos por instrucciones.
Para dar instrucciones a una computadora, escribes un programa en un lenguaje que la
computadora comprende. Este libro ensea un lenguaje de programacin llamado Python. Hay
muchos otros lenguajes de programacin, incluyendo BASIC, Java, JavaScript, PHP y C++.
Cuando era nio, era comn aprender BASIC como un primer lenguaje. Sin embargo, nuevos
lenguajes de programacin tales como Python han sido inventados desde entonces. Python es
an ms fcil de aprender que BASIC! Pero sigue siendo un lenguaje de programacin muy til
utilizado por programadores profesionales. Muchos adultos usan Python en su trabajo y cuando
programan por diversin.
Los juegos que crears a partir de este libro parecen simples comparados con los juegos para
Xbox, PlayStation, o Nintendo. Estos juegos no tienen grficos sofisticados porque estn
pensados para ensear conceptos bsicos de programacin. Son deliberadamente sencillos de
http://inventwithpython.com/es
modo que puedas enfocarte en aprender a programar. Los juegos no precisan ser complicados
para ser divertidos.
1. Cuando el paquete DMG se abra en una nueva ventana, haz doble clic sobre el archivo
Python.mpkg. Es posible que necesites ingresar la clave de administrador.
2. Haz clic en Continue (Continuar) para pasar la seccin Bienvenido y en Agree (Aceptar)
para aceptar la licencia.
3. Selecciona HD Macintosh (o como sea que se llame tu disco rgido) y haz clic en Install
(Instalar).
Si usas Ubuntu, puedes instalar Python del Centro de Software de Ubuntu siguiendo estos pasos:
1. Abre el Centro de Software de Ubuntu.
2. Escribe Python en el cuadro de bsqueda en la esquina superior derecha de la ventana.
3. Elige IDLE (using Python 3.4), o la que sea la ltima versin en este momento.
4. Haz clic en Install (Instalar). Tal vez necesites la clave de administrador para completar
la instalacin.
Iniciando IDLE
La sigla IDLE (Interactive DeveLopment Environment en ingls) significa Entorno Interactivo
de Desarrollo. El entorno de desarrollo es como un software procesador de palabras para escribir
programas de Python. Iniciar IDLE es diferente para cada sistema operativo.
Sobre Windows, haz clic en el botn Inicio en la esquina inferior izquierda, teclea IDLE y
selecciona IDLE (Python GUI).
Sobre Mac OS X, abre la ventana de Finder y haz clic en Applications. Luego haz clic en Python
3.4. Luego clic sobre el cono de IDLE.
Sobre Ubuntu o Linux, abre una terminal y teclea idle3. Tambin puede ser posible hacer clic
en Applications en el borde superior de la pantalla. Luego haz clic sobre Programming y
despus IDLE 3.
La ventana que aparece la primera vez que ejecutas IDLE es la consola interactiva, como se
muestra en la Figura 1-2. Puedes ingresar instrucciones de Python en la consola interactiva a a la
derecha del prompt >>> y Python las ejecutar. Luego de mostrar los resultados de la instruccin,
un nuevo prompt >>> estar esperando por tu prxima instruccin.
http://inventwithpython.com/es
Figure 1-2: La consola interactiva del programa IDLE en Windows, OS X, y Ubuntu Linux.
Esos nmeros estn ah slo para que este libro pueda referir a lneas especficas del programa.
No son parte del cdigo fuente de un programa real.
Aparte de los nmeros de lnea, escribe el cdigo exactamente como aparece. Ten en cuenta que
algunas de las lneas de cdigo estn indentadas por cuatro u ocho espacios. Cada caracter en
IDLE ocupa el mismo ancho, de modo que puedes contar el nmero de espacios contando el
nmero de caracteres en las lneas arriba o abajo.
Por ejemplo, los espacios indentados aqu estn marcados con un cuadrado negro para que
puedas verlos:
Si ests escribiendo a mano los programas de este libro y obtienes un error, primero
busca errores tipogrficos con la herramienta diff en http://invpy.com/es/diff. Copia y
http://inventwithpython.com/es
pega tu cdigo en la herramienta diff para encontrar las diferencias entre el cdigo del
libro y tu programa.
Explica lo que ests intentando hacer cuando expliques el error. Esto permitir a quien te
ayuda saber si ests equivocndote por completo.
Copia y pega el mensaje de error completo y tu cdigo.
Busca en la web para ver si alguien ya ha formulado (y respondido) tu pregunta.
Explica lo que ya has intentado hacer para resolver tu problema. Esto muestra a la gente
que ya has hecho algo de trabajo para tratar de entender las cosas por t mismo.
S amable. No exijas ayuda o presiones a quienes te ayudan para que respondan rpido.
Resumen
Este captulo te ha ayudado a comenzar con el software Python mostrndote el sitio web
http://python.org, de donde puedes descargarlo gratis. Luego de instalar y lanzar el software
Python IDLE, estars listo para aprender a programar a comenzando en el prximo captulo.
El sitio web de este libro en http://inventwithpython.com/es contiene ms informacin sobre cada
uno de los captulos, incluyendo un sitio web de trazado en lnea y una herramienta diff que
puede ayudarte a entender los programas de este libro.
Captulo 2
LA CONSOLA INTERACTIVA
Temas Tratados En Este Captulo:
Enteros y Nmeros de Punto Flotante
Expresiones
Valores
Operadores
Evaluacin de Expresiones
Almacenamiento de Valores en Variables
Antes de poder crear juegos, necesitas aprender algunos conceptos bsicos de programacin. No
crears juegos en este captulo, pero aprender estos conceptos es el primer paso para programar
videojuegos. Comenzaremos por aprender cmo usar la consola interactiva de Python.
http://inventwithpython.com/es
Cuando se usan de esta forma, +, -, *, y / se llama operadores. Los operadores le dicen a Python
qu hacer con los nmeros que los rodean.
Expresiones
Estos problemas matemticos son ejemplos de expresiones. Las computadoras pueden resolver
millones de estos problemas en segundos. Las expresiones se componen de valores (los nmeros)
conectadas por operadores (los smbolos matemticos). Prueba escribir algunos de estos
problemas matemticos en la consola interactiva, presiona la tecla INTRO despus de cada uno.
2+2+2+2+2
8*6
10-5+6
2 +
Luego de introducir estas instrucciones, la consola interactiva se ver como la Figura 2-2.
Evaluacin de Expresiones
Cuando una computadora resuelve la expresin 10 + 5 y obtiene el valor 15, ha evaluado la
expresin. Evaluar una expresin la reduce a un nico valor, igual que resolver un problema de
matemtica lo reduce a un nico nmero: la respuesta. Ambas expresiones 10 + 5 y 10 + 3 + 2
son evaluadas a 15.
Las expresiones pueden ser de cualquier tamao, pero siempre sern evaluadas a un valor nico.
Incluso valores nicos son expresiones: La expresin 15 se evala al valor 15. Por ejemplo, la
expresin 8 * 3 / 2 + 2 + 7 - 9 se evala al valor 12.0 a travs de los siguientes pasos:
8 * 3 / 2 + 2 + 7 9
24 / 2 + 2 + 7 9
12.0 + 2 + 7 9
14.0 + 7 9
21.0 9
12.0
No puedes ver todos estos pasos en la consola interactiva. La consola los realiza y slo te muestra
los resultados:
>>> 8 * 3 / 2 + 2 + 7 - 9
12.0
10
http://inventwithpython.com/es
Observa que el operador divisin / se evala a un valor float, como ocurre cuando 24 / 2
devuelve 12.0. Las operaciones matemticas con valores flotantes tambin devuelven valores
flotantes, como cuando 12.0 + 2 devuelve 14.0.
Notice that the / division operator evaluates to a float value, as in 24 / 2 evaluating to 12.0.
Math operations with float values also evaluate to float values, as in 12.0 + 2 evaluating to 14.0.
Errores de Sintaxis
Si escribes 5 + en la consola interactiva, obtendrs un mensaje de error.
>>> 5 +
SyntaxError: invalid syntax
Este ocurre porque 5 + no es una expresin. Las expresiones conectan valores mediante
operadores. Pero el operador + espera un valor despus del signo +. Cuando este valor no se
encuentra, aparece un mensaje de error.
SyntaxError
La caja de la variable spam tendr guardado el valor 15, como se muestra en la Figura 2-4. El
nombre spam es la etiqueta en la caja (para que Python pueda distinguir las variables) y el valor
est escrito en una pequea nota dentro de la caja.
11
Cuando presiones INTRO no recibirs ninguna respuesta. En Python, si no aparece ningn mensaje
de error significa que la instruccin se ha ejecutado correctamente. El indicador de consola >>>
aparecer para que puedas tipear la prxima instruccin.
Figura 2-4: Las variables son como cajas que pueden contener valores.
A diferencia de las expresiones, las sentencias no son evaluadas a ningn valor. Es por eso que
no se muestra ningn valor en la siguiente lnea de la consola interactiva a continuacin de spam
= 15. Puede ser confuso diferenciar cules instrucciones son expresiones y cules son sentencias.
Slo recuerda que las expresiones son evaluadas a un valor nico. Cualquier otro tipo de
instruccin es una sentencia.
Las variables almacenan valores, no expresiones. Por ejemplo, considera la expresin en las
sentencias spam = 10 + 5 y spam = 10 + 7 - 2. Ambas son evaluadas a 15. El resultado final
es el mismo: Las dos sentencias de asignacin almacenan el valor 15 en la variables spam.
La primera vez que una variables es usada en una sentencia de asignacin, Python crear esa
variable. Para comprobar qu valor contiene una variable dada, escribe el nombre de la variable
en la consola interactiva:
>>> spam = 15
>>> spam
15
La expresin spam se evala al valor dentro de la variable spam: 15. Puedes usar variables en
expresiones. Prueba escribir lo siguiente en la consola interactiva:
>>> spam = 15
>>> spam + 5
20
12
http://inventwithpython.com/es
Haz fijado el valor de la variable spam en 15, por lo que escribir spam + 5 es como escribir la
expresin 15 + 5. Aqu se muestran los pasos para la evaluacin de spam + 5:
spam + 5
15 + 5
20
No puedes usar una variable antes de que sea creada por una sentencia de asignacin. Python
responder con NameError porque todava no existe una variable con ese nombre. Escribir mal el
nombre de una variable tambin causa este error:
>>> spam = 15
>>> spma
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
spma
NameError: name 'spma' is not defined
El error aparece porque hay una variable llamada spam, pero ninguna llamada spma.
Puedes cambiar el valor almacenado en una variable escribiendo otra sentencia de asignacin. Por
ejemplo, prueba escribir lo siguiente en la consola interactiva:
>>>
>>>
20
>>>
>>>
8
spam = 15
spam + 5
spam = 3
spam + 5
La primera vez que escribes spam + 5, la expresin se evala a 20 porque has guardado 15 dentro
de spam. Sin embargo, cuando escribes spam = 3, el valor 15 es reemplazado, o sobrescrito, con
el valor 3. Ahora cuando escribes spam + 5, la expresin se evala a 8 porque el valor de spam es
ahora 3. La sobrescritura se muestra en la Figura 2-5.
13
La sentencia de asignacin spam = spam + 5 es como decir, el nuevo valor de la variable spam
ser el valor actual de spam ms cinco. Contina incrementando el valor de spam en 5 varias
veces escribiendo lo siguiente en la consola interactiva:
>>>
>>>
>>>
>>>
>>>
30
spam
spam
spam
spam
spam
=
=
=
=
15
spam + 5
spam + 5
spam + 5
Ahora la variable bacon almacena el valor 10, y eggs almacena el valor 15. Cada variable es una
caja independiente con su propio valor, como en la Figura 2-6.
14
http://inventwithpython.com/es
Figura 2-6: Las variables bacon y eggs almacenan valores dentro de ellas.
Intenta escribir spam = bacon + eggs en la consola interactiva, luego comprueba el nuevo valor
de spam:
>>>
>>>
>>>
>>>
25
bacon = 10
eggs = 15
spam = bacon + eggs
spam
El valor de spam es ahora 25. Cuando sumas bacon y eggs ests sumando sus valores, que son 10
y 15 respectivamente. Las variables contienen valores, no expresiones. La variable spam recibi
el valor 25, y no la expresin bacon + eggs. Luego de la sentencia de asignacin spam = bacon
+ eggs, cambiar bacon o eggs no afecta a spam.
Resumen
En este captulo has aprendido los conceptos bsicos para escribir instrucciones en Python.
Python necesita que le digas exactamente qu hacer de forma estricta. Las computadoras no
tienen sentido comn y slo entienden instrucciones especficas.
Las expresiones son valores (tales como 2 5) combinados con operadores (tales como + o -).
Python puede evaluar expresiones, es decir, reducirlas a un valor nico. Puedes almacenar valores
dentro de las variables de modo que tu programa sea capaz de recordarlas y usarlas ms adelante.
Hay muchos otros tipos de operadores y valores en Python. En el prximo captulo, repasaras
algunos conceptos ms y escribirs tu primer programa. Aprenders a trabajar con texto en
expresiones. Python no est limitado a nmeros; es ms que slo una calculadora!
15
Captulo 3
ESCRIBIENDO PROGRAMAS
Temas Tratados En Este Captulo:
Flujo de ejecucin
Cadenas
Concatenacin de cadenas
Tipos de datos (como cadenas o enteros)
Usando el editor de archivos para escribir programas
Guardar y ejecutar programas en IDLE
La funcin print()
La funcin input()
Comentarios
Sensibilidad a maysculas
Suficiente matemtica por ahora. Ahora veamos qu puede hacer Python con texto. En este
captulo, aprenders cmo almacenar texto en variables, combinar textos, y mostrar texto en
pantalla.
Casi todos los programas muestran texto al usuario, y el usuario ingresa texto en tus programas a
travs del teclado. En este captulo crears tu primer programa. Este programa muestra el saludo
Hola Mundo! y te pregunta tu nombre.
Cadenas
En Python, los valores de texto se llaman cadenas. Los valores cadena pueden usarse igual que
valores enteros o float. Puedes almacenar cadenas en variables. En cdigo, las cadenas comienzan
y terminan con una comilla simple ('). Prueba introducir este cdigo en la consola interactiva:
>>> spam = 'hola'
Las comillas simples le dicen a Python dnde comienza y termina la cadena, pero no son parte del
texto del valor de cadena. Ahora bien, si escribes spam en la consola interactiva, podrs ver el
contenido de la variable spam. Recuerda, Python evala las variables al valor almacenado dentro
de las mismas. En este caso, la cadena 'hola':
>>> spam = 'hola'
>>> spam
16
http://inventwithpython.com/es
'hola'
Las cadenas pueden contener cualquier caracter del teclado y pueden ser tan largas como quieras.
Todos estos son ejemplos de cadenas:
'hola'
'Oye t!'
'GATITOS'
'7 manzanas, 14 naranjas, 3 limones'
'Si no est relacionado con elefantes es irrelefante.'
'Hace mucho tiempo, en una galaxia muy, muy lejana...'
'O*&#wY%*&OCfsdYO*&gfC%YO*&%3yc8r2'
Concatenacin de cadenas
Las cadenas pueden combinarse con operadores para generar expresiones, al igual que los
nmeros enteros y floats. Puedes combinar dos cadenas con el operador +. Esto es concatenacin
de cadenas. Prueba ingresando 'Hola' + 'Mundo!' into the interactive shell:
>>> 'Hola' + 'Mundo!'
'HolaMundo!'
La expresin se evala a un valor nico de cadena, 'HolaMundo!'. No hay un espacio entre las
palabras porque no haba espacios en ninguna de las cadenas concatenadas, a diferencia del
siguiente ejemplo:
>>> 'Hola ' + 'Mundo!'
'Hola Mundo!'
El operador + funciona de forma diferente sobre valores enteros y cadenas, ya que son distintos
tipos de datos. Todos los valores tienen un tipo de datos. El tipo de datos del valor 'Hola' es una
cadena. El tipo de datos del valor 5 es un entero. El tipo de datos le dice a Python qu deben
hacer los operadores al evaluar expresiones. El operador + concatena valores de tipo cadena, pero
suma valores de tipo entero (o float).
17
Ventana). Aparecer una ventana vaca para que escribas el cdigo de tu programa, como se ve en
la Figura 3-1.
Figura 3-1: La ventana del editor de archivos (izquierda) y la consola interactiva (derecha).
Las dos ventanas se ven parecidas, pero slo recuerda esto: La ventana de la consola interactiva
tendr el smbolo de sistema >>>. La ventana del editor de archivos no lo tendr.
Hola Mundo!
Es tradicin entre programadores hacer que su primer programa muestre Hola Mundo! en la
pantalla. Ahora crears tu propio programa Hola Mundo.
Al ingresar tu programa, no escribas los nmeros a la izquierda del cdigo. Estn all slo para
que este libro pueda referirse al cdigo por nmero de lnea. La esquina inferior derecha de la
ventana del editor de archivos te indicar dnde est el cursor intermitente. La Figura 3-2 muestra
que el cursor se encuentra sobre la lnea 1 y sobre la columna 0.
Figura 3-2: La parte inferior derecha de la ventana del editor de archivos te indica en qu lnea
est el cursor.
18
http://inventwithpython.com/es
hola.py
Ingresa el siguiente texto en la nueva ventana del editor de archivos. Este es el cdigo fuente del
programa. Contiene las instrucciones que Python seguir cuando el programa se ejecute.
NOTA IMPORTANTE! Los programas de este libro slo podrn ejecutarse
sobre Python 3, no Python 2. Al iniciar la ventana IDLE, dir algo como Python
3.4.2 en la parte superior. Si tienes Python 2 instalado, es posible instalar
tambin Python 3 a la vez. Para descargar Python 3, dirgete a
https://python.org/download/.
hola.py
1.
2.
3.
4.
5.
El programa IDLE escribir diferentes tipos de instrucciones en diferentes colores. Cuando hayas
terminado de escribir el cdigo, la ventana debera verse as:
Figura 3-3: La ventana del editor de archivos se ver as luego de haber ingresado el cdigo.
Guardando el programa.
Una vez que hayas ingresado tu cdigo fuente, gurdalo haciendo clic en File (Archivo) Save
As (Guardar Como). O pulsa Ctrl-S para guardar usando un acceso directo del teclado. La Figura
3-4 muestra la ventana Guardar Como que se abrir. Escribe hola.py en el campo de texto
Nombre y haz clic en Guardar.
19
20
http://inventwithpython.com/es
...quiere decir que ests usando Python 2, en lugar de Python 3. Instala una versin de Python 3
de http://python.org. Luego, re-ejecuta el programa con Python 3.
21
Comentarios
1. # Este programa saluda y pregunta por mi nombre.
Esta instruccin es un comentario. Cualquier texto a continuacin del signo # (llamado smbolo
almohadilla) es un comentario. Los comentarios no son para Python, sino para t, el programador.
Python ignora los comentarios. Los comentarios son notas del programador acerca de lo que el
cdigo hace. Puedes escribir lo que quieras en un comentario. Para facilitar la lectura del cdigo
fuente, este libro muestra los comentarios en texto de color gris claro.
Los programadores usualmente colocan un comentario en la parte superior de su cdigo para dar
un ttulo a su programa.
Funciones
Una funcin es una especie de mini-programa dentro de tu programa. Las funciones contienen
instrucciones que se ejecutan cuando la funcin es llamada. Python ya tiene algunas funciones
integradas. Dos funciones, print() e input(), son descriptas a continuacin. Lo maravilloso
acerca de las funciones es que slo necesitas saber lo que la funcin hace, pero no cmo lo hace.
Una llamada a una funcin es un fragmento de cdigo que dice a Python que ejecute el cdigo
dentro de una funcin. Por ejemplo, tu programa llama a la funcin print() para mostrar una
cadena en la pantalla. La funcin print() toma la cadena que t escribes entre los parntesis
como entrada y muestra el texto en la pantalla.
Para mostrar Hola mundo! en la pantalla, escribe el nombre de la funcin print, seguido por un
parntesis de apertura, seguido por la cadena 'Hola mundo!' y un parntesis de cierre.
22
http://inventwithpython.com/es
La funcin print()
2. print('Hola mundo!')
3. print('Cmo te llamas?')
Las lneas 2 y 3 son llamadas a la funcin print(). Un valor entre los parntesis de la llamada a
una funcin es un argumento. El argumento en la llamada a la funcin print() de la lnea 2 es
'Hola mundo!'. El argumento en la llamada a print() de la lnea 3 es 'Cmo te llamas?'.
Esto se llama pasar el argumento a la funcin print().
En este libro, los nombres de funciones tienen parntesis al final. Esto deja en claro que print()
hace referencia a una funcin llamada print(), y no a una variable llamada print. Esto es como
el uso de comillas alrededor del nmero '42' para indicar a Python that que ests refirindote a la
cadena '42' y no al entero 42.
La funcin input()
4. miNombre = input()
La lnea 4 es una sentencia de asignacin con una variable (miNombre) y una llamada a una
funcin (input()). Cuando input() es llamada, el programa espera a que el usuario ingrese
texto. La cadena de texto que el usuario ingresa se convierte en el valor al que se evala la
llamada a la funcin. Las llamadas a funciones pueden usarse en expresiones, en cualquier lugar
en que pueda usarse un valor.
El valor al cual se evala la llamada a la funcin es llamado valor de retorno. (De hecho, el
valor devuelto por la llamada a una funcin significa lo mismo que el valor al que se evala la
llamada a la funcin.) En este caso, el valor devuelto por la funcin input() es la cadena que el
usuario ha escrito (su nombre). Si el usuario ha ingresado Alberto, la llamada a la funcin
input() se evala a la cadena 'Alberto'. La evaluacin se ve as:
miNombre = input()
miNombre = 'Alberto'
23
La ltima lnea es otra llamada a la funcin print(). La expresin 'Es un placer conocerte,
' + miNombre entre los parntesis de print(). Sin embargo, los argumentos son siempre valores
individuales. Python evaluar primero esta expresin y luego pasar este valor como argumento.
Si 'Alberto' est almacenado en miNombre, la evaluacin ocurre as:
print(Es un placer conocerte, ' + miNombre)
Terminando el Programa
Una vez que el programa ejecuta la ltima lnea, termina y se sale del programa. Esto quiere
decir que el programa deja de ejecutarse. Python olvida todos los valores almacenados en
variables, incluyendo la cadena almacenada en miNombre. Si ejecutas el programa de nuevo y
escribes un nombre diferente, el programa pensar que esa otra cadena es tu nombre.
Hola mundo!
Cmo te llamas?
Carolyn
Es un placer conocerte, Carolyn
Recuerda, la computadora hace exactamente lo que la programas para hacer. Las computadoras
son tontas y slo siguen tus instrucciones al pie de la letra. A la computadora no le importa si
escribes tu nombre, el nombre de otra persona, o slo algo absurdo. Escribe lo que quieras. La
computadora lo tratar de la misma forma:
Hola mundo!
Cmo te llamas?
pop
Es un placer conocerte, pop
Nombres de Variables
Dar nombres descriptivos a las variables facilita entender qu es lo que hace un programa.
Imagina si estuvieses mudndote a una nueva casa y hubieses colocado a cada una de tus cajas la
etiqueta Cosas. Eso no sera til en lo absoluto!
En lugar de miNombre, podras haber llamado a esta variable abrahamLincoln o nOmBrE. A
Python no le importa. Ejecutar el programa de la misma forma.
24
http://inventwithpython.com/es
Los nombres de variables son sensibles a maysculas. Sensible a maysculas significa que el
mismo nombre de variable con diferente capitalizacin se considera una variable diferente. De
modo que spam, SPAM, Spam, y sPAM son cuatro variables diferentes en Python. Cada una de ellas
contiene su propio valor independiente. Es una mala idea tener variables con diferente
capitalizacin en tu programa. En lugar de ello, usa nombres descriptivos para tus variables.
Los nombres de variables se escriben habitualmente en minscula. Si hay ms de una palabra en
el nombre de la variable, escribe en mayscula la primera letra de cada palabra despus de la
primera. Esto hace que tu cdigo sea ms legible. Por ejemplo, el nombre de variable
loQueHeDesayunadoEstaMaana es mucho ms fcil de leer que
loquehedesayunadoestamaana. Esto es una convencin: una forma opcional pero estndar de
hacer las cosas en Python.
Es preferible usar nombres cortos antes que largos a las variables: desayuno o
comidaEstaMaana son ms fciles de leer que loQueHeDesayunadoEstaMaana.
Los ejemplos en este libro de la consola interactiva usan nombres de variables como spam, eggs,
ham, y bacon. Esto es porque los nombres de variables en estos ejemplos no importan. Sin
embargo, todos los programas de este libro usan nombres descriptivos. Tus programas tambin
deberan usar nombres de variables descriptivos.
Resumen
Luego de haber aprendido acerca de cadenas y funciones, puedes empezar a crear programas que
interactan con usuarios. Esto es importante porque texto es la principal va de comunicacin
entre el usuario y la computadora. El usuario ingresa texto a travs el teclado mediante la funcin
input(), y la computadora muestra texto en la pantalla usando la funcin print().
Las cadenas son simplemente valores de un nuevo tipo de datos. Todos los valores tienen un tipo
de datos, y hay muchos tipos de datos en Python. El operador + se usa para unir cadenas.
Las funciones se usan para llevar a cabo alguna instruccin complicada como parte de nuestro
programa. Python tiene muchas funciones integradas acerca de las cuales aprenders en este libro.
Las llamadas a funciones pueden ser usadas en expresiones en cualquier lugar donde se usa un
valor.
La instruccin de tu programa en que Python se encuentra se denomina ejecucin. En el prximo
captulo, aprenders ms acerca de cmo hacer que la ejecucin proceda de otras formas que
simplemente en forma descendente a travs del programa. Una vez que aprendas esto, podrs
comenzar a crear juegos.
25
Captulo 4
ADIVINA EL NMERO
Temas Tratados En Este Captulo:
Sentencias import
Mdulos
Sentencias while
Condiciones
Bloques
Booleanos
Operadores de comparacin
La diferencia entre = y ==
Sentencias if
La palabra reservada break
Las funciones str(), int() y float()
La funcin random.randint()
En este captulo crears el juego Adivina el Nmero. La computadora pensar un nmero
aleatorio entre 1 y 20, y te pedir que intentes adivinarlo. La computadora te dir si cada intento
es muy alto o muy bajo. T ganas si adivinas el nmero en seis intentos o menos.
Este es un buen juego para codificar ya que usa nmeros aleatorios y bucles, y recibe entradas del
usuario en un programa corto. Aprenders cmo convertir valores a diferentes tipos de datos, y
por qu es necesario hacer esto. Dado que este programa es un juego, nos referiremos al usuario
como el jugador. Pero llamarlo usuario tambin sera correcto.
26
http://inventwithpython.com/es
adivinaElNmero.py
1. # Este es el juego de adivinar el nmero.
2. import random
3.
4. intentosRealizados = 0
5.
6. print('Hola! Cmo te llamas?')
7. miNombre = input()
8.
9. nmero = random.randint(1, 20)
10. print('Bueno, ' + miNombre + ', estoy pensando en un nmero entre 1 y 20.')
11.
12. while intentosRealizados < 6:
13.
print('Intenta adivinar.') # Hay cuatro espacios delante de print.
14.
estimacin = input()
15.
estimacin = int(estimacin)
16.
17.
intentosRealizados = intentosRealizados + 1
18.
19.
if estimacin < nmero:
20.
print('Tu estimacin es muy baja.') # Hay ocho espacios delante de
print.
27
21.
22.
if estimacin > nmero:
23.
print('Tu estimacin es muy alta.')
24.
25.
if estimacin == nmero:
26.
break
27.
28. if estimacin == nmero:
29.
intentosRealizados = str(intentosRealizados)
30.
print('Buen trabajo, ' + miNombre + '! Has adivinado mi nmero en ' +
intentosRealizados + ' intentos!')
31.
32. if estimacin != nmero:
33.
nmero = str(nmero)
34.
print('Pues no. El nmero que estaba pensando era ' + nmero)
Sentencias import
1. # Este es el juego de adivinar el nmero.
2. import random
La primera lnea es un comentario. Recuerda que Python ignorar todo lo que est precedido por
el signo #. Esto slo nos indica qu es lo que hace el programa.
La segunda lnea es una sentencia import. Recuerda, las sentencias son instrucciones que realizan
alguna accin, pero no son evaluadas a un valor como las expresiones. Ya has visto sentencias
antes: las sentencias de asignacin almacenan un valor en una variable.
Aunque Python incluye muchas funciones integradas, algunas funciones existen en programas
separados llamados mdulos. Puedes usar estas funciones importando sus mdulos en tu
programa con una sentencia import.
La lnea 2 importa el mdulo llamado random de modo que el programa pueda llamar a
random.randint(). Esta funcin generar un nmero aleatorio para que el usuario adivine.
4. intentosRealizados = 0
La lnea 4 crea una nueva variable llamada intentosRealizados. Guardaremos en esta variable
el nmero de veces que el jugador ha intentado adivinar el nmero. Ya que el jugador no ha
realizado ningn intento a esta altura del programa, guardaremos aqu el entero 0.
6. print('Hola! Cmo te llamas?')
28
http://inventwithpython.com/es
7. miNombre = input()
Las lneas 6 y 7 son iguales a las lneas en el programa Hola Mundo que viste en el Captulo 3.
Los programadores a menudo reutilizan cdigo de sus otros programas para ahorrarse trabajo.
La lnea 6 es una llamada a la funcin print(). Recuerda que una funcin es como un miniprograma dentro de tu programa. Cuando tu programa llama a una funcin, ejecuta este miniprograma. El cdigo dentro de la funcin print() muestra en la pantalla la cadena que ha
recibido como argumento.
La lnea 7 permite al usuario escribir su nombre y lo almacena en la variable miNombre.
(Recuerda, la cadena podra no ser realmente el nombre del jugador. Es simplemente cualquier
cadena que el jugador haya introducido. Las computadoras son tontas, y slo siguen sus
instrucciones sin importarles nada ms.)
La Funcin random.randint()
9. nmero = random.randint(1, 20)
La lnea 9 llama a una nueva funcin denominada randint() y guarda el valor que sta devuelve
en la variable nmero. Recuerda, las llamadas a funciones pueden ser parte de expresiones, ya que
son evaluadas a un valor.
La funcin randint() es parte del mdulo random, por lo que debes colocar random. delante de
ella (no olvides colocar el punto!) para decirle a Python que la funcin randint() est en el
mdulo random.
La funcin randint() devolver un entero aleatorio en el intervalo comprendido (incluidos los
bordes) entre los dos argumentos enteros que le pases. La lnea 9 pasa 1 y 20 separados por una
coma y entre los parntesis que siguen al nombre de la funcin. El entero aleatorio devuelto por
randint() es almacenado en una variable llamada nmero; este es el nmero secreto que el
jugador intentar adivinar.
Slo por un momento, vuelve a la consola interactiva y escribe import random para importar el
mdulo random. Luego escribe random.randint(1, 20) para ver a qu se evala la llamada a la
funcin. Devolver un entero entre 1 y 20. Repite el cdigo nuevamente y la llamada a la funcin
probablemente devolver un entero diferente. La funcin randint() devuelve un entero aleatorio
cada vez, de la misma forma en que tirando un dado obtendras un nmero aleatorio cada vez:
>>> import random
>>> random.randint(1, 20)
12
>>>
18
>>>
3
>>>
18
>>>
7
29
random.randint(1, 20)
random.randint(1, 20)
random.randint(1, 20)
random.randint(1, 20)
Usa la funcin randint() cuando quieras agregar aleatoriedad a tus juegos. Y vas a usar
aleatoriedad en muchos juegos. (Piensa en la cantidad de juegos de mesa que utilizan dados.)
Tambin puedes probar diferentes intervalos de nmeros cambiando los argumentos. Por
ejemplo, escribe random.randint(1, 4) para obtener slo enteros entre 1 y 4 (incluyendo 1 y 4).
O prueba random.randint(1000, 2000) para obtener enteros entre 1000 y 2000.
Por ejemplo, escribe lo siguiente en la consola interactiva. Los resultados que obtienes cuando
llamas a la funcin random.randint() sern seguramente diferentes (despus de todo es
aleatorio).
>>> random.randint(1, 4)
3
>>> random.randint(1000, 2000)
1294
Puedes cambiar ligeramente el cdigo fuente del juego para hacer que el programa se comporte
de forma diferente. Prueba cambiar las lneas 9 y 10 de:
9. nmero = random.randint(1, 20)
10. print('Bueno, ' + miNombre + ', estoy pensando en un nmero entre 1 y 20.')
a lo siguiente:
9. nmero = random.randint(1, 100)
10. print('Bueno, ' + miNombre + ', estoy pensando en un nmero entre 1 y
100.')
Y ahora la computadora pensar en un entero comprendido entre 1 y 100 en lugar de entre 1 y 20.
Cambiar la lnea 9 cambiar el intervalo del nmero aleatorio, pero recuerda cambiar tambin la
lnea 10 para que el juego le diga al jugador el nuevo rango en lugar del viejo.
30
http://inventwithpython.com/es
Recibiendo al Jugador
10. print('Bueno, ' + miNombre + ', estoy pensando en un nmero entre 1 y 20.')
En la lnea 10 la funcin print() recibe al jugador llamndolo por su nombre, y le dice que la
computadora est pensando un nmero aleatorio.
Puede parecer que hay ms de un argumento cadena en la lnea 10, pero observa la lnea con
cuidado. El signo suma concatena las tres cadenas de modo que son evaluadas a una nica
cadena. Y esa nica cadena es el argumento que se pasa a la funcin print(). Si miras
detenidamente, vers que las comas estn dentro de las comillas, por lo que son parte de las
cadenas y no un separador.
Bucles
12. while intentosRealizados < 6:
La lnea 12 es una sentencia while (mientras), que indica el comienzo de un bucle while. Los
bucles te permiten ejecuta cdigo una y otra vez. Sin embargo, necesitas aprender algunos otros
conceptos antes de aprender acerca de los bucles. Estos conceptos son bloques, booleanos,
operadores de comparacin, condiciones, y la sentencia while.
Bloques
Varias lneas de cdigo pueden ser agrupadas en un bloque. Un bloque consiste en lneas de
cdigo que comparten mnima indentacin posible. Puedes ver dnde comienza y termina un
bloque de cdigo mirando el nmero de espacios antes de las lneas. Esto se llama la indentacin
de la lnea.
Un bloque comienza cuando la indentacin de una lnea se incrementa (usualmente en cuatro
espacios). Cualquier lnea subsiguiente que tambin est indentada por cuatro espacios es parte
del bloque. El bloque termina cuando hay una lnea de cdigo con la misma indentacin que antes
de empezar el bloque. Esto significa que pueden existir bloques dentro de otros bloques. La
Figura 4-1 es un diagrama de cdigo con los bloques delineados y numerados. Los espacios son
cuadrados negros para que sean ms fciles de contar.
En la Figura 4-1, la lnea 12 no tiene indentacin y no se encuentra dentro de ningn bloque. La
lnea 13 tiene una indentacin de cuatro espacios. Como esta indentacin es mayor que la
indentacin de la lnea anterior, ha comenzado un nuevo bloque. Este bloque tiene la etiqueta (1)
31
en la Figura 4-1. Este bloque continuar hasta una lnea sin espacios (la indentacin original antes
de que comenzara el bloque). Las lneas vacas son ignoradas.
La lnea 20 tiene una indentacin de ocho espacios. Ocho espacios es ms que cuatro espacios, lo
que comienza un nuevo bloque. Este bloque se seala con (2) en la Figura 4-1. Este bloque se
encuentra dentro de otro bloque.
32
http://inventwithpython.com/es
Los tipos de datos que han sido introducidos hasta ahora son enteros, floats, cadenas, y ahora
bools.
Operadores de Comparacin
La lnea 12 tiene una sentencia while:
12. while intentosRealizados < 6:
La expresin que sigue a la palabra reservada while (la parte intentosRealizados < 6)
contiene dos valores (el valor en la variable intentosRealizados, y el valor entero 6)
conectados por un operador (el smbolo <, llamado el smbolo menor que). El smbolo < se
llama un operador de comparacin.
Los operadores de comparacin comparan dos valores y se evalan a un valor Booleano True o
False. En la Tabla 4-1 se muestra una lista de todos los operadores de comparacin.
Table 4-1: Operadores de comparacin.
Signo del Operador
Nombre del Operador
<
Menor que
>
Mayor que
<=
Menor o igual a
>=
Mayor o igual a
==
Igual a
!=
Diferente a
Ya has ledo acerca de los operadores matemticos +, -, *, y /. Como cualquier operador, los
operadores de comparacin se combinan con valores ara formar expresiones tales como
intentosRealizados < 6.
Condiciones
Una condicin es una expresin que combina dos valores con un operador de comparacin (tal
como < o >) y se evala a un valor Booleano. Una condicin es slo otro nombre para una
expresin que se evala a True o False. Las condiciones se usan en sentencias while (y en
algunas otras situaciones, explicadas ms adelante.)
33
0 < 6
True
La condicin 0 < 6 devuelve el valor Booleano True porque el nmero 0 es menor que el
nmero 6. Pero como 6 no es menor que 0, la condicin 6 < 0 se evala a False. 50 no es menor
que 10, luego 50 < 10 es False. 10 es menor que 11, entonces 10 < 11 es True.
Observa que 10 < 10 se evala a False porque el nmero 10 no es ms pequeo que el nmero
10. Son exactamente del mismo tamao. Si Alicia fuera igual de alta que Berto, no diras que
Alicia es ms alta que Berto o que Alicia ms baja que Berto. Ambas afirmaciones seran falsas.
Ahora prueba introducir estas expresiones en la consola interactiva:
>>> 10 == 10
True
>>> 10 == 11
False
>>> 11 == 10
False
34
http://inventwithpython.com/es
>>> 10 != 10
False
>>> 10 != 11
True
>>> 'Hola' == 'Hola'
True
>>> 'Hola' == 'Adios'
False
>>> 'Hola' == 'HOLA'
False
>>> 'Adios' != 'Hola'
True
La Diferencia Entre = y ==
Intenta no confundir el operador asignacin (=) y el operador de comparacin igual a (==). El
signo igual (=) se usa en sentencias de asignacin para almacenar un valor en una variable,
mientras que el signo igual-igual (==) se usa en expresiones para ver si dos valores son iguales. Es
fcil usar uno accidentalmente cuando quieres usar el otro.
Slo recuerda que el operador de comparacin igual a (==) est compuesto por dos caracteres,
igual que el operador de comparacin diferente a (!=) que tambin est compuesto por dos
caracteres.
Cadenas y valores enteros no pueden ser iguales. Por ejemplo, prueba escribiendo lo siguiente en
la consola interactiva:
>>> 42 == 'Hola'
False
>>> 42 != '42'
True
35
El Jugador Adivina
13.
14.
36
http://inventwithpython.com/es
Las lneas 13 a 17 piden al jugador que adivine cul es el nmeo secreto y le permiten formular
su intento. Este nmero se almacena en una variable llamada estimacin.
estimacin = int(estimacin)
En la lnea 15, llamas a una funcin llamada int(). La funcin int() toma un argumento y
devuelve un valor entero de ese argumento. Prueba escribir lo siguiente en la consola interactiva:
>>> int('42')
42
>>> 3 + int('2')
5
La llamada a int('42') devolver el valor entero 42. La llamada int(42) har lo mismo (a
pesar de que no tiene mucho sentido obtener la forma de valor entero de un valor que ya es
entero). Sin embargo, aunque la funcin int() acepta cadenas, no puedes pasarle cualquier
cadena. Pasarle 'cuarenta-y-dos' a int() resultar en un error. La cadena que recibe int()
debe estar compuesta por nmeros.
>>> int('cuarenta-y-dos')
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
int('cuarenta-y-dos')
ValueError: invalid literal for int() with base 10: 'cuarenta-y-dos'
La lnea 3 + int('2') muestra una expresin que usa el valor de retorno de int() como parte
de una expresin. Se evala al valor entero 5:
3 + int('2')
3 + 2
Recuerda, la funcin input() devuelve una cadena de texto que el jugador ha escrito. Si el
jugador escribe 5, la funcin input() devolver el valor de cadena '5', no el valor entero 5.
Python no puede usar los operadores de comparacin < y > para comparar una cadena y un valor
entero:
37
estimacin = input()
estimacin = int(estimacin)
El float(), str(), y bool() funciona de manera similar se volver float, str, y las versiones de
Boole de los argumentos que se pasan a ellos:
>>> float('42')
42.0
>>> float(42)
42.0
>>> str(42)
'42'
>>> str(42.0)
'42.0'
>>> str(False)
'False'
>>> bool('')
False
>>> bool('cualquier cadena no vaca')
True
intentosRealizados = intentosRealizados + 1
Una vez que el jugador ha realizado un intento, el nmero de intentos debera incrementarse en
uno.
38
http://inventwithpython.com/es
En la primera iteracin del bucle, intentosRealizados tiene el valor 0. Python tomar este valor
y le sumar 1. 0 + 1 se evala a 1, el cual se almacena como nuevo valor de
intentosRealizados. Piensa en la lnea 17 como diciendo, la variable intentosRealizados
debera ser uno ms que lo que es ahora.
Sumarle uno al valor entero o float de una variable es lo que se llama incrementar la variable.
Restarle uno al valor entero o float de una variable es decrementar la variable.
Sentencias if
19.
20.
print.
39
La lnea 22 comprueba si la estimacin del jugador es mayor que el entero aleatorio. Si esta
condicin es True, entonces la llamada a la funcin print() indica al jugador que su estimacin
es demasiado alta.
if estimacin == nmero:
break
La lnea 28 no tiene indentacin, lo que significa que el bloque while ha terminado y esta es la
primera lnea luego del mismo. La ejecucin ha abandonado el bloque while, sea porque la
condicin de la sentencia while era False (cuando el jugador se qued sin intentos) o porque se
ejecut la sentencia break (cuando el jugador adivina el nmero correctamente).
40
http://inventwithpython.com/es
La lnea 32 usa el operador comparacin != para comprobar si el ltimo intento del jugador no es
igual al nmero secreto. Si esta condicin se evala a True, la ejecucin se mueve dentro del
bloque if de la lnea 33.
Las lneas 33 y 34 estn dentro del bloque if, y slo se ejecutan si la condicin de la lnea 32 es
True.
33.
34.
nmero = str(nmero)
print('Pues no. El nmero que estaba pensando era ' + nmero)
En este bloque, el programa indica al jugador cul era el nmero secreto que no ha podido
adivinar correctamente. Esto requiere concatenar cadenas, pero nmero almacena un valor entero.
La lnea 33 reemplazar nmero con una forma cadena, de modo que pueda ser concatenada con
la cadena 'Pues no. El nmero que estaba pensando era ' de la lnea 34.
En este punto, la ejecucin ha alcanzado el final del cdigo, y el programa termina.
Felicitaciones! Acabas de programar tu primer juego de verdad!
Puedes cambiar la dificultad del juego modificando el nmero de intentos que el jugador recibe.
Para dar al jugador slo cuatro intentos, cambia esta lnea::
12. while intentosRealizados < 6:
41
Resumen
Si alguien te preguntase Qu es exactamente programar de todos modos?, qu podras
decirle? Programar es simplemente la accin de escribir cdigo para programas, es decir, crear
programas que puedan ser ejecutados por una computadora.
Pero qu es exactamente un programa? Cuando ves a alguien usando un programa de
computadora (por ejemplo, jugando tu juego Adivina el Nmero), todo lo que ves es texto
apareciendo en la pantalla. El programa decide exactamente qu texto mostrar en la pantalla (las
salidas del programa), basado en instrucciones y en el texto que el jugador ha escrito mediante el
teclado (las entradas del programa). Un programa es slo una coleccin de instrucciones que
actan sobre las entradas provistas por el usuario.
Qu tipo de instrucciones? Hay slo unos pocos tipos diferentes de instrucciones, de verdad.
1. Expresiones. Las expresiones son valores conectados por operadores. Todas las
expresiones son evaluadas a un nico valor, as como 2 + 2 se evala a 4 o 'Hola' + '
' + 'Mundo' se evala a 'Hola Mundo'. Cuando las expresiones estn al lado de las
palabras reservadas if y while, pueden recibir tambin el nombre de condiciones.
2. Sentencias de asignacin. Las sentencias de asignacin almacenan valores en variables
para que puedas recordar los valores ms adelante en el programa.
3. Sentencias de control de flujo if, while, y break. Las sentencias de control de flujo
pueden hacer que el flujo omita instrucciones, genere un bucle sobre un bloque de
42
http://inventwithpython.com/es
instrucciones o salga del bucle en el que se encuentra. Las llamadas a funciones tambin
cambian el flujo de ejecucin movindose al comienzo de una funcin.
4. Las funciones print() e input(). Estas funciones muestran texto en la pantalla y
reciben texto del teclado. Esto se llama E/S (o en ingls I/O), porque tiene que ver con las
Entradas y Salidas del programa.
Y eso es todo, slo estas cuatro cosas. Por supuesto, hay muchos detalles acerca de estos cuatro
tipos de instrucciones. En este libro aprenders acerca de nuevos tipos de datos y operadores,
nuevas sentencias de controlo de flujo, y muchas otras funciones que vienen con Python.
Tambin hay diferentes tipos de E/S tales como entradas provistas por el ratn o salidas de sonido
y grficos en lugar de slo texto.
En cuanto a la persona que usa tus programas, slo se preocupa acerca del ltimo tipo, E/S. El
usuario escribe con el teclado y luego ve cosas en la pantalla u oye sonidos de los altavoces. Pero
para que la computadora pueda saber qu imgenes mostrar y qu sonidos reproducir, necesita un
programa, y los programas son slo un manojo de instrucciones que t, el programador, has
escrito.
Captulo 5 Chistes
43
Captulo 5
CHISTES
Temas Tratados En Este Captulo:
Caracteres de escape
Utilizando comillas simples y comillas dobles para las cadenas.
Utilizando el argumento palabra clave final (end) de print() para evitar nuevas lineas
44
http://inventwithpython.com/es
Si obtienes errores despus de escribir este cdigo, compralo con el cdigo del libro con la
herramienta diff en lnea en http://invpy.com/es/diff/chistes.
chistes.py
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
Las lneas de la 1 a la 4 tienen tres llamadas a la funcin print(). No quieres que el jugador lea
de inmediato el remate del chiste, as que hay una llamada a la funcin print() despus del
primer print(). El jugador puede leer la primera lnea, presionar INTRO, y entonces leer el
remate del chiste.
El usuario todava puede escribir una cadena y pulsar INTRO, pero esta cadena devuelta no est
siendo almacenada en ninguna variable. El programa tan solo lo olvidar y se mover a la
siguiente lnea de cdigo.
La ltima llamada a la funcin print() no tiene argumento de cadena. Esto le indica al programa
que solamente escriba una lnea en blanco. Las lneas en blanco pueden ser tiles para evitar que
el texto quede unido.
Captulo 5 Chistes
45
Caracteres de Escape
5.
6.
7.
8.
En el primer print() de arriba, ha una barra invertida justo antes de la comillas simple (esto es,
el apstrofo). Nota que \ es una barra inversa, y / es una barra inclinada. Esta barra inversa
indica que la letra que est a su derecha es una caracter de escape. Un caracter de escape te
permite imprimir caracteres que son difciles de introducir en el cdigo fuente. En esta llamada a
print() el caracter de escape es una comilla simple.
El caracter de escape comilla simple est all porque de otra manera Python pensara que la
comilla indica el final de la cadena. Pero esta comilla necesita formar parte de la cadena. La
comilla simple de escape le indica a Python que la comilla simple es literalmente una parte de la
cadena en lugar de indicar el final del valor de la cadena.
Esto es porque la "t" en "turquesa" fue vista como un caracter de escape debido a que estaba
despus de una barra inversa. El caracter de escape t simula la pulsacin de la tecla TAB de tu
teclado. Hay caracteres de escape para que las cadenas puedan tener caracteres que no se pueden
escribir.
En lugar de eso, prueba con esta lnea:
>>> print('l se fue volando en un helicptero verde\\turquesa.')
l se fue volando en un helicptero verde\turquesa.
46
http://inventwithpython.com/es
Lo Que Imprime
Barra inversa (\)
Comilla simple (')
Comilla doble (")
Salto de lnea
Tabulador
Pero no puedes mexzclar las comillas. Esta lnea devolver un error si intentas utilizarla:
>>> print('Hola mundo")
SyntaxError: EOL while scanning single-quoted string
Me gusta utilizar las comillas simples, as no tengo que pulsar la tecla shift (maysculas) para
escribirlas. Es ms fcil de escribir, y a Python le da igual de cualquier manera.
Del mismo modo en que necesitas el caracter de escape \' para obtener una comilla simple en
una cadena rodeada de comillas simples, se necesita un caracter de escape \" para imprimir una
comilla doble en una cadena rodeada de comillas dobles. Por ejemplo, mira estas dos lneas:
>>> print('Le ped prestado el carro a Pedro pa\'ir al pueblo. El dijo,
"Seguro."')
Le ped prestado el carro a Pedro pa'ir al pueblo. El dijo, "Seguro."
>>> print("l dijo, \"No puedo creer que lo dejaste llevarse el carro pa'l
pueblo\"")
l dijo, "No puedo creer que lo dejaste llevarse el carro pa'l pueblo"
En las cadenas de comillas simples no necesitas escapar las comillas dobles, y en las cadenas de
comillas dobles no necesitas escapar las comillas simples. El intrprete de Python tiene
inteligencia suficiente para saber que si una cadena comienza con un tipo de comillas, el otro tipo
de comillas no significa que la cadena est terminada.
Captulo 5 Chistes
47
Te diste cuenta del segundo parmetro en el print de la lnea 15?. Normalmente, print() aade
un salto de lnea al final de la cadena que imprime. Por esta razn, una funcin print() en
blanco tan solo imprimir una nueva lnea. Pero la funcin print() tiene la opcin de un
segundo parmetro (que tiene nombre end (fin)).
La cadena en blanco dada se llama argumento de palabra clave. El parmetro final tiene un
nombre especfico, y para pasar un argumento a ese parmetro en particular necesitamos utilizar
la sintxis end=.
Pasando una cadena en blanco usando end, la funcin print() no aadir un salto de linea al
final de la cadena, en lugar de esto aadir una cadena en blanco. Por esta razn ' -Bien,
gracias.' aparece junto a la lnea anterior, en lugar de sobre una nueva lnea. No hubo salto de
lnea despus de la cadena 'Y la familia?'.
Resumen
Este captulo explora las diferentes formas en las que se puede utilizar la funcin print(). Los
caracteres de escape se utilizan para los caracteres que son difciles o imposibles de escribir en
cdigo usando el teclado. Los caracteres de escape se escriben en las cadenas comienzando con
una barra inversa \ seguida de una sola letra para el carcter de escape. Por ejemplo, \n sera un
salto de lnea. Para incluir una barra invertida en una cadena, debers utilizar el carcter de escape
\\.
La funcin print() aade automticamente un carcter de salto de lnea al final de la cadena que
se pasa para imprimr en pantalla. La mayor parte del tiempo, es un atajo til. Pero a veces no
quieres un carcter de salto de lnea al final. Para cambiar esto, puedes pasar el argumento de
palabra clave end con una cadena en blanco. Por ejemplo, para imprimir spam en la pantalla sin
un carcter de salto de lnea, podras hacer el llamado print('spam', end='').
Al aadir este nivel de control sobre el texto que mostraremos en la pantalla, puedes tener formas
ms flexibles para hacerlo.
48
http://inventwithpython.com/es
Captulo 6
REINO DE DRAGONES
Temas Tratados En Este Captulo:
La funcin time.sleep()
Creando nuestras propias funciones con la palabra reservada def
La palabra reservada return
Los operadores Booleanos and, or y not
Tablas de verdad
Entorno de variables (Global y Local)
Parmetros y Argumentos
Diagramas de Flujo
Las Funciones
Ya hemos usado dos funciones en nuestros programas anteriores: input() y print(). En los
programas anteriores, hemos llamado a estas funciones para ejecutar el cdigo dentro de ellas. En
este captulo, escribiremos nuestras propias funciones para que sean llamadas por programas. Una
funcin es como un mini-programa dentro de nuestro programa.
El juego que crearemos para presentar las funciones se llama "Reino de Dragones", y permite al
jugador elegir entre dos cuevas, en una de las cuales encontrar un tesoro y en la otra su
perdicin.
49
dragn.py
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
import random
import time
def mostrarIntroduccin():
print('Ests en una tierra llena de dragones. Frente a t')
print('hay dos cuevas. En una de ellas, el dragn es generoso y')
print('amigable y compartir su tesoro contigo. El otro dragn')
print('es codicioso y est hambriento, y te devorar inmediatamente.')
print()
def elegirCueva():
cueva = ''
while cueva != '1' and cueva != '2':
print('A qu cueva quieres entrar? (1 2)')
cueva = input()
return cueva
50
http://inventwithpython.com/es
18.
19. def explorarCueva(cuevaElegida):
20.
print('Te aproximas a la cueva...')
21.
time.sleep(2)
22.
print('Es oscura y espeluznante...')
23.
time.sleep(2)
24.
print('Un gran dragon aparece sbitamente frente a t! Abre sus fauces
y...')
25.
print()
26.
time.sleep(2)
27.
28.
cuevaAmigable = random.randint(1, 2)
29.
30.
if cuevaElegida == str(cuevaAmigable):
31.
print('Te regala su tesoro!')
32.
else:
33.
print('Te engulle de un bocado!')
34.
35. jugarDeNuevo = 's'
36. while jugarDeNuevo == 's' or jugarDeNuevo == 's':
37.
38.
mostrarIntroduccin()
39.
40.
nmeroDeCueva = elegirCueva()
41.
42.
explorarCueva(nmeroDeCueva)
43.
44.
print('Quieres jugar de nuevo? (s o no)')
45.
jugarDeNuevo = input()
51
Sentencias def
4. def mostrarIntroduccin():
5.
print('Ests en una tierra llena de dragones. Frente a t')
6.
print('hay dos cuevas. En una de ellas, el dragn es generoso y')
7.
print('amigable y compartir su tesoro contigo. El otro dragn')
8.
print('es codicioso y est hambriento, y te devorar inmediatamente.')
9.
print()
La lnea 4 es una sentencia def. La sentencia def crea, es decir, una nueva funcin que puede ser
llamada ms adelante en el programa. Luego de haber definido esta funcin, puedes llamarla de
la misma forma en que llamas a otras funciones. Cuando llamas a esta funcin, el cdigo dentro
del bloque def se ejecuta.
La Figura 6-1 muestra las partes de una sentencia def. Comienza con la palabra reservada def
seguida por un nombre de funcin con parntesis y luego dos puntos. El bloque a continuacin de
la sentencia def se llama el bloque def.
mostrarIntroduccin()
Entonces todas las llamadas a print() se ejecutan, y se muestra la introduccin Ests en una
tierra llena de dragones....
52
http://inventwithpython.com/es
La lnea 11 define otra funcin llamada elegirCueva(). El cdigo de esta funcin pregunta al
jugador a qu cueva quiere entrar, 1 2.
12.
13.
cueva = ''
while cueva != '1' and cueva != '2':
Esta funcin necesita asegurar que el jugador haya respondido 1 2, y no otra cosa. Un bucle
aqu seguir preguntando al jugador hasta que escriba alguna de estas dos respuestas vlidas. Esto
se llama validacin de entrada.
La lnea 12 crea una nueva variable llamada cueva y guarda en ella una cadena vaca. Luego un
bucle while comienza en la lnea 13. La condicin contiene un nuevo operador que no has visto
53
antes llamado and (y). Igual que los signos - o * son operadores matemticos y los signos == o !=
son operadores de comparacin, el operador and es un operador Booleano.
Operadores Booleanos
La lgica Booleana se ocupa de enunciados que son verdaderas (True) o falsos (False). Los
operadores Booleanos comparan dos valores Booleanos y se evalan a un nico valor Booleano.
Piensa en este enunciado, Los gatos tienen bigotes y los perros tienen colas. Los gatos tienen
bigotes es verdadero y los perros tienen colas tambin es verdadero, luego el enunciado
completo Los gatos tienen bigotes y los perros tienen colas es verdadero.
Pero el enunciado Los gatos tienen bigotes y los perros tienen alas sera falso. Incluso si los
gatos tienen bigotes es verdadero, los perros no tienen alas, luego los perros tienen alas es
falso. En lgica Booleana, los enunciados slo pueden ser completamente verdaderos o
completamente falsos. Debido a la conjuncin y, el enunciado completo es verdadero slo si
ambas partes son verdaderas. Si una o ambas partes son falsas, entonces el enunciado completo
es falso.
El operador or es similar al operador and, excepto que se evaluar a True si cualquiera de los dos
valores Booleanos es True. La nica vez en que el operador or se evala a False es si los dos
valores Booleanos son False.
Prueba escribir lo siguiente en la consola interactiva:
54
http://inventwithpython.com/es
El Operador
not
El operador not slo acta sobre un valor, en lugar de combinar dos valores. El operador not (no)
se evala al valor Booleano opuesto. La expresin not True se evaluar a False y not False se
evaluar a True.
Prueba escribir lo siguiente en la consola interactiva:
>>> not True
False
>>> not False
True
>>> not ('negro' == 'blanco')
True
Tablas de Verdad
Si alguna vez te olvidas cno funcionan los operadores Booleanos, puedes mirar estas tablas de
verdad:
Tabla 6-1: La tabla de verdad del operador and.
A
and
B
is
Enunciado completo
True
and
True
es
True
True
and
False
es
False
False
and
True
es
False
False
and
False
es
False
55
La condicin tiene dos partes conectadas por el operador Booleano and. La condicin es True
slo si ambas partes son True.
La primera vez que se comprueba la condicin de la sentencia while, cueva est definida como
la cadena vaca, ''. La cadena vaca no es igual a la cadena '1', luego el lado izquierdo se evala
a True. La cadena vaca tampoco es igual a la cadena '2', por lo que el lado derecho se evala a
True.
Entonces la condicin se transforma en True and True. Como ambos valores Booleanos son
True, la condicin finalmente se evala a True. Luego la ejecucin del programa entra al bloque
while.
As es como se ve la evaluacin de la condicin (si el valor de cueva es la cadena vaca):
while cueva != '1' and cueva != '2':
while
True
and cueva != '2':
while
True
and '' != '2':
56
http://inventwithpython.com/es
while
while
True
and
True:
True:
La lnea 14 pregunta al jugador qu cueva quiere elegir. La lnea 15 permite al jugador escribir la
respuesta y pulsar INTRO. Esta respuesta es almacenada en cueva. Despus de ejecutar este
cdigo, la ejecucin vuelve a la parte superior de la sentencia while y vuelve a comprobar la
condicin.
Si el jugador ha ingresado 1 2, entonces cueva ser '1' or '2' (ya que input() siempre
devuelve cadenas). Esto hace que la condicin sea False, y la ejecucin del programa continuar
debajo del bucle while. Por ejemplo, si el usuario escribiese '1' la evaluacin se vera as:
while cueva != '1' and cueva != '2':
while
'1' != '1' and cueva != '2':
while
False
and cueva != '2':
while
False
and '1' != '2':
while
False
and
True:
while
False:
Pero si el jugador hubiese escrito 3 o 4 o HOLA, esa respuesta habra sido invlida. La condicin
seguira siendo True y entrando al bloque while para preguntar de nuevo al jugador. El programa
simplemente contina preguntando hasta que el jugador responda 1 or 2. Esto garantiza que
cuando la ejecucin contine avanzando la variable cueva contendr una respuesta vlida.
Retorno de Valores
17.
return cueva
Esta es una sentencia return, la cual slo aparece dentro de bloques def. Recuerdas como la
funcin input() devuelve un valor de cadena que el jugador ha ingresado? La funcin
elegirCueva()
57
nmeroDeCueva = elegirCueva()
58
http://inventwithpython.com/es
variable local llamada spam al mismo tienpo que existe una variable global llamada spam. Python
las considerar dos variables distintas.
Mira el siguiente ejemplo para ver qu pasa cuando intentas modificar una variable global desde
dentro de un entorno local. Los comentarios explican qu es lo que est ocurriendo:
def bacon():
# Creamos una variable local llamada "spam"
# en lugar de cambiar el valor de la
# variable global "spam":
spam = 99
# El nombre "spam" se refiere ahora slo a la
# variable local por el resto de esta
# funcin:
print(spam)
# 99
spam = 42 # Una variable global llamada "spam":
print(spam) # 42
bacon() # Llama a la funcin bacon():
# La variable global no fue cambiada en bacon():
print(spam)
# 42
Dnde se crea una variables determina en qu entorno se encuentra. Cuando el programa Reino
de Dragones ejecuta por primera vez la lnea:
12.
cueva = ''
...la variable cueva se crea dentro de la funcin elegirCueva(). Esto significa que es creada en el
entorno local de la funcin elegirCueva(). Ser olvidada cuando elegirCueva() finalice, y ser
recreada si elegirCueva() es llamada por segunda vez. El valor de una variable local no es
recordado entre una llamada a una funcin local y otra.
Parmetros
19. def explorarCueva(cuevaElegida):
59
Cuando llamas a decirHola(), el argumento se asigna al parmetro nombre. Los parmetros son
simplemente variables locales ordinarias. Como todas las variables locales, los valores en los
parmetros sern olvidados cuando la llamada a la funcin retorne.
60
http://inventwithpython.com/es
El mdulo time tiene una funcin llamada sleep() que pone al programa en pausa. La lnea 21
pasa el valor entero 2 de modo que time.sleep() pondr al programa en pausa por 2 segundos.
22.
23.
Aqu el cdigo imprime algo ms de texto y espera por otros 2 segundos. Estas pequeas pausas
agregan suspenso al juego, en lugar de mostrar todo el texto a la vez. En el programa Chistes del
captulo anterior, has llamado a la funcin input() para poner el juego en pausa hasta que el
jugador pulsara la tecla INTRO. Aqu, el jugador no tiene que hacer nada excepto esperar un par de
segundos.
24.
y...')
25.
26.
cuevaAmigable = random.randint(1, 2)
if cuevaElegida == str(cuevaAmigable):
print('Te regala su tesoro!')
61
if int(cuevaElegida) == cuevaAmigable:
else:
print('Te engulle de un bocado!')
La lnea 32 es una sentencia else (si no). La palabra reservada else siempre viene a continuacin
del bloque if. El bloque else se ejecuta si la condicin de la sentencia if fue False. Piensa en esto
como la forma del programa de decir, Si esta condicin es verdadera entonces ejecuta el bloque
if, en caso contrario ejecuta el bloque else.
Recuerda colocar los dos puntos (el signo : ) luego de la palabra reservada else.
La lnea 35 es la primera lnea que no es una sentencia def ni pertenece a un bloque def. Esta
lnea es donde la parte principal del programa comienza. Las sentencias def anteriores slo
definen las funciones, pero sin ejecutarlas.
Las lneas 35 y 36 configuran un bucle que contiene al resto del juego. Al final del juego, el
jugador puede escribir si desea jugar de nuevo. Si es as, la ejecucin vuelve a entrar al bucle
while para ejecutar todo el juego otra vez. En caso contrario, la condicin de la sentencia while
ser False y la ejecucin continuar hasta el final del programa y terminar.
La primera vez que la ejecucin llega a esta sentencia while, la lnea 35 ha acabado de asignar
's' a la variable jugarDeNuevo. Esto significa que la condicin ser True. De esta forma se
garantiza que la ejecucin entrar al bucle al menos una vez.
mostrarIntroduccin()
62
40.
http://inventwithpython.com/es
nmeroDeCueva = elegirCueva()
La lnea 40 tambin llama a una funcin que t has definido. Recuerda que la funcin
elegirCueva() permite al jugador elegir la cueva a la que desea entrar. Cuando se ejecuta
return cueva en la lnea 17, la ejecucin del programa vuelve a la lnea 40, y la llamada a
elegirCueva() se evala al valor de retorno. Este valor de retorno es almacenado en una nueva
variable llamada nmeroDeCueva. Entonces la ejecucin del programa contina en la lnea 42.
42.
explorarCueva(nmeroDeCueva)
Sin importar si el jugador gana o pierde, se le pregunta si quiere jugar de nuevo. La variable
jugarDeNuevo almacena lo que haya ingresado el jugador. La lnea 45 es la ltima lnea del
bloque while, de modo que el programa vuelve a la lnea 36 para comprobar la condicin del
bucle while: jugarDeNuevo == 's' or jugarDeNuevo == 's'
Si el jugador ingresa la cadena 's' o 's', la ejecucin entrar nuevamente al bucle en la lnea
38.
Si el jugador ingresa 'no' o 'n', o una tontera como 'Abraham Lincoln', entonces la condicin
ser False. La ejecucin del programa contina a la lnea a continuacin del bloque while. Pero
dado que no hay ms lneas despus del bloque while, el programa termina.
Una cosa a tener en cuenta: la cadena 'S' no es igual a la cadena 's'. Si el jugador ingresa la
cadena 'S', entonces la condicin de la sentencia while se evaluar a False y el programa
terminar igualmente. Otros programas ms adelante en este libro te mostrarn cmo evitar este
problema.
Acabas de completar tu segundo juego! En Reino de Dragones, has usado mucho de cuanto
aprendiste en el juego Adivina el Nmero y has aprendido unos cuantos trucos nuevos. Si no
63
entendiste algunos de los conceptos en este programa, recorre cada lnea del cdigo fuente otra
vez e intenta modificar el cdigo fuente viendo cmo cambia el programa.
En el siguiente captulo no crears un juego, pero aprenders cmo usar una funcionalidad de
IDLE llamada depurador.
Diseando el Programa
Reino de Dragones es un juego simple. El resto de los juegos en este libro sern un poco ms
complicados. A veces ayuda escribir en papel todo lo que quieres que tu juego o programa haga
antes de comenzar a escribir el cdigo. Esto se llama disear el programa.
Por ejemplo, puede ayudar dibujar un diagrama de flujo. Un diagrama de flujo es una ilustracin
que muestra cada posible accin que puede ocurrir en el juego, y qu acciones llevan a qu otras
acciones. La Figura 6-2 es un diagrama de flujo para Reino de Dragones.
Para ver qu pasa en el juego, coloca tu dedo sobre el recuadro Inicio. Luego sigue una flecha
desde ese recuadro hasta otro recuadro. Tu dedo es como la ejecucin del programa. El programa
termina cuando tu dedo llega al recuadro Fin.
Cuando llegas al recuadro Comprobar dragn amistoso o hambriento, puedes ir al recuadro
Jugador gana o al recuadro Jugador pierde. Esta bifurcacin muestra cmo el programa
puede hacer diferentes cosas. De cualquier forma, ambos caminos conducirn al recuadro Ofrece
jugar de nuevo.
Resumen
En el juego Reino de Dragones, has creado tus propias funciones. Las funciones son un miniprograma dentro de tu programa. El cdigo dentro de la funcin se ejecuta cuando la funcin es
llamada. Al descomponer tu cdigo en funciones, puedes organizar tu cdigo en secciones ms
pequeas y fciles de entender.
Los argumentos son valores pasados al cdigo de la funcin cuando la funcin es llamada. La
propia llamada a la funcin se evala al valor de retorno.
64
http://inventwithpython.com/es
65
Captulo 7
USANDO EL DEPURADOR
Los tpicos cubiertos en este captulo:
3 tipos diferentes de errores
Depurador de IDLE
Entrar en, sobre, salir
Ir y salir
Puntos de quiebre
Bugs!
En dos ocaciones me han preguntado 'Reza, Sr. Babbage si pones en la mquina las figuras
incorrectas, saldrn las respuestas correctas?' No consigo comprender correctamente el grado de
confusin de ideas que puedan provocar dicha pregunta.
-Charles Babbage, originador del concepto de una computadora programable, siglo 19.
Si ingresas el cdigo errneo, la computadora no dar el programa correcto. Un programa de
computadora siempre har lo que tu le digas, pero lo que tu le digas al programa que haga puede
que no sea lo que tu quieres que haga. Estos errores son bugs de un programa de computadora.
Los bugs ocurren cuando el programador no pens cuidadosamente lo que el programa hace. Hay
tres tipos de bugs que pueden ocurrir en tu programa:
66
http://inventwithpython.com/es
Ahora presione y mantenga la tecla Ctrl y presiona la tecla C para parar el programa. La consola
interactiva se ver as:
Presiona Ctrl-C para parar este ciclo infinito!!!
Presiona Ctrl-C para parar este ciclo infinito!!!
Presiona Ctrl-C para parar este ciclo infinito!!!
Presiona Ctrl-C para parar este ciclo infinito!!!
Presiona Ctrl-C para parar este ciclo infinito!!!
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
while True: print('Presiona Ctrl-C para parar este ciclo infinito!!!')
KeyboardInterrupt
El Depurador
Puede ser difcil darse cuenta cmo el cdigo est causando un bug. Las lneas de cdigo se
ejecutan rpidamente y los valores en las variables cambian frecuentemente. Un depurador es un
programa que te permite correr tu programa una lnea de cdigo a la vez en el mismo orden que
Python. En depurador tamben muestra en cada paso cuales son los valores almacenados en las
variables.
67
Iniciando el Depurador
Luego de abrir el archivo dragn.py, presona Debug Debugger para hacer aparecer el Debug
Control (Control de Depuracin) (Figura 7-1).
68
http://inventwithpython.com/es
Ahora cuando corras el juego Reino de Dragones presionando F5, el depurador IDLE se activar.
Esto es conocido como correr un programa bajo un depurador. En la Ventana de Debug Control
(Control de Depuracin), selecciona los campos Source y Globals.
Cuando corres programas en Python con el depurador activado, el programa se frenar antes de
ejecutar la primer lnea de cdigo. Si presionas sobre la barra del ttulo del editor del archivo (y
has seleccionado el campo Source en la ventana del Control de Depuracin), la primera lnea de
codigo estar resaltada en gris. La ventana del Control de Depuracin muestra que la ejecucin se
encuentra en la lnea 1, la cual es import random.
Paso a Paso
El depurador te permite ejecutar una lnea de cdigo a la vez, llamado paso a paso (stepping en
ingls). Para ejecutar una sola instruccin, presiona el botn Step en la ventana del Depurador.
V y hazlo ahora. Python ejecutar la instruccin import random, y luego parar antes de ejecutar
la prxima instruccin. La ventana de control ahora mostrar que la ejecuccin ahora se
encuentra en la lnea 2, en import time. Presiona el botn Quit (Salir) para terminar el programa
por ahora.
Aqu hay un resumen de lo que pasa cuando presionas el botn Step mientras corres el juego
Reino de Dragones bajo el depurador. Presiona F5 para correr Reino de Dragones otra vez, luego
sigue estas instrucciones:
1.
2.
3.
4.
Presiona el botn Step dos veces para ejecutar las dos lneas de import.
Presiona el botn Step otras tres veces para ejecutar las tres declaraciones def.
Presiona el botn Step otra vez para definir la variable jugarDeNuevo.
Presiona Go para correr el resto del programa, o presiona Quit para terminar el mismo.
La ventana del Control de Depuracin mostrar que linea est por ser ejecutada cuando presiones
Step. El depurador salte la lnea 3 debido a que es una lnea en blanco. Notar que slo se puede
avanzar con el depurador, no puedes retroceder.
rea Globales
El rea de Globales en la ventana de control del depurador es donde se guardan todas las
variables globales. Recuerda, las variables globales son aquellas creadas fuera de cualquier
funcin (es decir, de alcalce global).
Debido a que las tres sentencias def ejecutan y definen funciones, aparecern en el area de
globales.
69
E texto junto a los nombres de las funciones se ver como "<function explorarCueva at
0x012859B0>". Los nombres de mdulos tambin tienen texto de aspecto confuso junto a ellos,
tales como <module 'random' from 'C:\\Python31\\lib\\random.pyc'>. Esta informacin
detallada es til para los programadores avanzados, pero no necesitas saber que significa para
depurar tus programas. Tan slo con ver que las funciones y los mdulos se encuentran en el rea
de globales te dir que la funcin fue definida o el mdulo importado.
Tambin puedes ingorar las lneas __builtins__, __doc__, and __name__. (Son variables que
aparecen en todo programa en Python.)
Cuando la variable jugarDeNuevo es creada, aparecer en la seccin Global. A su lado aparecer
el valor alojado en ella, la cadena 'si'. El depurador te permite ver los valores de todas las
variables en el programa mientras el mismo corre. Esto es til para solucionar bugs en tu
programa.
rea Locales
Existe tambin un rea Local, la cul muestra el mbito local de las variables y sus valores. El
rea local slo tendr variables cuando la ejecucin del programa se encuentre dentro de una
funcin. Cuando la ejecucin se encuentre en el mbito global, esta rea estar en blanco.
70
http://inventwithpython.com/es
71
V a la consola interactiva y tipea cul cueva deseas explorar. El cursor parpadeante debe estar en
la lnea inferior en la consola interactva antes de que puedas tipear. Caso contrario el texto que
ingreses no aparecer.
Una vez que presiones INTRO, el depurador continuar el paso sobre las lneas. Presiona el botn
Out en la ventana de control. A esto se le llama Salir (Stepping Out) porque har que el
depurador corra cuantas lneas sean necesarias hata salir de la funcin en la que se ecuentra.
Luego de que sale, la ejecucin debe estar en la lnea siguiente a la lnea que llam la funcin.
Por ejemplo, al presionar Out dentro de la funcin mostrarIntroduccin() en la lnea 6, se
correr hasta que la funcin retorne a la lnea posterior a la llamada a mostrarIntroduccin().
Si no te encuentras dentro de una funcin, presionar Out har que el depurador ejecute todas las
lneas restantes del programa. Este es el mismo comportamiento a presionar el botn Go.
Aqu un resumen de lo que cada botn hace:
Go - Ejecuta el resto del cdigo normalmente, o hasta que alcanza un punto de quiebre
(break, que ser descripto luego).
Step - Ejecuta una lnea de cdigo. Si la lnea es una llamada a una funcin, el depurador
ingresar dentro de la funcin.
Over - Ejecuta una lnea de cdigo. Si la lnea es una llamada a una funcin, el depurador
no ingresar dentro de la funcin.
Out - Ejecuta lneas de cdigo hasta que el depurador salga de la funcin en la que estaba
cuando se presion Out. Esto sale de la funcin.
Quit - Termina el programa inmediatamente.
Encuentra el Bug
El depurador puede ayudarte a encontrar la causa de bugs en tu programa. Por ejemplo, aqu hay
un pequeo programa con un bug. El programa brinda un problema de suma aleatoria para que el
usuario resuelva. En la consola interactiva, presiona en File, luego en New Window para abrir un
nuevo editor de archivos. Tipea este programa en dicha ventana, y guarda el programa como
bugs.py.
bugs.py
1.
2.
3.
4.
5.
6.
import random
numero1 = random.randint(1, 10)
numero2 = random.randint(1, 10)
print('Cunto es ' + str(numero1) + ' + ' + str(numero2) + '?')
respuesta = input()
if respuesta == numero1 + numero2:
72
http://inventwithpython.com/es
7.
print('Correcto!')
8. else:
9.
print('Nops! La respuesta es ' + str(numero1 + numero2))
Tipea el programa exctamente como se muestra, incluso si ya sabes cul es el bug. Luego intenta
corer el programa presionando F5. Este es una simple pregunta aritmetica que te pide sumer dos
nmeros aleatorios. Aqu es lo que es posible que veas al correr el programa:
Cunto es 5 + 1?
6
Nops! La respuesta es 6
El depurador comenzar en la lnea import random. Nada especial sucede aqu, as que presiona
Step para ejecutarlo. Vers que el mdulo random es agregado al rea de globales (Globals).
2. numero1 = random.randint(1, 10)
Presiona Step otra vez para ejecutar la lnea 2. Una nueva ventana de edicin aparecera con el
archivo random.py . Has ingresado dentro de la funcin randint() dentro del mdulo random.
Las funciones incorporadas en Python no sern fuente de tus errores, as que puedes presionar
Out para salir de la funcin randint() y volver a tu programa. Luego cierra la ventana de
random.py.
3. numero2 = random.randint(1, 10)
La prxima vez, puedes presionar Over para saltar la funcin randint() en vez de ingresar en
ella. La lnea 3 tambin es una llamada a randint(). Evita ingresar en su cdigo presionando
Over.
4. print('Cunto es ' + str(numero1) + ' + ' + str(numero2) + '?')
73
La lnea 4 es una llamada a print() para mostrarle al jugador los nmeros aleatorios. Tu sabes
que nmeros el programa mostrar incluso antes de que los imprima! Tan slo mira el rea de
globales en la ventana de contro. Puedes ver las variables numero1 y numero2, y a su lado los
valores enteros guardados en ellas.
La variable numero1 posee el valor 4 y la variable numero2 el valor 8. Cuando presiones Step, el
programa mostrar la cadena en la llamada print() con estosvalores. La funcin str()
concatenar las versiones cadena de estos enteros. Cuando corr el depurador, se vi como la
Figura 7-4. (Tus valores aleatorios probablemente sean diferentes.)
Presionando Step desde la lnea 5 ejecutar input(). El depurador esperar hasta que el jugador
ingrese una respuesta al programa. Ingresa la respuesta correcta (en mi caso, 19) en la consola
interactiva. El depurador continuar y se mover a la lnea 6.
6. if respuesta == numero1 + numero2:
7.
print('Correcto!')
La lnea 6 es un condicional if. La condicin es que el valor en la respuesta debe coincidir con
la suma de numero1 y numero2. Si la condicion es True, el depurador se mover a la lnea 7. Si es
74
http://inventwithpython.com/es
False,
el depurador se mover a la lnea 9. Presiona Step una vez mas para descubrir adonde se
mover.
8. else:
9.
print('Nops! La respuesta es ' + str(numero1 + numero2))
Esta vez, el programa funcion correctamente. Crrelo una vez ms e ingresa una respuesta
errnea a propsito. Esto comprobar el programa completamente. Ahora habrs depurado este
programa! Recuerda, la computadora correr tus programas exactamente como los tipeaste,
incluso si lo que tipeaste no es lo que querias.
Puntos de Quiebre
Ejecutar el cdigo una lnea a la vez puede ser demasiado lento. Con frecuencia quieres correr el
programa normalmente hasta que alcance cierta lnea. Un punto quiebre se establece en una lnea
donde quieres que el depurador tome el control una vez que la ejecucin alcanz dicha lnea. Si
crees que hay un programa en tu cdigo, digamos, en la lnea 17, tan slo estableces un punto de
quiebre en esa lnea (o tal vez unas lneas atrs).
Cuando la ejecucin alcance esa lnea, el depurador romper hacia el depurador. Luego podrs
correr las lneas una a la vez para ver que sucede. Presionar Go ejecutar el programa
normalmente hasta que alcance otro punto quiebre o el final del programa.
Para establecer un punto quiebre, en el editor de texto haz click derecho sobre una lnea y
selecciona Set Breakpoint en el men. Ahora el editor resaltar la lnea en amarillo. Puedes
75
establecer tantos puntos quiebre como desees. Para remover uno, clickea en la lnea y selecciona
Clear Breakpoint en el men que aparece.
lanzarMoneda.py
1. import random
2. print('Lanzar una moneda 1000 veces. Adivina cuantas veces caer Cara.
(Presiona enter para comenzar)')
3. input()
4. lanzamientos = 0
5. caras = 0
6. while lanzamientos < 1000:
7.
if random.randint(0, 1) == 1:
8.
caras = caras + 1
9.
lanzamientos = lanzamientos + 1
10.
11.
if lanzamientos == 900:
12.
print('900 lanzamientos y hubo ' + str(caras) + ' caras.')
76
http://inventwithpython.com/es
13.
if lanzamientos == 100:
14.
print('En 100 lanzamientos, cara sali ' + str(caras) + ' veces.')
15.
if lanzamientos == 500:
16.
print('La mitad de los lanzamientos y cara sali ' + str(caras) + '
veces.')
17.
18. print()
19. print('De 1000 lanzamientos, al final cara sali ' + str(caras) + '
veces!')
20. print('Estuviste cerca?')
El programa corre bastante rpido. Toma ms tiempo esperar a que el usuario presione INTRO que
realizar los lanzamientos. Digamos que deseamos ver los lanzamientos de moneda uno a uno. En
la consola interactiva, presiona Debug Debugger para abrir la ventana de control del
depurador. Luego presiona F5 para correr el programa.
El programa comienza dentro del depurador en la lnea 1. Presiona Step tres veces en la ventana
de control para ejecutar las primeras tres lneas (estas son, lneas 1, 2 y 3). Notaras que los
botones se deshabilitaran porque la funcin input() fue llamada y la consola interactiva est
esperando al usuario. Clickea en la ventana de la consola y presiona INTRO. (Asegrate de
presionar debajo del texto en la consola interactiva, de lo contrario puede que IDLE no reciba tu
tecla.)
Puedes presionar Step un par de veces mas, pero te encontars que tardar un tiempo atravesar
todo el programa. En vez, establece un punto de quiebre en las lneas 12, 14 y 16. El editor
resaltar estas tres lneas como se muestra en la Figura 7-6.
77
tenemos un break point) se ejecute, lo que le dice al depurador que frene el programa y tome el
control. Mira la ventana de control del depurador en la seccin de Globales para ver cul es el
valor de lanzamientos y caras.
Presiona nuevamente Go y el programa continuar hasta el siguiente punto quiebre en la lnea 16.
Otra vez, mira cmo los valores en lanzamientos y caras han cambiado.
Si presionas Go otra vez, la ejecucin continuar hasta el ltimo punto quiebre en la lnea 12.
Resumen
Escribir un programa es slo la primer parte de programar. La siguiente parte es cerciorarse que
lo escrito realmente funciona. Los depuradores te permiten atravesar el cdigo una lnea a la vez.
Puedes examinar qu lineas se ejecutan en qu orden, y qu valores contienen las variables.
Cuando esto es demasiado lento, puedes establecer puntos de quiebre para frenar el depurador
slo en las lnas que deseas.
Utilizar el depurador es una gran forma de entender exactamente lo que el programa est
haciendo. Mientras que este libro explica todo el cdigo dentro del mismo, el depurador puede
ayudarte a encontrar ms por tu cuenta.
78
http://inventwithpython.com/es
Captulo 8
DIAGRAMAS DE FLUJO
Temas Tratados En Este Captulo:
Cmo jugar al Ahorcado
Arte ASCII
Diseo de un Programa mediante Diagramas de Flujo.
En este captulo, disears el juego del Ahorcado. Este juego es ms complicado que nuestro
juego anterior, pero tambin ms divertido. Como este juego es avanzado, deberas planearlo
cuidadosamente antes creando un diagrama de flujo (explicado ms adelante). En el siguiente
captulo, escribiremos el cdigo para el Ahorcado.
_ _ _ _
Adivina una letra.
o
+---+
|
|
|
|
|
|
=========
Letras incorrectas:
_ o _ o
Adivina una letra.
l
+---+
|
|
O
|
|
|
|
=========
Letras incorrectas: l
_ o _ o
Adivina una letra.
r
+---+
|
|
O
|
|
|
|
|
=========
Letras incorrectas: lr
_ o _ o
Adivina una letra.
n
+---+
|
|
O
|
|
|
|
|
=========
Letras incorrectas: lr
_ o n o
Adivina una letra.
o
79
80
http://inventwithpython.com/es
Arte ASCII
Los grficos para el Ahorcado son caracteres del teclado impresos en la pantalla. Este tipo de
grficos se llama arte ASCII (se pronuncia asqui), y fue una especie de precursor a emojii. Aqu
hay un gato dibujado con arte ASCII:
__________________
_____/
xx
xxx
\_____
_/xxx
xx
xxx
xxx
\__
__/
xxx
xxx
xx
xxx
\__
/xxxxxxxxx
xx
xx
xx
xx
xxx\
/
xx /\
xx
xx\
/
/ \
x
xx \
|
/\
|
\
xx x\
|
| \
|
\____
Z
x
\
|
|
\____/
\
z
xxx
|
|
|
\ z
|
\/
\
\
/
____/
|
|
__|
\____
|
xxx|
/ |
___
___------__/
x|
/
|
|
|
_______
____/
|
| o\
-------\_/
_/
___/
xx /
|oo \
_____/
_/______/
xx/
\
\__
__/
xx /
\
\______________/
x_/
\____
_______/
\_______________________________/
81
Un diagrama de flujo es un diagrama que muestra una serie de pasos como recuadros conectados
por flechas. Cada recuadro representa un paso, y las flechas muestran qu pasos llevan a qu otros
pasos. Coloca tu dedo sobre el recuadro "Inicio" del diagrama de flujo y recorre el programa
siguiendo las flechas a los otros recuadros hasta que llegues al recuadro Fin.
La Figura 8-1 es un diagrama de flujo completo para el Ahorcado. Slo puedes moverte de un
recuadro a otro en la direccin de la flecha. Nunca puedes volver hacia atrs a menos que haya
una segunda flecha apuntando en direccin opuesta, como en el recuadro El jugador ya ha
probado esa letra.
82
http://inventwithpython.com/es
Figura 8-2: Comienza tu diagrama de flujo con los recuadros Inicio y Fin.
83
Ahora piensa en lo que ocurre cuando juegas al Ahorcado. Primero, la computadora piensa en una
palabra secreta. Luego el jugador intentar adivinar las letras. Agrega recuadros para estos
eventos, como se muestra en la Figura 8-3. Los recuadros que son nuevos para cada diagrama de
flujo se dibujan con bordes en lnea quebrada.
Las flechas muestran el orden en que el programa debera moverse. Es decir, primero el programa
debera generar una palabra secreta, y luego de eso debera invitar al jugador a adivinar una letra.
Figura 8-3: Dibuja los dos primeros pasos del Ahorcado como recuadros con descripciones.
Pero el juego no termina despus de que el jugador prueba una letra. Necesita comprobar si esa
letra pertenece o no a la palabra secreta.
84
http://inventwithpython.com/es
85
Si la letra est en la pantalla secreta, comprueba si el jugador ha adivinado todas las letras y ha
ganado. Si la letra no est en la palabra del juego, se agrega otra parte del cuerpo al ahorcado.
Agrega recuadros para esos casos tambin.
No necesitas una flecha desde el casillero La letra est en la palabra secreta al casillero El
jugador se ha quedado sin partes del cuerpo y pierde, porque es imposible perder mientras el
jugador acierte. Tambin es imposible ganar mientras el jugador no acierte, de modo que tampoco
precisamos dibujar esa otra flecha. El diagrama de flujo se ve ahora como la Figura 8-5.
Figura 8-5: Luego de la ramificacin, los pasos continan por caminos separados.
86
http://inventwithpython.com/es
Figura 8-6: El diagrama de flujo se ramifica al preguntar al jugador si quiere jugar de nuevo.
87
Adivinando Nuevamente
El jugador no adivina una letra slo una vez. Tiene que continuar probando letras hasta que gane
o pierda. Necesitars dibujar dos nuevas flechas, como se muestra en la Figura 8-7.
Figura 8-7: Las nuevas flechas (resaltadas) denotan que el jugador puede adivinar otra vez.
88
http://inventwithpython.com/es
Qu ocurre si el jugador prueba la misma letra ms de una vez? En lugar de ganar o perder en
este caso, le permitiremos probar una nueva letra. Este nuevo recuadro se muestra en la Figura 88.
Figura 8-8: Agregamos un paso en caso de que el jugador pruebe una letra por segunda vez.
89
Esta informacin deber ser actualizada cada vez que el jugador pruebe una letra. Agrega un
recuadro Mostrar el tablero y los espacios vacos al jugador al diagrama de flujo entre los
recuadros Generar una palabra secreta e Invitar al jugador a adivinar una letra. Estos
recuadros se muestran en la Figura 8-9.
Figura 8-9: Agregamos Mostrar el tablero y los espacios vacos al jugador para dar informacin
al jugador.
Eso se ve bien! Este diagrama de flujo reproduce completamente todo lo que puede ocurrir en el
Ahorcado y en qu orden. Cuando disees tus propios juegos, un diagrama de flujo puede
ayudarte a recordar todo lo que necesitas codificar.
90
http://inventwithpython.com/es
Resumen
Puede parecer muchsimo trabajo dibujar un diagrama de flujo del programa primero. Despus de
todo, la gente quiere jugar juegos, no mirar diagramas de flujo! Pero es mucho ms fcil hacer
cambios y notar problemas pensando un poco sobre cmo funciona el programa antes de escribir
el cdigo.
Si te lanzas a escribir el cdigo primero, puedes llegar a descubrir problemas que requieren que
cambies el cdigo que has escrito. Cada vez que cambias tu cdigo, ests corriendo el riesgo de
introducir nuevos errores por cambiar demasiado, o demasiado poco. Es mucho mejor saber qu
es lo que quieres construir antes de construirlo.
Captulo 9 El Ahorcado
91
Captulo 9
EL AHORCADO
Temas Tratados en este Captulo:
Cadenas multi-lnea
Mtodos
Listas
Los mtodos de lista append() y reverse()
Los mtodos de cadena lower(), upper(), split(), startswith(), y endswith()
Los operadores in y not in
Las funciones range() y list()
Las sentencias del
Los bucles for
Las sentencias elif
El juego de este captulo introduce muchos conceptos nuevos, pero no te preocupes.
Experimentars antes con estos conceptos de programacin en el terminal interactivo. Aprenders
sobre los mtodos, que son funciones vinculadas a valores. Aprenders tambin acerca de un
nuevo tipo de lazo de repeticin llamado el bucle for y un nuevo tipo de dato llamado lista. Una
vez entiendas estos conceptos, ser mucho mas fcil programar El Ahorcado.
ahorcado.py
1. import random
2. IMGENES_AHORCADO = ['''
3.
4.
+---+
5.
|
|
6.
|
7.
|
8.
|
9.
|
10. =========''', '''
11.
92
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
http://inventwithpython.com/es
+---+
|
|
O
|
|
|
|
=========''', '''
+---+
|
|
O
|
|
|
|
|
=========''', '''
+---+
|
|
O
|
/|
|
|
|
=========''', '''
+---+
|
|
O
|
/|\ |
|
|
=========''', '''
+---+
|
|
O
|
/|\ |
/
|
|
=========''', '''
+---+
|
|
O
|
/|\ |
/ \ |
|
=========''']
Captulo 9 El Ahorcado
93
59. palabras = 'hormiga babuino tejon murcielago oso castor camello gato
almeja cobra pantera coyote cuervo ciervo perro burro pato aguila huron zorro
rana cabra ganso halcon leon lagarto llama topo mono alce raton mula salamandra
nutria buho panda loro paloma piton conejo carnero rata cuervo rinoceronte
salmon foca tiburon oveja mofeta perezoso serpiente araa cigea cisne tigre
sapo trucha pavo tortuga comadreja ballena lobo wombat cebra'.split()
60.
61. def obtenerPalabraAlAzar(listaDePalabras):
62.
# Esta funcin devuelve una cadena al azar de la lista de cadenas
pasada como argumento.
63.
ndiceDePalabras = random.randint(0, len(listaDePalabras) - 1)
64.
return listaDePalabras[ndiceDePalabras]
65.
66. def mostrarTablero(IMGENES_AHORCADO, letrasIncorrectas, letrasCorrectas,
palabraSecreta):
67.
print(IMGENES_AHORCADO[len(letrasIncorrectas)])
68.
print()
69.
70.
print('Letras incorrectas:', end=' ')
71.
for letra in letrasIncorrectas:
72.
print(letra, end=' ')
73.
print()
74.
75.
espaciosVacos = '_' * len(palabraSecreta)
76.
77.
for i in range(len(palabraSecreta)): # completar los espacios vacos
con las letras adivinadas
78.
if palabraSecreta[i] in letrasCorrectas:
79.
espaciosVacos = espaciosVacos[:i] + palabraSecreta[i] +
espaciosVacos[i+1:]
80.
81.
for letra in espaciosVacos: # mostrar la palabra secreta con espacios
entre cada letra
82.
print(letra, end=' ')
83.
print()
84.
85. def obtenerIntento(letrasProbadas):
86.
# Devuelve la letra ingresada por el jugador. Verifica que el jugador
ha ingresado slo una letra, y no otra cosa.
87.
while True:
88.
print('Adivina una letra.')
89.
intento = input()
90.
intento = intento.lower()
91.
if len(intento) != 1:
92.
print('Por favor, introduce una letra.')
93.
elif intento in letrasProbadas:
94.
print('Ya has probado esa letra. Elige otra.')
94
http://inventwithpython.com/es
95.
elif intento not in 'abcdefghijklmnopqrstuvwxyz':
96.
print('Por favor ingresa una LETRA.')
97.
else:
98.
return intento
99.
100. def jugarDeNuevo():
101.
# Esta funcin devuelve True si el jugador quiere volver a jugar, en
caso contrario devuelve False.
102.
print('Quieres jugar de nuevo? (s o no)')
103.
return input().lower().startswith('s')
104.
105.
106. print('A H O R C A D O')
107. letrasIncorrectas = ''
108. letrasCorrectas = ''
109. palabraSecreta = obtenerPalabraAlAzar(words)
110. juegoTerminado = False
111.
112. while True:
113.
mostrarTablero(IMGENES_AHORCADO, letrasIncorrectas, letrasCorrectas,
palabraSecreta)
114.
115.
# Permite al jugador escribir una letra.
116.
intento = obtenerIntento(letrasIncorrectas + letrasCorrectas)
117.
118.
if intento in palabraSecreta:
119.
letrasCorrectas = letrasCorrectas + intento
120.
121.
# Verifica si el jugador ha ganado.
122.
encontradoTodasLasLetras = True
123.
for i in range(len(palabraSecreta)):
124.
if palabraSecreta[i] not in letrasCorrectas:
125.
encontradoTodasLasLetras = False
126.
break
127.
if encontradoTodasLasLetras:
128.
print('S! La palabra secreta es "' + palabraSecreta + '"!
Has ganado!')
129.
juegoTerminado = True
130.
else:
131.
letrasIncorrectas = letrasIncorrectas + intento
132.
133.
# Comprobar si el jugador ha agotado sus intentos y ha perdido.
134.
if len(letrasIncorrectas) == len(IMGENES_AHORCADO) - 1:
135.
mostrarTablero(IMGENES_AHORCADO, letrasIncorrectas,
letrasCorrectas, palabraSecreta)
136.
print('Te has quedado sin intentos!\nDespus de ' +
str(len(letrasIncorrectas)) + ' intentos fallidos y ' +
Captulo 9 El Ahorcado
95
El programa El Ahorcado selecciona aleatoriamente una palabra secreta a partir de una lista
secreta de palabras. El mdulo random provee est habilidad, por lo que la lnea 1 lo importa.
2. IMGENES_AHORCADO = ['''
3.
4.
+---+
5.
|
|
6.
|
7.
|
8.
|
9.
|
10. =========''', '''
...el resto del cdigo es demasiado largo para mostrarlo ac...
Cadenas Multi-Lnea
Hasta ahora todas las cadenas han sido de una sola lnea y tenan un carcter de comillas al
principio y al final. Sin embargo, si utiliza comillas triples al comienzo y al final entonces la
cadena puede ir a lo largo de varias lneas:
96
http://inventwithpython.com/es
Estas son cadenas multi-lnea. En una cadena multi-lnea, los caracteres de nueva lnea son
incluidos como parte de la cadena. No tienes que utilizar el caracter de escape \n, o las comillas
de escape siempre que no utilices tres comillas juntas. Esto hace que el cdigo sea fcil de leer
para grandes cantidades de texto.
Variables Constantes
El nombre de variable IMGENES_AHORCADO est todo en maysculas. Esta es una convencin en
programacin para variables constantes. Las constantes son variables que tienen por finalidad
almacenar valores que nunca cambian desde la primera sentencia de asignacin. Aunque puedes
cambiar el valor de IMGENES_AHORCADO como con cualquier otra variable, un nombre con todas
las maysculas te recuerda no hacerlo. Dado que la variable IMGENES_AHORCADO nunca necesita
ser cambiada, es marcada como constante.
Como todas las convenciones, es tu decisin seguirla. Pero siguiendo esta convencin le haces
mas fcil a otras y otros programadores leer tu cdigo. Sabrn que IMGENES_AHORCADO siempre
tendr el valor que se le asign en la lnea 2.
Listas
Un valor de lista puede contener otros valores dentro. Intenta introducir esto en el terminal
interactivo:
>>> spam = ['Vida', 'El Universo', 'Todo', 42]
>>> spam
['Vida', 'El Universo', 'Todo', 42]
Este valor lista en spam contiene cuatro valores. Cuando escribes el valor lista en tu cdigo,
comienza con un [ corchete y termina con otro corchete. Esto es igual a las cadenas que terminan
y empiezan con un caracter de comillas.
Captulo 9 El Ahorcado
97
Se separan con comas los valores individuales dentro de una lista. Estos valores son llamados
elementos o tems.
ndices
Intenta introducir animales = ['guila', 'alce', 'antlope', 'albert'] en el terminal interactivo para
alamcenar una lista en la variable animales. Los corchetes son usados tambin para acceder al
elemento dentro de una lista. Intenta introducir animales[0], animales[1], animales[2],
animales[3] en un terminal interactivo para ver qu devuelven:
>>> animales = ['guila', 'alce', 'antlope', 'albert']
>>> animals[0]
'guila'
>>> animals[1]
'alce'
>>> animals[2]
'antlope'
>>> animals[3] # el autor de este libro!
'albert'
El nmero entre los corchetes es el ndice. En Python, el ndice del primer elemento en una lista
es 0. El segundo elemento est en el ndice 1, el tercer elemento est en el ndice 2 y as. Debido a
que los ndices comienzan en 0 y no en 1, decimos que las listas de Python tiene ndices de base
cero.
Las listas son buenas para almacenar varios valores sin usar una variable para cada uno. En otro
caso, el cdigo se vera como esto:
>>>
>>>
>>>
>>>
animales1
animales2
animales3
animales4
=
=
=
=
'guila'
'alce'
'antlope'
'albert'
Este cdigo podra ser difcil de manejar si tiene ciento o miles de cadenas. Pero una lista puede
fcilmente contener cualquier nmero de valores. Utilizando corchetes, puedes tratar los
elementos en la lista como cualquie rotro valor. Intenta introducir animales[0] + animales[2]
en el intrprete interactivo:
>>> animales[0] + animales[2]
'guilaantlope'
98
http://inventwithpython.com/es
'guila' + animals[2]
'guila' + 'antlope'
'guilaantlope'
IndexError
Si intentas acceder a un ndice que es demasiado grande, obtendrs un IndexError que colgar tu
programa. Intenta introducir lo siguiente en el intrprete interactivo:
>>> animales = ['guila', 'alce', 'antlope', 'albert']
>>> animales[9999]
Traceback (most recent call last):
File "", line 1, in
animales[9999]
IndexError: list index out of range
Cambiando los Valores de los Elementos de una Lista con asignacin por
ndice
Tambin puedes usar los corchetes para cambiar el valor de un elemento en una lista. Intenta
introducir lo siguiente en el intrprete interactivo:
>>> animales = ['guila', 'alce', 'antlope', 'albert']
>>> animales[1] = 'ALCE'
>>> animales
['guila', 'ALCE', 'antlope', 'albert']
La nueva cadena 'ALCE' sobreescribe el segundo elemento en la lista animales. De manera que
animales[1] devolver el segundo elemento de la lista en las expresiones, pero tambin puedes
usarlo en el lado izquierdo de una sentencia de asignacin para asignar un valor como el segundo
elemento de la lista.
Concatenacin de Listas
Puedes unir listas en una sola lista con el operador +, del mismo modo como puedes unir cadenas.
Unir listas con el operador + es una concatenacin de listas. Intenta introducir lo siguiente en el
intrprete interactivo:
>>> [1, 2, 3, 4] + ['manzanas', 'naranjas'] + ['Alicia', 'Beto']
[1, 2, 3, 4, 'manzanas', 'naranjas', 'Alicia', 'Beto']
Captulo 9 El Ahorcado
99
['manzanas'] + ['naranjas']
El Operador in
El operador in te puede decir si un valor est en una lista o no. Las expresiones que usan el
operador in devuelven un valor lgico: True si el valor est en la lista y False si no est. Intenta
introducir lo siguiente en el intrprete interactivo:
>>> animales = ['guila', 'alce', 'antlope', 'albert']
>>> 'antlope' in animales
True
La expresin 'antlope' en animales devuelve True porque la cadena 'antlope' es uno de los
valores en la lista animales. Esta localizado en el ndice 2.
Pero si escribes la expresin 'atn' in animales, esto devolver False porque la cadena
'atn' no existe en la lista.
>>> animales = ['guila', 'alce', 'antlope', 'albert']
>>> 'antlope' in animales
True
>>> 'atn' in animales
False
El operador in tambin funciona para las cadenas. Verifica si una cadena existe en otra. Intenta
introducir lo siguiente en el intrprete interactivo:
>>> 'hola' in 'Alicia le dice hola a Beto.'
True
100
http://inventwithpython.com/es
Nota que cuando eliminas un elemento en el ndice 1, el elemento que estaba en el ndice 2 ahora
es el nuevo valor en el ndice 1. El elemento que estaba en el ndice 3 pasa a ser el nuevo valor en
el ndice 2. Todo lo que estaba por encima del elemento eliminado se mueve un ndice hacia
abajo.
Puedes escribir del spam[1] una y otra vez para seguir eliminando elementos de la lista:
>>>
>>>
>>>
[2,
>>>
>>>
[2,
>>>
>>>
[2,
La sentencia del es una sentencia, no una funcin ni un operador. No tiene parntesis ni devuelve
un valor.
Listas de Listas
Las listas pueden contener otros valores, incluyendo otras listas. Digamos que tienes una lista de
comestibles, una lista de tareas domesticas, y una lista de tus pasteles favoritos. Puedes poner las
tres listas en una sola lista. Intenta introducir lo siguiente en el intrprete interactivo:
>>> comestibles = ['huevos', 'leche', 'sopa', 'manzanas', 'pan']
>>> tareas = ['limpiar', 'cortar el csped', 'ir al supermercado']
>>> pastelesFavoritos = ['manzanas', 'zarzamora']
>>> listaDeListas = [comestibles, tareas, pastelesFavoritos]
>>> listaDeListas
[['huevos', 'leche', 'sopa', 'manzanas', 'pan'], ['limpiar', 'cortar el
csped', 'ir al supermercado'], ['manzanas', 'zarzamora']]
Para obtener un elemento dentro de una lista de listas, usaras dos conjuntos de corchetes como
este: listaDeListas[1][2] que devolvera la cadena 'ir al supermercado'.
Esto es porque listaDeListas[1][2] devuelve ['limpiar', 'cortar el csped', 'ir al
supermercado'][2]. Lo que finalmente devuelve 'ir al supermercado':
listaDeListas[1][2]
Captulo 9 El Ahorcado
101
'ir al supermercado'
La Figura 9-1 es otro ejemplo de una lista de listas, a lo largo de alguno de los ndices que
apuntan a los elementos. Las flechas apuntan a ndices de las propias listas internas. La imagen
est invertida lateralmente para facilitar su lectura.
Mtodos
Los mtodos son funciones adjuntas a un valor. Por ejemplo, todos los valores de cadena tienen
el mtodo lower(), el cul devuelve una copia de la cadena en minsculas. Lo puedes llamar
como 'Hola.lower()', lo cual devuelve 'hola'. No puedes llamar a lower() por s mismo y no
puedes pasar un argumento a lower() (como en lower('Hola')). Debes aadir el mtodo a una
cadena especifica usando un punto. La siguiente seccin describe en mayor profundidad los
mtodos de cadena.
102
http://inventwithpython.com/es
Tambin hay un mtodo upper() para cadenas, el cual devuelve una cadena con todos los
caracteres en maysculas. Intenta introducir 'Hola mundo!.upper() en el intrprete interactivo:
>>> '!Hola mundo!'.upper()
'!HOLA MUNDO!'
Ya que el mtodo upper() devuelve una cadena, tambin puedes llamar un mtodo en esa cadena
tambin. Intenta introducir 'Hola mundo!'.upper().lower() en el intrprete interactivo:
>>> 'Hola mundo!'.upper().lower()
'hola mundo!'
'Hola mundo!'.upper()
'HOLA MUNDO!'.lower()
'hola mundo!'
'hola mundo!'.upper()
'!HOLA MUNDO!'
Captulo 9 El Ahorcado
103
Si se almacena una cadena en una variable, puedes llamar un mtodo de cadena en esa variable.
Mira este ejemplo:
>>> spam = 'Hola mundo!'
>>> spam.upper()
'!HOLA MUNDO!'
Esto no cambia el valor en spam. La variable spam seguir conteniendo 'Hola mundo!'.
Note que los tipos de dato integer y float no tienen ningn mtodo.
El mtodo mas comn de lista que usars es append(). Este mtodo aadir el valor que pasas
como argumento al final de la lista- Intenta introducir lo siguiente en el intrprete interactivo:
>>> eggs = []
>>> eggs.append('hovercraft')
>>> eggs
['hovercraft']
>>> eggs.append('eels')
>>> eggs
['hovercraft', 'eels']
>>> eggs.append(42)
>>> eggs
['hovercraft', 'eels', 42]
Estos mtodos cambian las listas que los llaman. No devuelven una nueva lista. Decimos que esos
mtodos cambian la lista en el sitio.
104
http://inventwithpython.com/es
Esta sentencia de asignacin tiene solamente una larga cadena, llena de palabras separadas por
espacios. Al final de la cadena hay una llamada al mtodo split(). El mtodo split() devuelve
una lista en la que cada palabra en la cadena es un elemento aparte. La separacin ocurre en
cualquier lugar donde haya un espacio en la cadena.
Es fcil escribir cdigo utilizando split(). Si lo hubieses creado desde el principio como lista,
tendras que haber escrito: ['hormiga', 'babuino', 'tejon',... y as, con comillas y comas
para cada palabra.
Por ejemplo, intenta introducir lo siguiente en el intrprete interactivo:
>>> oracion = input()
Mi muy enrgica madre tan slo nos sirvi nachos.
>>> oracion.split()
['Mi', 'muy', 'enrgica', 'madre', 'tan', 'slo', 'nos', 'sirvi', 'nachos.']
El resultado es una lista de nueve cadenas, una cadena para una de las palabras de la cadena
original, Los espacios no estn incluidos en ningn elemento de la lista.
Tambin puedes aadir tus propias palabras a la cadena en la lnea 59, o eliminar cualquiera que
no quieres que est en el juego. Solamente asegurate que los espacios separan las palabras.
Captulo 9 El Ahorcado
105
62.
# Esta funcin devuelve una cadena al azar de la lista de cadenas
pasada como argumento.
63.
ndiceDePalabras = random.randint(0, len(listaDePalabras) - 1)
64.
return listaDePalabras[ndiceDePalabras]
http://inventwithpython.com/es
106
Este cdigo define una nueva funcin llamada mostrarTablero(). Esta funcin tiene cuatro
parmetros:
IMGENES_AHORCADO
letrasIncorrectas
en la palabra secreta.
letrasCorrectas
palabraSecreta
adivinar.
La primera llamada a la funcin print() mostrar el tablero. IMGENES_AHORCADO ser una lista
de cadenas para cada tablero posible. IMGENES_AHORCADO[0] muestra una horca vaca,
IMGENES_AHORCADO[1] muestra la cabeza (cuando el jugador falla una letra),
IMGENES_AHORCADO[2] muestra una cabeza y el cuerpo (cuando el jugador falla dos letras), y as
sucesivamente hasta IMGENES_AHORCADO[6] que muestra al ahorcado completo.
El nmero de letras en letrasIncorrectas reflejar cuantos intentos incorrectos ha hecho el jugador.
Llama a len(letrasIncorrectas) para averiguar este nmero. As que, si letrasIncorrectas
es 'aetr' entonces len('aetr') devolver 4. Imprimir IMGENES_AHORCADO[4] mostrar el
tablero del ahorcado apropiado para 4 fallos. Esto es lo que devuelve
IMGENES_AHORCADO[len(letrasIncorrectas)] en la lnea 67.
70.
71.
72.
73.
Captulo 9 El Ahorcado
107
La funcin list() es similar a las funciones str() o int(). Toma el valor que se pasa y
devuelve una lista. Es fcil generar enormes listas con la funcin range(). Prueba a introducir
list(range(10000)) en el intrprete interactivo:
>>> list(range(10000))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,...
...omitido por brevedad...
...9989, 9990, 9991, 9992, 9993, 9994, 9995, 9996, 9997, 9998, 9999]
La lista es tan grande, que ni siquiera cabr completa en la pantalla. Pero puedes almacenar la
lista en una variable:
>>> spam = list(range(10000))
Si pasa dos argumentos enteros a range(), el objeto range que devuelve va desde el primer
argumento entero hasta (pero sin incluir) el segundo argumento entero. Intenta introducir
list(range(10, 20)) en el intrprete interactivo:
>>> list(range(10, 20))
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
La funcin range() se utiliza a menudo en los bucles for, que son muy parecidos a los bucles
while que ya has visto.
108
http://inventwithpython.com/es
Captulo 9 El Ahorcado
109
Cortes
El corte de lista crea un nuevo valor de lista con un subconjunto de elementos de otra lista. En el
cdigo, se especifican dos ndices (al inicio y final) separados por dos puntos en los corchetes
despus de una lista. Por ejemplo, introduce lo siguiente en el intrprete interactivo:
>>> spam = ['manzanas', 'bananas', 'zanahorias', 'fechas']
>>> spam[1:3]
['bananas', 'zanahorias']
La expresin spam[1: 3] se evala a una lista con los elementos desde el ndice 1 hasta (pero sin
incluir) el ndice 3 en spam.
Si se omite el primer ndice, Python pensar automticamente que deseas el ndice 0 como primer
ndice:
>>> spam = ['manzanas', 'bananas', 'zanahorias', 'fechas']
110
http://inventwithpython.com/es
>>> spam[:2]
['manzanas', 'bananas']
Si se omite el segundo ndice, Python pensar automticamente que deseas el resto de la lista:
>>> spam = ['manzanas', 'bananas', 'zanahorias', 'fechas']
>>> spam [2:]
['zanahorias', 'fechas']
Cortar es una forma sencilla de obtener un subconjunto de los elementos de una lista. Utiliza
cortes con cadenas en la misma forma que los utilizas con listas. Cada caracter de la cadena es
como un elemento de la lista. Prueba a introducir lo siguiente en el intrprete interactivo:
>>> miNombre = 'Zophie el Gato Gordo'
>>> miNombre[4:12]
'ie el Ga'
>>> miNombre[:10]
'Zophie el '
>>> miNombre[7:]
'el Gato Gordo'
Captulo 9 El Ahorcado
111
79.
espaciosVacos = espaciosVacos[:i] + palabraSecreta[i] +
espaciosVacos[i+1:]
La lnea 77 tiene un bucle for que pasa por cada letra de palabraSecreta y reemplaza el
subrayado con la letra actual si existe en letrasCorrectas.
Por ejemplo, suponga que el valor de palabraSecreta es 'nutria' y el valor en
letrasCorrectas es 'tr'. Querras que la cadena '__tr__' sea mostrada al jugador. Vamos a
averiguar cmo crear esta cadena.
La llamada a len(palabraSecreta) devolvera 5. La llamada a range(len(palabraSecreta))
se convierte en range(5), lo que hace que el bucle itere sobre 0, 1, 2, 3 y 4.
Debido a que el valor de i se enfrentar a cada valor en [0, 1, 2, 3, 4], el cdigo en el bucle
for es lo mismo que:
if palabraSecreta[0] in letrasCorrectas:
espaciosVacos = espaciosVacos[:0] + palabraSecreta[0] + espaciosVacos[1:]
if palabraSecreta[1] in letrasCorrectas:
espaciosVacos = espaciosVacos[:1] + palabraSecreta[1] + espaciosVacos[2:]
if palabraSecreta[2] in letrasCorrectas:
espaciosVacos = espaciosVacos[:2] + palabraSecreta[2] + espaciosVacos[3:]
if palabraSecreta[3] in letrasCorrectas:
espaciosVacos = espaciosVacos[:3] + palabraSecreta[3] + espaciosVacos[4:]
if palabraSecreta[4] in letrasCorrectas:
espaciosVacos = espaciosVacos[:4] + palabraSecreta[4] + espaciosVacos[5:]
112
http://inventwithpython.com/es
Si reemplazas las cortes de lista y los ndices de lista con los valores que representan, el cdigo
del bucle sera algo como:
if 'n' in 'tr': # Falso
espaciosVacos = '' + 'n' + '_____' # Esta lnea se salta.
if 'u' in 'tr': # Falso
espaciosVacos = '_' + 'u' + '____' # Esta lnea se salta.
if 't' in 'tr': # Verdadero
espaciosVacos = '__' + 't' + '___' # Esta lnea se ejecuta.
if 'r' in 'tr': # Verdadero
espaciosVacos = '__t' + 'r' + '__' # Esta lnea se ejecuta.
if 'i' in 'tr': # Falso
espaciosVacos = '__tr' + 'i' + '_' # Esta lnea se salta.
if 'a' in 'tr': # Falso
espaciosVacos = '__tr_' + 'a' + '' # Esta lnea se salta.
# espaciosVacos ahora tiene el valor '__tr__'
Los ejemplos de cdigo anteriores todos hacen lo mismo cuando palabraSecreta es 'nutria' y
letrasCorrectas es 'tr'. Las siguientes lneas de cdigo imprimen el nuevo valor de los
espaciosVacos con espacios entre cada letra.
81.
for letra in espaciosVacos: # mostrar la palabra secreta con espacios
entre cada letra
82.
print(letra, end=' ')
83.
print()
Captulo 9 El Ahorcado
113
Se pasa como argumento una cadena de las letras que el jugador ha intentado para el parmetro
letrasProbadas. Entonces la funcin obtenerIntento() pide al jugador que adivine una sola
letra. Esta letra ser el valor que devolver obtenerIntento().
87.
88.
89.
90.
while True:
print('Adivina una letra.')
intento = input()
intento = intento.lower()
El bucle while de la lnea 87 se mantendr pidiendo al jugador una letra hasta que introduzca
texto que sea:
1. Una sola letra.
2. Una letra que no haya intentado previamente.
La condicin para el bucle while es simplemente el valor booleano True. Eso significa que la
nica forma de salir de la ejecucin de este bucle es mediante la ejecucin de una sentencia break
(que sale del bucle) o una sentencia return (que sale no slo del bucle sino de toda la funcin).
El cdigo dentro del bucle pide al jugador que introduzca una letra, que se almacena en la
variable intento. Si el jugador introduce una letra mayscula, se sobrescribe con una letra
minscula en la lnea 90.
114
http://inventwithpython.com/es
Cuando una de las condiciones elif es True, se ejecuta su cdigo y despus que es ejecutado salta
a la primera lnea despus del bloque else. As que uno y slo uno de los bloques en las
declaraciones if-elif-else se ejecutar. Tambin puedes obviar el bloque else si no necesitas
uno, y dejar tan slo sentencias if-elif.
if len(intento) != 1:
print('Por favor, introduce una letra.')
elif intento in letrasProbadas:
print('Ya has probado esa letra. Elige otra.')
elif intento not in 'abcdefghijklmnopqrstuvwxyz':
print('Por favor ingresa una LETRA.')
else:
return intento
La variable intento contiene la letra del intento del jugador. El programa necesita asegurarse que
escribi un intento vlido: una y slo una letra minscula. Si no lo hizo, la ejecucin debe
regresar al bucle y pedirle de nuevo una letra.
La condicin de la lnea 91 verifica si el intento no tiene longitud de un carcter. La condicin de
la lnea 93 verifica si el intento ya existe en la variable letrasProbadas. La condicin de la lnea
95 comprueba si el intento no es una letra minscula.
Si todas estas condiciones son falsas, entonces el bloque de la sentencia else se ejecuta y
obtenerIntento() devuelve el valor del intento en la lnea 98.
Recuerda, slo se ejecutar uno de los bloques en las sentencias if-elif-else.
Captulo 9 El Ahorcado
115
La funcin jugarDeNuevo() tiene slo una llamada a la funcin print() y una sentencia
return. La sentencia return tiene una expresin que parece complicada, pero se puede separar
por partes. Aqu hay una revisin paso a paso de cmo Python evala esta expresin, si el usuario
escribe SI.
input().lower().startswith('s')
'SI'.lower().startswith('s')
'si'.startswith('s')
True
http://inventwithpython.com/es
116
obtenerPalabraAlAzar(listaDePalabras)
el jugador de la palabra secreta hasta el momento, y las letras equivocadas que el jugador
ha intentado. Esta funcin necesita que se le pasen cuatro parmetros para que funcione
correctamente. IMGENES_AHORCADO es una lista de cadenas que contiene el arte ASCII
para cada posible tablero del ahorcado. letrasCorrectas y letrasIncorrectas son
cadenas formadas por las letras que el jugador ha intentado que estn y no estn en la
palabra secreta, respectivamente. Y palabraSecreta es la palabra secreta que el jugador
est tratando de adivinar. Esta funcin no tiene valor de retorno.
obtenerIntento(letrasProbadas) toma una cadena con las letras que el jugador ya ha
adivinado y se mantiene pidiendo al jugador por una letra que no est en
letrasProbadas. Esta funcin devuelve la cadena de la letra valida que el jugador
intent.
jugarDeNuevo() es una funcin que pregunta si el jugador quiere jugar otra ronda de
Ahorcado. Esta funcin devuelve True si el jugador lo hace y False si el jugador no lo
hace.
Despus de las funciones est el cdigo de la parte principal del programa en la lnea 106. Todo
lo anterior fueron solamente definiciones de funcin y una larga sentencia de asignacin para
IMGENES_AHORCADO.
print('A H O R C A D O')
letrasIncorrectas = ''
letrasCorrectas = ''
palabraSecreta = obtenerPalabraAlAzar(words)
juegoTerminado = False
La lnea 106 es la primera llamada a print() que se ejecuta cuando inicia el juego. Muestra el
ttulo del juego. A continuacin, se asignan cadenas en blanco para letrasIncorrectas y
letrasCorrectas ya que el jugador no ha intentado an ninguna letra falla o correcta.
La llamada a obtenerPalabraAlAzar(palabras) devolver una palabra seleccionada al azar
entre la lista de palabras.
Captulo 9 El Ahorcado
117
La condicin del bucle while siempre es True, lo que significa que se repetir indefinidamente
hasta que se encuentre una sentencia break. (Esto sucede ms adelante en la lnea 147.)
La lnea 113 llama a la funcin mostrarTablero(), pasndole la lista de imgenes de arte ASCII
del ahorcado y las tres variables creadas en las lneas 107, 108 y 109. En base al nmero de letras
que el jugador ha intentado correctamente y ha fallado, esta funcin muestra el tablero del
ahorcado adecuado al jugador.
if intento in palabraSecreta:
letrasCorrectas = letrasCorrectas + intento
118
124.
125.
126.
http://inventwithpython.com/es
Cmo puede el programa de saber si el jugador ha adivinado todas las letras de la palabra
secreta? Bueno, letrasCorrectas tiene cada letra que el jugador ha intentado correctamente y
palabraSecreta es la propia palabra secreta. Pero uno no puede comprobar si letrasCorrectas
== palabraSecreta ya que considera este caso: si palabraSecreta fue la cadena 'nutria' y
letrasCorrectas es la cadena 'tiranu', entonces letrasCorrectas == palabraSecreta
sera False aunque que el jugador ha adivinado todas las letras en la palabra secreta.
La nica manera en la que puedes estar seguro que el jugador gan es iterar sobre cada letra en
palabraSecreta y ver si existe en letrasCorrectas. Si, y slo si, existe cada letra de
palabraSecreta en letrasCorrectas el jugador habr ganado.
Si encuentras una letra en palabraSecreta que no existe en letrasCorrectas, sabes que el
jugador no ha adivinado todas las letras. La nueva variable encontradoTodasLasLetras se
establece en True en la lnea 122 antes de que empiece el bucle. El bucle comienza asumiendo
que se han encontrado todas las letras de la palabra secreta. Pero el cdigo del bucle en la lnea
125 va a cambiar encontradoTodasLasLetras a False la primera vez que encuentre una letra en
palabraSecreta que no est en letrasCorrectas.
127.
if encontradoTodasLasLetras:
128.
print('S! La palabra secreta es "' + palabraSecreta + '"!
Has ganado!')
129.
juegoTerminado = True
Si se han encontrado todas las letras de la palabra secreta, se le dice al jugador que ha ganado y
juegoTerminado se establece en True.
else:
letrasIncorrectas = letrasIncorrectas + intento
Este es el inicio del bloque else. Recuerda que el cdigo de este bloque se ejecutar si la
condicin es False. Pero cul condicin? Para averiguarlo, apunta el dedo al principio de la
palabra clave else y muvelo hacia arriba, como en la Figura 9-3. Vers que la indentacin de la
palabra clave else es la mismo que la indentacin de la palabra clave if en la lnea 118.
Captulo 9 El Ahorcado
119
Figura 9-3: La sentencia else coincide en la misma indentacin que la sentencia if.
As que si la condicin en la lnea 118 (intento in palabraSecreta) era False, entonces la
ejecucin se mueve a este bloque else.
Las letras que se han intentado errneamente se concatenan a la cadena letrasIncorrectas en
la lnea 131. Esto es como lo que se hizo en la lnea 119 para las letras que el jugador ha
adivinado correctamente.
133.
# Comprobar si el jugador ha agotado sus intentos y ha perdido.
134.
if len(letrasIncorrectas) == len(IMGENES_AHORCADO) - 1:
135.
mostrarTablero(IMGENES_AHORCADO, letrasIncorrectas,
letrasCorrectas, palabraSecreta)
136.
print('Te has quedado sin intentos!\nDespus de ' +
str(len(letrasIncorrectas)) + ' intentos fallidos y ' +
str(len(letrasCorrectas)) + ' aciertos, la palabra era "' + palabraSecreta +
'"')
137.
juegoTerminado = True
Cada vez que el jugador falla un intento, el cdigo concatena la letra incorrecta con la cadena en
letrasIncorrectas. As, la longitud de letrasIncorrectas (o, en cdigo,
len(letrasIncorrectas)) es tambin el nmero de intentos equivocados.
La lista IMGENES_AHORCADO tiene 7 cadenas de arte ASCII. As que cuando
len(letrasIncorrectas) sea igual a 6, ya sabes que el jugador ha perdido porque se habr
terminado la imagen del ahorcado. Recuerda, IMGENES_AHORCADO[0] es el primer elemento de
la lista, e IMGENES_AHORCADO[6] es la ltima.
As que, cuando la longitud de la cadena letrasIncorrectas es igual a len(IMGENES_AHORCADO) 1 (es decir, 6), el jugador se ha quedado sin intentos. La lnea 136 imprime la palabra secreta y la
lnea 137 asigna True a la variable juegoTerminado.
120
http://inventwithpython.com/es
139.
# Preguntar al jugador si quiere volver a jugar (pero slo si el juego
ha terminado).
140.
if juegoTerminado:
141.
if jugarDeNuevo():
142.
letrasIncorrectas = ''
143.
letrasCorrectas = ''
144.
juegoTerminado = False
145.
palabraSecreta = obtenerPalabraAlAzar(palabras)
Si el jugador gana o pierde despus de intentar una letra, el juego debe preguntar al jugador si
quiere volver a jugar. La funcin jugarDeNuevo() se encarga de obtener un si o no del jugador,
por lo que se llama en la lnea 141.
Si el jugador quiere jugar otra vez, los valores de letrasIncorrectas y letrasCorrectas deben
restablecerse a cadenas en blanco, juegoTerminado en False, y se almacena una nueva palabra
secreta en palabraSecreta. De esta manera, cuando la ejecucin vuelve al inicio del bucle while
en la lnea 112, el tablero volver a un nuevo juego.
146.
147.
else:
break
Si el jugador no escribe algo que comienza con s cuando se le pregunta si quiere volver a jugar,
entonces la condicin en la lnea 141 se evala a False y se ejecuta el bloque else. La sentencia
break hace que la ejecucin salte a la primera instruccin despus del bucle. Pero debido a que
no hay ms instrucciones despus del bucle, el programa termina.
Resumen
Este ha sido un captulo largo, y hemos introducido varios conceptos nuevos. Pero Ahorcado ha
sido nuestro juego ms avanzado hasta el momento. A medida que los juegos se vuelven ms y
ms complejos, va a ser una buena idea esbozar un diagrama de flujo en el papel sobre lo que
sucede en tu programa.
Las listas son valores que pueden contener otros valores. Los mtodos son funciones especficas a
un tipo de datos. Las listas tienen los mtodos append() y reverse(). Las cadenas tienen los
mtodos lower(), upper(), split(), startswith(), y endsWith(). Aprenders sobre muchos
ms tipos de datos y mtodos en lo que queda de este libro.
El bucle for es un bucle que itera sobre los elementos de una lista, a diferencia de un bucle while
que itera mientras una condicin es True. La declaracin elif te permite aadir una clusula "o de
lo contrario, si" en el medio de tus sentencias if-else. La sentencia del puede eliminar variables
o elementos dentro de las listas.
121
Captulo 9
EXTENDIENDO AHORCADO
Temas Tratados En Este Captulo:
Diccionario de datos
Duplas clave-valor (key-value)
Los mtodos keys() y values()
Los mtodos keys() y values() del diccionario
Asignaciones de variable mltiple
Este programa fue mucho mayor que el programa Reino de Dragones, pero tambin ms
sofisticado. Realizar un diagrama de flujo o un pequeo esquema realmente ayuda a recordar
como quieres que todo funcione.
Ahora que has creado un simple juego de Ahorcado, veamos unas modos de que puedas
extenderlo con nuevas funcionalidades.
Luego de que has jugado al Ahorcado unas veces, puedes pensar que seis intentos de adivinar no
son suficientes para la mayoria de las palabras. Podemos sencillamente darle al jugador mas
oportunidades agregando cadenas multi-linea a la lista IMAGENES_AHORCADO.
Guarda tu ahorcado.py como ahorcado2.py y luego agrega lo siguiente:
58. ==========''', '''
59.
+----+
60.
|
|
61. [O
|
62. /|\
|
63. / \
|
64.
|
65. ==========''', '''
66.
+----+
67.
|
|
68. [O]
|
69. /|\
|
70. / \
|
71.
|
72. ==========''']
122
http://inventwithpython.com/es
Ahora hay dos nuevas cadenas multi-lnea en la lista, uno con slo la oreja izquierda dibujada, y
la otra con ambas dibujadas. Debido a que el programa dir que el jugador perdi cuando el
nmero de intentos es igual al nmero de cadenas en IMGENES_AHORCADO (menos uno), este es el
unico cambio que debes hacer.
Cambia la lista de palabras cambiando las palabras en la lnea 59. En vez de animales, podemos
tener colores:
59. palabras = palabras = 'rojo naranja amarillo verde azul ail violeta blanco
negro marron'.split()
O formas:
59. palabras = 'cuadrado triangulo rectangulo circulo elipse rombo trapezoide
chevron pentagono hexagono heptagono octogono'.split()
O frutas:
59. palabras = 'manzana naranja limon lima pera sandia uva pomelo cereza banana
melon mango fresa tomate'.split()
Con algunas modificaciones, podemos cambiar nuestro cdigo para que nuestro juego de
Ahorcado pueda utilizar todas estas palabras como conjuntos diferentes.
Diccionarios
Para realizar este cambio, necesitaras una nueva estructura de datos llamada diccionario. Un
diccionario puede almacenar mltiples valores tal como una lista lo hace. Pero en vez de acceder
a los elementos con un ndice entero, puedes acceder a ellos con un ndice de cualquier tipo. Para
los diccionarios, estos ndices son llamados claves (keys en ingls).
Los diccionarios usan { y } llaves en vez de [ y ] corchetes. Prueba ingresando lo siguiente en la
consola interactiva:
>>> cosas = {'hola':'Hola como estas?',4:'panceta','spam':9999}
Los valores entre las llaves separadas por comas son los pares clave-valor. Las claves se
encuentran a la izquierda de los dos puntos y los valores a la derecha. Puedes acceder a los
valores como elementos en una lista utilizando la clave. Prueba ingresando en la consola
interactiva cosas['hola'], cosas[4] y cosas['spam']:
>>> cosas = {'hola':'Hola como estas?',4:'panceta','spam':9999}
123
>>> cosas['hola']
'Hola como estas?'
>>> cosas[4]
'panceta'
>>> cosas['spam']
9999
En vez de utilizar un entero entre los corchetes, utilizas una valor cadena. Esto evaluar el valor
para dicha clave.
Las claves en los diccionarios tambin pueden iterarse utilizando un ciclo for. Prueba ingresando
lo siguiente en la consola interactiva:
>>> favoritos = {'fruta':'manzanas', 'animal':'gatos', 'nmero':42}
>>> for i in favoritos:
...
print(i)
fruta
animal
nmero
>>> for i in favoritos:
...
print(favoritos[i])
manzanas
gatos
42
124
http://inventwithpython.com/es
Los diccionarios se diferencian de las listas porque los valores dentro de ellos no son ordenados.
El primer elemento en una lista llamada listaCosas sera listaCosas[0]. Pero no hay un
"primer" elemento en un diccionario, porque los diccionarios no tienen ningn tipo de orden.
Prueba ingresando lo siguiente en la consola interactiva:
>>> favoritos1 = {'fruta':'manzana', 'nmero:42, 'animal':'gatos'}
>>> favoritos2 = {'animal':'gatos', 'nmero:42, 'fruta':'manzana'}
>>> favoritos1== favoritos2
True
La expresin favoritos1 == favoritos2 se evala a True porque los diccionarios son noordenados. Dos diccionarios se consideran iguales si tienen los mismos pares clave-valor,
tipeados en cualquier orden. Mientras tanto, las listas son ordenadas, dos listas con los mismos
valores en distinto orden se consideran diferentes. Prueba ingresando lo siguiente en la consola
interactiva.
>>> listaFavs1 = ['manzanas', 'gatos', 42]
>>> listaFavs2 = ['gatos', 42, 'manzanas']
>>> listaFavs1 == listFavs2
False
Los diccionarios poseen dos mtodos tiles, keys() y values(). Estos devolvern valores de un
tipo llamados dict_keys y dict_values respectivamente (claves y valores). Similar a los objetos
de rango, los valores de estos tipo de datos pueden convertirse facilmente a listas con la funcin
list(). Prueba ingresando lo siguiente en la consola interactiva:
>>> favoritos = {'fruta':'manzanas', 'animal':'gatos', 'nmero:42}
>>> list(favoritos.keys())
['fruta', 'nmero, 'animal']
>>> list(favoritos.values())
['manzanas', 42, 'gatos']
125
61. 'Frutas':'manzana naranja limon lima pera sandia uva pomelo cereza banana
melon mango fresa tomate'.split(),
62. 'Animales':'murcielago oso castor gato pantera cangrejo ciervo perro burro
pato aguila pez rana cabra sanguijuela leon lagarto mono alce raton nutria buho
panda piton conejo rata tiburon oveja mofeta calamar tigre pavo tortuga
comadreja ballena lobo wombat cebra'.split()}
Este cdigo dispuesto en mltiples lneas es interpretado como una sola lnea, ya que la lnea
no termina hasta la clave } final.
La Funcin random.choice()
La funcin choice() del mdulo random requiere una lista como argumento y devuelve un valor
aleatorio de l, al igual que lo hacia tu funcin obtenerPalabraAlAzar(). Usars
random.choice() en la nueva versin de obtenerPalabraAlAzar().
Para ver como la funcin random.choice() funciona, prueba ingresando lo siguiente en la
consola interactiva:
>>> import random
>>> random.choice(['gato', 'perro', 'ratn'])
'mouse'
>>> random.choice(['gato', 'perro', 'ratn'])
'cat'
>>> random.choice([2, 22, 222, 223])
2
>>> random.choice([2, 22, 222, 223])
222
126
http://inventwithpython.com/es
67.
claveDePalabras = random.choice(list(diccionarioDePalabras.keys()))
68.
69.
# Segundo, elige una palabra aleatoria de la lista correspondiente a la
clave en el diccionario:
70.
ndiceDePalabra = random.randint(0,
len(diccionarioDePalabras[claveDePalabras]) - 1)
71.
72.
return [diccionarioDePalabras[claveDePalabras][ndiceDePalabra],
claveDePalabras]
diccionarioDePalabras['Frutas'][ndiceDePalabra]
'sandia'
En este caso, el elemento de la lista que devuele la funcin ser la cadena 'sandia'. (Recuerda
que los ndices comienzan en 0, as que [5] refiere al sexto elemento en la lista.)
Dado que obtenerPalabraAlAzar() ahora devuelve una lista de dos elementos en vez de una
cadena, a palabraSecreta se le asignar una lista y no una cadena. Puedes asignar ambos
elementos a dos variables separadas utilizando un truco de asignacin mltiple.
127
Asignacin Mltiple
Puedes especificar mltiples variables, separadas por comas, al lado izquierdo de la declaracin
de asignacin. Prueba ingresando lo siguiente en la consola interactiva:
>>> a, b, c = ['manzanas', 'gatos', 42]
>>> a
'manzanas'
>>> b
'gatos'
>>> c
42
El truco est en colocar la misma cantidad de variables como de elementos a la derecha del signo
=. Python automticamente asignar el valor del primer elemento a la primer variable, el valor del
segundo elemento en la segunda variable y as continuamente. Pero si no posees la misma
cnatidad de elemento, el interprete de Python dar un error.
>>> a, b, c, d = ['manzanas', 'gatos', 42, 10, 'hola']
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
a, b, c, d = ['manzanas', 'gatos', 42, 10, 'hola']
ValueError: too many values to unpack
>>> a, b, c, d = ['manzanas', 'gatos']
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
a, b, c = ['manzanas', 'gatos']
ValueError: need more than 2 values to unpack
letrasCorrectas = ''
palabraSecreta, claveSecreta = obtenerPalabraAlAzar(palabras)
juegoTerminado = False
juegoTerminado = False
palabraSecreta, claveSecreta = obtenerPalabraAlAzar(palabras)
128
http://inventwithpython.com/es
146. else:
Ahora has terminado con nuestros cambios al Ahorcado. En vez de slo una lista de cadenas, la
palabra secreta es elegida de diferentes listas de cadenas. El programa tambin dir al jugador de
qu conjunto de palabras es la palabra secreta.
Prueba jugar esta nueva versin. Luego puedes cambiar el diccionario de palabras de la lnea 59
para incluir mas conjuntos de palabrasAhora tu juego de Ahorcado puede ser extendido
facilmente!
Resumen
Hemos terminado el Ahorcado. Incluso luego de haber terminado de escribir un juego, puedes
facilmente agregar ms caractersticas luego de haber aprendio ms programacin.
El Ahorcado es ligeramente avanzado comparado con los juegos previos en este libro. Pero en
este punto, sabes muchos de los conceptos bsicos para escribir programas: variables, ciclos,
funciones y los tipos de datos de Python como listas y diccionarios. Los siguientes programas en
este libro segurn siendo un desafo a dominar, pero acabas de terminar el la parte ms empinada
de la escalada.
Captulo 10 Ta Te Ti
129
Captulo 10
TA T E TI
Temas Tratados En Este Captulo:
Inteligencia Artificial
Referencias en Listas
Evaluacin en Cortocircuito
El Valor None
Este captulo presenta un juego de Ta Te Ti contra una inteligencia artificial simple. Una
inteligencia artificial (IA) es un programa de computadora que puede responder inteligentemente
a los movimientos del jugador. Este juego no introduce ningn nuevo concepto que sea
complicado. La inteligencia artificial del juego de Ta Te Ti consiste en slo unas pocas lneas de
cdigo.
Dos personas pueden jugar Ta Te Ti con lpiz y papel. Un jugador es X y el otro es O. En un
tablero consistente en nueve cuadrados, los jugadores toman turnos para colocar sus X u O. Si un
jugador consigue ubicar tres de sus marcas en el tablero sobre la misma lnea, columna o alguna
de las dos diagonales, gana. Cuando el tablero se llena y ningn jugador ha ganado, el juego
termina en empate.
Este captulo no introduce muchos nuevos conceptos de programacin. Hace uso de nuestro
conocimiento adquirido hasta ahora para crear un jugador inteligente de Ta Te Ti. Empecemos
mirando una prueba de ejecucin del programa. El jugador hace su movimiento escribiendo el
nmero del espacio en el que quiere jugar. Estos nmeros estn dispuestos de igual forma que las
teclas numricas en tu teclado (ver Figura 10-2).
Prueba de Ejecucin de Ta Te Ti
Bienvenido al Ta Te Ti!
Deseas ser X o O?
X
La computadora ir primero.
|
|
O |
|
|
|
----------|
|
|
|
130
http://inventwithpython.com/es
|
|
----------|
|
|
|
|
|
Cul es tu prxima jugada? (1-9)
3
|
|
O |
|
|
|
----------|
|
|
|
|
|
----------|
|
O |
| X
|
|
Cul es tu prxima jugada? (1-9)
4
|
|
O |
| O
|
|
----------|
|
X |
|
|
|
----------|
|
O |
| X
|
|
Cul es tu prxima jugada? (1-9)
5
|
|
O | O | O
|
|
----------|
|
X | X |
|
|
----------|
|
O |
| X
|
|
La computadora te ha vencido! Has perdido.
Deseas volver a jugar? (s/no)?
no
Captulo 10 Ta Te Ti
131
tateti.py
1. # Ta Te Ti
2.
3. import random
4.
5. def dibujarTablero(tablero):
6.
# Esta funcin dibuja el tablero recibido como argumento.
7.
8.
# "tablero" es una lista de 10 cadenas representando la pizarra
(ignora ndice 0)
9.
print('
|
|')
10.
print(' ' + tablero[7] + ' | ' + tablero[8] + ' | ' + tablero[9])
11.
print('
|
|')
12.
print('-----------')
13.
print('
|
|')
14.
print(' ' + tablero[4] + ' | ' + tablero[5] + ' | ' + tablero[6])
15.
print('
|
|')
16.
print('-----------')
17.
print('
|
|')
18.
print(' ' + tablero[1] + ' | ' + tablero[2] + ' | ' + tablero[3])
19.
print('
|
|')
20.
21. def ingresaLetraJugador():
22.
# Permite al jugador typear que letra desea ser.
23.
# Devuelve una lista con las letras de los jugadores como primer item,
y la de la computadora como segundo.
24.
letra = ''
25.
while not (letra == 'X' or letra == 'O'):
26.
print('Deseas ser X o O?')
27.
letra = input().upper()
28.
29.
# el primer elemento de la lista es la letra del jugador, el segundo
es la letra de la computadora.
30.
if letra == 'X':
31.
return ['X', 'O']
32.
else:
33.
return ['O', 'X']
34.
35. def quienComienza():
36.
# Elije al azar que jugador comienza.
37.
if random.randint(0, 1) == 0:
132
http://inventwithpython.com/es
38.
return 'La computadora'
39.
else:
40.
return 'El jugador'
41.
42. def jugarDeNuevo():
43.
# Esta funcion devuelve True (Verdadero) si el jugador desea volver a
jugar, de lo contrario devuelve False (Falso).
44.
print('Deseas volver a jugar? (s/no)?')
45.
return input().lower().startswith('s')
46.
47. def hacerJugada(tablero, letra, jugada):
48.
tablero[jugada] = letra
49.
50. def esGanador(ta, le):
51.
# Dado un tablero y la letra de un jugador, devuelve True (verdadero)
si el mismo ha ganado.
52.
# Utilizamos reemplazamos tablero por ta y letra por le para no
escribir tanto.
53.
return ((ta[7] == le and ta[8] == le and ta[9] == le) or # horizontal
superior
54.
(ta[4] == le and ta[5] == le and ta[6] == le) or # horizontal medio
55.
(ta[1] == le and ta[2] == le and ta[3] == le) or # horizontal inferior
56.
(ta[7] == le and ta[4] == le and ta[1] == le) or # vertical izquierda
57.
(ta[8] == le and ta[5] == le and ta[2] == le) or # vertical medio
58.
(ta[9] == le and ta[6] == le and ta[3] == le) or # vertical derecha
59.
(ta[7] == le and ta[5] == le and ta[3] == le) or # diagonal
60.
(ta[9] == le and ta[5] == le and ta[1] == le)) # diagonal
61.
62. def obtenerDuplicadoTablero(tablero):
63.
# Duplica la lista del tablero y devuelve el duplicado.
64.
dupTablero = []
65.
66.
for i in tablero:
67.
dupTablero.append(i)
68.
69.
return dupTablero
70.
71. def hayEspacioLibre(tablero, jugada):
72.
# Devuelte true si hay espacio para efectuar la jugada en el tablero.
73.
return tablero[jugada] == ' '
74.
75. def obtenerJugadaJugador(tablero):
76.
# Permite al jugador escribir su jugada.
77.
jugada = ' '
78.
while jugada not in '1 2 3 4 5 6 7 8 9'.split() or not
hayEspacioLibre(tablero, int(jugada)):
79.
print('Cul es tu prxima jugada? (1-9)')
Captulo 10 Ta Te Ti
133
80.
jugada = input()
81.
return int(jugada)
82.
83. def elegirAzarDeLista(tablero, listaJugada):
84.
# Devuelve una jugada vlida en el tablero de la lista recibida.
85.
# Devuelve None si no hay ninguna jugada vlida.
86.
jugadasPosibles = []
87.
for i in listaJugada:
88.
if hayEspacioLibre(tablero, i):
89.
jugadasPosibles.append(i)
90.
91.
if len(jugadasPosibles) != 0:
92.
return random.choice(jugadasPosibles)
93.
else:
94.
return None
95.
96. def obtenerJugadaComputadora(tablero, letraComputadora):
97.
# Dado un tablero y la letra de la computadora, determina que jugada
efectuar.
98.
if letraComputadora == 'X':
99.
letraJugador = 'O'
100.
else:
101.
letraJugador = 'X'
102.
103.
# Aqu est nuestro algoritmo para nuestra IA (Inteligencia Artifical)
del Ta Te Ti.
104.
# Primero, verifica si podemos ganar en la prxima jugada
105.
for i in range(1, 10):
106.
copia = obtenerDuplicadoTablero(tablero)
107.
if hayEspacioLibre(copia, i):
108.
hacerJugada(copia, letraComputadora, i)
109.
if esGanador(copia, letraComputadora):
110.
return i
111.
112.
# Verifica si el jugador podra ganar en su prxima jugada, y lo
bloquea.
113.
for i in range(1, 10):
114.
copia = obtenerDuplicadoTablero(tablero)
115.
if hayEspacioLibre(copia, i):
116.
hacerJugada(copia, letraJugador, i)
117.
if esGanador(copia, letraJugador):
118.
return i
119.
120.
# Intenta ocupar una de las esquinas de estar libre.
121.
jugada = elegirAzarDeLista(tablero, [1, 3, 7, 9])
122.
if jugada != None:
123.
return jugada
134
http://inventwithpython.com/es
124.
125.
# De estar libre, intenta ocupar el centro.
126.
if hayEspacioLibre(tablero, 5):
127.
return 5
128.
129.
# Ocupa alguno de los lados.
130.
return elegirAzarDeLista(tablero, [2, 4, 6, 8])
131.
132. def tableroCompleto(tablero):
133.
# Devuelve True si cada espacio del tablero fue ocupado, caso
contrario devuele False.
134.
for i in range(1, 10):
135.
if hayEspacioLibre(tablero, i):
136.
return False
137.
return True
138.
139.
140. print('Bienvenido al Ta Te Ti!')
141.
142. while True:
143.
# Resetea el tablero
144.
elTablero = [' '] * 10
145.
letraJugador, letraComputadora = ingresaLetraJugador()
146.
turno = quienComienza()
147.
print(turno + ' ir primero.')
148.
juegoEnCurso = True
149.
150.
while juegoEnCurso:
151.
if turno == 'El jugador':
152.
# Turno del jugador
153.
dibujarTablero(elTablero)
154.
jugada = obtenerJugadaJugador(elTablero)
155.
hacerJugada(elTablero, letraJugador, jugada)
156.
157.
if esGanador(elTablero, letraJugador):
158.
dibujarTablero(elTablero)
159.
print('Felicidades, has ganado!')
160.
juegoEnCurso = False
161.
else:
162.
if tableroCompleto(elTablero):
163.
dibujarTablero(elTablero)
164.
print('Es un empate!')
165.
break
166.
else:
167.
turno = 'La computadora'
168.
169.
else:
Captulo 10 Ta Te Ti
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
135
# Turno de la computadora
jugada = obtenerJugadaComputadora(elTablero, letraComputadora)
hacerJugada(elTablero, letraComputadora, jugada)
if esGanador(elTablero, letraComputadora):
dibujarTablero(elTablero)
print('La computadora te ha vencido! Has perdido.')
juegoEnCurso = False
else:
if tableroCompleto(elTablero):
dibujarTablero(elTablero)
print('Es un empate!')
break
else:
turno = 'El jugador'
if not jugarDeNuevo():
break
Diseando el Programa
La Figura 10-1 muestra cmo se vera un diagrama de flujo del Ta Te Ti. En nuestro programa
del Ta Te Ti el jugador elige si quiere ser X u O. Quin toma el primer turno se elige al azar.
Luego el jugador y la computadora toman turnos para jugar.
Los recuadros a la izquierda del diagrama de flujo son lo que ocurre durante el turno del jugador.
El lado derecho muestra lo que ocurre durante el turno de la computadora. El jugador tiene un
recuadro extra para dibujar el tablero ya que la computadora no precisa ver el tablero impreso en
la pantalla. Luego de que el jugador o la computadora hacen su movimiento, revisamos si han
ganado u ocasionado un empate, y entonces cambia el turno del juego. Despus de que termina el
juego, le preguntamos al jugador si desea jugar otra vez.
136
http://inventwithpython.com/es
Figura 10-2: El tablero est ordenado igual que el teclado numrico de la computadora.
Captulo 10 Ta Te Ti
137
IA del Juego
La IA necesitar poder ver el tablero y decidir sobre qu tipo de espacios mover. Para ser claros,
definiremos tres tipos de espacios en el tablero de Ta Te Ti: esquinas, lados y el centro. La Figura
10-3 presenta un esquema de qu es cada espacio.
138
http://inventwithpython.com/es
Figura 10-4: Los cinco pasos del algoritmo Obtener movimiento de la computadora. Las
flechas salientes van al recuadro Comprobar si la computadora ha ganado.
Este algoritmo es implementado en la funcin obtenerJugadaComputadora() y las otras
funciones llamadas por obtenerJugadaComputadora().
Captulo 10 Ta Te Ti
139
El primer par de lneas son un comentario y la importacin del mdulo random para poder llamar
a la funcin randint().
140
http://inventwithpython.com/es
X | O |
|
|
----------|
|
|
|
|
|
>>> [' ', 'O', 'O', ' ', ' ', 'X', ' ', ' ', ' ', ' ']
|
|
|
|
|
|
----------|
|
| X |
|
|
----------|
|
O | O |
|
|
>>> [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
|
|
|
|
|
|
----------|
|
|
|
|
|
----------|
|
|
|
|
|
Captulo 10 Ta Te Ti
141
La condicin del bucle while contiene parntesis, lo que significa que la expresin dentro del
parntesis es evaluada primero. Si se asignara 'X' a la variable letra, la expresin se evaluara
de esta forma:
not (letra == 'X' or letra == 'O')
not (
True
or
False)
not (True)
not True
False
Si letra tiene valor 'X' o 'O', entonces la condicin del bucle es False y permite que la ejecucin
del programa contine luego del bloque while.
29.
# el primer elemento de la lista es la letra del jugador, el segundo
es la letra de la computadora.
30.
if letra == 'X':
31.
return ['X', 'O']
32.
else:
33.
return ['O', 'X']
Esta funcin devuelve una lista con dos elementos. El primer elemento (la cadena del ndice 0)
ser la letra del jugador, y el segundo elemento (la cadena del ndice 1) ser la letra de la
computadora. Estas sentencias if-else elige la lista adecuada a devolver.
La funcin quienComienza() lanza una moneda virtual para determinar quien comienza entre la
computadora y el jugador. El lanzamiento virtual de moneda se realiza llamando a
random.randint(0, 1). Si esta llamada a funcin devuelve 0, la funcin quienComienza()
devuelve la cadena 'La computadora'. De lo contrario, la funcin devuelve la cadena 'El
142
http://inventwithpython.com/es
jugador'.
El cdigo que llama a esta funcin usar el valor de retorno para saber quin har la
primera movida del juego.
La funcin jugarDeNuevo() pregunta al jugador si desea jugar de nuevo. Esta funcin devuelve
True si el jugador escribe 's' or 'S' or 's' o cualquier cosa que comience con la letra S. Con
cualquier otra respuesta, la funcin devuelve False. Esta funcin es igual a la utilizada en el juego
del Ahorcado.
La funcin hacerJugada() es simple y consiste en slo una lnea. Los parmetros son una lista
con diez cadenas llamada tablero, la letra de uno de los jugadores ('X' u 'O') llamada letra, y
un espacio en el tablero donde ese jugador quiere jugar (el cual es un entero de 1 a 9) llamado
jugada.
Pero espera un segundo. Este cdigo parece cambiar uno de los elementos de la lista tablero por
el valor en letra. Pero como este cdigo pertenece a una funcin, el parmetro tablero ser
olvidado al salir de esta funcin y abandonar el entorno de la funcin. El cambio a tablero
tambin ser olvidado.
En realidad, esto no es lo que ocurre. Esto se debe a que las listas se comportan en forma especial
cuando las pasas como argumentos a funciones. En realidad ests pasando una referencia a la lista
y no la propia lista. Vamos a aprender ahora sobre la diferencia entre las listas y las referencias a
listas.
Referencias
Prueba ingresar lo siguiente en la consola interactiva:
>>> spam = 42
>>> cheese = spam
Captulo 10 Ta Te Ti
143
Esto tiene sentido a partir de lo que sabes hasta ahora. Asignas 42 a la variable spam, y luego
copias el valor en spam y lo asignas a la variable cheese. Cuando luego cambias spam a 100, esto
no afecta al valor en cheese. Esto es porque spam y cheese son variables diferentes que
almacenan valores diferentes
Pero las listas no funcionan as. Cuando asignas una lista a una variable usando el signo =, en
realidad asignas a la variable una referencia a esa lista. Una referencia es un valor que apunta a
un dato. Aqu hay un ejemplo de cdigo que har que esto sea ms fcil de entender. Escribe esto
en la consola interactiva:
>>>
>>>
>>>
>>>
[0,
>>>
[0,
spam = [0, 1, 2, 3, 4, 5]
cheese = spam
cheese[1] = 'Hola!'
spam
'Hola!', 2, 3, 4, 5]
cheese
'Hola!', 2, 3, 4, 5]
Esto se ve raro. El cdigo slo modific la lista cheese, pero parece que tanto la lista cheese
como la lista spam han cambiado. Esto se debe a que la variable spam no contiene a la propia lista
sino una referencia a la misma, como se muestra en la Figura 10-5. La lista en s misma no est
contenida en ninguna variable, sino que existe por fuera de ellas.
144
http://inventwithpython.com/es
Observa que cheese = spam copia la referencia a lista en spam a cheese, en lugar de copiar el
propio valor de lista. Tanto spam como cheese guardan una referencia que apunta al mismo valor
de lista. Pero slo hay una lista. No se ha copiado la lista, sino una referencia a la misma. La
Figura 10-6 ilustra esta copia.
Captulo 10 Ta Te Ti
145
Figura 10-7: Cambio de la lista cambia todas las variables con referencias a la lista.
Si quieres que spam y cheese guarden dos listas diferentes, tienes que crear dos listas diferentes
en lugar de copiar una referencia:
>>> spam = [0, 1, 2, 3, 4, 5]
>>> cheese = [0, 1, 2, 3, 4, 5]
En el ejemplo anterior, spam y cheese almacenan dos listas diferentes (aunque el contenido de
ambas sea idntico). Pero si modificas una de las listas, esto no afectar a la otra porque las
variables spam y cheese tienen referencias a dos listas diferentes:
>>>
>>>
>>>
>>>
[0,
>>>
[0,
spam = [0, 1, 2, 3, 4, 5]
cheese = [0, 1, 2, 3, 4, 5]
cheese[1] = 'Hello!'
spam
1, 2, 3, 4, 5]
cheese
'Hello!', 2, 3, 4, 5]
La Figura 10-8 muestra como las dos referencias apuntan a dos listas diferentes.
146
http://inventwithpython.com/es
Cuando un valor de lista se pasa por el parmetro tablero, la variable local de la funcin es en
realidad una copia de la referencia a la lista, no una copia de la lista. Pero una copia de la
referencia sigue apuntando a la misma lista a la que apunta la referencia original. Entonces
cualquier cambio a tablero en esta funcin ocurrir tambin en la lista original. As es cmo la
funcin hacerJugada() modifica la lista original.
Los parmetros letra y jugada son copias de los valores cadena y entero que pasamos. Como
son copias de valores, si modificamos letra o jugada en esta funcin, las variables originales que
usamos al llamar a hacerJugada() no registrarn cambios.
Captulo 10 Ta Te Ti
53.
superior
54.
55.
56.
57.
58.
59.
60.
147
==
==
==
==
==
==
==
le
le
le
le
le
le
le
and
and
and
and
and
and
and
ta[5]
ta[2]
ta[4]
ta[5]
ta[6]
ta[5]
ta[5]
==
==
==
==
==
==
==
le
le
le
le
le
le
le
and
and
and
and
and
and
and
ta[6]
ta[3]
ta[1]
ta[2]
ta[3]
ta[3]
ta[1]
==
==
==
==
==
==
==
le) or
le) or
le) or
le) or
le) or
le) or
le)) #
# horizontal medio
# horizontal inferior
# vertical izquierda
# vertical medio
# vertical derecha
# diagonal
diagonal
Las lneas 53 a 60 en la funcin esGanador() son el realidad una larga sentencia return. Los
nombres ta y le son abreviaturas de los parmetros tablero y letra para no tener que escribir
tanto en esta funcin. Recuerda, a Python no le importa qu nombres uses para tus variables.
Hay ocho posibles formas de ganar al Ta Te Ti. Puedes formar una lnea horizontal arriba, al
medio o abajo. O puedes formar una lnea vertical a la izquierda, al medio o a la derecha. O
puedes formar cualquiera de las dos diagonales.
Fjate que cada lnea de la condicin comprueba si los tres espacios son iguales a la letra pasada
(combinados con el operador and) y usamos el operador or para combinar las ocho diferentes
formas de ganar. Esto significa que slo una de las ocho formas necesita ser verdadera para que
podamos afirmar que el jugador a quien pertenece la letra en le es el ganador.
Supongamos que le es 'O', y el tablero se ve as:
|
|
X |
|
|
|
----------|
|
| X |
|
|
----------|
|
O | O | O
|
|
le
le
le
le
and
and
and
and
ta[5]
ta[2]
ta[4]
ta[5]
==
==
==
==
le
le
le
le
and
and
and
and
ta[6]
ta[3]
ta[1]
ta[2]
==
==
==
==
le)
le)
le)
le)
or
or
or
or
#
#
#
#
horizontal medio
horizontal inferior
vertical izquierda
vertical medio
148
58.
59.
60.
http://inventwithpython.com/es
Primero Python reemplazar las variables ta y le por los valores que contienen::
return (('X' ==
(' ' == 'O' and
('O' == 'O' and
('X' == 'O' and
(' ' == 'O' and
(' ' == 'O' and
('X' == 'O' and
(' ' == 'O' and
'O'
'X'
'O'
' '
'X'
' '
'X'
'X'
'O'
' '
'O'
'O'
'O'
'O'
'O'
'O'
A continuacin, Python evaluar todas las comparaciones == dentro de los parntesis a un valor
Booleano:
return ((False and False and False) or
(False and False and False) or
(True and True and True) or
(False and False and True) or
(False and False and True) or
(False and False and True) or
(False and False and True) or
(False and False and True))
Luego el intrprete Python evaluar todas estas expresiones dentro de los parntesis:
return ((False) or
(False) or
(True) or
(False) or
(False) or
(False) or
(False) or
(False))
Como ahora hay slo un valor dentro del parntesis, podemos eliminarlos:
return (False or
False or
True or
False or
Captulo 10 Ta Te Ti
149
False or
False or
False or
False)
Una vez ms, eliminamos los parntesis y nos quedamos con un solo valor:
return True
Entonces dados estos valores para ta y le, la expresin se evaluara a True. As es cmo el
programa puede decir si uno de los jugadores ha ganado el juego.
La funcin obtenerDuplicadoTablero() est aqu para que podamos fcilmente hacer una copia
de una dada lista de 10 cadenas que representa un tablero de Ta Te Ti en nuestro juego. Algunas
veces querremos que nuestro algoritmo IA haga modificaciones temporarias a una copia
provisoria del tablero sin cambiar el tablero original. En ese caso, llamaremos a esta funcin para
hacer una copia de la lista del tablero. La nueva lista se crea en la lnea 64, con los corchetes []
de lista vaca.
Pero la lista almacenada en dupTablero en la lnea 64 es slo una lista vaca. El bucle for recorre
el parmetro tablero, agregando una copia de los valores de cadena en el tablero original al
tablero duplicado. Finalmente, despus del bucle, se devuelve dupTablero. La funcin
obtenerDuplicadoTablero() construye una copia del tablero original y devuelve una referencia
a este nuevo tablero, y no al original.
150
72.
73.
http://inventwithpython.com/es
Esta es una funcin simple que, dado un tablero de Ta Te Ti y una posible jugada, confirmar si
esa jugada est disponible o no. Recuerda que los espacios libres en la lista tablero se indican
como una cadena con un espacio simple. Si el elemento en el ndice del espacio indicado no es
igual a una cadena con un espacio simple, el espacio est ocupado y no es una jugada vlida.
Captulo 10 Ta Te Ti
151
Los operadores not se agregan a ambos lados de modo que la condicin ser True cuando
cualquiera de estos requerimientos deje de cumplirse. Esto har que el bucle pida al jugador una
nueva jugada una y otra vez hasta que la jugada ingresada sea vlida.
Finalmente, en la lnea 81, se devuelve la forma entera de la jugada ingresada por el jugador.
Recuerda que input() devuelve una cadena, as que la funcin int() es llamada para devolver la
forma entera de la cadena.
Evaluacin en Cortocircuito
Puede ser que hayas notado un posible problema problema en nuestra funcin
obtenerJugadaJugador(). Qu pasara si el jugador ingresara 'Z' o alguna otra cadena no
entera? La expresin jugada not in '1 2 3 4 5 6 7 8 9'.split() sobre el lado izquierdo
devolvera False de acuerdo con lo esperado, y entonces evaluaramos la expresin sobre el lado
derecho del operador or.
Pero llamar a int('Z') ocasionara un error. Python muestra este error porque la funcin int()
slo puede tomar cadenas o caracteres numricos, tales como '9' o '0', no cadenas como 'Z'.
Como un ejemplo de este tipo de error, prueba ingresar esto en la consola interactiva:
>>> int('42')
42
>>> int('Z')
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
int('Z')
ValueError: invalid literal for int() with base 10: 'Z'
Pero cuando juegas al Ta Te Ti e intentas ingresar 'Z' en tu jugada, este error no ocurre. La razn
de esto es que la condicin del bucle while est siendo cortocircuitada.
Evaluar en cortocircuito quiere decur que como el lado izquierdo de la palabra reservada or
(jugada not in '1 2 3 4 5 6 7 8 9'.split()) se evala a True, el intrprete Python saba
que la expresin completa ser evaluada a True. No importa si la expresin sobre el lado derecho
de la palabra reservada or se evala a True o False, porque slo uno de los valores junto al
operador or precisa ser True.
Piensa en esto: La expresin True or False se evala a True y la expresin True or True
tambin se evala a True. Si el valor sobre el lado izquierdo es True, no importa qu valor est
sobre el lado derecho:
152
http://inventwithpython.com/es
Captulo 10 Ta Te Ti
153
Si el lado izquierdo del operador and es False, entonces la expresin completa ser False. No
importa lo que sea el lado derecho del operador and, de modo que Python no se molesta en
evaluarlo. Tanto False and True como False and False se evalan a False, por lo que
Python cortocircuita la evaluacin.
154
http://inventwithpython.com/es
Sin embargo, elegirAzarDeLista() comprobar primero que es vlido realizar una jugada en
ese espacio. La lista jugadasPosibles comienza siendo una lista vaca. El bucle for itera sobre
listaJugada. Las jugadas para las cuales hayEspacioLibre() devuelve True se agregan a
jugadasPosibles usando el mtodo append().
91.
92.
93.
94.
if len(jugadasPosibles) != 0:
return random.choice(jugadasPosibles)
else:
return None
En este punto, la lista jugadasPosibles contiene todas las jugadas que estaban en listaJugada
y tambin son espacios libres en la lista tablero. Si la lista no est vaca, hay al menos una jugada
posible.
La lista podra estar vaca. Por ejemplo, si listaJugada fuera [1, 3, 7, 9] pero todas las
esquinas del tablero estuviesen tomadas, la lista jugadasPosibles sera []. En ese caso,
len(jugadasPosibles) se evaluara a 0 y la funcin devolvera el valor None. La prxima
seccin explica el valor None.
El Valor None
El valor None representa la ausencia de un valor. None es el nico valor del tipo de datos
NoneType. Puede ser til emplear el valor None cuando necesites un valor que exprese no existe
o ninguno de los anteriores.
Pongamos por caso que tienes una variable llamada respuestaExmen para guardar la respuesta a
una pregunta de seleccin mltiple. La variable podra contener True o False para indicar la
respuesta del usuario. Podras asignar None a respuestaExmen si el usuario saltease la pregunta
sin responderla. Usar None es una forma clara de indicar que el usuario no ha respondido la
pregunta.
Las funciones que retornan llegando al final de la funcin (es decir, sin alcanzar una sentencia
return) devolvern None. El valor None se escribe sin comillas, con una N mayscula y las
letras one en minsculas.
Como una nota al margen, None no se muestra en la consola interactiva como ocurrira con otros
valores:
>>> 2 + 2
4
>>> 'Esto es una cadena.'
'Esto es una cadena.'
>>> None
Captulo 10 Ta Te Ti
155
Las funciones que aparentan no devolver nada en realidad devuelven el valor None. Por ejemplo,
print() devuelve None:
>>> spam = print('Hola mundo!')
Hola mundo!
>>> spam == None
True
Primero, ver si hay una jugada con que la computadora pueda ganar el juego. Si la hay,
hacer esa jugada. En caso contrario, continuar al segundo paso.
Segundo, ver si hay una jugada con la que el jugador pueda vencer a la computadora. Si
la hay, la computadora debera jugar en ese lugar para bloquear al jugador. En caso
contrario, continuar al tercer paso.
Cuarto, comprobar si el centro est libre. Si lo est, jugar all. En caso contrario,
continuar al quinto paso.
Quinto, jugar sobre cualquiera de los lados (espacios 2, 4, 6 u 8). No hay ms pasos, pues
si hemos llegado al quinto paso significa que slo quedan los espacios sobre los lados.
156
http://inventwithpython.com/es
Antes que nada, si la computadora puede ganar en la siguiente jugada, debera hacer la jugada
ganadora inmediatamente. El bucle for que empieza en la lnea 105 itera sobre cada posible
jugada de 1 a 9. El cdigo dentro del bucle simula lo que ocurrira si la computadora hiciera esa
jugada.
La primera lnea en el bucle (lnea 106) crea una copia de la lista tablero. Esto es para que la
jugada simulada dentro del bucle no modifique el tablero real de Ta Te Ti guardado en la variable
tablero. La funcin obtenerDuplicadoTablero() devuelve una copia idntica pero
independiente del tablero.
La lnea 107 comprueba si el espacio est libre y, si es as, simula hacer la jugada en la copia del
tablero. Si esta jugada resulta en una victoria para la computadora, la la funcin devuelve el
entero correspondiente a esa jugada.
Si ninguna de las jugadas posibles resulta en una victoria, el bucle concluye y la ejecucin del
programa contina en la lnea 113.
A continuacin, el cdigo simula un movimiento del jugador en cada uno de los espacios. Este
cdigo es similar al bucle anterior, excepto que es la letra del jugador que se coloca sobre la copia
Captulo 10 Ta Te Ti
157
del tablero. Si la funcin esGanador() muestra que el jugador ganara con este movimiento, la
computadora devuelve esta jugada para bloquear la victoria del jugador.
Si el jugador humano no puede ganar en la siguiente movida, el bucle for eventualmente
concluye y la ejecucin del programa contina en la lnea 121.
Comprobando las Esquinas, Centro y Espacios sobre los Lados (en ese
Orden)
120.
121.
122.
123.
Si ninguna de las esquinas est disponible, la lnea 127 intentar jugar en el centro. Si el centro no
est libre, la ejecucin contina sobre la lnea 130.
129.
130.
Este cdigo tambin llama a elegirAzarDeLista(), slo que le pasamos una lista con los
espacios sobre los lados ([2, 4, 6, 8]). Esta funcin no devolver None pues los espacios sobre
los lados son los nicos que pueden estar disponibles. Esto concluye la funcin
obtenerJugadaComputadora() y el algoritmo IA.
158
http://inventwithpython.com/es
La ltima funcin es tableroCompleto(). Esta funcin devuelve True si la lista tablero pasada
como argumento tiene una 'X' o una 'O' en cada ndice (excepto por el ndice 0, que es ignorado
por el cdigo). Si hay al menos un casillero en el tablero con espacio simple ' ' asignado, esta
funcin devolver False.
El bucle for nos permite comprobar los espacios 1 a 9 en el tablero de Ta Te Ti. Apenas encuentra
un espacio libre en el tablero (es decir, cuando hayEspacioLibre(tablero, i) devuelve True)
la funcin tableroCompleto() devuelve False.
Si la ejecucin concluye todas las operaciones del bucle, significa que ninguno de los espacios
est libre. Entonces se ejecutar la lnea 137 y devolver True.
La lnea 140 es la primera lnea que no est dentro de una funcin, de modo que es la primera
lnea de cdigo que se ejecuta al entrar a este programa. Consiste en el saludo al jugador.
142. while True:
143.
# Resetea el tablero
144.
elTablero = [' '] * 10
Este bucle while tiene al valor True por condicin, y continuar iterando hasta que la ejecucin
llegue a una sentencia break. La lnea 144 configura el tablero principal de Ta Te Ti que
usaremos, al cual llamaremos elTablero. Es una lista de 10 cadenas, donde cada una de ellas es
un espacio simple ' '.
En lugar de escribir esta lista completa, la lnea 44 usa replicacin de listas. Es ms corto y claro
escribir [' '] * 10 que escribir [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '].
turno = quienComienza()
print(turno + ' ir primero.')
Captulo 10 Ta Te Ti
148.
159
juegoEnCurso = True
while juegoEnCurso:
El bucle de la lnea 150 continuar alternando entre el cdigo del turno del jugador y el del turno
de la computadora, mientras la juegoEnCurso tenga asignado el valor True.
151.
152.
153.
154.
155.
if esGanador(elTablero, letraJugador):
dibujarTablero(elTablero)
print('Felicidades, has ganado!')
juegoEnCurso = False
else:
if tableroCompleto(elTablero):
dibujarTablero(elTablero)
160
164.
165.
http://inventwithpython.com/es
print('Es un empate!')
break
Si el jugador no gan con esta ltima jugada, tal vez esta movida ha llenado el tablero y
ocasionado un empate. En este bloque else, la funcin tableroCompleto() devuelve True si no
hay ms movimientos disponibles. En ese caso, el bloque if que comienza en la lnea 162
muestra el tablero empatado y comunica al jugador que ha habido un empate. La ejecucin sale
entonces del bucle while y salta a la lnea 186.
166.
167.
else:
turno = 'La computadora'
else:
# Turno de la computadora
jugada = obtenerJugadaComputadora(elTablero, letraComputadora)
hacerJugada(elTablero, letraComputadora, jugada)
if esGanador(elTablero, letraComputadora):
dibujarTablero(elTablero)
print('La computadora te ha vencido! Has perdido.')
juegoEnCurso = False
else:
if tableroCompleto(elTablero):
dibujarTablero(elTablero)
print('Es un empate!')
break
else:
turno = 'El jugador'
Las lneas 170 a 184 son casi idnticas al cdigo del turno del jugador en las lneas 152 a 167. La
nica diferencia es que se comprueba si ha habido un empate luego del turno de la computadora
en lugar de hacerlo luego del turno del jugador.
Captulo 10 Ta Te Ti
161
if not jugarDeNuevo():
break
Las lneas 186 y 187 se encuentran inmediatamente a continuacin del bloque while que
comienza con la sentencia while en la lnea 150. Se asigna False a juegoEnCurso cuando el
juego ha terminado, por lo que en este punto se pregunta al jugador si desea jugar de nuevo.
Si jugarDeNuevo() devuelve False, la condicin de la sentencia if es True (porque el operador
not invierte el valor Booleano) y se ejecuta la sentencia break. Esto interrumpe la ejecucin del
bucle while que haba comenzado en la lnea 142. Como no hay ms lneas de cdigo a
continuacin de ese bloque while, el programa termina.
Resumen
Crear un programa que pueda jugar un juego se reduce a considerar cuidadosamente todas las
situaciones posibles en las que la IA pueda encontrarse y cmo responder en cada una de esas
situaciones. La IA del Ta Te Ti es simple porque no hay muchos movimientos posibles en Ta Te
Ti comparado con un juego como el ajedrez o las damas.
Nuestra IA simplemente comprueba si puede ganar en la prxima jugada. Si no es posible,
bloquea la movida del jugador cuando est a punto de ganar. En cualquier otro caso la IA
simplemente intenta jugar en cualquier esquina disponible, luego el centro y por ltimo los lados.
Este es un algoritmo simple y fcil de seguir.
La clave para implementar nuestro algoritmo IA es hacer una copia de los datos del tablero y
simular jugadas sobre la copia. De este modo, el cdigo de IA puede hacer esa jugada en el
tablero real. Este tipo de simulacin es efectivo a la hora de predecir si una jugada es buena o no.
http://inventwithpython.com/es
162
Captulo 11
PANECILLOS
Temas Tratados En Este Captulo:
Operadores de Asignacin Aumentada, +=, -=, *=, /=
La Funcin random.shuffle()
Los Mtodos de Lista sort() y join()
Interpolacin de Cadenas (tambin llamado Formateo de Cadenas)
Indicador de Conversin %s
Bucles Anidados
En este captulo aprenders algunos nuevos mtodos y funciones que vienen con Python.
Tambin aprenders acerca de operadores de asignacin aumentada e interpolacin de cadenas.
Estos conecptos no te permitirn hacer nada que no pudieras hacer antes, pero son buenos atajos
que hacen ms fcil escribir tu cdigo.
Panecillos es un juego simple que puedes jugar con un amigo. Tu amigo piensa un nmero
aleatorio de 3 cifras diferentes, y t intentas adivinar qu nmero es. Luego de cada intento, tu
amigo te dar tres tipos de pistas:
Panecillos Ninguna de las tres cifras del nmero que has conjeturado est en el nmero
secreto.
Pico Una de las cifras est en el nmero secreto, pero no en el lugar correcto.
Puedes recibir ms de una pista luego de un intento. Si el nmero secreto es 456 y t conjeturas
546, la pista sera "fermi pico pico". El nmero 6 da "Fermi" y el 5 y 4 dan "pico pico".
Prueba de Ejecucin
Estoy pensando en un nmero de 3 dgitos. Intenta adivinar cul es.
Aqu hay algunas pistas:
Cuando digo:
Eso significa:
Pico
Un dgito es correcto pero en la posicin incorrecta.
Fermi
Un dgito es correcto y en la posicin correcta.
Panecillos
Ningn dgito es correcto.
He pensado un nmero. Tienes 10 intentos para adivinarlo.
Conjetura #1:
Captulo 11 Panecillos
163
123
Fermi
Conjetura #2:
453
Pico
Conjetura #3:
425
Fermi
Conjetura #4:
326
Panecillos
Conjetura #5:
489
Panecillos
Conjetura #6:
075
Fermi Fermi
Conjetura #7:
015
Fermi Pico
Conjetura #8:
175
Lo has adivinado!
Deseas volver a jugar? (s o no)
no
panecillos.py
1. import random
2. def obtenerNumSecreto(digitosNum):
3.
# Devuelve un numero de largo digotosNUm, compuesto de dgitos nicos
al azar.
4.
numeros = list(range(10))
5.
random.shuffle(numeros)
6.
numSecreto = ''
7.
for i in range(digitosNum):
8.
numSecreto += str(numeros[i])
9.
return numSecreto
10.
11. def obtenerPistas(conjetura, numSecreto):
12.
# Devuelve una palabra con las pistas Panecillos Pico y Fermi en ella.
164
http://inventwithpython.com/es
13.
if conjetura == numSecreto:
14.
return 'Lo has adivinado!'
15.
16.
pista = []
17.
18.
for i in range(len(conjetura)):
19.
if conjetura[i] == numSecreto[i]:
20.
pista.append('Fermi')
21.
elif conjetura[i] in numSecreto:
22.
pista.append('Pico')
23.
if len(pista) == 0:
24.
return 'Panecillos'
25.
26.
pista.sort()
27.
return ' '.join(pista)
28.
29. def esSoloDigitos(num):
30.
# Devuelve True si el nmero se compone slo de dgitos. De lo
contrario False.
31.
if num == '':
32.
return False
33.
34.
for i in num:
35.
if i not in '0 1 2 3 4 5 6 7 8 9'.split():
36.
return False
37.
38.
return True
39.
40. def jugarDeNuevo():
41.
# Esta funcion devuelve True si el jugador desea vovler a jugar, de lo
contrario False.
42.
print('Deseas volver a jugar? (s o no)')
43.
return input().lower().startswith('s')
44.
45. digitosNum = 3
46. MAXADIVINANZAS = 10
47.
48. print('Estoy pensando en un nmero de %s dgitos. Intenta adivinar cul
es.' % (digitosNum))
49. print('Aqu hay algunas pistas:')
50. print('Cuando digo:
Eso significa:')
51. print(' Pico
Un dgito es correcto pero en la posicin
incorrecta.')
52. print(' Fermi
Un dgito es correcto y en la posicin correcta.')
53. print(' Panecillos
Ningn dgito es correcto.')
54.
55. while True:
Captulo 11 Panecillos
165
56.
numSecreto = obtenerNumSecreto(digitosNum)
57.
print('He pensado un nmero. Tienes %s intentos para adivinarlo.' %
(MAXADIVINANZAS))
58.
59.
numIntentos = 1
60.
while numIntentos <= MAXADIVINANZAS:
61.
conjetura = ''
62.
while len(conjetura) != digitosNum or not esSoloDigitos(conjetura):
63.
print('Conjetura #%s: ' % (numIntentos))
64.
conjetura = input()
65.
66.
pista = obtenerPistas(conjetura, numSecreto)
67.
print(pista)
68.
numIntentos += 1
69.
70.
if conjetura == numSecreto:
71.
break
72.
if numIntentos > MAXADIVINANZAS:
73.
print('Te has quedado sin intentos. La respuesta era %s.' %
(numSecreto))
74.
75.
if not jugarDeNuevo():
76.
break
Al comienzo del programa, importamos el mdulo random. Luego definimos una funcin llamada
obtenerNumSecreto(). La funcin crea un nmero secreto sin cifras repetidas. En lugar de
nmeros secretos de slo 3 cifras, el parmetro digitosNum permite a la funcin crear un nmero
secreto con cualquier cantidad de cifras. Por ejemplo, puedes crear un nmero secreto de cuatro o
seis cifras pasando 4 6 en digitosNum.
numeros = list(range(10))
random.shuffle(numeros)
166
http://inventwithpython.com/es
La funcin random.shuffle()
La funcin random.shuffle() cambia aleatoriamente el orden de los elementos de una lista. Esta
funcin no devuelve un valor, sino que modifica la lista que se le pasa "in situ". Esto es similar al
modo en que la funcin hacerJugada() en el captulo Ta Te Ti modifica la lista que se le pasa,
en lugar de devolver una nueva lista con el cambio. Por eso no necesitamos escribir numeros =
random.shuffle(numeros).
Experimenta con la funcin random.shuffle() ingresando el siguiente cdigo en la consola
interactiva:
>>>
>>>
>>>
[0,
import random
spam = list(range(10))
print(spam)
1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> random.shuffle(spam)
>>> print(spam)
[3, 0, 5, 9, 6, 8, 2, 4, 1, 7]
>>> random.shuffle(spam)
>>> print(spam)
[1, 2, 5, 9, 4, 7, 0, 3, 6, 8]
>>> random.shuffle(spam)
>>> print(spam)
[9, 8, 3, 5, 4, 7, 1, 2, 0, 6]
La idea es que el nmero secreto en Panecillos tenga cifras nicas. El juego Panecillos es mucho
ms divertido si no tienes cifras duplicadas en el nmero secreto, como en '244' o '333'. La
funcin shuffle() te ayudar a lograr esto.
numSecreto = ''
for i in range(digitosNum):
numSecreto += str(numeros[i])
return numSecreto
Captulo 11 Panecillos
167
El nmero secreto ser una cadena de las primeras digitosNum cifras de la lista mezclada de
enteros. Por ejemplo, si la lista mezclada en numeros fuese [9, 8, 3, 5, 4, 7, 1, 2, 0, 6]
y digitosNum fuese 3, entonces la cadena devuelta por obtenerNumSecreto() ser '983'.
Para hacer esto, la variable numSecreto comienza siendo una cadena vaca. El bucle for en la
lnea 7 itera digitosNum veces. En cada iteracin del bucle, el entero en el ndice i es copiado de
la lista mezclada, convertido a cadena, y concatenado al final de numSecreto.
Por ejemplo, si numeros se refiere a la lista [9, 8, 3, 5, 4, 7, 1, 2, 0, 6], entonces en la
primera iteracin, numeros[0] (es decir, 9) ser pasado a str(), que a su vez devolver '9' el
cual es concatenado al final de numSecreto. En la segunda iteracin, lo mismo ocurre con
numeros[1] (es decir, 8) y en la tercera iteracin lo mismo ocurre con numeros[2] (es decir, 3).
El valor final de numSecreto que se devuelve es '983'.
Observa que numSecreto en esta funcin contiene una cadena, no un entero. Esto puede parecer
raro, pero recuerda que no puedes concatenar enteros. La expresin 9 + 8 + 3 se evala a 20,
pero lo que t quieres ahora es '9' + '8' + '3', que se evala a '983'.
Los operadores de asignacin aumentada son un atajo que te libera de volver a escribir el nombre
de la variable. El siguiente cdigo hace lo mismo que el cdigo de ms arriba:
spam = 42
spam += 10
168
http://inventwithpython.com/es
40
>>> spam *= 3
>>> spam
120
>>> spam /= 10
>>> spam
12.0
La funcin obtenerPistas() devolver una sola cadena con las pistas fermi, pico, y panecillos
dependiendo de los parmetros conjetura y numSecreto. El paso ms obvio y sencillo es
comprobar si la conjetura coincide con el nmero secreto. En ese caso, la lnea 14 devuelve 'Lo
has adivinado!'.
16.
17.
18.
19.
20.
21.
22.
pista = []
for i in range(len(conjetura)):
if conjetura[i] == numSecreto[i]:
pista.append('Fermi')
elif conjetura[i] in numSecreto:
pista.append('Pico')
Si la conjetura no coincide con el nmero secreto, el cdigo debe determinar qu pistas dar al
jugador. La lista en pista comenzar vaca y se le aadirn cadenas 'Fermi' y 'Pico' a medida
que se necesite.
Hacemos esto recorriendo cada posible ndice en conjetura y numSecreto. Las cadenas en
ambas variables sern de la misma longitud, de modo que la lnea 18 podra usar tanto
len(conjetura) como len(numSecreto) y funcionar igual. A medida que el valor de i cambia
de 0 a 1 a 2, y as sucesivamente, la lnea 19 comprueba si la primera, segunda, tercera, etc. letra
de conjetura es la misma que el nmero correspondiente al mismo ndice en de numSecreto. Si
es as, la lnea 20 agregar una cadena 'Fermi' a pista.
De lo contrario, la lnea 21 comprobar si la cifra en la posicin i-sima de conjetura existe en
algn lugar de numSecreto. Si es as, ya sabes que la cifra est en algn lugar del nmero secreto
pero no en la misma posicin. Entonces la lnea 22 aadir 'Pico' a pista.
Captulo 11 Panecillos
23.
24.
169
if len(pista) == 0:
return 'Panecillos'
Si la pista est vaca luego del bucle, significa que no hay ninguna cifra correcta en la
conjetura. En ese caso, la lnea 24 devuelve la cadena 'Panecillos' como nica pista.
pista.sort()
Las listas tienen un mtodo llamado sort() que reordena los elementos de la lista para dejarlos
en orden alfabtico o numrico. Prueba escribir lo siguiente en la consola interactiva:
>>> spam = ['gato', 'perro', 'murcilago', 'antlope']
>>> spam.sort()
>>> spam
['antlope', 'gato', 'murcilago', 'perro']
>>>
>>>
>>>
[0,
spam = [9, 8, 3, 5, 4, 7, 1, 2, 0, 6]
spam.sort()
spam
1, 2, 3, 4, 5, 6, 7, 8, 9]
El mtodo sort() no devuelve una lista ordenada, sino que reordena la lista sobre la cual es
llamado in situ. De esta forma funciona tambin el mtodo reverse().
Nunca querrs usar esta lnea de cdigo: return spam.sort() porque esto devolvera el valor
None (que es lo que devuelve sort()). En lugar de esto probablemente quieras una lnea separada
spam.sort() y luego la lnea return spam.
La razn por la cual quieres ordenar la lista pista es para eliminar la informacin extra basada en
el orden de las pistas. Si pista fuese ['Pico', 'Fermi', 'Pico'], eso permitira al jugador
saber que la cifra central de la conjetura est en la posicin correcta. Como las otras dos pistas
son Pico, el jugador sabra que todo lo que tiene que hacer es intercambiar la primera cifra con la
tercera para obtener el nmero secreto.
Si las pistas estn siempre en orden alfabtico, el jugador no puede saber a cul de los nmeros se
refiere la pista Fermi. As queremos que sea el juego.
170
http://inventwithpython.com/es
El mtodo de cadena join() devuelve una lista de cadenas agrupada en una nica cadena. La
cadena sobre la cual se llama a este mtodo (en la lnea 27 es un espacio simple, ' ') aparece
entre las cadenas de la lista. Por ejemplo, escribe lo siguiente en la consola interactiva:
>>> ' '.join(['Mi', 'nombre', 'es', 'Zophie'])
'Mi nombre es Zophie'
>>> ', '.join(['Vida', 'el Universo', 'y Todo'])
'Vida, el Universo, y Todo'
Entonces la cadena que se devuelve en la lnea 27 corresponde a todas las cadenas en pista
agrupadas con un espacio simple entre cada una. El mtodo de cadena join() es algo as como el
opuesto al mtodo de cadena split(). Mientras que split() devuelve una lista a travs de
fragmentar una cadena, join() devuelve una cadena a travs de agrupar una lista.
for i in num:
if i not in '0 1 2 3 4 5 6 7 8 9'.split():
return False
return True
El bucle for itera sobre cada caracter en la cadena num. El valor de i tendr slo un caracter en
cada iteracin. Dentro del bloque for, el cdigo comprueba si i no existe en la lista devuelta por
'0 1 2 3 4 5 6 7 8 9'.split(). (El valor devuelto por split() es equivalente a ['0', '1',
'2', '3', '4', '5', '6', '7', '8', '9'] pero es ms fcil de escribir.) Si no existe,
sabemos que uno de los caracteres de num no es un dgito. En ese caso, la lnea 36 devuelve
False.
Si la ejecucin contina luego del bucle for, sabemos que cada caracter en num es una cifra. En
ese caso, a lnea 38 devuelve True.
Captulo 11 Panecillos
171
Despus de haber definido todas las funciones, aqu comienza el programa. En lugar de usar el
entero 3 para la cantidad de cifras en el nmero secreto, usamos la variable constante
digitosNum. Lo mismo corre para el uso de MAXADIVINANZAS en lugar del entero 10 para la
cantidad de conjeturas que se permite al jugador. Ahora ser fcil cambiar el nmero de
conjeturas o cifras del nmero secreto. Slo precisamos cambiar la lnea 45 46 y el resto del
programa funcionar sin ms cambios.
Las llamadas a la funcin print() explicarn al jugador las reglas de juego y lo que significan
las pistas Pico, Fermi, y Panecillos. La llamada a print() de la lnea 48 tiene % (digitosNum)
agregado al final y %s dentro de la cadena. Esto es una tcnica conocida como interpolacin de
cadenas.
Interpolacin de Cadenas
Interpolacin de cadenas es una abreviatura del cdigo. Normalmente, si quieres usar los valores
de cadena dentro de variables en otra cadena, tienes que usar el operador concatenacin + :
>>> nombre = 'Alicia'
>>> evento = 'fiesta'
172
http://inventwithpython.com/es
Como puedes ver, puede ser difcil escribir una lnea que concatena varias cadenas. En lugar de
esto, puedes usar interpolacin de cadenas, lo cual te permite utilizar comodines como %s. Estos
comodines se llaman especificadores de conversin. Luego colocas todos los nombres de
variables al final. Cada %s es reemplazado por una variable al final de la lnea. Por ejemplo, el
siguiente cdigo hace lo mismo que el anterior:
>>>
>>>
>>>
>>>
>>>
nombre = 'Alicia'
evento = 'fiesta'
dnde = 'la piscina'
da = 'sbado'
hora = '6:00pm'
La interpolacin de cadenas puede hacer tu cdigo mucho ms fcil de escribir. El primer nombre
de variable corresponde al primer %s, la segunda variable va con el segundo %s y as
sucesivamente. debes tener tantos especificadores de conversin %s como variables.
Otro beneficio de usar interpolacin de cadenas en lugar de concatenacin es que la interpolacin
funciona con cualquier tipo de datos, no slo cadenas. Todos los valores se convierten
automticamente al tipo de datos cadena. Si concatenases un entero a una cadena, obtendras este
error:
>>> spam = 42
>>> print('Spam == ' + spam)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly
La concatenacin de cadenas slo funciona para dos o ms cadenas, pero spam es un entero.
Tendras que recordar escribir str(spam) en lugar de spam. Pero con interpolacin de cadenas,
esta conversin a cadenas se realiza automticamente. Prueba escribir lo siguiente en la consola
interactiva:
Captulo 11 Panecillos
173
>>> spam = 42
>>> print('Spam es %s' % (spam))
Spam is 42
La lnea 55 es un bucle while infinito que tiene una condicin True, por lo que seguir
repitindose eternamente hasta que se ejecute una sentencia break. Dentro de este bucle infinito,
se obtiene un nmero secreto de la funcin obtenerNumSecreto(), pasndole a la misma
digitosNum para indicar cuntas cifras debe tener el nmero. Este nmero secreto es asignado a
numSecreto. Recuerda, el valor en numSecreto es una cadena, no un entero.
La lnea 57 indica al jugador cuntas cifras hay en el nmero secreto usando interpolacin de
cadena en lugar de concatenacin. La lnea 59 asigna 1 a la variable numIntentos para indicar
que este es el primer intento. Entonces la lnea 60 tiene un nuevo bucle while que se ejecuta
mientras numIntentos sea menor o igual que MAXADIVINANZAS.
conjetura = ''
while len(conjetura) != digitosNum or not esSoloDigitos(conjetura):
print('Conjetura #%s: ' % (numIntentos))
conjetura = input()
La variable conjetura almacenar la conjetura del jugador devuelta por input(). El cdigo
contina iterando y pidiendo al jugador una nueva conjetura hasta que el jugador ingrese una
conjetura vlida. Una conjetura vlida est compuesta nicamente por cifras y la misma cantidad
de cifras que el nmero secreto. Esta es la funcin que cumple el bucle while que comienza en la
lnea 62.
Se asigna una cadena vaca a la variable conjetura en la lnea 61, de modo que la condicin del
bucle while sea False en la primera comprobacin, asegurando que la ejecucin entre al bucle.
174
http://inventwithpython.com/es
Una vez que la ejecucin pasa el bucle while que comienza la lnea 62, la variable conjetura
contiene un nmero vlido. El mismo se pasa junto con numSecreto a la funcin
obtenerPistas(). Esta funcin devuelve una cadena de pistas, las cuales se muestran al jugador
en la lnea 67. La lnea 68 incrementa numIntentos usando el operador de asignacin aumentada
para la suma.
if not jugarDeNuevo():
break
Captulo 11 Panecillos
175
La lnea 75 pregunta al jugador si desea jugar otra vez llamando a la funcin jugarDeNuevo(). Si
jugarDeNuevo() devuelve False, se sale del bucle while comenzado en la lnea 55. Como no
hay ms cdigo luego de este bucle, el programa termina.
Si jugarDeNuevo() devolviese True, la ejecucin no pasara por la sentencia break y regresara a
la lnea 55. El programa generara entonces un nuevo nmero secreto para que el jugador pudiese
jugar otra vez.
Resumen
Panecillos es un juego simple de programar pero puede ser difcil de vencer. Pero si continas
jugando, descubrirs eventualmente mejores formas de conjeturar y usar las pistas que el juego te
da. De la misma forma, te convertirs en un mejor programador si continas practicando.
Este captulo ha introducido algunas nuevas funciones y mtodos (random.shuffle(), sort() y
join()), junto con un par de atajos. Los operadores de asignacin aumentada requieren escribir
menos cuando quieres cambiar el valor relativo de una variable, tal como en spam = spam + 1,
que puede abreviarse como spam += 1. La interpolacin de cadenas puede hacer que tu cdigo
sea mucho ms legible colocando %s (llamado especificador de conversin) dentro de la cadena
en lugar de usar muchas operaciones de concatenacin de cadenas.
El siguiente captulo no se enfoca directamente en programacin, pero ser necesario para los
juegos que crearemos en los ltimos captulos de este libro. Aprenderemos los conceptos
matemticos de coordenadas cartesianas y nmeros negativos. Nosotros slo los usaremos en los
juegos Sonar, Reversi y Evasor, pero estos conceptos se usan en muchos juegos. Si ya conoces
estos conceptos, igual puedes hojear el siguiente captulo para refrescarlos.
176
http://inventwithpython.com/es
Captulo 12
COORDENADAS CARTESIANAS
Temas Tratados En Este Captulo:
Sistemas de Coordenadas Cartesianas
Los ejes X e Y
La Propiedad Conmutativa de la Adicin
Valores absolutos y la funcin abs()
Este captulo no introduce un nuevo juego, sin embargo repasa ciertos conceptos matemticos que
sern utilizados en el resto de los juegos en este libro.
Cuando miras un juego 2D (como Tetris o un viejo juego de Super Nintendo o Sega Genesis)
puedes notar que la mayora de los grficos en la pantalla pueden moverse hacia la izquierda o
derecha (la primera dimensin) o arriba y abajo (la segunda dimensin, es decir 2D). Para que
podamos crear juegos con objetos movindose en dos dimensiones (como una pantalla de
computadora bidimensional), necesitamos un sistema que pueda traducir un lugar en la pantalla a
enteros que nuestro programa pueda interpretar.
Aqu es cuando se utilizan los sistemas de coordenadas Cartesianas. Las coordenadas pueden
apuntar a un punto muy especfico de la pantalla para que nuestro programa pueda rastrear
diferentes areas en la pantalla.
Figura 12-1: Un ejemplo de tablero de ajedrez con un caballo negro en a, 4 y un caballo blanco
en e, 6.
177
Un problema en muchos juegos es cmo hablar de puntos exactos en un tablero. Una solucin
comn a este problema es marcar cada fila y columna individual del tablero con una letra y un
nmero. La Figura 12-1 es un talbero de ajedrez con sus filas y columnas marcadas.
En el ajedrez, el caballo luce como una cabeza de caballo. El caballo blanco se encuentra en el
punto e, 6 y el caballo negro en el a, 4. Tambin podemos observar que todos los espacios en la
fila 7 y todos los espacios en la columna c se encuentran vacios.
Una cuadrcula con filas y columnas numeradas como el tablero de ajedrez es un sistema de
coordenadas cartesianas. Al utilizar una etiqueta para filas y columnas, podemos dar una
coordenada para un nico espacio en el tablero. Esto realmente nos ayuda a describirle a uan
computadora la posicin exacta que deseamos. Si aprendiste coordenadas Cartesianas en alguna
clase de matemtica, sabrs que usualmente se tanto las filas como columnas se representan con
nmeros. Esto es til, porque de otro modo luego de la columna 26 nos quedaramos sin letras.
Dicho tablero se vera como la Figura 12-2.
Figura 12-2: El mismo tablero de ajedrez pero con coordenadas numricas para filas y columnas.
Los nmeros de izquierda a derecha que describen las columnas son parte del eje X. Los nmeros
de arriba a abajo que describen las filas son parte del eje Y. Cuando describimos una coordenada,
siempre empleamos el eje X primero, seguido del eje Y. Eso significa que el caballo de la figura
superior se encuentra en la coordenada 5, 6 (y no 6, 5). El caballo blanco se encuentra en la
coordenada 1, 4 (no confundir con 4, 1).
Nota que para mover el caballo negro a la posicin del caballo blanco, el caballo negro debe
moverse dos espacios hacia arriba y luego cuatro a la derecha. (O moverse cuatro a la derecha y
luego dos arriba.) Perno no necesitamos mirar el tablero para deducir esto. Si sabemos que el
caballo blanco se encuentra en 5, 6 y el negro en 1, 4, entonces simplemente podemos restar para
obtener la informacin.
178
http://inventwithpython.com/es
Resta la coordenada X del caballo negro y la coordenada X del caballo blanco: 5 - 1 = 4. Eso
significa que el caballo negro debe moverse por el eje X cuatro espacios.
Resta la coordenada Y del caballo negro y la coordenada Y del caballo blanco: 6 - 4 = 2. Eso
significa que el caballo negro debe moverse por el eje Y dos espacios.
Nmeros Negativos
Otro concepto utilizado en las coordenadas Cartesianas son los nmeros negativos. Estos son
nmeros menores a cero. Ponemos un signo menos frente al nmero para mostrar que es un
nmero netagivo. -1 es menor que 0. Y -2 menor que -1. Y -3 menor que -2. Si piensas en los
nmeros regulares (llamados positivos) empezando del 1 e incrementando, puedes pensar en los
nmeros negativos comenzando del -1 y decreciendo. 0 en si mismo no es positivo ni negativo.
En esta imagen, puedes ver los nmeros positivos creciendo hacia la derecha y los negativos
decreciendo a la izquierda:
179
Figura 12-6: Incluso si el caballero blanco comienza en una coordenada negativa, moverse a la
derecha suma a la coordenada.
Figura 12-7: Ponendo dos lneas de nmeros juntas se crea un sistema de coordenadas
Cartesianas.
180
http://inventwithpython.com/es
La lnea de nmeros es igual al eje-X. Si hicieramos que la lnea de nmeros vaya de arriba a
abajo en vez de izquierda a derecha, sera igual al eje-Y. Sumando un nmero positivo (o
restando un nmero negativo) movera el caballo hacia arriba de lal nea, y restando un nmero
positivo (o sumando un nmero negativo) movera el caballo hacia abaho. Cuando ponemos
ambas lneas de nmeros juntas, tenemos un sistema de coordenadas Cartesianas tal como en la
Figura 12-7. La coordenada 0, 0 posee un nombre especial: el origen.
Trucos Matemticos
Sumar o restar nmeros negativos parece fcil cuando tienes una lnea de nmeros frente a ti,
pero puede ser igual de fcil cuando slo tienes los nmeros. Aqu hay tres trucos que puedes
hacer para que evaluar estas expresiones te sea ms sencillo.
181
182
http://inventwithpython.com/es
183
columna (ver la Figura 12-12). Este sola ser el mximo tamao de pantalla que los monitores
soportaban. Aunque los monitores actuales pueden mostrar mucho ms texto, no asumiremos que
la pantalla del usuario es mayor a 80 por 25.
Resumen
Esto no fue mucha matemtica para aprender a programar. De hecho, la mayora de la
programacin no requiere mucho conocimiento de matemtica. Hasta este captulo, nos las
arreglabamos con simples sumas y multiplicaciones.
Los sistemas de coordenadas Cartesianas son necesarios para describir con exactitud donde se
encuentra una posicin en un rea bidimensional. Las coordenadas se componen de dos nmeros:
eje-X y eje-Y. El eje-X corre de izquierda a derecha y el eje-Y de arriba a abajo. En una pantalla
de computadora (y casi siempre en programacin), el eje-X comienza en 0 a la izquierda e
incrementa hacia la derecha. El eje-Y comienza en 0 en la parte superior e incrementa hacia
abajo.
Los tres trucos que aprendimos en este captulo facilitan sumar enteros positivos y negativos. El
primer truco es un signo menos que comer un signo ms a su izquierda. El segundo truco es que
dos signos menos juntos se combinan en un signo ms. Y el tercer truco es que puedes
intercambiar las posiciones de los nmeros que ests sumando. Esto es llamado la propiedad
conmutativa de la adicin.
Para el resto de este libro, utilizaremos los conceptos aprendidos en este captulo en nuestros
juegos ya que tendrn reas bidimensionales en ellos. Todos los juegos grficos requieren
conocimientos del funcionamiento de las coordenadas Cartesianas.
184
http://inventwithpython.com/es
Captulo 13
185
Figura 13-1: El rea cuadrada del sonar toca el (oculto) cofre del tesoro.
Figura 13-2: Combinando mltiples reas cuadradas se muestra donde el tesoro puede estar.
Pero mltiples dispositivos de sonar trabajando en conjunto pueden reducir el rea a un punto
exacto donde las reas se intersecten. Ver Figura 13-2. (Normalmente estas reas seran
circulares, pero en este juego utilizaremos cuadrados para facilitar la programacin.)
0
1
2
3
186
4
5
6
7
8
9
10
11
12
13
14
http://inventwithpython.com/es
``~~`~~~``~``~~````~`~`~`~``~~~``~~```~`~~`~~`~`~`~~`~~~~```
~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~`
``~~`````~~~~`~`~~~```~~~~`~~`~~`~~```~~`~~~`~~~``~`~~~``~~~
`~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~
`~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`~`````~`~~```~`~~~~`
~```~~`~`~``~``~~``~``~```~`~``~~~~`~`~`~~~`~`~`~`~~~``~~```
```~`~```~``~``~`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~
```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~`
~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~
`~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~
`~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~
012345678901234567890123456789012345678901234567890123456789
1
2
3
4
5
An tienes 16 dispositivos sonar. Falta encontrar 3 cofres.
Dnde quieres dejar caer el siguiente dispositivo sonar? (0-59
salir)
10 10
1
2
3
4
5
012345678901234567890123456789012345678901234567890123456789
0 `~~~`~~~`~`~~`~~~~~`~``~~~~`~`~~~`~``~``~~````~`~```~`~~~~``
1 ~`~~~```~~~~`~`~~`~``~`~~```~`~`~~`~`~~~~~~`~`````~`~~`~~~~`
2 `~``~``~~~`~``~`~`~``~`````~~~~~~~~~`~`~~`~``~~~~~```~~`~```
3 ``~`~~``~`~``~`~`~`~~`~`~~`~`~``~~~`~``~````~``````~~~~``~``
4 ``~~`~~~``~``~~````~`~`~`~``~~~``~~```~`~~`~~`~`~`~~`~~~~```
5 ~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~`
6 ``~~`````~~~~`~`~~~```~~~~`~~`~~`~~```~~`~~~`~~~``~`~~~``~~~
7 `~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~
8 `~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`~`````~`~~```~`~~~~`
9 ~```~~`~`~``~``~~``~``~```~`~``~~~~`~`~`~~~`~`~`~`~~~``~~```
10 ```~`~```~5`~``~`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~
11 ```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~`
12 ~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~
13 `~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~
14 `~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~
012345678901234567890123456789012345678901234567890123456789
1
2
3
4
5
Tesoro detectado a una distancia 5 del dispositivo sonar.
An tienes 15 dispositivos sonar. Falta encontrar 3 cofres.
Dnde quieres dejar caer el siguiente dispositivo sonar? (0-59
salir)
15 6
1
2
3
4
5
012345678901234567890123456789012345678901234567890123456789
0 `~~~`~~~`~`~~`~~~~~`~``~~~~`~`~~~`~``~``~~````~`~```~`~~~~``
1 ~`~~~```~~~~`~`~~`~``~`~~```~`~`~~`~`~~~~~~`~`````~`~~`~~~~`
2 `~``~``~~~`~``~`~`~``~`````~~~~~~~~~`~`~~`~``~~~~~```~~`~```
3 ``~`~~``~`~``~`~`~`~~`~`~~`~`~``~~~`~``~````~``````~~~~``~``
4
5
6
7
8
9
10
11
12
13
14
0-14) (o teclea
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
0-14) (o teclea
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
``~~`~~~``~``~~````~`~`~`~``~~~``~~```~`~~`~~`~`~`~~`~~~~```
~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~`
``~~`````~~~~`~4~~~```~~~~`~~`~~`~~```~~`~~~`~~~``~`~~~``~~~
`~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~
`~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`~`````~`~~```~`~~~~`
~```~~`~`~``~``~~``~``~```~`~``~~~~`~`~`~~~`~`~`~`~~~``~~```
```~`~```~5`~``~`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~
```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~`
~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~
`~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~
`~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~
012345678901234567890123456789012345678901234567890123456789
1
2
3
4
5
Tesoro detectado a una distancia 4 del dispositivo sonar.
An tienes 14 dispositivos sonar. Falta encontrar 3 cofres.
Dnde quieres dejar caer el siguiente dispositivo sonar? (0-59
salir)
15 10
1
2
3
4
5
012345678901234567890123456789012345678901234567890123456789
0 `~~~`~~~`~`~~`~~~~~`~``~~~~`~`~~~`~``~``~~````~`~```~`~~~~``
1 ~`~~~```~~~~`~`~~`~``~`~~```~`~`~~`~`~~~~~~`~`````~`~~`~~~~`
2 `~``~``~~~`~``~`~`~``~`````~~~~~~~~~`~`~~`~``~~~~~```~~`~```
3 ``~`~~``~`~``~`~`~`~~`~`~~`~`~``~~~`~``~````~``````~~~~``~``
4 ``~~`~~~``~``~~````~`~`~`~``~~~``~~```~`~~`~~`~`~`~~`~~~~```
5 ~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~`
6 ``~~`````~~~~`~O~~~```~~~~`~~`~~`~~```~~`~~~`~~~``~`~~~``~~~
7 `~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~
8 `~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`~`````~`~~```~`~~~~`
9 ~```~~`~`~``~``~~``~``~```~`~``~~~~`~`~`~~~`~`~`~`~~~``~~```
10 ```~`~```~O`~``O`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~
11 ```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~`
12 ~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~
13 `~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~
14 `~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~
012345678901234567890123456789012345678901234567890123456789
1
2
3
4
5
Has encontrado un cofre del tesoro hundido!
An tienes 13 dispositivos sonar. Falta encontrar 2 cofres.
Dnde quieres dejar caer el siguiente dispositivo sonar? (0-59
salir)
187
4
5
6
7
8
9
10
11
12
13
14
0-14) (o teclea
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
0-14) (o teclea
188
http://inventwithpython.com/es
1
2
3
4
5
6
7
8
9
10
11
12
13
14
~`~~~```~~~~`~`~~`~``~`~~```~O~`~~`~`~~~~~~`~`````~`~~`~~~~` 1
`~``~``~~~`~``~`~`~``~`````~~O~~~O~~`~`~~`~``~~~~~```~~`~``` 2
``~3~~``8`~``~`~`~`~~`~`~~`~`~``~~~`~`O~````~``````~~~~``~`` 3
``~~`~~~``~``~~````~`~`~`~O`~~O``~~```~`~~`~~`~`~`~~`~~~~``` 4
~~```~~~`~`~~``~`~``~```~`~~`~~~~~`~~``~`~`~~~`~~`~`~`~`~~~` 5
``~~`````~~~~`~O~~~```~~~~`~~`~~`~~```~~`~~~`~~~``O`~~~``~~~ 6
`~`````````~```~``~``~~`~~~~`~~``~``~~~```~`~~`~``~``~~```~~ 7
`~````~```~`~~`~~~`~~``~~~``~`~``~~~``~`O```0`~`~~```~`~~~~` 8
~```~~`~`~``~``~~``~``~```~O~``~~~~`~`~`~~~`~`~`~`~~~``~~``` 9
```~`~```~O`~``O`~~`~``~````~``~~~`~~`~~``~~~~`~~~`~`~~````~ 10
```~```~~~`~```~~`~~~`~`````~`~~`~`~~`~~`~`~~`~~~````~````~` 11
~~~`~`~~~``~~~~~~`~~~``~`~`~~`~`~~`~```~~~```~~`~~`~``~``~`~ 12
`~~````~~``~```~~~`~```~`~~~~~~~~~`~~``~~~~~`````~`~`~``~~~~ 13
`~~`~`~````~```~`~`~```~~`~~~~`~```~``~``~``~~~````~~``````~ 14
012345678901234567890123456789012345678901234567890123456789
1
2
3
4
5
Tesoro detectado a una distancia 4 del dispositivo sonar.
Nos hemos quedado sin dispositivos sonar! Ahora tenemos que dar la vuelta y
dirigirnos
de regreso a casa dejando tesoros en el mar! Juego terminado.
Los cofres restantes estaban aqu:
0, 4
Quieres jugar de nuevo? (s o no)
no
sonar.py
1. # Sonar
2.
3. import random
4. import sys
5.
6. def dibujarTablero(tablero):
7.
# Dibuja la estructura de datos del tablero.
8.
9.
lneah = '
' # espacio inicial para los nmeros a lo largo del lado
izquierdo del tablero
10.
for i in range(1, 6):
11.
lneah += (' ' * 9) + str(i)
189
12.
13.
# imprimir los nmeros a lo largo del borde superior
14.
print(lneah)
15.
print('
' + ('0123456789' * 6))
16.
print()
17.
18.
# imprimir cada una de las 15 filas
19.
for i in range(15):
20.
# los nmeros de una sola cifra deben ser precedidos por un
espacio extra
21.
if i < 10:
22.
espacioExtra = ' '
23.
else:
24.
espacioExtra = ''
25.
print('%s%s %s %s' % (espacioExtra, i, obtenerFila(tablero, i),
i))
26.
27.
# imprimir los nmeros a lo largo del borde inferior
28.
print()
29.
print('
' + ('0123456789' * 6))
30.
print(lneah)
31.
32.
33. def obtenerFila(tablero, fila):
34.
# Devuelve una cadena con la estructura de datos de un tablero para
una fila determinada.
35.
filaTablero = ''
36.
for i in range(60):
37.
filaTablero += tablero[i][fila]
38.
return filaTablero
39.
40. def obtenerNuevoTablero():
41.
# Crear una nueva estructura de datos para un tablero de 60x15.
42.
tablero = []
43.
for x in range(60): # la lista principal es una lista de 60 listas
44.
tablero.append([])
45.
for y in range(15): # cada lista en la lista principal tiene 15
cadenas de un solo caracter
46.
# usar diferentes caracteres para el ocano para hacerlo ms
fcil de leer.
47.
if random.randint(0, 1) == 0:
48.
tablero[x].append('~')
49.
else:
50.
tablero[x].append('`')
51.
return tablero
52.
53. def obtenerCofresAleatorios(nmCofres):
190
http://inventwithpython.com/es
54.
# Crear una lista de estructuras de datos cofre (listas de dos tems
con coordenadas x, y)
55.
cofres = []
56.
for i in range(nmCofres):
57.
cofres.append([random.randint(0, 59), random.randint(0, 14)])
58.
return cofres
59.
60. def esMovidaVlida(x, y):
61.
# Devuelve True si las coordenadas pertenecen al tablero, de lo
contrario False.
62.
return x >= 0 and x <= 59 and y >= 0 and y <= 14
63.
64. def realizarMovida(board, chests, x, y):
65.
# Cambia la estructura de datos del tablero agregando un caracter de
dispositivo sonar. Elimina los cofres
66.
# de la lista de cofres a medida que son encontrados. Devuelve False
si la movida no es vlida.
67.
# En caso contrario, devuelve una cadena con el resultado de esa
movida.
68.
if not esMovidaVlida(x, y):
69.
return False
70.
71.
menorDistancia = 100 # cualquier cofre estar a una distancia menor
que 100.
72.
for cx, cy in cofres:
73.
if abs(cx - x) > abs(cy - y):
74.
distancia = abs(cx - x)
75.
else:
76.
distancia = abs(cy - y)
77.
78.
if distancia < menorDistancia: # queremos el cofre ms cercano.
79.
menorDistancia = distancia
80.
81.
if menorDistancia == 0:
82.
# xy est directamente sobre un cofre!
83.
tablero.remove([x, y])
84.
return 'Has encontrado un cofre del tesoro hundido!'
85.
else:
86.
if menorDistancia < 10:
87.
tablero[x][y] = str(menorDistancia)
88.
return 'Tesoro detectado a una distancia %s del dispositivo
sonar.' % (menorDistancia)
89.
else:
90.
tablero[x][y] = 'O'
91.
return 'El sonar no ha detectado nada. Todos los cofres estn
fuera del alcance del dispositivo.'
92.
191
93.
94. def ingresarMovidaJugador():
95.
# Permite al jugador teclear su movida. Devuelve una lista de dos
tems con coordenadas xy.
96.
print('Dnde quieres dejar caer el siguiente dispositivo sonar? (0-59
0-14) (o teclea salir)')
97.
while True:
98.
movida = input()
99.
if movida.lower() == 'salir':
100.
print('Gracias por jugar!')
101.
sys.exit()
102.
103.
movida = movida.split()
104.
if len(movida) == 2 and movida[0].isdigit() and
movida[1].isdigit() and esMovidaVlida(int(movida[0]), int(movida[1])):
105.
return [int(movida[0]), int(movida[1])]
106.
print('Ingresa un nmero de 0 a 59, un espacio, y luego un nmero
de 0 a 14.')
107.
108.
109. def jugarDeNuevo():
110.
# Esta funcin devuelve True si el jugador quiere jugar de nuevo, de
lo contrario devuelve False.
111.
print('Quieres jugar de nuevo? (s o no)')
112.
return input().lower().startswith('s')
113.
114.
115. def mostrarInstrucciones():
116.
print(Instrucciones:
117. Eres el capitn de Simn, un buque cazador de tesoros. Tu misin actual
118. es encontrar los tres cofres con tesoros perdidos que se hallan ocultos en
119. la parte del ocano en que te encuentras y recogerlos.
120.
121. Para jugar, ingresa las coordenadas del punto del ocano en que quieres
122. colocar un dispositivo sonar. El sonar puede detectar cul es la distancia
al cofre ms cercano.
123. Por ejemplo, la d abajo indica dnde se ha colocado el dispositivo, y los
124. nmeros 2 representan los sitios a una distancia 2 del dispositivo. Los
125. nmeros 4 representan los sitios a una distancia 4 del dispositivo.
126.
127.
444444444
128.
4
4
129.
4 22222 4
130.
4 2
2 4
131.
4 2 d 2 4
132.
4 2
2 4
133.
4 22222 4
192
http://inventwithpython.com/es
134.
4
4
135.
444444444
136. Pulsa enter para continuar...''')
137.
input()
138.
139.
print('''Por ejemplo, aqu hay un cofre del tesoro (la c) ubicado a
una distancia
140. 2 del dispositivo sonar (la d):
141.
142.
22222
143.
c
2
144.
2 d 2
145.
2
2
146.
22222
147.
148. El punto donde el dispositivo fue colocado se indicar con una d.
149.
150. Los cofres del tesoro no se mueven. Los dispositivos sonar pueden detectar
151. cofres hasta una distancia 9. Si todos los cofres estn fuera del alcance,
152. el punto se indicar con un O.
153.
154. Si un dispositivo es colocado directamente sobre un cofre del tesoro, has
155. descubierto la ubicacin del cofre, y este ser recogido. El dispositivo
156. sonar permanecer all.
157.
158. Cuando recojas un cofre, todos los dispositivos sonar se actualizarn para
159. localizar el prximo cofre hundido ms cercano.
160. Pulsa enter para continuar...''')
161.
input()
162.
print()
163.
164.
165. print(' S O N A R !')
166. print()
167. print('Te gustara ver las instrucciones? (s/no)')
168. if input().lower().startswith('s'):
169.
mostrarInstrucciones()
170.
171. while True:
172.
# configuracin del juego
173.
dispositivosSonar = 16
174.
elTablero = obtenerNuevoTablero()
175.
losCofres = obtenerCofresAleatorios(3)
176.
dibujarTablero(elTablero)
177.
movidasPrevias = []
178.
179.
while dispositivosSonar > 0:
193
180.
# Comienzo de un turno:
181.
182.
# mostrar el estado de los dispositivos sonar / cofres
183.
if dispositivosSonar > 1: extraSsonar = 's'
184.
else: extraSsonar = ''
185.
if len(losCofres) > 1: extraScofre = 's'
186.
else: extraScofre = ''
187.
print('An tienes %s dispositivos%s sonar. Falta encontrar %s
cofre%s.' % (dispositivosSonar, extraSsonar, len(losCofres), extraScofre))
188.
189.
x, y = ingresarMovidaJugador()
190.
movidasPrevias.append([x, y]) # debemos registrar todas las
movidas para que los dispositivos sonar puedan ser actualizados.
191.
192.
resultadoMovida = realizarMovida(elTablero, losCofres, x, y)
193.
if resultadoMovida == False:
194.
continue
195.
else:
196.
if resultadoMovida == 'Has encontrado uno de los cofres del
tesoro!':
197.
# actualizar todos los dispositivos sonar presentes en el
mapa.
198.
for x, y in movidasPrevias:
199.
realizarMovida(elTablero, losCofres, x, y)
200.
dibujarTablero(elTablero)
201.
print(resultadoMovida)
202.
203.
if len(losCofres) == 0:
204.
print('Has encontrado todos los cofres del tesoro!
Felicitaciones y buena partida!')
205.
break
206.
207.
dispositivosSonar -= 1
208.
209.
if dispositivosSonar == 0:
210.
print('Nos hemos quedado sin dispositivos sonar! Ahora tenemos
que dar la vuelta y dirigirnos')
211.
print('de regreso a casa dejando tesoros en el mar! Juego
terminado.')
212.
print('
Los cofres restantes estaban aqu:')
213.
for x, y in losCofres:
214.
print('
%s, %s' % (x, y))
215.
216.
if not jugarDeNuevo():
217.
sys.exit()
194
http://inventwithpython.com/es
Diseando el Programa
Antes de intentar entender el cdigo fuente, juega el juego un par de veces para entender lo que
sucede. El juego Sonar usa listas de listas y otras variables complicadas, llamadas estructuras de
datos. Las estructuras de datos son variables que almacenan arreglos de valores para representar
algo. Por ejemplo, en el captulo de Ta Te Ti, la estructura de datos de tablero era una lista de
cadenas. La cadena representaba una X, O, o un espacio en blanco y el ndice de la cadena dentro
de la lista representaba el espacio en el tablero. El juego del Sonar tendr estructuras de datos
similares para las ubicaciones de los tesoros y los dispositivos de sonar.
Las lneas 3 y 4 importan los mdulos random y sys. El mdulo sys contiene la funcin exit(),
la cul hace que el programa termine inmediatamente. Esta funcion es utilizada luego en el
programa.
El tablero del Sonar es un ocano de arte ASCII con los ejes X e Y a su alrededor. La tilde
invertida (`) y la virguilla (~) sern utilizadas para las olas del ocano. Se ver as:
0
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
012345678901234567890123456789012345678901234567890123456789
~~~`~``~~~``~~~~``~`~`~`~`~~`~~~`~~`~``````~~`~``~`~~```~`~`
`~`~````~~``~`~```~```~```~`~~~``~~`~~~``````~`~``~~``~~`~~`
```~~~~`~`~~```~~~``~````~~`~`~~`~`~`~```~~`~``~~`~`~~~~~~`~
~~~~`~~~``~```~``~~`~`~~`~`~~``~````~`~````~```~`~`~`~`````~
~```~~~~~`~~````~~~~```~~~`~`~`~````~`~~`~`~~``~~`~``~`~``~~
`~```~`~`~~`~~~```~~``~``````~~``~`~`~~~~`~~``~~~~~~`~```~~`
``~~`~~`~``~`````~````~~``~`~~~~`~~```~~~``~`~`~~``~~~```~~~
``~``~~~~~~```~`~```~~~``~`~``~`~~~~~~```````~~~`~~`~~`~~`~~
~~`~`~~```~``~~``~~~``~~`~`~~`~`~```~```~~~```~~~~~~`~`~~~~`
```~``~`~~~`~~```~``~``~~~```~````~```~`~~`~~~~~`~``~~~~~```
`~~~~```~`~````~`~`~~``~`~~~~`~``~``~```~~```````~`~``~`````
~~`~`~~`~``~`~~~````````````````~~`````~`~~``~`~~~~`~~~`~~`~
0
1
2
3
4
5
6
7
8
9
10
11
195
12 ~~`~~~~```~~~`````~~``~`~`~~``````~`~~``~```````~~``~~~`~~`~ 12
13 `~``````~~``~`~~~```~~~~```~~`~`~~~`~```````~~`~```~``~`~~~~ 13
14 ~~~``~```~`````~~`~`~``~~`~``~`~~`~`~``~`~``~~``~`~``~```~~~ 14
012345678901234567890123456789012345678901234567890123456789
1
2
3
4
5
Primero, crea una variable de cadena con 1, 2, 3, 4 y 5 espaciados con espacios anchos
(para marcar las coordenadas 10, 20, 30, 50 y 50 del eje X).
Segundo, utiliza esa cadena para mostrar las coordenadas del eje X en la parte superior de
la pantalla.
Tercero, imprime cada fila del ocano as como tambin las coordenadas del eje Y a
ambos lados de la pantalla.
Mira la parte superior del tablero en la Figura 13-3. Posee un + en vez de espacios en blanco para
que puedas contar los espacios facilmente:
+++++++++++++1+++++++++2+++++++++3
+++0123456789012345678901234567890123456789
+0 ~~~`~``~~~``~~~~``~`~`~`~`~~`~~~~```~`~` 0
Figura 13-3: El espaciado utilizado para imprimir la parte superior del tablero.
Los nmeros en la primer lnea que marcna las posiciones de diez, tienen nueve espacios entre
ellas, y hay trece espacios frente al 1. Las lneas 9 a 11 crean esta cadena con con esta lnea y la
guarda en una variable llamada lneah.
13.
196
14.
15.
16.
http://inventwithpython.com/es
print(lneah)
print('
' + ('0123456789' * 6))
print()
Para imprimir los nmeros en el tope del tablero, primero imprime el contenido de la variable
lineah. Luego en la siguiente lnea, imprime tres espacios (para alinear la lnea), y luego imprime
la cadena '012345678901234567890123456789012345678901234567890123456789'. Pero
como atajo puedes usar ('0123456789' * 6), que se evala a la misma cadena.
Las lneas 19 a 25 imprimen las olas del ocano, incluyendo los nmeros a los costados para
mostar el eje Y. Para el ciclo se imprimen las filas 0 a 14, incluyendo el nmero de fila a ambos
lados.
Hay un pequeo problema. Los nmeros con slo un dgito (como 0, 1, 2..) slo ocupan un
espacio, sin embargo nmeros con dos dgitos (como 10, 11 y 12) ocupan dos espacios. Las filas
no se alinearn si las coordenadas tienen distintos tamaos. Se ver as:
8 ~~`~`~~```~``~~``~~~``~~`~`~~`~`~```~```~~~```~~~~~~`~`~~~~` 8
9 ```~``~`~~~`~~```~``~``~~~```~````~```~`~~`~~~~~`~``~~~~~``` 9
10 `~~~~```~`~````~`~`~~``~`~~~~`~``~``~```~~```````~`~``~````` 10
11 ~~`~`~~`~``~`~~~````````````````~~`````~`~~``~`~~~~`~~~`~~`~ 11
La solucin es sencilla. Agrega espacios frente a todos los nmeros de un dgito. Las lineas 21 a
24 asignan a espacioExtra un espacio o una cadena vaca. La variable espacioExtra siempre es
impresa, pero slo posee un caracter en las filas de un dgito. Caso contrario, es una cadena vaca.
De esta manera, todas las filas se alinearn cuando las imprimas.
La funcin obtenerLinea() requere un nmero de fila y devuelve una cadena representando las
olas de dicha fila. Sus dos parmetros son la estructura de datos del tablero almacenada en la
variable tablero y un nmero de fila. Veremos esta funcin luego.
197
Las lneas 27 a 30 son similares a las 13 a 16. Ellas imprimen el eje X en la parte inferior de la
pantalla.
obtenerFila(tablero, fila):
# Devuelve una cadena con la estructura de datos de un tablero para
determinada.
filaTablero = ''
for i in range(60):
filaTablero += tablero[i][fila]
return filaTablero
Mientras el parmetro tablero es una estructura de datos para todo el ocano, la funcin
obtenerFila() crea una cadena para una sla lnea.
Primero, establece filaTablero a una cadena vaca. Las coordenadas Y son pasadas como el
parmetro fila. La cadena se consigue concatenando tablero[0][fila], tablero[1][fila] y
as sucesivamente hasta talbero[59][fila]. Esto se debe a que la fila contiene 60 carcteres,
desde ndice 0 al 59.
El ciclo for en la lnea 36 itera sobre los enteros 0 a 59. En cada iteracin, el siguiente carcter en
el tablero es copiado al final de filaTablero. Para cuando el ciclo se termina, filaTablero
tendr el arte ASCII de las olas completos y ser devuelto.
Una nueva estructura de datos de tablero es necesaria al inicio de cada juego. La estructura es
una lista de listas de cadenas. La primera lista repesenta el eje X. Dado que el tablero posee un
198
http://inventwithpython.com/es
ancho de 60 carcteres, la primera lsta necesita contener 60 listas. Crea un ciclo for que agregue
60 listas en blanco.
45.
for y in range(15): # cada lista en la lista principal tiene 15
cadenas de un solo caracter
46.
# usar diferentes caracteres para el ocano para hacerlo ms
fcil de leer.
47.
if random.randint(0, 1) == 0:
48.
tablero[x].append('~')
49.
else:
50.
tablero[x].append('`')
Pero el tablero es ms que slo una listta de 60 listas en blanco. Cada una de las 60 listas
representa una coordenada X del juego. Hay 15 filas en el tablero, as que cada una de estas 60
listas debe contener 15 carcteres en ellas. La lnea 45 es otro ciclo para agregar 15 carcteres que
representan el ocano.
El ocano ser un conjunto de '~' y '`' aleatorios. Si el valor de retorno de
random.randint() es 0, agrega '~'. De lo contrario agrega '`'. Esto le dar un aspecto aleatorio y
picado al ocano.
Recuerda que la variable tablero es una lista de 60 listas, cada una de las cuales contiene 15
cadenas. Esto significa que obtienes la cadena en la coordenada 26, 12, mediante
tablero[26][12] y no tablero[12][26]. La coordenada X primero y luego la Y.
51.
return tablero
El juego tambin decide aleatoriamente donde se encuentran los cofres ocultos. Los cofres son
representados como una lista de listas de dos enteros. Estos dos enteros sern el eje X e Y de un
nico cofre.
199
Por ejemplo, si la estructura de datos del cofre es [[2, 2], [2, 4], [10, 0]], significar que
hay tres cofres, uno en 2,2, otro en 2,4 y un tercero en 10,0.
El parmetro nmCofres le dice a la funcin cuantos cofres generar. La lnea 56 itera nmCofres
veces, y por cada una de esas iteraciones la lnea 57 agregar una lista de dos nmeros enteros
aleatorios. La coordenada X puede ser cualquiera de 0 a 59, y la Y cualquiera de 0 a 14. La
expresin [random.randint(0, 59), random.randint(0, 14)] que es pasada al mtodo
append se evaluar a una lista [2, 2] o [2, 4] o [10, 0]. Esta lista se agregar a cofres.
Cuando el jugador escribe las coordenadas X e Y donde quiere colocar un sonar, pueden no ser
coordenadas vlidas. Las coordenadas X deben ser de 0 a 59 y las coordenadas Y deben ser de 0 a
14.
La funcin esMovidaVlida() utiliza una simple expresin con operadores para asegurar que
cada parte del a condicin sea True (Verdadera). An con slo una expresin False (Falsa), toda
la expresin se evala como False. Esta funcin devuelve un valor Booleano.
En el juego Sonar, el tablero es actualizado para mostrar un nmero por cada uno de los
dispositivos empleados para mostrar que tan lejos se encuentra del cofre ms cercano. Cuando el
jugador realiza una movida dndole al programa unas coordenadas X e Y, el tablero cambia en
funcin de las posiciones de los cofres.
La funcin realizarMovida() requiere cuatro parmetros: las estructuras de datos
correspondiente a tablero y cofres, y coordenadas X e Y. La lnea 69 devuelve False si las
http://inventwithpython.com/es
200
71.
menorDistancia = 100 # cualquier cofre estar a una distancia menor
que 100.
72.
for cx, cy in cofres:
73.
if abs(cx - x) > abs(cy - y):
74.
distancia = abs(cx - x)
75.
else:
76.
distancia = abs(cy - y)
77.
78.
if distancia < menorDistancia: # queremos el cofre ms cercano.
79.
menorDistancia = distancia
Dadas las coordenadas de donde el jugador desea colocar un sonar y una lista de coordenadas XY
para los cofres, necesitars una algoritmo para encontrar cul cofre es el ms cercano.
201
Figura 13-3: Los cofres de tesoros representados por [[5, 0], [0, 2], [4, 2]].
202
http://inventwithpython.com/es
La lnea 72 utiliza el truco de asignacin mltiple para el ciclo. Por ejemplo, la asignacin
enlatado, huevos = [5, 10] asignar 5 a enlatado y 10 a huevos.
203
Dado que cofres es una lista donde cada item dentro de ella es una lista de dos enteros, el
primero de estos enteros es asignado a cx y el segundo a cy. Entonces si cofres posee el valor
[[5, 0], [0, 2], [4, 2]], cx tendr el valor de 5 y cy tendr el valor 0 en la primera
iteracin del ciclo.
La lnea 73 determina cul es mayor: el valor absoluto de la diferencia de las coordenadas X, o el
absoluto de la diferencia de las coordenadas Y. abs(cx - x) > abs(cy - y) parece ser una
forma mucho ms corta de decir eso, no? Las lneas 73 a 76 asignan el mayor de los valores a la
variable distancia.
Entonces en cada iteracin del ciclo, la variable distancia almacenar la distancia del sonar al
cofre. Pero queremos la distancia ms cercana de todos los tesoros. Aqu es cuando
menorDistancia entra en juego. Cuando la variable distancia sea menor que menorDistancia,
el valor en distancia se convierte en el nuevo valor de menorDistancia.
Dale a menorDistancia el valor alto imposible de 100 al comienzo del ciclo para que al menos
uno de los cofres que encuentres sea puesto en menorDistancia. Para el momento que el ciclo
concluya, sabrs que menorDistancia contiene la distancia mnima entre el sonar y todos los
cofres de tesoro del juego.
El valor 10 fue removido de la lista x. El mtodo remove() remueve la primer ocurrencia del
valor que le pases, y slo el primero. Por ejemplo, prueba lo siguiente en la consola interactiva:
>>>
>>>
>>>
[5,
Es evidente que slo el primer valor 42 fue removido, pero el segundo y tecer valor continuan en
la lista. El mtodo remove() causar error si intentas remover un valor que no se encuentra en la
lista:
>>> x = [5, 42]
204
http://inventwithpython.com/es
>>> x.remove(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
81.
82.
83.
84.
if menorDistancia == 0:
# xy est directamente sobre un cofre!
tablero.remove([x, y])
return 'Has encontrado un cofre del tesoro hundido!'
205
Asumiendo que el jugador no ha ingresado 'salir', el juego debe asegurarse de que sea una
jugada vlida: dos enteros separados por un espacio. La lnea 103 llama el mtodo split()
(separar) en movida como el nuevo valor de movida.
Si el jugador ingres un valor como '1 2 3', entonces la lista que retorna split() ser ['1',
'2', '3']. En dicho caso, la expresin len(movida) == 2 ser False y toda la expresin se
evaluar como False. Python no evala al resto de la expresin debido a cortocircuito (explicado
en el Captulo 10).
Si la longitud de la lista es 2, entonces ambos valores se encuentran en los ndices movida[0] y
movida[1]. Para verificar que esos valores seran numricos (como '2' o '17'), puedes usar una
funcin como esSoloDigitos() del Captulo 11. Pero Python ya posee una funcin que hace
esto.
El mtodo de cadena isdigit() devolver True si la cadena consiste nicamente de nmeros.
Caso contraro devuelve False. Prueba ingresando lo siguiente en la consola interactiva:
>>> '42'.isdigit()
True
>>> 'cuarenta'.isdigit()
False
>>> ''.isdigit()
False
>>> 'hola'.isdigit()
False
>>> x = '10'
>>> x.isdigit()
True
Tanto movida[0].isdigit() como movida[1].isdigit() deben ser True para que toda la
condicin sea True. Al final de la lnea 104 se llama a la funcin esMovidaVlida() para
verificar que las coordenadas XY existan en el tablero.
206
http://inventwithpython.com/es
Si toda la condicin es True, la lnea 105 devuelve una lista de dos enteros de las coordenadas
XY. En otro caso, la ejecucin har un ciclo y el jugador ser invitado a ingresar las coordenadas
otra vez.
207
imprimir la prxima cadena. Esto se debe a que la ventana IDLE slo puede imprimir una cierta
cantidad de texto a la vez.
139.
print('''Por ejemplo, aqu hay un cofre del tesoro (la c) ubicado a
una distancia
140. 2 del dispositivo sonar (la d):
141.
142.
22222
143.
c
2
144.
2 d 2
145.
2
2
146.
22222
147.
148. El punto donde el dispositivo fue colocado se indicar con una d.
149.
150. Los cofres del tesoro no se mueven. Los dispositivos sonar pueden detectar
151. cofres hasta una distancia 9. Si todos los cofres estn fuera del alcance,
152. el punto se indicar con un O.
153.
154. Si un dispositivo es colocado directamente sobre un cofre del tesoro, has
155. descubierto la ubicacin del cofre, y este ser recogido. El dispositivo
156. sonar permanecer all.
157.
158. Cuando recojas un cofre, todos los dispositivos sonar se actualizarn para
159. localizar el prximo cofre hundido ms cercano.
160. Pulsa enter para continuar...''')
161.
input()
162.
print()
print(' S O N A R !')
print()
print('Te gustara ver las instrucciones? (s/no)')
if input().lower().startswith('s'):
mostrarInstrucciones()
208
http://inventwithpython.com/es
El ciclo while de la lnea 171 es el ciclo principal del programa. Diversas variables son
inicializadas en las lineas 173 a 177 y son descriptas en la Tabla 13-1.
Tabla 13-1: Variables utilizadas en el ciclo principal de juego.
Variable
Descripcin
dispositivosSonar La cantidad de dispositios de sonar (y turnos) que el jugador tiene
disponibles.
elTablero
losCofres
movidasPrevias
El ciclo while de la lnea 179 se ejecuta siempre y cuando el jugador posea algn sonar restante.
La lnea 187 imprime un mensaje dicindole al jugador cuantos sonares y cofres restan. Pero hay
un pequeo problema.
209
Si hay dos o ms dispositivos de sonar restantes, deseas imprimir '2 dispositivos sonar'.
Pero si slo posees un dispositvo de sonar, deseas imprimir '1 dispositivo sonar'. Slo
deseas utilizar el plural de "dispositivos" si hay mltiples dispositivos de sonar. Lo mismo ocurre
para '2 cofres de tesoro' y '1 cofre de tesoro'.
Las lneas 183 a 186 poseen el cdigo luego de los dos puntos de las sentencias if y else. Esto
en Python es perfectamente vlido. En vez de poseer un bloque de cdigo luego de la sentencia,
puede utilizar el resto de la misma lnea para lograr un cdigo ms conciso.
Las dos variables llamadas extraSsonar y extraScofre son establecidas a 's' si hay mltiples
dispositivos de sonar o cofres de tesoro. Caso contrario, sern cadenas vacas. Estas variables son
utilizdas en la lnea 187.
La lnea 189 usa asignacin mltiple ya que ingresarMovidaJugador() devuelve una lista de
dos elementos. El primer elemento es asignado a la variable x. El segundo elemento es asignado a
la variable y.
Luego son ingresados al final de la lista movidasPrevias. Esto significa que movidasPrevias es
una lista de coordenadas XY de cada una de las jugadas realizadas por el jugador en el juego.
Esta lista es luego utilizada en el juego en la lnea 198.
Las variables x, y, elTablero, y losCofres son pasadas a la funcin realizarMovida(). Esta
funcin realizar las modificaciones necesarias al tablero para colocar un dispositivo sonar en el
mismo.
Si realizarMovida() devuelve False, entonces hubo un problema con las coordenadas x e y
pasadas. La sentencia continue enviar la ejecucin devuelta al comienzo del ciclo while de la
lnea 179 para pedirle al jugador las coordenadas XY otra vez.
210
http://inventwithpython.com/es
else:
if resultadoMovida == 'Has encontrado uno de los cofres del
# actualizar todos los dispositivos sonar presentes en el
for x, y in movidasPrevias:
realizarMovida(elTablero, losCofres, x, y)
dibujarTablero(elTablero)
print(resultadoMovida)
Si realizarMovida() no retorn False, habr retornado una cadena con los resultados de dicha
movida. Si la cadena es 'Has encontrado uno de los cofres del tesoro!', entonces todos
los dispositivos de sonar deben ser actualizados para detectar el prximo sonar ms cercano en el
tablero. Las coordenadas XY de todos los sonares se encuentran en movidasPrevias. Al iterar
sobre movidasPrevias en la lnea 198, puedes pasarle estas coordenadas XY de nuevo a
realizarMovida() para redibujar los valores en el tablero.
Como el programa no muestra nada aqu, el jugador no sabe que el programa est rehaciendo
todas las movidas previas. Slo se v que el tablero se actualiza a s mismo.
Recuerda que realizarMovida() modifica la lista losCofres pasadas por argumento. Como
losCofres es una lista, cualquier cambio realizado dentro de la funcin persitir luego de que la
funcin retorne. realizarMovida() remueve elementos de losCofres cuando un cofre es
encontrado, por lo que enventualmente (si el jugador contina acertando) todos los cofres de
tesoros sern removidos. Recuerda, por cofre de tesoro nos referimos a las listas de dos
elementos que representan las coordenads XY dentro de la lista losCofres.
Cuando todos los cofres de tesoro hayan sido encontrados en el tablero y removidos de
losCofres, la lista losCofres poseer una longitud de 0. Cuando esto suceda, se muestra una
felicitacin al jugador, y luego se ejecuta una sentencia break para salir del ciclo while. La
ejecucin luego se mover a la lnea 209, la primer lnea a continuacin del bloque while.
211
dispositivosSonar -= 1
La lnea 207 es la ltima lnea del ciclo while que comenz en la lnea 179. Decrementa la
variable dispositivosSonar porque el jugador ha utilizado uno. Si el jugador contina errando
los cofres de tesoro, eventualmente dispositivosSonar se reducir a 0. Luego de esta lnea, la
ejecucin salta atrs hacia la lnea 179 para reevaluar la condicin del ciclo while (que era
dispositivosSonar > 0).
Si dispositivosSonar es 0, entonces la condicin ser False y la ejecucin continuar afuera
del bloque while en la lnea 209. Pero hasta entonces, la condicin continuar siendo True y el
jugador podr continuar realizando intentos.
209.
if dispositivosSonar == 0:
210.
print('Nos hemos quedado sin dispositivos sonar! Ahora tenemos
que dar la vuelta y dirigirnos')
211.
print('de regreso a casa dejando tesoros en el mar! Juego
terminado.')
212.
print('
Los cofres restantes estaban aqu:')
213.
for x, y in losCofres:
214.
print('
%s, %s' % (x, y))
La lnea 209 es la primera lnea fuera del ciclo while. Cuando la ejecucin alcance este punto el
juego ha terminado. Si dispositivosSonar es 0, entonces sabes que el jugador se ha quedado sin
sonares antes de encontrar todos los cofres y ha perdido.
Las lneas 210 a 212 le dirn al jugador que ha perdido. Luego el ciclo for en la lnea 213
recorrer los cofres de tesoro restantes en losCofres y mostrar su posicin al jugador para que
pueda saber donde se encontraban.
La funcin sys.exit()
216.
217.
if not jugarDeNuevo():
sys.exit()
Pierda o gane, jugarDeNuevo() se vuelve a llamar para permitirle al jugador decidir si desea
volver a jugar o no. Si no, jugarDeNuevo() retornar False. El operador not en la lnea 216
cambia esto a True, haciendo la condicin de la sentencia if True, por lo que sys.exit() ser
ejecutado. Esto causar que el programa finalice.
212
http://inventwithpython.com/es
Si el jugador desea volver a jugar, la ejcucin volver al principio del ciclo while en la lnea 171
donde el juego comienza.
Resumen
Recuerdas como nuestro juego Tic Tac numeraba los espacios en el tablero del Tic Tac de 1 a 9
? Este tipo de sistemas de coordenadas puede haber servido para un tablero con menos de diez
espacios. Pero el tablero de sonar posee 900 espacios! El sistema de coordenadas Cartesianas
que hemos aprendido en el ltimo captulo realmente hace estos espacios manejables,
especialmente cuando nuestro juego necesita hallar distancias entre dos puntos del tablero.
Las posiciones en juegos que utilizen sistemas de coordenadas Cartesianas pueden ser
almacenadas en una lista de listas, siendo el primer ndice la coordenada X y el segundo ndice la
coordenada Y. Esto resultar en un acceso a las coordenadas del estilo tablero[x][y].
Estas estructuras de datos (como las utilizadas para el oceano y las posiciones de los tesoros)
hacen posible tener representaciones de conceptos complicados, y el juego se convierte
mayormente en modificar estas estructuras de datos.
En el prximo captulo, representaremos letras como nmeros utilizando su cdigo ASCII. (Este
es el mismo trmino ASCII utilizado previamente en "arte ASCII"). Al representar texto como
nmeros, podemoas realizar operaciones matemticas en ellos que encriptarn o desencriptarn
mensajes secretos.
213
Captulo 14
CIFRADO CSAR
Temas Tratados En Este Captulo:
Criptografa y cifrados
Encriptar y desencriptar
Texto cifrado, texto simple, claves y smbolos
El Cifrado Csar
Valores ordinales ASCII
Las funciones chr() y ord()
El mtodo de cadena isalpha()
Los mtodos de cadena isupper() & islower()
Criptoanlisis
El mtodo de fuerza bruta
El programa de este captulo no es realmente un juego, pero es un programa divertido. Este
programa traduce texto normal a un cdigo secreto. Tambin puede convertir mensajes en el
cdigo secreto a texto normal. Slo alguien que conozca este cdigo secreto podr entender
nuestros mensajes secretos.
Como este programa manipula texto para convertirlo en mensajes secretos, aprenders varios
nuevos mtodos y funciones para manipular cadenas. Tambin aprenders cmo los programas
pueden hacer matemtica con cadenas de texto as como lo hacen con nmeros.
Criptografa
La ciencia de escribir cdigos secretos se llama criptografa. Por miles de aos la criptografa ha
permitido crear mensajes secretos que slo el emisor y el receptor podan entender, incluso en
caso de que alguien capturase al mensajero y leyese el mensaje codificado. Los sistemas secretos
de codificacin se llaman cifrados. El cifrado que usa el programa de este captulo se llama
Cifrado Csar.
En criptografa, llamamos texto plano al mensaje que queremos codificar. El texto plano podra
ser algo como esto:
Hola! Las llaves de la casa te esperan escondidas bajo la maceta.
214
http://inventwithpython.com/es
Pero si conoces el sistema de cifrado usado para encriptar el mensaje, puedes desencriptar el
criptograma y convertirlo en el texto plano. (Desencriptar es lo opuesto a encriptar.)
Muchos cifrados tambin usan claves. Las claves son valores secretos que permiten desencriptar
los criptogramas que fueron encriptados usando un cifrado especfico. Piensa en el cifrado como
si fuera la cerradura de una puerta. Slo puedes abrirla con una llave particular.
Si ests interesado en escribir programas de criptografa, puedes leer mi otro libro, Hacking
Secret Ciphers with Python ("Descifrando Cdigos Secretos con Python"). Es gratis y puedes
descargarlo de http://inventwithpython.com/hacking.
El Cifrado Csar
La clave para el Cifrado Csar ser un nmero entre 1 y 26. A menos que conozcas la clave (es
decir, conozcas el nmero usado para encriptar el mensaje), no podrs desencriptar el cdigo
secreto.
El Cifrado Csar fue uno de los primeros sistemas de cifrado que se inventaron. Con este cifrado,
para encriptar un mensaje se toma cada letra del mismo (en criptografa, estas letras se llaman
smbolos porque pueden ser letras, nmeros o cualquier otro signo) y se la reemplaza con una
letra "desplazada". Si desplazas la letra A un espacio, obtienes la letra B. Si desplazas la A dos
espacios, obtienes la letra C. La Figura 14-1 es una ilustracin de letras desplazadas tres espacios.
215
Para obtener cada letra desplazada, dibuja una fila de casilleros con cada letra del alfabeto. Luego
dibuja una segunda fila de casilleros debajo de ella, pero comienza un cierto nmero (este nmero
es la clave) de casilleros hacia la derecha. Luego de la ltima letra, vuelve a comenzar con la
primera. Aqu hay un ejemplo con las letras desplazadas tres espacios.
La A se convierte en D.
La letra d se convierte en g.
La letra i se convierte en l.
La letra o se convierte en r.
La letra s se convierte en v.
La letra D se convierte en A.
La letra g se convierte en d.
La letra l se convierte en i.
La letra r se convierte en o.
La letra v se convierte en s.
http://inventwithpython.com/es
216
formar un nuevo ordinal (y una nueva letra). ASCII (que se pronuncia "asqui" y corresponde a las
siglas en ingls de Cdigo Estndar Americano para el Intercambio de Informacin) es un cdigo
que relaciona cada caracter con un nmero entre 32 y 126.
Las maysculas de la "A" a la "Z" reciben nmeros ASCII entre 65 y 90. Las minsculas de "a" a
"z" reciben los nmeros ASCII entre 97 y 122. Los caracteres numricos de "0" a "9" reciben
nmeros ASCII de 48 a 57. La Tabla 14-1 muestra todos los caracteres y ordinales ASCII.
Las computadoras modernas usan UTF-8 en lugar de ASCII. Pero UTF-8 es compatible con
ASCII, de modo que los ordinales UTF-8 para los caracteres ASCII son los mismos que los
ordinales ASCII.
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
espacio
!
"
#
$
%
&
'
(
)
*
+
,
.
/
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~
217
chr(65)
ord('A')
chr(65+8)
chr(52)
chr(ord('F'))
ord(chr(68))
En la tercera lnea, chr(65+8) se evala a chr(73). Si miras la tabla ASCII, puedes ver que 73 es
el ordinal para la letra mayscula "I".
En la quinta lnea, chr(ord('F')) se evala a chr(70) que a su vez se evala a 'F'. Las
funciones ord() y chr() son opuestas entre s.
218
http://inventwithpython.com/es
Ingresa tu mensaje:
Ry pvryb fboer ry chregb ren ry pbybe qry gryrivfbe, fvagbavmnaqb ha pnany
zhregb.
Ingresa el nmero de clave (1-26)
13
Tu texto traducido es:
El cielo sobre el puerto era el color del televisor, sintonizando un canal
muerto.
cifrado.py
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
# Cifrado Cesar
TAM_MAX_CLAVE = 26
def obtenerModo():
while True:
print('Deseas encriptar o desencriptar un mensaje?')
modo = input().lower()
if modo in 'encriptar e desencriptar d'.split():
return modo
else:
print('Ingresa "encriptar" o "e" o "desencriptar" o "d"')
def obtenerMensaje():
print('Ingresa tu mensaje:')
return input()
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
def obtenerClave():
clave = 0
while True:
print('Ingresa el nmero de clave (1-%s)' % (TAM_MAX_CLAVE))
clave = int(input())
if (clave >= 1 and clave <= TAM_MAX_CLAVE):
return clave
def obtenerMensajeTraducido(modo, mensaje, clave):
if modo[0] == 'd':
clave= -clave
traduccion = ''
for simbolo in mensaje:
if simbolo.isalpha():
num = ord(simbolo)
num += clave
if simbolo.isupper():
if num > ord('Z'):
num -= 26
elif num < ord('A'):
num += 26
elif simbolo.islower():
if num > ord('z'):
num -= 26
elif num < ord('a'):
num += 26
traduccion += chr(num)
else:
traduccion += simbolo
return traduccion
modo = obtenerModo()
mensaje = obtenerMensaje()
clave = obtenerClave()
print('Tu texto traducido es:')
print(obtenerMensajeTraducido(modo, mensaje, clave))
219
220
http://inventwithpython.com/es
16.
221
return input()
La funcin obtenerClave() permite al jugador escribir la clave que desea usar para encriptar o
desencriptar el mensaje. El bucle while asegura que la funcin se mantenga ciclando hasta que el
usuario ingrese una clave vlida.
Una clave vlida es aquella que est comprendida entre los valores enteros 1 y 26 (recuerda que
TAM_MAX_CLAVE tendr siempre el valor 26 porque es constante). La funcin devuelve entonces
esta clave. La lnea 22 establece la clave como la versin entera de lo que el jugador haya escrito,
de modo que obtenerClave() devuelve un entero.
modo
mensaje
clave
La lnea 27 comprueba si la primera letra en la variable modo es la cadena 'd'. En ese caso, el
programa entra en modo de desencriptacin. La nica diferencia entre los modos de
desencriptacin y encriptacin es que para desencriptar un mensaje se usa la versin negativa de
222
http://inventwithpython.com/es
la clave. Si clave fuera el entero 22, entonces en modo de desencriptacin clave se transforma en
-22. Explicaremos la razn de esto ms adelante.
traduccion
devuelve False porque ni '4' ni '2' son letras. isalpha() slo devuelve True
si la cadena no est vaca y est compuesta nicamente por letras.
El mtodo isalpha() se usa en las siguientes lneas del programa.
31.
32.
33.
34.
El bucle for de la lnea 31 itera sobre cada letra (en criptografa se llaman smbolos) de la cadena
del mensaje. En cada iteracin sobre este bucle, simbolo tendr el valor de una letra en el
mensaje.
La lnea 32 est presente porque slo las letras sern encriptadas o desencriptadas. Los nmeros,
signos de puntuacin y todo lo dems conservar su forma original. La variable num almacenar
223
el valor ordinal entero de la letra en la variable simbolo. La lnea 34 desplaza entonces el valor
de num en el nmero de casilleros correspondiente a la clave.
devuelve True si la cadena sobre la cual es llamado contiene al menos una letra
mayscula y ninguna minscula. islower() devuelve True si la cadena sobre la cual es llamado
contiene al menos una letra minscula y ninguna mayscula. De otro modo estos mtodos
devuelven False.
Prueba ingresar lo siguiente en la consola interactiva:
>>> 'HOLA'.isupper()
True
>>> 'hola'.isupper()
False
>>> 'hola'.islower()
True
>>> 'Hola'.islower()
False
>>> 'CUIDADO DETRAS DE TI!'.isupper()
True
>>> '42'.isupper()
False
>>> '42'.islower()
False
>>> ''.isupper()
False
>>> ''.islower()
False
if simbolo.isupper():
if num > ord('Z'):
num -= 26
elif num < ord('A'):
num += 26
La lnea 36 comprueba si el smbolo es una letra mayscula. Si lo es, hay dos casos especiales a
tener en cuenta. Qu ocurrira si el smbolo fuese 'Z' y la clave 4? En este caso, el valor de num
224
http://inventwithpython.com/es
aqu sera el caracter '^' (El ordinal de '^' es 94). Pero ^ no es ninguna letra. Y nosotros
queremos que el criptograma "reinicie la vuelta" por el principio del alfabeto.
Comprobamos si num tiene un valor mayor que el valor ordinal de Z. Si es as, restamos 26 a
num (porque hay 26 letras en total). Luego de hacer esto, el valor de num es 68. 68 es el valor
ordinal correcto ya que corresponde a 'D'.
41.
42.
43.
44.
45.
elif simbolo.islower():
if num > ord('z'):
num -= 26
elif num < ord('a'):
num += 26
Si el smbolo es una letra minscula, el programa ejecuta un cdigo que es similar a las lneas 36
a 40. la nica diferencia es que utiliza ord('z') y ord('a') en lugar de ord('Z') y ord ('A').
En modo desencriptacin, la clave es negativa. El caso especial sera si num -= 26 es menor que
el valor ASCII de a. En ese caso, sumamos 26 a num para que reinicie la vuelta por el final
del alfabeto.
47.
48.
49.
traduccion += chr(num)
else:
traduccion += simbolo
return traduccion
modo = obtenerModo()
mensaje = obtenerMensaje()
clave = obtenerClave()
print('Tu texto traducido es:')
print(obtenerMensajeTraducido(modo, mensaje, clave))
225
El comienzo del programa llama a cada una de las tres funciones definidas anteriormente para
obtener el modo, el mensaje y la clave del usuario. Estos tres valores son pasados a
obtenerMensajeTraducido(), cuyo valor de retorno (la cadena traducida) es mostrada en
pantalla al usuario.
Fuerza Bruta
Eso es todo con respecto al Cifrado Csar. Sin embargo, a pesar de que este cifrado puede
engaar a gente que no entiende criptografa, no ser suficiente para alguien que sepa de
criptoanlisis. As como criptografa es la ciencia de crear cdigos, criptoanlisis es la ciencia de
descifrarlos.
Deseas encriptar o desencriptar un mensaje?
encriptar
Ingresa tu mensaje:
La duda puede no ser agradable, pero la certeza es absurda.
Ingresa el nmero de clave (1-26)
8
Tu texto traducido es:
Ti lcli xcmlm vw amz iozilijtm, xmzw ti kmzbmhi ma ijaczli.
Fuerza bruta es la tcnica de probar cada todas las claves posibles hasta encontrar la correcta.
Como hay slo 26 claves posibles, sera fcil para un criptoanalista escribir un programa que
desencriptara con todas las claves posibles. Luego podra fijarse cul de las claves resulta en un
mensaje en Espaol. Agreguemos un modo de fuerza bruta a nuestro programa.
226
http://inventwithpython.com/es
10.
return modo[0]
11.
else:
12.
print('Ingresa "encriptar" o "e" o "desencriptar" o "d" o
"bruta" o "b".')
Este cdigo permitir al usuario elegir "fuerza bruta" como un modo. Modifica y agrega los
siguientes cambios a la parte principal del programa:
52. modo = getMode()
53. mensaje = obtenerMensaje()
54. if modo[0] != 'b':
55.
clave = obtenerClave()
56.
57. print('Tu texto traducido es:')
58. if modo[0] != 'b':
59.
print(obtenerMensajeTraducido(modo, mensaje, clave))
60. else:
61.
for clave in range(1, TAM_MAX_CLAVE + 1):
62.
print(clave, obtenerMensajeTraducido('desencriptar', mensaje,
clave))
Estos cambios piden una clave al usuario si no se encuentra en el modo de "fuerza bruta". Se
efecta entonces la llamada original a obtenerMensajeTraducido() y se muestra la cadena
traducida.
Sin embargo, si el usuario est en el modo de "fuerza bruta" entonces
obtenerMensajeTraducido() se ejecuta en un bucle que recorre todos los valores entre 1 y
TAM_MAX_CLAVE (que es 26). Recuerda que la funcin range() devuelve una lista de enteros hasta
el segundo parmetro pero sin incluirlo, por lo que agregamos + 1 a la expresin. Este programa
imprimir en la pantalla cada posible traduccin del mensaje (incluyendo el nmero de clave
usado para la traduccin). Aqu hay una prueba de ejecucin del programa modificado:
Deseas encriptar, desencriptar o descifrar por fuerza bruta un mensaje?
bruta
Ingresa tu mensaje:
Ti lcli xcmlm vw amz iozilijtm, xmzw ti kmzbmhi ma ijaczli.
Tu texto traducido es:
1 Sh kbkh wblkl uv zly hnyhkhisl, wlyv sh jlyalgh lz hizbykh.
2 Rg jajg vakjk tu ykx gmxgjghrk, vkxu rg ikxzkfg ky ghyaxjg.
3 Qf izif uzjij st xjw flwfifgqj, ujwt qf hjwyjef jx fgxzwif.
4 Pe hyhe tyihi rs wiv ekvehefpi, tivs pe givxide iw efwyvhe.
5 Od gxgd sxhgh qr vhu djudgdeoh, shur od fhuwhcd hv devxugd.
6 Nc fwfc rwgfg pq ugt citcfcdng, rgtq nc egtvgbc gu cduwtfc.
7 Mb eveb qvfef op tfs bhsbebcmf, qfsp mb dfsufab ft bctvseb.
227
Luego de examinar cada columna, puedes ver que el 8vo mensaje no es basura, sino texto en
espaol. El criptoanalista puede deducir que la clave original de este mensaje encriptado debe
haber sido 8. Este mtodo de fuerza bruta habra sido difcil de emplear en los tiempos del Csar
y del imperio romano, pero hoy en da tenemos computadoras que pueden examinar millones de
claves rpidamente.
Resumen
Las computadoras son muy efectivas para hacer operaciones matemticas. Cuando creamos un
sistema para traducir fragmentos de informacin a nmeros (as como hacemos con texto y
ordinales o con informacin espacial y sistemas de coordenadas), los programas de computadora
pueden procesar estos nmeros en forma rpida y eficiente.
Pero aunque nuestro programa de cifrado Csar puede encriptar mensajes y mantenerlos secretos
para gente que slo tiene a disposicin papel y lpiz, no conseguir ocultarlos a gente que sepa
cmo hacer que una computadora procese informacin por ellos. (Nuestro modo de fuerza bruta
lo comprueba.)
Una parte fundamental del proceso de escribir un programa es entender cmo representar la
informacin que queremos manipular utilizando valores que Python puede comprender.
El prximo captulo presentar Reversi (tambin conocido como Othello). La IA que maneja este
juego es mucho ms avanzada que la IA que diseamos para el Ta Te Ti en el captulo 9. De
hecho, la IA es tan buena que... te vencer en casi todas las partidas!
228
http://inventwithpython.com/es
Captulo 15
REVERSI
Topics Covered In This Chapter:
La Funcin bool()
Cmo Jugar a Reversi
En este captulo, crearemos un juego llamado Reversi (tambin llamado Othello). Reversi es un
juego de mesa que se juega sobre una grilla, de modo que tendremos que usar un sistema de
coordenadas Cartesiano con coordenadas XY. Es un juego para dos jugadores. En nuestra versin
del juego la computadora tendr una IA ms avanzada que la que hemos creado para el Ta Te Ti.
De hecho, esta IA es tan buena que probablemente te gane todas las partidas que juegues. (Al
menos yo pierdo cada vez que juego contra ella!)
Reversi tiene un tablero de 8 x 8 y baldosas que son negras de un lado y blancas del otro (nuestro
juego las reemplazar por O's y X's). El tablero inicial se ve como la Figura 15-1. El jugador
negro y el jugador blanco toman turnos para colocar una nueva baldosa de su color. Cualquier
baldosa del oponente que se encuentre entre la nueva baldosa y las otras baldosas de ese color es
convertida. El objetivo del juego es tener tantas baldosas de tu color como sea posible. Por
ejemplo, la Figura 15-2 es como se vera si el jugador blanco colocara una nueva baldosa blanca
en el espacio 5, 6.
Captulo 15 Reversi
229
Las baldosas en todas las direcciones son convertidas en tanto se encuentren entre la nueva
baldosa del jugador y sus baldosas existentes. En la Figura 15-5, el jugador blanco coloca una
baldosa en 3, 6 y convierte baldosas en ambas direcciones (indicadas por las lneas). El resultado
se muestra en la Figura 15-6.
Las baldosas en todas las direcciones son convertidas en tanto se encuentren entre la nueva
baldosa del jugador y sus baldosas existentes. En la Figura 15-5, el jugador blanco coloca una
baldosa en 3, 6 y convierte baldosas en ambas direcciones (indicadas por las lneas). El resultado
se muestra en la Figura 15-6.
230
http://inventwithpython.com/es
Cada jugador puede rpidamente convertir muchas baldosas en el tablero en uno o dos
movimientos. Los jugadores deben hacer siempre jugadas que capturen al menos una baldosa. El
juego termina cuando ningn jugador puede seguir moviendo, o el tablero est completamente
lleno. Gana el jugador con ms baldosas de su color.
La IA que crearemos para este juego simplemente intentar jugar en las esquinas. Si no es posible
jugar en una esquina, la computadora seleccionar la jugada que convierta ms baldosas.
Captulo 15 Reversi
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
Tienes 2 puntos. La computadora tiene 2 puntos.
Ingresa tu jugada, salir para terminar el juego, o pistas para
activar/desactivar las pistas.
53
1
2
3
4
5
6
7
8
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
3 |
|
|
|
| X |
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
4 |
|
|
| X | X |
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
5 |
|
|
| O | X |
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
Tienes 4 puntos. La computadora tiene 1 puntos.
Presiona enter para ver la jugada de la computadora.
...omitido por brevedad...
231
232
http://inventwithpython.com/es
1
2
3
4
5
6
7
8
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
1 | O | O | O | O | O | O | O | O |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
2 | O | O | O | O | O | O | O | O |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
3 | O | O | O | O | O | O | O | O |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
4 | O | O | X | O | O | O | O | O |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
5 | O | O | O | X | O | X | O | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
6 | O | X | O | X | X | O | O |
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
7 | O | X | X | O | O | O | O | O |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
8 | O | X | X | O |
|
| X |
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
Tienes 12 puntos. La computadora tiene 48
Ingresa tu jugada, salir para terminar el
activar/desactivar las pistas.
86
X ha obtenido 15 puntos. O ha obtenido 46
Has perdido. La computadora te ha vencido
Quieres jugar de nuevo? (s o no)
no
puntos.
juego, o pistas para
puntos.
por 31 puntos.
Como puedes ver, la IA hizo un buen trabajo y me venci 46 a 15. Para ayudar al jugador,
programaremos el juego de modo que ofrezca pistas. Si el jugador escribe 'pistas' como su
Captulo 15 Reversi
jugada, se activarn o desactivarn las pistas. Cuando el modo pistas est activado, se vern
marcas '.' en el tablero para todas las jugadas posibles, como se muestra a continuacin:
1
2
3
4
5
6
7
8
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
|
|
|
| . |
| . |
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
|
|
|
| O | O | O |
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
|
|
| . | O | O | X |
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
|
|
| . | O | O | O | X |
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
|
|
|
| . |
| . |
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
233
234
http://inventwithpython.com/es
Como hemos hecho con los otros programas, primero crearemos varias funciones para llevar a
cabo tareas relacionadas con Reversi que sern llamadas por la seccin principal.
Aproximadamente las primeras 250 lneas de cdigo son para estas funciones auxiliares, y las
ltimas 50 lneas de cdigo implementan el juego Reversi en s.
Si obtienes errores luego de escribir este cdigo, compara el cdigo que has escrito con el cdigo
del libro usando la herramienta diff online en http://invpy.com/es/diff/reversi.
reversi.py
1. # Reversi
2.
3. import random
4. import sys
5.
6. def dibujarTablero(tablero):
7.
# Esta funcion dibuja el tablero recibido. Devuelve None.
8.
LNEAH = ' +---+---+---+---+---+---+---+---+'
9.
LNEAV = ' |
|
|
|
|
|
|
|
|'
10.
11.
print('
1
2
3
4
5
6
7
8')
12.
print(LNEAH)
13.
for y in range(8):
14.
print(LNEAV)
15.
print(y+1, end=' ')
16.
for x in range(8):
17.
print('| %s' % (reiniciarTablero[x][y]), end=' ')
18.
print('|')
19.
print(LNEAV)
20.
print(LNEAH)
21.
22.
23. def reiniciarTablero(tablero):
24.
# Deja en blanco el tablero recibido como argumento, excepto la
posicin inicial.
25.
for x in range(8):
26.
for y in range(8):
27.
tablero[x][y] = ' '
28.
29.
# Piezas iniciales:
30.
tablero[3][3] = 'X'
31.
tablero[3][4] = 'O'
32.
tablero[4][3] = 'O'
33.
tablero[4][4] = 'X'
34.
35.
36. def obtenerNuevoTablero():
Captulo 15 Reversi
235
37.
# Crea un tablero nuevo, vaco.
38.
tablero= []
39.
for i in range(8):
40.
tablero.append([' '] * 8)
41.
42.
return tablero
43.
44.
45. def esJugadaVlida(tablero, baldosa, comienzox, comienzoy):
46.
# Devuelve False si la jugada del jugador en comienzox, comienzoy es
invalida
47.
# Si es una jugada vlida, devuelve una lista de espacios que pasaran
a ser del jugador si moviera aqu.
48.
if tablero[comienzox][comienzoy] != ' ' or not
estEnTablero(comienzox, comienzoy):
49.
return False
50.
51.
tablero[comienzox][comienzoy] = baldosa # coloca temporariamente la
baldosa sobre el tablero.
52.
53.
if baldosa == 'X':
54.
otraBaldosa = 'O'
55.
else:
56.
otraBaldosa = 'X'
57.
58.
baldosasAConvertir = []
59.
for direccinx, direcciny in [[0, 1], [1, 1], [1, 0], [1, -1], [0, 1], [-1, -1], [-1, 0], [-1, 1]]:
60.
x, y = comienzox, comienzoy
61.
x += direccinx # primer paso en la direccin
62.
y += direcciny # primer paso en la direccin
63.
if estEnTablero(x, y) and tablero[x][y] == otraBaldosa:
64.
# Hay una pieza perteneciente al otro jugador al lado de
nustra pieza
65.
x += direccinx
66.
y += direcciny
67.
if not estEnTablero(x, y):
68.
continue
69.
while tablero[x][y] == otraBaldosa:
70.
x += direccinx
71.
y += direcciny
72.
if not estEnTablero(x, y): # sale del bucle while y
continua en el bucle for.
73.
break
74.
if not estEnTablero(x, y):
75.
continue
76.
if tablero[x][y] == baldosa:
236
http://inventwithpython.com/es
77.
# Hay fichas a convertir. Caminar en direccin opuesta
hasta llegar al casillero original, registrando todas las posiciones en el
camino.
78.
while True:
79.
x -= direccinx
80.
y -= direcciny
81.
if x == direccinxand y == direcciny:
82.
break
83.
baldosasAConvertir.append([x, y])
84.
85.
tablero[comienzox][comienzoy] = ' ' # restablecer el espacio vaco
86.
if len(baldosasAConvertir) == 0: # Si no se convirti ninguna baldosa,
la jugada no es vlida.
87.
return False
88.
return baldosasAConvertir
89.
90.
91. def estEnTablero(x, y):
92.
# Devuelve True si las coordenadas se encuentran dentro del tablero
93.
return x >= 0 and x <= 7 and y >= 0 and y <= 7
94.
95.
96. def obtenerTableroConJugadasVlidas(tablero, baldosa):
97.
# Devuelve un nuevo tablero, marcando con "." las jugadas vlidas que
el jugador puede realizar.
98.
rplicaTablero = obtenerCopiaTablero(tablero)
99.
100.
for x, y in obtenerJugadasVlidas(rplicaTablero, baldosa):
101.
rplicaTablero[x][y] = '.'
102.
return rplicaTablero
103.
104.
105. def obtenerJugadasVlidas(tablero, baldosa):
106.
# Devuelve una lista de listas [x,y] de jugadas vlidas para el
jugador en el tablero dado.
107.
jugadasVlidas = []
108.
109.
for x in range(8):
110.
for y in range(8):
111.
if esJugadaVlida(tablero, baldosa, x, y) != False:
112.
jugadasVlidas.append([x, y])
113.
return jugadasVlidas
114.
115.
116. def obtenerPuntajeTablero(tablero):
117.
# Determina el puntaje contando las piezas. Devuelve un diccionario
con claves 'X' y 'O'.
Captulo 15 Reversi
237
118.
puntajex = 0
119.
puntajeo = 0
120.
for x in range(8):
121.
for y in range(8):
122.
if tablero[x][y] == 'X':
123.
puntajex += 1
124.
if tablero[x][y] == 'O':
125.
puntajeo += 1
126.
return {'X':puntajex, 'O':puntajeo}
127.
128.
129. def ingresarBaldosaJugador():
130.
# Permite al jugador elegir que baldosa desea ser.
131.
# Devuelve una lista con la baldosa del jugador como primer elemento y
el de la computadora como segundo.
132.
baldosa = ''
133.
while not (baldosa == 'X' or baldosa == 'O'):
134.
print('Deseas ser X O?')
135.
baldosa = input().upper()
136.
137.
# El primer elemento en la lista es la baldosa del juegador, el
segundo es la de la computadora.
138.
if baldosa == 'X':
139.
return ['X', 'O']
140.
else:
141.
return ['O', 'X']
142.
143.
144. def quinComienza():
145.
# Elije al azar qu jugador comienza.
146.
if random.randint(0, 1) == 0:
147.
return 'La computadora'
148.
else:
149.
return 'El jugador'
150.
151.
152. def jugarDeNuevo():
153.
# Esta funcin devuelve True si el jugador quiere jugar de nuevo, de
lo contrario devuelve False.
154.
print('Quieres jugar de nuevo? (s o no)')
155.
return input().lower().startswith('s')
156.
157.
158. def hacerJugada(tablero, baldosa, comienzox, comienzoy):
159.
# Coloca la baldosa sobre el tablero en comienzox, comienzoy, y
convierte cualquier baldosa del oponente.
160.
# Devuelve False si la jugada es invlida, True si es vlida.
238
http://inventwithpython.com/es
161.
baldosasAConvertir = esJugadaVlida(tablero, baldosa, comienzox,
comienzoy)
162.
163.
if baldosasAConvertir == False:
164.
return False
165.
166.
tablero[comienzox][comienzoy] = baldosa
167.
for x, y in baldosasAConvertir:
168.
tablero[x][y] = baldosa
169.
return True
170.
171.
172. def obtenerCopiaTablero(tablero):
173.
# Duplica la lista del tablero y devuelve el duplicado.
174.
rplicaTablero = obtenerNuevoTablero()
175.
176.
for x in range(8):
177.
for y in range(8):
178.
rplicaTablero[x][y] = tablero[x][y]
179.
180.
return rplicaTablero
181.
182.
183. def esEsquina(x, y):
184.
# Devuelve True si la posicion es una de las esquinas.
185.
return (x == 0 and y == 0) or (x == 7 and y == 0) or (x == 0 and y ==
7) or (x == 7 and y == 7)
186.
187.
188. def obtenerJugadaJugador(tablero, baldosaJugador):
189.
# Permite al jugador tipear su jugada.
190.
# Devuelve la jugada como [x, y] (o devuelve las cadenas 'pistas' o
'salir')
191.
CIFRAS1A8 = '1 2 3 4 5 6 7 8'.split()
192.
while True:
193.
print('Ingresa tu jugada, salir para terminar el juego, o pistas
para activar/desactivar las pistas.')
194.
jugada = input().lower()
195.
if jugada == 'salir':
196.
return 'salir'
197.
if jugada == 'pistas':
198.
return 'pistas'
199.
200.
if len(jugada) == 2 and jugada[0] in CIFRAS1A8 and jugada[1] in
CIFRAS1A8:
201.
x = int(jugada[0]) - 1
202.
y = int(jugada[1]) - 1
Captulo 15 Reversi
239
203.
if esJugadaVlida(tablero, baldosaJugador, x, y) == False:
204.
continue
205.
else:
206.
break
207.
else:
208.
print('Esta no es una jugada vlida. Ingresa la coordenada x
(1-8), luego la coordenada y (1-8).')
209.
print('Por ejemplo, 81 corresponde a la esquina superior
derecha.')
210.
211.
return [x, y]
212.
213.
214. def obtenerJugadaComputadora(tablero, baldosaComputadora):
215.
# Dado un tablero y la baldosa de la computadora, determinar dnde
216.
# jugar y devolver esa jugada como una lista [x, y].
217.
jugadasPosibles = obtenerJugadasVlidas(tablero, baldosaComputadora)
218.
219.
# ordena al azar el orden de las jugadas posibles
220.
random.shuffle(jugadasPosibles)
221.
222.
# siempre jugar en una esquina si est disponible.
223.
for x, y in jugadasPosibles:
224.
if esEsquina(x, y):
225.
return [x, y]
226.
227.
# Recorrer la lista de jugadas posibles y recordar la que da el mejor
puntaje
228.
mejorPuntaje = -1
229.
for x, y in jugadasPosibles:
230.
rplicaTablero = obtenerCopiaTablero(tablero)
231.
hacerJugada(rplicaTablero, baldosaComputadora, x, y)
232.
puntaje =
obtenerPuntajeTablero(rplicaTablero)[baldosaComputadora]
233.
if puntaje > mejorPuntaje:
234.
mejorJugada = [x, y]
235.
mejorPuntaje = puntaje
236.
return mejorJugada
237.
238.
239. def mostrarPuntajes(baldosaJugador, baldosaComputadora):
240.
# Imprime el puntaje actual.
241.
puntajes = obtenerPuntajeTablero(tableroPrincipal)
242.
print('Tienes %s puntos. La computadora tiene %s puntos.' %
(puntajes[baldosaJugador], puntajes[baldosaComputadora]))
243.
244.
240
http://inventwithpython.com/es
245.
246. print('Bienvenido a Reversi!')
247.
248. while True:
249.
# Reiniciar el tablero y la partida.
250.
tableroPrincipal = obtenerNuevoTablero()
251.
reiniciarTablero(tableroPrincipal)
252.
baldosaJugador, baldosaComputadora = ingresarBaldosaJugador()
253.
mostrarPistas = False
254.
turno = quinComienza()
255.
print(turno + ' comenzar.')
256.
257.
while True:
258.
if turno == 'El jugador':
259.
# Turno del jugador
260.
if mostrarPistas:
261.
tableroConJugadasVlidas =
obtenerTableroConJugadasVlidas(tableroPrincipal, baldosaJugador)
262.
dibujarTablero(tableroConJugadasVlidas)
263.
else:
264.
dibujarTablero(tableroPrincipal)
265.
mostrarPuntajes(baldosaJugador, baldosaComputadora)
266.
jugada = obtenerJugadaJugador(tableroPrincipal,
baldosaJugador)
267.
if jugada == 'salir':
268.
print('Gracias por jugar!')
269.
sys.exit() # terminar el programa
270.
elif jugada == 'pistas':
271.
mostrarPistas = not mostrarPistas
272.
continue
273.
else:
274.
hacerJugada(tableroPrincipal, baldosaJugador, jugada[0],
jugada[1])
275.
276.
if obtenerJugadasVlidas(tableroPrincipal, baldosaComputadora)
== []:
277.
break
278.
else:
279.
turn = 'La computadora'
280.
281.
else:
282.
# Turno de la computadora
283.
dibujarTablero(tableroPrincipal)
284.
mostrarPuntajes(baldosaJugador, baldosaComputadora)
285.
input('Presiona enter para ver la jugada de la computadora.')
286.
x, y = obtenerJugadaComputadora(tableroPrincipal,
baldosaComputadora)
Captulo 15 Reversi
241
287.
hacerJugada(tableroPrincipal, baldosaComputadora, x, y)
288.
289.
if obtenerJugadasVlidas(tableroPrincipal, baldosaJugador) ==
[]:
290.
break
291.
else:
292.
turno = 'El jugador'
293.
294.
# Mostrar el puntaje final.
295.
dibujarTablero(tableroPrincipal)
296.
puntajes = obtenerPuntajeTablero(tableroPrincipal)
297.
print('X ha obtenido %s puntos. O ha obtenido %s puntos.' %
(puntajes['X'], puntajes['O']))
298.
if puntajes[baldosaJugador] > puntajes[baldosaComputadora]:
299.
print('Has vencido a la computadora por %s puntos!
Felicitaciones!' % (puntajes[baldosaJugador] - puntajes[baldosaComputadora]))
300.
elif puntajes[baldosaJugador] < puntajes[baldosaComputadora]:
301.
print('Has perdido. La computadora te ha vencido por %s puntos.' %
(puntajes[baldosaComputadora] - puntajes[baldosaJugador]))
302.
else:
303.
print('Ha sido un empate!')
304.
305.
if not jugarDeNuevo():
306.
break
242
http://inventwithpython.com/es
1. # Reversi
2. import random
3. import sys
La lnea 2 importa el mdulo random para usar sus funciones randint() y choice(). La lnea 3
importa el mdulo sys para usar su funcin exit().
Ya que la lnea horizontal se imprime una y otra vez, la lnea 8 la almacena en una variable
constante llamada LNEAH. Esto nos ahorrar el trabajo de tener que escribir esta cadena cada vez.
Tambin hay lneas por encima y por debajo del centro de cada casillero que son simplemente
repeticiones del caracter '|' (llamado "pleca") con 3 espacios entre cada uno. Este conjunto se
almacena en una constante llamada LNEAV.
La lnea 11 contiene a la primera ejecucin de la funcin print(), e imprime las etiquetas para el
eje X a lo largo del borde superior del tablero. La lnea 12 imprime la lnea horizontal superior
del tablero.
13.
14.
15.
16.
17.
18.
for y in range(8):
print(LNEAV)
print(y+1, end=' ')
for x in range(8):
print('| %s' % (reiniciarTablero[x][y]), end=' ')
print('|')
Captulo 15 Reversi
19.
20.
243
print(LNEAV)
print(LNEAH)
El bucle for se ejecutar ocho veces, una por cada fila. La lnea 15 imprime la etiqueta del eje Y
sobre el lado izquierdo del tablero, y tiene como argumento la palabra clave end=' ' para
terminar la lnea con un espacio simple en lugar de saltar a la siguiente lnea. Esto es para que
otro bucle (que tambin se ejecuta ocho veces, una por cada espacio) imprima cada espacio (junto
con el caracter 'X', 'O' o ' ' dependiendo de lo que est almacenado en tablero[x][y].)
La llamada a la funcin print() dentro del bucle interno tambin tiene como argumento la
palabra clave end=' ' al final, con lo cual se imprime un espacio en lugar de una nueva lnea.
Esto produce una nica lnea con el aspecto '| X | X | X | X | X | X | X | X ' (si cada
uno de los valores de tablero[x][y] fuera 'X').
Luego de que el bucle interno ha concluido, la llamada a la funcin print() en la lnea 18
imprime el ltimo caracter '|' junto con una nueva lnea.
El cdigo comprendido por el bucle completo entre las lneas 14 y 20 imprime una fila completa
del tablero en este formato:
|
|
|
|
|
|
|
|
|
| X | X | X | X | X | X | X | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
Cuando el bucle for de la lnea 13 imprime la fila ocho veces, se forma el tablero completo (por
supuesto, algunos de los espacios en el tablero tendrn caracteres 'O' o ' ' en lugar de 'X'):
|
|
|
|
|
|
|
|
|
| X | X | X | X | X | X | X | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
| X | X | X | X | X | X | X | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
| X | X | X | X | X | X | X | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
| X | X | X | X | X | X | X | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
244
http://inventwithpython.com/es
| X | X | X | X | X | X | X | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
| X | X | X | X | X | X | X | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
| X | X | X | X | X | X | X | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
|
|
|
|
|
|
|
|
|
| X | X | X | X | X | X | X | X |
|
|
|
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+
reiniciarTablero(tablero):
# Deja en blanco el tablero recibido como argumento, excepto la
inicial.
for x in range(8):
for y in range(8):
tablero[x][y] = ' '
Las lneas 25 y 26 contienen bucles anidados para asignar espacios simples a cada elemento de la
estructura de datos tablero. Esto crea un tablero vaco de Reversi. La funcin
reiniciarTablero() es parte de la rutina de comenzar un nuevo juego.
# Piezas iniciales:
tablero[3][3] = 'X'
tablero[3][4] = 'O'
tablero[4][3] = 'O'
tablero[4][4] = 'X'
Al comienzo del juego, cada jugador tiene dos baldosas ya colocadas en el centro. Las lneas 30 a
33 colocan esas baldosas en el tablero vaco.
La funcin reiniciarTablero() no necesita devolver la variable tablero, porque tablero es una
referencia a una lista. Los cambios realizados en el entorno local de la funcin modificar la lista
original que fue pasada como argumento. (Explicado en la seccin Referencias del Captulo 10.)
Captulo 15 Reversi
245
Dada una estructura de datos tablero, la baldosa del jugador y las coordenadas XY de la jugada
del jugador, esJugadaVlida() devuelve True si las reglas de Reversi permiten una jugada en
esas coordenadas y False en caso contrario.
246
http://inventwithpython.com/es
La lnea 48 comprueba si las coordenadas XY estn fuera del tablero, o si el espacio no est
vaco. estEnTablero() es una funcin definida ms adelante en el programa que se asegura de
que el valor de ambas coordenadas X e Y est comprendido entre 0 y 7.
El prximo paso es colocar temporariamente la baldosa del jugador sobre el tablero. Esta baldosa
ser removida (volviendo a asignar ' ' al espacio del tablero antes de volver de la funcin).
La baldosa del jugador (tanto el jugador humano como la computadora) se guarda en la variable
baldosa, pero esta funcin necesitar saber la baldosa del otro jugador. Si la baldosa del jugador
es 'X' entonces obviamente la baldosa del otro jugador ser 'O' y viceversa.
Finalmente, si la coordenada XY ingresada corresponde a una jugada vlida, la funcin
esJugadaVlida() devuelve una lista de todas las baldosas del oponente que sern invertidas
con esta jugada.
59.
for direccinx, direcciny in [[0, 1], [1, 1], [1, 0], [1, -1], [0, 1], [-1, -1], [-1, 0], [-1, 1]]:
El bucle for itera a travs de una lista de listas que representan direcciones en las que puedes
moverte en el tablero de juego. El tablero es un sistema de coordenadas cartesianas con
direcciones X e Y. Hay ocho direcciones en las que puedes moverte: arriba, abajo, izquierda,
derecha y las cuatro diagonales. Cada una de las ocho listas de dos elementos en la lista de la
lnea 59 se usa para moverse en una de estas direcciones. El programa se mueve en una direccin
sumando el primer valor en la lista de dos elementos a la coordenada X, y el segundo valor a la
coordenada Y.
Como la coordenada X aumenta a medida que te desplazas hacia la derecha, puedes "moverte" a
la derecha sumando 1 a la coordenada X. Entonces, la lista [1, 0] suma 1 a la coordenada X y 0
a la coordenada Y, resultando en un "movimiento" hacia la derecha. Moverse a la izquierda es lo
opuesto: en este caso restas 1 (es decir, sumas -1) a la coordenada X.
Pero para moverte en diagonal necesitars sumar o restar a ambas coordenadas. Por ejemplo,
sumar 1 a la coordenada X para moverte hacia la derecha y a su vez sumar -1 a la coordenada Y
para moverte hacia arriba resulta en un movimiento en diagonal hacia arriba y a la derecha.
Captulo 15 Reversi
247
Figura 15-7: Cada lista de dos elementos representa una de las ocho direcciones.
59.
for direccinx, direcciny in [[0, 1], [1, 1], [1, 0], [1, -1], [0, 1], [-1, -1], [-1, 0], [-1, 1]]:
60.
x, y = comienzox, comienzoy
61.
x += direccinx # primer paso en la direccin
62.
y += direcciny # primer paso en la direccin
Recuerda que para que esta movida sea vlida, el primer paso en esta direccin debe 1) pertenecer
al tablero y 2) estar ocupado por una baldosa del otro jugador. De otro modo no hay ninguna
baldosa del oponente para convertir, y una jugada vlida debe convertir al menos una baldosa. Si
no se cumplen estas dos condiciones, la condicin de la lnea 63 no es True y la ejecucin vuelve
a la sentencia for correspondiente a la prxima direccin.
Pero si el primer espacio contiene una baldosa del otro jugador, entonces el programa debe seguir
comprobando en esta direccin hasta alcanzar una de las baldosas propias del jugador. Sin
embargo, si contina hasta ms all del tablero, la lnea 68 hace que el programa vuelva al
comienzo del bucle for y contine con la siguiente direccin.
248
http://inventwithpython.com/es
69.
while tablero[x][y] == otraBaldosa:
70.
x += direccinx
71.
y += direcciny
72.
if not estEnTablero(x, y): # sale del bucle while y
continua en el bucle for.
73.
break
74.
if not estEnTablero(x, y):
75.
continue
El bucle while de la lnea 69 deja de iterar cuando el cdigo ha llegado al final de la secuencia de
baldosas otraBaldosa. La lnea 76 comprueba si este espacio en el tablero contiene una de
nuestras baldosas. Si es as, entonces la jugada pasada originalmente a esJugadaVlida() es
vlida.
La lnea 78 itera moviendo x e y en reversa hacia la posicin original comienzox, comienzoy
restando pasos del movimiento anterior. Cada espacio es aadido al final de la lista
baldosasAConvertir.
85.
Captulo 15 Reversi
249
86.
if len(baldosasAConvertir) == 0: # Si no se convirti ninguna baldosa,
la jugada no es vlida.
87.
return False
88.
return baldosasAConvertir
El bucle for que comienza en la lnea 59 repite todo esto en cada una de las ocho direcciones.
Luego de que el bucle concluye, la lista baldosasAConvertir contendr las coordenadas XY de
todas las baldosas del oponente que seran convertidas si el jugador colocara una baldosa en
comienzox, comienzoy. Recuerda que la funcin esJugadaVlida() slo comprueba si la jugada
original era vlida. Esta funcin no altera permanentemente la estructura del tablero de juego.
Si ninguna de las ocho direcciones ha convertido al menos una de las baldosas del oponente,
entonces baldosasAConvertir contendr una lista vaca. Esto quiere decir que esta jugada no es
vlida, por lo que esJugadaVlida() debe devolver False.
En caso contrario, esJugadaVlida() devuelve baldosasAConvertir.
250
http://inventwithpython.com/es
obtenerTableroConJugadasVlidas()
La funcin obtenerJugadasVlidas() devuelve una lista de listas de dos elementos. Estas listas
contienen las coordenadas XY de todas las jugadas vlidas para el jugador correspondiente al
parmetro baldosa y el estado del juego correspondiente al parmetro tablero.
Esta funcin usa bucles anidados (en las lneas 109 y 110) para comprobar cada par de
coordenadas XY (las 64 combinaciones posibles) llamando a esJugadaVlida() en ese casillero
y comprobando si devuelve False o una lista de casilleros (en cuyo caso es una jugada vlida).
Cada coordenada XY vlida es aadida a la lista jugadasVlidas.
La Funcin bool()
La funcin bool() es similar a las funciones int() y str(). Devuelve la forma Booleana del
valor que recibe.
La mayora de los tipos de datos tiene un valor que se considera False para ese tipo de datos.
Todos los otros valores se consideran True. Por ejemplo, el entero 0, el nmero de punto flotante
0.0, la cadena vaca, la lista vaca y el diccionario vaco se consideran False cuando se usan
como condicin para una sentencia if o una sentencia de bucle. Todos los otros valores son True.
Prueba ingresar lo siguiente en la consola interactiva:
>>> bool(0)
False
Captulo 15 Reversi
251
>>> bool(0.0)
False
>>> bool('')
False
>>> bool([])
False
>>> bool({})
False
>>> bool(1)
True
>>> bool('Hola')
True
>>> bool([1, 2, 3, 4, 5])
True
>>> bool({'spam':'cheese', 'fizz':'buzz'})
True
La funcin obtenerPuntajeTablero() usa bucles for anidados para comprobar los 64 espacios
del tablero (8 filas multiplicadas por 8 columnas nos dan 64 espacios) y ver qu baldosas (si es
252
http://inventwithpython.com/es
que hay alguna) estn sobre ellos. Por cada baldosa 'X', el cdigo aumenta puntajex en la lnea
123. Por cada baldosa 'O', el cdigo incrementa puntajeo en la lnea 125.
ingresarBaldosaJugador():
# Permite al jugador elegir que baldosa desea ser.
# Devuelve una lista con la baldosa del jugador como primer elemento y
computadora como segundo.
baldosa = ''
while not (baldosa == 'X' or baldosa == 'O'):
print('Deseas ser X O?')
baldosa = input().upper()
Esta funcin pregunta al jugador qu baldosa quiere ser, 'X' u 'O'. El bucle for continuar
ciclando hasta que el jugador escriba 'X' u 'O'.
137.
# El primer elemento en la lista es la baldosa del juegador, el
segundo es la de la computadora.
138.
if baldosa == 'X':
139.
return ['X', 'O']
140.
else:
141.
return ['O', 'X']
Captulo 15 Reversi
253
if baldosasAConvertir == False:
return False
tablero[comienzox][comienzoy] = baldosa
for x, y in baldosasAConvertir:
tablero[x][y] = baldosa
return True
254
http://inventwithpython.com/es
De otro modo, esJugadaVlida() devuelve una lista de espacios del tablero donde colocar las
baldosas (las cadenas 'X' u 'O' en baldosa). La lnea 166 asigna el espacio en el que el jugador
ha jugado. El bucle for de la lnea 167 convierte todas las baldosas en baldosasAConvertir.
esEsquina(x, y):
# Devuelve True si la posicion es una de las esquinas.
return (x == 0 and y == 0) or (x == 7 and y == 0) or (x == 0 and y ==
== 7 and y == 7)
La funcin esEsquina() devuelve True si las coordenadas corresponden a uno de los espacios de
las esquinas con los valores (0,0), (7,0), (0,7) or (7,7). De lo contrario esEsquina() devuelve
False.
Captulo 15 Reversi
255
190.
# Devuelve la jugada como [x, y] (o devuelve las cadenas 'pistas' o
'salir')
191.
CIFRAS1A8 = '1 2 3 4 5 6 7 8'.split()
El bucle while continuar iterando hasta que el jugador haya ingresado una jugada vlida. Las
lneas 195 a 198 comprueban si el jugador quiere salir o activar/desactivar las pistas, y devuelve
en esos casos la cadena 'salir' o 'pistas' respectivamente. El mtodo lower() se llama sobre
la cadena devuelta por input(), de modo que el comando ser entendido aunque el jugador
escriba 'PISTAS' o 'Salir'.
El cdigo que llama a obtenerJugadaJugador() determinar qu hacer si el jugador quiere salir
o activar/desactivar las pistas.
200.
CIFRAS1A8:
201.
202.
203.
204.
205.
206.
El juego espera que el jugador ingrese las coordenadas XY de su jugada en forma de dos nmeros
sin otro caracter entre ellos. La lnea 200 comprueba primero que la longitud de la cadena
256
http://inventwithpython.com/es
ingresada sea 2. Luego de esto comprueba tambin que tanto jugada[0] (el primer caracter de la
cadena) como jugada[1] (el segundo caracter de la cadena) sean cadenas que existen en
CIFRAS1A8.
Recuerda que las estructuras de datos tableros tienen ndices que van de 0 a 7, no de 1 a 8. El
programa imprime 1 a 8 al mostrar el tablero en la funcin dibujarTablero() porque los noprogramadores estn acostumbrados a comenzar a contar desde 1 y no desde 0. Entonces para
convertir las cadenas en move[0] y move[1] a enteros, las lneas 201 y 202 restan 1 al valor
ingresado.
Incluso si el jugador ha ingresado una coordenada dentro del tablero, el cdigo necesita
comprobar que las reglas de Reversi permitan esa jugada. Esto se hace a travs de
esJugadaVlida() que recibe la estructura de datos tablero, la baldosa del jugador y las
coordenadas XY de la jugada.
Si esJugadaVlida() devuelve False, se ejecuta la sentencia continue de la lnea 204. Entonces
la ejecucin vuelve al principio del bucle while y vuelve a solicitar al jugador que ingrese una
jugada vlida.
En caso contrario, significa que el jugador ha ingresado una jugada vlida y la ejecucin debe
salir (break) del bucle while.
207.
else:
208.
print('Esta no es una jugada vlida. Ingresa la coordenada x
(1-8), luego la coordenada y (1-8).')
209.
print('Por ejemplo, 81 corresponde a la esquina superior
derecha.')
return [x, y]
Finalmente, obtenerJugadaJugador() devuelve una lista de dos elementos con las coordenadas
XY de la jugada vlida del jugador.
Captulo 15 Reversi
217.
257
obtenerJugadaComputadora()
Primero, la lnea 223 recorre cada jugada posible en jugadasPosibles. Si alguna de ellas
corresponde a una esquina, se devuelve como jugada ese espacio. Jugar en una esquina es una
buena idea en Reversi ya que una vez que una baldosa ha sido colocada sobre una esquina no
puede ser convertida. Como jugadasPosibles es una lista de listas de dos elementos, usamos
asignacin mltiple en el bucle for para asignar los valores x e y.
Si jugadasPosibles contiene mltiples jugadas sobre esquinas, se usa siempre la primera de la
lista. Pero como jugadasPosibles se ha mezclado previamente en la lnea 220, es aleatorio cul
de las esquinas aparecer primero en la lista.
258
http://inventwithpython.com/es
231.
hacerJugada(rplicaTablero, baldosaComputadora, x, y)
232.
puntaje =
obtenerPuntajeTablero(rplicaTablero)[baldosaComputadora]
233.
if puntaje > mejorPuntaje:
234.
mejorJugada = [x, y]
235.
mejorPuntaje = puntaje
236.
return mejorJugada
Si no se puede jugar sobre las esquinas, el programa recorre toda la lista y averigua qu jugada
resulta en el puntaje ms alto. El bucle for de la lnea 229 asigna x e y a cada posible jugada en
jugadasPosibles. Se asigna a mejorJugada la jugada con mejor puntaje que el cdigo ha
encontrado hasta el momento, y el puntaje de esta jugada se guarda en mejorPuntaje.
Cuando el cdigo del bucle encuentra una jugada que da un puntaje ms alto que mejorPuntaje,
las lneas 233 a 235 guardan esa jugada y su puntaje en mejorJugada y mejorPuntaje.
Captulo 15 Reversi
259
Aunque el cdigo elija siempre la primera en la lista si es que hay varias jugadas compartiendo el
mejor puntaje, la seleccin sigue siendo aleatoria pues la lista ha sido ordenada aleatoriamente en
la lnea 220. Esto asegura que la IA no ser predecible cuando haya ms que una nica opcin
para la mejor movida.
Estas son todas las funciones para el juego Reversi. El cdigo que comienza en la lnea 246
implementa el juego en s y llama a estas funciones a medida que son requeridas.
El bucle while de la lnea 248 es el bucle principal del juego. El programa volver a la lnea 248
cuando comience un nuevo juego. Primero se genera una nueva estructura de datos tablero
llamando a obtenerNuevoTablero() y se colocan las baldosas iniciales llamando a
reiniciarTablero(). tableroPrincipal es la principal estructura de datos tablero de este
programa. La llamada a ingresarBaldosaJugador() permitir al jugador elegir si quiere ser 'X'
u 'O'. Los valores de retorno se almacenan entonces en baldosaJugador y
baldosaComputadora utilizando asignacin mltiple.
260
http://inventwithpython.com/es
mostrarPistas
El bucle while que comienza en la lnea 257 se repetir cada vez que el jugador o la computadora
tomen un turno. La ejecucin saldr de este bucle al terminar el juego actual.
La lnea 258 tiene una sentencia if cuyo bloque asociado se ejecuta si es el turno del jugador. (El
bloque else que comienza en la lnea 282 se ejecuta si es el turno de la computadora.)
Primero se muestra el tablero en la pantalla. Si en modo pistas est activado (es decir,
mostrarPistas es True), entonces la estructura de datos tablero debe tener caracteres punto '.'
en cada espacio correspondiente a una jugada vlida para el jugador.
La funcin obtenerTableroConJugadasVlidas() se encarga de esto. Recibe como argumento
una estructura de datos tablero y devuelve una copia a la que se han agregado los caracteres punto
'.'. La lnea 262 pasa este tablero a la funcin dibujarTablero().
Si el modo pistas est desactivado, entonces la lnea 264 simplemente pasa tableroPrincipal a
dibujarTablero().
Luego de mostrar el tablero de juego al jugador, tambin queremos imprimir el puntaje actual
llamando a mostrarPuntajes() en la lnea 265.
266.
baldosaJugador)
jugada = obtenerJugadaJugador(tableroPrincipal,
Captulo 15 Reversi
261
if jugada == 'salir':
print('Gracias por jugar!')
sys.exit() # terminar el programa
elif jugada == 'pistas':
mostrarPistas = not mostrarPistas
continue
else:
hacerJugada(tableroPrincipal, baldosaJugador, jugada[0],
if obtenerJugadasVlidas(tableroPrincipal, baldosaComputadora)
break
else:
turn = 'La computadora'
Despus de hacer la jugada del jugador, la lnea 276 llama a obtenerJugadasVlidas() para ver
si la computadora tiene jugadas disponibles. Si obtenerJugadasVlidas() devuelve una lista
262
http://inventwithpython.com/es
vaca, quiere decir que no hay jugadas vlidas que la computadora pueda hacer. En ese caso, la
lnea 277 sale del bucle while y termina el juego.
Si este no es el caso, la lnea 279 asigna 'computadora' a la variable turno. El flujo de ejecucin
omite el bloque else y llega al final del bloque while, de modo que la ejecucin vuelve a la
sentencia while de la lnea 257. Esta vez, sin embargo, ser el turno de la computadora.
if obtenerJugadasVlidas(tableroPrincipal, baldosaJugador) ==
break
else:
turno = 'El jugador'
Captulo 15 Reversi
263
Las lneas 289 a 292 son similares a las lneas 276 a 279. Luego de que la computadora ha hecho
su jugada, la lnea 289 comprueba si hay alguna jugada vlida disponible para el jugador humano.
Si obtenerJugadasVlidas() devuelve una lista vaca, significa que no hay jugadas vlidas.
Entonces el juego concluye, y la lnea 290 sale del bucle while.
En caso contrario, hay al menos una jugada posible para el jugador. Entonces se asigna 'El
jugador' a la variable turno. No hay ms cdigo en el bloque while luego de la lnea 292, de
modo que la ejecucin regresa a la sentencia while de la lnea 257.
La lnea 294 es la primera lnea despus del bloque while que comenzaba en la lnea 257. Este
cdigo se ejecuta cuando se sale de ese bucle while desde las lneas 290 o 277. En este punto, el
juego ha terminado. Ahora el programa debe imprimir el tablero y los puntajes y determinar quin
ha ganado.
obtenerPuntajeTablero()
if not jugarDeNuevo():
break
Se llama a la funcin jugarDeNuevo(), la cual devuelve True si el jugador responde que quiere
volver a jugar. Si jugarDeNuevo() devuelve False, el operador not convierte a la condicin de
264
http://inventwithpython.com/es
la sentencia if en True. Entonces la ejecucin sale del bucle while que ha comenzado en la lnea
248. Como no hay ms lneas de cdigo despus de este bloque while, el programa termina.
En caso contrario, jugarDeNuevo() devuelve True (convirtiendo a la condicin de la sentencia
if en False), y entonces la ejecucin regresa a la sentencia while de la lnea 248 donde se crea
un nuevo tablero de juego.
Captulo 15 Reversi
265
Resumen
La IA puede parecer casi imbatible, pero esto no se debe a que la computadora sea inteligente. La
estrategia que sigue es simple: jugar en la esquina si es posible, de lo contrario tomar la jugada
que convierta ms baldosas. Nosotros podramos hacer lo mismo, pero nos tomara tiempo contar
cuntas baldosas seran convertidas con cada una de nuestras posibles jugadas. En cambio esto es
sencillo para la computadora. La computadora no es ms inteligente que nosotros, simplemente
es mucho ms rpida!
Este juego se parece a Sonar en el hecho de que utiliza una grilla como tablero. Tambin es
similar al Ta Te Ti, donde hay una IA que elige la mejor jugada para la computadora. Este
captulo slo ha introducido un nuevo concepto: las listas y cadenas vacas y el entero 0 se
evalan a False en el contexto de una condicin.
Descontando esto, este juego utiliza conceptos de programacin que ya conocas! No necesitas
saber mucho de programacin para crear juegos interesantes. Sin embargo, esto est cerca de lo
mejor que puedes lograr empleando slo arte ASCII. El tablero utiliza casi toda la pantalla, y el
juego no tiene colores.
Ms adelante en este libro, aprenderemos a crear juegos con grficos y animacin, no slo texto.
Lograremos esto utilizando un mdulo llamado Pygame, que agrega a Python nuevas funciones y
caractersticas que nos permitirn usar ms que slo texto y entradas del teclado.
http://inventwithpython.com/es
266
Captulo 16
SIMULACIN DE IA PARA
REVERSI
Temas Tratados En Este Captulo:
Simulaciones
Porcentajes
Grficos de Torta
Divisin Entera
La funcin round()
Partidas Computadora vs. Computadora
El algoritmo de IA de Reversi es simple, pero consigue vencerme en casi todas las partidas. Esto
es porque la computadora puede procesar instrucciones rpido, entonces comprobar todas las
posiciones en el tablero y elegir la jugada que da el mayor puntaje es fcil para la computadora.
Seguir este procedimiento a mano me tomara demasiado tiempo.
El programa Reversi del Captulo 14 tena dos funciones, obtenerJugadaJugador() y
obtenerJugadaComputadora(), las cuales devolvan una jugada en forma de lista [x, y] de dos
elementos. Adems ambas funciones tenan los mismos parmetros: la estructura de datos tablero
y la baldosa correspondiente al jugador. obtenerJugadaJugador() decida qu jugada [x, y]
devolver permitiendo al jugador escribir sus coordenadas. obtenerJugadaComputadora()
decida qu jugada devolver ejecutando el algoritmo de IA de Reversi.
Qu pasa cuando reemplazamos la llamada a obtenerJugadaJugador() por una llamada a
obtenerJugadaComputadora()? En este caso el jugador nunca ingresara una jugada, sino que
la computadora lo hara por l! La computadora estara jugando contra s misma!
Crearemos tres nuevos programas, cada uno basado en el programa Reversi del captulo anterior:
267
Ahora ejecuta el programa. Observa que el juego an te pregunta si quieres ser X u O, pero no te
pedir que ingreses ninguna jugada. Al haber reemplazado obtenerJugadaJugador(), ya no se
llama al cdigo que obtiene esta entrada del jugador. Todava debes pulsar ENTER luego de las
jugadas que originalmente correspondan a la computadora (debido a la instruccin
input('Presiona enter para ver la jugada de la computadora.') en la lnea 285), pero
la partida se juega sola!
Hagamos algunos otros cambios a AISim1.py. Todas las funciones que has definido para Reversi
pueden permanecer iguales. Pero reemplacemos toda la seccin principal del programa (de la
lnea 246 en adelante) por el cdigo que se muestra a continuacin. Aunque algo del cdigo
permanecer igual, cambiaremos la mayor parte. Pero las lneas anteriores a 246 son las mismas
que en el programa Reversi del captulo anterior.
Si obtienes errores luego de escribir este cdigo, compara lo que has escrito con el cdigo del
libro usando la herramienta diff online en http://invpy.com/es/diff/AISim1.
AISim1.py
246. print('Bienvenido a Reversi!')
247.
248. while True:
249.
# Reiniciar el tablero y la partida.
250.
tableroPrincipal = obtenerNuevoTablero()
251.
reiniciarTablero(tableroPrincipal)
252.
if quinComienza() == 'jugador':
253.
turno = 'X'
254.
else:
255.
turno = 'O'
256.
print('La ' + turno + ' comenzar.')
268
http://inventwithpython.com/es
257.
258.
while True:
259.
dibujarTablero(tableroPrincipal)
260.
puntajes = obtenerPuntajeTablero(tableroPrincipal)
261.
print('X ha obtenido %s puntos. O ha obtenido %s puntos.' %
(puntajes['X'], puntajes['O']))
262.
input('Presiona Enter para continuar.')
263.
264.
if turno == 'X':
265.
# Turno de X.
266.
otraBaldosa = 'O'
267.
x, y = obtenerJugadaComputadora(tableroPrincipal, 'X')
268.
hacerJugada(tableroPrincipal, 'X', x, y)
269.
else:
270.
# Turno de O.
271.
otraBaldosa = 'X'
272.
x, y = obtenerJugadaComputadora(tableroPrincipal, 'O')
273.
hacerJugada(tableroPrincipal, 'O', x, y)
274.
275.
if obtenerJugadasVlidas(tableroPrincipal, otraBaldosa) == []:
276.
break
277.
else:
278.
turno = otraBaldosa
279.
280.
# Mostrar el puntaje final.
281.
dibujarTablero(tableroPrincipal)
282.
puntajes = obtenerPuntajeTablero(tableroPrincipal)
283.
print('X ha obtenido %s puntos. O ha obtenido %s puntos.' %
(puntajes['X'], puntajes['O']))
284.
285.
if not jugarDeNuevo():
286.
sys.exit()
269
realmente cuestin de suerte quin gana. Cada jugador ganar aproximadamente la mitad de las
veces.
AISim2.py
Si obtienes errores luego de copiar este cdigo, compralo con el cdigo del libro usando la
herramienta diff online en http://invpy.com/es/diff/AISim2.
AISim2.py
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
print('Bienvenido a Reversi!')
victoriasx = 0
victoriaso = 0
empates = 0
numPartidas = int(input('Ingresa el nmero de partidas a jugar: '))
for partida in range(numPartidas):
print('Partida #%s:' % (partida), end=' ')
# Reiniciar el tablero y la partida.
tableroPrincipal = obtenerNuevoTablero()
reiniciarTablero(tableroPrincipal)
if quinComienza() == 'jugador':
turno = 'X'
else:
turno = 'O'
while True:
if turno == 'X':
# Turno de X.
270
http://inventwithpython.com/es
266.
otraBaldosa = 'O'
267.
x, y = obtenerJugadaComputadora(tableroPrincipal, 'X')
268.
hacerJugada(tableroPrincipal, 'X', x, y)
269.
else:
270.
# Turno de O.
271.
otraBaldosa = 'X'
272.
x, y = obtenerJugadaComputadora(tableroPrincipal, 'O')
273.
hacerJugada(tableroPrincipal, 'O', x, y)
274.
275.
if obtenerJugadasVlidas(tableroPrincipal, otraBaldosa) == []:
276.
break
277.
else:
278.
turno = otraBaldosa
279.
280.
# Mostrar el puntaje final.
281.
puntajes = obtenerPuntajeTablero(tableroPrincipal)
282.
print('X ha obtenido %s puntos. O ha obtenido %s puntos.' %
(puntajes['X'], puntajes['O']))
283.
284.
if puntajes['X'] > puntajes['O']:
285.
victoriasx += 1
286.
elif puntajes['X'] < puntajes['O']:
287.
victoriaso += 1
288.
else:
289.
empates += 1
290.
291. numPartidas = float(numPartidas)
292. porcentajex = round(((victoriasx / numPartidas) * 100), 2)
293. porcentajeo = round(((victoriaso / numPartidas) * 100), 2)
294. porcentajeempate = round(((empates / numPartidas) * 100), 2)
295. print('X ha ganado %s partidas (%s%%), O ha ganado %s partidas (%s%%),
empates en %s partidas (%s%%) sobre un total de %s partidas.' % (victoriasx,
porcentajex, victoriaso, porcentajeo, empates, porcentajeempate, numPartidas))
271
partidas sin detenerse a esperar que el usuario escriba nada. Aqu hay una prueba de ejecucin de
diez partidas computadora vs. computadora de Reversi:
Bienvenido a Reversi!
Ingresa el nmero de partidas a jugar: 10
Partida #0: X ha obtenido 40 puntos. O ha obtenido 23 puntos.
Partida #1: X ha obtenido 24 puntos. O ha obtenido 39 puntos.
Partida #2: X ha obtenido 31 puntos. O ha obtenido 30 puntos.
Partida #3: X ha obtenido 41 puntos. O ha obtenido 23 puntos.
Partida #4: X ha obtenido 30 puntos. O ha obtenido 34 puntos.
Partida #5: X ha obtenido 37 puntos. O ha obtenido 27 puntos.
Partida #6: X ha obtenido 29 puntos. O ha obtenido 33 puntos.
Partida #7: X ha obtenido 31 puntos. O ha obtenido 33 puntos.
Partida #8: X ha obtenido 32 puntos. O ha obtenido 32 puntos.
Partida #9: X ha obtenido 41 puntos. O ha obtenido 22 puntos.
X ha ganado 5 partidas (50.0%), O ha ganado 4 partidas (40.0%), empates en 1
partidas (10.0%) sobre un total de 10.0 partidas.
Como los algoritmos tienen una componente aleatoria, puede ser que no obtengas exactamente
los mismos nmeros.
Imprimir cosas en la pantalla enlentece a la computadora, pero ahora que hemos eliminado ese
cdigo, la computadora puede jugar una partida completa de Reversi en uno o dos segundos.
Piensa en esto. Cada vez que el programa imprime una de esas lneas con el puntaje final significa
que ha jugado una partida completa (que son alrededor de cincuenta o sesenta jugadas, cada una
de las cuales ha sido cuidadosamente comprobada para verificar que da la mayor cantidad posible
de puntos).
Porcentajes
Figura 16-1: Un grfico de torta con porciones de 10%, 15%, 25% y 50%.
272
http://inventwithpython.com/es
Los porcentajes son una porcin de una cantidad total, y estn comprendidos entre 0% y 100%.
Si tienes el 100% de una torta, quiere decir que tienes toda la torta. Si tienes el 0% de una torta,
significa que no tienes torta en absoluto. 50% de la torta corresponde a la mitad de la torta. Una
torta es una imagen de uso comn cuando hablamos de porcentajes. De hecho, hay un tipo de
grfico llamado grfico de torta que muestra a qu fraccin del total corresponde una dada
porcin. La Figura 16-1 muestra un grfico de torta dividido en porciones de 10%, 15%, 25% y
50%. Nota que la suma de 10% + 15% + 25% + 50% da un total de 100%: una torta entera.
Podemos calcular el porcentaje a travs de una divisin. Para obtener un porcentaje, divide la
parte que tienes por el total, y multiplica ese resultado por cien. Por ejemplo, si X ha ganado 50
partidas de 100, puedes calcular la expresin 50 / 100, que da como resultado 0.5. Multiplica
este resultado por 100 para obtener un porcentaje (en este caso, 50%).
Observa que si X hubiese ganado 100 partidas de 200, podras calcular el porcentaje dividiendo
100 / 200, que tambin se evaluara a 0.5. Al multiplicar 0.5 por 100 para obtener el porcentaje,
volveras a obtener 50%. Ganar 100 partidas de 200 es el mismo porcentaje (es decir, la misma
porcin) que ganar 50 juegos de 100.
Observa que en el ejemplo de arriba, el tipo de datos del valor almacenado en spam es siempre un
valor de punto flotante. Puedes pasar el valor de punto flotante a la funcin int(), la cul
devolver una forma entera del valor de punto flotante. Pero esto siempre redondear el valor de
punto flotante hacia abajo. Por ejemplo, las expresiones int(4.0), int(4.2) e int(4.9) se
evaluarn a 4, y nunca a 5.
273
La funcin round()
La funcin round() redondear un nmero float al float entero ms cercano. Prueba ingresar lo
siguiente en la consola interactiva:
>>> round(10.0)
10.0
>>> round(10.2)
10.0
>>> round(8.7)
9.0
>>> round(3.4999)
3.0
>>> round(2.5422, 2)
2.54
La funcin round() tambin tiene un parmetro opcional, donde puedes especificar hasta qu
lugar quieres redondear el nmero. Por ejemplo, la expresin round(2.5422, 2) se evala a
2.54 y round(2.5422, 3) se evala a 2.542.
El cdigo al final del programa muestra al usuario cuntas veces han ganado X y O, cuntas veces
han empatado, y a qu porcentajes corresponden estos nmeros. Estadsticamente, cuantas ms
partidas simules, ms exactos sern tus resultados para indicar cul es el mejor algoritmo IA. Si
slo simulas diez partidas y X gana 3 de ellas, parecer que el algoritmo de X gana slo el 30%
de las veces. Sin embargo, si simulases cien, o incluso mil partidas, probablemente veas que el
algoritmo de X gana cerca del 50% (es decir, la mitad) de las partidas.
Para hallar los porcentajes, divide el nmero de victorias o empates por el nmero total de
partidas. Luego, multiplica el resultado por 100. Aqu es posible que llegues a un nmero como
66.66666666666667. Entonces pasa este nmero a la funcin round() utilizando 2 como
segundo parmetro para limitar la precisin a dos lugares decimales, de modo que devuelva en su
lugar un float como 66.67 (el cual es mucho ms legible).
274
http://inventwithpython.com/es
Probemos otro experimento. Ejecuta AISim2.py de nuevo, pero esta vez hazlo simular 100 juegos:
100
ha obtenido
ha obtenido
ha obtenido
ha obtenido
18
37
29
24
puntos.
puntos.
puntos.
puntos.
Dependiendo de qu tan rpida sea tu computadora, esto puede llegar a tomar un par de minutos.
Puedes ver que los resultados de los cien juegos tienden a ser mitad y mitad, ya que tanto X como
O estn usando el mismo algoritmo.
AISim3.py
Si obtienes errores luego de escribir este cdigo, compralo con el cdigo fuente del libro usando
la herramienta diff online en http://invpy.com/es/diff/AISim3.
AISim3.py
245. def obtenerJugadaAleatoria(tablero, baldosa):
246.
# Devuelve una jugada al azar.
247.
return random.choice(obtenerJugadasVlidas(tablero, baldosa))
248.
249.
250. def esBorde(x, y):
251.
return x == 0 or x == 7 or y == 0 or y == 7
252.
253.
254. def
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274. def
275.
posible.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289. def
290.
291.
292.
293.
294.
295.
296.
297.
298.
275
obtenerEsquinaBordeMejorJugada(tablero, baldosa):
# Devuelve una jugada sobre una esquina, lado o la mejor jugada.
jugadasPosibles = obtenerJugadasVlidas(tablero, baldosa)
# Ordena al azar las jugadas posibles.
random.shuffle(jugadasPosibles)
# Ordena al azar las jugadas posibles.
for x, y in jugadasPosibles:
if esEsquina(x, y):
return [x, y]
# Siempre ir por una esquina de ser posible.
for x, y in jugadasPosibles:
if esBorde(x, y):
return [x, y]
return obtenerJugadaComputadora(tablero, baldosa)
obtenerBordeMejorJugada(tablero, baldosa):
# Devuelve una jugada a una esquina, un lado o la mejor jugada
jugadasPosibles = obtenerJugadasVlidas(tablero, baldosa)
# Ordena al azar las jugadas posibles.
random.shuffle(jugadasPosibles)
# Devuelve una jugada sobre un borde de ser posible.
for x, y in jugadasPosibles:
if esBorde(x, y):
return [x, y]
return obtenerJugadaComputadora(tablero, baldosa)
obtenerPeorJugada(tablero, baldosa):
# Devuelve la jugada que que convierta la menor cantidad de baldosas.
jugadasPosibles = obtenerJugadasVlidas(tablero, baldosa)
# Ordena al azar las jugadas posibles.
random.shuffle(jugadasPosibles)
# Recorre todas las jugadas posibles y recuerda la de mejor puntaje.
peorPuntaje = 64
for x, y in jugadasPosibles:
276
http://inventwithpython.com/es
299.
rplicaTablero = obtenerCopiaTablero(tablero)
300.
hacerJugada(rplicaTablero, baldosa, x, y)
301.
puntaje = obtenerPuntajeTablero(rplicaTablero)[baldosa]
302.
if puntaje < peorPuntaje:
303.
peorJugada = [x, y]
304.
peorPuntaje = score
305.
306.
return peorJugada
307.
308.
309. def obtenerEsquinaPeorJugada(tablero, baldosa):
310.
# Devuelve la esquina, el especio o la jugada que convierta la menor
cantidad de baldosas.
311.
jugadasPosibles = obtenerJugadasVlidas(tablero, baldosa)
312.
313.
# randomize the order of the possible moves
314.
random.shuffle(possibleMoves)
315.
316.
# Ordena al azar las jugadas posibles.
317.
for x, y in jugadasPosibles:
318.
if esEsquina(x, y):
319.
return [x, y]
320.
321.
return obtenerPeorJugada(tablero, baldosa)
322.
323.
324.
325. print('Bienvenido a Reversi!')
Function
obtenerJugadaAleatoria()
277
Description
Elige al azar una jugada vlida.
obtenerEsquinaBordeMejorJugada()
obtenerBordeMejorJugada()
obtenerPeorJugada()
obtenerEsquinaPeorJugada()
x, y = obtenerJugadaAleatoria(tableroPrincipal, 'O')
Ahora cuando ejecutes el programa con cien juegos, se ver aproximadamente as:
Bienvenido a Reversi!
Ingresa el nmero de partidas a jugar:
Partida #0: X ha obtenido 25 puntos. O
Partida #1: X ha obtenido 32 puntos. O
Partida #2: X ha obtenido 15 puntos. O
100
ha obtenido 38 puntos.
ha obtenido 32 puntos.
ha obtenido 0 puntos.
278
http://inventwithpython.com/es
Vaya! X ha ganado muchas ms partidas que O. Esto quiere decir que el algoritmo de
obtenerJugadaComputadora() (jugar sobre las esquinas disponibles, o de otro modo tomar la
jugada que convierta la mayor cantidad de baldosas) ha ganado ms partidas que el algoritmo de
obtenerJugadaAleatoria() (que elige jugadas al azar). Esto tiene sentido, ya que hacer
elecciones inteligentes suele ser mejor que simplemente jugar al azar.
Como puedes ver, cuando ambos jugadores juegan al azar cada uno gana aproximadamente el
50% de las veces. (En el caso de arriba, O parece haber tenido suerte ya que gan algo ms que la
mitad de las partidas.)
As como jugar en las esquinas es una buena idea porque estas fichas no pueden ser
reconvertidas, jugar sobre los bordes tambin puede ser una buena idea. Sobre los lados, una
baldosa tiene el borde del tablero y no est tan expuesta como las otras fichas. Las esquinas
siguen siendo preferibles a los bordes, pero jugar sobre los lados (incluso cuando haya una
movida que puede convertir ms fichas) puede ser una buena estrategia.
279
Vaya! Eso fue inesperado. Parece que elegir una jugada sobre el borde antes que una jugada que
convierte ms baldosas es una mala estrategia. El beneficio de jugar sobre el borde es menor que
el costo de convertir menos baldosas del oponente. Podemos confiar en estos resultados?
Ejecutemos el programa de nuevo, pero juguemos mil partidas esta vez. Es posible que esto tome
algunos minutos en tu computadora (pero tomara semanas si quisieras hacerlo a mano!) Intenta
cambiar las llamadas a las funciones y ejecutar de nuevo el programa.
Bienvenido a Reversi!
Ingresa el nmero de partidas a jugar: 1000
Partida #0: X ha obtenido 20 puntos. O ha obtenido 44 puntos.
Partida #1: X ha obtenido 54 puntos. O ha obtenido 9 puntos.
...omitido por brevedad...
Partida #998: X ha obtenido 38 puntos. O ha obtenido 23 puntos.
Partida #999: X ha obtenido 38 puntos. O ha obtenido 26 puntos.
X ha ganado 611 partidas (61.1%), O ha ganado 363 partidas (36.3%), empates en
26 partidas (2.6%) sobre un total de 1000.0 partidas.
280
http://inventwithpython.com/es
281
Efectivamente, incluso cuando en el resto de los casos estamos tomando la peor jugada, parece
ser que jugar sobre las esquinas resulta en un mayor nmero de victorias. A pesar de que hemos
comprobado que jugar sobre un borde hace que pierdas ms frecuentemente, jugar sobre las
esquinas siempre es una buena idea.
Resumen
Este captulo no se ha tratado realmente de ningn juego nuevo, sino que ha presentado el
modelado de varias estrategias para Reversi. Si quisisemos comprobar a mano la efectividad de
una estrategia tendramos que pasar semanas, incluso meses, ejecutando cuidadosamente partidas
de Reversi a mano y anotando los resultados. Pero si sabemos cmo programar una computadora
para jugar Reversi, entonces podemos hacer que simule partidas por nosotros utilizando estas
estrategias. Si piensas en esto, vers que la computadora ejecuta millones de lneas de nuestro
programa de Python en segundos! Tus experimentos con la simulacin de Reversi pueden incluso
ayudarte a mejorar tu estrategia para jugar Reversi en la vida real.
De hecho, este captulo podra ser un buen proyecto para una feria de ciencias. El problema aqu
es averiguar qu conjunto de jugadas consigue el mayor nmero de victorias contra otros
conjuntos de jugadas, y elaborar una hiptesis sobre cul es la mejor estrategia. Despus de
ejecutar varias simulaciones, puedes determinar qu estrategia funciona mejor. Usando
programacin puedes convertir en un proyecto de feria de ciencias la simulacin de cualquier
282
http://inventwithpython.com/es
juego de mesa! Y todo gracias a que sabes cmo dar instrucciones a la computadora para hacerlo,
paso por paso, lnea por lnea. Puedes hablar el idioma de la computadora, y conseguir que haga
por t una enorme cantidad de procesamiento de datos y clculo de nmeros.
Eso ha sido lo ltimo que veremos en este libro sobre juegos basados en texto. Los juegos que
slo utilizan texto pueden ser divertidos, incluso a pesar de ser simples. Pero la mayora de los
juegos modernos usan grficos, sonido y animacin para crear juegos con mucho ms atractivo
visual. En el resto de los captulos de este libro, aprenderemos a crear juegos con grficos usando
un mdulo de Python llamado Pygame.
283
Captulo 17
GRFICOS Y ANIMACIN
Temas Tratados En Este Captulo:
Instalando Pygame
Colores y Fuentes en Pygame
Grficos distorsionados y Anti-Aliasing
Atributos
Los Tipos de Datos pygame.font.Font, pygame.Surface, pygame.Rect y
pygame.PixelArray
Funcin Constructor
Las Funciones de Dibujo de Pygame
El Mtodo blit() para Objetos Surface
Eventos
Animacin
Hasta ahora, todos nuestros juegos han usado slo texto. El texto es presentado en la pantalla
como salida, y el jugador escribe texto mediante el teclado como entrada. Usar slo texto hace
que programar sea fcil de aprender. Pero en este captulo crearemos juegos ms emocionantes
con grficos y sonidos avanzados usando el mdulo Pygame.
Los captulos 17, 18 y 19 te ensean a usar Pygame para crear juegos con grficos, animacin,
entrada mediante el ratn y sonido. En estos captulos escribiremos cdigo fuente para programas
simples que no son juegos pero demuestran los conceptos de Pygame que iremos aprendiendo. El
juego en el captulo 20 usar todos estos conceptos juntos para crear un juego.
Instalando Pygame
Pygame no viene con Python. Igual que Python, Pygame puede descargarse gratis. En un
navegador, ve a la URL http://invpy.org/downloadpygame y descarga el archivo de instalacin de
Pygame para tu sistema operativo y versin de Python.
Abre el archivo de instalacin luego de descargarlo, y sigue las instrucciones hasta concluir la
instalacin de Pygame. Para comprobar que Pygame est correctamente instalado, escribe lo
siguiente en la consola interactiva:
>>> import pygame
284
http://inventwithpython.com/es
Si no aparece nada luego de haber pulsado la tecla INTRO, significa que Pygame se ha instalado
correctamente. Si aparece el error ImportError: No module named pygame, intenta volver a
instalar Pygame (y asegrate de haber escrito import pygame correctamente).
285
pygameHolaMundo.py
1. import pygame, sys
2. from pygame.locals import *
3.
4. # configurar pygame
5. pygame.init()
6.
7. # configurar la ventana
8. superficieVentana = pygame.display.set_mode((500, 400), 0, 32)
9. pygame.display.set_caption('Hola mundo!')
10.
11. # configurar los colores
12. NEGRO = (0, 0, 0)
13. BLANCO = (255, 255, 255)
14. ROJO = (255, 0, 0)
15. VERDE = (0, 255, 0)
16. AZUL = (0, 0, 255)
17.
18. # configurar fuentes
19. fuenteBsica = pygame.font.SysFont(None, 48)
20.
21. # configurar el texto
22. texto = fuenteBsica.render('Hola mundo!', True, BLANCO, AZUL)
23. textRect = texto.get_rect()
24. textRect.centerx = superficieVentana.get_rect().centerx
25. textRect.centery = superficieVentana.get_rect().centery
26.
27. # pintar un fondo blanco sobre la ventana
28. superficieVentana.fill(BLANCO)
29.
30. # dibujar un polgono verde sobre la superficie
31. pygame.draw.polygon(superficieVentana, VERDE, ((146, 0), (291, 106), (236,
277), (56, 277), (0, 106)))
32.
33. # dibujar algunas lneas azules sobre la superficie
34. pygame.draw.line(superficieVentana, AZUL, (60, 60), (120, 60), 4)
35. pygame.draw.line(superficieVentana, AZUL, (120, 60), (60, 120))
36. pygame.draw.line(superficieVentana, AZUL, (60, 120), (120, 120), 4)
37.
38. # dibujar un crculo azul sobre la superficie
39. pygame.draw.circle(superficieVentana, AZUL, (300, 50), 20, 0)
40.
41. # dibujar una elipse roja sobre la superficie
42. pygame.draw.ellipse(superficieVentana, ROJO, (300, 250, 40, 80), 1)
43.
44. # dibujar el rectngulo de fondo para el texto sobre la superficie
286
http://inventwithpython.com/es
287
La Funcin pygame.init()
4. # configurar pygame
5. pygame.init()
Todos los programas de Pygame deben llamar a la funcin pygame.init() luego de haber
importado el mdulo pygame, pero antes de llamar a cualquier otra funcin de Pygame. Esto
realiza los pasos necesarios para la inicializacin de Pygame.
Tuplas
Los valores de tuplas son similares a las listas, excepto que utilizan parntesis en lugar de
corchetes. Adems, como las cadenas, las tuplas no pueden ser modificadas. Por ejemplo, prueba
escribir lo siguiente en la consola interactiva:
288
http://inventwithpython.com/es
La lnea 8 crea una ventana GUI al llamar al mtodo set_mode() en el mdulo pygame.display.
(El mdulo display es un mdulo contenido dentro del mdulo pygame. El mdulo pygame
tiene hasta sus propios mdulos!)
Un pxel es el punto ms pequeo en la pantalla de tu computadora. Cada pxel puede iluminarse
con cualquier color. Todos los pxeles de tu pantalla trabajan juntos para mostrar todas las
imgenes que ves. Para crear una ventana de 500 pxeles de ancho por 400 pxeles de alto, se usa
la tupla (500, 400) como el primer parmetro de pygame.display.set_mode().
Se usan tres parmetros para el mtodo set_mode(). El primero es una tupla de dos enteros para
el ancho y alto de la ventana, en pxeles. El segundo y tercer parmetro son opciones avanzadas
que no trataremos en este libro. Slo pasa 0 y 32 respectivamente.
La funcin set_mode() devuelve un objeto pygame.Surface (al cual aqu llamaremos objeto
Surface por brevedad). Objeto es slo otro nombre para un valor de un tipo de datos que tiene
mtodos asociados. Por ejemplo, las cadenas son objetos en Python porque tienen datos (la
cadena en s misma) y mtodos (tales como lower() y split()). El objeto Surface representa la
ventana.
Las variables guardan referencias a objetos igual que podran guardar referencias a listas y
diccionarios. La seccin Referencias en el captulo 10 explica lo que son las referencias.
Colores RVA
11.
12.
13.
14.
15.
289
Hay tres colores primarios de luz: rojo, verde y azul. Combinando diferentes cantidades de estos
tres colores (que es lo que hace tu pantalla hace) puedes formar cualquier otro color. En Pygame,
las estructuras de datos que representan un color son tuplas de tres enteros. Se las llama valores
de Color RVA (en ingls, RGB por red, green, blue).
El primer valor en la tupla es la cantidad de rojo en el color. Un valor de 0 significa que no hay
rojo en este color, y un valor de 255 significa que est presente la mxima cantidad de rojo en el
color. Los valores segundo y tercero corresponden a la cantidad de verde y azul respectivamente.
Estos enteros forman una tupla RGB.
Por ejemplo, la tupla (0, 0, 0) no tiene nada de rojo, verde o azul. El color resultante es negro
puro. La tupla (255, 255, 255) tiene el mximo de rojo, verde y azul, resultando en blanco.
La tupla (255, 0, 0) representa el mximo de rojo pero nada de verde o azul, luego el color
resultante es rojo. Similarmente, (0, 255, 0) es verde y (0, 0, 255) es azul.
Puedes mezclar la cantidad de rojo, verde y azul para obtener cualquier matiz de cualquier color.
La Tabla 17-1 tiene algunos colores comunes y sus valores RGB. La pgina web
http://invpy.com/colors muestra otros valores de tuplas para diferentes colores.
290
http://inventwithpython.com/es
Figura 17-4: Una vista aumentada de una lnea sin antialiasing y otra con antialiasing.
El objeto Font que has guardado en la variable fuenteBsica tiene un mtodo llamado
render(). Este mtodo devolver un objeto Surface con el texto dibujado sobre l. El primer
parmetro de render() es la cadena de texto a dibujar. El segundo parmetro es un Booleano
para indicar si quieres utilizar antialiasing.
291
En la lnea 22, pasa True para usar antialiasing. El antialiasing difumina ligeramente tu texto para
que se vea ms suave. La Figura 17-4 muestra (con pxeles agrandados) cmo se ve una lnea con
y sin antialiasing.
Atributos
24. textRect.centerx = windowSurface.get_rect().centerx
25. textRect.centery = windowSurface.get_rect().centery
El tipo de datos pygame.Rect (llamado Rect para abreviar) representa reas rectangulares con un
cierto tamao y posicin asociados. Para crear un nuevo objeto llama a la funcin
pygame.Rect(). Los parmetros son enteros para las coordenadas XY de la esquina superior
izquierda, seguidos por el ancho y el alto, todos en pxeles.
El nombre de la funcin con los parmetros se ve as: pygame.Rect(izquierda, arriba,
ancho, alto)
De la misma forma que los mtodos son funciones asociadas con un objeto, los atributos son
variables asociadas con un objeto. El tipo de datos Rect tiene muchos atributos que describen el
rectngulo que representa. La Tabla 17-2 es una lista de atributos de un objeto Rect llamado
myRect.
Lo genial de los objetos Rect es que si modificas alguno de estos atributos, el resto de ellos se
modificar automticamente. Por ejemplo, si creas un objeto Rect que tiene 20 pxeles de ancho y
20 de alto, cuya esquina superior izquierda est en las coordenadas (30, 40), entonces la
coordenada X del lado derecho si fijar automticamente en 50 (porque 20 + 30 = 50).
Sin embargo, si cambias el atributo left (izquierda) con la lnea myRect.left = 100, entonces
Pygame cambiar automticamente el atributo right (derecho) a 120 (porque 20 + 100 = 120).
Todos los otros atributos para este objeto Rect tambin se actualizan.
292
http://inventwithpython.com/es
Pygame han decidido que los mdulos empezaran con minscula y los tipos de datos con
mayscula. Esto hace ms fcil distinguir los tipos de datos de los mdulos.
Funciones Constructor
Crea un objeto pygame.Rect llamando a la funcin pygame.Rect(). La funcin pygame.Rect()
tiene el mismo nombre que el tipo de datos pygame.Rect. Las funciones que tienen el mismo
nombre que su tipo de datos y crean objetos o valores de este tipo de datos se denominan
funciones constructor.
Un polgono es una forma cuyos mltiples lados son lneas rectas. Crculos y elipses no son
polgonos. La Figura 17-5 tiene algunos ejemplos de polgonos.
Tabla 17-2: Atributos Rect
293
pygame.Rect Atributo
miRect.left
Descripcin
Valor entero de la coordenada X del lado izquierdo del rectngulo.
miRect.right
miRect.top
miRect.bottom
miRect.centerx
miRect.centery
miRect.width
miRect.height
miRect.size
miRect.topleft
miRect.topright
miRect.bottomleft
miRect.bottomright
miRect.midleft
miRect.midright
miRect.midtop
miRect.midbottom
http://inventwithpython.com/es
294
Una tupla de tuplas que representa las coordenadas XY de los puntos a dibujar en orden.
La ltima tupla se conectar automticamente con la primera para cerrar la forma.
Opcionalmente, un entero para el ancho de las lneas del polgono. Sin esto, el polgono
ser rellenado del color de la lnea.
La funcin pygame.draw.line()
33.
34.
35.
36.
El color de la lnea.
Una tupla de dos enteros para las coordenadas XY del otro extremo de la lnea.
Si pasas 4 para el ancho, la lnea tendr 4 pxeles de ancho. Si no especificas este parmetro,
tomar el valor por defecto de 1. Las tres llamadas a pygame.draw.line() en las lneas 34, 35 y
36 dibujan la Z azul en el objeto Surface.
La Funcin.draw.circle()
38. # dibujar un crculo azul sobre la superficie
39. pygame.draw.circle(superficieVentana, AZUL, (300, 50), 20, 0)
Una tupla de dos enteros para las coordenadas XY del centro del crculo.
295
La Funcin pygame.draw.ellipse()
41. # dibujar una elipse roja sobre la superficie
42. pygame.draw.ellipse(superficieVentana, ROJO, (300, 250, 40, 80), 1)
El color de la elipse.
Una tupla de cuatro enteros para los bordes izquierdo y superior, ancho y altura de la
elipse.
La Funcin pygame.draw.rect()
44. # dibujar el rectngulo de fondo para el texto sobre la superficie
45. pygame.draw.rect(superficieVentana, ROJO, (textRect.left - 20, textRect.top
- 20, textRect.width + 40, textRect.height + 40))
296
http://inventwithpython.com/es
En la lnea 45, quieres que el rectngulo que dibujas est 20 pxeles alrededor del rectngulo de
texto. Es por esto que los bordes inquierdo y superior del rectngulo corresponden a los bordes
izquierdo y superior de textRect menos 20. (Recuerda, restas porque las coordenadas disminuyen
cuando te mueves hacia arriba y hacia la izquierda.) Y el ancho y la altura corresponden al ancho
y a la altura de textRect ms 40 (para compensar por el desplazamiento adicional de 20 pxeles de
los bordes izquierdo y superior).
El Tipo de pygame.PixelArray
47. # obtener un arreglo de pxeles de la superficie
48. arregloDePxeles = pygame.PixelArray(superficieVentana)
49. arregloDePxeles[480][380] = NEGRO
Crear un objeto PixelArray a partir de un objeto Surface bloquea al objeto Surface. Esto significa
que no puede llamarse a la funcin blit() (descripta a continuacin) sobre ese objeto Surface.
Para desbloquear el objeto Surface, debes borrar el objeto PixelArray con el operador del. Si
olvidas borrar el objeto PixelArray, recibirs un mensaje de error con el texto pygame.error:
Surfaces must not be locked during blit.
297
El mtodo blit() dibujar los contenidos de un objeto Surface sobre otro objeto Surface. La
lnea 53 imprime el objeto Surface Hola mundo! como texto y lo dibuja sobre el objeto
Surface guardado en la variable superficieVentana.
El segundo parmetro de blit() especifica dnde en la superficie de superficieVentana se dibuja
el texto. En este caso se pasa el objeto Rect que devuelve la llamada a text.get_rect() en la
lnea 23.
La Funcin pygame.display.update()
55. # dibujar la ventana sobre la pantalla
56. pygame.display.update()
En Pygame, nada se dibuja realmente sobre la pantalla hasta que se llama a la funcin
pygame.display.update(). Esto es porque dibujar sobre la pantalla es lento comparado con
dibujar sobre objetos Surface en la memoria de la computadora. No queremos actualizar la
pantalla despus de cada llamada a una funcin de dibujo, sino actualizar la pantalla una sola vez
despus de haber llamado a todas las funciones de dibujo.
La lnea 59 es el comienzo del bucle del juego. La condicin para la sentencia while se fija en
True de modo que el bucle contine para siempre. El nico caso en que el bucle se detiene es si
un evento causa que el programa termine.
298
http://inventwithpython.com/es
La Funcin pygame.event.get()
60.
61.
La Funcin pygame.quit()
62.
63.
pygame.quit()
sys.exit()
Animacin
En este programa tenemos varios bloques diferentes rebotando contra los bordes de la ventana.
Los bloques son de diferentes colores y tamaos, y se mueven slo en trayectorias diagonales.
Para animar los bloques (es decir, hacer que parezca que se estn moviendo) desplazaremos los
299
bloques unos pocos pxeles en cada iteracin del bucle del juego. Esto har parecer que los
bloques se estn moviendo por la pantalla.
animacin.py
1. import pygame, sys, time
2. from pygame.locals import *
3.
4. # Establece pygame
5. pygame.init()
6.
7. # Establece la ventana
8. ANCHOVENTANA = 400
9. ALTOVENTANA = 400
10. superficieVentana = pygame.display.set_mode((ANCHOVENTANA, ALTOVENTANA), 0,
32)
11. pygame.display.set_caption('Animacin')
12.
13. # Establece las variables de direccin
14. ABAJOIZQUIERDA = 1
15. ABAJODERECHA = 3
16. ARRIBAIZQUIERDA = 7
17. ARRIBADERECHA = 9
18.
19. VELOCIDADMOVIMIENTO = 4
20.
21. # Establece los colores
22. NEGRO = (0, 0, 0)
23. ROJO = (255, 0, 0)
24. VERDE = (0, 255, 0)
25. AZUL = (0, 0, 255)
26.
27. # Establece la estructura de datos de los bloques.
28. b1 = {'rect':pygame.Rect(300, 80, 50, 100), 'color':ROJO,
'dir':ARRIBADERECHA}
29. b2 = {'rect':pygame.Rect(200, 200, 20, 20), 'color':VERDE,
'dir':ARRIBAIZQUIERDA}
30. b3 = {'rect':pygame.Rect(100, 150, 60, 60), 'color':AZUL,
'dir':ABAJOIZQUIERDA}
31. bloques = [b1, b2, b3]
300
http://inventwithpython.com/es
32.
33. # Corre el ciclo de juego
34. while True:
35.
# Busca un evento QUIT.
36.
for evento in pygame.event.get():
37.
if evento.type == QUIT:
38.
pygame.quit()
39.
sys.exit()
40.
41.
# Dibuja el fondo negro sobre la superficie
42.
superficieVentana.fill(NEGRO)
43.
44.
for b in bloques:
45.
# mueve la estructura de datos de bloques
46.
if b['dir'] == ABAJOIZQUIERDA:
47.
b['rect'].left -= VELOCIDADMOVIMIENTO
48.
b['rect'].top += VELOCIDADMOVIMIENTO
49.
if b['dir'] == ABAJODERECHA:
50.
b['rect'].left += VELOCIDADMOVIMIENTO
51.
b['rect'].top += VELOCIDADMOVIMIENTO
52.
if b['dir'] == ARRIBAIZQUIERDA:
53.
b['rect'].left -= VELOCIDADMOVIMIENTO
54.
b['rect'].top -= VELOCIDADMOVIMIENTO
55.
if b['dir'] == ARRIBADERECHA:
56.
b['rect'].left += VELOCIDADMOVIMIENTO
57.
b['rect'].top -= VELOCIDADMOVIMIENTO
58.
59.
# Verifica si el bloque se movi fuera de
60.
if b['rect'].top < 0:
61.
# el bloque se movi por arriba de la
62.
if b['dir'] == ARRIBAIZQUIERDA:
63.
b['dir'] = ABAJOIZQUIERDA
64.
if b['dir'] == ARRIBADERECHA:
65.
b['dir'] = ABAJODERECHA
66.
if b['rect'].bottom > ALTOVENTANA:
67.
# el bloque se movi por debajo de la
68.
if b['dir'] == ABAJOIZQUIERDA:
69.
b['dir'] = ARRIBAIZQUIERDA
70.
if b['dir'] == ABAJODERECHA:
71.
b['dir'] = ARRIBADERECHA
72.
if b['rect'].left < 0:
73.
# el bloque se movi por la izquierda
74.
if b['dir'] == ABAJOIZQUIERDA:
75.
b['dir'] = ABAJODERECHA
76.
if b['dir'] == ARRIBAIZQUIERDA:
77.
b['dir'] = ARRIBADERECHA
78.
if b['rect'].right > ANCHOVENTANA:
la ventana
ventana
ventana
de la ventana
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
# el bloque se
if b['dir'] ==
b['dir'] =
if b['dir'] ==
b['dir'] =
301
302
http://inventwithpython.com/es
Por ejemplo, si un bloque que est movindose hacia abajo y a la derecha rebota contra el borde
inferior de la ventana, queremos que su nueva direccin sea hacia arriba y a la derecha.
Podemos usar objeto Rect para marcar la posicin y el tamao de cada bloque, una tupla de tres
enteros para representar el color del bloque y un entero para representar en cul de las cuatro
direcciones diagonales el bloque est movindose.
En cada iteracin de bucle de juego, ajustaremos las coordenadas X e Y del bloque en el objeto
Rect. Adems, en cada iteracin dibujaremos todos los bloques en la pantalla en su posicin
actual. A medida que la ejecucin del programa itere sobre el bucle de juego, los bloques irn
movindose gradualmente a lo largo de la pantalla y darn la impresin de estar suavemente
deslizndose y rebotando.
En este programa vers que el alto y el ancho de la ventana son usados para ms que slo la
llamada a set_mode(). Usaremos variables constantes de modo que si alguna vez quieres
303
cambiar el tamao de la ventana slo tengas que cambiar las lneas 8 y 9. Dado que el ancho y la
altura nunca cambian durante la ejecucin del programa, una variable constante es una buena
idea.
11. pygame.display.set_caption('Animacin')
Usaremos las teclas en la almohadilla numrica del teclado para recordarnos qu nmero
corresponde a cada direccin. Esto es similar a lo que hicimoe en el Ta Te Ti. 1 es hacia abajo y a
la izquierda, 3 es abajo a la derecha, 7 es arriba a la izquierda y 9 es arriba a la derecha. Sin
embargo, esto puede ser difcil de recordar, de modo que usaremos variables constantes en lugar
de estos valores enteros.
Podramos haber usado cualquier valor para estas direcciones en lugar de usar una variable
constante. Por ejemplo, podras usar la cadena 'abajoizquierda' para representar la direccin
diagonal hacia abajo y a la izquierda. Sin embargo, si alguna vez te equivocaras al escribir
'abajoizquierda' (y escribieras por ejemplo 'sbajoizquierda'), Python no reconocera que tu
intencin fue escribir 'abajoizquierda' en lugar de 'sbajoizquierda'. Esto hara que tu
programa se comportase en forma extraa, pero no ocasionara un fallo del programa.
Pero si usas variables constantes y accidentalmente escribes el nombre SBAJOIZQUIERDA en lugar
de ABAJOIZQUIERDA, Python se dar cuenta de que no existe una variable llamada
SBAJOIZQUIERDA y este error causara un fallo. Seguira siendo un error bastante feo, pero al
menos te daras cuenta inmediatamente y podras arreglarlo.
19. VELOCIDADMOVIMIENTO = 4
Usamos una variable constante para determinar qu tan rpido se mueve el bloque. Un valor de 4
significa que el bloque se mover 4 pxeles por cada iteracin del bucle del juego.
304
http://inventwithpython.com/es
Las lneas 22 a 25 establecen variables constantes para los colores. Recuerda que Python usa una
tupla de tres valores enteros para las cantidades de rojo, verde y azul, a la cual llamamos valor
RVA. Los enteros van desde 0 hasta 255.
Las variables constantes se usan por legibilidad. A la computadora no le preocupa si usas una
variable llamada VERDE para el color verde. Simplemente es ms fcil recordar que VERDE
corresponde al color verde, en lugar de (0, 255, 0).
Establece un diccionario como la estructura de datos que representa cada bloque. (El Captulo 9
introdujo los diccionarios.) El diccionario tendr las claves 'rect' (con un objeto Rect por
valor), 'color' (con una tupla de tres enteros por valor) y 'dir' (con una de las variables
constantes de direccin por valor).
La variable b1 guardar la estructura de datos de un bloque. La esquina superior izquierda de este
bloque en la coordenada X 300 y en la coordenada Y 80. Tiene un ancho de 50 pxeles y una
altura de 100 pxeles. Su color es rojo y su direccin es ARRIBADERECHA.
29. b2 = {'rect':pygame.Rect(200, 200, 20, 20), 'color':VERDE,
'dir':ARRIBAIZQUIERDA}
30. b3 = {'rect':pygame.Rect(100, 150, 60, 60), 'color':AZUL,
'dir':ABAJOIZQUIERDA}
Las lneas 29 y 30 crean dos estructuras de datos similares para bloques que tienen diferente
tamao, posicin, color y direccin.
31. bloques = [b1, b2, b3]
305
La lnea 31 pone todas estas estructuras de datos en una lista, y guarda la lista en una variable
llamada bloques.
La variable bloques guarda una lista. bloques[0] corresponde al diccionario guardado en b1.
bloques[0]['color'] sera la clave 'color' en b1, de modo que la expresin
bloques[0]['color'] se evaluara a (255, 0, 0). De este modo puedes referirte a cualquiera
de los valores de cualquiera de las estructuras de datos empezando con bloques.
Dentro del bucle del juego, los bloques se movern alrededor de la pantalla en la direccin en la
que traen y rebotarn cuando choquen contra alguno de los lados. Tambin hay cdigo para
dibujar todos los bloques sobre la superficie superficieVentana y llamar a
pygame.display.update().
El bucle for para comprobar todos los eventos en la lista devuelta por pygame.event.get() es el
mismo que en nuestro programa !Hola mundo!.
41.
42.
Primero, la lnea 42 rellena toda la superficie con negro de modo que todo lo que ha sido dibujado
anteriormente sea borrado.
for b in bloques:
A continuacin, el cdigo debe actualizar la posicin de cada bloque, as que itera sobre la lista
de bloques. Dentro del bucle, nos referiremos al bloque actual simplemente como b, para que sea
ms fcil de escribir.
45.
46.
306
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
http://inventwithpython.com/es
b['rect'].left -= VELOCIDADMOVIMIENTO
b['rect'].top += VELOCIDADMOVIMIENTO
if b['dir'] == ABAJODERECHA:
b['rect'].left += VELOCIDADMOVIMIENTO
b['rect'].top += VELOCIDADMOVIMIENTO
if b['dir'] == ARRIBAIZQUIERDA:
b['rect'].left -= VELOCIDADMOVIMIENTO
b['rect'].top -= VELOCIDADMOVIMIENTO
if b['dir'] == ARRIBADERECHA:
b['rect'].left += VELOCIDADMOVIMIENTO
b['rect'].top -= VELOCIDADMOVIMIENTO
El nuevo valor que se asigna a los atributos top y left depende de la direccin del bloque. Si la
direccin del bloque (que se guarda en la clave 'dir') es ABAJOIZQUIERDA o ABAJODERECHA, se
aumentar el atributo top. Si la direccin es ARRIBAIZQUIERDA o ARRIBADERECHA, se reducir el
atributo top.
Si la direccin del bloque es ABAJODERECHA o ARRIBADERECHA, se aumentar el atributo left. Si
la direccin es ABAJOIZQUIERDA o ARRIBAIZQUIERDA, se reducir el atributo left.
El valor en que cambian estor atributos es el entero guardado en VELOCIDADMOVIMIENTO.
VELOCIDADMOVIMIENTO guarda cuntos pxeles se mueven los bloques en cada iteracin del bucle
del juego, y fue establecido en la lnea 19.
Luego de que las lneas 44 a 57 muevan el bloque, se comprobar si el bloque ha cruzado el borde
de la ventana. Si es as, queremos que el bloque "rebote". En el cdigo esto significa asignar un
nuevo valor a su clave 'dir'. El bloque se mover en una nueva direccin en la siguiente iteracin
del bucle del juego. Esto se ve como si el bloque hubiese rebotado contra el lado de la ventana.
En la sentencia if de la lnea 60, consideramos que el bloque se ha movido por encima del borde
superior de la ventana si el atributo top del objeto Rect del bloque es menor que 0. En ese caso, se
cambia la direccin dependiendo de la direccin en la cual el bloque se estaba moviendo
(ARRIBAIZQUIERDA o ARRIBADERECHA).
307
if b['rect'].left < 0:
# el bloque se movi por la izquierda de la ventana
if b['dir'] == ABAJOIZQUIERDA:
b['dir'] = ABAJODERECHA
if b['dir'] == ARRIBAIZQUIERDA:
b['dir'] = ARRIBADERECHA
if b['rect'].right > ANCHOVENTANA:
# el bloque se movi por la derecha de la ventana
if b['dir'] == ABAJODERECHA:
b['dir'] = ABAJOIZQUIERDA
if b['dir'] == ARRIBADERECHA:
b['dir'] = ARRIBAIZQUIERDA
Las lneas 78 a 83 son similares a las lneas 72 a 77, pero comprueban si el lado izquierdo del
bloque ha cruzado el borde izquierdo de la ventana. Recuerda, la coordenada X comienza en 0 en
el borde izquierdo de la ventana y aumenta hasta ANCHOVENTANA en el borde izquierdo de la
ventana.
308
85.
86.
http://inventwithpython.com/es
Ahora que los bloques se han movido, deberan ser dibujados en sus nuevas posiciones en la
superficie superficieVentana llamando a la funcin pygame.draw.rect(). Pasamos
superficieVentana porque es el objeto Surface sobre el cual dibujaremos el rectngulo.
Pasamos b['color'] porque es el color del rectngulo. Pasamos b['rect'] porque es el objeto
Rect que contiene la posicin y el tamao del rectngulo a dibujar.
La lnea 86 es la ltima lnea del bucle for. Si quieres agregar nuevos bloques, slo tienes que
modificar la lista de bloques en la lnea 31 y el resto del cdigo seguir funcionando.
309
Recuerda que los bloques no estn realmente movindose. En cada iteracin del bucle del juego,
el cdigo vuelve a dibujar la ventana completa con nuevos bloques desplazados unos pocos
pxeles de la posicin de los bloques anteriores.
Resumen
Este captulo ha presentado una forma completamente nueva de crear programas de computadora.
Los programas de los captulos anteriores simplemente se detenan y esperaban a que el jugador
ingresara un texto. Sin embargo, en nuestro programa de animacin, el programa est
constantemente actualizando las estructuras de datos de las cosas sin esperar informacin del
jugador.
Recuerda cmo en nuestros juegos Ahorcado y Ta Te Ti tenamos estructuras de datos que
representaban el estado del tablero, y estas estructuras de datos eran pasadas a una funcin
dibujarTablero() para ser mostradas en la pantalla. Nuestro programa de animacin es similar. La
variable bloques contiene una lista de estructuras que representan bloques a dibujar en la
pantalla, y estos son dibujados sobre la pantalla dentro del bucle del juego.
Pero sin las llamadas a input(), cmo obtendremos informacin del jugador? En nuestro
prximo captulo, cubriremos cmo los programas pueden saber cundo el jugador pulsa teclas
del teclado. Tambin aprenderemos un concepto llamado deteccin de colisiones.
310
http://inventwithpython.com/es
Captulo 18
DETECCIN DE COLISIONES
Y ENTRADAS DE
TECLADO/RATN
Temas Tratados En Este Captulo:
Deteccin de Colisiones
No Modifiques una Lista Mientras Iteras Sobre Ella
Entrada de Teclado en Pygame
Entrada de Ratn en Pygame
Deteccin de colisiones es darse cuenta cuando dos cosas en la pantalla se han tocado (es decir,
han colisionado). Por ejemplo, si el jugador toca un enemigo puede perder salud. O quiz el
programa necesita saber cuando el jugador toca una moneda para recogerla automticamente.
Deteccin de colisiones puede ayudar a determinar si el personaje del juego est parado sobre el
suelo o si no hay nada ms que aire debajo de l.
En nuestros juegos, la deteccin de colisiones determinar si dos rectngulos se superponen o no.
Nuestro prximo programa de ejemplo cubrir esta tcnica bsica.
Ms adelante en este captulo, veremos cmo nuestros programas Pygame pueden recibir entradas
del usuario a travs del teclado o del ratn. Es un poco ms complicado que llamar a la funcin
input() como hicimos para nuestros programas de texto. Pero usar el teclado es mucho ms
interactivo en programas GUI. Y usar el ratn ni siquiera es posible en nuestros juegos de texto.
Estos dos conceptos harn que tus juegos sean mucho ms emocionantes.
311
En cada interaccin durante el bucle del juego, el programa leer cada objeto Rect en la lista y
dibujar un cuadrado verde en la ventana. Cada cuarenta iteraciones del bucle del juego
agregaremos un nuevo objeto Rect a la lista de modo que aparezcan constantemente nuevos
cuadrados de comida en la pantalla.
El rebotn es representado por un diccionario. El diccionario tiene una clave llamada 'rect'
(cuyo valor es un objeto pygame.Rect) y una clave llamada 'dir' (cuyo valor es una de las
variables constantes de direccin como en el programa de Animacin del captulo anterior).
A medida que el rebotn rebota por la ventana, comprobamos si colisiona con alguno de los
cuadrados de comida. Si es as, borramos ese cuadrado de comida de modo que ya no sea
dibujado en la pantalla. Esto dar la impresin de que el rebotn se come los cuadrados de
comida en la ventana.
Escribe lo siguiente en un nuevo archivo y gurdalo como deteccinColisin.py. Si obtienes
errores despus de haber copiado el cdigo, compara el cdigo que has escrito con el cdigo del
libro usando la herramienta online diff en http://invpy.com/es/diff/deteccionColision.
deteccinColisin.py
1. import pygame, sys, random
2. from pygame.locals import *
3.
4. def verifSuperposicinRects(rect1, rect2):
5.
for a, b in [(rect1, rect2), (rect2, rect1)]:
6.
# Verifica si las esquinas de a se encuentran dentro de b
7.
if ((puntoDentroDeRect(a.left, a.top, b)) or
8.
(puntoDentroDeRect(a.left, a.bottom, b)) or
9.
(puntoDentroDeRect(a.right, a.top, b)) or
10.
(puntoDentroDeRect(a.right, a.bottom, b))):
11.
return True
12.
13.
return False
14.
15. def puntoDentroDeRect(x, y, rect):
16.
if (x > rect.left) and (x < rect.right) and (y > rect.top) and (y <
rect.bottom):
17.
return True
18.
else:
19.
return False
20.
21.
22. # establece el juego
23. pygame.init()
24. relojPrincipal = pygame.time.Clock()
25.
312
http://inventwithpython.com/es
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
313
314
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
http://inventwithpython.com/es
El programa se ver como la Figura 18-1. El cuadrado rebotn ir rebotando por toda la pantalla.
Al colisionar con los cuadrados de comida verdes estos desaparecern de la pantalla.
Figura 18-1: Una captura de pantalla alterada del programa Deteccin de Colisiones.
El programa de deteccin de colisiones importa las mismas cosas que el programa de animacin
del captulo anterior, junto con el mdulo random.
Para detectar colisiones, necesitas una funcin que pueda determinar si dos rectngulos se
superponen o no. La Figura 18-2 muestra ejemplos de rectngulos superpuestos y no
superpuestos.
315
Figura 18-2: Ejemplos de rectngulos que colisionan (izquierda) y rectngulos que no colisionan
(derecha).
verifSuperposicinRects()
Las lneas 5 a 11 comprueban si las esquinas de un rectngulo estn dentro del otro. Ms tarde,
crearemos una funcin llamada puntoDentroDeRect() que devuelve True si las coordenadas XY
del punto est dentro del rectngulo. Llamaremos a esta funcin para cada una de las ocho
esquinas, y si alguna de estas llamadas devuelve True, los operadores or harn que toda la
condicin sea True.
Los parmetros de verifSuperposicinRects() son rect1 y rect2. Primero comprueba si las
esquinas de rect1 estn dentro de rect2, y despus si las esquinas de rect2 estn dentro de
rect1.
No necesitas repetir para rect1 y rect2 el cdigo que comprueba las cuatro esquinas. En
cambio, puedes usar a y b en las lneas 7 a 10. El bucle for en la lnea 5 usa asignacin mltiple.
En la primera iteracin, a toma el valor rect1 y b toma el valor rect2. En la segunda iteracin
del bucle, es lo opuesto: a adquiere el valor rect2 y b toma rect1.
13.
return False
http://inventwithpython.com/es
316
Si la lnea 11 nunca devuelve True, entonces nuinguna de las ocho esquinas comprobadas est
dentro del otro rectngulo. En ese caso, los rectngulos no han colisionado y la lnea 13 devuelve
False.
La coordenada X del punto es mayor que la coordenada X del borde izquierdo del
rectngulo.
La coordenada X del punto es menor que la coordenada X del borde derecho del
rectngulo.
La coordenada Y del punto es mayor que la coordenada Y del borde inferior del
rectngulo.
La coordenada Y del punto es menor que la coordenada Y del borde superior del
rectngulo.
Si alguna de estas es False, entonces el punto est fuera del rectngulo. La lnea 16 combina
estas cuatro afirmaciones en la condicin de la sentencia if utilizando operadores and.
317
Figura 18-3: Ejemplo de coordenadas dentro y fuera de un rectngulo. Los puntos (50, 30), (85,
30) y (50, 50) estn dentro del rectngulo, el resto estn afuera del mismo.
18.
19.
else:
return False
Esta funcin es llamada desde la funcin verifSuperposicinRects() para ver si alguna de las
esquinas de los objetos pygame.Rect est dentro del otro. Estas dos funciones te permiten
detectar colisiones entre dos rectngulos.
Las lneas 46 a 48 configuran algunas variables para los bloques de comida que aparecen en la
pantalla. contadorComida comenzar en el valor 0, NUEVACOMIDA en 40, y TAMAOCOMIDA en 20.
318
http://inventwithpython.com/es
La lnea 49 configura una nueva estructura de datos llamada rebotn. rebotn es un diccionario con
dos claves. La clave 'rect' contiene un objeto pygame.Rect que representa el tamao y la
posicin del rebotn.
La clave 'dir' contiene la direccin en la cual el rebotn se est moviendo. El rebotn se mover
de la misma forma en que se movan los bloques en el programa de animacin del Captulo 17.
50. comidas = []
51. for i in range(20):
52.
comidas.append(pygame.Rect(random.randint(0, ANCHOVENTANA TAMAOCOMIDA), random.randint(0, ALTOVENTANA - TAMAOCOMIDA), TAMAOCOMIDA,
TAMAOCOMIDA))
El programa lleva un registro de todos los cuadrados de comida con una lista de objetos Rect en
comidas. Las lneas 51 y 52 crean veinte cuadrados de comida ubicados aleatoriamente en la
pantalla. Puedes usar la funcin random.randint() para generar coordenadas XY aleatorias.
En la lnea 52, llamamos a la funcin constructor pygame.Rect() para que devuelva un nuevo
objeto pygame.Rect. Este objeto representar la posicin y el tamao del cuadrado de comida.
Los primeros dos parmetros para pygame.Rect() son las coordenadas XY de la esquina superior
izquierda. Queremos que la coordenada aleatoria est entre 0 y el tamao de la ventana menos el
tamao del cuadrado de comida. Si la coordenada aleatoria estuviese simplemente entre 0 y el
tamao de la ventana, el cuadrado de comida podra quedar fuera de la ventana, como en la
Figura 18-4.
Figura 18-4: Para un rectngulo de 20 por 20, tener su esquina superior izquierda en (400, 200)
en una ventana de 400 por 400 significara estar fuera de la ventana. Para que el cuadrado est
contenido en de la ventana, la esquina superior izquierda debera estar en (380, 200).
319
El tercer parmetro de pygame.Rect() es una tupla que contiene el ancho y la altura del cuadrado
de comida. Tanto el ancho como la altura corresponden al valor en la constante TAMAOCOMIDA.
Antes de dibujar los cuadrados de comida, se comprueba si el rebotn se superpone con alguno de
los cuadrados de comida. Si es as, quita ese cuadrado de comida de la lista de comidas. De esta
forma, Python no dibujar los cuadrados de comida que el rebotn se halla comido.
En cada iteracin del bucle for, el cuadrado de comida actual de la lista de comidas (plural) se
asigna a la variable comida (singular).
320
http://inventwithpython.com/es
comidas[:]
develve una copia de la lista con todos sus tems (del primero al ltimo).
Bsicamente, comidas[:] crea una nueva lista con una copia de todos los tems en comidas. Esta
es una forma de copiar la lista ms corta que, por ejemplo, lo que hace la funcin
obtenerDuplicadoTablero() en el juego de Ta Te Ti.
No puedes agregar o quitar tems de una lista mientras ests iterando sobre ella. Python puede
perder la cuenta de cul debera ser el prximo valor de la variable comida si el tamao de la lista
comidas est cambiando. Piensa en lo difcil que sera contar el nmero de caramelos en un
frasco mientras alguien est agregando o quitando caramelos.
Pero si iteras sobre una copia de la lista (y la copia no cambia mientras lo haces), agregar o quitar
tems de la lista original no ser un problema.
if verifSuperposicinRects(rebotn['rect'], comida):
comidas.remove(comida)
# Dibuja la comida
for i in range(len(comidas)):
pygame.draw.rect(superficieVentana, VERDE, comidas[i])
El cdigo en las lneas 120 y 121 es similar a la forma en que dibujamos el cuadrado blanco para
el jugador. La lnea 120 pasa por cada cuadrado de comida sobre la superficie de
superficieVentana. Este programa es similar al programa del rebotn en el captulo anterior,
slo que ahora el cuadrado que rebota se "come" a los otros cuadrados si pasa por encima de
ellos.
Estos ltimos programas son interesantes para observar, pero el usuario no puede controlar nada.
En el siguiente programa, aprenderemos a obtener entradas desde el teclado.
321
pygameEntrada.py
1. import pygame, sys, random
2. from pygame.locals import *
3.
4. # configurar pygame
5. pygame.init()
6. relojPrincipal = pygame.time.Clock()
7.
8. # configurar la ventana
9. ANCHOVENTANA = 400
10. ALTURAVENTANA = 400
11. superficieVentana = pygame.display.set_mode((ANCHOVENTANA, ALTURAVENTANA),
0, 32)
12. pygame.display.set_caption('Entrada')
13.
14. # configurar los colores
15. NEGRO = (0, 0, 0)
16. VERDE = (0, 255, 0)
17. BLANCO = (255, 255, 255)
18.
19. # configurar estructura de datos del jugador y la comida
20. contadorDeComida = 0
21. NUEVACOMIDA = 40
22. TAMAOCOMIDA = 20
23. jugador = pygame.Rect(300, 100, 50, 50)
24. comidas = []
25. for i in range(20):
26.
comidas.append(pygame.Rect(random.randint(0, ANCHOVENTANA TAMAOCOMIDA), random.randint(0, ALTURAVENTANA - TAMAOCOMIDA), TAMAOCOMIDA,
TAMAOCOMIDA))
27.
28. # configurar variables de movimiento
29. moverseIzquierda = False
30. moverseDerecha = False
31. moverseArriba = False
32. moverseAbajo = False
33.
34. VELOCIDADMOVIMIENTO = 6
35.
322
http://inventwithpython.com/es
36.
37. # ejecutar el bucle del juego
38. while True:
39.
# comprobar eventos
40.
for evento in pygame.event.get():
41.
if evento.type == QUIT:
42.
pygame.quit()
43.
sys.exit()
44.
if evento.type == KEYDOWN:
45.
# cambiar las variables del teclado
46.
if evento.key == K_LEFT or evento.key == ord('a'):
47.
moverseDerecha = False
48.
moverseIzquierda = True
49.
if evento.key == K_RIGHT or evento.key == ord('d'):
50.
moverseIzquierda = False
51.
moverseDerecha = True
52.
if evento.key == K_UP or evento.key == ord('w'):
53.
moverseAbajo = False
54.
moverseArriba = True
55.
if evento.key == K_DOWN or evento.key == ord('s'):
56.
moverseArriba = False
57.
moverseAbajo = True
58.
if evento.type == KEYUP:
59.
if evento.key == K_ESCAPE:
60.
pygame.quit()
61.
sys.exit()
62.
if evento.key == K_LEFT or evento.key == ord('a'):
63.
moverseIzquierda = False
64.
if evento.key == K_RIGHT or evento.key == ord('d'):
65.
moverseDerecha = False
66.
if evento.key == K_UP or evento.key == ord('w'):
67.
moverseArriba = False
68.
if evento.key == K_DOWN or evento.key == ord('s'):
69.
moverseAbajo = False
70.
if evento.key == ord('x'):
71.
jugador.top = random.randint(0, ALTURAVENTANA jugador.height)
72.
jugador.left = random.randint(0, ANCHOVENTANA jugador.width)
73.
74.
if evento.type == MOUSEBUTTONUP:
75.
comidas.append(pygame.Rect(evento.pos[0], evento.pos[1],
TAMAOCOMIDA, TAMAOCOMIDA))
76.
77.
contadorDeComida += 1
78.
if contadorDeComida >= NUEVACOMIDA:
79.
# agregar nueva comida
323
80.
contadorDeComida = 0
81.
comidas.append(pygame.Rect(random.randint(0, ANCHOVENTANA TAMAOCOMIDA), random.randint(0, ALTURAVENTANA - TAMAOCOMIDA), TAMAOCOMIDA,
TAMAOCOMIDA))
82.
83.
# dibujar el fondo negro sobre la superficie
84.
superficieVentana.fill(NEGRO)
85.
86.
# mover al jugador
87.
if moverseAbajo and jugador.bottom < ALTURAVENTANA:
88.
jugador.top += VELOCIDADMOVIMIENTO
89.
if moverseArriba and jugador.top > 0:
90.
jugador.top -= VELOCIDADMOVIMIENTO
91.
if moverseIzquierda and jugador.left > 0:
92.
jugador.left -= VELOCIDADMOVIMIENTO
93.
if moverseDerecha and jugador.right < ANCHOVENTANA:
94.
jugador.right += VELOCIDADMOVIMIENTO
95.
96.
# dibujar al jugador sobre la superficie
97.
pygame.draw.rect(superficieVentana, BLANCO, jugador)
98.
99.
# comprobar si el jugador ha intersectado alguno de los cuadrados de
comida
100.
for comida in comidas[:]:
101.
if jugador.colliderect(comida):
102.
comidas.remove(comida)
103.
104.
# dibujar la comida
105.
for i in range(len(comidas)):
106.
pygame.draw.rect(superficieVentana, VERDE, comidas[i])
107.
108.
# dibujar la ventana sobre la pantalla
109.
pygame.display.update()
110.
relojPrincipal.tick(40)
324
http://inventwithpython.com/es
Las cuatro variables tiene valores Booleanos para registrar cules de las flechas del teclado estn
siendo pulsadas. Por ejemplo, cuando el usuario pulsa la flecha izquierda en su teclado, se asigna
True a moverseIzquierda. Cuando el usuario suelta la tecla, moverseIzquierda vuelve a ser
False.
Las lneas 34 a 43 son idnticas al cdigo en los programas Pygame anteriores. Estas lneas
gestionan el comienzo del bucle de juego y qu hacer cuando el usuario sale del programa.
Omitiremos la explicacin de este cdigo que ya ha sido cubierto en el captulo anterior.
if evento.type == KEYDOWN:
Pygame tiene un tipo de evento llamado KEYDOWN. Este es uno de los otros eventos que Pygame
puede generar. La Tabla 18-1 muestra una breve lista de los eventons que pueden ser devueltos
por pygame.event.get().
Tipo de Evento
QUIT
325
KEYDOWN
KEYUP
MOUSEMOTION
MOUSEBUTTONDOWN
MOUSEBUTTONUP
326
http://inventwithpython.com/es
Tabla 18-2: Los valores del atributo button y su correspondiente botn del ratn.
Valor de button
Botn del ratn
1
Botn izquierdo
2
Botn central
Botn derecho
Si el tipo de evento es KEYDOWN, el objeto evento tendr un atributo key que indica qu tecla ha
sido pulsada. La lnea 46 compara este atributo con K_LEFT, que es la constante de
pygame.locals que representa la flecha izquierda del teclado. Las lneas 49 a 57 realizan
comprobaciones similares para cada una de las otras flechas del teclado: K_RIGHT, K_UP, K_DOWN.
Cuando una de estas teclas es pulsada, se asigna True a la variable de movimiento
correspondiente. Adems, se asigna False a la variable de movimiento en la direccin opuesta.
327
Por ejemplo, el programa ejecuta las lneas 47 y 48 cuando se pulsa la flecha izquierda. En este
caso, se asigna True a moverseIzquierda y False a moverseDerecha (es posible que
moverseDerecha ya sea False desde antes, pero le asignamos False slo para estar seguros).
En la lnea 46, evento.key puede ser igual a K_LEFT u ord('a'). El valor en evento.key
corresponde al valor ordinal entero de la tecla que fue pulsada en el teclado. (No hay valores
ordinales para las flechas del teclado, por eso es que usamos la variable constante K_LEFT.)
Puedes usar la funcin ord() para obtener el valor ordinal de cualquier caracter y compararlo con
evento.key.
Al ejecutar el cdigo en las lneas 47 y 48 en caso de que la tecla pulsada haya sido K_LEFT u
ord('a'), estamos haciendo que la flecha izquierda y la tecla A hagan la misma cosa. El
conjunto de teclas W, A, S y D es una alternativa a las flechas del teclado para jugadores que
prefieren utilizar la mano izquierda para jugar.
Figura 18-5: Las teclas WASD pueden ser programadas para hacer lo mismo que las flechas del
teclado.
if evento.type == KEYUP:
Cuando el usuario libera la tecla que mantena pulsada, se genera un evento KEYUP.
59.
60.
61.
if evento.key == K_ESCAPE:
pygame.quit()
sys.exit()
Si la tecla que el usuario liber es la tecla ESC, se termina el programa. Recuerda, en Pygame
debes llamar a la funcin pygame.quit() antes de llamar a la funcin sys.exit().
Las lneas 62 a 69 asignan False a una variable de movimiento si la tecla correspondiente a esa
direccin ha sido liberada.
62.
63.
64.
328
http://inventwithpython.com/es
65.
66.
67.
68.
69.
moverseDerecha = False
if evento.key == K_UP or evento.key == ord('w'):
moverseArriba = False
if evento.key == K_DOWN or evento.key == ord('s'):
moverseAbajo = False
Teletransportando al Jugador
70.
71.
jugador.height)
72.
jugador.width)
if evento.key == ord('x'):
jugador.top = random.randint(0, ALTURAVENTANA jugador.left = random.randint(0, ANCHOVENTANA -
Tambin podemos agregar teletransporte al juego. Si el usuario pulsa la tecla "X", las lneas 71 y
72 asignarn a la posicin del cuadrado del jugador un lugar aleatorio de la ventana. Esto dar al
jugador la habilidad de teletransportarse por la ventana pulsando la tecla "X". Si bien no puede
controlar a dnde se teletransportar; es completamente aleatorio.
Las entradas del ratn son manipuladas a travs de eventos, igual que las entradas del teclado. El
evento MOUSEBUTTONUP ocurre cuando el usuario suelta el botn del ratn luego de hacer clic. Se
asigna al atributo pos del objeto Event una tupla de dos enteros correspondientes a las
coordenadas XY de la posicin del ratn en el momento del clic.
En la lnea 75, la coordenada X se almacena en evento.pos[0] y la coordenada Y se almacena
en evento.pos[1]. La lnea 75 crea un nuevo objeto Rect que representa una nueva comida y la
ubica donde ha ocurrido el evento MOUSEBUTTONUP. Al agregar un nuevo objeto Rect a la lista de
comidas se muestra en la pantalla un nuevo cuadrado de comida.
# mover al jugador
if moverseAbajo and jugador.bottom < ALTURAVENTANA:
jugador.top += VELOCIDADMOVIMIENTO
if moverseArriba and jugador.top > 0:
jugador.top -= VELOCIDADMOVIMIENTO
91.
92.
93.
94.
329
El Mtodo colliderect()
99.
comida
100.
101.
102.
relojPrincipal.tick(40)
Resumen
Este captulo present el concepto de deteccin de colisiones, el cual se encuentra en muchos
juegos grficos. Detectar colisiones entre dos rectngulos es fcil: se comprueba si alguna de las
esquinas de cada rectngulo est dentro del otro rectngulo. Esta comprobacin es tan comn que
330
http://inventwithpython.com/es
Pygame incluye su propio mtodo de deteccin de colisiones para objetos pygame.Rect llamado
colliderect().
Los primeros juegos de este libro utilizaban la consola de texto. La salida del programa se
escriba en la pantalla y la entrada era texto que el jugador escriba con el teclado. Los programas
grficos, en cambio, pueden aceptar entradas del teclado y del ratn.
Por otra parte, estos programas pueden responder a los eventos generados cuando el jugador pulsa
o libera teclas individuales. El usuario no necesita escribir una respuesta completa y pulsar INTRO.
Esto permite que el programa responda instantneamente, lo que resulta en juegos mucho ms
interactivos.
331
Chapter 19
SONIDOS E IMGENES
Temas Tratados en Este Captulo:
Archivos de Sonido e Imagen
Dibujando Sprites
La Funcin pygame.image.load()
El Tipo de Datos pygame.mixer.Sound
El Mdulo pygame.mixer.music
En los ltimos dos captulos, hemos aprendido cmo hacer programas GUI que muestran grficos
y pueden aceptar entradas del teclado y del ratn. Tambin hemos aprendido cmo dibujar
diferentes formas. En este captulo aprenderemos cmo mostrar fotos e imgenes (llamadas
sprites) y reproducir sonidos y msica en nuestros juegos.
Sprite es el nombre dado a una imagen individual bidimensional que se usa como parte de los
grficos en la pantalla. La Figura 19-1 muestra algunos ejemplos de sprites.
332
http://inventwithpython.com/es
Figura 19-2: Un ejemplo de una escena completa, con sprites dibujados sobre una imagen de
fondo.
Los sprites se dibujan sobre una imagen de fondo. Nota que puedes invertir el sprite
horizontalmente o verticalmente de modo que las imgenes se den vuelta. Puedes dibujar el
mismo sprite mltiples veces en la misma ventana. Tambin puedes redimensionar los sprites
para que sean ms grandes o ms pequeos que la imagen original. Podemos considerar a la
imagen de fondo como un gran sprite.
El siguiente programa demostrar cmo reproducir sonidos y dibujar sprites usando Pygame.
333
Los formatos de archivos de sonido que Pygame soporta son MID, WAV y MP3. Puedes
descargar efectos de sonido de Internet igual que los archivos de imagen. Deben estar en uno de
estos tres formatos. Si tu computadora tiene un micrfono, tambin puedes grabar sonidos y crear
tus propios archivos WAV para usar en tus juegos.
spritesYsonidos.py
1. import pygame, sys, time, random
2. from pygame.locals import *
3.
4. # configurar pygame
5. pygame.init()
6. relojPrincipal = pygame.time.Clock()
7.
8. # configurar la ventana
9. ANCHOVENTANA = 400
10. ALTOVENTANA = 400
11. superficieVentana = pygame.display.set_mode((ANCHOVENTANA, ALTOVENTANA),
0, 32)
12. pygame.display.set_caption('Sprites y Sonido')
13.
14. # configurar los colores
15. NEGRO = (0, 0, 0)
16.
17. # configurar la estructura de bloque de datos
334
http://inventwithpython.com/es
335
64.
if evento.type == KEYUP:
65.
if evento.key == K_ESCAPE:
66.
pygame.quit()
67.
sys.exit()
68.
if evento.key == K_LEFT or evento.key == ord('a'):
69.
moverseIzquierda = False
70.
if evento.key == K_RIGHT or evento.key == ord('d'):
71.
moverseDerecha = False
72.
if evento.key == K_UP or evento.key == ord('w'):
73.
moverseArriba = False
74.
if evento.key == K_DOWN or evento.key == ord('s'):
75.
moverseAbajo = False
76.
if evento.key == ord('x'):
77.
jugador.top = random.randint(0, ALTOVENTANAjugador.height)
78.
jugador.left = random.randint(0, ANCHOVENTANAjugador.width)
79.
if evento.key == ord('m'):
80.
if msicaSonando:
81.
pygame.mixer.music.stop()
82.
else:
83.
pygame.mixer.music.play(-1, 0.0)
84.
msicaSonando = not msicaSonando
85.
86.
if evento.type == MOUSEBUTTONUP:
87.
comidas.append(pygame.Rect(evento.pos[0] - 10, evento.pos[1] 10, 20, 20))
88.
89.
contadorComida += 1
90.
if contadorComida >= NUEVACOMIDA:
91.
# agregar nueva comida
92.
contadorComida = 0
93.
comidas.append(pygame.Rect(random.randint(0, ANCHOVENTANA - 20),
random.randint(0, ALTOVENTANA - 20), 20, 20))
94.
95.
# pintar el fondo negro sobre la superficie
96.
superficieVentana.fill(NEGRO)
97.
98.
# move the player
99.
if moverseAbajo and jugador.bottom < ALTOVENTANA:
100.
jugador.top += VELOCIDADMOVIMIENTO
101.
if moverseArriba and jugador.top > 0:
102.
jugador.top -= VELOCIDADMOVIMIENTO
103.
if moverseIzquierda and jugador.left > 0:
104.
jugador.left -= VELOCIDADMOVIMIENTO
105.
if moverseDerecha and jugador.right < ANCHOVENTANA:
106.
jugador.right += VELOCIDADMOVIMIENTO
336
http://inventwithpython.com/es
107.
108.
109.
# dibujar el bloque sobre la superficie
110.
superficieVentana.blit(imagenEstiradaJugador, jugador)
111.
112.
# comprobar si el jugador ha intersectado alguno de los cuadrados de
comida
113.
for comida in comidas[:]:
114.
if jugador.colliderect(comida):
115.
comidas.remove(comida)
116.
jugador = pygame.Rect(jugador.left, jugador.top, jugador.width
+ 2, jugador.height + 2)
117.
imagenEstiradaJugador = pygame.transform.scale(imagenJugador,
(jugador.width, jugador.height))
118.
if msicaSonando:
119.
sonidoRecoleccin.play()
120.
121.
# dibujar la comida
122.
for comida in comidas:
123.
superficieVentana.blit(imagenComida, comida)
124.
125.
# dibujar la ventana sobre la pantalla
126.
pygame.display.update()
127.
relojPrincipal.tick(40)
Figura 19-3: Una captura de pantalla modificada del juego Sprites y Sonidos.
337
Primero, configuramos la leyenda de la barra de ttulo a una cadena que describa este programa
en la lnea 12. Pasamos la cadena 'Sprites y Sonido' a la funcin pygame.display.set_caption().
17.
18.
19.
20.
21.
Vamos a usar tres variables diferentes para representar al jugador, a diferencia de los programas
anteriores que slo usaban una.
La variable jugador en la lnea 18 almacenar un objeto rect que registra el tamao y la posicin
del jugador. La variable no contiene la imagen del jugador, slo su tamao y posicin. Al
principio del programa, la esquina superior izquierda del jugador se ubica en (300, 100) y el
jugador tiene una altura y un ancho de 40 pxeles para empezar.
La segunda variable de la lnea 19 que representa al jugador es imagenJugador. la funcin
pygame.image.load() recibe una cadena con el nombre de archivo de la imagen a cargar. El
valor de retorno es un objeto Surface que tiene el grfico del archivo dibujado sobre su superficie.
Guardamos este objeto Surface dentro de imagenJugador.
La tercera variable se explica en la prxima seccin.
La Funcin pygame.transform.scale()
En la lnea 20, utilizamos una nueva funcin en el mdulo pygame.transform. La funcin
pygame.transform.scale() puede reducir o agrandar un sprite. El primer argumento es un
objeto pygame.Surface con la imaten dibujada sobre l. El segundo argumento es una tupla con
los nuevos ancho y altura de la imagen en el primer argumento. La funcin The
pygame.transform.scale() devuelve un objeto pygame.Surface con la imagen dibujada en un
nuevo tamao. Almacenaremos la imagen original en la variable imagenJugador, y la imagen
estirada se guardar en la variable imagenEstiradaJugador.
En la lnea 21, llamamos nuevamente a pygame.image.load() para crear un objeto Surface con
la imagen de una cereza dibujada sobre l. Asegrate de tener los archivos jugador.png y
cereza.png en la misma carpeta que el archivo spritesYsonido.py, pues de otro modo Pygame no
podr encontrarlos y dar un error.
338
http://inventwithpython.com/es
# configurar msica
sonidoRecoleccin = pygame.mixer.Sound('recoleccin.wav')
pygame.mixer.music.load('musicaDeFondo.mid')
pygame.mixer.music.play(-1, 0.0)
msicaSonando = True
A continuacin necesitas cargar los archivos de sonido. Hay dos mdulos para sonido en Pygame.
El mdulo pygame.mixer puede reproducir efectos de sonido breves durante el juego. El mdulo
pygame.mixer.music puede reproducir msica de fondo.
Llamamos a la funcin constructor pygame.mixer.Sound() para crear un objeto
pygame.mixer.Sound (llamado objeto Sound por brevedad). Este objeto tiene un mtodo play()
que reproducir el efecto de sonido al ser llamado.
La lnea 39 llama a pygame.mixer.music.load() para cargar la msica de fondo. La lnea 40
llama a pygame.mixer.music.play() para comenzar a reproducir la msica de fondo. El primer
parmetro indica a Pygame cuntas veces repetir la msica de fondo luego de la primera vez que
se reproduce. Es decir que pasar 5 como argumento har que Pygame reproduzca la msica de
fondo 6 veces. -1 es un valor especial, y pasarlo como el primer parmetro hace que la msica de
fondo se repita siempre.
El segundo parmetro de pygame.mixer.music.play() es el punto en que el archivo de sonido
comienza su reproduccin. Pasar 0.0 hace que la msica comience desde el principio. Pasar 2.5
como segundo parmetro hace que la msica de fondo comience dos segundos y medio despus
del principio.
Finalmente, la variable musicPlaying tendr un valor Booleano que indica al programa si debe
reproducir la msica de fondo y los efectos o no. Es bueno dar al jugador la opcin de poder
ejecutar el programa en modo silencioso.
if evento.key == ord('m'):
if msicaSonando:
pygame.mixer.music.stop()
else:
pygame.mixer.music.play(-1, 0.0)
msicaSonando = not msicaSonando
339
La tecla M activa y desactiva la msica de fondo. Si msicaSonando tiene asignado el valor True,
la msica de fondo est reproducindose y debemos desactivar la msica llamando a
pygame.mixer.music.stop(). Si msicaSonando tiene asignado False, entonces la msica de
fondo no est sonando y debera activarse llamando a pygame.mixer.music.play().
Finalmente, sin importar el estado actual, queremos conmutar el valor de msicaSonando.
Conmutar un Booleano significa asignar el opuesto a su valor. La lnea msicaSonando = not
msicaSonando asigna False a la variable si tiene el valor True, o le asigna True si tiene el valor
False. Piensa en esta conmutacin como lo que ocurre cuando accionas un interruptor de luz:
mover el interruptor cambia al estado opuesto.
Este cdigo es similar al de programas anteriores, pero hay un par de lneas nuevas. Llamamos al
mtodo play() sobre el objeto Sonido almacenado en la variable sonidoRecoleccin. Pero slo
lo hacemos si el valor de msicaSonando es True (lo que significa que el sonido est activado).
Cuando el jugador se come una de las cerezas, su altura y ancho se incrementan en dos pxeles.
En la lnea 116 se asigna a jugador un nuevo objeto Rect que es 2 pxeles ms grande que el viejo
objeto Rect.
340
http://inventwithpython.com/es
Aunque el objeto Rect representa la posicin y el tamao del jugador, la imagen del jugador se
almacena en imagenEstiradaJugador como un objeto Surface. Crearemos una nueva imagen
estirada llamando a pygame.transform.scale(). Asegrate de pasarle a esta funcin el objeto
Surface original en imagenJugador y no imagenEstiradaJugador.
Estirar una imegen suele distorsionarla un poco. Si continuamos estirando la misma imagen una y
otra vez, las distorsiones se acumulan rpidamente. Pero si estiramos la imagen original a un
nuevo tamao, distorsionamos la imagen una sola vez. Es por esto que usamos imagenJugador
como primer argumento de pygame.transform.scale().
# dibujar la comida
for comida in comidas:
superficieVentana.blit(imagenComida, comida)
Resumen
Este captulo ha agregado imgenes y sonido a tus juegos. Las imgenes (llamadas sprites) se ven
mucho mejor que las siluetas de formas simples usadas en los programas anteriores. El juego
presentado en este captulo tambin reproduce msica de fondo y efectos de sonido.
Los sprites pueden ser escalados (es decir, estirados) a tamaos mayores o menores. De este
modo podemos mostrar sprites de cualquier tamao que queramos. Esto ser til para el juego
presentado en el prximo captulo.
Ahora que sabemos cmo crear una ventana, mostrar sprites, dibujar primitivas, recibir entradas
de teclado y ratn, reproducir sonidos e implementar deteccin de colisiones, estamos listos para
crear un juego grfico en Pygame. El prximo captulo combinar todos estos elementos para
crear el ms avanzado de nuestros juegos hasta ahora.
Captulo 20 Evasor
341
Captulo 20
EVASOR
Temas Tratados En Este Captulo:
La bandera pygame.FULLSCREEN
Variables constantes de Pygame para las teclas
El mtodo Rect.move_ip()
La funcin pygame.mouse.set_pos()
Implementando cdigos de trucos
Modificando el juego Evasor
En los ltimos tres captulos hemos repasado el mdulo Pygame y demostrado cmo usar sus
mltiples caractersticas. En este captulo, usaremos ese conocimiento para crear un juego grfico
llamado Evasor.
En el juego Evasor el jugador controla a una pequea persona (a quien llamamos el personaje del
jugador) que debe evadir a un montn de villanos que caen desde el borde superior de la pantalla.
Cuanto ms tiempo consiga el jugador evadir a los villanos mejor puntaje obtendr en el juego.
Slo por diversin, hemos agregado tambin al juego algunos trucos. Si el jugador mantiene
pulsada la tecla x, los villanos comienzan a moverse super lento. Si el jugador mantiene
pulsada la tecla z, los villanos revertirn su trayectoria y se movern hacia arriba en lugar de
hacia abajo.
pygame.Rect
pygame.Surface
http://inventwithpython.com/es
342
pygame.event.Event
evasor.py
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
Captulo 20 Evasor
343
15.
16. def terminar():
17.
pygame.quit()
18.
sys.exit()
19.
20. def esperarTeclaJugador():
21.
while True:
22.
for evento in pygame.event.get():
23.
if evento.type == QUIT:
24.
terminar()
25.
if evento.type == KEYDOWN:
26.
if evento.key == K_ESCAPE: # Sale del juego al presionar
ESCAPE
27.
terminar()
28.
return
29.
30. def jugadorGolpeaVillano(rectanguloJugador, villanos):
31.
for v in villanos:
32.
if rectanguloJugador.colliderect(v['rect']):
33.
return True
34.
return False
35.
36. def dibujarTexto(texto, fuente, superficie, x, y):
37.
objetotexto = fuente.render(texto, 1, COLORVENTANA)
38.
rectangulotexto = objetotexto .get_rect()
39.
rectangulotexto.topleft = (x, y)
40.
superficie.blit(objetotexto, rectangulotexto)
41.
42. # establece un pygame, la ventana y el cursor del ratn
43. pygame.init()
44. relojPrincipal = pygame.time.Clock()
45. superficieVentana = pygame.display.set_mode((ANCHOVENTANA, ALTOVENTANA))
46. pygame.display.set_caption('Evasor')
47. pygame.mouse.set_visible(False)
48.
49. # establece las fuentes
50. fuente = pygame.font.SysFont(None, 48)
51.
52. # establece los sonidos
53. sonidoJuegoTerminado = pygame.mixer.Sound('juegoterminado.wav')
54. pygame.mixer.music.load('musicaDeFondo.mid')
55.
56. # establece las imagenes
57. imagenJugador = pygame.image.load('jugador.png')
58. rectanguloJugador = imagenJugador.get_rect()
59. imagenVillano = pygame.image.load('villano.png')
60.
344
http://inventwithpython.com/es
Captulo 20 Evasor
345
106.
trucoReversa = False
107.
puntaje = 0
108.
if evento.key == ord('x'):
109.
trucoLento = False
110.
puntaje = 0
111.
if evento.key == K_ESCAPE:
112.
terminar()
113.
114.
if evento.key == K_LEFT or evento.key == ord('a'):
115.
moverIzquierda = False
116.
if evento.key == K_RIGHT or evento.key == ord('d'):
117.
moverDerecha = False
118.
if evento.key == K_UP or evento.key == ord('w'):
119.
moverArriba = False
120.
if evento.key == K_DOWN or evento.key == ord('s'):
121.
moverAbajo = False
122.
123.
if evento.type == MOUSEMOTION:
124.
# Si se mueve el ratn, este se mueve al lugar donde est
el cursor.
125.
rectanguloJugador.move_ip(evento.pos[0] rectanguloJugador.centerx, evento.pos[1] - rectanguloJugador.centery)
126.
127.
# Aade villanos en la parte superior de la pantalla, de ser
necesarios.
128.
if not trucoReversa and not trucoLento:
129.
contadorAgregarVillano += 1
130.
if contadorAgregarVillano == TASANUEVOVILLANO:
131.
contadorAgregarVillano = 0
132.
tamaoVillano = random.randint(TAMAOMINVILLANO,
TAMAOMAXVILLANO)
133.
nuevoVillano = {'rect': pygame.Rect(random.randint(0,
ANCHOVENTANA-tamaoVillano), 0 - tamaoVillano, tamaoVillano, tamaoVillano),
134.
'velocidad': random.randint(VELOCIDADMINVILLANO,
VELOCIDADMAXVILLANO),
135.
'superficie':pygame.transform.scale(imagenVillano,
(tamaoVillano, tamaoVillano)),
136.
}
137.
138.
villanos.append(nuevoVillano)
139.
140.
# Mueve el jugador.
141.
if moverIzquierda and rectanguloJugador.left > 0:
142.
rectanguloJugador.move_ip(-1 * TASAMOVIMIENTOJUGADOR, 0)
143.
if moverDerecha and rectanguloJugador.right < ANCHOVENTANA:
144.
rectanguloJugador.move_ip(TASAMOVIMIENTOJUGADOR, 0)
145.
if moverArriba and rectanguloJugador.top > 0:
346
http://inventwithpython.com/es
146.
rectanguloJugador.move_ip(0, -1 * TASAMOVIMIENTOJUGADOR)
147.
if moverAbajo and rectanguloJugador.bottom < ALTOVENTANA:
148.
rectanguloJugador.move_ip(0, TASAMOVIMIENTOJUGADOR)
149.
150.
# Mueve el cursor del ratn hacia el jugador.
151.
pygame.mouse.set_pos(rectanguloJugador.centerx,
rectanguloJugador.centery)
152.
153.
# Mueve los villanos hacia abajo.
154.
for v in villanos:
155.
if not trucoReversa and not trucoLento:
156.
v['rect'].move_ip(0, v['velocidad'])
157.
elif trucoReversa:
158.
v['rect'].move_ip(0, -5)
159.
elif trucoLento:
160.
v['rect'].move_ip(0, 1)
161.
162.
# Elimina los villanos que han caido por debajo.
163.
for v in villanos[:]:
164.
if v['rect'].top > ALTOVENTANA:
165.
villanos.remove(v)
166.
167.
# Dibuja el mundo del juego en la ventana.
168.
superficieVentana.fill(COLORFONDO)
169.
170.
# Dibuja el puntaje y el puntaje mximo
171.
dibujarTexto('Puntaje: %s' % (puntaje), fuente, superficieVentana,
10, 0)
172.
dibujarTexto('Puntaje Mximo: %s' % (puntajeMax), fuente,
superficieVentana, 10, 40)
173.
174.
# Dibuja el rectngulo del jugador
175.
superficieVentana.blit(imagenJugador, rectanguloJugador)
176.
177.
# Dibuja cada villano
178.
for v in villanos:
179.
superficieVentana.blit(v['superficie'], v['rect'])
180.
181.
pygame.display.update()
182.
183.
# Verifica si algn villano impact en el jugador.
184.
if jugadorGolpeaVillano(rectanguloJugador, villanos):
185.
if puntaje > puntajeMax:
186.
puntajeMax = puntaje # Establece nuevo puntaje mximo
187.
break
188.
189.
relojPrincipal.tick(FPS)
Captulo 20 Evasor
347
190.
191.
# Detiene el juego y muestra "Juego Terminado"
192.
pygame.mixer.music.stop()
193.
sonidoJuegoTerminado.play()
194.
195.
dibujarTexto('Juego Terminado', fuente, superficieVentana,
(ANCHOVENTANA / 3), (ALTOVENTANA / 3))
196.
dibujarTexto('Presione una tecla jugar de nuevo.', fuente,
superficieVentana, (ANCHOVENTANA / 3) - 80, (ALTOVENTANA / 3) + 50)
197.
pygame.display.update()
198.
esperarTeclaJugador()
199.
200.
sonidoJuegoTerminado.stop()
El juego Evasor importa los mismos mdulos que nuestros programas anteriores de Pygame:
pygame, random, sys y pygame.locals. El mdulo pygame.locals contiene unas cuantas
variables constantes que Pygame usa, tales como tipos de eventos (QUIT, KEYDOWN, etc.) y botones
348
http://inventwithpython.com/es
del teclado (K_ESCAPE, K_LEFT, etc.). Usando la sintxis from pygame.locals import *,
podemos escribir QUIT en el cdigo fuente en lugar de pygame.locals.QUIT.
ANCHOVENTANA = 600
ALTOVENTANA = 600
COLORVENTANA = (255, 255, 255)
COLORFONDO = (0, 0, 0)
Las variables constantes de las lneas 4 a 14 son mucho ms descriptivas que lo que sera
simplemente escribir sus valores. Por ejemplo, la lnea superficieVentana.fill(COLORFONDO)
es mucho ms entendible que superficieVentana.fill((0, 0, 0)).
Puedes modificar fcilmente el juego cambiando las variables constantes. Al cambiar
ANCHOVENTANA en la lnea 4, automticamente modificas el cdigo en cualquier lugar donde se
use ANCHOVENTANA. Si hubieras usado en su lugar el valor 600, tendras que cambiar cada
ocurrencia del valor 600 en el cdigo. Es ms fcil cambiar una vez valor en la constante.
8. FPS = 40
TAMAOMINVILLANO = 10
TAMAOMAXVILLANO = 40
VELOCIDADMINVILLANO = 1
VELOCIDADMAXVILLANO = 8
TASANUEVOVILLANO = 6
Las lneas 9 a 13 establecen ms variables constantes que describen a los villanos que caen. El
ancho y alto de los villanos estar comprendido entre TAMAOMINVILLANO y TAMAOMAXVILLANO.
La velocidad a la que los villanos bajan por la pantalla estar entre VELOCIDADMINVILLANO y
VELOCIDADMAXVILLANO pxeles por iteracin del bucle del juego. Y un nuevo villano ser
Captulo 20 Evasor
349
Definiendo Funciones
Hay varias funciones que crears para el juego:
16. def terminar():
17.
pygame.quit()
18.
sys.exit()
Pygame requiere que llames a pygame.quit() y sys.exit(). Coloca a ambos en una funcin
llamada terminar(). Ahora slo necesitas llamar a terminar(), en lugar de tener que llamar a
las dos funciones pygame.quit() y sys.exit().
20. def esperarTeclaJugador():
21.
while True:
22.
for evento in pygame.event.get():
En ocasiones desears poner el juego en pausa hasta que el jugador pulse una tecla. Crea una
nueva funcin llamada esperarTeclaJugador(). Dentro de esta funcin, hay un bucle infinito
del que slo se sale al recibir un evento KEYDOWN o QUIT. Al comienzo del bucle,
pygame.event.get() regresa una lista de objetos Event a revisar.
23.
24.
if evento.type == QUIT:
terminar()
Si el jugador cierra la ventana mientras el programa espera que el jugador pulse una tecla,
Pygame generar un evento QUIT. En ese caso, la lnea 24 llama a la funcin terminar().
25.
26.
ESCAPE
27.
28.
if evento.type == KEYDOWN:
if evento.key == K_ESCAPE: # Sale del juego al presionar
terminar()
return
350
http://inventwithpython.com/es
Si recibes un evento KEYDOWN, deberas primero comprobar si la tecla pulsada ha sido ESC. Si el
jugador pulsa la tecla ESC, el programa debera terminar. Si no ha sido ese el caso, entonces la
ejecucin omitir el bloque if de la lnea 27 y proseguir directamente a la sentencia return, la
cual sale de la funcin esperarTeclaJugador().
Si no se genera ningn evento QUIT o KEYDOWN, el cdigo contina recorriendo el bucle. Como el
bucle no hace nada, esto dar la impresin de que el juego se ha congelado hasta que el jugador
pulse una tecla.
30. def jugadorGolpeaVillano(rectanguloJugador, villanos):
31.
for v in villanos:
32.
if rectanguloJugador.colliderect(v['rect']):
33.
return True
34.
return False
Dibujar texto en la ventana involucra varios pasos. Primero, la llamada al mtodo render() en la
lnea 37 crea un objeto Surface sobre el cual se dibuja el texto con una fuente especfica.
A continuacin necesitas saber el tamao y la ubicacin del objeto Surface. Puedes obtener un
objeto Rect con esta informacin a partir del mtodo get_rect() de la clase Surface.
Captulo 20 Evasor
351
El objeto Rect devuelto en la lnea 38 por la funcin get_rect() tiene una copia de la
informacin de ancho y alto del objeto Surface. La lnea 39 cambia la ubicacin del objeto Rect
estableciendo un nuevo valor de tupla para su atributo topleft.
Finalmente, la lnea 40 dibuja el objeto Surface del texto renderizado sobre el objeto Surface que
recibi como argumento la funcin dibujarTexto(). Mostrar texto en Pygame requiere ms
pasos que simplemente llamar a la funcin print(). Pero si encapsulas este cdigo dentro de una
sola funcin llamada dibujarTexto(), entonces slo necesitas llamar a esta funcin para mostrar
texto en la pantalla.
La lnea 45 crea un nuevo objeto Surface el cual es utilizado por la ventana mostrada en la
pantalla. Puedes especificar el ancho y la altura de este objeto Surface (y de la ventana) pasando
como argumento una tupla con las variables constantes ANCHOVENTANA y ALTOVENTANA. Observa
que pygame.display.set_mode() recibe slo un argumento: una tupla. El argumento de
pygame.display.set_mode() no consiste en dos enteros sino una tupla de dos enteros.
46. pygame.display.set_caption('Evasor')
La lnea 46 establece la cadena 'Evasor' como ttulo de la ventana. Este ttulo aparecer en la
barra de ttulo en la parte superior de la ventana.
47. pygame.mouse.set_visible(False)
En Evasor, el cursor del ratn no debera ser visible. La razn de esto es que quieres usar el ratn
para mover el personaje del jugador por la pantalla, pero el cursor del ratn interferira con la
352
http://inventwithpython.com/es
Captulo 20 Evasor
353
A continuacin cargaremos los archivos de imagen a utilizar para el personaje del jugador y los
villanos en la pantalla. La imagen para el personaje se encuentra en jugador.png y la imagen para
los villanos est en el archivo villano.png. Todos los villanos son iguales, por lo que slo
necesitars un archivo de imagen para ellos. Puedes descargar estas imagenes del sitio web de
este libro en http://invpy.com/es.
http://inventwithpython.com/es
354
reduce la extensin del programa y hace ms fcil encontrar bugs ya que hay menos cdigo que
revisar.
La funcin esperarTeclaJugador() pondr al juego en pausa ejecutando sin parar un bucle
hasta que se genere un evento KEYDOWN. Entonces la ejecucin sale del bucle y el programa
contina ejecutndose.
El valor en la variable puntajeMax comienza siendo 0 cuando el programa se ejecuta por primera
vez. Cada vez que el jugador pierde y su puntaje es mayor que el puntaje mximo actual, el
puntaje mximo es reemplazado por este puntaje mayor.
El bucle infinito que comienza en la lnea 69 no es tcnicamente el bucle del juego. El bucle del
juego gestiona los eventos y dibujar la ventana mientras el juego est ejecutndose. En cambio,
este bucle while sumar una iteracin cada vez que el jugador comience un nuevo juego. Cuando
el jugador pierda y el juego se reinicie, la ejecucin del programa volver a la lnea 69.
70.
71.
72.
Al comienzo, quieres que villanos sea una lista vaca. La variable villanos es una list de objetos
diccionario con las siguientes claves:
'rect'
La ubicacin inicial del jugador es en el centro de la pantalla y 50 pxeles arriba del borde
inferior. El primer elemento en la tupla de la lnea 73 es la coordenada X de su borde izquierdo.
El segundo elemento es la coordenada Y de su borde superior.
Captulo 20 Evasor
74.
75.
76.
355
pygame.mixer.music.play(-1, 0.0)
La lnea 79 es el inicio del principal bucle del juego. La lnea 80 incrementa el puntaje del
jugador en cada iteracin del bucle del juego. Cuanto ms tiempo permanezca el jugador sin
356
http://inventwithpython.com/es
perder, mayor ser su puntaje. La ejecucin slo saldr del bucle cuando el jugador pierda o salga
del programa.
Gestin de Eventos
Hay cuatro tipos diferentes de eventos que el programa gestionar: QUIT, KEYDOWN, KEYUP y
MOUSEMOTION.
82.
83.
84.
if evento.type == KEYDOWN:
if evento.key == ord('z'):
trucoReversa = True
if evento.key == ord('x'):
trucoLento = True
Si el tipo de evento es KEYDOWN, el jugador ha pulsado una tecla. El objeto Event para eventos del
teclado tiene un atributo key que corresponde al valor ordinal entero de la tecla pulsada. La
funcin ord() devuelve el valor ordinal de la letra pasada como argumento.
Por ejemplo, la lnea 87 comprueba si el evento corresponde a la tecla z mediante event.key
== ord('z'). Si esta condicin es True, se asigna True a trucoReversa para indicar que este
truco ha sido activado. La lnea 89 comprueba si la tecla x ha sido pulsada para activar el truco
lento.
Por ejemplo, la lnea 87 comprueba si el evento corresponde a la tecla z mediante evento.key
== ord('z'). Si esta condicin es True, se asigna True a trucoReversa para indicar que este
truco ha sido activado. La lnea 89 comprueba si la tecla x ha sido pulsada para activar el truco
lento.
Captulo 20 Evasor
357
Los eventos de teclado de Pygame siempre usan los valores ordinales de teclas minsculas, no
maysculas. Siempre se usa evento.key == ord('z') en lugar de evento.key == ord('Z').
De otra forma, el programa no registrar que la tecla ha sido pulsada.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
Las lneas 91 a 102 comprueban si el evento ha sido generado por el jugador presionando una de
las flechas del teclado o las teclas WASD. No hay valores ordinales para cada botn del teclado,
como las flechas de direccin o la tecla ESC. El mdulo pygame.locals provee variables
constantes para usar en lugar de ordinales.
La lnea 91 comprueba si el jugador ha pulsado la flecha izquierda con el evento evento.key ==
K_LEFT. Observa que pulsar una flecha del teclado no slo asigna True a una variable de
movimiento, sino que adems asigna False a la variable de movimiento en la direccin opuesta.
Por ejemplo, si se pulsa la flecha izquierda, el cdigo en la lnea 93 asigna True a
moverIzquierda, pero tambin asigna False a moverDerecha. Esto previene que el programa se
confunda y piense que el personaje del jugador debe moverse en dos direcciones opuestas a la
vez.
La Tabla 20-1 lista variables constantes de uso comn para el atributo key de los objetos Event
relacionados al teclado.
Tabla 20-1: Variables Constantes para teclas comunes
Variable Constante Pygame
Tecla
Variable Constante Pygame Tecla
K_LEFT
K_HOME
Flecha izquierda
Inicio
K_RIGHT
Flecha derecha
K_END
Fin
K_UP
Flecha arriba
K_PAGEUP
Re pg
K_DOWN
Flecha abajo
K_PAGEDOWN
Av pg
358
http://inventwithpython.com/es
K_ESCAPE
Esc
K_F1
F1
K_BACKSPACE
Retroceso
K_F2
F2
K_TAB
Tab
K_F3
F3
K_RETURN
Intro
K_F4
F4
K_SPACE
Barra espaciadora
K_F5
F5
K_DELETE
Supr
K_F6
F6
K_LSHIFT
Shift izquierda
K_F7
F7
K_RSHIFT
Shift derecha
K_F8
F8
K_LCTRL
Ctrl izquierda
K_F9
F9
K_RCTRL
Ctrl derecha
K_F10
F10
K_LALT
Alt izquierda
K_F11
F11
K_RALT
Alt derecha
K_F12
F12
104.
105.
106.
107.
108.
109.
110.
if evento.type == KEYUP:
if evento.key == ord('z'):
trucoReversa = False
puntaje = 0
if evento.key == ord('x'):
trucoLento = False
puntaje = 0
El evento KEYUP se crea cuando el jugador libera una tecla que estaba pulsando. Los objetos
Event de tipo KEYUP tambin tienen un atributo key, igual que los eventos KEYDOWN.
La lnea 105 comprueba si el jugador ha liberado la tecla z, lo que desactivar el truco reversa.
En ese caso, la lnea 106 asigna False a trucoReversa y la lnea 107 reinicia el puntaje a 0.
Reiniciamos el puntaje para desalentar al jugador de usar los trucos.
Las lneas 108 a 110 hacen lo mismo para la tecla x y el truco lento. Cuando la tecla x es
liberada, se asigna False a trucoLento y el puntaje del jugador se reinicia a 0.
Captulo 20 Evasor
111.
112.
359
if evento.key == K_ESCAPE:
terminar()
En cualquier momento del juego, el jugador puede pulsar la tecla ESC para salir del juego. La
lnea 111 comprueba si la tecla liberada ha sido ESC evaluando evento.key == K_ESCAPE. En
ese caso, la lnea 112 llama a la funcin terminar() para salir del programa.
114.
115.
116.
117.
118.
119.
120.
121.
Las lneas 114 a 121 comprueban si el jugador ha liberado una flecha o alguna de las teclas
WASD. En ese caso, el cdigo asigna False a la variable de movimiento correspondiente.
Por ejemplo, si el jugador hubiese estado pulsando la flecha izquierda, la variable
moverIzquierda habra recibido el valor True en la lnea 93. Al soltar la tecla, la condicin de la
lnea 114 habra sido evaluada a True, y se habra asignado False a la variable
moverseIzquierda.
El Mtodo move_ip()
123.
if evento.type == MOUSEMOTION:
124.
# Si se mueve el ratn, este se mueve al lugar donde est
el cursor.
125.
rectanguloJugador.move_ip(evento.pos[0] rectanguloJugador.centerx, evento.pos[1] - rectanguloJugador.centery)
Ahora que hemos manipulado los eventos del teclado, vamos a manipular los eventos del ratn
que se hayan generado. El juego Evasor no hace nada si el jugador ha pulsado un botn del ratn,
pero responde al mover el ratn. Esto da al jugador dos formas de controlar al personaje del
jugador en el juego: el teclado o el ratn.
El evento MOUSEMOTION (movimiento del ratn) se genera cuando el ratn se mueve. Los objetos
Event cuyo atributo type es MOUSEMOTION tambin tienen un atributo llamado pos para la posicin
del evento. Este atributo pos almacena una tupla de coordenadas X e Y que indican a qu parte de
la ventana se ha movido el cursor del ratn. Si el tipo del evento es MOUSEMOTION, el personaje del
jugador se mueve a la posicin del cursor del ratn.
360
http://inventwithpython.com/es
El mtodo move_ip() para objetos Rect modificar horizontal o verticalmente la posicin del
objeto Rect en un nmero de pxeles. Por ejemplo, rectanguloJugador.move_ip(10, 20)
desplaza al objeto Rect 10 pxeles a la derecha y 20 pxeles hacia abajo. Para mover al objeto
Rect hacia la izquierda o hacia arriba, debes pasar valores negativos. Por ejemplo,
rectanguloJugador.move_ip(-5, -15) mueve al objeto Rect 5 pxeles hacia la izquierda y 15
pxeles hacia arriba.
El "ip" al final del mtodo move_ip() es la abreviatura de "in place" (que en espaol significa "en
el lugar"). Esto quiere decir que el mtodo modifica al propio objeto Rect, y no devuelve un
nuevo objeto Rect con los cambios. Tambin existe un mtodo move() que no modifica al objeto
Rect sino que crea y devuelve un nuevo objeto Rect en la nueva ubicacin.
Captulo 20 Evasor
361
La lnea 132 genera un tamao para el villano en pxeles. El tamao ser un entero aleatorio entre
TAMAOMINVILLANO y TAMAOMAXVILLANO, que son constantes que han recibido los valores 10 y
40 en las lneas 9 y 10.
La lnea 133 es donde se crea una nueva estructura de datos villano. Recuerda, la estructura de
datos para los villanos es simplemente un diccionario con claves 'rect', 'velocidad' y
'superficie'. La clave 'rect' contiene una referencia a un objeto Rect que almacena la
ubicacin y el tamao del villano. La llamada a la funcin constructor pygame.Rect() tiene
cuatro parmetros: la coordenada X del borde superior del rea, la coordenada Y del borde
izquierdo del rea, el ancho en pxeles y la altura en pxeles.
El villano debe aparecer en una posicin aleatoria sobre el borde superior de la pantalla, de modo
que pasamos pygame.Rect(random.randint(0, ANCHOVENTANA-tamaoVillano) como la
coordenada X del borde izquierdo. La razn de que pasemos ANCHOVENTANA-tamaoVillano en
lugar de ANCHOVENTANA es que este valor es para el borde izquierdo del villano. Si el borde
izquierdo del villano queda demasiado hacia la derecha, parte del villano quedar fuera de la
pantalla y no ser visible.
El borde inferior del villano debera estar justo por sobre el borde superior de la ventana. La
coordenada Y del borde superior de la ventana es 0. Para colocar all el borde inferior del villano,
asignamos 0 - tamaoVillano al borde superior del mismo
El ancho y la altura del villano deberan ser iguales (la imagen es un cuadrado), de modo que
pasamos tamaoVillano como tercer y cuarto argumentos.
La velocidad a la cual el villano se mueve hacia abajo en la pantalla corresponde al valor en la
clave 'velocidad'. Le asignaremos un entero aleatorio comprendido entre
VELOCIDADMINVILLANO y VELOCIDADMAXVILLANO.
138.
villanos.append(nuevoVillano)
La lnea 138 agrega la recientemente creada estructura de datos villano a la lista de villanos. El
programa usar esta lista para comprobar si el jugador ha colisionado con alguno de ellos, y para
saber en qu lugar de la pantalla dibujar los villanos.
# Mueve el jugador.
if moverIzquierda and rectanguloJugador.left > 0:
rectanguloJugador.move_ip(-1 * TASAMOVIMIENTOJUGADOR, 0)
362
http://inventwithpython.com/es
Las lneas 143 a 148 hacen lo mismo para las otras tres direcciones: derecha, arriba y abajo. Cada
uno de las tres sentencias if en las lneas 143 a 148 comprueba que su variable de movimiento
contenga el valor True y que el borde del objeto Rect del jugador est dentro de la ventana.
Entonces llama a move_ip() para desplazar al objeto Rect.
La Funcin pygame.mouse.set_pos()
150.
# Mueve el cursor del ratn hacia el jugador.
151.
pygame.mouse.set_pos(rectanguloJugador.centerx,
rectanguloJugador.centery)
La lnea 151 mueve el cursor a la misma posicin que el personaje del jugador. La funcin
pygame.mouse.set_pos() mueve el cursor del ratn a las coordenadas X e Y que le pases. Esto
es para que el cursor del ratn y el personaje del jugador estn siempre en el mismo lugar.
Especficamente, el cursor estar justo en el medio del objeto Rect del personaje ya que recibe
como coordenadas los atributos centerx y centery de rectanguloJugador. El cursor del ratn
Captulo 20 Evasor
363
sigue existiendo y puede ser desplazado, a pesar de que sea invisible a causa de la llamada a
pygame.mouse.set_visible(False) en la lnea 47.
153.
154.
Ahora recorre cada estructura villano en la lista de villanos para desplazarlos ligeramente hacia
abajo.
155.
156.
Si ninguno de los trucos ha sido activado, cada villano se desplaza hacia abajo en un nmero de
pxeles igual a su velocidad, la cual se almacena en la clave 'velocidad'.
elif trucoReversa:
v['rect'].move_ip(0, -5)
Si se activa el truco reversa, el villano se mover cinco pxeles hacia arriba. Para lograr este
desplazamiento del objeto Rect, pasamos -5 como segundo argumento a la funcin move_ip().
159.
160.
elif trucoLento:
v['rect'].move_ip(0, 1)
Si el truco lento ha sido activado, los villanos seguirn movindose hacia abajo pero su velocidad
se reducir a un pxel por iteracin del bucle del juego. La velocidad normal del villano
(almacenada en la clave 'velocidad' de la estructura de datos del villano) es ignorada mientras
el truco lento est activado.
Cada villano que caiga por debajo del borde inferior de la ventana debe ser quitado de la lista de
villanos. Recuerda que mientras iteramos sobre una lista no debemos modificar su contenido
agregando o quitando elementos. Entonces en lugar de iterar sobre la lista de villanos con el bucle
for, iteramos sobre una copia de la misma. Esta copia se crea usando una rebanada sin
argumentos [:].
364
http://inventwithpython.com/es
El bucle for de la lnea 163 usa una variable v para el elemento actual en la iteracin sobre
villanos[:].
164.
165.
Dibujando la Ventana
Despus de haber actualizado todas las estructuras de datos, debemos dibujar el universo del
juego usando las funciones grficas de Pygame. Dado que el bucle del juego se ejecuta varias
veces por segundo, simplemente dibujar a los villanos y al jugador en nuevas posiciones hace que
su movimiento se vea suave y natural.
167.
168.
Primero, antes de dibujar cualquier otra cosa, la lnea 168 pinta de negro toda la pantalla para
borrar todo lo anterior.
Recuerda que el objeto Surface en superficieVentana es especial porque es el objeto Surface
devuelto por pygame.display.set_mode(). Cualquier cosa que dbujemos sobre este objeto
Surface aparecer en la pantalla al llamar a pygame.display.update().
Captulo 20 Evasor
365
170.
# Dibuja el puntaje y el puntaje mximo
171.
dibujarTexto('Puntaje: %s' % (puntaje), fuente, superficieVentana,
10, 0)
172.
dibujarTexto('Puntaje Mximo: %s' % (puntajeMax), fuente,
superficieVentana, 10, 40)
Las lneas 171 y 172 muestran el texto con el puntaje y el puntaje mximo en la esquina superior
izquierda de la ventana. La expresin 'Puntaje: %s' % (puntaje) usa interpolacin de cadenas
para insertar el valor de la variable puntaje en la cadena.
Pasamos como argumentos esta cadena, el objeto Font guardado en la variable fuente, el objeto
Surface sobre el cual dibujar el texto y las coordenadas X e Y de donde deseamos colocar el
texto. La funcin dibujarTexto() gestionar la llamada a los mtodos render() y blit().
Hacemos lo mismo para el puntaje mximo. Slo pasamos 40 como coordenada Y en lugar de 0
de modo que el puntaje mximo aparezca debajo del puntaje actual.
El bucle for de la lnea 178 dibuja cada villano en el objeto superficieVentana. Cada elemento
en la lista de villanos es un diccionario. Las claves 'superficie' y 'rect' del diccionario
contienen al objeto Surface con la imagen del villano y al objeto Rect con informacin sobre su
tamao y ubicacin, respectivamente.
181.
pygame.display.update()
366
http://inventwithpython.com/es
Ahora que todo se ha dibujado sobre superficieVentana, dibujamos esta superficie sobre la
ventana llamando a pygame.display.update().
Deteccin de Colisiones
183.
184.
185.
186.
187.
relojPrincipal.tick(FPS)
Para evitar que la computadora recorra el bucle del juego a su mxima velocidad (lo cual sera
demasiado rpido incluso para las habilidades del mejor jugador), llamamos a
relojPrincipal.tick() para pausar el juego por un instante. La pausa ser suficiente para
asegurar que se realizarn alrededor de 40 (el valor almacenado en la variable FPS) iteraciones
sobre el bucle del juego.
Cuando el jugador pierde, el juego deja de reproducir la msica de fondo y reproduce el efecto de
sonido de Juego Terminado. La lnea 192 llama a la funcin stop() en el mdulo
pygame.mixer.music para detener la msica de fondo. La lnea 193 llama al mtodo play() del
objeto Sound guardado en sonidoJuegoTerminado.
195.
dibujarTexto('Juego Terminado', fuente, superficieVentana,
(ANCHOVENTANA / 3), (ALTOVENTANA / 3))
196.
dibujarTexto('Presione una tecla jugar de nuevo.', fuente,
superficieVentana, (ANCHOVENTANA / 3) - 80, (ALTOVENTANA / 3) + 50)
Captulo 20 Evasor
197.
198.
367
pygame.display.update()
esperarTeclaJugador()
Las lneas 195 y 196 llaman a la funcin dibujarTexto() para dibujar el texto Juego
Terminado sobre el objeto superficieVentana. La lnea 197 llama a
pygame.display.update() para dibujar este objeto Surface sobre la pantalla. Despus de
mostrar el texto, el juego se detiene mediante la funcin esperarTeclaJugador() hasta que el
jugador presione una tecla.
200.
sonidoJuegoTerminado.stop()
Luego de que el jugador pulse una tecla, la ejecucin del programa regresar de la llamada a
esperarTeclaJugador() en la lnea 198. Dependiendo de cunto demore el jugador en pulsar
una tecla, el efecto de sonido de Juego Terminado habr terminado de reproducirse o no. En el
segundo caso, para detener el efecto de sonido antes de comenzar un nuevo juego, la lnea 200
llama a sonidoJuegoTerminado.stop().
http://inventwithpython.com/es
368
Aunque la base del juego sigue siendo la misma, puedes modificar cualquiera de las variables
constantes para cambiar drsticamente el comportamiento del juego. Prueba con tus propios
nuevos valores para las variables constantes hasta que encuentres el conjunto de parmetros que
ms te guste.
Resumen
A diferencia de nuestros juegos anteriores basados en texto, Evasor realmente se ve como los
tipos de juego de computadora modernos que usualmente jugamos. Tiene grficos y msica y usa
el ratn. Aunque Pygame provee funciones y tipos de datos como bloques constructivos, eres t
el programador quien los combina para crear juegos divertidos e interactivos.
Y todo esto es posible gracias a que sabes cmo dar instrucciones paso a paso, lnea por lnea, a la
computadora para que lo haga. Puedes hablar el lenguaje de la computadora, y pedirle que haga
por t enormes cantidades de dibujos y procesamiento de nmeros. Esta es una habilidad muy til,
y espero que te motive a continuar aprendiendo ms acerca de programacin en Python. (Y
todava hay mucho por aprender!)
Aqu hay una lista de sitios web que pueden ensearte ms sobre programacin en Python: