Python - OOP Tic-Tac-Toe - python

I'm writing a tic-tac-toe game for an assignment. It needs to use object-oriented programming and it has to be relatively smart - it needs to block the player's success. I'm having a lot of trouble with this.
My trouble comes from my rowabouttowin method: I did a very convoluted list comprehension and I don't think that I did it correctly.
What I want is a method that checks if the player is about to win the game horizontally (O _ O, O O _, or _ O O if mark = O) and, if the player is, returns the position where the computer should play.
Any help or advice on how best to approach this?
from random import randint
class TTT:
board = [[' ' for row in range(3)] for col in range(3)]
currentgame = []
def print(self):
"""Displays the current board."""
print("\n-----\n".join("|".join(row) for row in self.board))
def mark(self,pos,mark):
"""Method that places designated mark at designated position on the board."""
x,y = pos
self.board[x][y] = mark
def win(self,mark):
"""Method that checks if someone has won the game."""
if mark == self.board[0][0] == self.board[1][1] == self.board[2][2]:
return True
if mark == self.board[2][0] == self.board[1][1] == self.board[0][2]:
return True
elif mark == self.board[0][0] == self.board[1][0] == self.board[2][0]:
return True
elif mark == self.board[1][0] == self.board[1][1] == self.board[1][2]:
return True
elif mark == self.board[0][1] == self.board[1][1] == self.board[2][1]:
return True
elif mark == self.board[0][2] == self.board[1][2] == self.board[2][2]:
return True
elif mark == self.board[0][0] == self.board[0][1] == self.board[0][2]:
return True
elif mark == self.board[2][0] == self.board[2][1] == self.board[2][2]:
return True
else:
return False
def choose(self,mark):
"""The computer chooses a place to play. If the player is not about to win,
plays randomly. Otherwise, does a series of checks to see if the player is about
to win horizontally, vertically, or diagonally. I only have horizontal done."""
spotx = randint(0,2)
spoty = randint(0,2)
if self.rowabouttowin(mark):
self.mark((self.rowabouttowin(mark)),mark)
elif self.legalspace(spotx,spoty):
self.mark((spotx,spoty),mark)
else:
self.choose(mark)
def legalspace(self,spotx,spoty):
"""Returns True if the provided spot is empty."""
if self.board[spotx][spoty] == ' ':
return True
else:
return False
def rowabouttowin(self,mark):
"""If the player is about to win via a horizontal 3-in-a-row,
returns location where the computer should play"""
for row in range(3):
if any(' ' == self.board[row][1] for i in range(3)) and any(self.board[row][i] == self.board[row][j] for i in range(3) for j in range(3)):
if self.board[row][i] == ' ' : yield(self.board[row][i % 3], self.board[row][i])
This currently gives this error message:
Traceback (most recent call last):
File "<pyshell#49>", line 1, in <module>
x.choose('x')
File "/Users/richiehoffman/Documents/Python Programs/Tic Tac Toe.py", line 40, in choose
self.mark((self.rowabouttowin(mark)),mark)
File "/Users/richiehoffman/Documents/Python Programs/Tic Tac Toe.py", line 11, in mark
x,y = pos
File "/Users/richiehoffman/Documents/Python Programs/Tic Tac Toe.py", line 61, in rowabouttowin
if self.board[row][i] == ' ' : yield(self.board[row][i % 3], self.board[row][i])
NameError: global name 'i' is not defined

