How to check if turtle is on stamp position - python

I am new to Python and trying to build a simple game. In the game is just one ant and this ant follows simple rules: if ant is on white field, he paints the tile black (or other color), turns right, moves forward. If ant is on colored tile, remove color, turn left, move forward.
Currently I am trying to do this with turtle and stamps. Stamp ID and coordinates are stored in a dict, a loop is checking if ant position is in dict keys and there is the problem: when the ant moves to the same position, where the first stamp is, this check return False and the ant never escape the loop.
from turtle import Turtle, Screen
screen = Screen()
screen.setup(height=900, width=1000)
screen.title("The Ant")
ant = Turtle()
ant.color("brown")
ant.pencolor("black")
ant.penup()
kleks_liste = {}
game_running = True
ant.shape("square")
ant.setpos(5, 5)
kleks_liste[ant.pos()] = ant.stamp()
ant.shape("classic")
ant.forward(20)
while game_running:
pos_key = ant.pos()
if pos_key in kleks_liste.keys():
ant.clearstamp(kleks_liste[pos_key])
kleks_liste.pop(pos_key)
ant.left(90.00)
ant.forward(20.00)
else:
ant.right(90.00)
ant.shape("square")
kleks_liste[pos_key] = ant.stamp()
ant.shape("classic")
ant.forward(20.00)
screen.exitonclick()
What am I doing wrong? If I comment out ant.forward in the else section, the check works and returns True. Can someone explain the behavior?

The Vec2D returned by pos() contains floats which need to be compared with an epsilon. One workaround is to cast these values to integers, either with int or round. This shouldn't lose accuracy since you're working on a grid.
Instead of pos_key = ant.pos(), try pos_key = tuple(map(int, ant.pos())). Don't forget to change the first stamp key outside the loop too.
As an aside, you can use if pos_key in kleks_liste: without .keys().

Related

Create random-path by randomly choosing from lists of stored turtle-graphics functions

I have stored turtle-graphics functions in list and am using random functions to call it to create a random-path, however code does not work.
Can someone please have a look on this and provide suggestion.
from turtle import Turtle
from turtle import Screen
import random
pen = Turtle()
pen.pensize(8)
pen.speed(10)
window = Screen()
window.colormode(255)
moves=[pen.forward(30),pen.backward(30)]
turns=[pen.right(90),pen.left(90)]
is_true = True
while is_true:
pen.color(random.randint(0,255),random.randint(0,255),random.randint(0,255))
random.choice(turns)
random.choice(moves)
window.exitonclick()
I would say the issue here is that you are using functions as data when you could simply use data as data. That is, giving forward() a negative distance is the same as backward(). Giving left() a negative angle is the same as right(). So we can simply do:
from turtle import Screen, Turtle
from random import random, choice
DISTANCES = [30, -30]
ANGLES = [90, -90]
def move():
turtle.color(random(), random(), random())
turtle.left(choice(ANGLES))
turtle.forward(choice(DISTANCES))
screen.ontimer(move, 10)
screen = Screen()
turtle = Turtle()
turtle.pensize(8)
turtle.speed('fastest')
move()
screen.exitonclick()
I also dealt the next issue, your implicit while True:. The way you structured your code, the exitonclick() is never reached and doesn't work. Now it works as we've kept both the drawing and exitonclick() in the event loop.
You execute the methods only when you define the two list. Change the relevant part of the code like this
moves=[pen.forward, pen.backward]
turns=[pen.right, pen.left]
while True:
pen.color(random.randint(0,255), random.randint(0,255), random.randint(0,255))
random.choice(turns)(90)
random.choice(moves)(30)
The first problem is that you don't list function calls in the lists moves and turns but the results of the calls. The second problem is that you don't call functions after the random.choice calls. What you are actually getting from this is the flickering effect of the visible pen tip that endlessly changes the color.
On way to fix it is already shown in buran's answer. Another way that keeps the turn and move arguments out of the loop is the following, here lambda : transforms the function calls into anonymous functions to which references are stored in moves and turns:
Another option is to extract the actual moves and turns into functions
from turtle import Turtle
from turtle import Screen
import random
pen = Turtle()
pen.pensize(8)
pen.speed(10)
window = Screen()
window.colormode(255)
moves=[lambda : pen.forward(30), lambda : pen.backward(30)]
turns=[lambda : pen.right(90), lambda : pen.left(90)]
for _ in range(100):
pen.color(random.randint(0,255),random.randint(0,255),random.randint(0,255))
random.choice(turns)()
random.choice(moves)()
window.exitonclick()
I decided to only draw 10 lines, so the turtle will most likely remain on screen. For a better way to get rid of the while True loop (including explanation) see cdlane's answer!

Top-Down map scrolling with python ncurses

