# Trexquant Interview Project (The Hangman Game)

* Copyright Trexquant Investment LP. All Rights Reserved. 
* Redistribution of this question without written consent from Trexquant is prohibited
## Instruction:
For this coding test, your mission is to write an algorithm that plays the game of Hangman through our API server. 

When a user plays Hangman, the server first selects a secret word at random from a list. The server then returns a row of underscores (space separated)—one for each letter in the secret word—and asks the user to guess a letter. If the user guesses a letter that is in the word, the word is redisplayed with all instances of that letter shown in the correct positions, along with any letters correctly guessed on previous turns. If the letter does not appear in the word, the user is charged with an incorrect guess. The user keeps guessing letters until either (1) the user has correctly guessed all the letters in the word
or (2) the user has made six incorrect guesses.

You are required to write a "guess" function that takes current word (with underscores) as input and returns a guess letter. You will use the API codes below to play 1,000 Hangman games. You have the opportunity to practice before you want to start recording your game results.

Your algorithm is permitted to use a training set of approximately 250,000 dictionary words. Your algorithm will be tested on an entirely disjoint set of 250,000 dictionary words. Please note that this means the words that you will ultimately be tested on do NOT appear in the dictionary that you are given. You are not permitted to use any dictionary other than the training dictionary we provided. This requirement will be strictly enforced by code review.

You are provided with a basic, working algorithm. This algorithm will match the provided masked string (e.g. a _ _ l e) to all possible words in the dictionary, tabulate the frequency of letters appearing in these possible words, and then guess the letter with the highest frequency of appearence that has not already been guessed. If there are no remaining words that match then it will default back to the character frequency distribution of the entire dictionary.

This benchmark strategy is successful approximately 18% of the time. Your task is to design an algorithm that significantly outperforms this benchmark.
import json
import requests
import random
import string
import secrets
import time
import re
import collections

try:
    from urllib.parse import parse_qs, urlencode, urlparse
except ImportError:
    from urlparse import parse_qs, urlparse
    from urllib import urlencode

