While loop for starting the game again - python

I designed a game for tic tac toe.
The game runs well.
However, I can not use the while loop to start the game over again at the very end with the Main. It starts with the board that breaks from the previous game intead of starting from a new and clean board.
Could anyone take a look at it?
Thank you all in advance.
"""
Tic Tac Toe Helper: provides two functions to be used for a game of Tic Tac Toe
1) Check for winner: determines the current state of the board
2) Print ugly board: prints out a tic tac toe board in a basic format
"""
# Given a tic, tac, toe board determine if there is a winner
# Function inputs:
# board_list: an array of 9 strings representing the tic tac toe board
# move_counter: an integer representing the number of moves that have
been made
# Returns a string:
# 'x' if x won
# 'o' if o won
# 'n' if no one wins
# 's' if there is a stalemate
board_list=["0","1","2","3","4","5","6","7","8"]
move_counter=0
def checkForWinner(board_list, move_counter):
j = 0
for i in range(0, 9, 3):
# Check for 3 in a row
if board_list[i] == board_list[i+1] == board_list[i+2]:
return board_list[i]
# Check for 3 in a column
elif board_list[j] == board_list[j+3] == board_list[j+6]:
return board_list[j]
# Check the diagonal from the top left to the bottom right
elif board_list[0] == board_list[4] == board_list[8]:
return board_list[0]
# Check the diagonal from top right to bottom left
elif board_list[2] == board_list[4] == board_list[6]:
return board_list[2]
j += 1
# If winner was not found and board is completely filled up, return stalemate
if move_counter > 8:
return "s"
# Otherwise, 3 in a row anywhere on the board
return "n"
# Print out the tic tac toe board
# Input: list representing the tic tac toe board
# Return value: none
def printUglyBoard(board_list):
print()
counter = 0
for i in range(3):
for j in range(3):
print(board_list[counter], end=" ")
counter += 1
print()
#check if the move is valid
def isValidMove(board_list,spot):
#only the input in the range of [0:8}, not occupied by x or o is valid
if 0<= spot <= 8 and board_list[spot]!='x' and board_list[spot]!='o':
print("True")
return True
else:
print("Invaild. Enter another value.")
return False
#update the board with the input
def updateBoard(board_list,spot,playerLetter):
result=isValidMove(board_list,spot,)
if result==True and playerLetter=='x':
board_list[spot]='x'
playerLetter='o'
elif result==True and playerLetter=='o':
board_list[spot]='o'
playerLetter='x'
print(board_list)
print(playerLetter)
printUglyBoard(board_list)
return playerLetter
def play():
#use the print function to show the board
printUglyBoard(board_list)
playerLetter='x'
move_counter=0
#while loop for keeping the player inputting till a valid one and switch player after a valid input is recorded
while True:
if playerLetter=='x':
spot = int(input('Player x,enter the value:'))
elif playerLetter=='o':
spot = int(input('Player o,enter the value:'))
isValidMove(board_list,spot)
result=isValidMove(board_list,spot,)
#count the move for checking the winner purposes
if result==True:
move_counter+=1
playerLetter=updateBoard(board_list,spot,playerLetter)
print(move_counter)
#check the winner
winner=checkForWinner(board_list, move_counter)
#determine the winner
if winner=='o':
print('o win')
break
if winner=='x':
print('x won')
break
if winner=='s':
print("Tie")
break
def main():
print("Welcome to Tic Tac Toe!")
#while loop for the contining the game
while True:
play()
choice=input("Would you like to play another round? (y/n)")
if choice=='y'.lower():
play()
elif choice=='n'.lower():
print("Goodbye")
break
main()

An easy fix for your problem is putting the variable initialization inside of the function play() like this:
def play():
board_list=["0","1","2","3","4","5","6","7","8"]
#rest of your code...
This way every time you loop and call play you reset the board to its original position.
really board_list should not be declared outside a function (globaly) because it's only used inside play and passed to the rest of the functions that use it.
Declaring the variables a function use only inside a function makes sure they are set correctly every time and improve the codes readability by showing all the pieces in play in that function