I'm new to curses but I have some experience. I'm trying to make a top-down style game in python with ncurses, but I don't even know where to start. I'm wanting the character to be centered on the screen and move around the map, but the environment to not all be visible on the screen at once. Is this possible? I would like to know before I get started.
For the background, if it's text-based, you could draw the map using 2-layer list or tuple that gets printed using loops and slices with window.addstr(x,y,string)
example, with stdscr as the name of the window:
map_tiles = [['a','1','+'],['b','2','-'],['c','3','=']]
inx = 0
iny = 0
range = 1
def scroll(bx,by):
stdscr.clear()
for y, x_line in enumerate(map_tiles[by : by+range]):
for x, tile in enumerate(x_line[bx : bx+range]):
stdscr.addstr(y, x, f'{tile}')
then, by calling scroll(inx.iny) the tiles within range of the slices map_tiles[by : by+range] and x_line[bx : bx+range] will get printed in the terminal.
You can then bind keys to add increments to inx and iny to change the visible tiles.

Pygame: trying to get the whole sprite to cross a boundary instead of just the top left pixel

I am trying to make my sprite move according to the astar pathfinding algorithm. However, after I implemented it, I realised that the movement of the sprite is only in accordance to the top left pixel. That means that if the algorithm tells it to move up after crossing a boundary, it will do so once the top left pixel crosses that boundary. However, this means that the entire sprite has not actually crossed, resulting in a collision if there is an obstacle just above it. Is there any way to tell it to move left more before moving up]1
def astar_ghost(pac,ghost):
maze=astar.create_maze(screen_width,screen_height,obstacles) #creates a maze of 0s and 1s. The 1s represent the obstacles
start=(ghost.gridloc[0],ghost.gridloc[1])
end=(ghost.goal_node[0],ghost.goal_node[1])
goal_node=astar.astar(maze,start,end)
if goal_node==None:
pass
else:
ghost.goal_node=goal_node
game=True
if ghost.goal_node[0]<ghost.gridloc[0]:#left
print('move left')
game=collision(pac,ghost) #collision is another function that checks for collision and returns True or False. If False, the game will be over
ghost.left=True
ghost.right=False
ghost.up=False
ghost.down=False
elif ghost.goal_node[0]>ghost.gridloc[0]:#right
print('move right')
game=collision(pac,ghost)
ghost.left=False
ghost.right=True
ghost.up=False
ghost.down=False
elif ghost.goal_node[1]<ghost.gridloc[1]:#up
print('move up')
game=collision(pac,ghost)
ghost.left=False
ghost.right=False
ghost.up=True
ghost.down=False
elif ghost.goal_node[1]>ghost.gridloc[1]:#down
print('move down')
game=collision(pac,ghost)
ghost.left=False
ghost.right=False
ghost.up=False
ghost.down=True
You are asking a few different questions here. I'll answer here to what I think you're trying to ask: Is there a way to check if an entire sprite has crossed a boundary, instead of just the top-left corner?. So, my answer (note this will only work if your boundary line is linear): You need to check each of the corners individually, then, if all of them have returned True, then you move on. Example:
def collision(sprite1, boundary):
def internal_collision(point, boundary):
... # The actual math happens here, returns True/False
corners = []
for h in [0, 1]:
for j in [0, 1]:
corners.append([sprite1.rect.x+(h*sprite1.rect.width),
sprite1.rect.y+(j*sprite1.rect.height)])
corner_check = []
for corner in corners:
corner_check.append(internal_collision(corner, boundary))
return all(corner_check)
I don't know how your code works, so I've tried to keep this as malleable and understandable as possible, so you can re-implement it in your own code.

why does this code only register certain collisions and not all?

