Spaces:
Paused
Paused
| # """ | |
| # This module defines a Gradio-based web application for the Semantrix game. The application allows users to play the game in either Spanish or English, using different embedding models for word similarity. | |
| # Modules: | |
| # gradio: Used for creating the web interface. | |
| # json: Used for loading configuration files. | |
| # game: Contains the Semantrix class for game logic. | |
| # File Paths: | |
| # config_file_path: Path to the configuration file. | |
| # logo_path: Path to the logo image. | |
| # logo_win_path: Path to the winning logo image. | |
| # Functions: | |
| # convert_to_markdown_centered(text): | |
| # Converts text to a centered markdown format for displaying game history and last attempt. | |
| # reset(difficulty, lang, model): | |
| # Resets the game state based on the selected difficulty, language, and model. | |
| # change(state, inp): | |
| # Changes the game state by incrementing the state variable. | |
| # update(state, radio, inp, hint): | |
| # Updates the game state and UI components based on the current state and user inputs. | |
| # Gradio Components: | |
| # demo: The main Gradio Blocks component that contains the entire UI layout. | |
| # header: A Markdown component for displaying the game header. | |
| # state: A State component for tracking the current game state. | |
| # difficulty: A State component for tracking the difficulty level. | |
| # hint: A State component for tracking if a hint is provided. | |
| # img: An Image component for displaying the game logo. | |
| # ranking: A Markdown component for displaying the ranking. | |
| # out: A Textbox component for displaying game messages. | |
| # hint_out: A Textbox component for displaying hints. | |
| # radio: A Radio component for user selections. | |
| # inp: A Textbox component for user input. | |
| # but: A Button component for several actions. | |
| # give_up: A Button component for giving up. | |
| # reload: A Button component for reloading the game. | |
| # model: A Dropdown component for selecting the embedding model. | |
| # lang: A Dropdown component for selecting the language. | |
| # Events: | |
| # inp.submit: Triggers the change function on input submission. | |
| # but.click: Triggers the change function on button click. | |
| # give_up.click: Triggers the change function on give up button click. | |
| # radio.input: Triggers the change function on radio input. | |
| # reload.click: Triggers the reset function on reload button click. | |
| # demo.load: Triggers the reset function on demo load. | |
| # lang[0].select: Triggers the reset function on language selection. | |
| # model[0].select: Triggers the reset function on model selection. | |
| # state.change: Triggers the update function on state change. | |
| # Main: | |
| # Launches the Gradio application if the script is run as the main module. | |
| # """ | |
| import gradio as gr | |
| import json | |
| from datetime import datetime, date | |
| from game import Semantrix, Model_class | |
| from huggingface_hub import CommitScheduler | |
| import os | |
| import logging | |
| # Configure logging | |
| logging.basicConfig( | |
| level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # File paths for configuration and images | |
| base_path = os.path.dirname(os.path.abspath(__file__)) | |
| config_file_path = os.path.join(base_path, "config/lang.json") | |
| logo_path = os.path.join(base_path, "config/images/logo.png") | |
| logo_win_path = os.path.join(base_path, "config/images/logo_win.gif") | |
| condition_config_path = os.path.join(base_path, "config/condition.json") | |
| data_path = os.path.join(base_path, "data") | |
| plays_path = os.path.join(data_path, "plays") | |
| condition_name = "condition_2" | |
| # Dynamically determine the condition name based on the folder name | |
| # folder_name = os.path.basename(os.path.dirname(os.path.abspath(__file__))) | |
| # condition = folder_name.split("_")[-1][-1] | |
| # condition_name = "condition_" + condition | |
| with open(condition_config_path, "r") as file: | |
| condition_config = json.load(file) | |
| model = condition_config[condition_name]["model"] | |
| hints_enabled = condition_config[condition_name]["hints"] | |
| # Loading the configuration file | |
| with open(config_file_path, "r") as file: | |
| Config_full = json.load(file) | |
| scheduler = CommitScheduler( | |
| repo_id="Jsevisal/semantrix_data_" + condition_name[-1], | |
| repo_type="dataset", | |
| folder_path=plays_path, | |
| path_in_repo="data", | |
| every=10, | |
| ) | |
| lang = 0 # Language configuration flag (0 for Spanish, 1 for English) | |
| # Setting the configuration based on the language flag | |
| if lang == 1: | |
| Config = Config_full["ENG"]["Game"] | |
| Menu = Config_full["ENG"]["Menu"] | |
| else: | |
| Config = Config_full["SPA"]["Game"] | |
| Menu = Config_full["SPA"]["Menu"] | |
| # Function to convert text to centered markdown format | |
| def convert_to_markdown_centered(text): | |
| lines = text.strip().split("\n") | |
| if not lines: | |
| return "" | |
| last_attempt = lines[0] | |
| history_attempts = lines[2:12] | |
| markdown = '<div align="center">\n\n' | |
| markdown += "## " + Menu["Best_tries"] + "\n" | |
| markdown += "<table>\n" | |
| markdown += " <tr>\n" | |
| markdown += " <th>" + Menu["N_try"] + "</th>\n" | |
| markdown += " <th>" + Menu["Word"] + "</th>\n" | |
| markdown += " <th>" + Menu["Score"] + "</th>\n" | |
| markdown += " </tr>\n" | |
| for line in history_attempts: | |
| items = eval(line.strip()) | |
| markdown += " <tr>\n" | |
| markdown += f" <td><strong>{items[0]}</strong></td>\n" | |
| markdown += f" <td>{items[1]}</td>\n" | |
| markdown += f" <td>{items[2]}</td>\n" | |
| markdown += " </tr>\n" | |
| markdown += "</table>\n\n" | |
| last_items = eval(last_attempt) | |
| markdown += f"## " + Menu["Last_try"] + "\n" | |
| markdown += ( | |
| f"**{last_items[0]}:** {last_items[1]} - " | |
| + Menu["Score"] | |
| + f": {last_items[2]}\n\n" | |
| ) | |
| markdown += "---\n\n" | |
| markdown += "</div>" | |
| return markdown | |
| # | |
| with gr.Blocks( | |
| theme="ocean", | |
| ) as demo: | |
| # Initializing state variables to manage the internal state of the application | |
| state = gr.State(-1) # State variable to track the current game state | |
| difficulty = gr.State(-1) # State variable to track the difficulty level | |
| hint = gr.State(False) # State variable to track if the hint is provided | |
| secret_word_used = gr.BrowserState( | |
| 0 | |
| ) # State variable to track the number of secret words used | |
| # Initializing the game using the Semantrix class with default parameters | |
| game_instances = {} | |
| sessions_to_remove = {} | |
| cooldown_time = 120 # Time in seconds to remove the session from the game_instances | |
| model_class = Model_class(lang=0, model_type=model) | |
| # Creating a Markdown component to display the header | |
| header = gr.Markdown( | |
| """ | |
| <p style="text-align:center"> """ | |
| + Menu["Header"] | |
| + """ </p> | |
| """ | |
| ) | |
| # Function to reset the game | |
| def reset(reload, request: gr.Request): | |
| global Config, game, Menu, model, lang # Declare global variables to modify them within the function | |
| if reload: | |
| game_instances[request.session_hash] = Semantrix( | |
| lang=0, model_type=model, session_hash=request.session_hash | |
| ) | |
| game_instances[request.session_hash].reset_game() # Reset the game state | |
| logger.info("New session detected: %s", request.session_hash) | |
| logger.info("Game instances: %s", game_instances) | |
| # Define the initial output components for the UI | |
| output = [ | |
| -1, | |
| gr.Textbox(visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Image(logo_path, visible=True, interactive=False), | |
| gr.Button(Menu["Start"], visible=True, variant="secondary"), | |
| gr.Radio(visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Button(visible=False), | |
| gr.Markdown( | |
| """ | |
| <p style="text-align:center"> """ | |
| + Menu["Header"] | |
| + """ </p> | |
| """ | |
| ), | |
| gr.Button(visible=False), | |
| ] | |
| # Return the initial output components | |
| return output | |
| def remove_game_instance(timer_tick=False, request: gr.Request | None = None): | |
| request = None if timer_tick else request | |
| if request is not None: | |
| logger.info("Session on inactivity timer: %s", request.session_hash) | |
| sessions_to_remove[request.session_hash] = datetime.now() | |
| if len(sessions_to_remove.items()) > 0: | |
| for session_hash, timestamp in list(sessions_to_remove.items()): | |
| if (datetime.now() - timestamp).seconds > cooldown_time: | |
| del sessions_to_remove[session_hash] | |
| session_id = game_instances[session_hash].get_session_id() | |
| del game_instances[session_hash] | |
| # Delete ranking file if it exists | |
| ranking_file = os.path.join( | |
| data_path, f"rankings/ranking_{session_hash}.txt" | |
| ) | |
| if os.path.exists(ranking_file): | |
| os.remove(ranking_file) | |
| # Delete plays files if it exists | |
| session_files = [ | |
| f for f in os.listdir(plays_path) if f.startswith(session_id) | |
| ] | |
| for file_name in session_files: | |
| with open(os.path.join(plays_path, file_name), "r") as file: | |
| os.remove(os.path.join(plays_path, file_name)) | |
| logger.info("Deleted session: %s", session_hash) | |
| # Function to change the state of the game | |
| def change(state, inp): | |
| # Increment the state by 1 | |
| state = state + 1 | |
| # Return the updated state and input component | |
| return [state, inp] | |
| # Function to update the game state based on the current state of the game | |
| def update(state, radio, inp, hint, secret_word_used, request: gr.Request): | |
| global difficulty, hints_enabled | |
| # Define the difficulty state | |
| dif_state = 3 | |
| # Initialize the output component list with the current state | |
| state_int = state | |
| output = [state] | |
| if request.session_hash in sessions_to_remove: | |
| sessions_to_remove.pop(request.session_hash) | |
| logger.info("Session saved: %s", request.session_hash) | |
| # Define UI components for the initial state | |
| if state_int == -1: | |
| output.extend( | |
| [ | |
| gr.Button(Menu["Start"], visible=True), | |
| gr.Radio(label="", visible=False), | |
| gr.Textbox( | |
| Config[list(Config.keys())[state_int]], visible=False, label="" | |
| ), | |
| gr.Button(Menu["Give_up"], visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Image(interactive=False, visible=True), | |
| gr.Textbox(visible=False), | |
| gr.Button(visible=False), | |
| gr.Markdown(visible=False), | |
| gr.Button(visible=False), | |
| secret_word_used, | |
| ] | |
| ) | |
| # Define UI components for the first state, ask the user if they want to know the rules | |
| elif state_int == 1: | |
| output.extend( | |
| [ | |
| gr.Button(visible=False), | |
| gr.Radio( | |
| [Menu["Yes"], Menu["No"]], value=None, label="", visible=True | |
| ), | |
| gr.Textbox( | |
| Config[list(Config.keys())[state_int]], visible=True, label="" | |
| ), | |
| gr.Button(Menu["Give_up"], visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Image(interactive=False, visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Button(visible=False), | |
| gr.Markdown(visible=False), | |
| gr.Button(visible=False), | |
| secret_word_used, | |
| ] | |
| ) | |
| # Define UI components for the second state, Depending on the answer, show the rules or keep going | |
| elif state_int == 2: | |
| if radio == Menu["No"]: | |
| output = [ | |
| dif_state, | |
| gr.Button("Introducir", visible=True), | |
| gr.Radio(visible=False), | |
| gr.Textbox( | |
| Config[list(Config.keys())[state_int]], visible=True, label="" | |
| ), | |
| gr.Button(Menu["Give_up"], visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Image(interactive=False, visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Button(visible=False), | |
| gr.Markdown(visible=False), | |
| gr.Button(visible=False), | |
| secret_word_used, | |
| ] | |
| else: | |
| output.extend( | |
| [ | |
| gr.Button(Menu["Next"], visible=True), | |
| gr.Radio(visible=False), | |
| gr.Textbox( | |
| Config[list(Config.keys())[state_int]], | |
| visible=True, | |
| label="", | |
| ), | |
| gr.Button(Menu["Give_up"], visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Image(interactive=False, visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Button(visible=False), | |
| gr.Markdown(visible=False), | |
| gr.Button(visible=False), | |
| secret_word_used, | |
| ] | |
| ) | |
| # Define UI components for the difficulty state, ask the user to select the difficulty level | |
| elif state_int == dif_state: | |
| output.extend( | |
| [ | |
| gr.Button(Menu["Start"], visible=True, variant="primary"), | |
| gr.Radio(visible=False), | |
| gr.Textbox( | |
| Config[list(Config.keys())[state_int]], visible=True, label="" | |
| ), | |
| gr.Button(Menu["Give_up"], visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Image(interactive=False, visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Button(visible=False), | |
| gr.Markdown(visible=False), | |
| gr.Button(visible=False), | |
| secret_word_used, | |
| ] | |
| ) | |
| # Define UI components for the difficulty state + 2, play the game based on the selected difficulty level and prepare the game for the word guessing | |
| elif state_int == dif_state + 1: | |
| game_instances[request.session_hash].prepare_game( | |
| secret_word_used, 2 if hints_enabled else 4 | |
| ) | |
| output.extend( | |
| [ | |
| gr.Button(Menu["Send"], visible=True, variant="primary"), | |
| gr.Radio(label="", visible=False), | |
| gr.Textbox(visible=False, label=""), | |
| gr.Button(visible=False, variant="stop"), | |
| gr.Textbox( | |
| value="", | |
| visible=True, | |
| autofocus=True, | |
| placeholder=Menu["New_word"], | |
| ), | |
| gr.Image(interactive=False, visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Button(visible=False), | |
| gr.Markdown(visible=False), | |
| gr.Button(visible=False), | |
| secret_word_used + 1, | |
| ] | |
| ) | |
| # Define UI components for the state greater than the difficulty state + 2, play the game and provide feedback based on the user input | |
| elif state_int > dif_state + 1: | |
| # Send the user input to the game and get the feedback from the game | |
| feed = game_instances[request.session_hash].play_game(inp, model_class) | |
| # Check if the feedback contains the ranking information and process it | |
| feedback_trim = feed.split("[rank]") | |
| if len(feedback_trim) > 1: | |
| ranking_vis = True | |
| ranking_md = convert_to_markdown_centered(feedback_trim[1]) | |
| else: | |
| ranking_vis = False | |
| ranking_md = "" | |
| # Check if the feedback contains a hint, win, or lose message | |
| feedback = feedback_trim[0].split("[hint]") | |
| win = feedback_trim[0].split("[win]") | |
| lose = feedback_trim[0].split("[lose]") | |
| # Check if the feedback contains a hint message | |
| if len(feedback) > 1: | |
| hint = True | |
| hint_out = feedback[1] | |
| feedback = feedback[0] | |
| else: | |
| hint = False | |
| feedback = feedback[0] | |
| # Check if the feedback contains a win or lose message and process it | |
| if len(win) > 1 or len(lose) > 1: | |
| # Check if the user won the game | |
| won = True if len(win) > 1 else False | |
| # Get the curiosity message from the game | |
| curiosity = game_instances[request.session_hash].curiosity() | |
| # Define the output components for the win or lose state | |
| output.extend( | |
| [ | |
| gr.Button(Menu["Send"], visible=False, variant="primary"), | |
| gr.Radio(label="", visible=False), | |
| gr.Textbox(win[1] if won else lose[1], visible=True, label=""), | |
| gr.Button(visible=False, variant="stop"), | |
| gr.Textbox( | |
| value="", visible=False, placeholder=Menu["New_word"] | |
| ), | |
| gr.Image( | |
| logo_win_path if won else logo_path, | |
| interactive=False, | |
| visible=True, | |
| ), | |
| gr.Textbox(curiosity, visible=True, label=Menu["Curiosity"]), | |
| gr.Button(Menu["Play_again"], variant="primary", visible=True), | |
| gr.Markdown(visible=False), | |
| gr.Button(visible=True), | |
| secret_word_used, | |
| ] | |
| ) | |
| return output | |
| # Define the output components for the feedback and keep playing | |
| output.extend( | |
| [ | |
| gr.Button(Menu["Send"], visible=True, variant="primary"), | |
| gr.Radio(label="", visible=False), | |
| gr.Textbox(feedback, visible=True, label=""), | |
| gr.Button(visible=True, variant="stop"), | |
| gr.Textbox(value="", visible=True, placeholder=Menu["New_word"]), | |
| gr.Image(logo_path, interactive=False, visible=False), | |
| gr.Textbox(hint_out if hint else "", visible=hint, label="Pista"), | |
| gr.Button(visible=False), | |
| gr.Markdown(ranking_md, visible=ranking_vis), | |
| gr.Button(visible=False), | |
| secret_word_used, | |
| ] | |
| ) | |
| # Define UI components for the rest of the states, state for showing basic text to the user | |
| else: | |
| output.extend( | |
| [ | |
| gr.Button(Menu["Next"], visible=True), | |
| gr.Radio(label="", visible=False), | |
| gr.Textbox( | |
| Config[list(Config.keys())[state_int]], visible=True, label="" | |
| ), | |
| gr.Button("Pista", visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Image(interactive=False, visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Button(visible=False), | |
| gr.Markdown(visible=False), | |
| gr.Button(visible=False), | |
| secret_word_used, | |
| ] | |
| ) | |
| # Return the output components | |
| return output | |
| def commit_data(request: gr.Request): | |
| session_id = game_instances[request.session_hash].get_session_id() | |
| session_files = [f for f in os.listdir(plays_path) if f.startswith(session_id)] | |
| combined_data = [] | |
| for file_name in session_files: | |
| with open(os.path.join(plays_path, file_name), "r") as file: | |
| combined_data.append(json.load(file)) | |
| os.remove(os.path.join(plays_path, file_name)) | |
| combined_file_path = os.path.join(plays_path, f"{session_id}.json") | |
| with open(combined_file_path, "w") as combined_file: | |
| json.dump(combined_data, combined_file, indent=4) | |
| scheduler.push_to_hub() | |
| if os.path.exists(combined_file_path): | |
| os.remove(combined_file_path) | |
| return [ | |
| gr.Button(visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Textbox(visible=False), | |
| gr.Button( | |
| "Rellenar cuestionario", | |
| variant="primary", | |
| link="https://docs.google.com/forms/d/e/1FAIpQLSdGkCMsjk4r5Zvm1hZuebbPRK5LVc_aivbb8e9WrblX_yTHvg/viewform?usp=pp_url&entry.1459162177=" | |
| + session_id, | |
| visible=True, | |
| ), | |
| ] | |
| # Define the UI layout for the gam | |
| img = gr.Image(logo_path, height=430, interactive=False, visible=True) | |
| ranking = gr.Markdown(visible=False) | |
| with gr.Row(): | |
| out = gr.Textbox(visible=False, placeholder=Config[list(Config.keys())[0]]) | |
| hint_out = gr.Textbox(visible=False) | |
| radio = gr.Radio(visible=False) | |
| with gr.Row(): | |
| inp = gr.Textbox(visible=False, interactive=True, label="") | |
| but = gr.Button(Menu["Start"]) | |
| give_up = gr.Button("Pista", visible=False) | |
| reload = gr.Button(Menu["Play_again"], visible=False) | |
| finish = gr.Button( | |
| "Terminar", | |
| variant="stop", | |
| # link="https://docs.google.com/forms/d/e/1FAIpQLSd0z8nI4hhOSR83yPIw_bR3KkSt25Lsq0ZXG1pZnkldeoceqA/viewform?usp=pp_url&entry.327829192=Condici%C3%B3n+1", | |
| visible=False, | |
| ) | |
| timer_delete = gr.Timer(value=30) | |
| # Define the UI events for the game | |
| # Define events that trigger the game state change | |
| timer_delete.tick(remove_game_instance, inputs=[gr.State(True)]) | |
| inp.submit( | |
| change, | |
| inputs=[state, inp], | |
| outputs=[state, inp], | |
| concurrency_limit=5, | |
| ) | |
| but.click( | |
| change, | |
| inputs=[state, inp], | |
| outputs=[state, inp], | |
| concurrency_limit=5, | |
| ) | |
| give_up.click( | |
| change, | |
| inputs=[ | |
| state, | |
| gr.Textbox("give_up", visible=False, interactive=True, label=""), | |
| ], | |
| outputs=[state, inp], | |
| concurrency_limit=5, | |
| ) | |
| radio.input( | |
| change, | |
| inputs=[state, inp], | |
| outputs=[state, inp], | |
| concurrency_limit=5, | |
| ) | |
| finish.click( | |
| commit_data, outputs=[reload, out, hint_out, finish] | |
| ) # Define events that trigger the game reset | |
| reload.click( | |
| reset, | |
| inputs=[gr.State(False)], | |
| outputs=[state, out, inp, img, but, radio, hint_out, reload, header, finish], | |
| ) | |
| demo.load( | |
| reset, | |
| inputs=[gr.State(True)], | |
| outputs=[state, out, inp, img, but, radio, hint_out, reload, header, finish], | |
| ) | |
| demo.unload(remove_game_instance) | |
| # Define events that trigger the game state update | |
| state.change( | |
| update, | |
| inputs=[state, radio, inp, hint, secret_word_used], | |
| outputs=[ | |
| state, | |
| but, | |
| radio, | |
| out, | |
| give_up, | |
| inp, | |
| img, | |
| hint_out, | |
| reload, | |
| ranking, | |
| finish, | |
| secret_word_used, | |
| ], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(debug=True) | |