server.py
import asyncio
import websockets
import json

PORT = 6789

# Manage connected players
waiting_players = []
active_games = {}

class Game:
    def __init__(self, player1, player2):
        self.players = [player1, player2]
        self.symbols = {player1: 'X', player2: 'O'}
        self.board = [''] * 9
        self.current_turn = player1
        self.active = True

    async def start(self):
        # Notify players of game start and their symbols
        start_msg_p1 = {'type': 'start', 'symbol': self.symbols[self.players[0]]}
        start_msg_p2 = {'type': 'start', 'symbol': self.symbols[self.players[1]]}
        await self.players[0].send(json.dumps(start_msg_p1))
        await self.players[1].send(json.dumps(start_msg_p2))
        await self.notify_turn()

    async def notify_turn(self):
        # Notify whose turn it is
        if not self.active:
            return
        current_symbol = self.symbols[self.current_turn]
        # The client-side code already manages turn based on symbol
        # So no need to send explicit turn notifications
        pass

    def check_winner(self):
        winning_combinations = [
            [0,1,2], [3,4,5], [6,7,8], # rows
            [0,3,6], [1,4,7], [2,5,8], # columns
            [0,4,8], [2,4,6]           # diagonals
        ]
        for combo in winning_combinations:
            a, b, c = combo
            if self.board[a] and self.board[a] == self.board[b] == self.board[c]:
                return self.board[a]
        if all(cell != '' for cell in self.board):
            return 'Draw'
        return None

    async def handle_move(self, player, index):
        if player != self.current_turn or not self.active:
            return
        if self.board[index] != '':
            return
        symbol = self.symbols[player]
        self.board[index] = symbol
        # Broadcast move to both players
        move_msg = {'type': 'move', 'index': index}
        await asyncio.gather(
            self.players[0].send(json.dumps(move_msg)),
            self.players[1].send(json.dumps(move_msg))
        )
        # Check for winner
        result = self.check_winner()
        if result:
            self.active = False
            if result == 'Draw':
                end_msg = {'type': 'end', 'result': 'It\'s a draw!'}
            else:
                end_msg = {'type': 'end', 'result': f'{result} wins!'}
            await asyncio.gather(
                self.players[0].send(json.dumps(end_msg)),
                self.players[1].send(json.dumps(end_msg))
            )
        else:
            # Switch turn
            self.current_turn = self.players[1] if self.current_turn == self.players[0] else self.players[0]

    async def handle_reset(self, player):
        if not self.active:
            # Reset the game
            self.board = [''] * 9
            self.active = True
            await asyncio.gather(
                self.players[0].send(json.dumps({'type': 'reset'})),
                self.players[1].send(json.dumps({'type': 'reset'}))
            )
            # Optionally, decide who starts
            self.current_turn = self.players[0]
            await self.start()
        else:
            # If game is active, do not allow reset
            await player.send(json.dumps({'type': 'error', 'message': 'Cannot reset during an active game.'}))

async def handler(websocket, path):
    print(f"New connection: {websocket.remote_address}")
    waiting_players.append(websocket)
    try:
        if len(waiting_players) >= 2:
            player1 = waiting_players.pop(0)
            player2 = waiting_players.pop(0)
            game = Game(player1, player2)
            active_games[player1] = game
            active_games[player2] = game
            await game.start()

        async for message in websocket:
            data = json.loads(message)
            game = active_games.get(websocket)
            if not game:
                await websocket.send(json.dumps({'type': 'error', 'message': 'You are not in a game yet.'}))
                continue
            if data['type'] == 'move':
                index = data.get('index')
                if index is not None and 0 <= index < 9:
                    await game.handle_move(websocket, index)
            elif data['type'] == 'reset':
                await game.handle_reset(websocket)
    except websockets.exceptions.ConnectionClosed:
        print(f"Connection closed: {websocket.remote_address}")
    finally:
        waiting_players.remove(websocket) if websocket in waiting_players else None
        game = active_games.get(websocket)
        if game and game.active:
            # Notify the opponent that the player has left
            opponent = game.players[1] if game.players[0] == websocket else game.players[0]
            await opponent.send(json.dumps({'type': 'end', 'result': 'Opponent disconnected.'}))
            # Clean up
            active_games.pop(opponent, None)
            active_games.pop(websocket, None)

