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 just have learned backtracking yesterday and I'm so confused. In this section
if y == 9:
if x == 8:
display(s)
return 1
else:
sudoku(s,x+1,0)
I use return to stop running program but print(sudoku(s,0,0)) still prints None value and not 1. Can anyone figure out why?
s = [[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 display(s):
for i in s:
print(*i)
def checkvalid(s,x,y,k):
for i in range(9):
if s[x][i] == k or s[i][y] == k:
return False
for i in range(x//3*3, x//3*3+3):
for j in range(y//3*3, y//3*3+3):
if s[i][j] == k:
return False
return True
def sudoku(s,x,y):
if y == 9:
if x == 8:
display(s)
return 1
else:
sudoku(s,x+1,0)
elif s[x][y] == 0:
for k in range(1,10):
if checkvalid(s,x,y, k):
s[x][y] = k
sudoku(s,x,y+1)
s[x][y] = 0
else:
sudoku(s,x,y+1)
print(sudoku(s,0,0))
This is because you call sudoku() recursively. Thereby you return 1 to the last caller, which is sudoku() itself from one of the elif/else paths. In those paths you don't return the value back. Instead, you leave the if/elif/else block and exit the function by returning the default value of None.
My Python code is throwing an expected indented block error right after the elif.
I am having trouble seeing where the indenting mistake is.
What indentation mistake have I made?
def expandProcedure(node, queue):
successors = []
n = 4
while (n > 0):
parent = node
depth = node[2] + 1
pathCost = node[3] + 1
newState = testState(node[0], n)
if newState == 0:
## do nothing
elif inQueue(newState[0], queue):
#do nothing
else:
s = makeNode(newState, parent, depth, pathCost)
successors.insert(0, s)
n = n - 1
return successors
You can't have a null block. Use the pass (non-)command:
if newState == 0:
pass
elif inQueue(newState[0], queue):
pass
You cannot make empty blocks in Python. At least put pass there if you want a real do-nothing:
if newState == 0:
pass
elif newState != 0:
pass
I was trying to make a simple Ideal Gases calculator but for some reason I cannot seem to get any of my if statements to return any values.
def Ideal():
p = input("Pressure:")
v = input("Volume:")
n = input("Moles:")
t = input("Temperature in K:")
r = 8.31
if p is None:
y =(n*r*t)/v
return y
elif v is None:
return(n*r*t)/p
elif n is None:
return (p*v)/(r*t)
elif n is None:
return (p*v)/(n*r)
I went from the above code to this
def input_float(prompt):
result = input(prompt)
if result:
return float(result)
else:
return None
def Ideal():
p = input_float("Pressure:")
v = input_float("Volume:")
n = input_float("Moles:")
t = input_float("Temperature in K:")
r = 8.31
if p is None:
y =(n*r*t)/v
print(y)
elif v is None:
u = (n*r*t)/p
print(u)
elif n is None:
i = (p*v)/(r*t)
print(i)
elif t is None:
o = (p*v)/(n*r)
print(o)
else:
print("This should never happen")
Ideal()
and now it works great. But I am guessing there was an easier way to go about this and since I am here to learn I would love to here opinions.
input gives you a string. If the user provides no input, you don't get None — you get an empty string. You might want to make a new function to take input from the user but return None if they enter nothing and parse it otherwise:
def input_float(prompt):
result = input(prompt)
if result:
return float(result)
else:
return None
Then you can replace the inputs in your program with input_floats.