Cap. 3 - Dibujando con Tortugas

Hay muchos módulos en Python que nos permiten hacer cosas muy variadas, desde enviar emails a públicar páginas webs. El que utilizaremos en este capítulo permite crear tortugas con las que podremos dibujar formas y patrones.

Dibujar con tortugas es entretenido, pero nuestro objetivo en este capítulo es ir aprendiendo a pensar computacionalmente, como haría un programador. Muchos temas que sólo mencionaremos en este capítulo serán cubiertos con detalle en capítulos siguientes.

3.1) Nuestro primer programa con la tortuga

Escribamos un primer programa, con una tortuga a la que llamaremos "alex" (L3_tortuga)

import turtle             # Allows us to use turtles
wn = turtle.Screen()      # Creates a playground for turtles
alex = turtle.Turtle()    # Create a turtle, assign to alex

alex.forward(50)          # Tell alex to move forward by 50 units
alex.left(90)             # Tell alex to turn by 90 degrees
alex.forward(30)          # Complete the second side of a rectangle

wn.mainloop()             # Wait for user to close window

El programa muestra una ventana en la que ha dibujado medio rectángulo (lado inferior y lado derecho)

Un objeto puede tener varios métodos (cosas que puede hacer) y también varios atributos (o propiedades). Por ejemplo, cada turtle tiene un atributo color

Existen varios métodos que nos permiten modificar el estado de la tortuga y la ventana. Veremos sólo algunos.

El siguiente programa es el mismo que teníamos antes con algunas líneas nuevas intercaladas: (L3_tortuga (2))

import turtle
wn = turtle.Screen()
wn.bgcolor("lightgreen")      # Set the window background color
wn.title("Hello, Tess!")      # Set the window title

tess = turtle.Turtle()
tess.color("blue")            # Tell tess to change her color
tess.pensize(3)               # Tell tess to set her pen width

tess.forward(50)
tess.left(120)
tess.forward(50)

wn.mainloop()

Lo que da una nueva ventana, de fondo verde, con la tortuga de tinta azul, y el dibujo de un ángulo en vez de un semirrectángulo.

Tarea: extender este programa:

  1. Modificar el programa para que, antes de crear la ventana, pida al usuario que ingrese el color de fondo deseado
    • debería guardar la respuesta del usuario en una variable, y modificar el color de la ventana según lo pedido por el usuario
    • una lista de color permitidos se puede buscar en: http://www.tcl.tk/man/tcl8.4/TkCmd/colors.htm
  2. Hacer cambios similares para permitir al usuario elegir el color de la tortuga
  3. Hacer lo mismo para el ancho del lápiz de la tortuga
    • pista: el diálogo retorna un string, pero la tortuga espera un entero, por lo cual habrá que hacer una conversión de string a int

3.2) Instancias - un rebaño de tortugas

Así como podemos tener muchos enteros en un programa, podemos tener muchas tortugas. Cada una se llama instancia y cada instancia tiene sus propios atributos y métodos.

Ejemplo: (L3_tortuga (3))

import turtle
wn = turtle.Screen()         # Set up the window and its attributes
wn.bgcolor("lightgreen")
wn.title("Tess & Alex")

tess = turtle.Turtle()       # Create tess and set some attributes
tess.color("hotpink")
tess.pensize(5)

alex = turtle.Turtle()       # Create alex

tess.forward(80)             # Make tess draw equilateral triangle
tess.left(120)
tess.forward(80)
tess.left(120)
tess.forward(80)
tess.left(120)               # Complete the triangle

tess.right(180)              # Turn tess around
tess.forward(80)             # Move her away from the origin

alex.forward(50)             # Make alex draw a square
alex.left(90)
alex.forward(50)
alex.left(90)
alex.forward(50)
alex.left(90)
alex.forward(50)
alex.left(90)

wn.mainloop()

Al final alex completa un rectángulo negro y tess un triángulo rosa.

Aquí hay algunas observaciones del tipo cómo pensar como un programador:

3.3) El loop FOR

Para dibujar un cuadrado debimos repetir 4 veces la misma instrucción. De haber sido un hexágono u octógono habría que haber hecho más repeticiones.

Para evitar esta clase de repeticiones es bueno utilizar un bucle como for, que permite repetir el mismo código con ligeras modificaciones.

Por ejemplo, podemos usar un bucle for para enviar un email a cada uno de los amigos (como aun no sabemos enviar emails, lo haremos sólo imprimiendo un mensaje para cada uno)

	for f in ["Joe","Zoe","Brad","Angelina","Zuki","Thandi","Paris"]:
		invite = "Hi " + f + ".  Please come to my party on Saturday!"
		print(invite)
	# more code can follow here ...

