How can I scan a every tile in a game in Python? - python

So I am trying to scan for every tile in a game ("scan" as stepping in a tile and saving its coordinates). If the player bumps against an object, then it must try to go to one tile down and keep walking. I can even know which face the player is facing.
I am using pydirectinput because pyautogui wasn't really working with my game.
This is my code so far:
def coordinates():
visited_coordinates = []
t_end = time.time() + 90
movements = []
while time.time() < t_end:
for message in generated_object(): # generated object returns a list with 4 elements
if [message[0], message[1]] in visited_coordinates:
if "left" in movements:
if "down" in movements:
if "right" in movements:
pydirectinput.press("up")
if message[3] != 4: # 4 means it is facing up
pydirectinput.press("up") # if it isn't facing up, it needs to be pressed twice
movements.append("up")
else:
pydirectinput.press("right")
if message[3] != 12: # 12 means it is facing right
pydirectinput.press("right") # same with facing right, and so on
movements.append("right")
else:
pydirectinput.press("down")
if message[3] != 0: # 0 means it is facing down
pydirectinput.press("down")
movements.append("down")
else:
pydirectinput.press("left")
if message[3] != 8: # 8 means it is facing left
pydirectinput.press("left")
movements.append("left")
continue
movements = []
if message[2] != 4: # this means it went beyond the border, and it must return
pydirectinput.press("right", presses=2)
visited_coordinates.append([message[0], message[1]])
return visited_coordinates
One (of the many) problems with this code is that it takes forever to perform an action (I am working with named pipes, so I have to read every received message). And in the same vein, this is classic spaghetti code, which we all despise.
How can I write this code to be more "readable" and more efficient?
And on the other hand, as soon as I go beyond that border, I can't really come back. I should make the player not to move left again, but I don't really know how since I've been resetting the movements list

Could put items in a dictionary, then loop through those entries.
directions = { 'up': 4, 'right': 12, 'down': 0, 'left':8 }
for direction, val in directions .items():
if direction in movements:
pydirectinput .press( direction )
if message[3] != val:
pydirectinput .press( direction )
movements .append( direction )
break ## you might want to break out of the loop here

Related

What would be the OOP way to solve this?

I am trying to solve this problem.
There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its moves, judge if this robot ends up at (0, 0) after it completes its moves.
The move sequence is represented by a string, and the character moves[i] represents its ith move. Valid moves are R (right), L (left), U (up), and D (down). If the robot returns to the origin after it finishes all of its moves, return true. Otherwise, return false.
Note: The way that the robot is "facing" is irrelevant. "R" will always make the robot move to the right once, "L" will always make it move left, etc. Also, assume that the magnitude of the robot's movement is the same for each move.
Example 1:
Input: moves = "UD"
Output: true
Explanation: The robot moves up once, and then down once. All moves have the same magnitude, so it ended up at the origin where it started. Therefore, we return true.
Example 2:
Input: moves = "LL"
Output: false
Explanation: The robot moves left twice. It ends up two "moves" to the left of the origin. We return false because it is not at the origin at the end of its moves.
Example 3:
Input: moves = "RRDD"
Output: false
Example 4:
Input: moves = "LDRRLRUULR"
Output: false
Even though I solved it using counters as well as conditionals, I would like to see how I could use Python's OOP components to solve this. How can I separate out the valid moves, which are R (right), L (left), U (up), and D (down) into separate methods for better abstraction, so that I could add more methods in future? How would the refactored code look like? This is the solution:
class Solution(object):
def judgeCircle(self, moves):
x = y = 0
for move in moves:
if move == 'U': y -= 1
elif move == 'D': y += 1
elif move == 'L': x -= 1
elif move == 'R': x += 1
return x == y == 0
Here's one way to do it by encapsulating the data and the code that acts on it — both key OOP features. Note though that using "getters" and "setters" is not "pythonic" (because they are generally unnecessary and can be added retroactively if the need ever arises for some reason).
class Solution:
class Mover:
def __init__(self, x, y):
self.x, self.y = x, y
def new_pos(self, x, y):
return x + self.x, y + self.y
WALKS = dict(U=Mover(0, -1), D=Mover(0, 1),
L=Mover(-1, 0), R=Mover(1, 0))
def judge_circle(self, moves):
x = y = 0
for id in moves:
x, y = self.WALKS[id].new_pos(x, y)
return x == y == 0
solution = Solution()
sequences = "UD", "LL", "RRDD", "LDRRLRUULR"
for moves in sequences:
print(solution.judge_circle(moves))

How to move a character in pygame without making more than one picture