A few tips:
You're using class variables, not instance variables, so look up the difference. I changed your class to use instance variables, as the variables that you set should belong in an instance.
Consider making things more readable.
Use __str__ to make the printable version of your class. That way, you can do print(class_instance) and it'll come out all nice.
Here's what I changed:
from random import randint
class TTT(object):
def __init__(self):
self.board = [[' ' for row in range(3)] for col in range(3)]
self.currentgame = []
def __str__(self):
"""Displays the current board."""
return "\n-----\n".join("|".join(row) for row in self.board)
def mark(self, pos, mark):
"""Method that places designated mark at designated position on the board."""
x, y = pos
self.board[x][y] = mark
def win(self, mark):
"""Method that checks if someone has won the game."""
for row in self.board:
if row[0] == row[1] == row[2]:
return True
for i in range(3):
if self.board[0][i] == self.board[1][i] == self.board[2][i]:
return True
if board[0][0] == board[1][1] == board[2][2]:
return True
elif board[0][2] == board[1][1] == board[2][0]:
return True
else:
return False
def choose(self, mark):
"""The computer chooses a place to play. If the player is not about to win,
plays randomly. Otherwise, does a series of checks to see if the player is about
to win horizontally, vertically, or diagonally. I only have horizontal done."""
spotx = randint(0, 2)
spoty = randint(0, 2)
if self.rowabouttowin(mark):
self.mark((self.rowabouttowin(mark)), mark)
elif self.legalspace(spotx, spoty):
self.mark((spotx, spoty), mark)
else:
self.choose(mark)
def legalspace(self, spotx, spoty):
"""Returns True if the provided spot is empty."""
return self.board[spotx][spoty] == ' '
def rowabouttowin(self, mark):
"""If the player is about to win via a horizontal 3-in-a-row,
returns location where the computer should play"""
for row in range(3):
check_one = any(' ' == self.board[row][1] for i in range(3))
check_two = any(self.board[row][i] == self.board[row][j] for i in range(3) for j in range(3))
# I have no idea what this code does
if self.board[row][i] == ' ' :
yield self.board[row][i % 3], self.board[row][i]

Related

PYTHON TypeError: validTicTacToe() missing 1 required positional argument: 'board'