El output se verá así:

	Hi Joe.  Please come to my party on Saturday!
	Hi Zoe.  Please come to my party on Saturday!
	Hi Brad.  Please come to my party on Saturday!
	Hi Angelina.  Please come to my party on Saturday!
	Hi Zuki.  Please come to my party on Saturday!
	Hi Thandi.  Please come to my party on Saturday!
	Hi Paris.  Please come to my party on Saturday!

Algunas observaciones:

3.4) Flujo de ejecución del bucle FOR

A medida que avanza un programa, el intérprete lleva la cuenta de cuál es la siguiente sentencia a ser ejecutada. Esto se llama control de flujo o flujo de ejecución del programa.

Hasta ahora el flujo de ejecución había sido vertical ("de arriba abajo"), pero con el for loop esto cambia.

Es fácil de visualizarlo con un diagrama de flujo (flow chart).

3.5) El bucle simplifica nuestro programa con la tortuga

Para dibujar un cuadrado podemos reducir los 4 pares de líneas que ejecutaba alex por sólo un par, así: (L3_tortuga (for))

	for i in [0,1,2,3]:
	    alex.forward(50)
	    alex.left(90)

Algunas observaciones:

			for i in range(4):
			    # Executes the body with i = 0, then 1, then 2, then 3
			for x in range(10):
			    # Sets x to each of ... [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Algunas observaciones:

En suma, para repetir una acción 4 veces, un buen programador Python haría algo así: (L3_tortuga (for))

		for i in range(4):
		    alex.forward(50)
		    alex.left(90)

A estas alturas deberías poder hacer que tess dibuje su triángulo equilátero también mediante un for loop. Sería algo así: (L3_tortuga (for))

	for i in range(3):          # Make tess draw equilateral triangle
	    tess.forward(80)
	    tess.left(120)

Qué pasaría si hiciéramos esta versión del loop de alex? (L3_tortuga (for))

		for c in ["yellow", "red", "purple", "blue"]:
		    alex.color(c)
		    alex.forward(50)	
		    alex.left(90)

A una variable se le puede asignar un valor que sea una lista. Luego esa variable se puede utilizar en el for loop, como en el siguiente ejemplo: (L3_tortuga (for))

	# Assign a list to a variable
	clrs = ["yellow", "red", "purple", "blue"]
	for c in clrs:
	    alex.color(c)
	    alex.forward(50)
	    alex.left(90)

3.6) Algunos métodos y trucos más con la tortuga

Los métodos de turtle pueden usar ángulos o distancias negativas. Así, tess.forward(-100) moverá a tess 100 hacia atrás. Y tess.left(-30) le hará rotar 30° hacia la derecha.

Por otro lado, como hay 360° en un círculo, rotar 30° hacia la izquierda tiene le mismo efecto que rotar 330° hacia la derecha (la única diferencia estará en la animación del movimiento, que nos permite distinguir entre ambos casos).

Esto sugiere que no necesitamos un método "left" y un método "right" de rotación: podríamos usar uno solo. Del mismo modo, existe un método backward que no es estrictamente necesario usar.

Parte de pensar como un programador requiere comprender la estructura y ricas relaciones que ocurren en nuestro campo. En este caso, revisando algunos hechos básicos sobre la geometría y comprendiendo las relaciones entre left/right, backward/forward, distancias positivas/negativas y ángulos positivos/negativos mientras jugamos con tortuas es un buen inicio para ese camino.

El lápiz de una tortuga puede ponerse "up" o "down", indicando en el primer caso que queremos movernos sin dibujar.

Las tortugas pueden tener distintas formas. Las formas disponibles por defecto son arrow, blank, circle, classic, square, triangle, turtle

Se puede regular la velocidad con que se mueve la tortuga, desde 1 (lento) hasta 10 (rápido). Si se elige 0, se interpreta como "tan rápido como sea posible" (L3_tortuga (for + shape))

Una tortuga puede "estampar" su huella en el canvas, y ésta quedará aun cuando la tortuga se haya movido. El estampado funciona incluso cuando el pen está "up".

Veamos un ejemplo que aplica todas estas funcionalidades: (L3_tortuga (espiral))

	import turtle
	wn = turtle.Screen()
	wn.bgcolor("lightgreen")
	tess = turtle.Turtle()
	tess.shape("turtle")
	tess.color("blue")

	tess.penup()                # This is new
	size = 20
	for i in range(30):
	   tess.stamp()             # Leave an impression on the canvas
	   size = size + 3          # Increase the size on every iteration
	   tess.forward(size)       # Move tess along
	   tess.right(24)           #  ...  and turn her

	tess.color("orange")

	wn.mainloop()

