How can I append Turtle clones to a list without cloning indefinitely? - python-3.x

I have to write a program in Python using Turtle that reads a list of instructions from a file and draws a Turtle diagram based on the inputs. The possible inputs are forward, left, right, and split. If the input is split, I have to clone all of the turtles in a list and append the new Turtles to the list. All new clones should be turned right by x degrees. The problem is, the turtles clone indefinitely.
def navigate(directions):
turtles = []
commands = []
first = turtle.Turtle()
turtles.append(first)
turtle.width(10)
for turt in turtles:
turt.speed('fastest')
for step in directions:
if step[0] == 'forward':
turt.forward(step[1])
elif step[0] == 'left':
turt.left(step[1])
elif step[0] == 'right':
turt.right(step[1])
elif step[0] == 'split':
new = turt.clone()
turtles.append(new)
turt.right(step[1])
Directions is a list of tuples where the first value of each tuple is the command (e.g. forward, left, right, or split) and the second is the degree (How far forward to go, how many degrees to turn). But the turtle continue to clone forever. How can I adjust this code so that it only clones a given number of times? Here is my sample input file:
forward 50
left 20
split 40
forward 50
left 20
split 40
forward 50
left 20
split 40
forward 50
left 20
split 40
forward 50
left 20

Firstly, you have a logic error. Your outer loop iterates over the turtles, and then inside the loop, it processes all the steps for each turtle separately. This is incorrect; even if it worked, it would apply instructions from before the split to turtles created after it. (That would effectively apply those preceding instructions twice to the same turtle, as well as execute the split again.) You need to process a single step at a time on all turtles. So the loop needs to be over the directions, not the turtles:
for step in directions:
# Process the step
Now that we have the loops swapped, a solution becomes more obvious. We can check the step and have special handling for the 'split' case:
for step in directions:
if step[0] == 'split':
# Clone all the turtles here
else:
for turt in turtles:
if step[0] == 'forward':
turt.forward(step[1])
elif step[0] == 'left':
...
I chose to use a single loop for the movement cases, but it would be equally valid to have a loop for each case if you prefer.
We still have the issue of creating the clones and adding them to the list. You already saw this won't work:
for turt in turtles:
turtles.append(turt.clone())
So we need to store all these new clones without modifying the list we're looping over. We can do that with a temporary list:
turtle_clones = []
for turt in turtles:
turtle_clones.append(turt.clone())
And then we just need to put turtle_clones onto the end of turtles. I'll leave combining the two lists as an exercise for you.
Bonus Material
Here are some suggestions to make your code cleaner:
Use unpacking so you don't have to have indexes everywhere.
You actually don't need to access the two pieces of data in step using an index. You can use unpacking to put each element in its own variable:
for action, value in directions:
if action == 'split':
...
else:
...
turt.forward(value)
This makes you code much simpler and easy to read, although you can probably think of a better name for value.
You actually don't have to use an explicit temporary list and for loop to make the clones. You can use a list comprehension:
turtle_clones = [t.clone() for t in turtles]
Again, much simpler and easy to read.
The first variable appears unnecessary. You can initialize the list containing the first turtle directly:
turtles = [turtle.Turtle()]

Related

Finding rightmost greater element in array with positive integers Python

I am working on a code logic to find the farthest right greater element than a given element.
For example, if input array is [3,7,9,4,1] then output should be [4, 9, 9, 4, 1]. Notice that since 9 is the greatest element, it will remain unchanged, and similarly with the element on the right boundary.
Here is my working code for it,
def rightmost_greater(arr):
n=len(arr)
left=0
right=n-1
L=[]
while left<=right:
if arr[left]>arr[right]:
right-=1
elif arr[left]==arr[right]:
L.append(arr[left])
left+=1 # move to next element
right=n-1 # reset the right pointer
else:
L.append(arr[right])
left+=1 # move to next element
right=n-1 # reset the right pointer
return L
As you can see, this code seems quite wordy, I'm resetting the right pointer several times; is it possible to use an efficient way to write the code(preferably recursive logic). Advice is greatly appreciated. Thanks
This is a solution with almost the same logic but a recursive one and a little efficient. see if this helps.
arr=[3,7,9,4,1]
n=len(arr)
L=[]
def rightmost_greater(arr,left,right):
if left<=right:
if arr[left]>arr[right]:
rightmost_greater(arr,left,right-1)
else:
L.append(arr[right])
rightmost_greater(arr,left+1,n-1)
rightmost_greater(arr,0,n-1)
print(L)

