Search code examples
pythonturtle-graphicsgoto

Python: turtle goto() command


My assignment asks me to code a graphical representation of some nested squares varying in size from 20-80, like this:

enter image description here

After creating the first square, I need to move position to the start of the next square. I use the goto() command for this. My problem is the goto() command as I use two variables for the horizontal and vertical inputs but only one of them works at a time--I need them both to work. Any help would be appreciated.

#Draw a set of nested squares, increasing in size
from turtle import *

number_of_shapes = 4

for shape in range(1, number_of_shapes + 1):
    #Draw a square
    for sides in range(1,5):
        forward(20 + shape * 10)
        right(90)

#Move to position of next square
    penup()
    goto(shape * 10, shape * 10)
    pendown()

Solution

  • One of the great things about Python is that it is easy to test small pieces of code in the interpreter. Let's use the interpreter to check the length of the sides:

    In [82]: number_of_shapes = 4
    
    In [83]: for shape in range(1, number_of_shapes + 1):
       ....:     print(20 + shape * 10)
    30
    40
    50
    60
    

    Oops. Now we can clearly see forward(20 + shape * 10) isn't going to make the sides have lengths 20, 40, 60, and 80. Notice that (20 + shape * 10) makes the side lengths increment by 10. We want to increment by 20, so use (20 + shape * 20) instead:

    In [84]: for shape in range(1, number_of_shapes + 1):
       ....:     print(20 + shape * 20)
       ....: 
    40
    60
    80
    100
    

    Oops, I got it wrong. No problem, we just need to decrement the whole thing by 20:

    In [85]: for shape in range(1, number_of_shapes + 1):
       ....:     print(shape * 20)
       ....: 
    20
    40
    60
    80
    

    Ah, much better.


    So now the code looks like this:

    import turtle
    
    number_of_shapes = 4
    
    for shape in range(1, number_of_shapes+1):
        #Draw a square
        for sides in range(4):
            turtle.forward(20 + shape * 20)
            turtle.right(90)
    
        #Move to position of next square
        turtle.penup()
        turtle.goto(shape * 10, shape * 10)
        turtle.pendown()
    turtle.mainloop()
    

    enter image description here

    When you run this code, notice that the turtle starts each square at the top left corner and starts drawing to the right.


    Now let's think about the goto(shape * 10, shape * 10) statement. Where is it taking us? Let's use the interpreter to find out:

    In [87]: for shape in range(1, number_of_shapes + 1):
       ....:     print(shape * 10, shape * 10)
       ....: 
    10 10
    20 20
    30 30
    40 40
    

    Comparing these coordinates with the result above, you can see that the turtle is being moved up and to the right. The top left corner of each square is starting a little higher and a little to the right. Instead, we want the top left corner of each new square to start a little higher and to the left:

    In [88]: for shape in range(1, number_of_shapes + 1):
       ....:     print(-shape * 10, shape * 10)
       ....: 
    -10 10
    -20 20
    -30 30
    -40 40
    

    Let's see what happens now:

    enter image description here

    Bingo.


    By the way, mixing relative commands like left and right and forward with absolute commands like goto requires... coordination. The math is finicky. If you stick with purely relative commands, you don't have to think hard about getting the goto coordinate formulas correct:

    turtle.penup()
    turtle.left(90)
    turtle.forward(10)
    turtle.left(90)
    turtle.forward(10)
    turtle.left(180)
    turtle.pendown()
    

    The advantage of using purely relative commands is that now you can place the turtle anywhere you want, with any initial heading, and still draw nested squares:

    import turtle
    
    number_of_shapes = 4
    turtle.setheading(45)
    turtle.penup()
    turtle.goto(20, 50)
    turtle.pendown()
    for shape in range(1, number_of_shapes+1):
        #Draw a square
        for sides in range(4):
            turtle.forward(20 + shape * 20)
            turtle.right(90)
    
        #Move to position of next square
        turtle.penup()
        turtle.left(90)
        turtle.forward(10)
        turtle.left(90)
        turtle.forward(10)
        turtle.left(180)
        turtle.pendown()
    turtle.mainloop()
    

    enter image description here