Tic-Tac-Toe / app.py
hardik-0212's picture
Upload app.py
05a6f90 verified
import gradio as gr
import random
import threading
import time
class TicTacToe:
def __init__(self):
self.reset_game()
self.scores = {
"pvp": {"X": 0, "O": 0, "Draws": 0},
"pvc": {"X": 0, "O": 0, "Draws": 0}
}
def reset_game(self):
"""Reset game state completely"""
self.board = [" " for _ in range(9)]
self.current_player = "X"
self.game_over = False
self.player_names = {"X": "Player X", "O": "Player O"}
def reset_scores_for_mode(self, mode):
"""Reset scores when switching game modes"""
if mode in self.scores:
self.scores[mode] = {"X": 0, "O": 0, "Draws": 0}
def check_winner(self):
win_conditions = [
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6]
]
for condition in win_conditions:
if self.board[condition[0]] == self.board[condition[1]] == self.board[condition[2]] != " ":
return self.board[condition[0]]
if " " not in self.board:
return "Draw"
return None
def computer_move(self):
empty_spots = [i for i, spot in enumerate(self.board) if spot == " "]
if not empty_spots:
return None
for spot in empty_spots:
self.board[spot] = "O"
if self.check_winner() == "O":
self.board[spot] = " "
return spot
self.board[spot] = " "
for spot in empty_spots:
self.board[spot] = "X"
if self.check_winner() == "X":
self.board[spot] = " "
return spot
self.board[spot] = " "
if 4 in empty_spots:
return 4
return random.choice(empty_spots)
def make_move(self, index):
if self.game_over or self.board[index] != " ":
return self.get_game_state()
self.board[index] = self.current_player
winner = self.check_winner()
if winner:
self.game_over = True
if winner != "Draw":
self.scores[self.game_mode][winner] += 1
else:
self.scores[self.game_mode]["Draws"] += 1
else:
self.current_player = "O" if self.current_player == "X" else "X"
if hasattr(self, 'game_mode') and self.game_mode == "pvc" and self.current_player == "O" and not self.game_over:
comp_move = self.computer_move()
if comp_move is not None:
return self.make_move(comp_move)
return self.get_game_state()
def get_game_state(self):
status = ""
if self.game_over:
winner = self.check_winner()
if winner == "Draw":
status = "🤝 Game ended in a draw!"
else:
status = f"🎉 {self.player_names[winner]} wins!"
else:
status = f"🎯 {self.player_names[self.current_player]}'s turn"
current_scores = {"X": 0, "O": 0, "Draws": 0}
if hasattr(self, 'game_mode') and self.game_mode in self.scores:
current_scores = self.scores[self.game_mode]
return {
"board": self.board,
"status": status,
"scores": current_scores,
"game_over": self.game_over
}
def create_ui():
game = TicTacToe()
custom_css = """
#main_title {
text-align: center;
color: #2E86AB;
font-size: 2.5em;
margin-bottom: 20px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
#game_board {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
max-width: 300px;
margin: 20px auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 15px;
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
}
.cell_button {
height: 80px;
font-size: 36px;
font-weight: bold;
border-radius: 10px;
border: 2px solid #fff;
background: rgba(255,255,255,0.9);
backdrop-filter: blur(10px);
transition: all 0.3s ease;
cursor: pointer;
color: #333;
}
.cell_button:hover {
background: rgba(255,255,255,1);
transform: scale(1.05);
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.cell_x {
background: linear-gradient(135deg, #ff6b6b, #ee5a52) !important;
color: white !important;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
border: 2px solid #ff4757 !important;
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4) !important;
}
.cell_o {
background: linear-gradient(135deg, #4ecdc4, #44a08d) !important;
color: white !important;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
border: 2px solid #26d0ce !important;
box-shadow: 0 4px 15px rgba(78, 205, 196, 0.4) !important;
}
.cell_x:hover {
background: linear-gradient(135deg, #ee5a52, #ff6b6b) !important;
transform: scale(1.08) !important;
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.6) !important;
}
.cell_o:hover {
background: linear-gradient(135deg, #44a08d, #4ecdc4) !important;
transform: scale(1.08) !important;
box-shadow: 0 6px 20px rgba(78, 205, 196, 0.6) !important;
}
#status_display {
text-align: center;
font-size: 1.4em;
font-weight: bold;
padding: 15px;
margin: 20px 0;
background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
border-radius: 10px;
color: #333;
}
#scores_section {
background: rgba(255,255,255,0.8);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 20px;
margin: 20px 0;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.score_display {
text-align: center;
font-size: 1.2em;
font-weight: bold;
color: #333;
}
.control_button {
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
border: none;
padding: 12px 25px;
border-radius: 25px;
font-weight: bold;
font-size: 16px;
margin: 5px;
cursor: pointer;
transition: all 0.3s ease;
}
.control_button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.mode_button {
background: linear-gradient(45deg, #f093fb, #f5576c);
color: white;
border: none;
padding: 15px 30px;
border-radius: 25px;
font-weight: bold;
font-size: 18px;
margin: 10px;
cursor: pointer;
transition: all 0.3s ease;
}
.mode_button:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0,0,0,0.2);
}
"""
with gr.Blocks(theme=gr.themes.Soft(), css=custom_css, title="Ultimate Tic-Tac-Toe") as app:
gr.HTML('<h1 id="main_title">🎮 Ultimate Tic-Tac-Toe</h1>')
current_view = gr.State("main_menu")
game_mode = gr.State()
player_x_name = gr.State()
player_o_name = gr.State()
with gr.Column(visible=True, elem_id="main_menu") as main_menu:
gr.HTML('<div style="text-align: center; margin: 30px 0;"><p style="font-size: 1.2em; color: #666;">Challenge yourself in the classic game of strategy!</p></div>')
play_btn = gr.Button("🚀 Start Playing", elem_classes="mode_button", size="lg")
with gr.Column(visible=False, elem_id="mode_select") as mode_select:
gr.HTML('<h2 style="text-align: center; color: #333; margin: 20px 0;">🎯 Select Game Mode</h2>')
with gr.Row():
pvp_btn = gr.Button("👥 Player vs Player", elem_classes="mode_button", size="lg")
pvc_btn = gr.Button("🤖 Player vs Computer", elem_classes="mode_button", size="lg")
back_to_menu_btn = gr.Button("← Back to Menu", elem_classes="control_button")
with gr.Column(visible=False, elem_id="name_input") as name_input:
gr.HTML('<h2 style="text-align: center; color: #333; margin: 20px 0;">📝 Player Setup</h2>')
with gr.Row():
p1_name = gr.Textbox(label="🎯 Player X Name", value="Player X", max_lines=1)
p2_name = gr.Textbox(label="🎯 Player O Name", value="Player O", visible=False, max_lines=1)
start_game_btn = gr.Button("🚀 Start Game", elem_classes="mode_button", size="lg")
back_to_mode_btn = gr.Button("← Back", elem_classes="control_button")
with gr.Column(visible=False, elem_id="game_ui") as game_ui:
with gr.Row(elem_id="scores_section"):
x_score = gr.Textbox("X: 0", label="🔥 Player X Wins", interactive=False, elem_classes="score_display")
draws_score = gr.Textbox("Draws: 0", label="🤝 Draws", interactive=False, elem_classes="score_display")
o_score = gr.Textbox("O: 0", label="⭐ Player O Wins", interactive=False, elem_classes="score_display")
status_display = gr.HTML('<div id="status_display">🎯 Game Starting...</div>')
with gr.Column(elem_id="game_board"):
with gr.Row():
btn_0 = gr.Button(" ", elem_classes="cell_button", size="lg", min_width=80)
btn_1 = gr.Button(" ", elem_classes="cell_button", size="lg", min_width=80)
btn_2 = gr.Button(" ", elem_classes="cell_button", size="lg", min_width=80)
with gr.Row():
btn_3 = gr.Button(" ", elem_classes="cell_button", size="lg", min_width=80)
btn_4 = gr.Button(" ", elem_classes="cell_button", size="lg", min_width=80)
btn_5 = gr.Button(" ", elem_classes="cell_button", size="lg", min_width=80)
with gr.Row():
btn_6 = gr.Button(" ", elem_classes="cell_button", size="lg", min_width=80)
btn_7 = gr.Button(" ", elem_classes="cell_button", size="lg", min_width=80)
btn_8 = gr.Button(" ", elem_classes="cell_button", size="lg", min_width=80)
buttons = [btn_0, btn_1, btn_2, btn_3, btn_4, btn_5, btn_6, btn_7, btn_8]
with gr.Row():
reset_btn = gr.Button("🔄 New Game", elem_classes="control_button")
menu_btn = gr.Button("🏠 Main Menu", elem_classes="control_button")
def show_mode_select():
return {
main_menu: gr.Column(visible=False),
mode_select: gr.Column(visible=True),
current_view: "mode_select"
}
def show_main_menu():
game.reset_game()
return {
main_menu: gr.Column(visible=True),
mode_select: gr.Column(visible=False),
name_input: gr.Column(visible=False),
game_ui: gr.Column(visible=False),
current_view: "main_menu"
}
def show_name_input(mode):
game.reset_scores_for_mode(mode)
game.game_mode = mode
return {
mode_select: gr.Column(visible=False),
name_input: gr.Column(visible=True),
p2_name: gr.Textbox(visible=(mode == "pvp")),
current_view: "name_input",
game_mode: mode
}
def start_game(p1, p2, mode):
try:
game.reset_game()
game.game_mode = mode
game.player_names["X"] = p1 or "Player X"
game.player_names["O"] = p2 if mode == "pvp" else "Computer"
initial_state = game.get_game_state()
updates = {
name_input: gr.Column(visible=False),
game_ui: gr.Column(visible=True),
status_display: f'<div id="status_display">{initial_state["status"]}</div>',
x_score: f'{game.player_names["X"]}: {initial_state["scores"]["X"]}',
o_score: f'{game.player_names["O"]}: {initial_state["scores"]["O"]}',
draws_score: f'Draws: {initial_state["scores"]["Draws"]}',
current_view: "game_ui"
}
for i in range(9):
updates[buttons[i]] = gr.Button(" ", elem_classes="cell_button")
return updates
except Exception as e:
print(f"Error starting game: {e}")
return {}
def handle_click(index):
try:
if game.game_over:
return {status_display: f'<div id="status_display">Game Over! Click New Game to restart.</div>'}
new_state = game.make_move(index)
updates = {
status_display: f'<div id="status_display">{new_state["status"]}</div>',
x_score: f'{game.player_names["X"]}: {new_state["scores"]["X"]}',
o_score: f'{game.player_names["O"]}: {new_state["scores"]["O"]}',
draws_score: f'Draws: {new_state["scores"]["Draws"]}'
}
for i in range(9):
cell_value = new_state['board'][i]
if cell_value == "X":
updates[buttons[i]] = gr.Button("X", elem_classes="cell_button cell_x")
elif cell_value == "O":
updates[buttons[i]] = gr.Button("O", elem_classes="cell_button cell_o")
else:
updates[buttons[i]] = gr.Button(" ", elem_classes="cell_button")
if new_state["game_over"]:
def auto_restart():
time.sleep(3)
pass
threading.Thread(target=auto_restart, daemon=True).start()
return updates
except Exception as e:
print(f"Error handling click: {e}")
return {status_display: f'<div id="status_display">Error occurred. Please start a new game.</div>'}
def reset_game_manual():
try:
game.reset_game()
initial_state = game.get_game_state()
updates = {
status_display: f'<div id="status_display">{initial_state["status"]}</div>'
}
for i in range(9):
updates[buttons[i]] = gr.Button(" ", elem_classes="cell_button")
return updates
except Exception as e:
print(f"Error resetting game: {e}")
return {status_display: f'<div id="status_display">Error resetting game. Please try again.</div>'}
play_btn.click(
show_mode_select,
outputs=[main_menu, mode_select, current_view]
)
back_to_menu_btn.click(
show_main_menu,
outputs=[main_menu, mode_select, name_input, game_ui, current_view]
)
pvp_btn.click(
lambda: show_name_input("pvp"),
outputs=[mode_select, name_input, p2_name, current_view, game_mode]
)
pvc_btn.click(
lambda: show_name_input("pvc"),
outputs=[mode_select, name_input, p2_name, current_view, game_mode]
)
back_to_mode_btn.click(
lambda: show_mode_select(),
outputs=[mode_select, name_input, current_view]
)
start_game_btn.click(
lambda p1, p2, mode: start_game(p1, p2, mode),
inputs=[p1_name, p2_name, game_mode],
outputs=[name_input, game_ui, status_display, x_score, o_score, draws_score] + buttons + [current_view]
)
for i, btn in enumerate(buttons):
btn.click(
lambda idx=i: handle_click(idx),
outputs=[status_display, x_score, o_score, draws_score] + buttons
)
reset_btn.click(
reset_game_manual,
outputs=[status_display] + buttons
)
menu_btn.click(
show_main_menu,
outputs=[main_menu, mode_select, name_input, game_ui, current_view]
)
return app
if __name__ == "__main__":
app = create_ui()
app.launch()