I am making my first game in pygame, but when I have generated my world and try to move my character his sprite is still left in the old position. I can't blit my background every frame either as its too large and grinds pygame's performance to a halt.
I tried bliting every frame but that also slowed pygame down as well. Then I tried to make it blit around the character so it covers the old pic of my character, but I couldn't get that to work either.
def MakeTerrain():
TempY = 0
for o in range(51):
for i in range(51):
TempX = 16 * i
if TempX >= 800:
TempY = 16 * o
TempX = 0
rnd = random.randrange(10)
if rnd <= 8:
tile = Tile("Grass",[TempX,TempY])
else:
tile = Tile("Stone",[TempX,TempY])
Tiles.append(tile)
tile.BlitTile(Display)
Function for making my terrain [a.k.a world/background]
class Player():
def __init__(self,MovementSpeed=1):
self.DirectionX = 0
self.DirectionY = 0
self.PlayerPos = [0,0]
self.MovementSpeed = MovementSpeed
self.PlayerSprite = pygame.image.load("Jeffrey.png")
def BlitPlayer(self,display):
display.blit(self.PlayerSprite,(self.PlayerPos[0],self.PlayerPos[1]))
Class of my player with my blit function
class Tile():
def __init__(self,sprite,Position):
self.TilePosition = Position
self.TileSprite = pygame.image.load(sprite+".png")
def BlitTile(self,display):
display.blit(self.TileSprite,(self.TilePosition[0],self.TilePosition[1]))
Tile class
http://prntscr.com/nrtnyx
This is what happens
I think the idea of only redrawing the tiles near the player can work, but it needs to be reworked slightly.
This will require changing your position values to pygame.math.Vector2, so do that first, I would also recommend changing all instances of these values to self.pos so that the loop I will show you is easier to make.
self.pos = pygame.math.Vector2(Position)
To ascess the x and y values, they are simply self.pos.x or self.pos.y
Within the drawing code, you can first change it so it iterates through every sprite:
for sprite in my_sprites:
pass
Inside this loop, you can check the distance from the sprite to the player using these new vectors:
deltaVec = player.pos - sprite.pos # Find delta [difference] between player position and sprite position
len = deltaVec.length() # Find the length between those two points through the distance formula
Then, at the end, check to see if the new distance is under a certain threshhold:
if len < 100: # 100 being the distance, change if needed
sprite.draw()
In the end, the new drawing code would look like this:
for sprite in my_sprites:
deltaVec = player.pos - sprite.pos
len = deltaVec.length()
if len < 100: # 100 being the distance, change if needed
sprite.draw()
This should only redraw the tiles nearest to the player and thats it without having to deal with managing each tile.

Python: how to make a turtle never cross over a line

I'm trying to write a python program that will make a turtle either move forward, turn left and then move, or turn right and then move randomly without ever crossing over its own previously drawn lines. It must stay within the screen and clear and restart once it has done the most possible moves. This is as far as I got:
import turtle
positions=[]
while 'x':
count=132
for x in range (132):
count-=1
print(count)
move = randint(1,3)
if move == 1:
turtle.fd(50)
if move == 2:
turtle.rt(90)
turtle.fd(50)
if move == 3:
turtle.lt(90)
turtle.fd(50)
if turtle.xcor()>290:
turtle.rt(180)
turtle.fd(50)
if turtle.xcor()<-290:
turtle.rt(180)
turtle.fd(50)
if turtle.ycor()>240:
turtle.rt(180)
turtle.fd(50)
if turtle.ycor()<-240:
turtle.rt(180)
turtle.fd(50)
turtle.clear()
How can I make it remember its positions and not go over them? Many thanks!
Below is my attempt to solve your autonomous turtle problem. I chose a set() to track visited positions but also coerced the turtle onto a grid to make sure that only a limited set of points could be visited. I didn't like your approach of doing a 180 when you hit a wall as that just makes you retrace your path and fail -- instead my turtle tries to avoid hitting the walls.
I use an invisible turtle clone to "test the waters" to see if a move is good or bad. If there are no good moves, it gives up, resets, and starts again. You have to close the window to kill the program:
import turtle
from random import shuffle
WIDTH, HEIGHT = 600, 500
INCREMENT = 50
TURTLE_WIDTH = 20
X, Y = 0, 1
def dot_round(x, base=INCREMENT):
return int(base * round(float(x) / base))
turtle.setup(WIDTH, HEIGHT)
turtle.shape("turtle")
while True:
positions = set()
while True:
position = (dot_round(turtle.xcor()), dot_round(turtle.ycor())) # coerce position to grid
if position in positions:
break # collision with line
positions.add(position)
turtle.setposition(position) # coerce turtle back onto our grid
moves = list(range(3))
shuffle(moves)
clone = None
for move in moves:
clone = turtle.clone() # use an invisible clone to test the waters
clone.hideturtle()
clone.penup()
if move == 1:
clone.right(90)
elif move == 2:
clone.left(90)
clone.forward(INCREMENT)
position = (dot_round(clone.xcor()), dot_round(clone.ycor()))
if position[X] <= TURTLE_WIDTH//2 - WIDTH//2 or position[X] > WIDTH//2 - TURTLE_WIDTH//2:
continue # avoid collision with wall
if position[Y] <= TURTLE_WIDTH//2 - HEIGHT//2 or position[Y] > HEIGHT//2 - TURTLE_WIDTH//2:
continue # avoid collision with wall
if position not in positions:
break
else: # no break
break # accept the inevitable, there's no good move
turtle.setheading(clone.heading())
turtle.forward(INCREMENT)
turtle.reset()
# close the turtle window to get out of this program
This turtle only looks one move ahead -- he easily gets himself into jams he can't get himself out of.
This hopefully gives you some ideas how to design your own turtle automaton.

