Problems with Tic Tac Toe Game - python

Im making a Tic Tac Toe game but I have met a couple of problems while doing it.
The firs problem, is that when I had the board like:
X | - | X
or like:
- | X | X
It counts as a Win and I don't know why. If I put:
X | X | -
there's no problem with that.
The second problem, is that I want to prevent the user to select a position that is already in use, so my idea is to add to a list the position that has been selected and compare if it is already in it or not. The problem is that when I call the function for the second time it doesn't have the previus list that contain the information of the previus position (it makes a new one), therefore it never detects that the position that the user is giving is already in use.
board = ["-","-","-",
"-","-","-",
"-","-","-"]
def display():
print(board[0] + " | " + board[1] + " | " + board[2])
print(board[3] + " | " + board[4] + " | " + board[5])
print(board[6] + " | " + board[7] + " | " + board[8])
def turn():
exist=[]
pos = int(input("Choose your position"))
while True:
if pos not in exist:
exist.append(pos)
print(exist)
print("Correct position")
break
else:
print("That position has been selected, choose another one")
board[pos-1] = "X"
display()
def checkwin():
#row
if (board[0] and board[1] and board[2] == "X") or (board[3] and board[4] and board[5] == "X") or (board[6] and board[7] and board[8] == "X"):
print("Victory")
return False
else:
return True
display()
while checkwin():
turn()
checkwin()
print(board)
print("Game has ended")
NOTE: Right now the game only can have the X player, I need still to add the O player. Also, it only can detect the win for the rows. Im still working on this.

To your second question, you are declaring your list inside the function turn, which is called every time a player makes a choice. When a function returns, all of its local variables are no longer accessible. Compare this with your code.
def addToList( item ):
items = []
items.append( item )
addToList( 'item1' )
addToList( 'item2' )
Each time the function is called, items = [] is called as well. Edit to clarify my first statement, functions create their own scope. The scope of a variable is where it is available in your program. For example
def addItem( item ):
items = []
items.append( item )
addItem( "item1" )
print( items )
ERROR: items is not defined

The logic of (board[0] and board[1] and board[2] == "X") won't work because you are only checking is board[2] == 'X', the others are just truthy, thus '-' will also evaluate to True
You should rather check like this (board[0] == board[1] == board[2] == "X").
This ensures that all values are equal to 'X'

sin tribu nailed it as to why your turn function is behaving the way it is.
As to how to make it work right... perhaps I'm missing something, but aren't you looking to make sure you can't select a space that's already occupied? For that, you could just check the board directly.
def turn():
pos = int(input("Choose your position"))
while board[pos-1] == "X":
pos = int(input("That position has been selected, choose another one"))
print("Correct position")
board[pos-1] = "X"
display()
This will keep asking until you specify an empty square.

Related

Recursive Minimax Function for Tic Tac Toe