from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
class HangmanAPI(object):
    def __init__(self, access_token=None, session=None, timeout=None):
        self.hangman_url = self.determine_hangman_url()
        self.access_token = access_token
        self.session = session or requests.Session()
        self.timeout = timeout
        self.guessed_letters = []
        
        full_dictionary_location = "words_250000_train.txt"
        self.full_dictionary = self.build_dictionary(full_dictionary_location)        
        self.full_dictionary_common_letter_sorted = collections.Counter("".join(self.full_dictionary)).most_common()
        
        self.current_dictionary = []
        
    @staticmethod
    def determine_hangman_url():
        links = ['https://trexsim.com', 'https://sg.trexsim.com']

        data = {link: 0 for link in links}

        for link in links:

            requests.get(link)

            for i in range(10):
                s = time.time()
                requests.get(link)
                data[link] = time.time() - s

        link = sorted(data.items(), key=lambda x: x[1])[0][0]
        link += '/trexsim/hangman'
        return link

    def guess(self, word): # word input example: "_ p p _ e "
        ###############################################
        # Replace with your own "guess" function here #
        ###############################################

        # clean the word so that we strip away the space characters
        # replace "_" with "." as "." indicates any character in regular expressions
        clean_word = word[::2].replace("_",".")
        
        # find length of passed word
        len_word = len(clean_word)
        
        # grab current dictionary of possible words from self object, initialize new possible words dictionary to empty
        current_dictionary = self.current_dictionary
        new_dictionary = []
        
        # iterate through all of the words in the old plausible dictionary
        for dict_word in current_dictionary:
            # continue if the word is not of the appropriate length
            if len(dict_word) != len_word:
                continue
                
            # if dictionary word is a possible match then add it to the current dictionary
            if re.match(clean_word,dict_word):
                new_dictionary.append(dict_word)
        
        # overwrite old possible words dictionary with updated version
        self.current_dictionary = new_dictionary
        
        
        # count occurrence of all characters in possible word matches
        full_dict_string = "".join(new_dictionary)
        
        c = collections.Counter(full_dict_string)
        sorted_letter_count = c.most_common()                   
        
        guess_letter = '!'
        
        # return most frequently occurring letter in all possible words that hasn't been guessed yet
        for letter,instance_count in sorted_letter_count:
            if letter not in self.guessed_letters:
                guess_letter = letter
                break
            
        # if no word matches in training dictionary, default back to ordering of full dictionary
        if guess_letter == '!':
            sorted_letter_count = self.full_dictionary_common_letter_sorted
            for letter,instance_count in sorted_letter_count:
                if letter not in self.guessed_letters:
                    guess_letter = letter
                    break            
        
        return guess_letter

    ##########################################################
    # You'll likely not need to modify any of the code below #
    ##########################################################
    
    def build_dictionary(self, dictionary_file_location):
        text_file = open(dictionary_file_location,"r")
        full_dictionary = text_file.read().splitlines()
        text_file.close()
        return full_dictionary
                
    def start_game(self, practice=True, verbose=True):
        # reset guessed letters to empty set and current plausible dictionary to the full dictionary
        self.guessed_letters = []
        self.current_dictionary = self.full_dictionary
                         
        response = self.request("/new_game", {"practice":practice})
        if response.get('status')=="approved":
            game_id = response.get('game_id')
            word = response.get('word')
            tries_remains = response.get('tries_remains')
            if verbose:
                print("Successfully start a new game! Game ID: {0}. # of tries remaining: {1}. Word: {2}.".format(game_id, tries_remains, word))
            while tries_remains>0:
                # get guessed letter from user code
                guess_letter = self.guess(word)
                    
                # append guessed letter to guessed letters field in hangman object
                self.guessed_letters.append(guess_letter)
                if verbose:
                    print("Guessing letter: {0}".format(guess_letter))
                    
                try:    
                    res = self.request("/guess_letter", {"request":"guess_letter", "game_id":game_id, "letter":guess_letter})
                except HangmanAPIError:
                    print('HangmanAPIError exception caught on request.')
                    continue
                except Exception as e:
                    print('Other exception caught on request.')
                    raise e
               
                if verbose:
                    print("Sever response: {0}".format(res))
                status = res.get('status')
                tries_remains = res.get('tries_remains')
                if status=="success":
                    if verbose:
                        print("Successfully finished game: {0}".format(game_id))
                    return True
                elif status=="failed":
                    reason = res.get('reason', '# of tries exceeded!')
                    if verbose:
                        print("Failed game: {0}. Because of: {1}".format(game_id, reason))
                    return False
                elif status=="ongoing":
                    word = res.get('word')
        else:
            if verbose:
                print("Failed to start a new game")
        return status=="success"
        
    def my_status(self):
        return self.request("/my_status", {})
    
    def request(
            self, path, args=None, post_args=None, method=None):
        if args is None:
            args = dict()
        if post_args is not None:
            method = "POST"

        # Add `access_token` to post_args or args if it has not already been
        # included.
        if self.access_token:
            # If post_args exists, we assume that args either does not exists
            # or it does not need `access_token`.
            if post_args and "access_token" not in post_args:
                post_args["access_token"] = self.access_token
            elif "access_token" not in args:
                args["access_token"] = self.access_token

        time.sleep(0.2)

        num_retry, time_sleep = 50, 2
        for it in range(num_retry):
            try:
                response = self.session.request(
                    method or "GET",
                    self.hangman_url + path,
                    timeout=self.timeout,
                    params=args,
                    data=post_args,
                    verify=False
                )
                break
            except requests.HTTPError as e:
                response = json.loads(e.read())
                raise HangmanAPIError(response)
            except requests.exceptions.SSLError as e:
                if it + 1 == num_retry:
                    raise
                time.sleep(time_sleep)

        headers = response.headers
        if 'json' in headers['content-type']:
            result = response.json()
        elif "access_token" in parse_qs(response.text):
            query_str = parse_qs(response.text)
            if "access_token" in query_str:
                result = {"access_token": query_str["access_token"][0]}
                if "expires" in query_str:
                    result["expires"] = query_str["expires"][0]
            else:
                raise HangmanAPIError(response.json())
        else:
            raise HangmanAPIError('Maintype was not text, or querystring')

        if result and isinstance(result, dict) and result.get("error"):
            raise HangmanAPIError(result)
        return result
    
class HangmanAPIError(Exception):
    def __init__(self, result):
        self.result = result
        self.code = None
        try:
            self.type = result["error_code"]
        except (KeyError, TypeError):
            self.type = ""

        try:
            self.message = result["error_description"]
        except (KeyError, TypeError):
            try:
                self.message = result["error"]["message"]
                self.code = result["error"].get("code")
                if not self.type:
                    self.type = result["error"].get("type", "")
            except (KeyError, TypeError):
                try:
                    self.message = result["error_msg"]
                except (KeyError, TypeError):
                    self.message = result

        Exception.__init__(self, self.message)