Related

How to reduce the amount of lines needed for this logic?

I have a simple Python game where two players deal some damage to each other in turns until one player has 0 or less than 0 HP. I don't like how much I've repeated myself but I'm unsure how to fix this. I thought about using a list of players but then I'm unsure how to index the next player when it's their turn, without running into the list index out of range error.
If this kind of question is not allowed please let me know and I will remove it.
Below is my game logic to determine when a player has won. I may not have pasted in the exact code I have but it runs how it's expected locally.
def check_win_state(current_player):
if current_player.get_player_health() > 0:
return True
elif current_player.get_player_health() <= 0:
return False
def main():
player1 = player.Player("Me")
player2 = player.Player("You")
while True:
if check_win_state(player1):
take_turn(player1, player2)
else:
print(f"\n{player2.get_player_name()} ({player2.get_player_health()} HP) wins! {player1.get_player_name()} has {player1.get_player_health()} HP left.\n")
break
if check_win_state(player2):
take_turn(player2, player1)
else:
print(f"\n{player1.get_player_name()} ({player1.get_player_health()} HP) wins! {player2.get_player_name()} has {player2.get_player_health()} HP left.\n")
break
The easiest approach to reduce code duplication in situations like these is to use a secondary variable to hold the primary variable. Here, instead of having different code for player1 and player2, we instead have just one code, and use the variables current_player and opposing_player to hold player1 and player2, and swap with every iteration.
def main():
player1 = player.Player("Me")
player2 = player.Player("You")
current_player = player1
opposing_player = player2
while True:
if check_win_state(current_player):
take_turn(current_player, opposing_player)
else:
print(f"\n{opposing_player.get_player_name()} ({opposing_player.get_player_health()} HP) wins! {current_player.get_player_name()} has {current_player.get_player_health()} HP left.\n")
break
current_player, opposing_player = opposing_player, current_player
If you have more than two players, then a more extensible approach might be to have a list of players, and have an index rotate through the list with each iteration to specify which player is the 'current' one.
also, you can simplify check_win_state():
def check_win_state(current_player):
return current_player.get_player_health() > 0
because the check a > b returns a boolean anyway.
You can make the first function short using this logic:-
def check_win_state(current_player):
return current_player.get_player_health() > 0:
And now you can create another function to do the work of inside while loop
def work_in_while(pl, another_pl):
if check_win_state(pl):
take_turn(pl, another_pl)
else:
print(f"\n{another_pl.get_player_name()} ({another_pl.get_player_health()} HP) wins! {pl.get_player_name()} has {pl.get_player_health()} HP left.\n")
return "kill"
And use it like:-
while True:
if work_in_while(player1, player2) == "kill":
break
if work_in_while(player2, player1) == "kill":
break

Boolean condition is reading True, but the while loop still isn't breaking

