Split tictactoe into different files, classes, and functions to comply with MVC

This commit is contained in:
2025-09-22 19:43:52 +02:00
parent 18e1fb9a1e
commit 17b832064c
12 changed files with 357 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
MVC/main.py Normal file
View File

@@ -0,0 +1,3 @@
import tictactoe
newGame = tictactoe.Tictactoe()
newGame.main()

69
MVC/minimax.py Normal file
View File

@@ -0,0 +1,69 @@
import tictactoe
import random
class Minimax:
def minimax(self, current_field, is_maximizing, player1_char, player2_char):
checkwin = self.check_win(current_field, player1_char, player2_char)
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 = self.minimax(current_field, not is_maximizing, player1_char, player2_char)[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]
def check_win(self, ifield, player1_char, player2_char):
# 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

59
MVC/model.py Normal file
View File

@@ -0,0 +1,59 @@
import os.path
if os.path.isfile("./savestate.py"):
from savestate import save
class Model:
field = None
def __init__(self):
self.field = [[" ", "|", " ", "|", " "],
["", "+", "", "+", ""],
[" ", "|", " ", "|", " "],
["", "+", "", "+", ""],
[" ", "|", " ", "|", " "]]
def do_move(self, x, y, player1, player1_char, player2_char):
self.field[y*2][x*2] = (player1_char if player1 else player2_char)
# printer()
# win = check_win(field)
# if win == (0, 0):
# savetosavestate
"""player1 = not player1
turn() controller"""
'''elif win == (1, 1):
elif win == (1, 0):
elif win == (0, 1):
'''
def loadsavestate(self):
if os.path.isfile('./savestate.py'):
from savestate import save
self.field = save
def checksavestate(self):
if os.path.isfile('./savestate.py'):
return True
return False
def savetosavestate(self):
f = open("savestate.py", "w")
f.write("save = " + repr(self.field))
f.close()
def deletesavestate(self):
os.remove("savestate.py")
def check_move(self, x, y, verbose):
x = int(x * 2)
y = int(y * 2)
if not self.field[y][x] == " ":
if verbose:
# self.printer.occupied()
return False
else:
return True
# else:
# do_move(x, y)

16
MVC/playerhandler.py Normal file
View File

@@ -0,0 +1,16 @@
class Playerhandler:
player1 = None # bool that states which player is currently playing
player1_char = None
player2_char = None
ai = None
def __init__(self):
self.player1 = True # bool that states which player is currently playing
self.player1_char = "X"
self.player2_char = "O"
self.ai = False
'''
def getPlayer(self):
return'''

84
MVC/printer.py Normal file
View File

@@ -0,0 +1,84 @@
# Das hier ist die View
class Printer:
def printfield(self, ifield): # function that prints the field
print(" 0 1 2", end="\n")
for j in range(0, len(ifield)):
print("0" if j == 0 else "1" if j == 2 else "2" if j == 4 else " ", end=" ")
for k in range(0, len(ifield)):
print(str(ifield[j][k]), end=" ")
print("\n", end="")
def occupied(self):
print("Your given coordinates are already occupied. \nPlease Try again.\n")
def aicalc(self):
print("Next move is being calculated...")
def aimoved(self, best_move):
print("AI moved to ", best_move[1] // 2, best_move[2] // 2)
def playermove(self, player1, player1_char, player2_char):
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.")
next_move = (next_move_x, next_move_y)
return next_move
def welcomemessage(self):
print("Welcome to Kat&Paul's TicTacToe:")
def checksavestate(self):
return input("An older savestate has been found. Do you want to continue it? (y/n): ")
def invalidsavestate(self):
return input("That was not a valid input. Type y/n if you want to continue or not: ")
def checkai(self):
inp = input("Do you want to play against AI or local multiplayer? 1/2: ")
while True:
if inp == "1":
return 1
break
elif inp == "2":
return 2
break
return 3
def invalidai(self):
return input("That was not a valid input. Type 1/2 if you want to play against AI or local multiplayer: ")
def startsplaying(self, player1, player1_char, player2_char):
print(f"Player {1 if player1 else 2} ({player1_char if player1 else player2_char}) will start playing.")
def endmessage(self,i, j, ai):
if i == 1 and j == 1:
print("Its a draw.")
if i == 1 and j == 0:
print("Player 1 won.")
if i == 0 and j == 1 and ai:
print("AI won.")
if i == 0 and j == 1 and not ai:
print("Player 2 won.")

126
MVC/tictactoe.py Normal file
View File

@@ -0,0 +1,126 @@
import os.path
import random
import printer
import playerhandler
import model
import minimax
class Tictactoe:
printer = None
playerhandler = None
minimax = None
model = None
def __init__(self):
self.printer = printer.Printer()
self.playerhandler = playerhandler.Playerhandler()
self.minimax = minimax.Minimax()
self.model = model.Model()
def turn(self): # function that checks whose turn it is and where they can place their symbol
self.printer.printfield(self.model.field)
if self.playerhandler.ai and not self.playerhandler.player1:
self.printer.aicalc()
best_move = self.minimax.minimax(self.model.field, not self.playerhandler.player1, self.playerhandler.player1_char, self.playerhandler.player2_char)
self.printer.aimoved(best_move)
self.model.do_move(best_move[1]//2, best_move[2]//2, self.playerhandler.player1, self.playerhandler.player1_char,
self.playerhandler.player2_char)
# self.printer.printfield(self.model.field)
else:
while True:
nextmove = self.printer.playermove(self.playerhandler.player1, self.playerhandler.player1_char,
self.playerhandler.player2_char)
next_move_x, next_move_y = nextmove
# return self.model.check_move(next_move_x, next_move_y, True)
if self.model.check_move(next_move_x, next_move_y, True):
self.model.do_move(next_move_x, next_move_y, self.playerhandler.player1, self.playerhandler.player1_char, self.playerhandler.player2_char)
break
# self.playerhandler.player1 = not self.playerhandler.player1
else:
self.printer.occupied()
self.printer.printfield(self.model.field)
win = self.check_win(self.model.field, self.playerhandler.player1_char, self.playerhandler.player2_char)
if win == (0, 0):
self.model.savetosavestate()
self.playerhandler.player1 = not self.playerhandler.player1
self.turn()
elif win == (1, 1):
self.model.deletesavestate()
elif win == (1, 0):
self.model.deletesavestate()
elif win == (0, 1):
self.model.deletesavestate()
self.printer.endmessage(win[0], win[1], self.playerhandler.ai)
self.turn()
def check_win(self, ifield, player1_char, player2_char):
# 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
# verbose = True
def main(self):
inp = None
self.printer.welcomemessage()
if self.model.checksavestate():
inp = self.printer.checksavestate()
while True:
if inp == "n":
break
if inp == "y":
self.model.loadsavestate()# self.tictactoe.checksavestateplayer()
spacecount = 0
for j in range(0, len(self.model.field)):
for k in range(0, len(self.model.field)):
if self.model.field[j][k] == " ":
spacecount += 1
if spacecount % 2 == 0:
self.playerhandler.player1 = False
break
# self.printer.checkai()
# inp = self.printer.invalidsavestate()
# inp = self.printer.checkai()
while True:
checkai = self.printer.checkai()
if checkai == 1:
self.playerhandler.ai = True
break
elif checkai == 2:
break
elif checkai == 3:
self.printer.invalidai()
# printer.printfield(self.model.field)
self.printer.startsplaying(self.playerhandler.player1, self.playerhandler.player1_char,
self.playerhandler.player2_char)
self.turn()