ZoyaRabail's picture
Update app.py
35f36b2 verified
import streamlit as st
import numpy as np
import random
import time
# JavaScript confetti function
def show_confetti():
st.components.v1.html("""
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.5.1/dist/confetti.browser.min.js"></script>
<script>
confetti({
particleCount: 150,
spread: 70,
origin: { y: 0.6 },
colors: ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff']
});
setTimeout(() => {
confetti({
particleCount: 100,
angle: 60,
spread: 55,
origin: { x: 0 }
});
confetti({
particleCount: 100,
angle: 120,
spread: 55,
origin: { x: 1 }
});
}, 250);
</script>
""", height=0)
# Custom CSS for premium UI
st.markdown("""
<style>
/* Main container */
.main {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
/* Title styling */
.title {
background: linear-gradient(to right, #3a7bd5, #00d2ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 3rem;
text-align: center;
margin-bottom: 0.5rem;
font-weight: 800;
text-shadow: 1px 1px 3px rgba(0,0,0,0.1);
}
/* Status message */
.status {
text-align: center;
font-size: 1.8rem;
margin: 1rem 0;
padding: 0.8rem;
border-radius: 15px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
/* Player X (human) */
.player-x {
background: linear-gradient(to right, #ff416c, #ff4b2b);
color: white;
}
/* Player O (AI) */
.player-o {
background: linear-gradient(to right, #4776E6, #8E54E9);
color: white;
}
/* Win message */
.win-message {
background: linear-gradient(to right, #4CAF50, #8BC34A);
color: white;
animation: pulse 1.5s infinite;
}
/* Tie message */
.tie-message {
background: linear-gradient(to right, #9E9E9E, #607D8B);
color: white;
}
/* Game board */
.board-container {
background: white;
border-radius: 20px;
padding: 20px;
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
margin: 0 auto;
max-width: 500px;
}
.board-button {
font-size: 4rem !important;
height: 120px !important;
margin: 0.3rem !important;
border-radius: 15px !important;
transition: all 0.3s ease !important;
border: none !important;
box-shadow: 0 4px 8px rgba(0,0,0,0.1) !important;
}
.board-button:hover {
transform: translateY(-5px) !important;
box-shadow: 0 8px 15px rgba(0,0,0,0.15) !important;
}
/* X button */
.x-button {
color: #FF5252 !important;
background: linear-gradient(135deg, #FFF6F6 0%, #FFEBEE 100%) !important;
}
/* O button */
.o-button {
color: #536DFE !important;
background: linear-gradient(135deg, #F5F7FF 0%, #E8EAF6 100%) !important;
}
/* Highlight last move */
.last-move {
box-shadow: 0 0 0 3px #FFD700 !important;
transform: scale(1.05) !important;
}
/* Scoreboard */
.scoreboard {
display: flex;
justify-content: space-around;
margin: 1rem 0;
background: white;
border-radius: 15px;
padding: 1rem;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.score-item {
text-align: center;
padding: 0.5rem;
border-radius: 10px;
flex: 1;
margin: 0 0.5rem;
}
.score-x {
background: #FFEBEE;
color: #FF5252;
}
.score-o {
background: #E8EAF6;
color: #536DFE;
}
.score-tie {
background: #EEEEEE;
color: #616161;
}
/* Difficulty selector */
.difficulty-selector {
background: white;
border-radius: 15px;
padding: 1rem;
margin: 1rem auto;
max-width: 500px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
/* Reset button */
.reset-button {
background: linear-gradient(to right, #3a7bd5, #00d2ff) !important;
color: white !important;
font-weight: bold !important;
margin-top: 1rem !important;
border: none !important;
border-radius: 10px !important;
padding: 0.8rem !important;
font-size: 1.1rem !important;
box-shadow: 0 4px 8px rgba(0,0,0,0.1) !important;
transition: all 0.3s ease !important;
}
.reset-button:hover {
transform: translateY(-2px) !important;
box-shadow: 0 6px 12px rgba(0,0,0,0.15) !important;
}
/* Animations */
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.fade-in {
animation: fadeIn 0.5s ease-in;
}
</style>
""", unsafe_allow_html=True)
# Sound effects (using HTML audio)
sound_effects = """
<audio id="clickSound" src="https://www.soundjay.com/buttons/sounds/button-09.mp3" preload="auto"></audio>
<audio id="winSound" src="https://www.soundjay.com/human/sounds/applause-8.mp3" preload="auto"></audio>
<script>
function playSound(soundId) {
document.getElementById(soundId).play();
}
</script>
"""
st.components.v1.html(sound_effects, height=0)
def initialize_game():
"""Initialize the game board."""
st.session_state.game['board'] = [' ' for _ in range(9)]
st.session_state.game['current_player'] = 'X'
st.session_state.game['winner'] = None
st.session_state.game['game_over'] = False
st.session_state.game['last_move'] = None
def check_winner(board):
"""Check if there's a winner or if the game is a tie."""
# Check rows
for i in range(0, 9, 3):
if board[i] == board[i+1] == board[i+2] != ' ':
return board[i]
# Check columns
for i in range(3):
if board[i] == board[i+3] == board[i+6] != ' ':
return board[i]
# Check diagonals
if board[0] == board[4] == board[8] != ' ':
return board[0]
if board[2] == board[4] == board[6] != ' ':
return board[2]
# Check for tie
if ' ' not in board:
return 'Tie'
return None
def make_move(board, position, player):
"""Make a move on the board."""
if board[position] == ' ':
board[position] = player
st.session_state.game['last_move'] = position
st.components.v1.html("<script>playSound('clickSound')</script>", height=0)
return True
return False
def minimax(board, depth, is_maximizing):
"""Minimax algorithm for unbeatable AI."""
winner = check_winner(board)
if winner == 'O':
return 10 - depth
elif winner == 'X':
return depth - 10
elif winner == 'Tie':
return 0
if is_maximizing:
best_score = -float('inf')
for i in range(9):
if board[i] == ' ':
board[i] = 'O'
score = minimax(board, depth + 1, False)
board[i] = ' '
best_score = max(score, best_score)
return best_score
else:
best_score = float('inf')
for i in range(9):
if board[i] == ' ':
board[i] = 'X'
score = minimax(board, depth + 1, True)
board[i] = ' '
best_score = min(score, best_score)
return best_score
def ai_move(board, difficulty):
"""AI move based on selected difficulty."""
if difficulty == 'Easy':
# Random moves
available = [i for i, spot in enumerate(board) if spot == ' ']
return random.choice(available) if available else -1
elif difficulty == 'Medium':
# Mix of random and smart moves
if random.random() < 0.5:
return ai_move(board, 'Easy')
else:
return ai_move(board, 'Hard')
elif difficulty == 'Hard':
# Unbeatable minimax AI
best_score = -float('inf')
best_move = -1
for i in range(9):
if board[i] == ' ':
board[i] = 'O'
score = minimax(board, 0, False)
board[i] = ' '
if score > best_score:
best_score = score
best_move = i
return best_move
def display_board(board):
"""Display the Tic Tac Toe board with premium UI."""
st.markdown('<div class="board-container fade-in">', unsafe_allow_html=True)
cols = st.columns(3)
for i in range(9):
with cols[i % 3]:
button_label = board[i] if board[i] != ' ' else ' '
button_class = ""
if board[i] == 'X':
button_class = "x-button"
elif board[i] == 'O':
button_class = "o-button"
# Highlight last move
last_move_class = "last-move" if i == st.session_state.game['last_move'] else ""
if st.button(
button_label,
key=f"btn_{i}",
disabled=(board[i] != ' ' or
st.session_state.game['game_over'] or
st.session_state.game['current_player'] == 'O'),
use_container_width=True,
kwargs={"class": f"board-button {button_class} {last_move_class}"}
):
if make_move(board, i, 'X'):
st.session_state.game['winner'] = check_winner(board)
if st.session_state.game['winner']:
st.session_state.game['game_over'] = True
if st.session_state.game['winner'] == 'X':
st.session_state.game['scores']['X'] += 1
show_confetti()
st.components.v1.html("<script>playSound('winSound')</script>", height=0)
elif st.session_state.game['winner'] == 'O':
st.session_state.game['scores']['O'] += 1
else:
st.session_state.game['scores']['Ties'] += 1
else:
st.session_state.game['current_player'] = 'O'
st.rerun()
st.markdown('</div>', unsafe_allow_html=True)
def ai_turn():
"""Handle the AI's turn with visual feedback."""
if st.session_state.game['current_player'] == 'O' and not st.session_state.game['game_over']:
with st.spinner("πŸ€– AI is thinking..."):
time.sleep(0.8) # Simulate thinking time
move = ai_move(st.session_state.game['board'], st.session_state.game['difficulty'])
if move != -1 and make_move(st.session_state.game['board'], move, 'O'):
st.session_state.game['winner'] = check_winner(st.session_state.game['board'])
if st.session_state.game['winner']:
st.session_state.game['game_over'] = True
if st.session_state.game['winner'] == 'X':
st.session_state.game['scores']['X'] += 1
show_confetti()
st.components.v1.html("<script>playSound('winSound')</script>", height=0)
elif st.session_state.game['winner'] == 'O':
st.session_state.game['scores']['O'] += 1
else:
st.session_state.game['scores']['Ties'] += 1
else:
st.session_state.game['current_player'] = 'X'
st.rerun()
def display_status():
"""Display the current game status with premium UI."""
if st.session_state.game['winner']:
if st.session_state.game['winner'] == 'Tie':
st.markdown('<div class="status tie-message">πŸ† It\'s a Tie! πŸ†</div>', unsafe_allow_html=True)
elif st.session_state.game['winner'] == 'X':
st.markdown('<div class="status win-message">πŸŽ‰ Congratulations! You Won! πŸŽ‰</div>', unsafe_allow_html=True)
else:
st.markdown('<div class="status win-message">πŸ€– AI Defeated You!</div>', unsafe_allow_html=True)
else:
if st.session_state.game['current_player'] == 'X':
st.markdown('<div class="status player-x">✨ Your Turn (X) ✨</div>', unsafe_allow_html=True)
else:
st.markdown('<div class="status player-o">πŸ€– AI\'s Turn (O)</div>', unsafe_allow_html=True)
def display_scoreboard():
"""Display the scoreboard with premium UI."""
st.markdown("""
<div class="scoreboard fade-in">
<div class="score-item score-x">
<div style="font-size: 1.2rem; font-weight: bold;">You (X)</div>
<div style="font-size: 1.8rem;">{x_score}</div>
</div>
<div class="score-item score-tie">
<div style="font-size: 1.2rem; font-weight: bold;">Ties</div>
<div style="font-size: 1.8rem;">{tie_score}</div>
</div>
<div class="score-item score-o">
<div style="font-size: 1.2rem; font-weight: bold;">AI (O)</div>
<div style="font-size: 1.8rem;">{o_score}</div>
</div>
</div>
""".format(
x_score=st.session_state.game['scores']['X'],
o_score=st.session_state.game['scores']['O'],
tie_score=st.session_state.game['scores']['Ties']
), unsafe_allow_html=True)
def difficulty_selector():
"""Display difficulty selector."""
st.markdown('<div class="difficulty-selector">', unsafe_allow_html=True)
st.write("### Difficulty Level")
difficulty = st.radio(
"Choose AI difficulty:",
("Easy", "Medium", "Hard"),
index=["Easy", "Medium", "Hard"].index(st.session_state.game['difficulty']),
horizontal=True,
label_visibility="collapsed"
)
st.session_state.game['difficulty'] = difficulty
st.markdown('</div>', unsafe_allow_html=True)
def main():
"""Main function to run the Tic Tac Toe game."""
st.markdown('<h1 class="title">Ultimate Tic Tac Toe</h1>', unsafe_allow_html=True)
st.markdown('<h4 style="text-align: center; color: #555;">You (X) vs AI (O)</h4>', unsafe_allow_html=True)
# Display difficulty selector
difficulty_selector()
# Display scoreboard
display_scoreboard()
# Display game status
display_status()
# Display the board
display_board(st.session_state.game['board'])
# Handle AI turn
ai_turn()
# Reset button
if st.button("πŸ”„ New Game", key="reset", use_container_width=True,
kwargs={"class": "reset-button"}):
initialize_game()
st.rerun()
if __name__ == "__main__":
main()