251 lines
8.2 KiB
Python
251 lines
8.2 KiB
Python
import asyncio
|
|
import logging
|
|
import random
|
|
import socket
|
|
|
|
from enum import Enum
|
|
from typing import List, Dict, Tuple, Optional
|
|
|
|
from .message import parse_message, BiCommand
|
|
|
|
|
|
class Color(Enum):
|
|
TREFLE = "trefle"
|
|
CARRAUX = "carraux"
|
|
COEUR = "cœurs"
|
|
PIQUES = "piques"
|
|
|
|
|
|
class Carte:
|
|
color: Color
|
|
value: int
|
|
|
|
def __init__(self, color: Color, value: int):
|
|
self.color = color
|
|
self.value = value
|
|
|
|
def __str__(self):
|
|
return str(self.color.value) + " " + str(self.value)
|
|
|
|
|
|
def create_deck() -> List[Carte]:
|
|
cartes = []
|
|
for color in [Color.TREFLE, Color.CARRAUX, Color.COEUR, Color.PIQUES]:
|
|
for num in range(1, 14):
|
|
cartes.append(Carte(color, num))
|
|
|
|
random.shuffle(cartes)
|
|
|
|
return cartes
|
|
|
|
|
|
class Table:
|
|
__name: str
|
|
__delay: int
|
|
|
|
def __init__(self, name: str, delay: int) -> None:
|
|
self.__name = name
|
|
self.__delay = delay
|
|
|
|
@property
|
|
def delay(self) -> int:
|
|
return self.__delay
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return self.__name
|
|
|
|
|
|
class Joueur:
|
|
__socket: socket.socket
|
|
cartes: List[Carte]
|
|
|
|
def __init__(self, client_socket: socket.socket):
|
|
self.__socket = client_socket
|
|
self.cartes = list()
|
|
|
|
@property
|
|
def socket(self) -> socket.socket:
|
|
return self.__socket
|
|
|
|
@property
|
|
def points(self) -> int:
|
|
return points_cartes(self.cartes)
|
|
|
|
|
|
def points_cartes(cartes: List[Carte]) -> int:
|
|
value = 0
|
|
as_count = 0
|
|
|
|
for carte in cartes:
|
|
if carte.value == 1:
|
|
as_count += 1
|
|
else:
|
|
value += carte.value
|
|
|
|
if as_count > 0:
|
|
if value + 11 + as_count - 1 < 21:
|
|
value += 11 + as_count - 1
|
|
else:
|
|
value += as_count
|
|
|
|
return value
|
|
|
|
|
|
class Game:
|
|
__players: List[Joueur]
|
|
__table: 'Table'
|
|
__started: bool
|
|
__accepting: bool
|
|
__cards: List[Carte]
|
|
__dealer_cards: List[Carte]
|
|
__logger: logging.Logger
|
|
|
|
def __init__(self, table: 'Table') -> None:
|
|
self.__table = table
|
|
self.__started = False
|
|
self.__players = []
|
|
self.__accepting = True
|
|
self.__cards = create_deck()
|
|
self.__dealer_cards = list()
|
|
self.__logger = logging.getLogger(f"Game-{table.name}")
|
|
|
|
async def add_client(self, client: socket.socket, data: 'ConcurrentData') -> bool:
|
|
if self.__accepting:
|
|
loop = asyncio.get_event_loop()
|
|
joueur = Joueur(client)
|
|
self.__players.append(joueur)
|
|
if not self.__started:
|
|
loop.create_task(self.run_game(data))
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
async def run_game(self, data: 'ConcurrentData'):
|
|
self.__logger.error(f"Waiting {self.__table.delay}s")
|
|
self.__started = True
|
|
await asyncio.sleep(self.__table.delay)
|
|
self.__accepting = False
|
|
for _ in range(0, 2):
|
|
self.__dealer_cards.append(self.__cards.pop(0))
|
|
for joueur in self.__players:
|
|
joueur.cartes.append(self.__cards.pop(0))
|
|
|
|
loop = asyncio.get_event_loop()
|
|
for c in self.__players:
|
|
await loop.sock_sendall(c.socket, (
|
|
"Vos cartes sont : " + ", ".join(str(carte) for carte in c.cartes) + "\r\n").encode("utf8"))
|
|
await loop.sock_sendall(c.socket, (
|
|
"La premiere carte du donneur est : " + str(self.__dealer_cards[0]) + "\r\n").encode('utf8'))
|
|
|
|
await self.run_game_loop(loop)
|
|
|
|
for c in self.__players:
|
|
await loop.sock_sendall(c.socket, b"Service termine\r\n")
|
|
await loop.sock_sendall(c.socket,
|
|
f"L'autre carte du donneur est : {self.__dealer_cards[1]}\r\n".encode(
|
|
'utf8'))
|
|
await loop.sock_sendall(c.socket,
|
|
f"Vous avez {c.points} points\r\n".encode('utf8'))
|
|
|
|
max_player, max_player_index = self.max_player()
|
|
self.__logger.debug(f"Max player have {max_player} points")
|
|
|
|
while max_player is not None and len(self.__cards) > 0 and points_cartes(self.__dealer_cards) < max_player:
|
|
carte = self.__cards.pop(0)
|
|
for c in self.__players:
|
|
await loop.sock_sendall(c.socket, f"Le donneur a pioché : {carte}\r\n".encode('utf8'))
|
|
self.__dealer_cards.append(carte)
|
|
|
|
await self.send_victory_status(loop, max_player, max_player_index)
|
|
await data.remove_table(self.__table.name)
|
|
|
|
self.__logger.debug(f"Removed table \"{self.__table.name}\"")
|
|
|
|
async def run_game_loop(self, loop):
|
|
for i, joueur in enumerate(self.__players):
|
|
for j in self.__players:
|
|
await loop.sock_sendall(j.socket, f"C'est au tour de joueur {i + 1}\r\n".encode('utf8'))
|
|
|
|
playing = True
|
|
while playing:
|
|
if len(self.__cards) > 0 and joueur.points < 21:
|
|
await loop.sock_sendall(joueur.socket, b".\r\n")
|
|
request = parse_message((await loop.sock_recv(joueur.socket, 4096)).decode('utf8'))
|
|
if len(request) == 2 and request[0] == BiCommand.MORE:
|
|
if request[1] == "0":
|
|
playing = False
|
|
elif request[1] == "1":
|
|
carte = self.__cards.pop(0)
|
|
joueur.cartes.append(carte)
|
|
await loop.sock_sendall(joueur.socket,
|
|
f"Votre carte est : {carte}, il reste {len(self.__cards)} cartes\r\n".encode(
|
|
'utf8'))
|
|
else:
|
|
await loop.sock_sendall(joueur.socket, b"ERROR : INVALID COMMAND\r\n")
|
|
else:
|
|
await loop.sock_sendall(joueur.socket, b"ERROR : INVALID COMMAND\r\n")
|
|
else:
|
|
playing = False
|
|
|
|
async def send_victory_status(self, loop, max_player, max_player_index: List[int]):
|
|
points_donneur = points_cartes(self.__dealer_cards)
|
|
self.__logger.debug(f"Dealer have {points_donneur} points")
|
|
if (-1 if max_player is None else max_player) < points_donneur < 21:
|
|
for c in self.__players:
|
|
await loop.sock_sendall(c.socket, b"Vous avez perdu !\r\nEND\r\n")
|
|
c.socket.close()
|
|
else:
|
|
for i, c in enumerate(self.__players):
|
|
if i in max_player_index:
|
|
if max_player == points_donneur or len(max_player_index) > 1:
|
|
await loop.sock_sendall(c.socket, b"Egalite !\r\nEND\r\n")
|
|
else:
|
|
await loop.sock_sendall(c.socket, b"Vous avez gagne !\r\nEND\r\n")
|
|
else:
|
|
await loop.sock_sendall(c.socket, b"Vous avez perdu !\r\nEND\r\n")
|
|
c.socket.close()
|
|
|
|
def max_player(self) -> Tuple[Optional[int], Optional[List[int]]]:
|
|
m = None
|
|
mp = []
|
|
for i, j in enumerate(self.__players):
|
|
p = j.points
|
|
if p < 21:
|
|
if m is None or m < p:
|
|
m = p
|
|
mp = [i]
|
|
elif m == p:
|
|
mp.append(i)
|
|
return m, mp
|
|
|
|
|
|
class TableAlreadyExistException(Exception):
|
|
pass
|
|
|
|
|
|
class ConcurrentData:
|
|
def __init__(self) -> None:
|
|
self.__lock = asyncio.Lock()
|
|
self.__data: Dict[str, Game] = dict()
|
|
|
|
async def add_table(self, table_name: str, delay: int):
|
|
async with self.__lock:
|
|
if table_name in self.__data.keys():
|
|
raise TableAlreadyExistException()
|
|
else:
|
|
self.__data[table_name] = Game(Table(table_name, delay))
|
|
logging.debug("Created a new table called %s" % table_name)
|
|
|
|
async def has_table(self, table_name: str) -> bool:
|
|
async with self.__lock:
|
|
return table_name in self.__data.keys()
|
|
|
|
async def get_table(self, table_name: str) -> Game:
|
|
async with self.__lock:
|
|
return self.__data[table_name]
|
|
|
|
async def remove_table(self, table_name: str):
|
|
async with self.__lock:
|
|
self.__data.pop(table_name)
|