start_server = websockets.serve(handler, "0.0.0.0", PORT)

print(f"Server started on port {PORT}")
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()


game.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Multiplayer Tic Tac Toe</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin-top: 50px;
        }
        #status {
            margin-bottom: 20px;
            font-size: 1.2em;
        }
        #game {
            display: grid;
            grid-template-columns: repeat(3, 100px);
            grid-gap: 5px;
            justify-content: center;
            margin: 0 auto;
        }
        .cell {
            width: 100px;
            height: 100px;
            background-color: #f2f2f2;
            font-size: 2em;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
        }
        .cell.disabled {
            cursor: not-allowed;
            background-color: #e0e0e0;
        }
        #restart {
            margin-top: 20px;
            padding: 10px 20px;
            font-size: 1em;
        }
    </style>
</head>
<body>

<h1>Multiplayer Tic Tac Toe</h1>
<div id="status">Connecting to server...</div>
<div id="game">
    <!-- 9 cells -->
    <div class="cell" data-index="0"></div>
    <div class="cell" data-index="1"></div>
    <div class="cell" data-index="2"></div>
    <div class="cell" data-index="3"></div>
    <div class="cell" data-index="4"></div>
    <div class="cell" data-index="5"></div>
    <div class="cell" data-index="6"></div>
    <div class="cell" data-index="7"></div>
    <div class="cell" data-index="8"></div>
</div>
<button id="restart" disabled>Restart</button>

<script>
    const statusDiv = document.getElementById('status');
    const cells = document.querySelectorAll('.cell');
    const restartButton = document.getElementById('restart');

    let socket;
    let mySymbol;
    let myTurn = false;
    let gameActive = false;

    function connect() {
        // Replace 'localhost' and port '6789' with your server's address and port if different
        socket = new WebSocket('ws://localhost:6789');

        socket.onopen = () => {
            statusDiv.textContent = 'Connected. Waiting for opponent...';
        };

        socket.onmessage = (event) => {
            const message = JSON.parse(event.data);
            switch(message.type) {
                case 'start':
                    mySymbol = message.symbol;
                    myTurn = message.symbol === 'X';
                    gameActive = true;
                    updateStatus();
                    break;
                case 'move':
                    handleMove(message.index);
                    break;
                case 'end':
                    gameActive = false;
                    statusDiv.textContent = message.result;
                    restartButton.disabled = false;
                    break;
                case 'reset':
                    resetGame();
                    break;
                case 'error':
                    statusDiv.textContent = message.message;
                    break;
                default:
                    console.log('Unknown message type:', message.type);
            }
        };

        socket.onclose = () => {
            statusDiv.textContent = 'Disconnected from server.';
            gameActive = false;
        };

        socket.onerror = (error) => {
            console.error('WebSocket error:', error);
        };
    }

    function updateStatus() {
        if (!gameActive) return;
        if (myTurn) {
            statusDiv.textContent = 'Your turn (' + mySymbol + ')';
        } else {
            statusDiv.textContent = 'Opponent\'s turn';
        }
    }

    function handleMove(index) {
        // Update the cell
        cells[index].textContent = mySymbol === 'X' ? 'O' : 'X';
        cells[index].classList.add('disabled');
        myTurn = !myTurn;
        updateStatus();
    }

    function resetGame() {
        cells.forEach(cell => {
            cell.textContent = '';
            cell.classList.remove('disabled');
        });
        restartButton.disabled = true;
        statusDiv.textContent = 'Waiting for opponent...';
        myTurn = false;
        gameActive = false;
    }

    cells.forEach(cell => {
        cell.addEventListener('click', () => {
            if (!gameActive || !myTurn || cell.textContent !== '') return;
            const index = cell.getAttribute('data-index');
            socket.send(JSON.stringify({ type: 'move', index: parseInt(index) }));
            cell.textContent = mySymbol;
            cell.classList.add('disabled');
            myTurn = false;
            updateStatus();
        });
    });

    restartButton.addEventListener('click', () => {
        socket.send(JSON.stringify({ type: 'reset' }));
        resetGame();
    });

    connect();
</script>

</body>
</html>



fix the game make it work with rooms code system and make the game look modern