I am new to python and trying to run this code on VScode. It gives an error saying TypeError: validTicTacToe() missing 1 required positional argument: 'board'. What am I doing wrong here?
I am actually trying to understand how does the self works. I know c++ so if you can explain in comparison to c++ it would be of great help.
Also, this is a leetcode problem, it works good on leetcode, but looks like I have to make some changes before running it on VScode.
TIA
def validTicTacToe(self, board) -> bool:
X, O = self.countSymbols(board)
if O > X:
# Os can't be greater than Xs
return False
elif abs(X-O) > 1:
# Difference can only be 1
return False
elif X > O:
# X can't have more moves than O if O is already won
if self.checkTriads(board, 'O'): return False
else:
# X and O can't have equal moves if X is winning
if self.checkTriads(board, 'X'): return False
return True
def countSymbols(self, board):
X = 0
O = 0
for row in board:
for i in row:
if i == 'X':
X+=1
elif i == 'O':
O+=1
return X,O
def checkTriads(self, board, sym='X'):
# Checking for Hight triads
i = 0
while i<3:
if (board[0][i] == board[1][i] == board[2][i] == sym):
return True
i+=1
# Checking for width
i=0
while i<3:
if (board[i][0] == board[i][1] == board[i][2] == sym):
return True
i+=1
# Checking for diag.
if (board[0][0] == board[1][1] == board[2][2] == sym):
return True
if (board[0][2] == board[1][1] == board[2][0] == sym):
return True
return False
board=["O "," "," "]
validTicTacToe(board)```
You defined your functions in the manner of object methods. These will always automatically receive their instance object as the first parameter. By convention, this parameter is named self. You however didn't declare a class to contain these methods. You can either remove the self parameters from your functions or wrap them inside a class definition:
class TicTacToe():
def validTicTacToe(self, board) -> bool:
# ...
game = TicTacToe()
board = ["O ", " ", " "]
game.validTicTacToe(board)
To make actual use of the class mechanism, the board should be a property:
class TicTacToe():
def __init__(self, board):
self.board = board
def validTicTacToe(self) -> bool:
# now you can use self.board instead of passing it around
board = ["O ", " ", " "]
game = TicTacToe(board)
game.validTicTacToe()

List value being overwritten even though I am checking for it

I'm new to python and writing my first project. I'm trying to implement a check that if a space is already occupied, not to move there. I can't seem to figure out why my move_player method overwrites the index value of the board even though I am explicitly checking it (If it is O's turn and X has already been placed in the index O is trying to move to, it just overwrites it). I have tried hard coding the check for 'X' and 'O' instead of player.player as well but can't seem to figure it out. Does it have to do something with how Python works or am I implementing it wrong?
class Player:
def __init__(self, player):
self.player = player
class Board:
def __init__(self):
self.board = [[' ' for i in range(3)] for j in range(3)]
def display_board(self):
print('---------')
for row in self.board:
print('| ', end='')
for col in row:
print(f'{col} ', end='')
print('|')
print('---------')
def move_player(self, player):
try:
p1 = Player('X')
p2 = Player('O')
coordinates = [int(i) for i in input("Enter coordinates for move: ").split()]
xCoordinate = coordinates[0]
yCoordinate = coordinates[1]
if ((self.board[xCoordinate][yCoordinate] == p1.player) or
(self.board[xCoordinate][yCoordinate] == p2.player)):
print("That space is occupied, please choose another one.")
self.move_player(player)
else:
self.board[xCoordinate - 1][yCoordinate - 1] = player.player
except (ValueError, IndexError):
print("Please only enter numbers between 1 and 3.")
self.move_player(player)
def has_won(self, player):
if self.check_diagonal(player):
return True
elif self.check_across(player):
return True
elif self.check_down(player):
return True
return False
if __name__ == '__main__':
board = Board()
player1 = Player('X')
player2 = Player('O')
player = player1
while True:
board.display_board()
board.move_player(player)
if board.has_won(player):
board.display_board()
print(f'{player.player} wins!!!')
break
if player == player1:
player = player2
else:
player = player1
The code is very convoluted but from what I can see:
if ((self.board[xCoordinate][yCoordinate] == p1.player) or
(self.board[xCoordinate][yCoordinate] == p2.player)):
...
self.board[xCoordinate - 1][yCoordinate - 1] = player.player
You are checking [x,y] but assigning to [x-1,y-1].

Minimax implementation in tic tac toe (Python) not working - no idea why

I am currently trying to make a Tic Tac Toe game with minimax implemented in Python. Another feature that I'm trying to implement is different board sizes. But overall, unfortunately, the algorithm is not working.
As a beginner, this is not a surprise for me, but this case seems hopeless. I tried tweaking quite a lot of things (may seem like a lot just to me) just to end up with the same result - the computer filling up the fields from top left to bottom right.
#Sprawdzenie czy ktos wygral w poziomie lub pionie / straight line check
def winLine(line, letter):
return all(n == letter for n in line)
#Utworzenie nowej listy z elementow przekatnych / diagonal check
def winDiagonal(board, letter):
arr = []
tArr = []
for n in range(boardSize):
arr.append(board[n][n])
tArr.append(board[n][boardSize-n-1])
if winLine(arr, letter):
return True
elif winLine(tArr, letter):
return True
else:
return False
def checkWinner (board):
#Liczenie wolnych pol / checking the available fields
openSpots = 9
for n in range(boardSize):
for m in range(boardSize):
if board[n][m] == '0':
openSpots += 1
#Transpozycja planszy, by mozna bylo latwo zastosowac winLine na kolumach / transposition of the board
for letter in (person, ai):
transPos = list(zip(*board))
#Sprawdzanie w poziomie / horizontal check
if any(winLine(row, letter) for row in board):
#print('winline horizontal')
return letter
#Sprawdzanie w pionie / vertical check
elif any (winLine(col, letter) for col in transPos):
#print('winline vertical')
return letter
#Sprawdzanie po przekatnych / diagonal check
elif winDiagonal(board, letter):
return letter
elif openSpots == 0: return 'tie'
else: return 'none'
#Funkcja sprawdzajaca czy dane pole jest wolne / checks whether the field is available
def available (row, col):
global board
#Sprawdzenie czy pole jest wolne
if board[row][col] == '0':
return True
else:
return False
#Stale dla algorytmu minimax / minimax initial scores to compare against
minTarget = float('inf')
maxTarget = float('-inf')
#Slownik z wartosciami liczbowi dla wynikow, komputer maksymalizuje / a dictionary with scores for particular results
scores = {
'X': 10,
'O': -10,
'tie': 0
}
#Algorytm minimax
def minimax(myBoard, depth, maximizes):
#Sprawdzenie czy zaszla wygrana lub remis / Checking whether there is a win or tie
res = checkWinner(myBoard)
if (res != 'none'):
return scores[res]
#Gracz maksymalizujacy/ Maximizing player
elif maximizes == True:
bestScoreMax = maxTarget
for n in range(boardSize):
for m in range(boardSize):
if board[n][m] == '0':
board[n][m] = person
score = minimax(board, depth + 1, False)
board[n][m] = '0'
bestScoreMax = max([score, bestScoreMax])
return bestScoreMax
#Gracz minimalizujacy / minimizing player
elif maximizes == False:
bestScoreMin = minTarget
for n in range(boardSize):
for m in range(boardSize):
if board[n][m] == '0':
board[n][m] = ai
score = minimax(board, depth + 1, True)
board[n][m] = '0'
bestScoreMin = min([score, bestScoreMin])
return bestScoreMin
def makeMove(row, col):
global board
board[row][col] = ai
def computedMove():
global board
myBoard = board
computedTarget = maxTarget
moveX = 2
moveY = 2
for n in range(boardSize):
for m in range(boardSize):
if myBoard[n][m] == '0':
score = minimax(myBoard, 0, True)
if score > computedTarget:
computedTarget = score
moveX = n
moveY = m
makeMove(moveX, moveY)
#print(str(move.x) + ' ' + str(move.y))
# Funkcja pobierajaca ruch uzytkownika / player input for the move
def getPlayerMove():
global board
res = input('Please type in your move on the form \"x y\", x being the number of the row and y the number of the column of your choosing.\n')
col, row = res.split(" ")
row = int(row)
col = int(col)
if available(row-1, col-1):
board[row-1][col-1] = person
else:
print('You cannot make that move')
getPlayerMove()
def drawBoard():
global board
for n in range(boardSize):
for m in range(boardSize):
if board[n][m] == '0':
print(' - ', end='')
else:
print(' '+board[n][m]+' ', end='')
print('\n')
#Zmienna powiadamiajaca o stanie rozgrywki / variable indicating the state of the game
playing = False
# initializing the variable
boardSize = 0
# initializing the players
person = 'X'
ai = 'O'
#Gracz posiadajacy ruch (a takze rozpoczynajacy) / player who is playing at the moment
currentPlayer = ''
while True:
currentPlayer = person
boardSize = int(input("Please enter the size of the board. (one side)\n"))
global board
board = [['0' for i in range(boardSize)] for i in range(boardSize)]
print("You go first.")
playing = True
while playing:
if currentPlayer == person:
drawBoard()
getPlayerMove()
if checkWinner(board) == person:
drawBoard()
print("Yaay, you won!")
playing = False
else:
if checkWinner(board) == 'tie':
drawBoard()
print('It\'s a tie!')
break
else:
currentPlayer = ai
elif currentPlayer == ai:
computedMove()
if checkWinner(board) == ai:
drawBoard()
print('You lose!')
playing = False
else:
if checkWinner(board) == 'tie':
drawBoard()
print('It\'s a tie!')
break
else:
currentPlayer = person
if not input('Do you want to play again?').lower().startswith('y'):
break
In computedMove, you should first play the ai move then check the scores.
def computedMove():
global board
myBoard = board
computedTarget = maxTarget
moveX = 2
moveY = 2
for n in range(boardSize):
for m in range(boardSize):
if myBoard[n][m] == '0':
myBoard[n][m] = ai #This is added
score = minimax(myBoard, 0, True)
if score > computedTarget:
computedTarget = score
moveX = n
moveY = m
makeMove(moveX, moveY)
Also in minimax function, you should use same variable for myBoard and board.

Minimax solution not working

So I've been looking at this problem for a very long time now. But I just can't figure it out.
I have got a player class that looks like this:
class perfectplayer():
def __init__(self, player, game):
self.player = player
self.game = game
def possible_moves(self, board):
'''Finds all possible moves of given board'''
possible_moves = []
for x in range(len(board)):
for y in range(len(board[x])):
if board[x][y] is None:
possible_moves.append((x, y))
return possible_moves
def score(self, game, depth):
'''Gives score to given board based on depth and who wins'''
if game.win()[0]:
if game.win()[1] is self.player:
return 10 - depth
else:
return depth - 10
return 0
def minimax(self, game, depth):
'''Constructs minimax tree and selects best move based on that tree'''
if game.game_over():
return self.score(game, depth)
depth += 1
scores = []
moves = []
# Recursively creates tree of possible decisions
for move in self.possible_moves(game.board):
possible_game = game
possible_game.move(move)
scores.append(self.minimax(possible_game, depth))
moves.append(move)
# max the score if it is player's turn
if game.current_player is self.player:
max_score_idx = scores.index(max(scores))
self.choice = moves[max_score_idx]
return scores[max_score_idx]
# min the score if it is not player's turn
else:
min_score_idx = scores.index(min(scores))
self.choice = moves[min_score_idx]
return scores[min_score_idx]
def best_move(self, game):
'''returns best move in give game state'''
self.minimax(game, 0)
return self.choice
Which hooks into a game class that has all the required functions to work properly. It can return if a game is over, who won it and who's currently going to move. This all works as I have tested it. The idea is basically that the best_move function returns the next choice for the AI-player. But when I run my little game:
game1 = game()
player1 = perfectplayer("x", game1)
while not game1.game_over():
# finds best choice
choice = player1.best_move(game1)
# puts down a mark
game1.move(choice)
# renders game
game1.render()
move_x = int(input("please input row: "))
move_y = int(input("please input column: "))
game1.move((move_x, move_y))
if game1.win()[0]:
print(game1.win()[1], "has won!")
I get this output:
>> o|o|x|
o|x|o|
x|o|x|
>> please input row: 1
>> please input column: 1
>> x has won!
So it correctly knows that x has won and it properly asks for my input, but why is my game board filled up after 1 turn? Where in my code does this happen? The choice variable is correct as it is always a tuple of two coordinates.
The pseudo-code is from this website: https://www.neverstopbuilding.com/blog/minimax
Here is the code for the game class:
class game():
def __init__(self, board=None, starting_player="x"):
if board is None:
self.board = [[None, None, None],
[None, None, None],
[None, None, None]]
else:
self.board = board
self.current_player = starting_player
def render(self):
for row in self.board:
for item in row:
if item is None:
print(".", end="|")
else:
print(item, end="|")
print()
def move(self, move):
move_x = move[0]
move_y = move[1]
self.board[move_x][move_y] = self.current_player
self.switch()
def legalmove(self, move):
move_x = move[0]
move_y = move[1]
if self.board[move_x][move_y] is None:
return True
return False
def switch(self):
if self.current_player == "x":
self.current_player = "o"
else:
self.current_player = "x"
def win(self):
board = self.board
for x in range(len(board)):
for y in range(len(board[x])):
if board[0][y] == board[1][y] == board[2][y] is not None:
return True, board[0][y]
if board[x][0] == board[x][1] == board[x][2] is not None:
return True, board[x][0]
if x == y and board[0][0] == board[1][1] == board[2][2] is not None:
return True, board[0][0]
if x + y == 2 and board[0][2] == board[1][1] == board[2][0] is not None:
return True, board[0][2]
return False, None
def game_over(self):
for x in range(len(self.board)):
for y in range(len(self.board[x])):
if self.board[x][y] is None:
return False
return True
I think the problem is that this line:
possible_game = game
Does not create a copy of the game's state, but merely another reference to it. So your search modifies the actual board. To fix this, you could either:
Implement an 'undo' operation to undo a move after exploring the resulting state.
or implement a copy operation for game states.

Python: Trying to program a variant of connect four: Winning condition doesn't stop

I have been trying to program a variant of connect four for my programming class. The board is 6x8 in size. In the variant I'm trying to program, the winning condition is to essentially build an L.
This means any construction of the form
X
X
X X
is a winning condition.
I have been trying to make a function that checks every single column for the same symbol consecutively to build a pair. And a function to do the same for every row. With these two functions I would then check if 2 pairs are consecutive, because no matter how you combine a vertical and horizontal pair, it will always build an 'L'.
To make a clear board I'm using
def ClearBoardSingle():
global Board
Board = [['0' for i in range(8)] for i in range(6)]
BoardPrint()
PlayerMoveSingle()
And for my interface I'm using
def BoardPrint():
global Board
global GameMoves
global PlayerTurn
global Player1Symbol
global Player2Symbol
print('\n\nMoves done: ' + str(GameMoves))
print('To Restart: R | To Quit: Q')
print('Valid choices: 1, 2, 3, 4, 5, 6, 7, 8')
if PlayerTurn == 0:
print('It\'s ' +str(Player1Symbol) + '\'s Turn')
if PlayerTurn == 1:
print('It\'s ' +str(Player2Symbol) + '\'s Turn')
print(Board[0])
print(Board[1])
print(Board[2])
print(Board[3])
print(Board[4])
print(Board[5])
I already figured out how to change Variables inside the Board, and I'm pretty much done. The only thing I don't know how to implement is the winning condition. I tried this function for the Rows:
def VerticalList(Column):
global Board
global Choice
global Row0
Column = int(Column)
Choice = int(Choice)
print(Column,' C')
while Column > 0:
for Board[Column][Choice] in range(Column):
Row0.append(Board[Column][Choice])
if Column ==6 or Column == -1:
break
else:
VerticalList(Column-1)
if Column ==0:
break
else:
continue
if Column == 0:
Column += 1
while Column < 5:
Column +=1
if Row0[Column] == Row0[Column-1]:
print('Pair')
else:
print('No Pair')
pass
else:
pass
But it enters an endless Loop.
I have no ideas anymore on how to implement the winning condition. I'd appreciate any kind of help or ideas. If you want me to post the whole code or other kinds of snippets, ask for them.
Thank you in anticipation!
Cool problem, below looks like a lot of code, but it's not really. I haven't checked this extensively, so I'm not confident that it doesn't find false positives, but it seems to find L's that it should be finding. The main thing I did was use itertools.combinations to take all 4-sized groups of the positions of X's and then check if they looked like patterns I was expecting for L's. In check_four_group I look at the differences within the row and columns.
from itertools import combinations
def disp_board(board):
for row in board:
print(row)
def check_winning(board):
winning = False
#Find all row,col positions of the X's
x_poses = [(i,j) for i in range(6) for j in range(8) if board[i][j] == 'X']
#Loop through every combination of four X's since it takes four to make the 'L'
for group in combinations(x_poses,4):
if(check_four_group(group)):
winning = True
break
return winning
def check_four_group(group):
rows,cols = zip(*[(r,c) for r,c in group])
row_diffs = [rows[i+1]-rows[i] for i in range(len(rows)-1)]
col_diffs = [cols[i+1]-cols[i] for i in range(len(cols)-1)]
#Uncomment this to print the row and col diffs
#print(row_diffs)
#print(col_diffs)
# Finds:
# X
# X
# X X
if row_diffs == [1,1,0] and col_diffs == [0,0,1]:
return True
# Finds:
# X
# X
# X X
elif row_diffs == [1,1,0] and col_diffs == [0,-1,1]:
return True
# Finds:
# X X
# X
# X
elif row_diffs == [0,1,1] and col_diffs == [1,0,0]:
return True
# Finds:
# X X
# X
# X
elif row_diffs == [0,1,1] and col_diffs == [1,-1,0]:
return True
# Otherwise it's not there at all (not thinking about horizontal L's but could add that)
else:
return False
#Test case 1
def test_case_1():
board = [['0' for i in range(8)] for i in range(6)]
board[2][1] = 'X'
board[2][2] = 'X'
board[3][1] = 'X'
board[4][1] = 'X'
return board
#Test case 2
def test_case_2():
board = [['0' for i in range(8)] for i in range(6)]
board[2][1] = 'X'
board[2][0] = 'X'
board[3][1] = 'X'
board[4][1] = 'X'
return board
#Test case 3
def test_case_3():
board = [['0' for i in range(8)] for i in range(6)]
board[1][0] = 'X'
board[2][0] = 'X'
board[3][0] = 'X'
board[3][1] = 'X'
return board
#Test case 4
def test_case_4():
board = [['0' for i in range(8)] for i in range(6)]
board[1][2] = 'X'
board[2][2] = 'X'
board[3][2] = 'X'
board[3][1] = 'X'
return board
##################
#Start of program#
##################
board = test_case_1()
#board = test_case_2()
#board = test_case_3()
#board = test_case_4()
disp_board(board)
if check_winning(board):
print('Victory')
else:
print('Keep playing')

Categories

Resources