Pressing SHIFT interrupts holding down W in pygame

I am making a roguelike in Pygame. I am trying to make my character move forward with either the W or the UP key. When SHIFT is held down, movement should be twice as fast. If I hold down SHIFT and then press W, the program works as it should, however, if I am holding down W, and the character is already moving forward, and then I press SHIFT, the character stops. This happens only occasionally, but it needs to be fixed.
character_pos = [0,0]
move_speed = 1
mods = pygame.key.get_mods()
if mods & KMOD_SHIFT:
move_speed = 2
key = pygame.key.get_pressed()
if key[pygame.K_w] or key[pygame.K_UP]:
w_count += move_speed
if w_count == 20:
w_count = 0
if not is_wall(character_pos[0], character_pos[1]-1):
character_pos[1] -= 1
Any ideas?
If w_count starts at 0 and is incremented in steps of either 1 or 2, it will always pass 20 as it increases. But if it is incremented by a combination of 1s and 2s, it might go from 19 to 21. Could that be the problem? Try changing if w_count == 20 to if w_count >= 20.
Apologies if I have misunderstood your code, I'm not 100% clear on the purpose of the counter.

Fluid-like characteristics with Pygame

So I'm trying to write a little simulator thing with pygame, but im stuck
I'm trying to make particles that fall and settle but i want them to stack and stop when they hit a wall
This may sound confusing so here is an example:
http://www.coolmath-games.com/0-sugar-sugar/
I want to make particles that resemble the sugar in the game above:
I started by trying:
if pygame.sprite.spritecollideany(self, self.game.block_list):
self.rect.x += 0
self.rect.y += 0
but then the particles just stop and they don't roll down sloped surfaces
also I need to know how to check if a sprite collides with any other sprite in its group
So if anyone knows how I can duplicate this liquid like particle movement in pygame that would be awesome!
Thank You!
As I stated above, I tried to write the exactly same game with pygame too but left it uncompleted.
First of all, I preferred NOT to store these particles as different objects. Instead I used a dictionary to store their coordinates. I made it so because there are HUNDREDS of them and you have to check for collisions ~50 times per second for each of them. If you try to make them all different objects, it may go out of hand at some point.
After you detect a collision, in order to make them roll down sloped surface, let them move diagonally too. First check for the cell below, if that cell is not empty, check for the cells at bottom left and bottom right of the particle.
Btw, I found my function that moves the particles but it is not really readable.
def spawnSugar(spawnPoint) :
global sugarList,mapDict
mapDict[spawnPoint] = 1
sugarList.append(spawnPoint)
def moveAll() :
global mapDict,sugarList
sugarListTmp = sugarList
sugarList = []
for sugarX,sugarY in sugarListTmp :
# m stands for the vertical movement (1 for down, 0 for staying still)
# k stands for horizontal movement (-1 for left, +1 for right)
m = 1
if mapDict[( sugarX , (sugarY+1)%mapSizeY )]==0:
# checks whether the coordinate below this particle is empty
k = randint( -(mapDict[((sugarX-1)%mapSizeX , (sugarY+1)%mapSizeY)]==0) , mapDict[((sugarX+1)%mapSizeX , (sugarY+1)%mapSizeY)]==0 )
# If it is empty; randomly chooses 1 of the 3 coordinates below the particle (1 of them is just below and the other 2 are diagonally below)
elif mapDict[((sugarX-1)%mapSizeX,(sugarY+1)%mapSizeY)]==0 and mapDict[((sugarX-1)%mapSizeX,(sugarY)%mapSizeY)]==0 and mapDict[((sugarX+1)%mapSizeX,(sugarY+1)%mapSizeY)]==0 and mapDict[((sugarX+1)%mapSizeX,(sugarY)%mapSizeY)]==0:
# If the coordinate below the particle is not empty but other 2 diagonals are empty
k = -1 if randint(0,1) else 1 #chooses 1 of them randomly
else : # If at most 1 of these 2 diagonal coordinates are empty
k = (mapDict[((sugarX+1)%mapSizeX,(sugarY+1)%mapSizeY)]==0 and mapDict[((sugarX+1)%mapSizeX,(sugarY)%mapSizeY)]==0) or -(mapDict[((sugarX-1)%mapSizeX,(sugarY+1)%mapSizeY)]==0 and mapDict[((sugarX-1)%mapSizeX,(sugarY)%mapSizeY)]==0)
if not k: # If none of them are empty
m = 0
mapDict[(sugarX,sugarY)] = 0
mapDict[((sugarX+k)%mapSizeX,(sugarY+m)%mapSizeY)] = 1
sugarList.append(((sugarX+k)%mapSizeX,(sugarY+m)%mapSizeY))
# Values to assign before entering the main loop
mapDict = {}
sugarList = []
for x in range(mapSizeX):
for y in range(mapSizeY):
mapDict[(x,y)] = 0

Categories