How to break multiple while loops - python

I'm making a program that is a game of pick up sticks. I'm still quite confused with the logic of the entire thing. My biggest problem is I have multiple nested while loops and want to end all of them. Heres my code.
x = 1
while x == 1:
sticks = int(input('How many sticks are on the tables (10 to 100): '))
if sticks not in range(10,101):
print('Invalid.')
continue
while x == 1:
print('There are',sticks,'sticks on the table.')
print('Player 1')
p1 = int(input('How many sticks do you want to remove?'))
if p1 == sticks:
print('Player one wins.')
x == 2
break
elif p1 not in range(1,4):
print('Invalid.')
continue
else:
while x == 1:
sticks -= p1
print('There are',sticks,'sticks on the table.')
print('Player 2.')
p2 = int(input('How many sticks do you want to remove?'))
if p2 == sticks:
print('Player two wins.')
x == 2
break
elif p2 not in range(1,4):
print('Invalid.')
continue
else:
sticks -= p2
My output continues to prompt player 1 and 2 for input.
I want the program to end after printing "Player _ wins".
Any helps/ tips would be greatly appreciated! Or even a simpler way to write the program.

I always find building a state machine for a multi-player turn-based game helps a lot. Because it provides a clear and easy way to decompose the logic and avoid using a lot of break and continue or even goto in nested loops.
For example, here is a state machine which has 4 states:
For each state, there's a handler function, it will decide which state to go next(even itself) based on current player, sticks and user input:
def initialize():
global sticks, state
n = int(input('How many sticks are on the tables (10 to 100): '))
if n not in range(10, 101):
print('Invalid. It should be between 10 ~ 100.')
else:
state = 'ask_player1'
sticks = n
def ask_player1():
global sticks, state
print('There are', sticks, 'sticks on the table.')
print('Player 1')
n = int(input('How many sticks do you want to remove?'))
if n not in range(1, 4):
print('Invalid. It should be between 1 ~ 4')
else:
sticks -= n
if sticks == 0:
print('Player one wins.')
state = 'end'
else:
state = 'ask_player2'
def ask_player2():
global sticks, state
print('There are', sticks, 'sticks on the table.')
print('Player 2')
n = int(input('How many sticks do you want to remove?'))
if n not in range(1, 4):
print('Invalid. It should be between 1 ~ 4')
else:
sticks -= n
if sticks == 0:
print('Player two wins.')
state = 'end'
else:
state = 'ask_player1'
state_machine = {
'initialize': initialize,
'ask_player1': ask_player1,
'ask_player2': ask_player2,
}
sticks = 0
state = 'initialize'
while state != 'end':
state_machine[state]()

Your best bet is probably to move the code into a function that returns. Multi-breaks have been proposed multiple times and have been rejected. You can put this whole code into a function that returns after "Player _ wins", so it doesn't keep running. Other alternatives are using a flag variable that is set after a player wins and raising an exception that is subsequently handled.

Related

How to only show the final result after adding or multiplying all natural numbers

It has been a week since I started to self-study python and I tried making a program that adds or multiplies all natural numbers and the problem is I want to only show the final result of all the sum or product of all natural numbers. How do I do it?
repeat = 'y'
a=0
while repeat.lower() == 'y':
result = 0
choice = 0
i=0
product = 1
num = int(input("Enter the value of n: "))
if num < 1 or num > 100 :
print('must be from 1-100 only')
repeat = input("\nDo you want to try again?Y/N\n>>> ")
continue
print('1. Sum of all natural numbers')
print('2. Product of all numbers')
choice = int(input("Enter choice: "))
if choice == 1:
while(num > 0):
result += num
num -= 1
print(' ',result)
if choice ==2:
while i<num:
i=i+1
product=product*i
print(' ', product)
repeat = input("\nDo you want to try again Y/N? \n>>> ")
while repeat.lower() == 'n':
print('\nthank you')
break
The program prints you all the numbers because the print statement is in a while loop, so it gets executed with each run of the loop. Just move the print function out of the while.
if choice == 1:
while(num > 0):
result += num
num -= 1
print(' ',result)
if choice ==2:
while i<num:
i=i+1
product=product*i
print(' ', product)
You have two problems. First, your print statements that print the results need to be un-indented by one step, so they are not PART of loop, but execute AFTER the loop. Second, you need to initialize product = 1 after the if choice == 2:. As a side note, you don't need that final while loop. After you have exited the loop, just print('Thanks') and leave it at that.
So the end of the code is:
if choice == 1:
while num > 0 :
result += num
num -= 1
print(' ',result)
if choice == 2:
product = 1
while i<num:
i=i+1
product=product*i
print(' ', product)
repeat = input("\nDo you want to try again Y/N? \n>>> ")
print('thank you\n')
I presume you'll learn pretty quickly how to do those with a for loop instead of a while loop.