Pygame for loop iterates once, and then does not iterate again

Essentially, what I'm doing is taking the enemies list (on line 1), which is holding a list of coordinates and iterate through each pair in the enemies list at the bottom.
I want to go through each enemy in the list, get the y coordinate, add 10 and then go to the next enemy and add 10, so on and so forth. For some reason, it adds 10 ONCE and then stops, and the enemies do not fall down the screen. I don't know why this is happening. Why is it not running through the for loop anymore? Thank you so much for any help.
NOTE: I removed some code at the top for the sake of being less confusing. The update() function is just the pygame flip function.
enemies = [[100,0], [150,0]]
while True:
for enemy in enemies:
x = enemy[0]
y = enemy[1]
y += 10
pygame.draw.rect(screen, (255,0,0), (x, y,10,10))
# uses flip to update the screen
update()
# FPS
clock.tick(20)
You're trying to modify a local variable, not the value in the list. You need to write:
enemy[1] += 10
Since integers are immutable (they cannot be changed), the line y = enemy[1] can be thought of as "copy the value from enemy[1] into y".

Python 3.x Homework help. Sequential number guessing game.

We are supposed to make a number guessing game where depending on what difficulty the player chooses the game generates 4 or 5 numbers and the player is given all but the last, which they have to guess in 3 tries. The numbers have to be equal distances apart, and the numbers have to be within the 1 - 100 range.
So far I know what it will look like roughly.
def guesses:
function for accumulating tries as long as guesses_taken < 3
let user retry, or congratulate and offer to replay
def game_easy:
code for number generation, step value, etc
guesses()
def game_hard:
same code as easy mode, with the appropriate changes
guesses()
For the random numbers, all I have so far is this
guess_init = rand.int (1,100)
step = rand.int (1,20)
guess_init = guess_init + step
and just having it loop and add the step 4 or 5 times respectively.
Where I'm stuck is 1. How to ensure that none of the numbers generated exceed 100 (so it can't be a step of 1 starting at 98), and 2. how to print all but the last number generated.
What I was thinking was assigning the last number generated to a variable that the player input must match. But I was also thinking that if "guess_init" has ran through the loop, then it will already be holding the value of the last number and all Ill have to check is that user input == guess_init.
In your Case you should read the random section from the Python Standard Library. Especially this is relevant:
random.randrange(start, stop[, step])
Return a randomly selected element from range(start, stop, step). This is equivalent to choice(range(start, stop, step)), but doesn’t actually build a range object.

Explain this Towers of Hanoi solution (written in python)

