Python tic tac toe program closes on "3 1" input - python

My problem: Python tab closes on "3 1" input.
This code is heavily inspired by Hafeezul Kareem Shaik's tic-tac-toe game on geekflare.
My code:
import random
class TicTacToe:
def __init__(self):
self.board = []
def create_board(self):
for r0w in range(3):
row = []
for c0l in range(3):
row.append('-')
self.board.append(row)
def get_random_first_player(self):
return random.randint(0,1)
def fix_spot(self, row, col, player):
self.board[row][col] = player
def is_player_win(self, player):
win = None
n = len(self.board)
#checking rows
for r0w in range(n):
win = True
for c0l in range(n):
if self.board[r0w][c0l] != player:
win = False
break
if win:
return win
#checking columns
for r0w in range(n):
win = True
for c0l in range(n):
if self.board[c0l][r0w] != player:
win = False
break
if win:
return win
#checking diagonals
win = True
for i in range (n):
if self.board[i][i] != player:
win = False
break
if win:
return win
win = True
for i in range(n):
if self.board[r0w][n - 1 - r0w] != player:
win = False
break
if win:
return win
return False
for row in self.board:
for item in row:
if item == '-':
return False
return True
def is_board_filled(self):
for row in self.board:
for item in row:
if item == "-":
return False
return True
def swap_player_turn(self, player):
return 'X' if player =='O' else 'O'
def show_board(self):
for row in self.board:
for item in row:
print(item, end=" ")
print()
def start(self):
self.create_board()
player = 'X' if self.get_random_first_player() == 1 else 'O'
while True:
print(f"Player {player} turn")
self.show_board()
# user input
row, col = list(
map(int, input("Enter row and column numbers to fix spot: ").split()))
print()
#fixing spot
self.fix_spot(row - 1, col - 1, player)
#has current player won
if self.is_player_win(player):
print(f"{player} Wins!")
break
#is game a draw
if self.is_board_filled():
print("Draw!")
break
#swapping turn
player = self.swap_player_turn(player)
#final board
print()
self.show_board()
tic_tac_toe = TicTacToe()
tic_tac_toe.start()
The Tic_Tac-Toe Game worked up until I typed "3 1". I tested multiple games and it would always close after that input.
I have tried editing the scope of the following, as I believe that is what is causing it to malfunction
if win:
return win
return False
Unfortunately that has not fixed the problem. Any ideas or suggestions?

This code is so badly written that I wouldn't take it as an example for anything.
But if you really want to know why it ends on 3 1 as input, you should look here:
win = True
for i in range(n):
if self.board[r0w][n - 1 - r0w] != player:
win = False
break
if win:
return win
return False
Here r0w is not reset, it has the last value from the for loop, which is 2, and it's not changing in the loop, so there's only one check made, which is 3 1, translated to 2 0, which is the current player, so that's an instant win.
I assume this was supposed to be a loop to check the other diagonal, but it's clearly flawed.

Related

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].

MCTS Agent making bad decisions on Tic-Tac-Toe

I've been working on a MCTS AI for a couple days now. I tried to implement it on Tic-Tac-Toe, the least complex game I could think of, but for some reason, my AI keeps making bad decisions. I've tried change the values of UCB1's exploration constant, the number of iterations per search, and even the points awarded to winning, losing, and getting to tie the game (trying to make a tie more rewarding, as this AI only plays second, and try to get a draw, win otherwise). As of now, the code looks like this:
import random
import math
import copy
class tree:
def __init__(self, board):
self.board = board
self.visits = 0
self.score = 0
self.children = []
class mcts:
def search(self, mx, player,):
root = tree(mx)
for i in range(1200):
leaf = mcts.expand(self, root.board, player, root)
result = mcts.rollout(self, leaf)
mcts.backpropagate(self, leaf, root, result)
return mcts.best_child(self, root).board
def expand(self, mx, player, root):
plays = mcts.generate_states(self, mx, player) #all possible plays
if root.visits == 0:
for j in plays:
root.children.append(j) #create child_nodes in case they havent been created yet
for j in root.children:
if j.visits == 0:
return j #first iterations of the loop
for j in plays:
if mcts.final(self, j.board, player):
return j
return mcts.best_child(self, root) #choose the one with most potential
def rollout(self, leaf):
mx = leaf.board
aux = 1
while mcts.final(self, mx, "O") != True:
if aux == 1: # "X" playing
possible_states = []
possible_nodes = mcts.generate_states(self, mx, "X")
for i in possible_nodes:
possible_states.append(i.board)
if len(possible_states) == 1: mx = possible_states[0]
else:
choice = random.randrange(0, len(possible_states) - 1)
mx = possible_states[choice]
if mcts.final(self, mx, "X"): #The play by "X" finished the game
break
elif aux == 0: # "O" playing
possible_states = []
possible_nodes = mcts.generate_states(self, mx, "O")
for i in possible_nodes:
possible_states.append(i.board)
if len(possible_states) == 1: mx = possible_states[0]
else:
choice = random.randrange(0, len(possible_states) - 1)
mx = possible_states[choice]
aux += 1
aux = aux%2
if mcts.final(self, mx, "X"):
for i in range(len(mx)):
for k in range(len(mx[i])):
if mx[i][k] == "-":
return -1 #loss
return 0 #tie
elif mcts.final(self, mx, "O"):
for i in range(len(mx)):
for k in range(len(mx[i])):
if mx[i][k] == "-":
return 1 #win
def backpropagate(self, leaf, root, result): # updating our prospects stats
leaf.score += result
leaf.visits += 1
root.visits += 1
def generate_states(self, mx, player):
possible_states = [] #generate child_nodes
for i in range(len(mx)):
for k in range(len(mx[i])):
if mx[i][k] == "-":
option = copy.deepcopy(mx)
option[i][k] = player
child_node = tree(option)
possible_states.append(child_node)
return possible_states
def final(self,mx, player): #check if game is won
possible_draw = True
win = False
for i in mx: #lines
if i == [player, player, player]:
win = True
possible_draw = False
if mx[0][0] == player: #diagonals
if mx[1][1] == player:
if mx[2][2] == player:
win = True
possible_draw = False
if mx[0][2] == player:
if mx[1][1] == player:
if mx[2][0] == player:
win = True
possible_draw = False
for i in range(3): #columns
if mx[0][i] == player and mx[1][i] == player and mx[2][i] == player:
win = True
possible_draw = False
for i in range(3):
for k in range(3):
if mx[i][k] == "-":
possible_draw = False
if possible_draw:
return possible_draw
return win
def calculate_score(self, score, child_visits, parent_visits, c): #UCB1
return score / child_visits + c * math.sqrt(math.log(parent_visits) / child_visits)
def best_child(self, root): #returns most promising node
treshold = -1*10**6
for j in root.children:
potential = mcts.calculate_score(self, j.score, j.visits, root.visits, 2)
if potential > treshold:
win_choice = j
treshold = potential
return win_choice
#todo the AI takes too long for each play, optimize that by finding the optimal approach in the rollout phase
First off, the purpose of this AI is to return an altered matrix, with the best play he could make in that circunstance. I find myself questioning if the MCTS algorithm is the reason behind all these broken plays, due to some possible mistakes in its implementation. With that said, in my eyes, the code does the following:
Check if the root already has its children, in case it has, choose the most promising.
Rollout a random simulation and save the result.
Update the leaf's score, its number of visits and the root's number of visits.
Repeat for 1200 iterations, in my example
Return the best move (matrix, child_node) possible.
Why is it not working? Why is it choosing bad plays instead of the optimal one? Is the algorithm wrongly implemented?
My mistake was choosing the node with the most visits in the expansion phase, when it should have been the one with the most potential according to the UCB1 formula. I also had some errors when it came to implementing some if clauses, as all the losses weren't being counted.

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.