For some reason the while loop is not breaking when the condition is met. The while loop should be checking for a players input to fill up a tic tac toe board until the variable "win" reads True.
Once the board reflects one of the winning conditions of tic tac toe, it assigns the variable "win" to True, and in turn should break out of the loop.
For some reason the loop isn't breaking, but the variable "win" is still reading True.
Can someone explain why the loop isn't breaking? I have tried rewriting the condition for the while loop to read "while win == False", but that doesn't seem to resolve the issue either.
I have included some of the functions I am using, and explained some of the simpler ones with a comment next to it.
I am using repl.it to do all of this online and not on a program on my local machine, so I think this may be part of the issue as well.
import os
board = ["#"," "," "," "," "," "," "," "," "," "]
def determine_win(marker):
# Winning Patterns:
# (1,2,3), (4,5,6), (7,8,9), (1,4,7), (2,5,8), (3,6,9), (3,5,7), (1,5,9)
if board[1]== board[2]==board[3]==marker:
return True
elif board[4]== board[5]==board[6]==marker:
return True
elif board[7]== board[8]==board[9]==marker:
return True
elif board[1]== board[4]==board[7]==marker:
return True
elif board[2]== board[5]==board[8]==marker:
return True
elif board[3]== board[6]==board[9]==marker:
return True
elif board[3]== board[5]==board[7]==marker:
return True
elif board[1]== board[5]==board[9]==marker:
return True
else:
return False
player1 = xo() # A Function that takes user input either "X" or O"
if player1 == "X":
player2 = "O"
else:
player2 = "X"
win = False
while not win:
display_board(board) # display_baord(board) takes the list "board" and uses it as input to display the tic tac toe board to the screen.
print("\nPlayer 1")
board[player_turn()] = player1
win = determine_win(player1)
print(win) # used to verify if win is changing
input() # used to pause the screen for troubleshooting
display_board(board)
print("\nPlayer 2")
board[player_turn()] = player2
win = determine_win(player2)
print(win) # used to verify if win is changing
input() # used to pause the screen for troubleshooting
print("Win Declared")
As the comment said, the reason is because while only check the condition of win when you finish the whole iteration of the loop. I would prefer the following way to make the code neater:
# win = False <-- you don't need this now
while True:
display_board(board) # display_baord(board) takes the list "board" and uses it as input to display the tic tac toe board to the screen.
print("\nPlayer 1")
board[player_turn()] = player1
if determine_win(player1):
print("Player 1 won")
break # break out of the loop
display_board(board)
print("\nPlayer 2")
board[player_turn()] = player2
if determine_win(player2):
print("Player 2 won")
break # break out of the loop
if not determine_win(player1):
display_board(board)
print("\nPlayer 2")
board[player_turn()] = player2
win = determine_win(player2)
# player 2 wins
else:
# player 1 wins
win = True
use something like this. It is same as #jasonharper and #Idlehands answered

Python: My code repeats itself after it should have finished

I am writing a rock, paper, scissors game in Python but my code doesn't work as it should. I'm new to Python so please let me know if my code isn't formatted corectly. The game runs fine, assuming you enter one of the already existing answers. However, if you enter one that is different, the code seems to loop randomly after the 'end()' function is executed.
Here is my code:
# imports needed files
from random import randint
import time
# creates a function that ends the game
def end(cpuScore,playerScore):
time.sleep(1)
cont = input("Would you like to play again? (y or n)\n")
if cont=="y":
time.sleep(1)
start()
else:
print("Well... That's a bit rude.")
# creates a function to play the game
def rps(cpuScore,playerScore,num):
# loops code 3 times (unless 'num' is different)
for x in range(num):
num-=1
# creates options
options = ["rock","paper","scissors"]
# picks a random choice for cpu
cpu = options[randint(0,2)]
# asks the player to choose
player = input("rock, paper or scissors?\n")
# why not gun?
if player=="gun":
result = "w"
elif player==cpu:
result = "d"
elif player=="rock":
if cpu=="paper":
result = "l"
if cpu=="scissors":
result = "w"
elif player=="paper":
if cpu=="scissors":
result = "l"
if cpu=="rock":
result = "w"
elif player=="scissors":
if cpu=="rock":
result = "l"
if cpu=="paper":
result = "w"
# if they choose something other than rock, paper, scissors or gun
else:
print("That's an invalid input!")
# adds one to num so that this round is not counted as one of the 3
num+=1
# plays the game again with the amount of rounds remaining
rps(cpuScore,playerScore,num)
# tells the player how they did
if result=="w":
playerScore+=1
time.sleep(1)
print("Fine! You win! Your silly " + player + " beat my " + cpu + "!!!")
if result=="l":
cpuScore+=1
time.sleep(1)
print("Ha! Sucker!! My epic " + cpu + " smashed your measly " + player + "!!!")
if result=="d":
time.sleep(1)
print("Ah! We drew by both choosing %s! Like they say, great minds think alike!" % cpu)
# announces the scores
print("You are on %s and the computer is on %s!" % (playerScore,cpuScore))
# ends the game after 3 rounds
end(cpuScore,playerScore)
# creates the funtion that sets the variables and starts the game
def start():
result=""
cont=""
cpuScore=0
playerScore=0
rps(cpuScore,playerScore,3)
# begins the game
start()
Thanks
Basically your rps function loops num times, with num = 3 initially. If the user enters an incorrect input, you call back the function, which starts the whole process again, in a new context, for num+1 times.
Thus if you answer wrong the first time you have at least six games to play: four new added and the two initial ones you didn't try to play.
My advice try first to do a program that do one and only one iteration of the rock-paper-scissor game. Adding more iteration is a simple fact of adding a global loop.

