I have this code for a hangman solver project.
import collections
import time
import requests
import json
from urllib.parse import parse_qs
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Embedding
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau
import re
from tensorflow.keras.layers import Dropout
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import Bidirectional

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 = "/kaggle/input/training-words-250000/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 = []
        
        # Initialize the LSTM model and related variables
        self.char_to_idx = None
        self.idx_to_char = None
        self.model = None
        self.max_word_length = None
        self.vocab_size = None

        # Prepare data and train the model
        self.prepare_model()
        
    @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 prepare_model(self):
        # Set maximum sequence length
        max_seq_length = 15 # Adjust based on resources
        self.X, self.y, self.char_to_idx, self.idx_to_char = self.prepare_data(self.full_dictionary, max_seq_length)
        self.vocab_size = len(self.char_to_idx)
        self.max_seq_length = max_seq_length
        self.model = self.build_model(self.max_seq_length, self.vocab_size)
        self.train_model(self.model, self.X, self.y)

    def build_dictionary(self, dictionary_file_location):
        with open(dictionary_file_location, "r") as text_file:
            full_dictionary = [line.strip().lower() for line in text_file if line.strip().isalpha()]
        return full_dictionary
    
    # Modified prepare_data function with data augmentation
    def prepare_data(self, dictionary, max_seq_length):
        sequences = []
        next_chars = []
        chars = set()
        for word in dictionary:
            word = '^' + word + '$'
        # Forward sequences
            for i in range(len(word) - 1):
                for seq_length in range(1, min(max_seq_length, len(word) - i)):
                    seq = word[i:i + seq_length]
                    next_char = word[i + seq_length]
                    sequences.append(seq)
                    next_chars.append(next_char)
                    chars.update(seq)
                    chars.add(next_char)
        # Reversed sequences
        reversed_word = word[::-1]
        for i in range(len(reversed_word) - 1):
            for seq_length in range(1, min(max_seq_length, len(reversed_word) - i)):
                seq = reversed_word[i:i + seq_length]
                next_char = reversed_word[i + seq_length]
                sequences.append(seq)
                next_chars.append(next_char)
                chars.update(seq)
                chars.add(next_char)

        # Create a mapping from characters to integers
        chars = sorted(list(chars))
        char_to_idx = {char: idx + 1 for idx, char in enumerate(chars)} # Start indices from 1
        idx_to_char = {idx: char for char, idx in char_to_idx.items()}
        char_to_idx[''] = 0
        idx_to_char[0] = ''

        # Convert sequences to integer format
        X = [[char_to_idx[char] for char in seq] for seq in sequences]
        y = [char_to_idx[char] for char in next_chars]

        # Pad sequences
        X = pad_sequences(X, maxlen=max_seq_length, padding='pre')
        y = to_categorical(y, num_classes=len(char_to_idx))

        return X, y, char_to_idx, idx_to_char

    # Updated build_model function
    def build_model(self, max_seq_length, vocab_size):
        model = Sequential()
        model.add(Embedding(input_dim=vocab_size, output_dim=128, input_length=max_seq_length))
        model.add(Bidirectional(LSTM(128, return_sequences=False)))
        model.add(Dropout(0.3)) 
        model.add(Dense(vocab_size, activation='softmax'))
        optimizer = Adam(learning_rate=0.0005, beta_1=0.9, beta_2=0.999)
        model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])
        return model


    # Train the model
    def train_model(self, model, X, y, epochs=20, batch_size=256):
        early_stopping = EarlyStopping(monitor='loss', patience=3)
        reduce_lr = ReduceLROnPlateau(monitor='loss', factor=0.5, patience=2)
        model.fit(X, y, epochs=epochs, batch_size=batch_size)


    
    # Updated predict_next_letters function
    def predict_next_letters(self, masked_word, guessed_letters):
        masked_word = '^' + masked_word.replace(' ', '').replace('', '^').lower() + '$'
        sequences = []
        for i in range(len(masked_word)):
            for seq_length in range(1, min(self.max_seq_length, len(masked_word) - i)):
                seq = masked_word[i:i + seq_length]
                if '^' in seq[1:-1] or '$' in seq[1:-1]:
                    continue  # Skip sequences with start/end tokens in the middle
                sequences.append(seq)

        probs = np.zeros(self.vocab_size)
        for seq in sequences:
            seq_indices = [self.char_to_idx.get(char, 0) for char in seq]
            seq_padded = pad_sequences([seq_indices], maxlen=self.max_seq_length, padding='pre')
            seq_probs = self.model.predict(seq_padded)[0]
            probs += seq_probs

        probs /= len(sequences)

        for char in guessed_letters:
            idx = self.char_to_idx.get(char, None)
            if idx is not None:
                probs[idx] = 0

        total = np.sum(probs)
        if total > 0:
            probs = probs / total

        return probs

    # Make a guess
    def make_guess(self, masked_word, guessed_letters):
        probs = self.predict_next_letters(masked_word.replace(' ', '').lower(), guessed_letters)
        # Select the letter with the highest probability
        idx = np.argmax(probs)
        guess = self.idx_to_char.get(idx, None)
        # If no valid guess is found, select a random unguessed letter
        if not guess or guess in guessed_letters:
            all_chars = set(self.char_to_idx.keys()) - {''}
            remaining_letters = all_chars - guessed_letters - set(masked_word.replace('', '').replace(' ', '').lower())
            if remaining_letters:
                guess = max(remaining_letters, key=lambda x: self.full_dictionary_common_letter_sorted.index((x, 0)) if (x, 0) in self.full_dictionary_common_letter_sorted else -1)
            else:
                # If no letters remain unguessed, return None
                return None
        return guess

    def guess(self, word): # word input example: "_ p p _ e "
        ###############################################
        # Replace with your own "guess" function here #
        ###############################################
        masked_word = word.replace(' ', '').lower()
        guessed_letters_set = set(self.guessed_letters)
        # Create a regex pattern from the masked word
        pattern = '^' + masked_word.replace('', '.') + '$'
        regex = re.compile(pattern)
        # Filter dictionary to get candidate words
        candidates = [w for w in self.full_dictionary if regex.match(w) and not set(w) & guessed_letters_set - set(masked_word.replace('', ''))]

        if candidates:
            # Build letter frequency from candidates
            letter_counts = collections.Counter(''.join(candidates))
            for letter in guessed_letters_set:
                if letter in letter_counts:
                    del letter_counts[letter]
            # If letter_counts is not empty, select the most common letter
            if letter_counts:
                guess_letter = letter_counts.most_common(1)[0][0]
                return guess_letter
        # Fallback to model prediction
        return self.make_guess(masked_word, guessed_letters_set)

    ##########################################################
    # 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)

I want to use return_sequences=True for the model. Based on this could you revise the code and give me the entire updated code