Where to put closing for Pygame - python

I've made a game using Pygame module and Tkinter module using mostly messagebox part. I don't know where should I put code for closing Pygame window because I can't close the window.
for e in event.get():
if e.type == QUIT:
pygame_quit()
sys_exit()
This is the code where I need to put it:
def main(playing=True):
turn = 0
characters = sorted(['cruise', 'car', 'cap', 'derby', 'cat', 'cloud', 'tractor', 'tomato'])
for index in range(1, 5):
try:
characters.remove(settings[f'player_{index}_character'])
except ValueError:
pass
players_count = settings['players_number']
if players_count == 0:
players_count = 2
for player_index in range(1, players_count + 1):
with open('resources/players.json', 'r') as f:
players_info = load(f)
player_name = settings[f'player_{player_index}_username']
player_character = settings[f'player_{player_index}_character']
if player_name == '':
player_name = f'guest_{player_index}'
if player_character == '':
player_character = choice(characters)
characters.remove(player_character)
players_info['username'] = player_name
players_info['character'] = player_character
players[f'player_{player_index}'] = players_info
PygameClass.players_start()
while playing:
for e in event.get():
if e.type == QUIT:
pygame_quit()
sys_exit()
turn += 1
if turn > players_count:
turn = 1
player_name = players[f'player_{turn}']['username']
player_character = players[f'player_{turn}']['character']
messagebox.showinfo('Monopoly', f'It\'s {player_name}\'s turn. ({player_character.upper()})')
if players[f'player_{turn}']['in_jail']:
messagebox.showinfo('Monopoly', 'You paid $50. You are out of Jail!')
players[f'player_{turn}']['in_jail'] = False
players[f'player_{turn}']['money'] -= 50
players[f'player_{turn}']['plot_index'] = 10
PygameClass.move_player(turn)
PygameClass.money.play()
else:
move(dice(), turn)
main()
mainloop()
In the code calling move() function many more code is getting executed tho.

You are using pygame loop wrong.
def main():
# something
while playing:
for e in event.get():
if e.type == QUIT:
pygame_quit()
playing = False
main()
Mixing tkinter and pygame is a bad idea, pygame is a great module for making games and it's not that hard to learn so spend a bit more time learning it instead of mixing it with tkinter.

Related

why isn't pygame playing the audio files the correct amount of times?

Sorry if I just did something stupid, I have never used pygame before.
I'm trying to make pygame play morse code by looking at a string and playing the sound accordingly, although when I run it, it only plays the short beep 3 times and the long beep once. is this because multiple beeps are playing at the same time? can someone help, because I have no experience whatsoever with pygame.
Here is my code:
from pygame import mixer
import os
import time
CURR_DIR = os.path.dirname(os.path.realpath(__file__))
mixer.init()
l = ".... . .-.. ---"
h = list(l)
print(h)
def play(CURR_DIR, l):
for i in l:
if i == ".":
mixer.music.load(CURR_DIR + "\short beep.mp3")
mixer.music.set_volume(0.7)
mixer.music.play()
print(".")
elif i == "-":
mixer.music.load(CURR_DIR + "\long beep.mp3")
mixer.music.set_volume(0.7)
mixer.music.play()
print("-")
elif i == " ":
time.sleep(1)
print(" ")
play(CURR_DIR, l)
You have to wait until the music has finished playing with pygame.mixer.music.get_busy(). e.g.:
import os
import pygame
CURR_DIR = os.path.dirname(os.path.realpath(__file__))
pygame.mixer.init()
clock = pygame.time.Clock()
l = ".... . .-.. ---"
i = 0
pause = False
pause_end = 0
run = True
while run:
clock.tick(100)
if pause:
if pygame.time.get_ticks() > pause_end:
pause = False
elif not pygame.mixer.music.get_busy():
if i < len(l):
if l[i] == ".":
pygame.mixer.music.load(CURR_DIR + "/short beep.mp3")
pygame.mixer.music.play(0)
print(".")
elif l[i] == "-":
pygame.mixer.music.load(CURR_DIR + "/long beep.mp3")
pygame.mixer.music.play(0)
print("_")
elif l[i] == " ":
pause_end = pygame.time.get_ticks() + 1000
pause = True
print(" ")
i += 1
else:
run = False
pygame.quit()
exit()
I think I found a solution that does what you are trying to do in another SO question. It requires pygame.mixer.Channel and its set_endevent() function.
https://stackoverflow.com/a/61068408/11620108
In their example:
horn = pygame.mixer.Sound( 'car-horn2.ogg' ) # converted from MP3 to OGG
quack = pygame.mixer.Sound( 'duck-quack.ogg' )
ca_ching = pygame.mixer.Sound( 'cash-register.ogg' )
bark = pygame.mixer.Sound( 'dog-bark.ogg' )
ding = pygame.mixer.Sound( 'single-ding.ogg' )
gobble = pygame.mixer.Sound( 'turkey-gobble.ogg' )
You could assign a variable for each sound. For the pause, you can use your long beep with the volume set to 0.
Example:
morse_dot = pygame.mixer.Sound('short_beep.mp3')
morse_dash = pygame.mixer.Sound('long_beep.mp3')
morse_pause = pygame.mixer.Sound('long_beep.mp3')
morse_pause.set_volume(0)
You could then convert a morse code string into a list of the corresponding sounds by their variable name:
sound_list =[]
for c in morse_code:
if c == ".":
sound_list.append(morse_dot)
elif c == "-":
sound_list.append(morse_dash)
elif c == " ":
sound_list.append(morse_pause)
and play it using the advice found in the other SO answer.

