Skip to content

Commit 60bd05a

Browse files
committed
add files
1 parent 15edb5c commit 60bd05a

File tree

2 files changed

+278
-0
lines changed

2 files changed

+278
-0
lines changed

MiniMax/TicTacTok.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
from flask import Flask, render_template, request, jsonify
2+
3+
app = Flask(__name__)
4+
5+
class TicTacToe:
6+
def __init__(self):
7+
"""Initialize the Tic-Tac-Toe game."""
8+
self.board = [[" " for _ in range(3)] for _ in range(3)]
9+
self.players = ["X", "O"]
10+
self.current_player = 0
11+
12+
def reset(self):
13+
self.board = [[" " for _ in range(3)] for _ in range(3)]
14+
self.current_player = 0
15+
16+
def check_winner(self):
17+
"""Checks if there is a winner or a draw."""
18+
for i in range(3):
19+
if self.board[i][0] == self.board[i][1] == self.board[i][2] != " ":
20+
return self.board[i][0] # Row winner
21+
if self.board[0][i] == self.board[1][i] == self.board[2][i] != " ":
22+
return self.board[0][i] # Column winner
23+
24+
if self.board[0][0] == self.board[1][1] == self.board[2][2] != " ":
25+
return self.board[0][0]
26+
if self.board[0][2] == self.board[1][1] == self.board[2][0] != " ":
27+
return self.board[0][2]
28+
29+
for row in self.board:
30+
if " " in row:
31+
return None # Game is still ongoing
32+
33+
return "Draw" # No spaces left and no winner
34+
35+
def make_move(self, row, col):
36+
"""Handles a player's move."""
37+
if self.board[row][col] != " ":
38+
raise ValueError("Cell is already occupied!")
39+
40+
self.board[row][col] = self.players[self.current_player]
41+
42+
def switch_player(self):
43+
"""Switches to the next player."""
44+
self.current_player = 1 - self.current_player
45+
46+
def find_best_move(self):
47+
"""Finds the best move for the AI using Minimax."""
48+
def minimax(board, is_maximizing):
49+
winner = self.check_winner()
50+
if winner == self.players[1]:
51+
return 1
52+
elif winner == self.players[0]:
53+
return -1
54+
elif winner == "Draw":
55+
return 0
56+
57+
if is_maximizing:
58+
best_score = float('-inf')
59+
for i in range(3):
60+
for j in range(3):
61+
if board[i][j] == " ":
62+
board[i][j] = self.players[1]
63+
score = minimax(board, False)
64+
board[i][j] = " "
65+
best_score = max(best_score, score)
66+
return best_score
67+
else:
68+
best_score = float('inf')
69+
for i in range(3):
70+
for j in range(3):
71+
if board[i][j] == " ":
72+
board[i][j] = self.players[0]
73+
score = minimax(board, True)
74+
board[i][j] = " "
75+
best_score = min(best_score, score)
76+
return best_score
77+
78+
best_score = float('-inf')
79+
best_move = (-1, -1)
80+
for i in range(3):
81+
for j in range(3):
82+
if self.board[i][j] == " ":
83+
self.board[i][j] = self.players[1]
84+
score = minimax(self.board, False)
85+
self.board[i][j] = " "
86+
if score > best_score:
87+
best_score = score
88+
best_move = (i, j)
89+
return best_move
90+
91+
# Initialize game
92+
game = TicTacToe()
93+
94+
@app.route('/')
95+
@app.route('/')
96+
def index():
97+
# Prepare the board with indices
98+
board_with_indices = [
99+
[{"value": cell, "row": i, "col": j} for j, cell in enumerate(row)]
100+
for i, row in enumerate(game.board)
101+
]
102+
return render_template(
103+
'index.html',
104+
board=board_with_indices,
105+
current_player=game.players[game.current_player]
106+
)
107+
108+
@app.route('/move', methods=['POST'])
109+
def move():
110+
data = request.get_json()
111+
row, col = data['row'], data['col']
112+
113+
try:
114+
game.make_move(row, col)
115+
winner = game.check_winner()
116+
if not winner:
117+
game.switch_player()
118+
if game.current_player == 1: # AI's turn
119+
ai_move = game.find_best_move()
120+
game.make_move(ai_move[0], ai_move[1])
121+
winner = game.check_winner()
122+
if not winner:
123+
game.switch_player()
124+
125+
return jsonify({
126+
'board': game.board,
127+
'winner': winner,
128+
'current_player': game.players[game.current_player]
129+
})
130+
except ValueError as e:
131+
return jsonify({'error': str(e)}), 400
132+
133+
@app.route('/restart', methods=['POST'])
134+
def restart():
135+
# Reset the game board and state
136+
global game
137+
game.reset() # Assuming there's a reset method in your game class
138+
return '', 204 # Respond with no content
139+
140+
if __name__ == '__main__':
141+
app.run(host='0.0.0.0', port=5000, debug=True, threaded=True)