# API Usage Examples
## To start a new game:
1. Make sure you have implemented your own "guess" method.
2. Use the access_token that we sent you to create your HangmanAPI object. 
3. Start a game by calling "start_game" method.
4. If you wish to test your function without being recorded, set "practice" parameter to 1.
5. Note: You have a rate limit of 20 new games per minute. DO NOT start more than 20 new games within one minute.
api = HangmanAPI(access_token="INSERT_YOUR_TOKEN_HERE", timeout=2000)

## Playing practice games:
You can use the command below to play up to 100,000 practice games.
api.start_game(practice=1,verbose=True)
[total_practice_runs,total_recorded_runs,total_recorded_successes,total_practice_successes] = api.my_status() # Get my game stats: (# of tries, # of wins)
practice_success_rate = total_practice_successes / total_practice_runs
print('run %d practice games out of an allotted 100,000. practice success rate so far = %.3f' % (total_practice_runs, practice_success_rate))

## Playing recorded games:
Please finalize your code prior to running the cell below. Once this code executes once successfully your submission will be finalized. Our system will not allow you to rerun any additional games.

Please note that it is expected that after you successfully run this block of code that subsequent runs will result in the error message "Your account has been deactivated".

Once you've run this section of the code your submission is complete. Please send us your source code via email.
for i in range(1000):
    print('Playing ', i, ' th game')
    # Uncomment the following line to execute your final runs. Do not do this until you are satisfied with your submission
    #api.start_game(practice=0,verbose=False)
    
    # DO NOT REMOVE as otherwise the server may lock you out for too high frequency of requests
    time.sleep(0.5)

## To check your game statistics
1. Simply use "my_status" method.
2. Returns your total number of games, and number of wins.
[total_practice_runs,total_recorded_runs,total_recorded_successes,total_practice_successes] = api.my_status() # Get my game stats: (# of tries, # of wins)
success_rate = total_recorded_successes/total_recorded_runs
print('overall success rate = %.3f' % success_rate)

study well above task
and correct my below code make  my below code perfect to run with highest accuracy
import json
import requests
import random
import string
import time
import re
import collections
from urllib.parse import parse_qs, urlparse

# Disable SSL warnings (optional, based on your environment)
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# Vowels list for analysis (optional, can be used for advanced strategies)
vowels = ['a', 'e', 'i', 'o', 'u']


# Function to calculate vowel ratio in a word (optional)
def vowel_count(clean_word):
    count = sum(1 for i in clean_word if i in vowels)
    return count / len(clean_word) if len(clean_word) > 0 else 0


# Load and clean the dictionary
def load_dictionary(file_path):
    try:
        with open(file_path, "r") as f:
            # Load words, convert to lowercase, and ensure they are purely alphabetic
            words = [line.strip().lower() for line in f if line.strip().isalpha()]
        return words
    except FileNotFoundError:
        print(f"Dictionary file not found at {file_path}. Please verify the path.")
        return []


# Function to count letter appearances
def count_letters(word_list):
    dictx = collections.Counter()
    for word in word_list:
        unique_letters = set(word) - set(dictx)
        dictx.update(unique_letters)
    return dictx


class HangmanAPIError(Exception):
    """Custom exception for Hangman API errors."""
    def __init__(self, result):
        self.result = result
        self.code = None
        try:
            self.type = result["error_code"]
        except (KeyError, TypeError):
            self.type = ""
        try:
            self.message = result["error_description"]
        except (KeyError, TypeError):
            try:
                self.message = result["error"]["message"]
                self.code = result["error"].get("code")
                if not self.type:
                    self.type = result["error"].get("type", "")
            except (KeyError, TypeError):
                try:
                    self.message = result["error_msg"]
                except (KeyError, TypeError):
                    self.message = "Unknown Hangman API Error."
        Exception.__init__(self, self.message)