For loop breaks whole script

I'm trying to build a Wordle clone, a word game where one gets 6 chances to guess a 5 letter word. The game itself works but when I try to show the definition of the word at the end of the game, my whole game gets stuck.
My code is this:
import pygame
import pygame.display
import sys
import random
from words import *
from PyDictionary import PyDictionary
pygame.init()
# a py file with list of words
CORRECT_WORD = WORDS
CORRECT_WORD_LIST = list(CORRECT_WORD)
word = random.choice(CORRECT_WORD_LIST)
# number of attempts, 6 in total
attempts = 0
# a list to store the previous guesses
guesses = [[]] * 6
cur_guess = []
cur_guess_str = ""
current_letter_bg_x = 110
game_result = ""
# get the meaning of the word
def get_meaning(word):
dicto = PyDictionary()
meaning = dicto.meaning(word)
if meaning == "":
meaning = "No definition found"
return meaning
meaning = get_meaning(word)
def blit_text(surface, text, pos, font):
"""Multi-line text blit from https://stackoverflow.com/a/42015712/2280890"""
def check_guess(guess):
# Check if letter is Green, Yellow or grey if not in word
# trimmed, for not being part of the problem
attempts += 1
cur_guess = []
cur_guess_str = ""
current_letter_bg_x = 110
def play_again():
SCREEN.blit(BACKGROUND, BG_RECT)
play_again_text = play_again_font.render("ENTER to Play Again? or Q to Quit!", True, "#FCFCFC")
pygame.display.update()
word_text = play_again_font.render(f"{word.upper()}", True, "#FFB90F")
SCREEN.blit(play_again_text, play_again_rect)
SCREEN.blit(word_text, word_rect)
pygame.display.flip()
def reset():
# Reset all global variables
I've trimmed/removed a bunch of lines relating to creating/drawing letters and backspace etc.
Up until this point everything works as expected. The letters, if in correct position get colored as expected.
My problem starts here. Instead of only showing the definition after 6 attempts it shows the meaning as soon as the game starts. This is the block of code that breaks the game:
for i in enumerate(guesses[attempts]):
if i == 6 and game_result is not "W":
word = random.choice(CORRECT_WORD_LIST)
meaning = get_meaning(word)
txt = f"{meaning}"
txt_pos = (40, 70)
window.blit(BACKGROUND, [-317, -300])
f"{blit_text(window, txt, txt_pos, sys_font)}", True, "#FFB90F"
pygame.display.update
If I omit that block the game runs as expected and if I leave as below, it works as expected.
Game
while running:
if game_result != "":
play_again()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
if game_result != "":
reset()
else:
if len(cur_guess_str) == 5 and cur_guess_str.lower() in WORDS:
check_guess(cur_guess)
elif event.key == pygame.K_BACKSPACE:
if len(cur_guess_str) > 0:
delete_letter()
else:
key_pressed = event.unicode.upper()
if key_pressed in "QWERTYUIOPASDFGHJKLZXCVBNM" and key_pressed != "":
if len(cur_guess_str) < 5:
new_letter()
Can someone help me understand why my logic/reasoning is flawed?
The block of code that is causing issues I insert it right after ### game, in between while running: and right before
if game_result != "":
play_again()
while True:
meaning = ""
for i in range(len(guesses[attempts])):
if i == 6 and game_result is not "W":
word = random.choice(CORRECT_WORD_LIST)
meaning = get_meaning(word)
txt = f"{meaning}"
txt_pos = (40, 70)
window.blit(BACKGROUND, [-317, -300])
f"{blit_text(window, txt, txt_pos, sys_font)}", True, "#FFB90F"
pygame.display.update()
for the code above you have put while running but you never change running so wouldn`t it be better to do while True. Also if you put while True a better way of ending the game is to just do break instead of pygame.quit() and sys.exit(). Also can I see the output that prints when the game breaks please?
Don't Work (I have edited the code and it prints because as soon as the game starts to run you have put txt=f"{meaning}" by moving it across that only happens when i == 6 and game_result is not "W". I don't know if this will work but its worth a try.)
If that doesn`t work from what I can see you don't use enumerate so I would replace it with range(len(guesses[attempts]))