MiniMax/templates/index.html

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Tic Tac Toe</title>
7+
<style>
8+
body {
9+
font-family: Arial, sans-serif;
10+
display: flex;
11+
justify-content: center;
12+
align-items: center;
13+
height: 100vh;
14+
margin: 0;
15+
background-color: #f4f4f9;
16+
}
17+
18+
.container {
19+
text-align: center;
20+
}
21+
22+
.board {
23+
display: grid;
24+
grid-template-columns: repeat(3, 100px);
25+
grid-gap: 5px;
26+
margin: 20px auto;
27+
}
28+
29+
.cell {
30+
width: 100px;
31+
height: 100px;
32+
display: flex;
33+
justify-content: center;
34+
align-items: center;
35+
font-size: 2em;
36+
background: #fff;
37+
border: 2px solid #333;
38+
cursor: pointer;
39+
}
40+
41+
.cell.taken {
42+
cursor: not-allowed;
43+
background: #ddd;
44+
}
45+
46+
.message {
47+
font-size: 1.2em;
48+
margin-top: 10px;
49+
}
50+
51+
button {
52+
padding: 10px 20px;
53+
font-size: 1em;
54+
cursor: pointer;
55+
margin-top: 10px;
56+
}
57+
58+
</style>
59+
</head>
60+
<body>
61+
<div class="container">
62+
<h1>Tic Tac Toe</h1>
63+
<div class="board" id="board">
64+
{% for row in board %}
65+
{% for cell in row %}
66+
<div class="cell {{ 'taken' if cell.value != ' ' }}" data-row="{{ cell.row }}" data-col="{{ cell.col }}">
67+
{{ cell.value }}
68+
</div>
69+
{% endfor %}
70+
{% endfor %}
71+
</div>
72+
<div class="message" id="message">
73+
Player {{ current_player }}'s turn.
74+
</div>
75+
<button id="restart">Restart</button>
76+
</div>
77+
78+
<script>
79+
const board = document.getElementById('board');
80+
const message = document.getElementById('message');
81+
82+
board.addEventListener('click', function(e) {
83+
if (e.target.classList.contains('cell') && !e.target.classList.contains('taken')) {
84+
const row = e.target.getAttribute('data-row');
85+
const col = e.target.getAttribute('data-col');
86+
87+
fetch('/move', {
88+
method: 'POST',
89+
headers: {
90+
'Content-Type': 'application/json'
91+
},
92+
body: JSON.stringify({ row: parseInt(row), col: parseInt(col) })
93+
})
94+
.then(response => response.json())
95+
.then(data => {
96+
if (data.error) {
97+
alert(data.error);
98+
} else {
99+
updateBoard(data.board);
100+
if (data.winner) {
101+
message.textContent = data.winner === 'Draw' ? "It's a draw!" : `Player ${data.winner} wins!`;
102+
board.classList.add('disabled');
103+
} else {
104+
message.textContent = `Player ${data.current_player}'s turn.`;
105+
}
106+
}
107+
});
108+
}
109+
});
110+
111+
function updateBoard(newBoard) {
112+
const cells = document.querySelectorAll('.cell');
113+
cells.forEach((cell, index) => {
114+
const row = Math.floor(index / 3);
115+
const col = index % 3;
116+
cell.textContent = newBoard[row][col];
117+
if (newBoard[row][col] !== ' ') {
118+
cell.classList.add('taken');
119+
}
120+
});
121+
}
122+
123+
const restartButton = document.getElementById('restart');
124+
125+
restartButton.addEventListener('click', () => {
126+
fetch('/restart', { method: 'POST' })
127+
.then(() => {
128+
location.reload(); // Reload the page to reflect the reset state
129+
})
130+
.catch(err => {
131+
console.error('Error restarting the game:', err);
132+
});
133+
});
134+
135+
</script>
136+
</body>
137+
</html>

0 commit comments

Comments
 (0)