Error in repeating input in a tictactoe game

I am trying to prevent the user from inputting a square that is already marked, but the for loop moves on to the next player's input without decrementing the value of i by one, so the player 1 can repeat his input. How do I fix this?
arr = [[0,0,0],[0,0,0],[0,0,0]]
grid = grid(arr)
grid.print_grid()
for i in range(9):
row = int(input("Enter the row name: "))
col = int(input("Enter the column name: "))
if(arr[row][col] == 0):
if(i%2):
arr[row][col] = 1
else:
arr[row][col] = 2
else:
print("\nThat square has already been marked! Please select another square")
i = i-1
continue
grid.print_grid()
res = grid.grid_checker()
if (res == 1):
print("\nPlayer 1 wins the game!")
break
elif(res == 2):
print("\nPlayer 2 wins the game!")
break
elif(i == 8):
print("\nThe game has ended in a draw!")
You need to store another variable to keep track of whose turn it is. You cannot modify the variable you are looping on while you are in the loop body. This means that i cannot be manipulated while you are running in the loop. Here's how I would change it.
turn = 0
while True:
row = int(input("Enter the row name: "))
col = int(input("Enter the column name: "))
if(arr[row][col] == 0):
if(i%2):
arr[row][col] = 1
turn = turn + 1
else:
arr[row][col] = 2
turn = turn + 1
else:
print("\nThat square has already been marked! Please select another square")
continue
grid.print_grid()
res = grid.grid_checker()
if (res == 1):
print("\nPlayer 1 wins the game!")
break
elif(res == 2):
print("\nPlayer 2 wins the game!")
break
elif(turn == 8):
print("\nThe game has ended in a draw!")
Here we're saving the turn number in the variable turn and only incrementing the variable when we can confirm a player has successfully completed their turn.
Why you cannot modify i: For optimisations, loops are often expanded by python before they are converted to assembly instructions. For example a simple loop like this:
for i in range(9):
print(i)
May be expanded to something like this:
i = 0
print(i)
i = 1
print(i)
# and so on, upto
i = 9
print(i)
This is done to avoid having to jump around in memory. So as you can see here, i is reassigned for every iteration of the loop. Therefore, even if you change the value of i in the body of the loop, it will simply be reassigned before the next iteration.

How can I make sure that I can print 0 instead a negative number to refer as the enemy being dead in a fighting game

I'm a fairly new programmer, but I am trying to make a fighting game code, but I have been stumped on one part, within my code I am trying to make sure that when the enemy health = 0, it will end. I can make the program end, well. But I don't want to enemy health to go under 0, it can work with me fine, but I really want to change this.
import random
gameactive = 0
x= 100
while gameactive == 0:
if x > 0:
crit = random.randint(8,9)
base = random.randint(5,6)
a = random.randint(1,10)
if a == 10:
x -= crit
print("\nITS A CRITICAL HIT!") # Dont mind this please, this chunk about critical hits.
print("\n")
print(str(x) + " Health left")
else:
x -= base
print("\n")
print(str(x) + " Health left")
else:
break
so what happens when the program is ran, it will generate numbers and decreases x using those generated numbers. But when running the program, I want the code to only limit itself 0 health and not go under.
Sorry if i horribly explained it simply put, I want a way so x will be capped at 0, and it will not print negative numbers and instead print some sample text like enemy died.
use max function like t his:
import random
gameactive = 0
x= 100
while gameactive == 0:
if x > 0:
crit = random.randint(8,9)
base = random.randint(5,6)
a = random.randint(1,10)
if a == 10:
x -= crit
print("\nITS A CRITICAL HIT!") # Dont mind this please, this chunk about critical hits.
else:
x -= base
print("\n")
print(str(max(x,0)) + " Health left")
else:
break
One way would be to change where you print "x Health left". So in the code below, at each iteration the value of x will be reported before the hit, until x goes below zero, in which case the custom message "x died" or similar can be printed, and the loop breaks.
while gameactive == 0:
if x > 0:
#report current health here, before hit
print(str(x) + " Health left")
crit = random.randint(8,9)
base = random.randint(5,6)
a = random.randint(1,10)
if a == 10:
x -= crit
print("\nITS A CRITICAL HIT!") # Dont mind this please, this chunk about critical hits.
print("\n")
else:
x -= base
print("\n")
else:
#x died
print("x died.")
break