How can I alternate between a Computer turn and a Player turn in a card game?

This is my first project using Pygame, and I've already taken a lot of inspiration from others' codes while trying to adapt it to my own purposes.
Game Rules:
I'm writing a card game that will be the Player vs the Computer. It is similar to Solitaire in that there are 4 play-piles that will take a sequence of cards from Ace-Queen (King being a wildcard that can be anything except a 7).
Each round starts with either the Player or Computer drawing cards to make a hand of 5.
The Player and Computer have a deck of 26 cards (the game is played with 2 standard decks), the objective is to play the top card on one of the 4 play piles and finish the deck before the other Player does.
To end a turn, a card from the hand must be thrown onto one of four bone piles, after which the next player's turn starts.
My Problem:
The Computer successfully draws a hand to make 5 cards, and then will randomly choose a card to throw on to the bone pile. The issue is that the Computer will continue to draw cards and play them onto the bone pile until the main deck is empty, at which point an error is raised (IndexError: pop from empty list).
From my understanding, the issue is that the draw_hand() function for the Computer continues to loop while the main loop runs.
My question is how to implement a code that will recognize when a card has been thrown onto a bone pile and then switch to the next player.
My code:
In the Deck class:
def draw_card(self):
return self.cards.pop()
In the Computer class:
def draw_hand(self, deck):
while len(self.hand) < self.max_hand:
self.hand.append(deck.draw_card())
return self
In the Main file:
def computer_turn():
computer.turn = True
player.turn = False
time.sleep(1)
def computer_end_turn():
pile_choice = randint(1, 5)
card_choice = randint(1, (len(computer.hand) - 1))
throw_card = computer.hand.pop(card_choice)
if pile_choice == 1:
comp_bp1.cards.append(throw_card)
elif pile_choice == 2:
comp_bp2.cards.append(throw_card)
elif pile_choice == 3:
comp_bp3.cards.append(throw_card)
elif pile_choice == 4:
comp_bp4.cards.append(throw_card)
def player_turn():
global app_running
global moving
computer.turn = False
player.turn = True
main_deck_rect = Rect(main_deck.rect)
for event in pygame.event.get():
if event.type == QUIT:
app_running = False
if event.type == MOUSEBUTTONDOWN:
if main_deck_rect.collidepoint(event.pos):
player.draw_hand(game_deck)
pygame.display.update()
Mainloop:
while app_running:
first_player()
if computer.first_turn:
if drawing == 0:
computer.draw_hand(game_deck)
computer_turn()
else:
computer_end_turn()
drawing += 1
drawing = drawing % 2
else:
player_turn()
I tried to implement different states for when the Computer is drawing a card, and when it's playing, but that still didn't work. I have a feeling that the issue is with the draw_hand() method that keeps running even after a card has been played from the hand, and I'm unsure as to how I can stop that from happening.
Any help would be greatly appreciated.
Edit:
Here is the code I wrote to determine which player goes first:
def first_player():
if comp_top_card.name == 'king' or player_top_card.name == 'king':
if comp_top_card.name == 'king':
computer.first_turn = True
player.first_turn = False
# print('computer goes first')
elif player_top_card.name == 'king':
computer.first_turn = False
player.first_turn = True
# print('player goes first')
elif comp_top_card.name and player_top_card.name == 'king':
comp_choice = randint(0, 52)
play_choice = randint(0, 52)
if comp_choice > play_choice:
computer.first_turn = True
player.first_turn = False
# print('computer goes first')
else:
computer.first_turn = False
player.first_turn = True
# print('player goes first')
else:
if comp_top_card.value > player_top_card.value:
computer.first_turn = True
player.first_turn = False
# print('computer goes first')
elif comp_top_card.value < player_top_card.value:
computer.first_turn = False
player.first_turn = True
# print('player goes first')
else:
comp_choice = randint(0, 52)
play_choice = randint(0, 52)
if comp_choice > play_choice:
computer.first_turn = True
player.first_turn = False
# print('computer goes first')
else:
computer.first_turn = False
player.first_turn = True
# print('player goes first')
(I'm sure this code if pretty messy and there's a more efficient way of doing things...but this is what worked for me hahahah)
I experimented with various alterations to the code and I got it to work the way I want by moving the first_player() function out of the main loop as that function should only be called once at the beginning of the game.
So now my code looks like this:
first_player()
while app_running:
if computer.turn:
if drawing == 0:
computer.draw_hand(game_deck)
else:
computer_turn()
check_for_turn_end()
drawing += 1
drawing = drawing % 2
else:
player_turn()