class HangmanAPI(object):
    def __init__(self, access_token=None, session=None, timeout=10):
        self.hangman_url = self.determine_hangman_url()
        self.access_token = access_token
        self.session = session or requests.Session()
        self.timeout = timeout
        self.guessed_letters = []
        
        # Update the path to your dictionary file as needed
        full_dictionary_location = "/kaggle/input/hangmen/words_250000_train.txt"  # Ensure this path is correct
        self.full_dictionary = self.build_dictionary(full_dictionary_location)
        if not self.full_dictionary:
            raise ValueError("Failed to load the dictionary. Terminating the program.")
        print(f"Loaded dictionary with {len(self.full_dictionary)} words.")
        
        # Precompute letter frequencies in the full dictionary
        self.full_dictionary_common_letter_sorted = collections.Counter("".join(self.full_dictionary)).most_common()
        
        # Initialize current dictionary
        self.current_dictionary = self.full_dictionary.copy()
        
        # RL variables
        self.q_table = {}
        self.learning_rate = 0.1
        self.discount_factor = 0.9
        self.epsilon = 1.0
        self.epsilon_decay = 0.995
        self.min_epsilon = 0.01
        
        # Statistics
        self.total_practice_runs = 0
        self.total_recorded_runs = 0
        self.total_recorded_successes = 0
        self.total_practice_successes = 0

    @staticmethod
    def determine_hangman_url():
        """Determine the best Hangman API URL based on response times."""
        links = ['https://trexsim.com', 'https://sg.trexsim.com']
        data = {link: 0 for link in links}
        
        print("Determining the fastest Hangman API URL...")
        for link in links:
            try:
                s = time.time()
                response = requests.get(link, timeout=5)
                if response.status_code == 200:
                    elapsed = time.time() - s
                    data[link] = elapsed
                    print(f"Link {link} responded in {elapsed:.4f} seconds.")
                else:
                    data[link] = float('inf')
                    print(f"Link {link} responded with status code {response.status_code}.")
            except requests.RequestException as e:
                data[link] = float('inf')
                print(f"Link {link} failed with exception: {e}")
                
        sorted_links = sorted(data.items(), key=lambda x: x[1])
        best_link = sorted_links[0][0] if sorted_links and sorted_links[0][1] != float('inf') else 'https://trexsim.com'
        hangman_endpoint = '/trexsim/hangman'
        final_url = best_link + hangman_endpoint
        print(f"Determined Hangman URL: {final_url}")
        return final_url

    def build_dictionary(self, file_path):
        """Load the dictionary from the specified file."""
        words = load_dictionary(file_path)
        if words:
            print(f"Dictionary loaded from {file_path}.")
        return words

    def request(self, path, args=None, post_args=None, method=None):
        """Send a request to the Hangman API."""
        if args is None:
            args = {}
        if post_args is not None:
            method = "POST"

        # Add access_token to the request
        if self.access_token:
            if post_args and "access_token" not in post_args:
                post_args["access_token"] = self.access_token
            elif "access_token" not in args:
                args["access_token"] = self.access_token

        time.sleep(0.2)  # Rate limiting

        num_retry, time_sleep = 50, 2
        for attempt in range(num_retry):
            try:
                response = self.session.request(
                    method or "GET",
                    self.hangman_url + path,
                    timeout=self.timeout,
                    params=args,
                    json=post_args,
                    verify=False
                )
                response.raise_for_status()
                break
            except requests.HTTPError as e:
                try:
                    error_content = e.response.text
                    error_json = json.loads(error_content)
                    raise HangmanAPIError(error_json)
                except json.JSONDecodeError:
                    print(f"Non-JSON error response received: {e.response.text}")
                    raise HangmanAPIError({"error_msg": "HTTP Error with no content."})
            except requests.exceptions.SSLError as e:
                if attempt + 1 == num_retry:
                    raise
                print(f"SSL Error encountered. Retrying in {time_sleep} seconds...")
                time.sleep(time_sleep)
            except requests.RequestException as e:
                if attempt + 1 == num_retry:
                    raise
                print(f"Request exception encountered: {e}. Retrying in {time_sleep} seconds...")
                time.sleep(time_sleep)

        headers = response.headers
        content_type = headers.get('content-type', '')
        if 'application/json' in content_type:
            try:
                result = response.json()
            except json.JSONDecodeError:
                print(f"Failed to decode JSON response. Response Text: {response.text}")
                raise HangmanAPIError({"error_msg": "Failed to decode JSON response."})
        else:
            # Handle non-json responses gracefully
            try:
                result = json.loads(response.text)
            except json.JSONDecodeError:
                print(f"Non-JSON response received: {response.text}")
                raise HangmanAPIError({"error_msg": "Response content type not supported."})
        
        if isinstance(result, dict) and result.get("error"):
            print(f"API Error: {result.get('error')}")
            raise HangmanAPIError(result)
        return result

    def build_state(self, word, guessed_letters, tries_left):
        """Create a hashable state from the current word, guessed letters, and tries left."""
        clean_word = word.replace(" ", "").lower()  # Remove spaces and lowercase
        state = (clean_word, "".join(sorted(guessed_letters)), tries_left)
        return state

    def update_q_table(self, state, action, reward, next_state):
        """Update Q-Table using Q-Learning formula."""
        if state not in self.q_table:
            self.q_table[state] = {letter: 0 for letter in string.ascii_lowercase}
        
        if next_state not in self.q_table:
            self.q_table[next_state] = {letter: 0 for letter in string.ascii_lowercase}
        
        q_current = self.q_table[state][action]
        q_next_max = max(self.q_table[next_state].values()) if self.q_table[next_state] else 0
        
        # Q-Learning update rule
        self.q_table[state][action] = q_current + self.learning_rate * (reward + self.discount_factor * q_next_max - q_current)

    def guess_rl(self, word, tries_left):
        """Guess the next letter based on the RL algorithm."""
        state = self.build_state(word, self.guessed_letters, tries_left)
        
        # Epsilon-greedy action selection
        if random.uniform(0, 1) < self.epsilon:
            # Explore: Random action
            available_actions = [letter for letter in string.ascii_lowercase if letter not in self.guessed_letters]
            if not available_actions:
                return None
            guess_letter = random.choice(available_actions)
        else:
            # Exploit: Choose best action based on Q-Table
            if state not in self.q_table:
                self.q_table[state] = {letter: 0 for letter in string.ascii_lowercase}
            
            sorted_actions = sorted(self.q_table[state].items(), key=lambda x: x[1], reverse=True)
            guess_letter = None
            for letter, _ in sorted_actions:
                if letter not in self.guessed_letters:
                    guess_letter = letter
                    break
            if not guess_letter:
                available_actions = [letter for letter in string.ascii_lowercase if letter not in self.guessed_letters]
                guess_letter = random.choice(available_actions) if available_actions else None
        
        return guess_letter

    def start_game(self, practice=True, verbose=True):
        """Play a full game of hangman, implementing the RL-based guessing."""
        # Reset guessed letters and dictionary
        self.guessed_letters = []
        self.current_dictionary = self.full_dictionary.copy()
        
        # Start new game with GET method and 'practice' parameter
        try:
            response = self.request("/new_game", args={"practice": int(practice)}, post_args=None, method="GET")
        except HangmanAPIError as e:
            if verbose:
                print(f"HangmanAPIError during new_game: {e.message}")
            return False
        except Exception as e:
            if verbose:
                print(f"Unexpected error during new_game: {e}")
            return False

        if response.get('status') == "approved":
            game_id = response.get('game_id')
            word = response.get('word')  # Example: "_ p p _ e "
            tries_remains = response.get('tries_remains')
            
            if verbose:
                print(f"New game started. Game ID: {game_id}. Tries left: {tries_remains}. Word: {word}")
            
            # Update statistics
            if practice:
                self.total_practice_runs += 1
            else:
                self.total_recorded_runs += 1
            
            state = self.build_state(word, self.guessed_letters, tries_remains)
            
            while tries_remains > 0:
                # Guess based on RL algorithm
                guess_letter = self.guess_rl(word, tries_left=tries_remains)
                if not guess_letter:
                    if verbose:
                        print("No available letters to guess.")
                    break
                self.guessed_letters.append(guess_letter)
                
                if verbose:
                    print(f"Guessing letter: '{guess_letter}'")
                
                # Make the guess and get response
                try:
                    res = self.request("/guess_letter", args=None, post_args={
                        "request": "guess_letter",
                        "game_id": game_id,
                        "letter": guess_letter
                    }, method="POST")
                except HangmanAPIError as e:
                    if verbose:
                        print(f"HangmanAPIError during guess_letter: {e.message}")
                    break
                except Exception as e:
                    if verbose:
                        print(f"Unexpected error during guess_letter: {e}")
                    break
                
                if verbose:
                    print(f"Server response: {res}")
                
                status = res.get('status')
                tries_remains = res.get('tries_remains')
                correct_guess = res.get('correct_guess', False)
                
                # Assign reward and update Q-Table
                reward = self.reward_function(correct_guess)
                new_word = res.get('word', word).lower()
                next_state = self.build_state(new_word, self.guessed_letters, tries_remains)
                self.update_q_table(state, guess_letter, reward, next_state)
                
                # Update state
                state = next_state
                
                # Update epsilon (decay for exploration-exploitation trade-off)
                self.epsilon = max(self.min_epsilon, self.epsilon * self.epsilon_decay)
                
                if status == "success":
                    if verbose:
                        print("Game won!")
                    if practice:
                        self.total_practice_successes += 1
                    else:
                        self.total_recorded_successes += 1
                    break
                elif status == "failed":
                    if verbose:
                        print("Game lost.")
                    break
                elif status == "ongoing":
                    word = res.get('word', word).lower()
        else:
            if verbose:
                print("Failed to start a new game.")
                print(f"Response: {response}")
            return False
        return status == "success"

    def reward_function(self, correct_guess):
        """Assign a reward based on the result of the guess."""
        return 10 if correct_guess else -5

    def my_status(self):
        """Get game statistics."""
        try:
            response = self.request("/my_status", args=None, post_args=None, method="GET")
            if isinstance(response, list) and len(response) == 4:
                return response
            else:
                raise HangmanAPIError({"error_msg": "Unexpected response format for my_status."})
        except HangmanAPIError as e:
            print(f"HangmanAPIError: {e.message}")
            return [self.total_practice_runs, self.total_recorded_runs, self.total_recorded_successes, self.total_practice_successes]
        except Exception as e:
            print(f"Unexpected error while fetching status: {e}")
            return [self.total_practice_runs, self.total_recorded_runs, self.total_recorded_successes, self.total_practice_successes]

