Aleckandre97
Aleckandre97

Reputation: 31

Python - Modifying a Turtle program to turn around when it hits a wall

I created 2 turtle's that keep moving until one of them hits a wall which causes them to stop completely.

My question is how could I make it so when it hits a wall/the other turtle, it turns instead of stopping and continues? I have a rough idea of how to do it with 1 turtle however whenever I tried it with 2, it just isn't working.

import random
import turtle

def moveRandom(wn, t):
    coin = random.randrange(0,2)
    if coin == 0:
        t.left(90)
    else:
        t.right(90)

    t.forward(50)

def areColliding(t1, t2):
    if t1.distance(t2) < 2:
        return True
    else:
        return False

def isInScreen(w, t):
    leftBound = - w.window_width() / 2
    rightBound = w.window_width() / 2
    topBound = w.window_height() / 2
    bottomBound = -w.window_height() / 2

    turtleX = t.xcor()
    turtleY = t.ycor()

    stillIn = True
    if turtleX > rightBound or turtleX < leftBound:
        stillIn = False
    if turtleY > topBound or turtleY < bottomBound:
        stillIn = False
    return stillIn

t1 = turtle.Turtle()
t2 = turtle.Turtle()
wn = turtle.Screen()

t1.shape('turtle')
t2.shape('circle')

leftBound = -wn.window_width() / 2
rightBound = wn.window_width() / 2
topBound = wn.window_height() / 2
bottomBound = -wn.window_height() / 2

t1.up()
t1.goto(random.randrange(leftBound, rightBound),
        random.randrange(bottomBound, topBound))
t1.setheading(random.randrange(0, 360))
t1.down()

t2.up()
t2.goto(random.randrange(leftBound, rightBound),
        random.randrange(bottomBound, topBound))
t2.setheading(random.randrange(0, 360))
t2.down()


while isInScreen(wn, t1) and isInScreen(wn, t2):
    moveRandom(wn, t1)
    moveRandom(wn, t2)

wn.exitonclick()

Upvotes: 2

Views: 3051

Answers (2)

keepAlive
keepAlive

Reputation: 6655

You actually need to reorganize your code, so as to not repeat yourself.

import random
import turtle

Let us build the properties of the world

wn          = turtle.Screen()
leftBound   = -wn.window_width() / 2
rightBound  = wn.window_width() / 2
topBound    = wn.window_height() / 2
bottomBound = -wn.window_height() / 2

And of its living creatures

t1 = turtle.Turtle()
t1.shape('turtle')
t2 = turtle.Turtle()
t2.shape('circle')

delta_move = 50

# Let us build a list so as to have all turtles in hands in one object
turtles = [
    t1,
    t2,
    #t3,
    #t4,
    #...
]

for t in turtles:
    t.speed(0) # To make turtles be faster (0 is the FASTEST SPEED)
    t.setheading(random.randrange(0, 360))
    # We do not need what follows anymore, since collisions
    #  will be managed thereafter
    ##t.goto(random.randrange(leftBound, rightBound),
    ##        random.randrange(bottomBound, topBound))

Some physical constraints

def isCollidingOtherTurtle(t):
    # Let us define the reciprocity between turtles
    #  using the just-defined list "turtles" above
    other_turtles = [t_ for t_ in turtles if t_ is not t]
    # This python function called any,
    #  checks whether there is at least one 
    #   other turtle at a distance lower than 2.
    #    In this case, any(...) returns True
    return any([t.distance(ot) < 2\
                for ot in other_turtles])


def isInScreen(t):
    # There is no need to redefine the bounds
    #  since they already have a global scope 
    #   definition working in the local
    #    scope of this function 
    #     [see global versus local variables]
    ## leftBound   = - w.window_width() / 2
    ## rightBound  = w.window_width() / 2
    ## topBound    = w.window_height() / 2
    ## bottomBound = -w.window_height() / 2

    turtleX = t.xcor()
    turtleY = t.ycor()

    stillIn = True
    if turtleX > rightBound or turtleX < leftBound:
        stillIn = False
    if turtleY > topBound or turtleY < bottomBound:
        stillIn = False
    return stillIn

Define each generic turtle's brain

def getDirection():
    #return random.random()*360. - 180 # returns a float between -180 and 180
    return random.choice([-180,-90,0,90,180])

def moveRandom(t):
    # A generic turtle moves forward 
    #  if it is going to collide 
    #   with no other turtle
    if not isCollidingOtherTurtle(t):
        if isInScreen(t):        
            t.left(getDirection())    
            t.forward(delta_move)
        else:
            t.backward(delta_move)
    else:
        t.backward(delta_move)

And let us define how to animate this world, and animate it

def anim(delta_time=60):
    wn.tracer(0, 0)
    for t in turtles:
        moveRandom(t)
    wn.update()
    wn.ontimer(anim, delta_time) # delta_time is in milliseconds

anim()
turtle.mainloop()

Upvotes: 1

cdlane
cdlane

Reputation: 41872

Here's a rework of your code that I believe does as you desire. Most of my other changes are simplifications of your code. One major change is I tossed the while() loop that was moving the turtles and put them on their own timers instead:

import random
from turtle import Turtle, Screen

def moveRandom(wn, t):
    coin = random.randrange(0, 2)

    [t.left, t.right][coin](90)

    t.forward(50)

    if not isInScreen(t) or areColliding(t1, t2):
        t.undo()  # pretend it never happened
        t.left(180)  # turn around
        t.forward(50)  # go the other way

    wn.ontimer(lambda: moveRandom(wn, t), 100)

def areColliding(t1, t2):
    return t1.distance(t2) < 2

def isInScreen(t):
    turtleX, turtleY = t.position()

    return leftBound < turtleX < rightBound and bottomBound < turtleY < topBound

t1 = Turtle('turtle', visible=False)
t2 = Turtle('circle', visible=False)
wn = Screen()

leftBound, rightBound = -wn.window_width() // 2, wn.window_width() // 2
bottomBound, topBound = -wn.window_height() // 2, wn.window_height() // 2

t1.up()
t1.goto(random.randrange(leftBound, rightBound), random.randrange(bottomBound, topBound))
t1.setheading(random.randrange(0, 360))
t1.showturtle()
t1.down()

t2.up()
t2.goto(random.randrange(leftBound, rightBound), random.randrange(bottomBound, topBound))
t2.setheading(random.randrange(0, 360))
t2.showturtle()
t2.down()

moveRandom(wn, t1)
moveRandom(wn, t2)

wn.exitonclick()

To handle the collisions, I call isInScreen(t) and areColliding(t1, t2) in moveRandom(wn, t) and simply undo the move if it causes a collision, turn around move the opposite direction.

Upvotes: 1

Related Questions