Access user input from previous move

I am creating a simple game that takes a user input (either 0 or 1) and compares it to the computer input. Depending on the input of the user in the previous moves, the computer will take the decision to play 0 or 1.
select_difficulty = int(input("Choose the type of game (1: Easy; 2: Difficult): "))
moves = int(input("Enter the number of moves: "))
MS = 0
PS = 0
xi = 1234
throw00 = []
throw01 = []
throw10 = []
throw11 = []
The conditions are the following:
throw00 = count of the number of times the human player chose 0 given that in the previous move he chose 0
throw01 = count of the number of times the human player chose 0 given in the previous move he chose 1
throw10 = count of the number of times the human player chose 1 given that his previous move was 0 throw11 = count of the number of times the human player chose 1 given his/her previous bid was 1
the following cases may occur:
If the player's last throw was 0:
If throw10 > throw00: then the computer chooses 1
If throw10 < throw00: then the computer chooses 0
If throw10 = throw00: then the computer chooses randomly 0 or 1
If the player's last throw was 1:
If throw11 > throw01: then the computer chooses 1
If throw11 < throw01: then the computer chooses 0
If throw11 = throw01: then the computer chooses randomly 0 or 1.
I've tried to this by saving the player move in a list and then access the previous move (one turn later) from that list.
if select_difficulty == 2:
for turn in range(moves):
player_move_dif = [int(n) for n in input("Choose your move number %s (0 or 1):" % (turn+1)).split()]`
player_previous_move = player_move_dif[1]
Based on these two variables on these two variables I then append the player_move choices (0 or 1) to the respective list which is in turn used by the "computer" to choose his move
if player_move_dif == 0 and player_previous_move == 0:
throw00.append(player_move_dif)
elif player_move_dif == 0 and player_previous_move == 1:
throw01.append(player_move_dif)
elif player_move_dif == 1 and player_previous_move == 0:
throw10.append(player_move_dif)
else:
throw11.append(player_move_dif)
#define computer behavior depening on previous player move
if player_previous_move == 0:
if len(throw10) > len(throw00):
computer_move = 1
elif len(throw10) < len(throw00):
computer_move = 0
else:
computer_move,xi = linear_congruence(xi)
elif player_previous_move == 1:
if len(throw11) > len(throw01):
computer_move = 1
elif len(throw11) < len(throw01):
computer_move = 0
else:
computer_move,xi = linear_congruence(xi)
else:
computer_move,xi = linear_congruence(xi)
this does not work since I need the values from the player moves (as integers) for print statements to show how the game goes on
if player_move_dif == computer_move:
MS = MS + 1
print("player = %d machine = %d - Machine wins!" % (player_move_dif, computer_move))
print("You: %d Computer: %d" % (PS, MS))
else:
PS = PS + 1
print("player = %d machine = %d - Player wins!" % (player_move_dif, computer_move))
print("You: %d Computer: %d" % (PS, MS))
print('PLAYER: ' + '*'*PS)
print('MACHINE: ' + '*'*MS)
if turn == moves and PS > MS:
print("The game has ended, you win!")
elif PS == MS:
print("The game has ended, it is a draw!")
else:
print("the game has ended, the machine wins!")
this way this currently results in a TypeError %d format: a number is required, not list. I suspect I am on the wrong path how to solve this the way I'm trying now as I cannot find solution to define the variables the way I want to while simultaneously be able to access them later as needed.
Besides i'm not sure if the logic of the game is implemented correctly, i found some issues which led to your error.
If you want to save the player's move into a list, you have to create an empty list before the for loop, otherwise you overwrite it again and again. Also you got the problem, that there are no previous moves at the very first move, so you need a start number at first:
player_move_dif = [] #create an empty list out of your for-loop
for i, turn in enumerate(range(moves)): #use enumerate() to count the loops
if i > 0: #first move has no previous one
player_move_dif.append(input("Choose your move number %s (0 or 1):" % (turn+1)).split()))
player_previous_move = int(player_move_dif[i-1])
else:
player_move_dif.append(input("Choose your start number %s (0 or 1):" % (turn+1)).split()))
continue #no action after the first move
Then you can access the current moves by an index of your move list. In your case the last index of your list is the last move the player did:
if player_move_dif == computer_move:
MS = MS + 1
print("player = %d machine = %d - Machine wins!" % (int(player_move_dif[-1]), computer_move)) #pick the last move from play_move_dif
print("You: %d Computer: %d" % (PS, MS))
else:
PS = PS + 1
print("player = %d machine = %d - Player wins!" % (int(player_move_dif[-1]), computer_move)) #pick the last move from play_move_dif
print("You: %d Computer: %d" % (PS, MS))

Dice Game Simulation

I have this question for an assignment:
Your friend has devised a game with two players. The two players,
called A and B, take turns rolling an ordinary six-sided die, with A being
the first to roll.
The first player who rolls a six wins the game.
You and your friend do not agree about what the probability of A winning
the game is, and you therefore decide to simulate the game with a
computer.
Thus: write a Python program that performs 10 trials, each consisting of
10000 games, and for each trial prints the fraction of the games won by
player A.
This is the code I've gotten so far, it's just returning a number in and around 1667 every time. I'm mainly wondering how to differentiate between A or B winning the game.
Any help would be appreciated!
EDITED CODE
import random
def rollDie():
return random.choice([1,2,3,4,5,6])
def roll_first():
if random.choice([0,1]) == 0:
return 'A'
else:
return 'B'
def rollSim():
while True:
turn = roll_first()
numTrials = 10
numThrows = 10000
totalThrows = numTrials*numThrows
game_on = True
winsA = 0
winsB = 0
while game_on:
for n in range(numTrials):
games = 0
for i in range(numThrows):
throw = rollDie()
if turn == 'A':
if throw == 6:
winsA += 1
games += 1
break
else:
turn = 'B'
else:
if throw == 6:
winsB += 1
games += 1
break
else:
turn = 'A'
return winsA/totalThrows
The best way to achieve a clean code would be to separate in functions each one of the tasks in hand, meaning:
1. Run a play -> For each dice rolling
2. Run a game -> Alternation of plays between A and B, until the first one gets a 6 on the dice (here considering that if A gets a 6, B doesn't even need to play, as A won)
3. Run a trial -> Consisting of a specific number of plays
4. Run the main program -> Consisting of playing all the number of trials required
So, below, is one of the possible solutions (here you see that my play function already returns the result, meaning if the player won or not):
import random
def play():
won = True
keepPlaying = False
rollDice = random.choice([1,2,3,4,5,6])
if rollDice == 6:
return won
return keepPlaying
def run_game(winsA, winsB):
while True:
playA = play()
playB = play()
if playA:
winsA += 1
return winsA, winsB
elif playB:
winsB += 1
return winsA, winsB
def run_trial(numGames):
winsA = 0
winsB = 0
for i in range(numGames):
wins = run_game(winsA, winsB)
winsA = wins[0]
winsB = wins[1]
print("winsA:", winsA, "| winsB:", winsB, "| Fraction of A wins:", "{} %".format(winsA / ( winsA + winsB ) * 100))
numTrials = 10
numGames = 10000
for i in range(numTrials):
run_trial(numGames)
You really only need to count wins for player A. Since you play 10000 games per trial, if you know the number of games that A won, you know the other games must have been won by B, and A+B=10000.
You seem to decide randomly who has the first turn, but the task states that it should always be player A taking the first turn.
You can use a boolean isPlayerA to know whose turn it is. Start with isPlayerA = True and then toggle it with isPlayerA = not isPlayerA.
Here is how you could code it:
import random
def rollDie():
return random.choice([1,2,3,4,5,6])
def winFraction(): # Perform one trial of 10000 games and return fraction of wins for A
winsA = 0 # only count wins for A
for numTrow in range(10000):
isPlayerA = True # Player A always takes the first turn
throw = rollDie()
while throw != 6: # While the game is not over yet:
isPlayerA = not isPlayerA # Switch to the other player
throw = rollDie()
if isPlayerA:
winsA += 1 # Only count the wins for player A
return winsA/10000.0 # This is the fraction: we know the number of played games
def sim():
for trial in range(10): # Perform 10 trials
print(winFraction()) # Print the result of the trial
sim() # Start the simulation

Categories