# Usage Example
if __name__ == "__main__":
    # Initialize HangmanAPI with your access token
    api = HangmanAPI(access_token="775976eaf5c7653d7f9cc3f826ad3e", timeout=10)
    
    # Start a single practice game with verbose logging to monitor progress
    print("Starting a single practice game with verbose output for debugging...")
    success = api.start_game(practice=True, verbose=True)
    
    # Retrieve and display status
    status = api.my_status()
    if isinstance(status, list) and len(status) == 4:
        total_practice_runs, total_recorded_runs, total_recorded_successes, total_practice_successes = status
    else:
        # Fallback to internal counters if the response is not as expected
        total_practice_runs = api.total_practice_runs
        total_recorded_runs = api.total_recorded_runs
        total_recorded_successes = api.total_recorded_successes
        total_practice_successes = api.total_practice_successes
    
    # Calculate success rates safely
    if total_recorded_runs > 0:
        recorded_success_rate = total_recorded_successes / total_recorded_runs
    else:
        recorded_success_rate = 0.0
    
    if total_practice_runs > 0:
        practice_success_rate = total_practice_successes / total_practice_runs
    else:
        practice_success_rate = 0.0
    
    print(f'Run {total_practice_runs} practice games out of an allotted 100,000.')
    print(f'Practice success rate: {practice_success_rate:.2%}')
    print(f'Overall success rate = {recorded_success_rate:.3f}')
    
    # To play multiple practice games, uncomment the following loop with verbose=False after initial testing
    """
    for i in range(1, 1001):
        print(f'Playing {i}th practice game')
        api.start_game(practice=True, verbose=False)
        time.sleep(3)  # Adjust sleep time according to rate limits
    """
    
    # To start recorded games (once finalized), uncomment and use cautiously
    """
    for i in range(1, 1001):
        print(f'Playing {i}th recorded game')
        api.start_game(practice=False, verbose=False)
        time.sleep(3)  # Adjust sleep time according to rate limits
    """
Determining the fastest Hangman API URL...
Link https://trexsim.com responded in 0.4472 seconds.
Link https://sg.trexsim.com responded in 1.7137 seconds.
Determined Hangman URL: https://trexsim.com/trexsim/hangman
Dictionary loaded from /kaggle/input/hangmen/words_250000_train.txt.
Loaded dictionary with 227300 words.
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[1], line 256
    254 games_to_play = 100  # Number of games to play
    255 for i in range(games_to_play):
--> 256     api.play(verbose=False)  # Set verbose=False for cleaner output
    257     if i % 10 == 0:
    258         api.print_accuracy()  # Print accuracy every 10 games

Cell In[1], line 199, in HangmanAPI.play(self, verbose)
    197     if verbose:
    198         print(f"New game started: {result}")
--> 199     word, tries_remains = result["word"], result["tries_remains"]
    200 except HangmanAPIError as e:
    201     if verbose:

KeyError: 'word'