create html game online wiht python server and points system and name system have 8 colums and 6 row each box have the game have play 2 players blue and red make it draw circle player by player the player who make 4 circles in horzintal win a point and reset the game,  the player can put circle if there is ciracle under it, modern style, client static html , server in python, no flask, server like minecraft spreated


index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Connect Four Online</title>
    <style>
        /* Modernized and responsive styling */
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: #1e272e;
            color: #d2dae2;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
            min-height: 100vh;
            box-sizing: border-box;
        }

        h1 {
            color: #00a8ff;
            margin-bottom: 20px;
        }

        #room-selection, #join-game {
            background-color: #485460;
            padding: 20px;
            border-radius: 10px;
            width: 100%;
            max-width: 500px;
            margin-bottom: 20px;
        }

        input[type="text"], input[type="password"] {
            width: calc(100% - 22px);
            padding: 10px;
            margin: 10px 0;
            border: none;
            border-radius: 5px;
        }

        button {
            padding: 10px 20px;
            margin: 10px 5px 0 0;
            border: none;
            border-radius: 5px;
            background-color: #00a8ff;
            color: white;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        button:hover {
            background-color: #0097e6;
        }

        #game {
            display: grid;
            grid-template-columns: repeat(8, 60px);
            grid-template-rows: repeat(6, 60px);
            gap: 5px;
            background-color: #2f3640;
            padding: 10px;
            border-radius: 10px;
        }

        .cell {
            width: 60px;
            height: 60px;
            background-color: #dcdde1;
            border-radius: 50%;
            position: relative;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        .cell:hover {
            background-color: #a4b0be;
        }

        .red {
            background-color: #e74c3c !important;
        }

        .blue {
            background-color: #3498db !important;
        }

        #status, #points {
            margin-top: 20px;
            text-align: center;
        }

        #rooms-list {
            max-height: 200px;
            overflow-y: auto;
            margin-top: 10px;
        }

        .room-item {
            background-color: #353b48;
            padding: 10px;
            border-radius: 5px;
            margin-bottom: 10px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        .room-item:hover {
            background-color: #40739e;
        }

        .hidden {
            display: none;
        }

        @media (max-width: 600px) {
            #game {
                grid-template-columns: repeat(8, 40px);
                grid-template-rows: repeat(6, 40px);
            }

            .cell {
                width: 40px;
                height: 40px;
            }
        }
    </style>