how to fix a random choice generator?

My python game isn't working, the sequences beginning with:
if int(total_time) > 10:
isn't triggering, but when I press D, C or W I am getting the 'you opened something' text though. The code there is right as far as I know, it's just not working. I used the or prevtime to allow you to do it the first time.
import random, time, pygame, sys
from pygame.locals import *
total_time = time.clock()
pygame.init()
XQR_prevtime = 0
ppayh_prevtime = 0
pu_ekaw_prevtime = 0
diff = 1
windowSurface = pygame.display.set_mode((400,400),0,32)
time.sleep(3)
XQR_awakened = False
ppayh_awakened = False
pu_ekaw_awakened = False
if int(total_time) > 10:
if int(XQR_prevtime) > (12 - diff) or int(XQR_prevtime) == 0 or XQR_awakened == True:
if XQR_awakened == True:
print("You left something open...")
time.sleep(2)
print("And a mystery came in")
time.sleep(2)
sys.exit()
if random.randint(0,diff) == 1:
print(3)
XQR_prevtime = time.clock()
door_opening.play()
XQR_awakened = True
if int(ppayh_prevtime) > (12 - diff) or int(ppayh_prevtime) == 0 or ppayh_awakened == True:
if ppayh_awakened == True:
print("You left something open...")
time.sleep(2)
print("And a friend came in")
time.sleep(2)
sys.exit()
if randint(0,diff) == 1:
print(3)
ppayh_prevtime = time.clock()
closet_opening.play()
ppayh_awakened = True
if int(pu_ekaw_prevtime) > (12 - diff) or int(pu_ekaw_prevtime) == 0 or pu_ekaw_prevtime == True:
if ekaw_up_awakened == True:
print("You left something open...")
time.sleep(2)
print("And an answer came in")
time.sleep(2)
sys.exit()
if randint(0,diff) == 1:
print(3)
pu_ekaw_prevtime = time.clock()
window_opening.play()
pu_ekaw_awakened = True
total_time never changes, so you can never reach your condition.
The line
total_time = time.clock()
assigns a numeric value (a float) to total_time. There is no reference back to the time.clock() function, the function returns just a normal float object, not a timer object.
And normal float values don't change, they are immutable. The total_time value is not going to change as you game runs.
If you want to measure elapsed time, just keep calling time.clock():
if time.clock() > 10:
You don't need to convert a float value to int here, comparisons with integers just work.