How do I eliminate the "'str' object is not callable"?

I get the following error on the code below, and have no idea what is going on. I'm very much a beginner and need some help. I am trying to make a very basic game of tic-tac-toe in the console, and am having trouble.
Traceback (most recent call last):
File "D:/Eric/Projects/Tic Tac Toe/Tic Tac Toe.py", line 68, in <module>
printboard()
File "D:/Eric/Projects/Tic Tac Toe/Tic Tac Toe.py", line 15, in printboard
print("",c[0],"|",c[1],"|",c[2],"\n-----------\n",c[3],"|",c[4],"|",c[5],"\n-----------\n",c[6],"|",c[7],"|",c[8])
TypeError: 'str' object is not callable
Code:
import random, time
#Variables
c = [str(i) for i in range(1,10)] #Cells
cpuletter = "string input"
coinoptions = ["heads", "tails"]
coinflip = random.choice(coinoptions)
play = ""
playercell = 0
#Functions
def printboard(): #Prints the gameboard with the cell variables in the spaces
print("",c[0],"|",c[1],"|",c[2],"\n-----------\n",c[3],"|",c[4],"|",c[5],"\n-----------\n",c[6],"|",c[7],"|",c[8])
return
#Introduction
print("Welcome to Tic Tac Toe. Below is the playing table.") #Welcome + Explanation
printboard()
print("The playing spaces are numbered 1 through 9.\nYou will use these numbers to refer to the places you would like to play your piece.\n")
#X or O?
playerletter = input("Would you like to be X's or O's?\n")
playerletter = playerletter.capitalize()
playerletter = playerletter[0]
while playerletter not in ["X", "O"]: #Confirm choice == "X" or "O." If not, ask again.
playerletter = input("""Sorry, that's not a valid option. Please choose "X" or "O."\n""")
playerletter = playerletter.capitalize()
playerletter = playerletter[0]
if playerletter == "X":
cpuletter = "O"
elif playerletter == "O":
cpuletter = "X"
#Who goes first?
playercoin = input("Randomizing who goes first:\nDo you choose heads or tails?\n") #Asking for "heads" or "tails"
playercoin = playercoin.lower()
while playercoin not in ["heads", "tails"]: #Confirm choice == "Heads" or "Tails." If not, ask again.
playercoin = input("""Sorry, that's not a valid option. Please choose "heads" or "tails."\n""")
playercoin = playercoin.lower()
print("...")
time.sleep(1) #Waits 1 seconds
if coinflip != playercoin: #Tells player who goes first
print("It landed " + coinflip + "! I will go first.")
play = 0
elif coinflip == playercoin:
print("It landed " + coinflip + "! You will go first.")
play = 1
#Game
input("""Ready to play? Press "Enter" to begin!\n""")
if play == 0:
random_index = random.randrange(9) #Randomly selects a cell to change to cpuletter
c[random_index] = cpuletter
print = ("Here is my move.")
printboard()
elif play == 1: #Allows player to choose cell to change to playerletter
printboard()
playercell = int(input("It's your turn. Type a cell number to choose where you play.\n"))
playercell = playercell - 1
c[playercell] = playerletter
printboard()
Edit: Forgot to say that this only happens when the computer goes first, and not the player. I fixed the print = ("Here is my move.") but am still having problems.
print = "Here is my move."
That line reassigns the builtin print function to a string. Don't do that.
Why make such a complex printing function? Format would come very handy right about now:
def printboard(): #Prints the gameboard with the cell variables in the spaces
table = \
'''
{} | {} | {}
----------
{} | {} | {}
----------
{} | {} |
'''
print(table.format(c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8]))
return
And finally to fix your error, change line 65:
move = ("Here is my move")
I ran your code, but I did not get any errors. This particular TypeError is usually caused by programmers mistakenly switching out square brackets with parentheses (therefore "calling" it as if the string was a function, which it's not.). Please check and run your code again.
EDIT: Solution: Change the variable name of print = "Here is my move." on line 65 to something else, like movePrint = "Here is my move".

Checking for a draw in Tic Tac Toe [Python]

I have this Python program that plays tic tac toe between two human players. If Player A or Player B ever wins, it is announced and the program terminates. However, if the program ends in a draw, it will keep requesting user input.
I'm not sure how to check for a draw. Does it have to be within the while loop or does it require it's own separate function?
import sys
## Define and create tic tac toe gameboard
board = range(0,9)
def show_board():
print board[0], '|', board[1], '|', board[2]
print '---------'
print board[3], '|', board[4], '|', board[5]
print '---------'
print board[6], '|', board[7], '|', board[8]
# Function used to check for winner
def line(char, box1, box2, box3):
if board[box1] == char and board[box2] == char and board[box3] == char:
return True
# Function used to automate process for checking every possible way to win
def all(char):
# Horizontal check
if line(char, 0, 1, 2):
return True
if line(char, 3, 4, 5):
return True
if line(char, 6, 7, 8):
return True
# Vertical check
if line(char, 0, 3, 6):
return True
if line(char, 1, 4, 7):
return True
if line(char, 2, 5, 8):
return True
# Diagnol check
if line(char, 0, 4, 8):
return True
if line(char, 2, 4, 6):
return True
show_board()
# Initial while loop will ask for player A input and show the board as well
# check conditions to see whether or not player A wins. If player A wins,
# the program will terminate so it does not ask for player B input after.
while True:
player_a = int(raw_input('Player A, please select a spot that is not taken \
(0-8): '))
# Checks for empty spot and places an 'X' if it exists, otherwise
# asks again.
if board[player_a] != 'X' and board[player_a] != 'O':
board[player_a] = 'X'
show_board()
# Check to see if Player A wins.
if all('X') == True:
print "Player A wins."
sys.exit()
break;
# While loop to ask for player B input and display the board as well as check
# the conditions as to whether or not player B wins. If player B wins, the
# program will terminate so it does not ask for player A input after.
while True:
player_b = int(raw_input('Player B, please select a spot that is \
not taken (0-8): '))
# Checks for empty spot and places an 'O' if it exists, otherwise
# asks again.
if board[player_b] != 'O' and board[player_b] != 'X':
board[player_b] = 'O'
# Check to see if Player B wins.
if all('O') == True:
show_board()
print "Player B wins."
sys.exit()
break;
break;
show_board()
Without extensively going through the code, I can tell you that a draw is occurs after 9 turns, and only if both Player A and B do not receive a win on that final turn. For your simple program, what I would do is create a global variable called ELAPSED_TURNS or something of the sort, that increments each time a player enters a character, and then after checking both of the players' win conditions, and if there is no win, check ELAPSED_TURNS. If it is equal to 9, then the game must be a draw.
Here's the function that does what you want.
def is_tie():
for position in board:
if isinstance(position, int):
return False
print "The game is a tie."
sys.exit()
Put a call to is_tie() directly after both while loops like this...
while True:
is_tie()
player_a = int(raw_input(...
while True:
is_tie()
player_b = int(raw_input(...
If this is a learning exercise for you I would recommend refactoring your code to find a way wrap all your logic blocks into functions and getting everything to flow without the use of sys.exit(). Doing so will increase the readability and make it easier to splice in new logic.
EDIT to your comment:
You can create an integer test function using just try / except statements like this...
def is_int(s):
try:
int(s)
return True
except ValueError:
return False
Credit here: Python: Check if a string represents an int, Without using Try/Except?
Your is_tie() function would then look like this:
def is_tie():
for position in board:
if is_int(position):
return False
print "The game is a tie."
sys.exit()

Categories

Resources