I'm trying to make a chess game, and I've encountered a problem:
I'm trying to update the display for every 'tick' by using pygame.Surface.fill(black_sc).
But as a result, it seems I'm not able to draw anything on top of the now black screen:
#GAME LOOP
while True:
screen.fill(black_sc)
#SQUARE DRAWING LOOP
s_draw_loop()
#DRAW THE PIECES
draw_pieces()
m_pos = pygame.mouse.get_pos()
for x in pygame.event.get():
if x.type == pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE] == True:
exit()
if x.type == pygame.MOUSEBUTTONDOWN:
for space in range(len(rect_database)):
if rect_database[space].collidepoint(m_pos) == True:
print(space)
pygame.display.flip()
Here's the s_draw_loop():
class s_draw_loop():
s_posx = 0
s_posy = 0
s_w = size[0] / 8
s_h = size[1] / 8
row = 0
i = 0
while i < 64:
if i % 2 == 0:
if row % 2 == 0:
current_rect = pygame.Rect(s_posx, s_posy, s_w, s_h)
screen.fill(white, current_rect)
else:
current_rect = pygame.Rect(s_posx, s_posy, s_w, s_h)
screen.fill(black,current_rect)
s_posx += s_w
i += 1
else:
if row % 2 == 0:
current_rect = pygame.Rect(s_posx, s_posy, s_w, s_h)
screen.fill(black,current_rect)
else:
current_rect = pygame.Rect(s_posx, s_posy, s_w, s_h)
screen.fill(white,current_rect)
s_posx += s_w
i += 1
if i % 8 == 0:
s_posx = 0
s_posy += s_h
row += 1
I'll spare you from the entire function drawing the pieces, unless you need it, but it's basically just using pygame.Surface.blit() to draw the pictures onto the display.
I've tried including the 'drawing functions' into the game-loop itself, but it seems to make no difference.
Here's the output when including 'screen.fill(black_sc)'
And here it is when commenting 'screen.fill(black_sc)' out
It's because s_draw_loop is a class, not a function.
The drawing code inside s_draw_loop is only executed once, before entering the game loop, when the python runtime reads the code of the class.
Calling s_draw_loop() inside your game loop does actually nothing (except creating an useless instace of that class that does nothing).
Simply change
class s_draw_loop():
...
to
def s_draw_loop():
...
so the code inside s_draw_loop gets executed every frame.
Related
So, i have been working on a snake game made in Pygame. So far, everything is fine, except for one problem: When the snake eats the fruit, the fruit (that randomly spawns) sometimes appears inside the snake's body. So, to avoid this, i made this function:
def random_fruit(body_pos):
global general_fruit_x, general_fruit_y # Fruit rectangle coordinates
while True:
general_fruit_x = randrange(window[0] // snake.w) * snake.w # (Snake is a pygame.Rect)
general_fruit_y = randrange(window[1] // snake.h) * snake.h
if len(list(filter(lambda z: body_pos == (general_fruit_x, general_fruit_y), body_pos))) > 0:
continue # If the spawning position of the fruit is the same as the snake's body, we continue the loop
else:
break # If not, we are done
set_obj_coordinates(general_fruit, general_fruit_x, general_fruit_y) # set fruit random position
And implemented it in the main game loop:
if fruit_eated:
random_ind1 = random_ind2
snake_len += 1
apple_sound.play()
random_fruit(snake_pos) # snake_pos is a list of tuples with all snake's body coordinates
for m in range(3):
snake_imgs[random_ind1][m] = img("snake_" + snake_colors[random_ind1] + str(m + 1)) # Reset snake image
random_ind2 = randint(0, 3)
if x_move < 0:
rotate_imgs(90, random_ind1)
if x_move > 0:
rotate_imgs(-90, random_ind1)
if y_move > 0:
rotate_imgs(180, random_ind1)
if y_move < 0:
pass
But it seems that the random_fruit function ignores the condition of the snake's body.
Here is the complete code: https://github.com/n4tm/PySnake/tree/main/snake
You have to check if any position of the body is equal to the new random position of the fruit:
if len(list(filter(lambda z: body_pos == (general_fruit_x, general_fruit_y), body_pos))) > 0:`
if any(pos == (general_fruit_x, general_fruit_y) for pos in body_pos):
random_fruit function:
def random_fruit(body_pos):
global general_fruit_x, general_fruit_y
while True:
general_fruit_x = randrange(window[0] // snake.w) * snake.w
general_fruit_y = randrange(window[1] // snake.h) * snake.h
if not any(pos == (general_fruit_x, general_fruit_y) for pos in body_pos):
break
set_obj_coordinates(general_fruit, general_fruit_x, general_fruit_y)
I'm writing a doodle-jump terminal game for which I need to know whether or not the player has hit an obstacle. For that I'm using the instr() function to tell me if an obstacle was hit. But it doesn't register and also during debugging it doesn't activate the if-clause. Heres the python-code to my while playing function (the weird string is the player):
def play(difficulty, resize_w, resize_h):
playing = True
current_x, current_y = 10, resize_h//2
env = create_env(resize_h, resize_w)
counter = 0
while playing:
counter += 1
stdscr.clear()
print_env(env,counter)
stdscr.refresh()
stdscr.addstr(current_y,current_x,"o/00\o")
if stdscr.instr(current_y+1, current_x,1) == "=":
stdscr.addstr(5,5,"Yes")
else:
stdscr.addstr(5,5,"NO")
stdscr.timeout(60)
inp = stdscr.getch()
if inp == curses.KEY_LEFT and current_x > 0:
current_x -= 2
move_left(current_x, current_y)
elif inp == curses.KEY_RIGHT and current_x < (resize_w-6):
current_x += 2
move_right(current_x, current_y)
stdscr.instr will return a bytes object -- bytes objects are never equal to strings (str type) in python 3+
try:
if stdscr.instr(current_y+1, current_x,1) == b"=":
I just currently finished making the game 'snake' as a practice to learn how to program, as I am new to programming for about 3 months.
Although the game is completed and runs the way I intended, I want to try to simplify my code and reduce the amount of lines as much as possible, and possibly make the script tidier as the current majority of my codes are cluster in the while loop.
Until now I haven't touched upon class objects, and I want everything in the while loop to go into individual classes that get called out from the while loop to reduce the amount of lines in it.
off-topic: by reading through the script, how else can I improve it to be run more efficiently, including simplifying some code as I may have over-complicated it?
I looked up how class object is used from w3school and other programming tutorials, but I still don't fully understand it as it only shows examples in using print. I did play around and experimented with class object examples and attempted to call them without using print, but I lack the knowledge of how to use them properly.
from graphics import *
from threading import Timer
import keyboard, random, time
# configurations
width = 400
gridHeight = width
height = 470
timer = False
game = True
score = 0
bonus = 0
x = 70
y = 30
radius = 10
length = radius * 2
playerLength = 3
poisonLength = playerLength
i = 0
k = 0
pointRadius = 5
points = False
cherryPoints = False
key = "Right"
countDown = 0
# set coordinations
cX = 90
cY = 30
coordX = [10]
coordY = [10]
while coordX[len(coordX)-1] != width-10:
cX+=20
coordX.append(cX)
while coordY[len(coordY)-1] != 390:
cY+=20
coordY.append(cY)
randomX = random.choice(coordX)
randomY = random.choice(coordY)
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
poisonRandomX = random.choice(coordX)
poisonRandomY = random.choice(coordY)
# window set up
win = GraphWin("SNAKE", width, height, autoflush = False)
win.setBackground(color_rgb(15,15,15))
# grid
lineX = 20
while lineX < width:
gridX = Line(Point(lineX,0),Point(lineX,gridHeight))
gridX.setOutline(color_rgb(25,25,25))
gridX.draw(win)
lineX += 20
lineY = 20
while lineY <= gridHeight:
gridX = Line(Point(0,lineY),Point(width,lineY))
gridX.setOutline(color_rgb(25,25,25))
gridX.draw(win)
lineY += 20
# snake banner
UI = Rectangle(Point(0,400),Point(width,height))
UI.setFill(color_rgb(102,51,0))
UI.setOutline(color_rgb(102,51,0))
UI.draw(win)
snakeTitle = Text(Point(width/2,420),"SNAKE")
snakeTitle.setTextColor("green")
snakeTitle.setSize(20)
snakeTitle.draw(win)
scoreTitle = Text(Point(320,424),"SCORE")
scoreTitle.setTextColor("white")
scoreTitle.setSize(10)
scoreTitle.draw(win)
scoreUI = Text(Point(320,435),score)
scoreUI.setTextColor("white")
scoreUI.setSize(10)
scoreUI.draw(win)
# make player
player = {}
player[0] = Rectangle(Point(x-20-radius,y-radius), Point(x-20+radius, y+radius))
player[1] = Rectangle(Point(x-40-radius,y-radius), Point(x-40+radius, y+radius))
player[2] = Rectangle(Point(x-60-radius,y-radius), Point(x-60+radius, y+radius))
# make poison
poison = {}
def main():
global timer, scoreUI, score, bonus, playerLength, poisonLength, x, y, points, cherryPoints, randomX, randomY, cherryRandomX, cherryRandomY, poisonRandomX, poisonRandomY, key, countDown, k, game
while(game==True):
# score update
scoreUI.undraw()
scoreUI = Text(Point(320,435),score)
scoreUI.setTextColor("white")
scoreUI.setSize(10)
scoreUI.draw(win)
# generating new body blocks
if len(player) < playerLength:
i+=1
player[i] = player[i-1].clone()
# body following player
player[0].undraw()
for i in range(1,len(player)):
player[len(player)-i].undraw()
player[len(player)-i] = player[len(player)-i-1].clone()
player[len(player)-i].draw(win)
# update player's head coordinate
player[0] = Rectangle(Point(x-radius,y-radius), Point(x+radius,y+radius))
player[0].setFill("green")
player[0].setWidth(2)
player[0].draw(win)
# player movement
if keyboard.is_pressed("Up") and key != "Down":
key = "Up"
elif keyboard.is_pressed("Left") and key != "Right":
key = "Left"
elif keyboard.is_pressed("Down") and key != "Up":
key = "Down"
elif keyboard.is_pressed("Right") and key != "Left":
key = "Right"
if key == "Up":
y -= length
elif key == "Left":
x -= length
elif key == "Down":
y += length
elif key == "Right":
x += length
# point
if points == False: # generates new point when eaten
point = Rectangle(Point(randomX-pointRadius,randomY-pointRadius),Point(randomX+pointRadius,randomY+pointRadius))
point.setFill("white")
point.setWidth(2)
point.draw(win)
points = True
if player[0].getCenter().getX() == point.getCenter().getX() and player[0].getCenter().getY() == point.getCenter().getY(): # when player eats the point
point.undraw()
playerLength += 1
poisonLength += 1
score += 200+bonus
randomX = random.choice(coordX)
randomY = random.choice(coordY)
for i in range(len(player)):
if (point.getCenter().getX() == player[i].getCenter().getX() and point.getCenter().getY() == player[i].getCenter().getY()) or (cherryPoints == True and cherryPoint.getCenter().getX() == point.getCenter().getX() and cherryPoint.getCenter().getY() == point.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and cherry
randomX = random.choice(coordX)
randomY = random.choice(coordY)
for i in range(len(poison)): # regenerate x and y coordinate if point shares the same coordinate to other array of poisons
if point.getCenter().getX() == poison[i].getCenter().getX() and point.getCenter().getY() == poison[i].getCenter().getY():
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
points = False
# cherry
if countDown == 150:
countDown = 0
if cherryPoints == False: # generates new cherry from countdown
cherryPoint = Rectangle(Point(cherryRandomX-pointRadius,cherryRandomY-pointRadius),Point(cherryRandomX+pointRadius,cherryRandomY+pointRadius))
cherryPoint.setFill(color_rgb(213,0,50))
cherryPoint.setWidth(2)
cherryPoint.draw(win)
cherryPoints = True
if cherryPoints == True:
for i in range(2, 6): # cherry blinks between countdown 40 to 100
if countDown == 20*i:
cherryPoint.undraw()
elif countDown == 10+(20*i):
cherryPoint.draw(win)
if countDown >= 100: # when countdown becomes 100, remove cherry and reset count down
cherryPoints = False
countDown = 0
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
if cherryPoints==True and player[0].getCenter().getX() == cherryPoint.getCenter().getX() and player[0].getCenter().getY() == cherryPoint.getCenter().getY(): # when player eats the cherry
cherryPoint.undraw()
score += 500
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
for i in range(len(player)):
if (cherryPoint.getCenter().getX() == player[i].getCenter().getX() and cherryPoint.getCenter().getY() == player[i].getCenter().getY()) or (cherryPoint.getCenter().getX() == point.getCenter().getX() and cherryPoint.getCenter().getY() == point.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and point
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
for i in range(len(poison)): # regenerate x and y coordinate if cherry shares the same coordinate to other array of poisons
if cherryPoint.getCenter().getX() == poison[i].getCenter().getX() and cherryPoint.getCenter().getY() == poison[i].getCenter().getY():
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
cherryPoints = False
# poison
if poisonLength % 5 == 0: # generates a poison block each time the player size reaches the multiple of 5
poison[k] = Rectangle(Point(poisonRandomX-pointRadius,poisonRandomY-pointRadius),Point(poisonRandomX+pointRadius,poisonRandomY+pointRadius))
poison[k].setFill("green")
poison[k].setWidth(2)
poison[k].draw(win)
poisonRandomX = random.choice(coordX)
poisonRandomY = random.choice(coordY)
for i in range(len(player)):
if (poison[k].getCenter().getX() == player[i].getCenter().getX() and poison[k].getCenter().getY() == player[i].getCenter().getY()) or (poison[k].getCenter().getX() == point.getCenter().getX() and poison[k].getCenter().getY() == point.getCenter().getY()) or (cherryPoints==True and poison[k].getCenter().getX() == cherryPoint.getCenter().getX() and poison[k].getCenter().getY() == cherryPoint.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and point and cherry
poisonRandomX = random.choice(coordX)
poisonRandomY = random.choice(coordY)
for i in range(len(poison)):
if poison[k].getCenter().getX() == poison[i].getCenter().getX() and poison[k].getCenter().getY() == poison[i].getCenter().getY(): # regenerate x and y coordinate if new poison shares the same coordinate to other array of poisons
poisonRandomX = random.choice(coordX)
poisonRandomY = random.choice(coordY)
bonus+=50
k+=1
poisonLength+=1
# game over requirements
for i in range(len(poison)): # if player touches poison
if player[0].getCenter().getX() == poison[i].getCenter().getX() and player[0].getCenter().getY() == poison[i].getCenter().getY():
game = False
for i in range(2, len(player)): # if player touches its own body or reach out of window
if (player[0].getCenter().getX() == player[i].getCenter().getX() and player[0].getCenter().getY() == player[i].getCenter().getY()) or x < 0 or x > width or y < 0 or y > gridHeight:
game = False
# FPS
update(10)
countDown += 1
# GAME OVER
gameOver = Text(Point(width/2,200), "GAME OVER")
gameOver.setTextColor("red")
gameOver.setSize(30)
gameOver.draw(win)
update()
time.sleep(2)
win.close()
main()
Ideally the result should replace each code in the while loop with individual classes outside of the function to reduce the amount of lines in the main() function and make the script easier to read.
Classes are essentially just bundles of code that contain various attributes and methods.
A Snake class might have a list of coordinates for each section of the body (the first is the head).
class Snake:
def __init__(self, x, y):
self.positions = [(x, y)]
def get_head(self):
return self.positions[0]
def move_forward(self):
self.positions.pop()
self.positions.insert(0, self.get_head()[1] + 1)
def move_backward(self):
self.positions.pop()
self.positions.insert(0, self.get_head()[1] - 1)
...
And so on. Classes, at this level, let you think of objects as concrete entities, distinct from each other but easily manipulated.
I am building a simple A.I for a "spider game" (pretty much the same concept as the snake game but the moving logic is a bit different). I am trying to implement a BFS algorithm such that the spider finds the path that leads it to the ant. The algorithm seems to work for several iterations but when I run it outside of the debugger it gets a None value inside the node_list and that makes the other methods fail (since you cannot get the next move for anything).
This is the BFS algorithm:
def BFS(spider_state, ant_state):
goal = False
count = 0
initial_node = Node(spider_state, None, 0)
node_list = [initial_node]
initial_ant_state = ant_state
while goal == False or len(node_list) == 0:
e = node_list.pop(0)
future_ant_state = initial_ant_state
for i in range(0, e.depth):
future_ant_state = get_next_ant_move(border_choice, initial_ant_state)
for move in POSSIBLE_MOVES:
count += 1
next_node = Node(None, None, None)
next_node.state = get_next_spider_move(deepcopy(e.state), move)
next_node.parent = e
next_node.depth = e.depth + 1
if next_node.state == future_ant_state:
goal = True
break
else:
node_list.append(next_node)
return node_list
Node:
class Node():
def __init__(self, state, parent, depth):
self.state = state
self.parent = parent
self.depth = depth
Spider and ant are represented as a simple list of x and y positions:
spider = [15, 35]
ant = [20, 10]
The get next move methods look like this:
def get_next_spider_move(spidy, move):
if spidy:
# check the bounds and assign the new value to spidy
spidy = spider_bounds(spidy)
# farthest right
if move == 0:
spidy[1] += 2
spidy[0] -= 1
# furhter up and right
if move == 1:
spidy[1] += 1
spidy[0] -= 2
# backwords
if move == 2:
spidy[0] += 1
# farthest left
if move == 3:
spidy[1] -= 2
spidy[0] -= 1
# furhter up and to the left
if move == 4:
spidy[1] += 1
spidy[0] -= 2
# one left
if move == 5:
spidy[1] -= 1
# one right
if move == 6:
spidy[1] -= 1
# side right
if move == 7:
spidy[1] += 1
spidy[0] += 1
# side left
if move == 8:
spidy[1] -= 1
spidy[0] -= 1
else:
# if no valid direction was given
return spidy
else:
raise ValueError('spidy must contain an x and y position. %s', spidy, ' was found')
The resulting error when run:
File "spider_game_bfs.py", line 141, in <module>
path = BFS(spider, ant)
File "spider_game_bfs.py", line 130, in BFS
next_node.state = get_next_spider_move(deepcopy(e.state), move)
File "spider_game_bfs.py", line 100, in get_next_spider_move
raise ValueError('spidy must contain an x and y position. %s', spidy, ' was found')
ValueError: ('spidy must contain an x and y position. %s', None, ' was found')
You have a logic error at the bottom of your move function. The last complete statement is
if move == 8:
spidy[1] -= 1
spidy[0] -= 1
else:
# if no valid direction was given
return spidy
Your comment is incorrect: the else clause is executed by any move other than 8. If the move is 8, then you return None, as you've skipped the statement that returns spidy.
As the first comment mentioned, you will do better with if ... elif ... else as your logic structure. Even better than that, follow the many on-line examples for moving an item: make a list or dict of the moves, something like this:
move_dir = [
(-1, +2), # move 0
(-2, +1), # move 1
(+1, 0), # move 2
... # fill in the rest
]
if move in range(len(move_dir)):
spidy[0] += move_dir[move[0]]
spidy[1] += move_dir[move[1]]
return spidy
else:
raise ValueError ...
first time poster here.
Ill try to keep this as simple as possible. I am trying to make a game in pygame but it seems my collision detection is acting up. I have a player class that detects whether or not the player is colliding with the ground or any other objects in the environment list, and I have an enemy class that collides with anything in the same list. The enemies work out what direction they need to travel in in order to try and hit the player. There is gravity which is very likely to play a major part in the problem.
The problem is that when the 'flies' are placed in and fall to the ground, they immediately jump off screen, even though their X and Y values seem (according to logs of items on the screen) to not move at all?
As clarification, the two 'flies' are placed into a secondary list to allow for collision detection. Also, as a side note, the 'glitch' doesn't occur if there is no left and right collide detection... Thanks to anyone who can provide help :)
def collisions():
#Detection Working as intended
for fly in Fly.List:
fly_proj = pygame.sprite.spritecollide(fly, Projectile.List, True)
if len(fly_proj) > 0:
for hit in fly_proj:
fly.health -= 100
X = pygame.sprite.spritecollide(fly, current_level.object_list, False)
if len(X) == 0: #Gravity here:
if (fly.vspeed == 0):
fly.vspeed = 1
print("Gravity")
else:
fly.vspeed += 0.35
if len(X) > 1:
for hit in X:
if fly.vspeed > 0:
fly.rect.bottom = hit.rect.top +1
fly.vspeed = 0
elif fly.vspeed < 0:
fly.rect.top = hit.rect.bottom -1
elif fly.hspeed > 0:
fly.rect.right = hit.rect.left
fly.hspeed = 0
elif fly.hspeed < 0:
fly.rect.left = hit.rect.right
fly.hspeed = 0
print(len(X),framecounter, fly.vspeed, fly.hspeed)
#Kill fly if health reaches 0
if fly.health <= 0:
fly.destroy(Fly)
#Detect where the player is
if window_rect.contains(fly.rect):
fly.playersnapshot = player.rect.x
if fly.rect.x - player.rect.x >= -10:
#print("LEFTING")
fly.hspeed = -2
if fly.rect.x - player.rect.x <= 10:
fly.hspeed = 2
fly.truefalse = True
event = None
fly.rect.y += fly.vspeed
fly.rect.x += fly.hspeed
I think your if/else is incorrect.
Probably when fly touch ground you set vspeed to zero and then if/else checks hspeed and it use ground left/right to change fly left/right.
I know one method.
move fly vertically
check collision and use if/else with vspeed
move fly horizontally
check collision and use if/else with hspeed
--
EDIT:
# move horizontally only
fly.rect.x += fly.hspeed
X = pygame.sprite.spritecollide( ... )
for hit in X:
if fly.hspeed > 0:
fly.rect.right = hit.rect.left
else:
fly.rect.left = hit.rect.right
# move vertically only
fly.rect.y += fly.vspeed
X = pygame.sprite.spritecollide( ... )
for hit in X:
if fly.vspeed > 0:
fly.rect.bottom = hit.rect.top
else:
fly.rect.top = hit.rect.bottom
# on_ground = True
I found this method in source code of "Platform Jumper" on ProgramArcadeGames.com
see page with: platform_jumper.py