I'm simply working on a Minimax algorithm that can play TicTacToe, For some reason the max_value and min_value functions occasionally return None.
def player(board):
if Terminal(board) != False:
return None
else:
if turn(board) == "X":
value,move = max_value(board)
return move
else:
value,move = min_value(board)
return move
def max_value(board):
if Terminal(board) != False:
return Utility(board),None
else:
v = -1000
move = None
for action in Actions(board):
aux,act = min_value(Result(board,action))
print(aux)
if aux > v:
v = aux
move = action
if v == 1:
return v,move
return v,move
def min_value(board):
if Terminal(board) != False:
return Utility(board),None
else:
v = 1000
move = None
for action in Actions(board):
aux,act = max_value(Result(board,action))
print(aux)
if aux < v:
v = aux
move = action
if v == -1:
return v,move
return v,move
Terminal returns state of the game,Action returns possible moves and result creates a board given an action.
The error I get is '>' not supported between instances of 'NoneType' and 'int' pops up for if aux < v: and if aux > v:
When I print aux, it occasionally appears as None.
Thanks.
The code you have shared is fine.
The error message indicates that Utility returns None when you call it. This should not happen. Utility should always return an integer when the game is over, and looking at your code it should return -1, 0 or 1.
There is also the possibility that Utility returns None when the game is not over, but that Terminal has a bug such that it returns something else than False even when the game is not over.
Here is an implementation of the functions Terminal and Utility that do not produce the error you got, but make it work as expected.
In fact, I added all missing functions based on a board representation that is a string of 9 characters, and a game loop so you can play against the minimax algorithm:
import re
def Utility(board):
m = re.findall(r"([XO])(?:\1\1(?:...)*$|..\1..\1|...\1...\1|.\1.\1..$)", board)
return -1 if "O" in m else len(m)
def Terminal(board):
return "O X"[Utility(board)+1].strip() or not board.count(".") and "draw"
def turn(board):
return "OX"[board.count(".") % 2]
def Actions(board):
return (i for i in range(9) if board[i] == ".")
def Result(board, action):
return board[:action] + turn(board) + board[action+1:]
board = "." * 9
while True: # Interactive game between human and minimax
print(f"{board[:3]}\n{board[3:6]}\n{board[6:]}")
winner = Terminal(board)
if winner:
print(f"Winner: {winner}")
break
if turn(board) == "X":
action = int(input("Your move (0..8)? "))
if board[action] != ".":
print("Invalid move. Try again.")
continue
else:
action = player(board)
print("minimax chooses", action)
board = Result(board, action)
The example of too-many-return-statements is as above, but my scenario is as follows, how to make the function more beautiful?
def func():
# do something1
ret1 = do_something1()
if ret1 != 0:
return ret1
# do something2
ret2 = do_something2()
if ret2 != 0:
return ret2
# ......
return 0
You could try something like this:
def foo(x):
nums = ['one','two','three','four','five','six','seven']
return 'This is ' + nums[x-1]
to solve your example. And you could solve your scenario like this:
def func():
functions = [do_something1,do_something2,...]
for function in functions:
ret = function()
if ret != 0:
return ret
return 0
Check out this example.
It converted:
def func(x):
if x == 1:
return "hi1"
if x == 2:
return "hi2"
if x == 3:
return "hi3"
if x == 4:
return "hi4"
if x == 5:
return "hi5"
if x == 6:
return "hi6"
if x == 7:
return "hi7"
to:
d = {1: "hi1", 2: "hi2", 3: "hi3", 4: "hi4", 5: "hi5", 6: "hi6", 7: "hi7"}
def func(x):
return d[x]
Just to silence it another option is:
def func(x):
if x == something1:
res = "something1"
elif x == something2:
res = "something2"
elif x == something3:
res = "something3"
elif x == something4:
res = "something4"
elif x == something5:
res = "something5"
elif x == something6:
res = "something6"
elif x == something7:
res = "something7"
else:
res = "default"
return res
You can also silence it in settings.json file if you think it's too strict rule which I think it is:
"python.linting.pylintArgs": [
"--disable=R0911"
],
def func():
res = default_res
if foo:
res = res_foo
elif bar:
res = res_bar
return res
I recommend doing it this way
Using some form of lookup is the way to handle this. A dictionary is a good general purpose idea. A list is useful when the lookup 'key' is an integer.
HOWEVER
When using a lookup technique you have to consider that your 'key' may not be available. If your dictionary (for example) has 7 integer keys (1->7 inclusive) and your function is passed a value of 8, what are you going to do? You could allow the exception and deal with that separately or you need to avoid an exception and return some default value, perhaps implicitly.
In general you can use the proposed solution by pylint documentation:
https://pylint.pycqa.org/en/latest/user_guide/messages/refactor/too-many-return-statements.html
But IMHO in some case it's impossible to use.
I don't kwonw your specific case, but you can disable pylint in a specific line of code using a comment like this.
def func(): #pylint: disable=R0911
# do something1
ret1 = do_something1()
if ret1 != 0:
return ret1
# do something2
ret2 = do_something2()
if ret2 != 0:
return ret2
# ......
return 0
I'm currently working on a project where I need to implement a Minimax algorithm to create a tic-tac-toe game. At some point I'm comparing tuples and floats, and while I know this can't actually be done, I don't know where that problem comes from or how do I fix it. If anyone could help me understand that would be amazing. Thanks!
Here's my code
import copy
X = "X"
O = "O"
EMPTY = None
def initial_state():
"""
Returns starting state of the board.
"""
return [[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY],
[EMPTY, EMPTY, EMPTY]]
def player(board):
Xboard_count = board[0].count(X) + board[1].count(X) + board[2].count(X)
if Xboard_count % 2 == 1:
return 0
else:
return X
def actions(board):
"""
Returns set of all possible actions (i, j) available on the board.
"""
move = []
for i in range(3):
for j in range(3):
if board[i][j] == EMPTY:
move.append((i, j))
return move
def result(board, move):
"""
Returns the board that results from making move (i, j) on the board.
"""
if move not in actions(board):
raise Exception("this action is not a valid action")
dcopb = copy.deepcopy(board)
dcopb[move[0]][move[1]] = player(board)
return dcopb
def winner(board):
"""
Returns the winner of the game, if there is one.
"""
for x in range(3):
if board[x][0] == board[x][1] == board[x][2] != EMPTY:
return board[x][0]
for y in range(3):
if board[0][y] == board[1][y] == board[2][y] != EMPTY:
return board[0][y]
if board[0][0] == board[1][1] == board[2][2] != EMPTY:
return board[0][0]
if board[2][0] == board[1][1] == board[0][2] != EMPTY:
return board[2][0]
else:
return None
def terminal(board):
"""
Returns True if game is over, False otherwise.
"""
if winner(board) == X:
return True
elif winner(board) == O:
return True
for x in range(3):
for y in range(3):
if board[x][y] is None:
return False
return True
('si winner est égal à X :\n'
' return True\n'
' si winner est égal à 0\n'
' return true \n'
' \n'
' si le nombre de cases vides == 0\n'
' return true\n'
' else:\n'
' return false')
def utility(board):
"""
Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
"""
if winner(board) == X:
return 1
if winner(board) == 0:
return -1
else:
return 0
def minimax(board):
"""
Returns the optimal action for the current player on the board.
"""
if terminal(board):
return None
if player(board) == X:
v = v = float('-inf')
for move in actions(board):
temp = MinValue(result(board, move))
if temp > v:
v = temp
best = move
else:
v = float('inf')
for move in actions(board):
temp = MaxValue(result(board, move))
if temp < v:
v = temp
best = move
return best
def MaxValue(board):
if terminal(board):
return utility(board), None
v = float('-inf')
for move in actions(board):
v = max(v, MinValue(result(board, move)))
return
def MinValue(board):
if terminal(board):
return utility(board), None
v = float('inf')
for move in actions(board):
v = min(v, MaxValue(result(board, move)))
return v
I get this exact error message
TypeError: '>' not supported between instances of 'tuple' and 'float'
You're returning a tuple at return utility(board), None in MinValue. So when you call the function at temp = MinValue(result(board, move)), temp is a tuple if that return statement was invoked
v is a float. Now when you try to compare it with temp at if temp < v:, you're comparing a float with a tuple
First of all, there was a problem with the MinValue and MaxValue function, they were sometimes returning two values instead of one.
Then, there was the same TypeError, this time with a problem when comparing NoneType variables with floats. Turns out there was just a small mistake because my MaxValue function wasn't returning anything.
I was working on a Sudoku solver, and when I ran it, it just didn't work- the function f returned a none type. After debugging, I found out the code didn't solve some of the squares. I don't know why this happened, but I suspect the loop checking all the numbers possible in a square stops after finding one possible number and doesn't check the rest too. Can you please pinpoint me the problem in my code? Thanks.
Note: for empty spaces in the Sudoku, enter 0.
# enter the Sudoku as a two-dimensional array (a[i][j])
def square_check(array, loc_x, loc_y, val):
added_x = loc_x - (loc_x % 3)
added_y = loc_y - (loc_y % 3)
for i in range(3):
for j in range(3):
if i+added_y != loc_y or j+added_x != loc_x:
if array[i+added_y][j+added_x] == val:
return False
return True
def line_check(array, loc_x, loc_y, val):
for i in range(9):
if i != loc_x:
if array[loc_y][i] == val:
return False
return True
def col_check(array, loc_x, loc_y, val):
for i in range(9):
if i != loc_y:
if array[i][loc_x] == val:
return False
return True
def f(array):
flag = True
mark = True
for i in range(0, 9):
for j in range(0, 9):
if array[i][j] == 0:
flag = False
if flag:
return array
for i in range(0, 9):
for j in range(0, 9):
if array[i][j] == 0:
for num in range(1, 10):
if square_check(array, j, i, num):
if col_check(array, j, i, num):
if line_check(array, j, i, num):
new_array = array.copy()
new_array[i][j] = num
mark = False
f(new_array)
if mark:
break
def to_str(array):
for i in range(len(array)):
print(array[i])
print("")
to_str(f(sudoku))
I created a sudoku solver with backtracking algorithm (Python 3.8). It is a recursive algorithm. The sudoku board (puzzle) is a global variable (multiple functions need to share it). The solve() function does its task, but the value of the board doesn't change even after using the global keyword. Your help is needed.
The code:
board = [
[5,3,0,0,7,0,0,0,0],
[6,0,0,1,9,5,0,0,0],
[0,9,8,0,0,0,0,6,0],
[8,0,0,0,6,0,0,0,3],
[4,0,0,8,0,3,0,0,1],
[7,0,0,0,2,0,0,0,6],
[0,6,0,0,0,0,2,8,0],
[0,0,0,4,1,9,0,0,5],
[0,0,0,0,8,0,0,7,9]]
def isPossible(y, x, val): # checks if it is legal to put a value at a certain position
for row in board: # row condition
if val == row[x]:
return False
for col in board[y]: # column condition
if val == col:
return False
# subcell condition
subCellRow = (y // 3) * 3
subCellCol = (x // 3) * 3
for row in board[subCellRow:subCellRow + 3]:
for col in row[subCellCol:subCellCol + 3]:
if val == col:
return False
return True
def solve():
global board
for y in range(9):
for x in range(9):
if board[y][x] == 0:
for n in range(1, 10):
if isPossible(y, x, n): # python stairs
board[y][x] = n
solve() # recursion starts
board[y][x] = 0 # 1-line backtracking algorithm
return
printPuzzle() # prints the solved puzzle
return True
def printPuzzle(): # to display the puzzle
print()
for row in board:
for val in row:
print(val, end = ' ')
print()
printPuzzle()
solve()
printPuzzle() # prints the unsolved board
This wasn't the mistake of the global variable, this is happening because there is a slight mistake in your implementation of the algorithm which is reassigning the value to zero at board[y][x] = 0.
Here is the implementation which I think is correct.
board = [
[5,3,0,0,7,0,0,0,0],
[6,0,0,1,9,5,0,0,0],
[0,9,8,0,0,0,0,6,0],
[8,0,0,0,6,0,0,0,3],
[4,0,0,8,0,3,0,0,1],
[7,0,0,0,2,0,0,0,6],
[0,6,0,0,0,0,2,8,0],
[0,0,0,4,1,9,0,0,5],
[0,0,0,0,8,0,0,7,9]]
def isPossible(y, x, val): # checks if it is legal to put a value at a certain position
for row in board: # row condition
if val == row[x]:
return False
for col in board[y]: # column condition
if val == col:
return False
# subcell condition
subCellRow = (y // 3) * 3
subCellCol = (x // 3) * 3
for row in board[subCellRow:subCellRow + 3]:
for col in row[subCellCol:subCellCol + 3]:
if val == col:
return False
return True
def solve():
global board
for y in range(9):
for x in range(9):
if board[y][x] == 0:
for n in range(1, 10):
if isPossible(y, x, n): # python stairs
board[y][x] = n
if solve():
return True # recursion starts
board[y][x] = 0
return False
printPuzzle() # prints the solved puzzle
return True
def printPuzzle(): # to display the puzzle
print()
for row in board:
for val in row:
print(val, end = ' ')
print()
printPuzzle()
solve()
printPuzzle() # prints the unsolved board
Output I am getting: