I am trying to create right triangles using random coordinates in turtle. Sometimes my code works, while other times the hypotenuse of my triangle goes in the wrong direction. I have spent hours trying to figure out what it causing the inconsistency.
There are several shapes within the code. However, only the RightTriangle class is giving me issues so I have removed the others.
If anyone is able to figure out how I can resolve the issues I am having I would be extremely grateful.
from turtle import Turtle
import random
from math import sqrt, degrees, asin, acos, atan
class Shape():
turtle = Turtle()
class RightTriangle(Shape):
def __init__(self, A, B):
self.A = A
self.B = B
def draw(self):
a = (self.B[0] - (self.A[0]))
b = (self.B[1] - (self.A[1]))
c = (sqrt((self.B[0] - self.A[0])**2 + (self.B[1] - self.A[1])**2))
angleA = degrees(atan(a/b))
angleB = degrees(atan(b/a))
Shape.turtle.penup()
Shape.turtle.setposition(self.A)
Shape.turtle.pendown()
Shape.turtle.forward(a)
Shape.turtle.right(90)
Shape.turtle.forward(b)
Shape.turtle.right(180-angleA)
Shape.turtle.forward(c)
Shape.turtle.penup()
def random_shapes(count):
def random_point():
return (random.randint(-200,200), random.randint(-200,200))
shapes = []
for i in range(1, count+1):
shapes += [RightTriangle(random_point(), random_point())]
return shapes
def main():
shapes = random_shapes(15)
for s in shapes:
s.draw()
input ('Hit <enter> key to end.')
input ('Have a nice day!')
main()
With your existing code the lengths of the edges can be negative which changes the meaning of your "forward" movements and "right" turns. Use absolute values when calculating lengths:
a = abs(self.B[0] - (self.A[0]))
b = abs(self.B[1] - (self.A[1]))
Note that c is always positive.
As a side note, you don't need the angleB variable in your code.
You are using a and b to represent dx and dy, and always starting at A. Think about whether it can be right to always do right(90) (hint, it cannot). If B is vertically above A you will go down instead of up (if you adopt the abs mentioned by Selcuk).
One really simply way to do this is to just point back towards point A in the turtle code. You can do this with
Shape.turtle.setheading(Shape.turtle.towards(self.A))
in the place of
Shape.turtle.right(180-angleA)
This is definitely the least truly mathematical way to solve this but it does work quite nicely.
Related
I need a loop over all of my clans, which are instances of a class. Each clan needs to be assigned a position, a x and a y coordinate. Preferably as two lists or as a single tuple (but no idea how I specify that). This is how it works for the 1st clan. Afterwards I always have to check, if the position is already assigned. If so, you have to search for a new position until it is free.
I then coded my class like this:
width = 20
height = 20
no_of_clans = 50
import random
class clan:
def __init__(self, index, position_list):
self.index = index
self.position_list = position_list
def index(no_of_clans):
return list(range(1, no_of_clans +1))
def position_list(self):
for i in range(1, no_of_clans +1):
positions = ()
if i ==1: #i can do it either like this
positions = [(random.randint(0, width)), (random.randint(0, height))]
positions.append(x,y)
else: #or like this, I do not know, which one is better, both are running
x = (random.randint(0, width))
y = (random.randint(0, height))
#check if x and y not already used
#assert
positions.append(x,y)
return positions
print(positions)
how can I assign positions randomly when also using 0.5 steps? I always get an error message.
I never get a list of my positions back, but I do not know what I did wrong.
I know those are probably fairly basic questions but I am quite new to python an already searched for 5h today and I really do ot know where to start anymore. I would be really happy if someon could help me. Thank you so much in advance <3
I found the answer to my original question in this post:
Coordinates of the edges of a honeycomb grid in python
Will update if I am finished with the class-thing :)
I am creating a snake game and I want an action to happen if I touch another turtle on my screen?
fruit = turtle.Turtle()
fruit.penup()
fruit.size = 45
fruit.color("Black")
fruit.shape("circle")
fruit.goto(randint(-180,0),randint(0,180))
That is the fruit code so you know what I have.
Unfortunately the Turtle package doesn't have a built-in collision mechanism, so you will have to define what a collision is, and repeatedly check for a collision yourself.
You can define a "collision" checking function using whatever definiton of collision you want.
For example if the distance between the centers of the two fruits is less than the combined radii of the turtles, that means the edges of the circles are overlapping. (For more complex shape a custom box or shape could be used to detect collisions)
Turtles' shapesize gives a multiplier on the default turtle size of 20, which we half to get the radius:
def is_collided(a, b):
distance = b.distance(a.pos())
radius_a = a.shapesize()[0] * 10
radius_b = b.shapesize()[0] * 10
return radius_a + radius_b >= distance
You can then define a list of different turtles, and loop through them to check whether any of them have collided with your fruit, and print if so:
other_turtles = [turtle1, turtle2, turtle3]
for t in other_turtles:
if is_collided(fruit, t):
# can define other actions here
print(f'collision between fruit and {t}')
If you just copy those two snippets directly below your sample code, and replace turtle1, turtle2 with the names of your other turtles, it should work.
You can also see this in action with different fruits here.
Hey I've been stuck on this issue for quite a while and was hoping someone could help me out:
I'm using pyglet and have got all of the code working in my project (even what I was having the issue with) then I restarted my computer and suddenly it didn't work...
This is the loop that is instantiating my 'Letter' objects:
main_st = ut.makeString("EXNXYXAXDAADUXMDXLGEQTAQXDDQSVXUTSXKHXHRXYFUXLXJUTHXYVADSUXKHUQUIXSJHXHDPKXFQUXILNXORMXRPL")
letter_list = []
for i in range(len(main_st)):
letter_list.append(l.Letter(pyglet.resource.image("Letters/" + main_st[i] + ".png"),main_st[i],10,10))
And this is the Letter class constructor Letter is a subclass of pyglet.sprite.Sprite:
def __init__(self,im,iden,xx,yy):
super(Letter,self).__init__(img=im,x=xx,y=yy)
At no point in the program do I modify the x and y coordinates of sprite but when I go to draw them, no matter what I put in for xx and yy they're always drawn in the same place on the window UNLESS I do a very large number for yy, and in those cases it simply disappears (I assume it's outside of the window).
I'm having each letter flash on the screen for 1 second and in order to do that here's my on_draw method
def on_draw():
background.draw()
if not key_manager.cur_letter == None:
key_manager.cur_letter.draw()
(only key_manager.cur_letter gets drawn and that switches every second).
The problem might be related to older versions.
But after calling super(Letter, self)... you could do:
def __init__(self,im,iden,xx,yy):
super(Letter,self).__init__(img=im,x=xx,y=yy)
self.x = xx
self.y = yy
And that should do the trick.
I'm making a game where I have a force field. I need the force towards the source to increase as I get closer. I have a code that gets the distance between the player and the source and I want to set the strength of the force as the distance between the objects. However, as I get closer to the source, the distance decreases and so does the strength of the force. Is it possible to have another variable that somehow goes opposite of the distance so that as distance decreases, it increases?
Try the following structure:
import math
force_constant = 100 #linearly increases the force applied to the player
max_pull_constant = 1 #fractionally decreases the maximum force applied to the player
point_source = [0,0,0]
point_player = [0,10,0]
def radius (point_player, point_source):
return_value_vector = [(p-s)**2 for p, s in zip(point_player, point_source)]
return_value_scalar = math.sqrt(sum(return_value_vector))
return return_value_scalar
def pulling_force(point_player, point_source):
return_value_scalar = force_constant/(max_pull_constant + radius(point_player, point_source))
def pulling_force_vector(point_player, point_source):
return_radius = radius(point_player, point_source)
return_vector = [(p-s)/return_radius for p, s in zip(point_player, point_source)]
return_force = pulling_force(point_player, point_source)
return_value_force_vector = [return_force*v for v in return_vector]
return return_value_force_vector
#Main program here
#Call pulling_force(player_location, source_location) to get the force from the force field
This structure should be what you are looking for, I wrote it assuming three dimensions, edit as needed. Enjoy!
I am currently in the process of making a new cannon game. How can I make it so that there is just one cannon, on the bottom left hand of the screen?
from graphics import *
from math import sqrt
from math import trunc
def PinR(p,r):
if p.getX()>=r.getP1().getX() and p.getX()<=r.getP2().getX()and p.getY()>=r.getP1().getY() and p.getY()<=r.getP2().getY():
return True;
else:
return False;
def distance(p1,p2):
dx=p1.getX()-p2.getX();
dy=p1.getY()-p2.getY();
dist=sqrt(dx*dx+dy*dy);
return dist;
#parameter
FieldWidth=700;
FieldHeight=700;
GroundDepth=75;
BallSize=10;
OriginalSpeed=4;
FieldBackground="brown";
FieldBorder="brown";
tickLength=800000;
buttonSize=8;
# number of cannons and balls
numBalls=4;
# initial cannon power
explosionStrength=30;
# intial gravitational constant
g=1;
# clock tick delay
delay=0.05;
#Create field
Field=GraphWin("B",FieldWidth,FieldHeight);
Field.setBackground(FieldBackground);
#set of balls
spacing=FieldWidth/(numBalls);
ball=[];
for b in range (0,numBalls):
newball=Circle(Point(spacing*b+spacing//2,FieldHeight-GroundDepth),BallSize);
newball.setFill("black");
newball.draw(Field);
ball.append(newball);
#cannon
cannon=[]
for c in range (0,numBalls):
newCannon=Rectangle(Point(spacing*c+spacing//2-BallSize,FieldHeight-GroundDepth-BallSize*5),
Point(spacing*c+spacing//2+BallSize,FieldHeight-GroundDepth+BallSize));
newCannon.setFill("black");
newCannon.draw(Field);
cannon.append(newCannon);
#set of button groups (fire, powerup, powerdown)
fire=[];
for f in range (0,numBalls):
newbutton=Rectangle(Point(spacing*f+spacing//2-buttonSize//2,FieldHeight-GroundDepth-BallSize),
Point(spacing*f+spacing//2+buttonSize//2,FieldHeight-GroundDepth-BallSize+buttonSize));
newbutton.setFill("red");
newbutton.draw(Field);
fire.append(newbutton);
#wall
#target(red,white,red,white)
balldistance=20;
ball1=Circle(Point(FieldWidth//2-20,FieldHeight//2+20),BallSize);
ball1.setFill("red");
ball1.draw(Field);
The reason you get 4 cannons is that you're doing this:
for c in range (0,numBalls):
… where numBalls is 4, and you create a new cannon each time through the loop.
Presumably with only 1 cannon you also only want one cannon ball and one shot, so just set numBalls = 1 instead of numBalls = 4.
However, it might make more sense to simplify the program while you're at it. Replace the lists of 4 cannons with a single cannon, get rid of the loop, do the same for the 4 balls, etc. Then you can also simplify the layout rules—no need for a spacing variable to configure how far apart the cannons are if there's only 1 of them. And so on. This might make it easier for you to understand how the program works—and figuring out how to simplify it might similarly be beneficial on its own.
And if you want to change its position, that's being set in this line:
newCannon=Rectangle(Point(spacing*c+spacing//2-BallSize,FieldHeight-GroundDepth-BallSize*5),
Point(spacing*c+spacing//2+BallSize,FieldHeight-GroundDepth+BallSize));
So, you can tweak the various constants (which all seem to have pretty reasonable names) to get the result you want—or, of course, just hardcode the position you want instead of calculating it.