How to make snake's body parts follow each other - python

I'm making a variation of the "Snake" game. Segments1 is a list containg the snake's body parts
This piece of code works fine in making the first part of the body follow the head
if len(segments1) > 0:
x = player1.xcor()
y = player1.ycor()
segments1[0].goto(x,y)
But the rest of my code, which is supposed to make the other body parts follow the first body part, just stacks the body parts on top each other
for index in range(len(segments1)):
if index > 0:
x = segments1[index-1].xcor()
y = segments1[index-1].ycor()
segments1[index].goto(x,y)
Image shows snake after eating two pieces of food, black being the head, one grey body part a little behind it and another grey body part stacked on top

You have the wrong algorithm. Since each body part follows the one in front, then the net effect is to simply make the end segment go away.
segments[-1].del

The concept is that each segment is set the to prior segment's location. I guess the assumption is that player1 is the head, but conceptually the head could also be the "first" segment and in that case prior_position would be set to where ever the head was expected next.
prior_position = (player1.xcor(), player1.ycor())
for segment in segments1:
current_position = (segment.xcor(), segment.ycor())
segment.goto(**prior_position)
prior_position = current_position

Related

Optimising pygame

For my project in AH Computing I'm recreating my version of Nidhogg. Everything runs smoothly until the blood starts spraying. Im not too sure how i can make the code more efficient as i am still fairly new to python.
This is the class for the the spraying blood:
class bloodmaker():
def __init__(self,xv,yv,colour,x,y):
self.gravity = 2
self.air_resistance = 0.25
self.xv = xv
self.yv = yv
self.x = x
self.y = y
self.pastx = 0
self.pasty = 0
self.colour = colour
def move(self):
if self.y < 400:
self.pastx = self.x
self.pasty = self.y
#so that it doesnt curve backwards
if self.xv > 0:
self.xv -= self.air_resistance
self.yv += self.gravity
self.x += self.xv
self.y += self.yv
#so that the drawn line doesnt go over the edge
if self.y > 400:
self.y = 400
if self.colour is "o":
py.draw.line(screen, (255, 165, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
py.draw.line(screen, (255, 255, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
global bloodgrid
try:
#the bloodgrid are squares 5 pixels wide, covering the bottom section, so we we divide by 5 to find where to put the blood
bloodgrid[int(self.x/5)].insert(0,self.colour)
except:
pass
#deleting this object as it is no longer required
return True
[Here is an image of the blood spraying][1]
(excuse the incomplete sprite)
[1]: https://i.stack.imgur.com/hXiAa.png
Underneath there is a floor of blood that works using an array of stacks, which is added above code when it the blood reaches the floor.
bloodgrid = [[] for x in range(512)]
Here is the code for destroying the flying blood object and blitzing the blood floor to the screen.
def blood():
for i in range(len(bloodarray)):
killyourself = bloodarray[i].move()
if killyourself is True:
kill.append(i)
#appends to a kill list as if i popped here the list would get shorter while the for loop stays the same giving an out of index error
for i in range(len(kill)):
bloodarray.pop(kill[0]-i)
kill.pop(0)
#printing the entire bloodgrid
for i in range(512):
for ii in range(len(bloodgrid[i])):
try:
if bloodgrid[i][ii] is "o":
py.draw.rect(screen, (255, 165, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
else:
py.draw.rect(screen, (255, 255, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
except:
pass
I don't think it is possible to only update parts of the screen as the camera moves about and the blood on the floor moves too.
As more blood accumulates on the floor the game framerate starts to drop and it gets especially choppy when the blood sprays. Is there any ways I could make this more efficient? I don't want to hand in a choppy game but I really like how the blood looks. Thanks.
A couple of points that are hopefully helpful.
(This isn't directly related to speeding up your code, but...) Try to get away from using global variables. Just pass them into your functions. Several of the ones you posted are difficult to figure out because you are accessing variables that are outside the scope of the function.
First reason things are bogging down here: You are using insert() to insert new elements at the start of a list, and you are doing that inside of a loop where you call the move() function. Inserting anywhere into a list (except the end) is horribly slow for reasons beyond the scope of this post. You should consider putting a deque from collections in there, which can be inserted front or back efficiently. If that is daunting, you could also "do a little math" and insert at the end of your list and draw it backwards by counting down from the last element, which is also efficient.
Also in the same notion, as mentioned in the comments, poping things out of the middle of lists or arrays is also horribly slow. Figure out a different way or look at a different data structure. It is tough to give advice on this point because bloodarray is not defined in your code.
Basically: things are bogging down because you are choosing the wrong data structures. tweak a couple of them and you'll get better results.

Optimize For loop for speed python

Like pointed out, let me explain a bit of the code. I have 2 for loops because i have a board of a game called Tablut. I have to check from the pawn position (row_index and column_index) if there is an obstacle on the left and on the right. So, the first loop take the row_index of the pawn, then check for every row on the right side and stop when it finds an obstacle. Then do the same for the left side. If i use a single loop i don't have a way to stop searching in one direction if found an obstacle and start looking in the other, because the break will stop the entire for. The loop are used to generate all the possible actions that a move can do inside the board in that turn. Action(Position(....)) is one action that the pawn can make.
So, basically, i check in the right and left direction, if the next tile is empty i can move there, so i create a new action and add it to a list of all the possible moves, if there is an obstacle, since i can't jump it, i stop the loop.
i have this for loop and i would like to optimize it for speed.
for row in range(row_index - 1, -1, -1):
if bitboard_util.get_bit(obstacle_bitboard, row, column_index) != 1:
action = Action(Position(row_index, column_index), Position(row, column_index), color)
all_available_moves_for_pawn.append(action)
else:
break
for row in range(row_index + 1, 9):
if bitboard_util.get_bit(obstacle_bitboard, row, column_index) != 1:
action = Action(Position(row_index, column_index), Position(row, column_index), color)
all_available_moves_for_pawn.append(action)
else:
break
You could optimize this a little by preparing a structure based on your obstacle bits that will give the range of available "moves" for each position. You can prepare that structure once and reuse it multiple times to obtain the move lists directly for all column_index/row_index afterward.
from itertools import accumulate
rowCount = 9
colCount = 9
obstacleRanges = []
for col in range(colCount):
obstacles = [-1] + [ row*(bitboard_util.get_bit(obstacle_bitboard, row, col) == 1) for row in range(rowCount) ] + [rowCount]
prevObstacle = [*accumulate(obstacles,max)]
nextObstacle = [*accumulate(obstacles[::-1],lambda p,o:[p,o][o>0])][::-1]
obstacleRanges.append([*zip(prevObstacle[1:-1],nextObstacle[1:-1])])
obstacleRanges will contain tuples with the position (row) of the previous and next obstacles for every position on the board. This is computed once for the whole board and allows you to find the range of moves directly without additional calls to get_bit
usage:
prevObstacle,nextObstacle = obstacleRanges[column_index,row_index]
for moveRow in range(prevObstacle+1, nextObstacle):
action = Action(Position(row_index, column_index), Position(moveRow, column_index), color)
all_available_moves_for_pawn.append(action)
Note that this may or may not improve performance depending on the distribution and number of you pawns and also on how many times you query the actions with the same obstacle layout. For example, if your pawns ARE the obstacles and they are moving, it would be preferable to update the structure with the effect of pawn movements rather than recalculate it completely.

how to sense if a character is touching a colour

i am making a game and i need to be able to sense if a character is touching a tree or not and i can't because the tree is part of the background.
i've already tried using x and y co-ordinates but the trees aren't in a specific clump so this doesn't work. i also tried using a piece of code - look below - that detects if something is touching something else, however, the thing has to be defined and i can't define the tree.
def tree():
tree = #brown
if sprite_x < 0 + sprite_width and sprite_x + tree_width > 0 and sprite_y < 0 + sprite_height and sprite_height + sprite_y > tree_height :
#do stuff
i expect the code to sense if the character is touching a brown colour (the tree) and to allow me to use that output.

Simple Graphics library not coloring lines

I'm a student learning to program, and my current assignment is to write a graphing calculator using the Simple Graphics library. I got everything working, except for coloring the lines. The first line should be red, the second should be green and the third one blue, then they repeat. Here is the code I added for the colors:
if count % 3 == 1:
print("red")
setColor = ("red")
elif count % 3 == 2:
print("green")
setColor = ("green")
else:
print("blue")
setColor = ("blue")
Earlier in the code, I set count = 1 and at the end of my drawing loop, I have count = count + 1.
Whenever I try to use the program, all the lines appear black. When I look at the terminal, I see "red", "green" and "blue" all being printed successfully at the right times. Using RGB values to define the color doesn't help either.
Does anyone have any idea of what I could be doing wrong? I can post the entire code for drawing the lines, but I thought people wouldn't want to sift through 30 lines.
setColor = ("color") should just be setColor("color"). setColor is a function, that takes in input and performs an action. If it was a variable, which is just a name for some data that you will provide, your code would be correct. (Variables almost never have verbs in their names.)

How to conceptually implement brute-force / tree traversal?

As a beginner programmer, I don't know how to conceptually think about brute-forcing. My mind can't really fathom how to write code that will try every possibility. My answer is recursion, but as you will see below, I have failed.
If you need the full source
I have a problem that I want to solve. Here is a code snippet (there are other functions, but no reason to include them here, they just do background work):
def initiate(seen):
step_segment = []
STATS = [250,0,0,0,13,0]
if len(seen) >= 256: # when to escape the recursion
return seen
while (len(step_segment)) < 128:
step_segment, STATS = take_step(step_segment, STATS)
if STATS[5] == "B": # if there is a battle
if STATS[0] in seen: # if battle has been done before
status = seen.index(STATS[0]) # get battle status:
status += 1 # which is next to step_id (= G or B)
if seen[status] == "B": # for seen battles, try Glitch ("G")
step_segment = do_glitch(step_segment, STATS)
else:
step_segment, STATS = do_fight(step_segment, STATS) # fight
seen = seen + [STATS[0],STATS[5]]
time = get_frames(step_segment)
print "\nTime:", time
print seen
return initiate(seen)
The goal: I want to produce a list of every possible decision through a segment, along with how long it takes.
Description:
I will take a step (There's only one direction: forward). Every time I take a step, my stats are updated. This takes a step and updates the stats: step_segment, STATS = take_step(step_segment, STATS)
A list of steps taken, along with the stats, are kept in
step_segment. This is so I can 'undo' an arbitrary amount of
steps, if I want. To undo a step call the function:
step_segment, STATS = undo_step(step_segment, STATS)
I can see how long my current route has taken by doing: time = frames(step_segment).
At some point, I will get into a Battle. I get into a battle when
STATS[5] == "B"
When there is a battle I have to make a decision, I simply have two choices: i. Fight the
battle (B), or, ii. Run away glitch (G).
If I want to Fight, I do: step_segment = do_fight(step_segment, STATS). This also records that I chose to fight, along with the stats, in step_segment. (So I can undo it, if i want).
If I want to Run Away Glitch, I do: step_segment = do_glitch(step_segment,STATS).
I want to see every possible combination of Glitch & Fight (the only two choices, when I reach a battle).
At the moment, my code is very bad and does not try all of the possibilities. I don't really know how to code for all possibilities.
So, that's why I'm here. How can I implement a way of trying all possibilities when facing a decision?
I understand the problem has exponential amount of possibilities, but thankfully the maximum number is pretty small (<1024).
I have read about tree traversal, but I have no idea how my problem can be put into that format. Eg, I don't know what a 'node' would be in my problem. I actually don't know what anything would be... That's why I'm asking.

Categories

Resources