</head>
<body>
    <h1>Connect Four Online</h1>

    <div id="room-selection">
        <h2>Join or Create a Room</h2>
        <input type="text" id="room-name" placeholder="Room Name">
        <input type="password" id="room-password" placeholder="Room Password (Optional)">
        <button onclick="createRoom()">Create Room</button>
        <button onclick="searchRooms()">Search Rooms</button>

        <div id="rooms-list" class="hidden"></div>
    </div>

    <div id="join-game" class="hidden">
        <input type="text" id="player-name" placeholder="Enter your name">
        <button onclick="joinGame()">Join Game</button>
    </div>

    <div id="game" class="hidden"></div>
    <div id="status" class="hidden"></div>
    <div id="points" class="hidden"></div>

    <script>
        let socket;
        let playerColor = null;
        let currentRoom = null;

        const roomSelectionDiv = document.getElementById('room-selection');
        const joinGameDiv = document.getElementById('join-game');
        const roomsListDiv = document.getElementById('rooms-list');
        const gameDiv = document.getElementById('game');
        const statusDiv = document.getElementById('status');
        const pointsDiv = document.getElementById('points');

        function createRoom() {
            const roomName = document.getElementById('room-name').value.trim();
            const roomPassword = document.getElementById('room-password').value;

            if (!roomName) {
                alert("Please enter a room name.");
                return;
            }

            socket = new WebSocket('ws://localhost:6789');

            socket.onopen = () => {
                socket.send(JSON.stringify({ type: 'create_room', name: roomName, password: roomPassword }));
            };

            socket.onmessage = (event) => {
                const data = JSON.parse(event.data);
                if (data.type === 'error') {
                    alert(data.message);
                } else if (data.type === 'room_created') {
                    currentRoom = data.room;
                    roomSelectionDiv.classList.add('hidden');
                    joinGameDiv.classList.remove('hidden');
                } else if (data.type === 'update') {
                    updateGame(data);
                }
            };

            socket.onclose = () => {
                statusDiv.innerText = "Connection closed.";
            };
        }

        function searchRooms() {
            socket = new WebSocket('ws://localhost:6789');

            socket.onopen = () => {
                socket.send(JSON.stringify({ type: 'list_rooms' }));
            };

            socket.onmessage = (event) => {
                const data = JSON.parse(event.data);
                if (data.type === 'room_list') {
                    displayRooms(data.rooms);
                } else if (data.type === 'error') {
                    alert(data.message);
                }
            };
        }

        function displayRooms(rooms) {
            roomsListDiv.innerHTML = '';
            if (rooms.length === 0) {
                roomsListDiv.innerHTML = '<p>No available rooms.</p>';
            } else {
                rooms.forEach(room => {
                    const roomItem = document.createElement('div');
                    roomItem.classList.add('room-item');
                    roomItem.innerText = `${room.name} (${room.players}/2)`;
                    roomItem.onclick = () => joinSelectedRoom(room.name);
                    roomsListDiv.appendChild(roomItem);
                });
            }
            roomsListDiv.classList.remove('hidden');
        }

        function joinSelectedRoom(roomName) {
            const roomPassword = prompt("Enter room password (if any):") || '';
            const playerName = prompt("Enter your name:") || 'Player';

            if (!playerName) {
                alert("Name is required.");
                return;
            }

            // Close previous socket if any
            if (socket) socket.close();

            socket = new WebSocket('ws://localhost:6789');

            socket.onopen = () => {
                socket.send(JSON.stringify({ type: 'join_room', room: roomName, password: roomPassword, name: playerName }));
            };

            socket.onmessage = (event) => {
                const data = JSON.parse(event.data);
                if (data.type === 'error') {
                    alert(data.message);
                } else if (data.type === 'joined_room') {
                    currentRoom = data.room;
                    roomSelectionDiv.classList.add('hidden');
                    joinGameDiv.classList.add('hidden');
                    gameDiv.classList.remove('hidden');
                    statusDiv.classList.remove('hidden');
                    pointsDiv.classList.remove('hidden');
                } else if (data.type === 'update') {
                    updateGame(data);
                }
            };

            socket.onclose = () => {
                statusDiv.innerText = "Connection closed.";
            };
        }

        function joinGame() {
            const playerName = document.getElementById('player-name').value.trim();
            if (!playerName) {
                alert("Please enter your name.");
                return;
            }
            const roomPassword = document.getElementById('room-password').value;

            socket.send(JSON.stringify({ type: 'join', name: playerName }));
            joinGameDiv.classList.add('hidden');
            gameDiv.classList.remove('hidden');
            statusDiv.classList.remove('hidden');
            pointsDiv.classList.remove('hidden');
        }

        function updateGame(data) {
            // Update board
            gameDiv.innerHTML = '';
            for (let row = 0; row < 6; row++) {
                for (let col = 0; col < 8; col++) {
                    const cell = document.createElement('div');
                    cell.classList.add('cell');
                    if (data.board[row][col]) {
                        cell.classList.add(data.board[row][col]);
                    }
                    cell.dataset.column = col;
                    cell.onclick = () => makeMove(col);
                    gameDiv.appendChild(cell);
                }
            }

            // Update current turn
            statusDiv.innerText = `Current Turn: ${capitalize(data.current_turn)}`;

            // Update player info
            pointsDiv.innerHTML = '';
            for (const [color, info] of Object.entries(data.players)) {
                if (playerColor === null && socket.readyState === WebSocket.OPEN) {
                    // Assign player color
                    playerColor = color;
                }
                pointsDiv.innerHTML += `<p>${capitalize(color)} Player: ${info.name} | Points: ${info.points}</p>`;
            }
        }

        function makeMove(column) {
            if (!socket || socket.readyState !== WebSocket.OPEN) return;
            socket.send(JSON.stringify({ type: 'move', column }));
        }

        function capitalize(str) {
            return str.charAt(0).toUpperCase() + str.slice(1);
        }
    </script>
</body>
</html>


server.py

import asyncio
import websockets
import json
from collections import defaultdict

# Game constants
GAME_ROWS = 6
GAME_COLS = 8