Se obtiene lo siguiente:

Cuántas veces se ejecutó el cuerpo del loop? (30) Cuántas tortugas se ven en pantalla? (31) Todas salvo la última son huellas creadas con el llamado a stamp. La última es en realidad la única instancia de la tortuga, es decir la tortuga en su estado actual (la cual todavía no ha impreso ninguna huella en esa posición). Para distinguirla de las demás, agregamos la línea que le asigna un color naranja, lo cual la hace bien distinta de las que sólo son huellas.

3.7) Glosario

3.8) Ejercicios

1) Escribir un programa que escriba 1000 veces "Dibujamos en Python con tortugas".

2) Imagina un objeto celular y designa tres posibles atributos y 3 posibles métodos.

3) Escribe un programa que use un loop para imprimir:

	Uno de los meses del año es enero
	Uno de los meses del año es febrero
	...

4) Supón que nuestra tortuga tess está con heading 0 (mirando hacia el este). Si ejecutamos tess.left(3645), ¿qué hace tess, y cuál es su heading final?

5) Assume you have the assignment xs = [12, 10, 32, 3, 66, 17, 42, 99, 20]

  1. Write a loop that prints each of the numbers on a new line.
  2. Write a loop that prints each number and its square on a new line.
  3. Write a loop that adds all the numbers from the list into a variable called total. You should set the total variable to have the value 0 before you start adding them up, and print the value in total after the loop has completed.
  4. Print the product of all the numbers in the list. (product means all multiplied together)

6) Utiliza loops for para hacer que la tortuga dibuje los siguientes polígonos regulares (regular significa que todos los lados y ángulo son iguales):

7) Un pirata borracho hace un giro aleatorio y luego avanza 100 pasos hacia adelante, luego hace otro giro aleatorio y da otros 100 pasos, hace un nuevo giro, etc. Una estudiante de ciencias sociales registra el ángulo de cada giro antes de que el pirata dé los 100 pasos siguientes. Sus datos experimentales son [160, -43, 270, -97, -43, 200, -940, 17, -86]. (Los ángulos positivos son antihorarios.) Usa una tortuga para dibujar el camino que ha seguido el pirata.

	import turtle             # Allows us to use turtles
	wn = turtle.Screen()      # Creates a playground for turtles
	pirata = turtle.Turtle()    # Creo la tortuga y la llamo pirata

	angulos = [160, -43, 270, -97, -43, 200, -940, 17, -86]
	for angulo in angulos:
	    pirata.left(angulo)     #positivos antihorarios (hacia la "izquierda")
	    pirata.forward(100)

	wn.mainloop()             # Wait for user to close window

8) Mejorar el programa anterior para que reporte el heading del pirata al terminar su recorrido (asumir que el heading inicial es cero).

	import turtle             # Allows us to use turtles
	wn = turtle.Screen()      # Creates a playground for turtles
	pirata = turtle.Turtle()    # Creo la tortuga y la llamo pirata

	angulos = [160, -43, 270, -97, -43, 200, -940, 17, -86]
	heading = 0
	for angulo in angulos:
	    pirata.left(angulo)     #positivos antihorarios (hacia la "izquierda")
	    pirata.forward(100)
	    heading = heading + angulo

	heading = heading % 360
	print("El heading final del pirata es: " + str(heading));
	wn.mainloop()             # Wait for user to close window

9) Si fueras a dibujar un polígono regular de 18 lados, cuál sería el ángulo de giro de la tortuga en cada esquina?

10) En el prompt interactivo, anticipa qué hará cada una de las siguientes líneas y luego verifica que así ocurra. Ganas un punto por cada vez que aciertes.

	>>> import turtle
	>>> wn = turtle.Screen()
	>>> tess = turtle.Turtle()
	>>> tess.right(90)
	>>> tess.left(3600)
	>>> tess.right(-90)
	>>> tess.speed(10)
	>>> tess.left(3600)
	>>> tess.speed(0)
	>>> tess.left(3645)
	>>> tess.forward(-100)

11) Escribe un programa para dibujar una estrella pentagonal regular

12) Escribe un programa que dibuje un reloj similar al de la siguiente figura

13) Crear una tortuga y asignarla a una variable. Si se pregunta por su tipo, cuál es el resultado?