Added Ai MiniMax functionality
This commit is contained in:
182
tictactoe.py
182
tictactoe.py
@@ -1,7 +1,5 @@
|
|||||||
# first two player tictactoe then one player against ai
|
|
||||||
# Ich hab jetzt einfach mal ohne OOP(bzw MVC/MVP) angefangen.
|
|
||||||
# Ich denke es ist einfacher es erst so zu machen und dann einzuteilen.
|
|
||||||
import os.path
|
import os.path
|
||||||
|
import random
|
||||||
|
|
||||||
if os.path.isfile('./savestate.py'):
|
if os.path.isfile('./savestate.py'):
|
||||||
from savestate import save
|
from savestate import save
|
||||||
@@ -15,11 +13,12 @@ field = [[" ", "|", " ", "|", " "],
|
|||||||
player1 = True # bool that states which player is currently playing
|
player1 = True # bool that states which player is currently playing
|
||||||
player1_char = "X"
|
player1_char = "X"
|
||||||
player2_char = "O"
|
player2_char = "O"
|
||||||
draw = False
|
ai = False
|
||||||
|
|
||||||
|
|
||||||
|
# verbose = True
|
||||||
|
|
||||||
def printer(): # function that prints the field
|
def printer(): # function that prints the field
|
||||||
|
|
||||||
print(" 0 1 2", end="\n")
|
print(" 0 1 2", end="\n")
|
||||||
for j in range(0, len(field)):
|
for j in range(0, len(field)):
|
||||||
print("0" if j == 0 else "1" if j == 2 else "2" if j == 4 else " ", end=" ")
|
print("0" if j == 0 else "1" if j == 2 else "2" if j == 4 else " ", end=" ")
|
||||||
@@ -29,81 +28,135 @@ def printer(): # function that prints the field
|
|||||||
|
|
||||||
|
|
||||||
def turn(): # function that checks whose turn it is and where they can place their symbol
|
def turn(): # function that checks whose turn it is and where they can place their symbol
|
||||||
|
if ai and not player1:
|
||||||
while True:
|
print("Next move is being calculated...")
|
||||||
next_move_x = int(input(
|
best_move = minimax(field, not player1)
|
||||||
f"Player {1 if player1 else 2} ({player1_char if player1 else player2_char}), please choose the x ("
|
print("AI moved to ", best_move[1], best_move[2])
|
||||||
f"horizontal) coordinates of your next move: "))
|
do_move(best_move[1], best_move[2])
|
||||||
if next_move_x in list(range(3)):
|
# check_move(best_move[0], best_move[1], False)
|
||||||
break
|
|
||||||
else:
|
|
||||||
print("Please be sure to input a valid number (0-2). \nPlease try Again.")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
next_move_y = int(input(
|
|
||||||
f"Player {1 if player1 else 2} ({player1_char if player1 else player2_char}), please choose the y ("
|
|
||||||
f"vertical) coordinates of your next move: "))
|
|
||||||
if next_move_y in list(range(3)):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print("Please be sure to input a valid number (0-2). \nPlease try Again.")
|
|
||||||
|
|
||||||
# it might be possible to compress the two above loops into one
|
|
||||||
|
|
||||||
return check_move(next_move_x, next_move_y)
|
|
||||||
|
|
||||||
|
|
||||||
def check_win():
|
|
||||||
global draw
|
|
||||||
for j in range(0, len(field), 2):
|
|
||||||
if field[j][0] == field[j][2] == field[j][4] == (player1_char if player1 else player2_char):
|
|
||||||
return True
|
|
||||||
elif field[0][j] == field[2][j] == field[4][j] == (player1_char if player1 else player2_char):
|
|
||||||
return True
|
|
||||||
if field[0][0] == field[2][2] == field[4][4] == (player1_char if player1 else player2_char):
|
|
||||||
return True
|
|
||||||
elif field[0][4] == field[2][2] == field[4][0] == (player1_char if player1 else player2_char):
|
|
||||||
return True
|
|
||||||
if all(field[j][k] != " " for j in range(len(field)) for k in range(len(field))):
|
|
||||||
# code to execute if all elements are not equal to " "
|
|
||||||
draw = True
|
|
||||||
return True
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return False
|
while True:
|
||||||
|
next_move_x = int(input(
|
||||||
|
f"Player {1 if player1 else 2} ({player1_char if player1 else player2_char}), please choose the x ("
|
||||||
|
f"horizontal) coordinates of your next move: "))
|
||||||
|
if next_move_x in list(range(3)):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print("Please be sure to input a valid number (0-2). \nPlease try Again.")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
next_move_y = int(input(
|
||||||
|
f"Player {1 if player1 else 2} ({player1_char if player1 else player2_char}), please choose the y ("
|
||||||
|
f"vertical) coordinates of your next move: "))
|
||||||
|
if next_move_y in list(range(3)):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print("Please be sure to input a valid number (0-2). \nPlease try Again.")
|
||||||
|
|
||||||
|
return check_move(next_move_x, next_move_y, True)
|
||||||
|
|
||||||
|
|
||||||
|
def check_win(ifield): # nothing 0, 0; draw 1, 1; win player1 1, 0; win player2 0, 1
|
||||||
|
for j in range(0, len(ifield), 2):
|
||||||
|
if ifield[j][0] == ifield[j][2] == ifield[j][4] == player1_char:
|
||||||
|
return 1, 0
|
||||||
|
elif ifield[j][0] == ifield[j][2] == ifield[j][4] == player2_char:
|
||||||
|
return 0, 1
|
||||||
|
elif ifield[0][j] == ifield[2][j] == ifield[4][j] == player1_char:
|
||||||
|
return 1, 0
|
||||||
|
elif ifield[0][j] == ifield[2][j] == ifield[4][j] == player2_char:
|
||||||
|
return 0, 1
|
||||||
|
|
||||||
|
if ifield[0][0] == ifield[2][2] == ifield[4][4] == player1_char:
|
||||||
|
return 1, 0
|
||||||
|
elif ifield[0][0] == ifield[2][2] == ifield[4][4] == player2_char:
|
||||||
|
return 0, 1
|
||||||
|
elif ifield[0][4] == ifield[2][2] == ifield[4][0] == player1_char:
|
||||||
|
return 1, 0
|
||||||
|
elif ifield[0][4] == ifield[2][2] == ifield[4][0] == player2_char:
|
||||||
|
return 0, 1
|
||||||
|
elif all(ifield[j][k] != " " for j in range(len(ifield)) for k in range(len(ifield))):
|
||||||
|
return 1, 1
|
||||||
|
else:
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
|
||||||
def do_move(x, y):
|
def do_move(x, y):
|
||||||
global player1 # Tells do_move to look for the global var player1
|
global player1
|
||||||
global draw
|
|
||||||
field[y][x] = (player1_char if player1 else player2_char)
|
field[y][x] = (player1_char if player1 else player2_char)
|
||||||
printer()
|
printer()
|
||||||
if not check_win():
|
win = check_win(field)
|
||||||
|
if win == (0, 0):
|
||||||
f = open("savestate.py", "w")
|
f = open("savestate.py", "w")
|
||||||
f.write("save = " + repr(field))
|
f.write("save = " + repr(field))
|
||||||
f.close()
|
f.close()
|
||||||
player1 = not player1
|
player1 = not player1
|
||||||
turn()
|
turn()
|
||||||
elif check_win():
|
elif win == (1, 1):
|
||||||
if draw:
|
print("It's a draw.")
|
||||||
print("Its a draw.")
|
|
||||||
else:
|
elif win == (1, 0):
|
||||||
print(f"Player {1 if player1 else 2} won")
|
print("Player 1 won")
|
||||||
|
if os.path.exists("savestate.py"):
|
||||||
|
os.remove("savestate.py")
|
||||||
|
elif win == (0, 1):
|
||||||
|
print("Player 2 won")
|
||||||
if os.path.exists("savestate.py"):
|
if os.path.exists("savestate.py"):
|
||||||
os.remove("savestate.py")
|
os.remove("savestate.py")
|
||||||
|
|
||||||
|
|
||||||
def check_move(x, y):
|
|
||||||
# translate x and y coordinates to field coordinates
|
def check_move(x, y, verbose):
|
||||||
x = int(x * 2)
|
x = int(x * 2)
|
||||||
y = int(y * 2)
|
y = int(y * 2)
|
||||||
if not field[y][x] == " ":
|
if not field[y][x] == " ":
|
||||||
print("Your given coordinates are already occupied. \nPlease Try again.\n")
|
if verbose:
|
||||||
|
print("Your given coordinates are already occupied. \nPlease Try again.\n")
|
||||||
turn()
|
turn()
|
||||||
else:
|
else:
|
||||||
do_move(x, y)
|
do_move(x, y)
|
||||||
|
|
||||||
|
|
||||||
|
def minimax(current_field, is_maximizing):
|
||||||
|
checkwin = check_win(current_field)
|
||||||
|
if checkwin == (1, 1): # Draw
|
||||||
|
return 0, 0, 0
|
||||||
|
if checkwin == (1, 0): # Player wins
|
||||||
|
return -1, 0, 0
|
||||||
|
if checkwin == (0, 1): # AI wins
|
||||||
|
return 1, 0, 0
|
||||||
|
|
||||||
|
best_score = float('-inf') if is_maximizing else float('inf')
|
||||||
|
best_move = None
|
||||||
|
|
||||||
|
for j in range(0, len(current_field), 2):
|
||||||
|
for k in range(0, len(current_field), 2):
|
||||||
|
if current_field[j][k] == " ":
|
||||||
|
current_field[j][k] = player1_char if not is_maximizing else player2_char
|
||||||
|
score = minimax(current_field, not is_maximizing)[0]
|
||||||
|
current_field[j][k] = " "
|
||||||
|
|
||||||
|
if is_maximizing and score > best_score:
|
||||||
|
best_score = score
|
||||||
|
best_move = j, k
|
||||||
|
elif not is_maximizing and score < best_score:
|
||||||
|
best_score = score
|
||||||
|
best_move = j, k
|
||||||
|
|
||||||
|
if best_score == 0 and best_move == (0, 0): # is None:
|
||||||
|
empty_cells = [(j, k) for j in range(0, len(current_field), 2) for k in range(0, len(current_field), 2) if
|
||||||
|
current_field[j][k] == " "]
|
||||||
|
random.seed()
|
||||||
|
random_move = random.choice(empty_cells)
|
||||||
|
return 0, random_move[0], random_move[1]
|
||||||
|
else:
|
||||||
|
if best_move is None:
|
||||||
|
return 0, -1, -1 # No available moves
|
||||||
|
else:
|
||||||
|
print('The next best move is:', best_move, "score: ", best_score)
|
||||||
|
return best_score, best_move[1], best_move[0]
|
||||||
|
|
||||||
|
|
||||||
print("Welcome to Kat&Paul's TicTacToe:")
|
print("Welcome to Kat&Paul's TicTacToe:")
|
||||||
|
|
||||||
if os.path.isfile('./savestate.py'):
|
if os.path.isfile('./savestate.py'):
|
||||||
@@ -120,14 +173,25 @@ if os.path.isfile('./savestate.py'):
|
|||||||
for k in range(0, len(field)):
|
for k in range(0, len(field)):
|
||||||
if field[j][k] == " ":
|
if field[j][k] == " ":
|
||||||
spacecount += 1
|
spacecount += 1
|
||||||
# (player1 = True) if spacecount % 2 == 1 else (player1 = False)
|
|
||||||
if spacecount % 2 == 0:
|
if spacecount % 2 == 0:
|
||||||
player1 = False
|
player1 = False
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
inp = input("That was not a valid input. Type y/n if you want to continue or not: ")
|
inp = input("That was not a valid input. Type y/n if you want to continue or not: ")
|
||||||
|
|
||||||
|
'''if input("Do you want to play against AI or local multiplayer? 1/2: ") == "1":
|
||||||
|
ai = True'''
|
||||||
|
inp = input("Do you want to play against AI or local multiplayer? 1/2: ")
|
||||||
|
while True:
|
||||||
|
if inp == "1":
|
||||||
|
ai = True
|
||||||
|
break
|
||||||
|
elif inp == "2":
|
||||||
|
break
|
||||||
|
inp = input("That was not a valid input. Type 1/2 if you want to play against AI or local multiplayer: ")
|
||||||
|
|
||||||
|
|
||||||
printer()
|
printer()
|
||||||
print(f"Player {1 if player1 else 2} ({player1_char if player1 else player2_char}) will start playing.")
|
print(f"Player {1 if player1 else 2} ({player1_char if player1 else player2_char}) will start playing.")
|
||||||
turn()
|
turn()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user