I am trying to understand this particular solution of Towers of Hanoi problem. It is a recursive solution.
A = [5, 4, 3, 2, 1]
B = []
C = []
def move(n, source, target, auxiliary, frm):
print('n here : ', n)
if n > 0:
print('frm : ', frm, n)
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, auxiliary, target, '1')
# move the nth disk from source to target
target.append(source.pop())
print('n:', n)
# Display our progress
print(source, target, auxiliary, '##############', sep = '\n')
# move the n - 1 disks that we left on auxiliary onto target
move(n - 1, auxiliary, target, source, '2')
# initiate call from source A to target C with auxiliary B
move(5, A, C, B, '0')
You can see it live here
https://repl.it/JUzY/0
After the first line of ######### gets printed, the value of n = 0, becomes 2. How does this happen? Am I missing out some concept of recursion ? Help me in understanding this.
You need to understand scope. You say "the value of n = 0, but suddenly it becomes 2". But there is more than one "n", because the scope of n is the method. Each time move gets called, that method call has its own instance of n with its own value.
At the bottom of the script, you call move(5, A, C, B, '0'). Inside that call, n == 5. The first thing that does is call move(n - 1, source, auxiliary, target, '1'). Inside that call, n == 4. But when it eventually returns, n in the original function call is still 5.
A good way to understand how recursion works, is to work through the program execution by hand, using sheets of paper. I'm going to use a Post-It note for function calls, and a notebook for output. You also have your "model", in the form of the lists A,B,C. Since elements move around, you could represent those elements with scraps of paper or scrabble tiles.
Start with the intial move(4, A, C, B, '0'). (I'm starting at 4 because the number of steps grows rapidly). Since we are calling a function, take a new post-it to represent the function call, and write on it:
n = 4
source = A
target = C
auxiliary = B
frm = '0'
Now work through the code of move doing what it says. Write any print output on this one.
When you reach the call to move(n - 1, source, auxiliary, target, '1'), make a note on the post-it of the line number you're at, then get a new post-it, and write the values that are going into it:
n = 4 (because 4 - 1)
source = A
target = B (caller's auxiliary)
auxiliary = C (caller's target)
frm = '1'
Place this on top of the previous post-it. You are not allowed to look at the covered up post-it until it's revealed again. Keep doing this, and you'll end up with a stack of five post-its.
The fifth post-it has n = 0. Now when you step through its code, because it starts with if n > 0:, the call returns immediately. This function call is over, so tear up that one post-it and throw it away. The values of n etc. in that post-it have no effect on the other post-its.
You made a note of the line-number you were at, so carry on executing the code by hand from that line. You will produce some output, then call move again -- use post-its for this move too, again covering up the current post-it and growing your stack until you make a call with n == 0 and can remove an item from the stack.
If you keep doing this, you'll see the stack grow and shrink several times, you'll reach the original post-it once, do its print, then grow the stack again, then reach the original post-it again, and finally finish.
The stack of post-it notes is an exact model of the execution stack of a program's runtime. When you see stack traces (even of non-recursive programs), this is what they represent. Each post-it is a stack frame, and local variables have a scope that is restricted to that stack frame.
The recursive solution to Hanoi can be expressed in English as:
To move a stack of size zero, do nothing
To move a bigger stack from src to destination via auxiliary:
First move n-1 discs from src to auxiliary (to get them out of the way)
do that the same way we are doing this.
Then move the revealed disc to destination
Then move the n-1 discs from auxiliary to destination, using src as a spare peg
do that the same way we are doing this

How to pull tuples out of a list and make a turtle connect it as coordinates?

Write a function called connectTheDots that takes in a list of tuples as its input and an optional color input as well. The default color value should be black. Each tuple is a coordinate pair (x, y) for a turtle. The function will have the turtle trace out a picture by starting at the first coordinate and then moving to each coordinate in turn.
Your function should do the following:
a. Create a turtle, setting the turtle’s color and speed appropriately
b. Check if the input list is empty: if it is empty then nothing else should happen!
c. Without leaving a line behind, move the turtle to the first location given in the list. Then start leaving a line again. Note: recall how to pull values out of a list, and also know that the goto method can take a single (x, y) tuple as its input: myTurtle.goto( (25, 25) ) will move myTurtle to x = 25 and y = 25.
d. After the turtle is at the starting coordinate, move it to each coordinate in the list in turn.
This is what I have been able to do so far:
def connectTheDots(list1, color ="black"):
myTurtle = turtle.Turtle()
myTurtle.speed(1)
myTurtle.goto(list1[0])
for x,y in list1[1:]: #I'm unsure if this is correct
myTurtle.goto(x,y)
You have most of what you need but are probably making it more complicated than needed and are missing some small details.
For step "a" you need to explicitly set the color (you passed it in just fine). You are probably better off using a symbolic speed instead of a numeric one.
For step "b", if you have a proper for ... in loop, you don't need to explicitly check if the list is empty as the loop won't run if it is. Your splitting off the first item myTurtle.goto(list1[0]) works against you here as there may not be one, causing an IndexError.
For step "c" you need to add another command. Turtles start life in the center of the screen with their pens down. You need to raise the pen up after creating your turtle. But you don't need to explicitly move to the starting position, let your loop handle that.
The trick we'll use for step "c" and step "d" is to put the pen down after the goto() in the loop. The first time, this actually puts the pen down, after that, it's a harmless no-op:
import turtle
def connectTheDots(coordinates, color="black"):
myTurtle = turtle.Turtle()
myTurtle.speed("slowest")
myTurtle.color(color)
myTurtle.penup()
for coordinate in coordinates:
myTurtle.goto(coordinate)
myTurtle.pendown() # redundant after first iteration
dots = ((34, 56), (100, 240), (230, 105), (34, 56))
connectTheDots(dots, "green")
turtle.done()
If it bothers you that we're putting the pen down unnecessarily in the loop, then we can replace myTurtle.pendown() with:
if not myTurtle.isdown():
myTurtle.pendown()

Resources