class Game:
    def __init__(self):
        self.reset_game()
        self.players = {}  # websocket: { 'name': str, 'color': 'red'/'blue', 'points': int }

    def reset_game(self):
        self.board = [[None for _ in range(GAME_COLS)] for _ in range(GAME_ROWS)]
        self.current_turn = 'red'  # red starts
        self.game_over = False

    def add_player(self, websocket, name):
        if len(self.players) >= 2:
            return False  # Game full
        color = 'red' if not any(p['color'] == 'red' for p in self.players.values()) else 'blue'
        self.players[websocket] = {'name': name, 'color': color, 'points': 0}
        return True

    def remove_player(self, websocket):
        if websocket in self.players:
            del self.players[websocket]

    def make_move(self, column, color):
        if self.game_over or color != self.current_turn:
            return False, "Not your turn or game over."

        # Place the piece in the column
        for row in reversed(range(GAME_ROWS)):
            if self.board[row][column] is None:
                self.board[row][column] = color
                if self.check_winner(row, column, color):
                    self.game_over = True
                    return True, "win"
                self.current_turn = 'blue' if self.current_turn == 'red' else 'red'
                return True, "success"
        return False, "Column full."

    def check_winner(self, row, col, color):
        # Check horizontal
        count = 0
        for c in range(GAME_COLS):
            if self.board[row][c] == color:
                count += 1
                if count == 4:
                    return True
            else:
                count = 0
        # You can add vertical and diagonal checks similarly
        return False

    def to_json(self):
        return json.dumps({
            'type': 'update',
            'board': self.board,
            'current_turn': self.current_turn,
            'players': {p['color']: {'name': p['name'], 'points': p['points']} for p in self.players.values()}
        })

class Room:
    def __init__(self, name, password=''):
        self.name = name
        self.password = password
        self.game = Game()
        self.players = {}  # websocket: player_info

    def add_player(self, websocket, name, password):
        if self.password and self.password != password:
            return False, "Incorrect password."
        if len(self.players) >= 2:
            return False, "Room is full."
        color = 'red' if not any(p['color'] == 'red' for p in self.players.values()) else 'blue'
        self.players[websocket] = {'name': name, 'color': color, 'points': 0}
        self.game.players = self.players
        return True, color

    def remove_player(self, websocket):
        if websocket in self.players:
            del self.players[websocket]
            self.game.remove_player(websocket)

    def to_json(self):
        return json.dumps({
            'type': 'update',
            'board': self.game.board,
            'current_turn': self.game.current_turn,
            'players': {p['color']: {'name': p['name'], 'points': p['points']} for p in self.players.values()}
        })

rooms = defaultdict(lambda: None)  # room_name: Room

async def handler(websocket, path):
    current_room = None

    try:
        async for message in websocket:
            data = json.loads(message)

            if data['type'] == 'create_room':
                room_name = data['name']
                room_password = data.get('password', '')
                if room_name in rooms and rooms[room_name]:
                    await websocket.send(json.dumps({'type': 'error', 'message': 'Room already exists.'}))
                else:
                    room = Room(room_name, room_password)
                    rooms[room_name] = room
                    success = room.add_player(websocket, 'Host', room_password)
                    if success:
                        current_room = room
                        await websocket.send(json.dumps({'type': 'room_created', 'room': room_name}))
                    else:
                        await websocket.send(json.dumps({'type': 'error', 'message': 'Failed to create room.'}))

            elif data['type'] == 'list_rooms':
                available_rooms = []
                for room in rooms.values():
                    if room and len(room.players) < 2:
                        available_rooms.append({'name': room.name, 'players': len(room.players)})
                await websocket.send(json.dumps({'type': 'room_list', 'rooms': available_rooms}))

            elif data['type'] == 'join_room':
                room_name = data['room']
                room_password = data.get('password', '')
                player_name = data.get('name', 'Player')

                if room_name not in rooms or not rooms[room_name]:
                    await websocket.send(json.dumps({'type': 'error', 'message': 'Room does not exist.'}))
                else:
                    room = rooms[room_name]
                    success, info = room.add_player(websocket, player_name, room_password)
                    if not success:
                        await websocket.send(json.dumps({'type': 'error', 'message': info}))
                    else:
                        current_room = room
                        await websocket.send(json.dumps({'type': 'joined_room', 'room': room_name}))
                        # Notify all players in the room
                        await asyncio.wait([ws.send(room.to_json()) for ws in room.players])

            elif data['type'] == 'join':
                if not current_room:
                    await websocket.send(json.dumps({'type': 'error', 'message': 'No room joined.'}))
                    continue
                player_name = data['name']
                success = current_room.game.add_player(websocket, player_name)
                if not success:
                    await websocket.send(json.dumps({'type': 'error', 'message': 'Room is full.'}))
                    continue
                await websocket.send(current_room.game.to_json())
                await asyncio.wait([ws.send(current_room.game.to_json()) for ws in current_room.players])

            elif data['type'] == 'move':
                if not current_room:
                    await websocket.send(json.dumps({'type': 'error', 'message': 'No room joined.'}))
                    continue
                column = data['column']
                player = current_room.players.get(websocket)
                if not player:
                    await websocket.send(json.dumps({'type': 'error', 'message': 'Player not found in room.'}))
                    continue
                color = player['color']
                success, status = current_room.game.make_move(column, color)
                if not success:
                    await websocket.send(json.dumps({'type': 'error', 'message': status}))
                    continue
                if status == "win":
                    # Update points
                    player['points'] += 1
                    # Notify all players
                    await asyncio.wait([ws.send(current_room.to_json()) for ws in current_room.players])
                    current_room.game.reset_game()
                else:
                    # Notify all players
                    await asyncio.wait([ws.send(current_room.to_json()) for ws in current_room.players])

    except websockets.exceptions.ConnectionClosed:
        pass
    finally:
        if current_room:
            current_room.remove_player(websocket)
            # Notify remaining players
            if current_room.players:
                await asyncio.wait([ws.send(current_room.to_json()) for ws in current_room.players])
            else:
                # Remove the room if empty
                rooms.pop(current_room.name, None)

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

