peux tu me dire si les fonctions sont bien organisées dans le code et si il y a des problèmes de sécurité ?  // SPDX-License-Identifier: MIT
//PARTIE 1 : 
//Jeu qui consiste à deviné un mot. Il se joue à 2 joueurs. Chacun doit miser la même mise des ethers
pragma solidity 0.8.17;

import "../node_modules/@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "../node_modules/@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

contract Penduel is VRFConsumerBaseV2 {
    VRFCoordinatorV2Interface COORDINATOR;

    uint64 s_subscriptionId;
    address vrfCoordinator = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;
    bytes32 keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;
    uint32 callbackGasLimit = 100000;
    uint16 requestConfirmations = 3; 
    uint32 numWords = 2;
    uint256 public delay = 10; 

    address public s_owner;
    address public player1;
    address public player2;
    uint256 public gameId = 0;
    uint256 public betAmount; 
    uint256 public pot; 
    uint256 public requestId; 
    uint256 public amount; 
    uint256[] private requestIds;
    bool public lettersWinInit;
    string[] public lettersWin;
    string[] public wordGuesses;
    string private filteredWord;

    struct PlayerAccount { 
        uint256 balance;
        bool hasWithdrawn;
    }
    struct Game {
        string filteredWord;
        string letterWin;
        string letterGuess;
        string wordGuess;
        string wordWin; 
        string firstLetter;
        address activePlayer;
        uint256 betAmount; 
        uint256 balance;
        uint256 moveCount; 
        GameStatus status; 
    }
    struct Roles {
        address player1;
        address player2;
    }
    enum GameStatus { 
        waitingForPlayers, 
        waitingForRandomWord, 
        inProgress, 
        finished, 
        cancelled
    }
    mapping(uint256 => Game) public games;
    mapping(address => PlayerAccount) public playersAccounts; //compte de joueurs
    mapping(uint256 => Roles) public gameRoles;

    event GameCreated(uint256 indexed gameId, address indexed player1, address indexed player2);
    event GameJoined(uint256 indexed _gameId, address indexed player2, address indexed player1);
    event GameStarted(uint256 indexed gameId, string firstLetter);
    event GameEnded(uint256 indexed gameId, address activePLayer, address otherPlayer);
    event GameCancelled(uint256 indexed gameId, address cancelled);
    event FirstLetter(uint256 indexed gameId, bytes1 _letterToGuess);
    event LetterSuggested(uint256 indexed gameId, address indexed player1, address indexed player2, string letterToGuess);
    event LetterWin(uint256 indexed _gameId,address indexed player1, address indexed player2, string letterWin);
    event WordGuessed(uint256 indexed gameId, address activePlayer, address otherPlayer, string wordGuess);
    event WordWin(uint256 indexed gameId, address activePlayer, address otherPlayer, string wordFound);
    event PlayerSwitched(uint256 gameId, address indexed player1, address indexed player2);
    event BalanceDeposited(address indexed player, uint256 amount);
    event BalanceWithdrawn(address indexed player, uint256 amount);
    event PotWon(uint256 indexed gameId, address activePlayer, uint256 currentPot);

    constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) {
        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        s_owner = msg.sender;
        s_subscriptionId = subscriptionId;
    }
    modifier onlyOwner() {
        require(msg.sender == s_owner, "Seul l'admin peut appeler cette fonction");
        _;
    }
    modifier inProgress() {
        require(pot > 0, "Le jeu n'est pas actif");
        _;
    }
    //PARTIE 2
   /**
     * @notice  permet au joueur 1 de créer un nouvelle partie en effectuant une mise
     * @dev     propriétés du jeu : met à jour le solde du joueur 1 en ajoutant le montant de la mise transférée 
     * @dev     incrémente l'ID de la partie pour le prochain jeu et met à jour le solde de la partie 
     * @dev     emet un event pour la création du jeu identifiant de la partie, adresse du joueur1 et adresse vide du joueur 2
     * @return  identifiant du jeu créé
     */
    function createGame(uint256 _betAmount) public payable returns (uint256) {
        require(_betAmount > 0, unicode"Le montant de la mise doit être supérieur à zéro");

        address player1Address = msg.sender;
        playersAccounts[player1Address].balance = msg.value;
        gameId = gameId + 1;
        games[gameId].balance = msg.value;
        games[gameId].status = GameStatus.waitingForPlayers;
        games[gameId].betAmount = _betAmount;
        gameRoles[gameId].player1 = player1Address;
        gameRoles[gameId].player2 = address(0);

        emit GameCreated(gameId, player1Address, address(0));

        return gameId;
    }
     /** 
     * @notice  permet au joueur 2 de rejoindre un jeu existant en effectuant une mise égale au montant de la mise
     * @dev     le joueur 2 peut rejoindre un jeu identifié par_gameId
     * @dev     récupération des références de jeu et roles associés à l'id du jeu
     * @dev     enregistrement del'adresse du joueur 2 dans le compte des joueurs et mise à jour de son solde
     * @dev     ajout du montant envoyé par le joueur 2 au solde du jeu
     * @dev     inversement des rôlms pour que le joueur 2 puisse jouer en premier
     * @param   _gameId l'identifiant du jeu auquel le joueur 2 souhaite se joindre
     */
    function joinGame(uint256 _gameId) public payable {
        Game storage game = games[_gameId];
        require(_gameId < gameId, "Identifiant de jeu invalide");
        require(msg.value == betAmount, unicode"Le montant envoyé doit être égal au montant de la mise"); 
        Roles memory roles = gameRoles[_gameId];
        require(game.status == GameStatus.waitingForPlayers, "Le jeu n'attend pas de joueurs");
        require(roles.player1 != address(0), "Le joueur 1 n'a pas encore rejoint le jeu");
        require(roles.player2 == address(0), unicode"Le joueur 2 est déjà présent dans le jeu");

        address player2Address = msg.sender;
        playersAccounts[player2Address].balance = msg.value;
        game.balance += msg.value;
        game.status = GameStatus.waitingForRandomWord;
        roles.player2 = player2Address;
        address tempPlayer = roles.player1;
        roles.player1 = roles.player2;
        roles.player2 = tempPlayer;

        emit GameJoined(_gameId, roles.player2, roles.player1);
    }
     /**
     * @notice  permet de démarrer un jeu avec l'identifiant de jeu spécifié 
     * @dev     elle vérifie les conditions et initialise les paramètres du jeu :
     * @dev     elle : récupère les références au jeu et aux rôles de jeu et génère un mot aléatoire
     * @dev     à deviner en utilisant la fonction requestRandomWords, récupère le mot filtré généré 
     * @dev     et la première lettre du mot filtré puis attribue le mot filtré au jeu ainsi que
     * @dev     la première lettre du mot filtré
     * @param   _gameId identifiant du jeu à démarrer
     */
    function startGame(uint256 _gameId) public {
        Game storage game = games[_gameId];
        require(_gameId < gameId, "Identifiant de jeu invalide");
        Roles memory roles = gameRoles[_gameId];
        require(game.status == GameStatus.waitingForPlayers, "Le jeu n'attend pas de joueurs");
        require(roles.player1 != address(0), "Le joueur 1 n'a pas encore rejoint le jeu");
        require(roles.player2 != address(0), "Le joueur 2 n'a pas encore rejoint le jeu");
        require(game.balance == betAmount * 2, unicode"Chaque joueur doit miser le même montant");
        require(game.status != GameStatus.inProgress, unicode"Un autre jeu est déjà en cours");

        requestId = requestRandomWords();
        filteredWord = games[_gameId].filteredWord;
        bytes1 firstLetterBytes = bytes(filteredWord)[0];
        game.filteredWord = filteredWord;
        game.firstLetter = string(abi.encodePacked(firstLetterBytes));
        game.status = GameStatus.inProgress;

        emit GameStarted(_gameId, game.firstLetter);
    }

    
     /**
     * @notice  permet d'obtenir l'adresse du joueur actif dans un jeu spécifique
     * @dev     elle est utilisée en interne par les fonctions setLetterGuess, setWordGuess
     * @dev     et endGame pour obtenir l'adresse du joueur actif en fonction de l'état du jeu et
     * @dev     des rôles des joueurs. Si le nombre de mouvements est pair, cela signifie que le joueur 1 a déjà joué et 
     * @dev     c'est maintenant au tour du joueur 2. Si le nombre de mouvements est impair, c'est le tour du joueur 1
     * @param   _gameId identifiant du jeu pour lequel récupérer le joueur actif
     * @return  adresse du joueur actif. Si aucun joueur actif n'est trouvé ou si l'identifiant du jeu 
     *           est invalide, renvoie address(0).
     */
    function getActivePlayer(uint256 _gameId) internal view returns (address) {
        Roles memory roles = gameRoles[_gameId];

        if (roles.player1 != address(0) && roles.player2 == address(0)) {
            return roles.player1;
        } else if (roles.player1 != address(0) && roles.player2 != address(0)) {
            Game memory game = games[_gameId];
            if (game.status == GameStatus.waitingForRandomWord) {
                return address(this);
            } else if (game.status == GameStatus.inProgress) {
                if (game.moveCount % 2 == 0) {
                    return roles.player1;
                } else {
                    return roles.player2;
                }
            }
        }
        return address(0);
    }
     /**
     * @notice  permet de récupérer le nombre de mouvements effectués dans un jeu spécifié
     * @dev     elle renvoie le nombre de mouvements effectués dans le jeu spécifié, permettant
     * @dev     ainsi de suivre l'avancement du jeu et de déterminer quel joueur est censé jouer ensuite
     * @dev     elle est utilisée en interne par la fonction getActivePlayer 
     * @param   _gameId identifiant du jeu pour lequel récupérer le nombre de mouvements
     * @return  nombre de mouvements effectués dans le jeu spécifié
     */
    function moveCount(uint256 _gameId) internal view returns (uint256) {
        Game memory game = games[_gameId];
        return game.moveCount;
    }
    //PARTIE 3
    /**
     * @notice  permet au joueur actif de deviner une lettre dans le jeu spécifié
     * @dev     elle ne peut être appelée que lorsque le jeu est en cours
     * @dev     getActivePlayer renvoie l'adresse qui est stockée (activePlayer) du joueur actif dans le jeu (gameId)
     * @dev     canProposeWord true permet au joueur de proposer un mot en appelant la fonction setWordToGuess
     * @dev     letterWin représente la lettre validée dans le mot
     * @dev     le joueur actif doit être l'appelant de la fonction
     * @param   _gameId identifiant du jeu dans lequel le joueur souhaite deviner une lettre.
     * @param   _letterGuess lettre proposée par le joueur
     * @return  booléen indiquant si la lettre a été trouvée dans le mot filtré.
     */
    function setLetterToGuess(uint256 _gameId, string memory _letterGuess) public inProgress returns (bool) {
        require(_gameId < gameId, "Identifiant de jeu invalide");
        address activePlayer = getActivePlayer(_gameId);
        require(msg.sender == activePlayer, unicode"Vous n'êtes pas le joueur actif dans ce jeu");
        require(bytes(_letterGuess).length == 1, "Veuillez deviner une seule lettre");

        if (!lettersWinInit) {
            lettersWin = new string[](0);
            lettersWinInit = true;
        }
        bool canProposeWord = false;
        Game storage game = games[_gameId];
        game.activePlayer = activePlayer;
        string memory letterWin;
        bytes memory wordBytes = bytes(filteredWord);
        for (uint256 i = 0; i < wordBytes.length; i++) {
            if (wordBytes[i] == bytes(_letterGuess)[0]) {
            lettersWin.push(_letterGuess);
            letterWin = _letterGuess;
            emit LetterWin(_gameId, player1, player2, letterWin);
            canProposeWord = true;
            break;
            }
        }
        if (canProposeWord) {
            setWordToGuess(_gameId, game.wordGuess);
            return true;
        } else {
            emit LetterSuggested(_gameId, player1, player2, _letterGuess);
            playersSwitched(_gameId);
            return false;
        }
    }
     /**
     * @notice  permet de renvoyer les informations sur la lettre devinée dans le jeu spécifié.
     * @dev     elle est pure et ne modifie pas l'état du contrat
     * @param   _gameId identifiant du jeu pour lequel on souhaite obtenir les informations sur la lettre devinée.
     * @param   _letterWin lettre trouvée dans le mot filtré
     * @param   _letterGuess Lettre devinée par le joueur
     * @return  Identifiant du jeu, lettres trouvées et lettre devinée.
     */
    function getLetterToGuess(uint256 _gameId, string memory _letterWin, string memory _letterGuess) public pure returns (uint256, string memory, string memory ) {
        return (_gameId, _letterWin, _letterGuess);
    }
     /**
     * @notice  permet à un joueur de proposer un mot à deviner dans un jeu en cours
     * @dev     elle doit être appelée par le joueur actif dans le jeu, elle convertit
     * @dev     le mot filtré en bytes32 pour vérification, récupère le jeu en cours
     * @dev     vérifie le mot proposé par le joueur, notifie les joueurs que le mot a été trouvé
     * @dev     attribue la cagnotte au joueur gagnant, notifie la fin de la partie et que la cagnotte est remportée
     * @dev     Ensuite, elle appelle la fonction endGame pour terminer le jeu et distribuer la cagnotte au joueur actif
     * @dev     sinon si le mot est incorrecte,elle notifie que le mot proposé est incorrect. 
     * @param   _gameId identifiant du jeu en cours
     * @param   _wordGuess mot proposé par le joueur
     */
    function setWordToGuess(uint256 _gameId, string memory _wordGuess) public inProgress {
        require(_gameId < gameId, "Identifiant de jeu invalide");
        address activePlayer = getActivePlayer(_gameId);
        require(msg.sender == activePlayer, unicode"Vous n'êtes pas le joueur actif dans ce jeu.");

        bytes32 filteredWordBytes = bytes32(uint256(keccak256(abi.encodePacked(games[_gameId].filteredWord))));
        require(filteredWordBytes != bytes32(0), unicode"Le mot à deviner n'a pas été défini.");
        Game storage game = games[_gameId];
        game.activePlayer = activePlayer;
        address otherPlayer;

        if (keccak256(abi.encodePacked(_wordGuess)) == keccak256(abi.encodePacked(filteredWord))) {
            game.wordWin = _wordGuess;
            emit WordWin(_gameId, activePlayer, otherPlayer, game.wordWin);
            game.status = GameStatus.finished;
            uint256 currentPot = pot;
            pot = 0;
            playersAccounts[msg.sender].balance += currentPot;

            endGame(_gameId);

            emit GameEnded(_gameId, msg.sender, otherPlayer);
            emit PotWon(_gameId, msg.sender, currentPot);
            } else {
                Roles memory roles = gameRoles[_gameId];
                if (msg.sender == roles.player1) {
                    otherPlayer = roles.player2;
                } else if (msg.sender == roles.player2) {
                    otherPlayer = roles.player1;
                }
            wordGuesses.push(_wordGuess);
            emit WordGuessed(_gameId, activePlayer, otherPlayer, _wordGuess);
            playersSwitched(_gameId);
            }
    }
    function getWordToGuess(uint256 _gameId, string memory _wordGuess) public pure returns (uint256, string memory ) {
        return (_gameId, _wordGuess);
    }
    /**
     * @notice  permet d'échanger les rôles des joueurs dans un jeu spécifié
     * @dev     la fonction est utilisée pour échanger les rôles des joueurs dans le jeu identifié par l'identifiant _gameId
     * @param   _gameId identifiant du jeu dans lequel les rôles des joueurs doivent être échangés
     */
    function playersSwitched(uint256 _gameId) private {
        Roles memory roles = gameRoles[_gameId];
        require(msg.sender == roles.player1 || msg.sender == roles.player2, unicode"Vous n'êtes pas un joueur dans ce jeu");

        address player1Address = roles.player1;
        roles.player1 = roles.player2;
        roles.player2 = player1Address;

        Game storage game = games[_gameId];
        game.activePlayer = roles.player1;

        emit PlayerSwitched(_gameId, roles.player1, roles.player2);
    }
    /**
     * @dev     permet de convertir un uint256 en sa représentation décimale ASCII string
     * @dev     Inspired by OraclizeAPI's implementation - MIT licence
     * @dev     https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
     * @param   value nombre uint256 à convertir
     * @return  la représentation décimale ASCII du nombre sous forme de string
     */
    function uint256ToString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
        bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
        return string(buffer);
    }
    /**
     * @notice  permet de convertir un bytes32 en string
     * @dev     elle prend un bytes32 en paramètre et le convertit en string
     * @param   _bytes32 Le bytes32 à convertir en string
     * @return  string résultant de la conversion du bytes32
     */
    function bytes32ToString(bytes32 _bytes32) internal pure returns (string memory) {
        bytes memory bytesArray = new bytes(32);
         for (uint256 i = 0; i < 32; i++) {
        bytesArray[i] = bytes1(uint8(uint256(_bytes32) >> (i * 8)));
    }
        return string(bytesArray);
    }
    /**
    * @notice   permet de convertir un bytes1 en string
     * @dev     la fonction prend un bytes1 en paramètre et le convertit en string
     * @param   _letter Le bytes1 à convertir en string
     * @return  string résultant de la conversion du bytes1
     */
    function bytes1ToString(bytes1 _letter) internal pure returns (string memory) {
        bytes memory letterBytes = new bytes(1);
        letterBytes[0] = _letter;
        return string(letterBytes);
    }
     /**
     * @notice  permet de filtrer les caractères spéciaux d'une chaîne de caractères en ne gardant que les lettres minuscules
     * @dev     convertit la chaîne de caractères en un tableau de bytes et parcourt chaque caractère
     * @dev     si le caractère est une lettre majuscule, il est converti en minuscule en ajoutant 32 à la valeur ASCII.
     * @param   _randomString La chaîne de caractères à filtrer
     * @return  chaîne de caractères filtrée, ne contenant que les lettres minuscules
     */
    function filterSpecialCharacters(string memory _randomString) internal pure returns (string memory) {
        bytes memory randomBytes = bytes(_randomString);
        bytes memory filteredBytes = new bytes(randomBytes.length);
        uint256 filteredIndex = 0;
            for (uint256 i = 0; i < randomBytes.length; i++) {
                bytes1 currentChar = randomBytes[i];
                bytes1 lowercaseChar = bytes1(uint8(currentChar));

                if ((lowercaseChar >= bytes1("A") && lowercaseChar <= bytes1("Z"))) {
                    lowercaseChar = bytes1(uint8(lowercaseChar) + 32); 
                }
                if ((lowercaseChar >= bytes1("a") && lowercaseChar <= bytes1("z"))) {
                    filteredBytes[filteredIndex] = lowercaseChar;
                    filteredIndex++;
                }
            }
            bytes memory filteredResult = new bytes(filteredIndex);
            for (uint256 i = 0; i < filteredIndex; i++) {
                filteredResult[i] = filteredBytes[i];
            }
        return string(filteredResult);
    }
     /**
     * @notice  permet de récupérer les détails d'un jeu spécifié par son identifiant
     * @dev     la fonction permet de récupérer les détails d'un jeu, tels que les joueurs impliqués, le solde du jeu et son statut
     * @param   _gameId identifiant du jeu dont on souhaite obtenir les détails
     * @return  activePlayer adresse du joueur actif
     * @return  firstLetter la première lettre
     * @return  letterFound tableau des lettres trouvées
     * @return  balance le solde du jeu
     * @return  wordGuesses tableau des mots devinés dans le jeu
     * @return  status  le statut du jeu
     */
    function getGameDetails(uint256 _gameId) public view returns (uint256, uint256,  address, string memory, string memory, string memory, uint256, string memory, GameStatus) {
        Game storage game = games[_gameId];
        return (gameId, game.betAmount, game.activePlayer, game.firstLetter, game.letterWin, game.letterGuess, game.balance, game.wordGuess, game.status);
    }
    //PARTIE 4
    /**
     * @notice  permet de terminer un jeu spécifié et d'attribuer la cagnotte du jeu au joueur actif gagnant
     * @dev     elle est appelée lorsque le mot est trouvé dans la fonction setWordToGuess
     * @dev     le solde du jeu au joueur activePlayer
     * @param   _gameId identifiant du jeu qui doit être terminé
     */
    function endGame(uint256 _gameId) public {
        Game storage game = games[_gameId];
        game.status = GameStatus.finished;
        address activePlayer = getActivePlayer(_gameId);
        address otherPlayer = address(0);

        uint256 currentPot = game.balance;
        pot = 0;
        playersAccounts[activePlayer].balance += currentPot;

        emit GameEnded(_gameId, activePlayer, otherPlayer);
        emit PotWon(_gameId, activePlayer, currentPot);
    }
    /**
     * @notice  permet d'effectuer un dépôt pour participer au jeu
     * @dev     la fonction permet à un joueur d'effectuer un dépôt pour participer au jeu. 
     * @dev     le montant du dépôt doit être égal au montant de la mise.
     */
    function deposit() external payable {
        require(msg.value == betAmount, unicode"Le montant n'est pas égal à la mise attendu");

        PlayerAccount storage account = playersAccounts[msg.sender];
        account.balance += msg.value;
        pot += msg.value;

        emit BalanceDeposited(msg.sender, msg.value);
    }
     modifier finished() {
        require(pot == 0, "Le jeu est en cours");
        _;
    }
    /**
     * @notice  permetd'effectuer un retrait des fonds du joueur
     * @dev     la fonction permet à un joueur de retirer les fonds de son compte après la fin du jeu 
     * @dev     le joueur doit avoir un solde positif et ne doit pas avoir déjà effectué un retrait
     * @param   _amount Montant à retirer du solde du joueur
     */
    function withdraw(uint256 _amount) external payable finished {
        PlayerAccount storage account = playersAccounts[msg.sender];
        require(account.balance > 0, unicode"Vous n'avez pas de solde à retirer");
        require(!account.hasWithdrawn, unicode"Vous avez déjà retiré votre solde");
        require(account.balance >= _amount, unicode"Montant de retrait supérieur au solde");
        require(!isContract(player1), "Le joueur 1 est un contrat malveillant");
        require(!isContract(player2), "Le joueur 1 est un contrat malveillant");

        uint256 withdrawAmount = account.balance;
        account.balance = 0;
        account.hasWithdrawn = true;
        (bool success, ) = payable (msg.sender).call{value: withdrawAmount}("");
        require(success, unicode"Le retrait a échoué");

        emit BalanceWithdrawn(msg.sender, withdrawAmount);
    }
     /**
     * @notice  permet d'effacer les données de devinettes de lettres et de mots pour un jeu spécifique (_gameId)
     * @dev     la fonction est appelée en interne
     * @dev     le jeu doit être terminé et la cagnotte dot être distribuée au gagnant avant d'appeler cette fonction
     * @param   _gameId identifiant du jeu pour lequel les devinettes de lettres et de mots doivent être effacées
     */
    function clear(uint256 _gameId) internal {
        require(games[_gameId].status == GameStatus.finished, unicode"Le jeu n'est pas terminé.");
        require(pot == 0, unicode"La cagnotte n'a pas encore été distribuée au gagnant.");
        delete games[_gameId].letterWin;
        delete games[_gameId].letterGuess;
        delete lettersWin;
        delete games[_gameId].wordGuess;
        delete wordGuesses;
    }
        /**
     * @notice  permet de vérifier si l'adresse donnée correspond à un contrat ou non
     * @dev     utilise une technique basée sur l'assemblage pour déterminer si l'adresse a du code de contrat associé à l'adresse donnée
     * @dev     si la taille est > 0 =  true, il y a du bytecode déployé à l'adresse donc c'est un contrat sinon false
     * @dev     elle est appelée par la fonction cancelGame
     * @param   _addr L'adresse à vérifier
     * @return  valeur booléenne indiquant si l'adresse donnée correspond à un contrat ou non
     */
    function isContract(address _addr) public view returns (bool) {
        uint32 size;
        assembly {
            size := extcodesize(_addr)
        }
        return (size > 0);
    }
     /**
     * @notice  permet d'annuler un jeu spécifique en cours
     * @dev     récupère le jeu à partir de son identifiant, vérifie que :
     * @dev     le jeu est en attente de joueur, l'appelant est l'un des joueurs du jeu
     * @dev     si l'adresse donnée correspond au contrat (appel de isContract) alors :
     * @dev     le joueur 1 n'est pas un contrat malveillant puis transfère les fonds au joueur 1
     * @dev     le joueur 2 n'est pas un contrat malveillant puis transfère les fonds au joueur 2
     * @dev     l'adresse du
     * @dev     met à jour le statut du jeu à (annulé) puis réinitialise le montant total du pot à zéro
     * @param   _gameId identifiant du jeu à annuler.
     */
    function cancelGame(uint256 _gameId) external {
        Game storage game = games[_gameId];
        require(game.status == GameStatus.waitingForPlayers, "Le jeu est en cours");
        require(player1 == msg.sender || player2 == msg.sender, unicode"Vous n'êtes pas un joueur dans ce jeu");

        bool success;
        if (player1 == msg.sender) {
            require(game.balance > 0, unicode"Vous n'avez pas de solde à retirer");
            require(!isContract(player1), "Le joueur 1 est un contrat malveillant");
            (success, ) = payable(player1).call{value: game.balance}("");
            require(success, unicode"Échec du transfert de fonds au joueur 1");

        } else if (player2 == msg.sender) {
            require(game.balance > 0, unicode"Vous n'avez pas de solde à retirer");
            require(!isContract(player2), "Le joueur 2 est un contrat malveillant");
            (success, ) = payable(player2).call{value: game.balance}("");
            require(success, unicode"Échec du transfert de fonds au joueur 2");
        }
        game.status = GameStatus.cancelled;
        pot = 0;

        emit GameCancelled(_gameId, address(0));
    }
    //PARTIE 5
     /**
     * @notice  permet de demander la génération de mots aléatoires à l'oracle Chainlink
     * @dev     la fonction est accessible uniquement par le propriétaire du contrat
     * @dev     elle utilise l'interface du Coordinateur Chainlink pour demander la génération de mots aléatoires
     * @dev     l'identifiant de la demande est ensuite stocké dans le tableau requestIds et renvoyé en sortie de la fonction :
     * @return  _requestId identifiant de la demande de génération de mots aléatoires
     */
    function requestRandomWords() public onlyOwner returns (uint256 _requestId) {
        requestId = COORDINATOR.requestRandomWords(
            keyHash,
            s_subscriptionId,
            requestConfirmations,
            callbackGasLimit,
            numWords
        );
        requestIds.push(requestId);
        return requestId;
    }
     /**
     * @notice  permet d'obtenir le mot filtré dans le jeu en utilisant les mots aléatoires générés
     * @dev     la fonction est appelée par le service Oracle pour fournir les mots aléatoires demandés puis
     * @dev     parcourt chaque mot aléatoire et le convertit en une représentation de chaine de caractères
     * @dev     en appelant la fonction uint256ToString, elle filtre les caractères spéciaux dans le mot en appelant
     * @dev     filterSpecialCharacter puis assigne le mot à la structure du jeu
     * @param   _requestId identifiant de la demande pour laquelle les mots aléatoires sont générés
     * @param   _randomWords mots aléatoires générés
     */
    function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override {
        requestIds.push(_requestId);

        for (uint256 i = 0; i < _randomWords.length; i++) {
            string memory word = uint256ToString(_randomWords[i]);
            filteredWord = filterSpecialCharacters(word);
            games[_requestId].filteredWord = filteredWord;
        }
    }
     /**
     * @notice  Permet de définir la valeur du délai pour la demande de VRF.
     * @dev     La fonction est utilisée pour définir le délai en blocs pour la demande de VRF.
     * @param   _delay La valeur du délai à définir.
     */
    function setDelay(uint256 _delay) external onlyOwner {
        require(requestId == 0, unicode"La réponse VRF a déjà été reçue");
        delay = _delay;
    }

}

