diff --git a/tictactoe.py b/tictactoe.py index 4c88983..372def8 100644 --- a/tictactoe.py +++ b/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 random if os.path.isfile('./savestate.py'): from savestate import save @@ -15,11 +13,12 @@ field = [[" ", "|", " ", "|", " "], player1 = True # bool that states which player is currently playing player1_char = "X" player2_char = "O" -draw = False +ai = False +# verbose = True + def printer(): # function that prints the field - print(" 0 1 2", end="\n") for j in range(0, len(field)): 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 - - 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.") - - # 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 - + if ai and not player1: + print("Next move is being calculated...") + best_move = minimax(field, not player1) + print("AI moved to ", best_move[1], best_move[2]) + do_move(best_move[1], best_move[2]) + # check_move(best_move[0], best_move[1], False) 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): - global player1 # Tells do_move to look for the global var player1 - global draw + global player1 field[y][x] = (player1_char if player1 else player2_char) printer() - if not check_win(): + win = check_win(field) + if win == (0, 0): f = open("savestate.py", "w") f.write("save = " + repr(field)) f.close() player1 = not player1 turn() - elif check_win(): - if draw: - print("Its a draw.") - else: - print(f"Player {1 if player1 else 2} won") + elif win == (1, 1): + print("It's a draw.") + + elif win == (1, 0): + 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"): 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) y = int(y * 2) 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() else: 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:") if os.path.isfile('./savestate.py'): @@ -120,14 +173,25 @@ if os.path.isfile('./savestate.py'): for k in range(0, len(field)): if field[j][k] == " ": spacecount += 1 - # (player1 = True) if spacecount % 2 == 1 else (player1 = False) if spacecount % 2 == 0: player1 = False - break 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() print(f"Player {1 if player1 else 2} ({player1_char if player1 else player2_char}) will start playing.") turn() +