print("Server started on ws://0.0.0.0:6789")
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()


fix game not function problay and fix this error
Traceback (most recent call last):
  File "C:\Users\PC\scoop\apps\python\current\Lib\site-packages\websockets\legacy\server.py", line 236, in handler     
    await self.ws_handler(self)
  File "C:\Users\PC\scoop\apps\python\current\Lib\site-packages\websockets\legacy\server.py", line 1175, in _ws_handler
    return await cast(
           ^^^^^^^^^^^
  File "c:\Users\PC\Documents\cocoOnline\server\server.py", line 156, in handler
    await asyncio.wait([ws.send(current_room.game.to_json()) for ws in current_room.players])
  File "C:\Users\PC\scoop\apps\python\current\Lib\asyncio\tasks.py", line 461, in wait
    raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
TypeError: Passing coroutines is forbidden, use tasks explicitly.
C:\Users\PC\scoop\apps\python\current\Lib\site-packages\websockets\legacy\server.py:-1: RuntimeWarning: coroutine 'WebSocketCommonProtocol.send' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
connection handler failed
Traceback (most recent call last):
  File "C:\Users\PC\scoop\apps\python\current\Lib\site-packages\websockets\legacy\server.py", line 236, in handler
    await self.ws_handler(self)
  File "C:\Users\PC\scoop\apps\python\current\Lib\site-packages\websockets\legacy\server.py", line 1175, in _ws_handler
    return await cast(
           ^^^^^^^^^^^
  File "c:\Users\PC\Documents\cocoOnline\server\server.py", line 156, in handler
    await asyncio.wait([ws.send(current_room.game.to_json()) for ws in current_room.players])
  File "C:\Users\PC\scoop\apps\python\current\Lib\asyncio\tasks.py", line 461, in wait
    raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
TypeError: Passing coroutines is forbidden, use tasks explicitly.
connection handler failed
Traceback (most recent call last):
  File "C:\Users\PC\scoop\apps\python\current\Lib\site-packages\websockets\legacy\server.py", line 236, in handler
    await self.ws_handler(self)
  File "C:\Users\PC\scoop\apps\python\current\Lib\site-packages\websockets\legacy\server.py", line 1175, in _ws_handler
    return await cast(
           ^^^^^^^^^^^
  File "c:\Users\PC\Documents\cocoOnline\server\server.py", line 156, in handler
    await asyncio.wait([ws.send(current_room.game.to_json()) for ws in current_room.players])
  File "C:\Users\PC\scoop\apps\python\current\Lib\asyncio\tasks.py", line 461, in wait
    raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
TypeError: Passing coroutines is forbidden, use tasks explicitly.