I have recently being try to make a tic tac toe game using the mini max algorithm. I first created a board, then two player. Afterwards, I changed one of the players into the algorithm. I tried using something similar to this javascript implementation. I am not getting an syntax error, just the algorithm is not working.
For example, take the following game sequence.
The algorithm starts the game and places an "X" at the top right of the board or in index[0].
I, the player place "O" in the top right of the board or in index[2].
The algorithm places an "X" at the top center of the board or in index [1].
I, the player place "O" in the middle center of the board or in index[4].
The algorithm places an "X" at the middle left of the board or in index [3].
The error is that rather than stopping the win, the algorithm is playing the next free position.
In the min-max algorithm, this is a minimizing agent (seeks the least score) and the maximizing agent (the higher score) (in this case the AI). Below is the code, can you help me find the issue? Or how should I go along? Because I have been trying for the last two days. Hopefully the explanation above made sense.
board = ["."] * 9
winning_comb = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[3,4,6],[0,4,8]]
game = True
def new_board():
print (board[0] + "|" + board[1] + "|" + board[2])
print (board[3] + "|" + board[4] + "|" + board[5])
print (board[6] + "|" + board[7] + "|" + board[8])
new_board()
def winning(comb):
global game
for l in range(len(winning_comb)):
a = winning_comb[l][0]
f = winning_comb[l][1]
v = winning_comb[l][2]
if comb[a] == comb[f] == comb[v] == "O" or "x" == comb[a] == comb[f] == comb[v]:
game = False
if comb[a] == "x":
return 1
else:
return -1
break
else:
game = True
def minmax(board,depth, ismax):
if winning(board) != None:
h = winning(board)
return h
else:
if ismax == True:
bestscore = float('-inf')
for k in range(len(board)):
if board[k] == ".":
board[k] = "x"
score = minmax(board,depth+1,False)
board[k] = '.'
bestscore = max(bestscore, score)
return bestscore
else:
bestscore = float('inf')
for k in range(len(board)):
if board[k] == ".":
board[k] = "O"
score = minmax(board,depth+1,True)
board[k] = '.'
bestscore = min(bestscore,score)
return bestscore
def player1() :
bestscore = float('-inf')
bestmove = 0
for k in range(len(board)):
if board[k] == ".":
board[k] = "x"
score = minmax(board, 0, False)
board[k] = "."
if score > bestscore:
bestscore = score
bestmove = k
board[bestmove] = "x"
new_board()
def player2():
number = int(input("Please enter your poistion?") )
board[number - 1 ] = "O"
new_board()
winning(board)
while game==True:
player1()
player2()
[3,4,6] shouldn't be a winning combination. It should be [2,4,6]
Some issues:
[3,4,6] should be [2,4,6]
Indentation of return bestscore is wrong in the second instance: currently it interrupts the loop.
If you backtrack after game = False is executed, that assignment should be undone. For that reason it is probably easier to not use that variable at all, and just call the function winning when needed. The main loop could then be:
while winning(board) is None:
player1()
if winning(board) is not None: # need to exit
break
player2()
bestscore will be (-)infinity when there is no more free cell in the grid, and the game really is a draw. In that case bestscore should be 0, so this draw is considered better than a loss, and worse than a win. So make sure winning does not return None in that case, but 0:
if not "." in comb:
return 0
Not an error, but it is a bit odd that some functions take the board as argument, and others not. Make this consistent, and always pass the board as argument (player1(board), player2(board) and winning(board)).
With these changes, the AI will play the best play, although the calculation for the first move takes quite some time. You could improve by applying alpha-beta pruning.

Tic Tac Toe isn't showing winner correctly with lists