I'm stuck in the creating of a Board game in python

I have an excercise to do and I'm stuck. It's the board game Alak, not much known, that I have to code in python. I can link the execrcise with the rules so you can help me better. The code has the main part and the library with all the procedures and function.
from Library_alak import *
n = 0
while n < 1:
n = int(input('Saisir nombre de case strictement positif : '))
loop = True
player = 1
player2 = 2
removed = [-1]
board = newboard(n)
display(board, n)
while loop:
i = select(board, n, player, removed)
print(i)
board = put(board, player, i)
display(board, n)
capture(board, n, player, player2)
loop = True if again(board, n, player, removed) is True else False
if player == 1 and loop:
player, player2 = 2, 1
elif player == 2 and loop:
player, player2 = 1, 2
win(board, n)
print(win(board, n))
And here is the library:
def newboard(n):
board = ([0] * n)
return board
def display(board, n):
for i in range(n):
if board[i] == 1:
print('X', end=' ')
elif board[i] == 2:
print('O', end=' ')
else:
print(' . ', end=' ')
def capture(board, n, player, player2):
for place in range(n):
if place == player:
place_beginning = place
while board[place] != player:
place_end = place
if board[place + x] == player:
return board
else:
return board
def again(board, n, player, removed):
for p in board(0):
if p == 0:
if p not in removed:
return True
else:
return False
def possible(n, removed, player, i, board):
for p in range(n + 1):
if p == 1:
if board[p-1] == 0:
if p not in removed:
return True
else:
return False
def win(board, n):
piecesp1 = 0
piecesp2 = 0
for i in board(0):
if i == 1:
piecesp1 += 1
else:
piecesp2 += 1
if piecesp1 > piecesp2:
print('Victory : Player 1')
elif piecesp2 > piecesp1:
print('Victory : Player 2')
else:
return 'Equality'
def select(board, n, player, removed):
loop = True
while loop:
print('player', player)
i = int(input('Enter number of boxes : '))
loop = False if possible(n, removed, player, i, board)is True else True
return i
def put(board, player, i):
i -= 1
if board[i] == 0:
if player == 1:
board[i] = 1
return board
else:
board[i] = 2
return board
else:
put(board, player, i)
So my problems here are that I have few errors, the first one is that when I enter the number '1' when asked to enter a number of boxes ( which is the place to play on ) nothing happens. Then when entering any other number, either the error is : if board[place + x] == player:
NameError: name 'x' is not defined
or there seems to be a problem with the : if board[place + x] == player:
NameError: name 'x' is not defined
I would appreciate a lot if anyone could help me. I'm conscious that it might not be as detailed as it should be and that you maybe don't get it all but you can contact me for more.
Rules of the Alak game:
Black and white take turns placing stones on the line. Unlike Go, this placement is compulsory if a move is available; if no move is possible, the game is over.
No stone may be placed in a location occupied by another stone, or in a location where a stone of your own colour has just been removed. The latter condition keeps the game from entering a neverending loop of stone placement and capture, known in Go as ko.
If placing a stone causes one or two groups of enemy stones to no longer have any adjacent empty spaces--liberties, as in Go--then those stones are removed. As the above rule states, the opponent may not play in those locations on their following turn.
If placing a stone causes one or two groups of your own colour to no longer have any liberties, the stones are not suicided, but instead are safe and not removed from play.
You shouldn't use "player2" as a variable, there's an easier way, just use "player" which take the value 1 or 2 according to the player. You know, something like that : player = 1 if x%2==0 else 2
and x is just a increasing int from 0 until the end of the game.

Categories

Resources