Hi i have a code for a game where there are multiple fruits falling from the sky and the frog at the bottom has to try and catch them. When he catches one the score goes up. This only happens when the frog collides with some of the fruit and not all of them. And the score randomly starts increasing unstoppably for no reason after a certain point. Here is most of the code as im not sure where the error would be :
import pygame, sys, time, random
from pygame.locals import *
from math import fabs
######### constants ##########
jumpvel=20
fallingspeed=1
running= True
blue= [129,183 ,253]
pink=[255,174,201]
textcolour= [255,255,255]
x=700//2
y=1000//2
score=0
thingylist= ['fruit1.bmp','fruit2.bmp','fruit3.bmp','fruit4.bmp','fruit5.bmp','fruit1.bmp','fruit2.bmp','fruit3.bmp','fruit4.bmp','fruit5.bmp','naughty1.bmp','naughty2.bmp','naughty3.bmp',]
all_things=[]
for i in range (12):
new_thing_image=pygame.image.load(thingylist[(random.randrange(0,12))])
new_thing_image.set_colorkey(pink)
new_thing_rect=new_thing_image.get_rect()
new_thing_rect.x=random.randrange(0,950)
new_thing_rect.y=-random.randrange(50,500)
all_things.append([new_thing_image,new_thing_rect])
def checkCollision (frog_rect,all_things,score):
collides_with=None
for thing_image, thing_rect in all_things:
if frog_rect.colliderect(thing_rect):
collides_with=True
if collides_with == True:
score= score+100
return collides_with,score
######## initialising screen#########
pygame.init()
gamedisplay=pygame.display.set_mode((1000,600)) #making the screen
pygame.display.set_caption('frog')
clock=pygame.time.Clock()# frames per second
bg=pygame.image.load('actual clouds.bmp').convert()
############ initialising sprites##############
frog= pygame.image.load('actual frog.bmp')
frog.set_colorkey(blue)
frog_rect=frog.get_rect()
frog_rect.centerx=(x)
frog_rect.centery=(y)
##########drawing things#############
def drawThings (all_things):
for item in all_things:
new_thing_image, new_thing_rect= item
gamedisplay.blit(new_thing_image, (new_thing_rect.x, new_thing_rect.y))
#########update display function###########
def update(x,y,all_things,score):
gamedisplay.blit(bg,[0,0])
gamedisplay.blit(frog,(x,y))
for thing in range (len(all_things)):
new_thing_rect=all_things[i][1]
#thing_rect.y=thing_rect.y+fallingspeed
new_thing_rect.y+= fallingspeed
drawThings(all_things)
label=font.render("score "+ str(score) ,1,textcolour)
gamedisplay.blit(label,(750,10))
gamedisplay.blit(heart1,(750,50))
gamedisplay.blit(heart2,(850,50))
gamedisplay.blit(heart2,(800,50))
pygame.display.update()
pygame.time.delay(50)
while running == True:
gamedisplay.blit(bg,[0,0])
gamedisplay.blit(frog,(x,y))
drawThings(all_things)
label=font.render("score "+ str(score) ,1,textcolour)
gamedisplay.blit(label,(750,10))
pygame.display.flip()
pygame.event.pump()
key=pygame.key.get_pressed()
for item in all_things:
new_thing_image, new_thing_rect= item
new_thing_rect.y+= fallingspeed
if new_thing_rect.y >450:
new_thing_rect.x=random.randrange(0,950)
new_thing_rect.y=-random.randrange(50,500)
############collision detection##########
detect,score =checkCollision (frog_rect, all_things,score)
update(x,y,all_things,score)
The score should increase every time it collides with any of the falling friuts not just certain ones and not just start increasing randomly non-stop. Any help would be appreciated thankyou !
Based on the code snippet - which does not include the frog-position updating code, I would guess that the problems of:
Score randomly increasing
Collisions not always working
Are caused by the collision rectangle being defined once for the frog's starting position, but then never being updated with changes in the frog's position.
############ initialising sprites##############
frog= pygame.image.load('actual frog.bmp')
frog.set_colorkey(blue)
frog_rect=frog.get_rect()
frog_rect.centerx=(x) # <-- rect only ever updated here!
frog_rect.centery=(y)
This would lead to the described symptoms, since scoring-objects (fruit?) falling through the collision rectangle would add to the score (seemingly randomly), and when the frog was still over it's original position, collision would work perfectly. If the frog was partially over the rect it would somewhat work, and not at all once the frog moved away that start-position rect.
The solution to the problem is to update the co-ordinates of the rectangle frog_rect whenever the x & y of the frog's position is updated. This can be achieved by setting the frog_rect.centerx and frog_rect.centery inside the update() function:
def update(x, y, all_things, score ):
frog_rect.x = x
frog_rect.y = y
The frog's rect is initialised with .centerx, .centery co-ordinates, but the frog is draw at x,y, so the collision rect begins a little off-centre too. Thus it would be best to update the initialisation function as well:
frog_rect=frog.get_rect()
frog_rect.x = x
frog_rect.y = y

How to make the turtle draw one step at a time

I have a hangman game, im suppose to draw one part of the hangman every wrong letter, but I do not know how to draw one step of the turtle at a time
That's what I have:
def drawsturtle(userInput,word):
then the steps I have to make:
import turtle
hangman = turtle.Turtle()
hangman.circle(45)
hangman.right(90)
hangman.forward(150)
....
How can I write the code so that every userInput not in word one of those steps gets drawn?
Thanks everyone
If you defined a count variable to track the number of incorrect guesses, you could define a function to draw out the required parts. In the example below I assumed 3 incorrect guesses. I've added a 3 second pause so you can see the output.
import turtle, time
hangman = turtle.Turtle()
def draw_hangman(count):
if count >= 1:
hangman.circle(45)
if count >= 2:
hangman.right(90)
if count == 3:
hangman.forward(150)
time.sleep(3)
draw_hangman(3)
Once you have code to detect that an incorrect letter has been guessed, you can call the turtle.write method to write out the incorrect letter.
Refer to the method signature below:
turtle.write(arg, move=False, align="left", font=("Arial", 8, "normal"))
Parameters:
arg – object to be written to the TurtleScreen
move – True/False
align – one of the strings “left”, “center” or right”
font – a triple (fontname, fontsize, fonttype)
Write text - the string representation of arg - at the current turtle position according to align (“left”, “center” or right”) and with the given font. If move is true, the pen is moved to the bottom-right corner of the text. By default, move is False.
For more details:
https://docs.python.org/2.7/library/turtle.html

Categories

Resources