In my tic tac toe game, after 4 turns, it declares X (the first player) to be the winner even if not. I don't know what im missing and it was working when just checking columns but now with rows it doesn't. I have a feeling it's a problem when I am calling the function with the letter but I'm not entirely sure.
moves = [["1", "2", "3"],
["1", "2", "3"],
["1", "2", "3"]]
def win(letter):
if(moves[0][0] == letter and
moves[1][0] == letter and
moves[2][0] == letter):
print("~~~ " + letter + " WON!!! CONGRATS!!!! ~~~")
quit()
elif(moves[0][1] == letter and
moves[1][1] == letter and
moves[2][1] == letter):
print("~~~ " + letter + " WON!!! CONGRATS!!!! ~~~")
quit()
elif(moves[0][2] == letter and
moves[1][2] == letter and
moves[2][2] == letter ):
print("~~~ " + letter + " WON!!! CONGRATS!!!! ~~~")
quit()
elif(moves[0][0] == letter and
moves[0][1] == letter and
moves[0][2]):
print("~~~ " + letter + " WON!!! CONGRATS!!!! ~~~")
quit()
elif(moves[1][0] == letter and
moves[1][1] == letter and
moves[1][2]):
print("~~~ " + letter + " WON!!! CONGRATS!!!! ~~~")
quit()
elif(moves[2][0] == letter and
moves[2][1] == letter and
moves[2][2]):
print("~~~ " + letter + " WON!!! CONGRATS!!!! ~~~")
quit()
def playerInput():
player1 = input("Where do you want to place your X, player 1? (row number, space, number)")
moves[int(player1[0]) - 1][int(player1[2]) - 1] = "X"
player2 = input("Where do you want to place your O, player 2? (row number, space, number)")
moves[int(player2[0]) - 1][int(player2[2]) - 1] = "O"
boardDraw()
def boardDraw():
print("1| "+moves[0][0]+" | "+moves[0][1]+" | "+moves[0][2]+" |")
print(" |---+---+---|")
print("2| "+moves[1][0]+" | "+moves[1][1]+" | "+moves[1][2]+" |")
print(" |---+---+---|")
print("3| "+moves[2][0]+" | "+moves[2][1]+" | "+moves[2][2]+" |")
win("X")
win("O")
playerInput()
print("OK SO....\nPlayer 1 is X\nPlayer 2 is O\nGOOOOO!!")
boardDraw()
I'm assuming part of your problem has to do with the fact that in some of your if cluases, you check the first two squares or equal to letter, but not the third. Most objects in Python, except for things like None or 0 will be evaluated to True. So If there's a non-zero number in your array, or a character, it will be evaluated to True. This causes the program to think the player won when a line of only two things are formed.
Also, you six specific win conditions, and there are definitely more than the ones you specified to win in Tic Tac Toe. I would suggest a more logical (and readable) approach of consolidating the win scenarios together. For example, you can check all the horizontal and vertical win conditions in one loop:
for i in range(3):
if ((moves[i][0] == moves[i][1] == moves[i][2] == letter) or
(moves[0][i] == moves[1][i] == moves[2][i] == letter):
# do win-condition stuff here
Finally, I would recommend checking for invalid moves, since your current code would just let the user over-write existing moves.
You're clearly missing 2 winning cases:
moves[0][0] == moves[1][1] == moves[2][2]
and:
moves[0][2] == moves[1][1] == moves[2][0]
I'd rather rewrite your winning detection function as:
def win(letter) :
for i in range(3) : # rows
if set(moves[i]) == set([letter]) :
print( 'WIN' )
quit()
for x in zip(moves[0], moves[1], moves[2]) : # columns
if set(x) == set([letter]) :
print( 'WIN' )
quit()
# you have completely missed the part below...
if set(moves[i][i] for i in range(3)) == set([letter]) : # one diagonal
print( 'WIN' )
quit()
if set(moves[i][3-i] for i in range(3)) == set([letter]) : # another diagonal
print( 'WIN' )
quit()
or even more compact:
def win(letter) :
possible_wins = [ set(moves[i]) for i in rage(3) ] + # rows
[ set(x) for x in zip(moves[0], moves[1], moves[2]) ] + # columns
[ set(moves[i][i] for i in range(3)) ] + # diagonal
[ set(moves[i][3-i] for i in range(3)) ] # another diagonal
if any( p == set([letter]) for p in possible_wins ) :
print( 'WIN' )
quit()

How to use a return value from a function in a completely different function

I am writing a tic-tac-toe game and currently have the following functions:
# Need: Board, Display Board, Play Game, Alternating Terms, Os, Xs, Wins, Losses
board = ["-", "-", "-",
"-", "-", "-",
"-", "-", "-", ]
def display_board():
print(board[0] + " | " + board[1] + " | " + board[2])
print(board[3] + " | " + board[4] + " | " + board[5])
print(board[6] + " | " + board[7] + " | " + board[8])
def handle_turn():
position = input("\nChoose a position from 1 to 9: ")
position = int(position) - 1
return position
def choose_player_marker(marker):
player_marker = ''
if marker == 'X' or marker == 'x':
player_marker = 'X'
return player_marker
elif marker == 'O' or marker == 'o':
player_marker = 'O'
return player_marker
def opp_move():
opp_marker = ''
if choose_player_marker() == 'X':
opp_marker = 'O'
else:
opp_marker = 'X'
def play_game():
game_current = True
display_board()
marker = input("\nPlease choose a marker: 'X' or 'x' OR 'O' or 'o' : ")
choose_player_marker(marker)
opp_move()
handle_turn()
play_game()
In the function def choose_player_marker(marker): I return player_marker = 'X' or player_marker = 'O', depending on the condition.
I would like to use the resulting 'X' or 'O' in the function def opp_move(): to determine which marker the opponent will choose. However, I am having difficulty in properly delegating the return statement. I tried using marker as the argument in the def opp_move(): function and also have tried if choose_player_marker(marker) == 'X' to no avail.
How do I correctly handle the return statement from def choose_player_marker(marker): so that my def opp_move(): function will be able to determine what marker the opponent will choose?
Now we can see your full code, the solution is simple.
You already call choose_player_marker() in main. You don't need to call it in opp_move as well. What you need to do instead is to capture the result when you call it in main, and then pass in the value to opp_move:
def opp_move(marker):
if marker == 'X':
opp_marker = 'O'
else:
opp_marker = 'X'
return opp_marker
def play_game():
game_current = True
display_board()
marker = input("\nPlease choose a marker: 'X' or 'x' OR 'O' or 'o' : ")
marker = choose_player_marker(marker)
opp_move(marker)
handle_turn()
Now there are various other things wrong with this code, like the fact that handle_turn doesn't do anything useful, and that as others have pointed out choose_player_marker can be trivially replaced with a call to marker.upper(), but at least this should get you going.
The function choose_player_marker doesn't do anything useful at present. If you give it an X it gives you back and X, if you give it an O it gives you back an O. The only use it has right now is converting lowercase x and o to uppercase X and O.
Thus there is no need to call it, because if we know the input (X) then we know the output (X).
Given the name: choose_player_marker I suspect you want either a different name (e.g. normalise_player_marker() or a different implementation, i.e. one that prompts for user-input asking the person if they want to be X or O?)
As for opp_move : There's no need to call choose_player_marker. If you know the player is X then you know the opponent is O, and vice versa. Thus I suspect you want something like this:
def normalise_marker(marker):
if marker == 'X' or marker == 'x':
return 'X'
elif marker == 'O' or marker == 'o':
return 'O'
def opp_move(player_marker):
opp_marker = ''
normed_marker = normalise_marker(player_marker)
if normed_marker == 'X':
opp_marker = 'O'
else:
opp_marker = 'X'
# use opp_marker here
Note that in its current state normalise_marker doesn't handle the case of marker NOT being an X or O, and can be reduced to an even simpler implementation, but that is left as an exercise for you :)
Im not familiar with Python, but I guess it would make it more simple if the provided argument was set to lower case, the additional check for Capital case wouldn't be needed. Still You could probably shorten the code this way.
def choose_player_marker(marker):
return marker.lower()
def opp_move(result):
if result == 'x':
return 'o'
else:
return 'x'
playerMarker = 'X'
res = choose_player_marker(playerMarker)
opp = opp_move(res)
print(result, opp)

Modifying a list outside a 'While' loop after running it through the 'While' loop

I'm currently trying to create my own game of TicTacToe from the ground up and I'm currently having difficulty with a 'while' loop corresponding to a list that I have created.
I'm using the following list:
board = [1,2,3,4,5,6,7,8,9]
to mark all 9 slots on a 3x3 game of TicTacToe.
However when player one makes a move(in slot '1' for example) the list should be changed to show
board = [X,2,3,4,5,6,7,8,9]
This should continue on until all 9 indexes(I believe is the appropriate term) within the list should be taken up by either 'X' or 'O' which will equal a tie in the game!
For now I'm just experimenting as I go along so pardon the rest of the code but the full code I'm using is:
board = [1,2,3,4,5,6,7,8,9]
def CreateBoard(board):
print(' | |')
print(' ' + board[7] + ' | ' + board[8] + ' | ' + board[9])
print(' | |')
print('-----------')
print(' | |')
print(' ' + board[4] + ' | ' + board[5] + ' | ' + board[6])
print(' | |')
print('-----------')
print(' | |')
print(' ' + board[1] + ' | ' + board[2] + ' | ' + board[3])
print(' | |')
PlayerOne = 'X'
Turn = 'player one'
GameRunning = True
while [0] == 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 or 9 in board == True:
if Turn == 'player one':
letter = 'X'
Move = input("Please pick where to go next: ")
Move = int(Move)
if Move in board:
board.insert(Move, letter)
board.remove(Move)
print(board)
Turn = 'player two'
else:
print("This move is invalid")
if Turn == 'player two':
letter = 'O'
Move = input("Pick where to go next: ")
Move = int(Move)
if Move in board:
board.insert(Move, letter)
board.remove(Move)
print(board)
Turn = 'player one'
else:
print("This move is invalid")
The output I get as I go along are:
I'm guessing the while loop is running the list that's outside of the loop but I'm trying to find a way to change that!
I've also not yet worked out why its printing 'That move is invalid'!
The issue with your while loop is that non-zero integers are always considered to be "true". Therefore
while [0] == 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 or 9 in board == True:
actually means "while the list containing 0 is equal to the integer 1 (always false), or 2 (true), or 3 (true)... or 9 in (board) (true if 9 is still on the board), then enter the block."
I believe that you meant was more:
while any((cell in range(1,10) for cell in board)):
which means while any cell in the board is in the range from 1 (inclusive) to 10 (exclusive), then enter the block.
Take a look at this code:
player = [
{"name": "Player One", "letter": "X"},
{"name": "Player Two", "letter": "O"},
]
def print_board(board):
def print_vbars():
print(" | |")
def print_hbars():
print("-----------")
print_vbars()
for row in range(3):
print(" {} | {} | {}".format(*(board[row*3 : row*3+3])))
print_vbars()
print_hbars()
print_vbars()
print_vbars()
def final_state(board):
winning_combinations = (
(0,1,2), # Horizontal top row
(3,4,5), # Horizontal middle row
(6,7,8), # Horizontal bottom row
(0,3,6), # Vertical left row
(1,4,2), # Vertical middle row
(2,5,7), # Vertical right row
(0,4,8), # Downward diagonal
(2,4,6), # Upward diagonal
)
for letter in ("X", "O"):
for combination in winning_combinations:
row_state = (board[index] for index in combination)
if all(map(lambda cell: letter == cell, row_state)):
return "{} wins".format(letter)
if all(map(lambda cell: cell in ("X", "O"), board)):
return "Game is a draw."
return False
def play_game():
board = list(range(9))
starting_player = 0
turn = 0
active_player = starting_player
while final_state(board) is False:
turn += 1
print("Turn {}:".format(turn))
print_board(board)
name, letter = player[active_player]["name"], player[active_player]["letter"]
move = None
while move is None:
try:
move = input("{} please make your move: ".format(name))
move = int(move)
if move in board:
board[move] = letter
print("{} played {} on {}.".format(name, letter, move))
active_player = 1 - active_player
else:
print("Move {} is invalid.".format(move))
move = None
except Exception as e:
print("Move {} is invalid.".format(move))
move = None
print_board(board)
print(final_state(board))
if __name__ == "__main__":
play_game()

Issues with For loops inside of Functions in Python 3.3

Okay, so I'm working on writing a tic tac toe game, and have run into a pretty bad error that I can't seem to solve. I have created a function that will have the computer block the player if the player is about to win, however, after if successfully blocks once, it doesn't trigger anymore even if the conditions are satisfied. The code for the function is:
def block():
for t in range(0, 9, 3):
if slot[t] == user_team and slot[t+1] == user_team and (slot[t+2] \
!= user_team) and (slot[t+2] != computer_team):
slot[int(t+2)] = computer_team
return
elif slot[t+1] == user_team and slot[t+2] == user_team and (slot[t] \
!= user_team) and (slot[t] != computer_team):
slot[int(t)] = computer_team
return
elif slot[t] == user_team and slot[t+2] == user_team and (slot[t+1] \
!= user_team) and (slot[t+1] != computer_team):
slot[int(t+1)] = computer_team
return
for t in range(3):
if slot[t] == user_team and slot[t+3] == user_team and (slot[t + 6] \
!= user_team) and (slot[t+6] != computer_team):
slot[int(t+6)] = computer_team
return
elif slot[t+3] == user_team and slot[t+6] == user_team and (slot[t] \
!= user_team) and (slot[t] != computer_team):
slot[int(t)] = computer_team
return
elif slot[t] == user_team and slot[t+6] == user_team and (slot[t+3] \
!= user_team) and (slot[t+3] != computer_team):
slot[int(t+3)] = computer_team
Also, user_team and computer_team lead back to whether that player is X or O, and the slot[int()] = computer_team is used to place the move on the board.
And below is where the function is called (just in case I messed up here.):
else:
draw_board()
'''win()'''
block()
cmove()
turn = "user"
if end_game() == True:
computer_win += 1
draw_board()
print ("The computer has won! But... We already knew that would happen. (:")
playing = False
elif end_game() == "Tie":
tie_win += 1
draw_board()
print ("The game is a tie. You're going to have to try harder " \
+ "\n" + "if you wish to beat the computer!" + "\n")
playing = False
else:
pass
If any of you could tell me where I went wrong, then that would make my day. c:
Board (Prints are indented, it just doesn't want to here.)
def draw_board():
'''Opted to use lists so that the numbers can be replaced with either
X or O later on and so that testing whether the game is over is simpler'''
print (" " + str(slot[0]) + " | " + str(slot[1]) + " | " + str(slot[2]))
print ("-----------")
print (" " + str(slot[3]) + " | " + str(slot[4]) + " | " + str(slot[5]))
print ("-----------")
print (" " + str(slot[6]) + " | " + str(slot[7]) + " | " + str(slot[8]))
print ("\n")
New ERRROR:
This is my board after I put in move 4
X | O | X
O | 4 | 5
X | 7 | X
The computer's board after it's move 4 (two moves, and replaces an x)
X | O | X
O | 4 | O
O | 7 | X
I believe your problem is in the logic of your block function.
Here is your board:
0 1 2
3 4 5
6 7 8
Walking through the first pair of nested for loops, let's see what your code does:
for t in range(0,9,3):
for y in range(1, 9, 3):
This will give you the follow pairs of t, y: (0,1), (0,4), (0,7), (3,1), (3,4), (3,7), (6,1), (6,4), and (6,7). Right away, I don't think this is what you had intended. From what I can tell, you are trying to check to see if the player has two marks in a row.
This problem is easy to fix - you don't need two for loops. Instead, just use t, t+1, and t+2.
Next, consider a row:
0 1 2
There are three conditions to check for, player has marks at 0 and 1, at 0 and 2, or at 1 and 2. You only check for two of these conditions - 0 and 1, and 1 and 2.
Additionally, the if statement isn't doing what you think it's doing:
if ... and slot[y+1] != user_team and computer_team:
This is equivalent to this:
if ... and (slot[y+1] != user_team) and computer_team:
I assume computer_team is 'x' or 'o', in which case python would use it the same as True in an if statement. What you want is this:
if ... and (slot[y+1] != user_team) and (slot[y+1] != computer_team):
This is also probably why your code would work only once - the next time it goes to evaluate the same row or column it found previously, the if statement will evaluate to True again, and it will set the same space again, which to you looks as if it is doing nothing.
Your code for checking the columns has the same issues. Hopefully the issues I've pointed out will be enough for you to figure out how to fix your code. Good luck!

Categories

Resources