How to implement turns while still executing the main loop? - python

I am creating a trading game in Python, and want to know how to implement turns without pausing the gameloop. I know that I will have to change the way movement is implemented, but how would I do that?
Note: code can be reached here (May be old): http://pastebin.com/rZbCXk5i

This is usually done with something called a game state machine
What that is, is extremely simple. I can show you with an example.
def main_game_loop():
if state == "player_turn":
# logic for player's turn
elif state == "enemy_turn":
# logic for enemy's turn
# they can also be used for other things, such as where you are in the game
elif state == "paused":
# pause logic etc etc

Related

How do I get this input to work inside of an inherited class in Python?

So, to preface this I’m self learning python and I’m trying to build a Tic-Tac-Toe game using the command line as an interface. The issue that I have is that I can’t get the input inside one of the inherited class for the player to work (so the game itself doesn’t work aside from the 3x3 board showing up on the command line)
The section of code that I’m having issues with goes as such:
class HumanPlayer(Player):
def __init__(self, letter):
super().__init__(letter)
def get_move(self, game):
valid_square = False
val = None
while not valid_square:
square = input(self.letter + ' s turn. Input move (0-9):')
# we are going to check that this is a correct value by trying to cast
# it into an integer, and if it's not, then we will say its invalid
# if that spot is not available on the board, then we also say it's invalid
try:
val = int(square)
if val not in game.available_moves:
raise ValueError
valid_square = True # if these are successful, then oh yeah!
except ValueError:
print ('Invalid Square. Try Again. ')
return val
I’ve tried to make sure that my spacing is correct within this class, but now I’m not sure what else to do. Any help, suggestions, or the like would be appreciated since I’m learning to program in general
Thanks!
Although there is nothing wrong with an object-oriented approach, and it can be the (or at least a) right approach for many problems, it looks like your program has "classes because of classes". It's probably easier if you don't bother with the object-orientation too much at this stage and focus on the main gameplay loop.
Try to imagine how the game should progress: you start the game, you make a move, another player makes a move, this continues until the game decides either play has won and then perhaps you can start a new game. And the other player might be an "AI" (tic tac toe doesn't require much intelligence) or another live player.
Your code covers what needs to happen for a single player to enter a valid square and it appears you have another class somewhere that's a Game, an instance of which has an attribute available_moves that contains all the currently valid moves. Or perhaps that Game class is the next thing you plan to write.
The main game loop would be to ask players for a move in alternating fashion, update the game board, decide if someone has won yet, and keep doing that. And you'll need to get the whole thing started, some core routine that sets up the game and the players and gets the ball rolling.
If you have a more specific problem getting all that to work, you should post a question about that - but without a more specific problem, it's hard to provide a better answer.
Where is the code for th

Using recursion in pygame

I'm new to python and programming in general and am working on a final project for a python centric class. One of the requirements that I cant seem to figure out how to make work is to integrate recursion into our code in order to show a working knowledge. I've worked up a simple "bullet hell" style game using pygame.
My goal is that when contact is made between a bullet and an enemy, that a series of bullet sets will be launched from the player position as a sort of short-term modifier.
This code runs in the main loop whenever a bullet hits an enemy:
for i in reversed(range(len(bullets))):
for j in reversed(range(len(enemies))):
if bullets[i].collided(enemies[j].rect):
del enemies[j]
del bullets[I]
s.global_score += 100
more_bullets(10)
#print("Hit!")
#print(s.global_score)
break
The "more_bullets" function is the focus of my recursion, and calls this:
def more_bullets(n):
if(n > 0):
spawnx = sq.rect.x+10 + sq.rect.width/2 - 10
b = Square(s.red, spawnx,sq.rect.y, 10,30)
b.direction = 'N'
b.player_speed = 10
bullets.append(b)
spawnx = sq.rect.x-10 + sq.rect.width/2 - 10
b = Square(s.red, spawnx,sq.rect.y, 10,30)
b.direction = 'N'
b.player_speed = 10
bullets.append(b)
pygame.display.update()
more_bullets(n-1)
print(f"Fired x {n}")
The outcome currently is that my debug does print 10 times making me think that the recursion is functioning correctly, however only one set of bullets is firing when the collision occurs. I'm thinking that all 10 bullets are firing faster than I can register it and just stacking on the screen.
Is there an easy-to-use function that might slow down the firing of the bullets? or have messed up something more fundamentally here?
I'm thinking that all 10 bullets are firing faster than I can register it and just stacking on the screen.
You're correct.
I don't believe there's an "easy way" to do what you're asking the way you think. The recursion is immediate, meaning that the function runs 10 times right away when it's called. For it to send out a burst of staggered bullets, you'd need some kind of timer or queue or something along those lines that runs alongside your main loop, and recursion isn't really a natural fit for that. The same will go for any kind of game logic function that plays out over a period of time.
This isn't what's being asked, but here's an idea of what you could do, even though it's kind of a redundant use of recursion: add a parameter to that function that dictates the angle the bullet is being shot. Then, add a little bit to that parameter on every recursive call. That way an arc of bullets will be shot at the same time.

Tkinter "intervention" in a logic loop

So, I'm not sure the title describes my issue in a comprehensive manner.
I have made a working "loop" code in Python (even with working recursive functions) for some sort of a card game "AI". See the following pseudo-code:
def move():
if Turn == "Computer":
if Computer cannot play:
return False
else:
Do some "AI" stuff... the computer plays
Turn = "Player"
move()
elif Turn == "Player":
if Player cannot play:
return False:
else:
in = input("Select Card: ")
****So here the loop stops and waits for the input from the player
...then the input is evaluated and "played" as a player move
Turn = "Computer"
move()
while continue:
continue = move()
So if the computer can play, it plays the card, the turn changes to Player and the move function is called again, otherwise false is returned and the PC-Player back and forth turn is over. In the Player's turn the loop stops to wait for the user input from terminal and then it continues...
So far everything works, but now I want to implement a GUI for player input. I have some basic knowledge with Tkinter and so far I created buttons on screen rapresenting the player's cards. The question is how can I "stop" the loop waiting for the user to click a button (corresponding to the card - or the PASS TURN). Maybe with some sort of threading but sounds complicated... there must be an easier solution like in the "Terminal input version" that I have already working.
Thanks
You can't use a loop in a GUI, because the GUI has it's own loop that needs to run. The mainloop() in tkinter. You need to shift your planning to "event driven" programming. It sounds like in your case the event would be the user choosing a card.
def player_chooses_card():
# graphic and logic updates for player's choice
# computer turn
if Computer cannot play:
trigger graphic for end of game
else:
Do some "AI" stuff... the computer plays
# function is done; back to idle state waiting for an event
FWIW making a loop with recursion is bad style in python.
I feel your pain on the Tkinter loops. It was a hard lesson for me to learn as well. Loops such as while and for DO NOT WORK in tkinter. The way I typically solve this problem is with "root.after." Take the code in your loop and define it as a command (adding root.after(1, name of your command) like so:
def myloop():
move()
root.after(1, myloop)
Then, call it for the first time right before 'root.mainloop', like so:
<your other code>
root.after(1, myloop)
root.mainloop()
Also keep in mind that Tkinter does not make nice nice with multiprocessing/threading within mainloops, so you have to set up each process/thread as its own canvas and root.
Finally, you could set up a series of if statements and variables to separate your loop into chunks so that it stops and starts based on player input. For example,
def myloop():
if playerhasdonething == True:
move()
else:
print("waiting on player")
root.after(1, my mainloop)
I apologize if I missed the point of your question, this is how I personally would try to solve your problem

Python thread executing function twice

Atm I'm working on a game using tkinter. I'm working on algorithm that moves enemies through the game map.
I will post only relevant lines, since the code is quiet hard-to-read.
I'm using class Timer imported from threading.
2 relevant functions are:
def add_enemies(self):
enemy = self.enemies.pop(0)
if enemy in '.':
Timer(1, self.add_enemies).start()
else:
self.move_enemy(enemy,set())
if not self.game_ended and self.enemies:
Timer(1, self.add_enemies).start()
def move_enemy(self, enemy, visited):
if type(enemy) == str:
if enemy in 'm':
enemy = Mongol(self.canvas, *self.start)
visited.add((enemy.x,enemy.y))
for move in (-1,0),(0,1),(1,0),(0,-1):
pos = (enemy.x + move[0], enemy.y+move[1])
if pos in self.way and pos not in visited:
print(pos)
enemy.move(*move)
enemy.take_shot(6)
visited.add(pos)
Timer(0.5, lambda: self.move_enemy(enemy, visited)).start()
This function should move the enemy, wait 0.5 second, then create a thread in which it recursively calls itself and moves the enemy again.
It might not be the best solution the create another thread inside the thread, but all other solutions made my GUI to freeze while executing the function.
Problem is, that Timer executes function move_enemy twice. Nothing else in the code can cause this problem.
Simple print test of enemy position shows this:
It simply moves the enemy twice, and increases its position twice as shown on the picture.
This is a wild guess, but substituting the enemy object with a new enemy object with a modified location in
Timer(0.5, lambda: self.move_enemy(enemy, visited)).start() might get the inbetween coordinates ((3,1),(5,1) and others). (If you do the same thing twice with the same input, you get two identical values.)
From the output I deduce: visited.add(pos) doesn't add positions to visited hence the if pos in self.way and pos not in visited: clause doesn't filter out already tested values (hence the (2,1)(2,1) printout). Try, if .append() works better.
I tried my best :D gl hf
Judging from the output, it looks like what's actually happening is that you were expecting things to execute in this order
print
move
print
move
print
move
print
move
....
but you failed to include enough synchronization and they are instead happening in this order:
print
print
move
move
print
print
move
move
....
It's not doing anything extra: it's just doing things not in the order you were hoping for.
Problem was, that tkinter is not thread safe hence it doesn't act deterministic.
I solved the problem by using Queue. My main thread, in which tkinter runs, periodically checks wheter or not it has something to draw.

How to create a "play again" option with Pygame

I am new to programming with Python. I have been working through a tutorial book that I found, and got the game up and running, but then decided I wanted to have a "Play Again?" option at the end. I can get the game to quit out with a press of the "n" key, but cannot work out how to get the game to restart.
Here is the code I think is giving the trouble:
#player reaches treasure
if player_rectangle.colliderect(treasure_rectangle):
#display text
screen.blit(text,(screen_width/2-195,screen_height/2-25))
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_n:
exit()
elif event.key==pygame.K_y:
pygame.display.update()
I know something needs to go after the elif event, and I have tried all that I can think of. I tried to define the whole program, and call it but that stopped the whole thing running. I have looked around internet sites, but just cannot seem to come up with a answer.
Can some one help out in easy terms how to get the game to restart to the starting position when the y key is pressed? I know it has something to do with a loop, I just cannot place my finger on what.
Many thanks.
It's not entirely clear how your code is organized, so I'll be very general. Usually games are implemented with a "main loop" that handles all of the action. In "pseudo"-python:
def main_loop():
while True:
handle_next_action()
draw_screen()
if game_is_over():
break
Before you start the loop, you usually do some setup to get the game state how you want it:
def main():
setup()
main_loop()
shut_down()
Given those parts, you can reset the game by having the main loop code call setup again (it may need to be specifically designed to be runable more than once):
def main_loop():
while True:
handle_events()
draw_screen()
if game_is_over():
if play_again(): # new code here!
setup()
else:
break
You might want to split the setup code into two parts, one which only needs to be run when the program begins (to read configuration files and set up things like the window system), and a second that gets repeated for each new game (setting up the game's state).
To restart a game you normally need to reset all variables to their initial value (e.g. number of lives, score, initial position, ...).
The best way is to put all initialisations inside a procedure:
def init():
global score,lives # use these global variables
score=0
lives=3
init() # call init() to define and reset all values
while 1: # main loop
...
elif event.key==pygame.K_y:
init() # restart the game

Categories

Resources