Python 3.3 pygame 1.9.2a0 (64-bit) graphics window displays 1st image but then freezes

Using Python 2.7.3 and Pygame on a classroom PC, I created a movie-quote guessing game using both the command prompt window (to interact with the user) and a graphics window (to display still .png files, such as photos from movies). The game ran successfully.
Now I want to run and enhance the game on my own Windows 7 64-bit PC. I downloaded Python version 3.3.5 and pygame-1.9.2a0.win-amd64-py3.3.exe. Then I made two changes to my game code to adjust from Python 2.7.3 to Python 3.3.5 environment: (1) deleted "raw_" from "raw_input()" commands; and (2) deleted 1st line, which instructor had told us to use so that Python 2.6 would act like later versions: "from future import division, absolute_import, print_function, unicode_literals".
Now, on my PC, the command prompt window and the audio both work fine. The pygame graphics window displays only first .png image. Top of window (next to pygame logo) immediately says "(Not Responding)". There are no error messages. Thank you for any help.
Here's the code:
# Import common modules
import pygame, pygame.mixer, os
from pygame.locals import *
# Initialize pygame, window and sound mixer
pygame.init()
screen = pygame.display.set_mode((600,450))
pygame.display.set_caption('Greatest Movie Lines')
pygame.mouse.set_visible(0)
pygame.mixer.init()
# Create and display background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))
screen.blit(background, (0,0))
# Initialize variables that will persist through entire game
gameRunning = True
roundsCompleted = 0
totalRoundsAvailable = 5
scoreSoFar = 0
quitOrContinue = 'Try another movie line? Type y or n: '
def beginGame():
titleDisplay = pygame.image.load('titleSlide.png')
titleDisplay = pygame.transform.scale(titleDisplay, (600, 450))
screen.blit(titleDisplay, (0,0))
pygame.display.flip()
sound = pygame.mixer.music.load('20fox-fanfare-w-cinemascope-ext_anewman.mp3')
pygame.mixer.music.play()
print('First, move the photo window rightwards and make this black window')
print('smaller so that you can see both windows completely (no overlap).')
print( )
doneFixingWindow = input('When done repositioning windows, hit enter here.')
howToPlay = pygame.image.load('howToPlay.png')
howToPlay = pygame.transform.scale(howToPlay, (600, 450))
screen.blit(howToPlay, (0,0))
pygame.display.flip()
print( )
print('Read the instructions at right.')
doneFixingWindow = input('Then hit enter to play!')
print( )
def endGame():
endDisplay = pygame.image.load('ending.png')
endDisplay = pygame.transform.scale(endDisplay, (600, 450))
screen.blit(endDisplay, (0,0))
pygame.display.flip()
sound = pygame.mixer.music.load('warnerbros_fanfare.mp3')
pygame.mixer.music.play()
print(' ')
print('Game over. Thank you for playing.')
raw_input('Hit enter to exit the game.')
def playRound(cumScoreLastRound,roundsDone):
# Initialize variables and constants used in the game rounds
hintUsed = False
guessOrHint = 'Would you like to (g)uess or get a(h)int first? Type g or h: '
requestGuess = 'Guess the movie line (no commas): '
noKeywordsMatched = "Sorry, your guess didn't match any keywords."
oneKeywordMatched = 'Not bad. You got one keyword right:'
twoKeywordsMatched = 'Pretty good! You got two keywords right:'
threeKeywordsMatched = 'Great! You got all three keywords:'
# Load variables specific to this round
fo = open("quoteData.csv","r")
movieData = fo.readlines()
line = movieData[roundsDone + 1]
movie = line.split(",")
droodle = pygame.image.load(movie[3])
droodle = pygame.transform.scale(droodle, (600, 450))
hint = movie[4]
keyword1 = movie[5]
keyword2 = movie[6]
keyword3 = movie[7]
answer = pygame.image.load (movie[8])
answer = pygame.transform.scale(answer, (600, 450))
# Initialize counters specific to this round
keywordMatches = 0
keyword1Yes = ' '
keyword2Yes = ' '
keyword3Yes = ' '
# Display this round's droodle
screen.blit(droodle, (0, 0))
pygame.display.flip()
print()
print('Here is the droodle portraying a famous movie line.')
# Give user option of hint before guessing
playerChoice = input(guessOrHint)
while playerChoice != 'g' and playerChoice != 'h': # Ensure valid selection
print(' ')
print('Not a valid selection')
playerChoice = input(guessOrHint)
if playerChoice == 'h': # Display hint if player chooses to see one
print(' ')
print('Hint: ',hint)
hintUsed = True
# Solicit and evaluate the player's guess
print( )
guess = str.lower(input(requestGuess))
guessParsed = guess.split() # Determine which keywords match, if any
if word == keyword1:
keyword1Yes = keyword1
keywordMatches = keywordMatches + 1
if word == keyword2:
keyword2Yes = keyword2
keywordMatches = keywordMatches + 1
if word == keyword3:
keyword3Yes = keyword3
keywordMatches = keywordMatches + 1
# Display and play the correct answer
screen.blit(answer, (0, 0))
pygame.display.flip()
if roundsDone == 0:
sound = pygame.mixer.Sound('casab.wav')
sound.play()
elif roundsDone == 1:
sound = pygame.mixer.Sound('oz6.wav')
sound.play()
elif roundsDone == 2:
sound = pygame.mixer.music.load('WaterfrontClass.mp3')
pygame.mixer.music.play()
elif roundsDone == 3:
sound = pygame.mixer.Sound('offer.wav')
sound.play()
else:
sound = pygame.mixer.Sound('gwtw.wav')
sound.play()
# Calculate score for this round and new total score
if keywordMatches == 0:
scoreThisRound = 0
if keywordMatches == 1:
scoreThisRound = 25
if keywordMatches == 2:
scoreThisRound = 50
if keywordMatches == 3:
scoreThisRound = 100
if hintUsed == True:
scoreThisRound = scoreThisRound - 20
newCumScore = cumScoreLastRound + scoreThisRound
# Display player's result, score for round, and cumulative score
print(' ')
if keywordMatches == 0:
print(noKeywordsMatched, keyword1Yes, keyword2Yes, keyword3Yes)
if keywordMatches == 1:
print(oneKeywordMatched, keyword1Yes, keyword2Yes, keyword3Yes)
if keywordMatches == 2:
print(twoKeywordsMatched, keyword1Yes, keyword2Yes, keyword3Yes)
if keywordMatches == 3:
print(threeKeywordsMatched, keyword1Yes, keyword2Yes, keyword3Yes)
print('Your score for this round is ', scoreThisRound)
print( 'Your new total score is ', newCumScore)
return newCumScore
while gameRunning:
# To begin game, display title page and instructions
if roundsCompleted == 0:
beginGame()
# Play the round
scoreSoFar = playRound(scoreSoFar,roundsCompleted)
# Check to see if any rounds left to be played
roundsCompleted = roundsCompleted + 1
if roundsCompleted == totalRoundsAvailable:
# End game if no rounds left to play
print()
input('That was our last quote. Hit enter to exit the game.')
endGame()
gameRunning = False
# Ask player whether to continue
else:
print(' ')
playerContinue = input(quitOrContinue)
while playerContinue != 'y' and playerContinue != 'n': # Ensure valid selection
print(' ')
print('Not a valid selection')
playerContinue = input(quitOrContinue)
if playerContinue == 'n': # End game if player wants to quit
endGame()
gameRunning = False
pygame.quit()
PyGame is an event-driven system. If you're not using its internal event loop to drive your game, you still need to let it take some time occasionally to process internal events, like windows being moved or resized. There's a function specifically for this: pygame.event.pump.
I think you can get your screen to be responsive if you put a call to that function in your code in a few places (perhaps just before or after you collect input on the console).

Categories