Spaces:
Running
Running
Javierss
commited on
Commit
·
0659ca5
1
Parent(s):
1fd4e85
Clone old repo
Browse files- .gitattributes +11 -0
- .gitignore +6 -0
- README.md +107 -5
- app.py +641 -0
- config/condition.json +19 -0
- config/images/logo.png +0 -0
- config/images/logo_win.gif +3 -0
- config/lang.json +153 -0
- config/secret.json +1035 -0
- data/plays/__init__.py +0 -0
- data/rankings/__init__.py +0 -0
- game.py +417 -0
- gen_model.py +13 -0
- hints.py +286 -0
- requirements.txt +8 -0
- tracking.py +23 -0
.gitattributes
CHANGED
@@ -33,3 +33,14 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
# *.gif filter=lfs diff=lfs merge=lfs -text
|
37 |
+
config/w2v_models/esp_w2v_model filter=lfs diff=lfs merge=lfs -text
|
38 |
+
config/w2v_models/eng_w2v_model filter=lfs diff=lfs merge=lfs -text
|
39 |
+
config/words.txt filter=lfs diff=lfs merge=lfs -text
|
40 |
+
config/words.gz filter=lfs diff=lfs merge=lfs -text
|
41 |
+
config/w2v_models/words.gz filter=lfs diff=lfs merge=lfs -text
|
42 |
+
config/possible_words.json filter=lfs diff=lfs merge=lfs -text
|
43 |
+
config/strans_models/. filter=lfs diff=lfs merge=lfs -text
|
44 |
+
config/strans_models/esp_strans_model filter=lfs diff=lfs merge=lfs -text
|
45 |
+
config/strans_models/esp_strans_model.vectors.npy filter=lfs diff=lfs merge=lfs -text
|
46 |
+
config/images/logo_win.gif filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
data/plays/*.txt
|
2 |
+
data/plays/*.json
|
3 |
+
data/plays/data/*.json
|
4 |
+
__pycache__/*
|
5 |
+
data/rankings/*.txt
|
6 |
+
config/*_models
|
README.md
CHANGED
@@ -1,12 +1,114 @@
|
|
1 |
---
|
2 |
-
title: Semantrix
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: gradio
|
7 |
sdk_version: 5.22.0
|
|
|
8 |
app_file: app.py
|
9 |
pinned: false
|
|
|
10 |
---
|
11 |
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
title: Semantrix
|
3 |
+
emoji: 🔠
|
4 |
+
colorFrom: indigo
|
5 |
+
colorTo: pink
|
6 |
sdk: gradio
|
7 |
sdk_version: 5.22.0
|
8 |
+
python_version: 3.11.5
|
9 |
app_file: app.py
|
10 |
pinned: false
|
11 |
+
license: other
|
12 |
---
|
13 |
|
14 |
+
# Semantrix Game
|
15 |
+
|
16 |
+
This repository contains the implementation of the Semantrix game, a word guessing game using word embeddings. The game supports multiple languages (Spanish and English) and can be configured to use either a Word2Vec model or a SentenceTransformer model for word embeddings.
|
17 |
+
|
18 |
+
## Modules
|
19 |
+
|
20 |
+
|
21 |
+
|
22 |
+
### app.py
|
23 |
+
|
24 |
+
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.
|
25 |
+
|
26 |
+
#### Functions
|
27 |
+
|
28 |
+
- `convert_to_markdown_centered(text)`: Converts text to a centered markdown format for displaying game history and last attempt.
|
29 |
+
- **Parameters**:
|
30 |
+
- `text (str)`: The text to be converted.
|
31 |
+
- **Returns**: `str`: The centered markdown formatted text.
|
32 |
+
|
33 |
+
- `reset(difficulty, lang, model)`: Resets the game state based on the selected difficulty, language, and model.
|
34 |
+
- **Parameters**:
|
35 |
+
- `difficulty`: The selected difficulty level.
|
36 |
+
- `lang`: The selected language.
|
37 |
+
- `model`: The selected embedding model.
|
38 |
+
- **Returns**: `list`: A list of initial output components for the UI.
|
39 |
+
|
40 |
+
- `change(state, inp)`: Changes the game state by incrementing the state variable.
|
41 |
+
- **Parameters**:
|
42 |
+
- `state`: The current game state.
|
43 |
+
- `inp`: The user input.
|
44 |
+
- **Returns**: `list`: A list containing the updated state and input component.
|
45 |
+
|
46 |
+
- `update(state, radio, inp, hint)`: Updates the game state and UI components based on the current state and user inputs.
|
47 |
+
- **Parameters**:
|
48 |
+
- `state`: The current game state.
|
49 |
+
- `radio`: The radio input component.
|
50 |
+
- `inp`: The user input.
|
51 |
+
- `hint`: The hint state.
|
52 |
+
- **Returns**: `list`: A list of updated output components for the UI.
|
53 |
+
|
54 |
+
### game.py
|
55 |
+
|
56 |
+
This module defines the Semantrix class, which implements a word guessing game using word embeddings. The game can be configured to use either a Word2Vec model or a SentenceTransformer model for word embeddings. The game supports multiple languages and difficulty levels.
|
57 |
+
|
58 |
+
#### Classes
|
59 |
+
|
60 |
+
- `Semantrix`: A class that implements the Semantrix word guessing game.
|
61 |
+
- **Methods**:
|
62 |
+
- `__init__(self, lang=0, model_type="SentenceTransformer")`: Initializes the Semantrix game with the specified language and model type.
|
63 |
+
- `prepare_game(self, difficulty)`: Prepares the game with the selected difficulty level.
|
64 |
+
- `gen_rank(self, repeated)`: Generates the ranking file based on the scores.
|
65 |
+
- `play_game(self, word)`: Plays the game with the selected word and returns feedback.
|
66 |
+
- `curiosity(self)`: Generates a curiosity hint about the secret word once the game is over.
|
67 |
+
|
68 |
+
### hints.py
|
69 |
+
|
70 |
+
This module provides functions to generate dynamic hints and curiosities about a secret word using language models (LLMs).
|
71 |
+
|
72 |
+
#### Functions
|
73 |
+
|
74 |
+
- `hint(secret, n, model, last_hint, lang, Config)`: Generates a dynamic hint based on the secret word and the number of hints given.
|
75 |
+
- **Parameters**:
|
76 |
+
- `secret (str)`: The secret word.
|
77 |
+
- `n (int)`: The number of hints already given.
|
78 |
+
- `model`: The sentence transformer model used for encoding.
|
79 |
+
- `last_hint (int)`: The index of the last hint given.
|
80 |
+
- `lang (int)`: The language code (0 for Spanish, 1 for English).
|
81 |
+
- `Config`: Configuration object containing hint templates.
|
82 |
+
- **Returns**: `tuple`: A tuple containing the generated hint (str), the updated number of hints (int), and the index of the last hint given (int).
|
83 |
+
|
84 |
+
- `curiosity(secret, Config)`: Generates a curiosity about the secret word.
|
85 |
+
- **Parameters**:
|
86 |
+
- `secret (str)`: The secret word.
|
87 |
+
- `Config`: Configuration object containing the curiosity template.
|
88 |
+
- **Returns**: `str`: The generated curiosity.
|
89 |
+
|
90 |
+
- `ireplace(old, new, text)`: Replaces all occurrences of a substring in a string, case-insensitively.
|
91 |
+
- **Parameters**:
|
92 |
+
- `old (str)`: The substring to be replaced.
|
93 |
+
- `new (str)`: The substring to replace with.
|
94 |
+
- `text (str)`: The original string.
|
95 |
+
- **Returns**: `str`: The modified string with all occurrences of the old substring replaced by the new substring.
|
96 |
+
|
97 |
+
|
98 |
+
## How to Run
|
99 |
+
|
100 |
+
1. Clone the repository.
|
101 |
+
2. Install the required dependencies.
|
102 |
+
3. Run the `app.py` script to launch the Gradio web application.
|
103 |
+
|
104 |
+
## Dependencies
|
105 |
+
|
106 |
+
- Gradio
|
107 |
+
- OpenAI
|
108 |
+
- SentenceTransformers
|
109 |
+
- Gensim
|
110 |
+
- NumPy
|
111 |
+
|
112 |
+
## License
|
113 |
+
|
114 |
+
This project is licensed under the MIT License.
|
app.py
ADDED
@@ -0,0 +1,641 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# """
|
2 |
+
# 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.
|
3 |
+
|
4 |
+
# Modules:
|
5 |
+
# gradio: Used for creating the web interface.
|
6 |
+
# json: Used for loading configuration files.
|
7 |
+
# game: Contains the Semantrix class for game logic.
|
8 |
+
|
9 |
+
# File Paths:
|
10 |
+
# config_file_path: Path to the configuration file.
|
11 |
+
# logo_path: Path to the logo image.
|
12 |
+
# logo_win_path: Path to the winning logo image.
|
13 |
+
|
14 |
+
# Functions:
|
15 |
+
# convert_to_markdown_centered(text):
|
16 |
+
# Converts text to a centered markdown format for displaying game history and last attempt.
|
17 |
+
|
18 |
+
# reset(difficulty, lang, model):
|
19 |
+
# Resets the game state based on the selected difficulty, language, and model.
|
20 |
+
|
21 |
+
# change(state, inp):
|
22 |
+
# Changes the game state by incrementing the state variable.
|
23 |
+
|
24 |
+
# update(state, radio, inp, hint):
|
25 |
+
# Updates the game state and UI components based on the current state and user inputs.
|
26 |
+
|
27 |
+
# Gradio Components:
|
28 |
+
# demo: The main Gradio Blocks component that contains the entire UI layout.
|
29 |
+
# header: A Markdown component for displaying the game header.
|
30 |
+
# state: A State component for tracking the current game state.
|
31 |
+
# difficulty: A State component for tracking the difficulty level.
|
32 |
+
# hint: A State component for tracking if a hint is provided.
|
33 |
+
# img: An Image component for displaying the game logo.
|
34 |
+
# ranking: A Markdown component for displaying the ranking.
|
35 |
+
# out: A Textbox component for displaying game messages.
|
36 |
+
# hint_out: A Textbox component for displaying hints.
|
37 |
+
# radio: A Radio component for user selections.
|
38 |
+
# inp: A Textbox component for user input.
|
39 |
+
# but: A Button component for several actions.
|
40 |
+
# give_up: A Button component for giving up.
|
41 |
+
# reload: A Button component for reloading the game.
|
42 |
+
# model: A Dropdown component for selecting the embedding model.
|
43 |
+
# lang: A Dropdown component for selecting the language.
|
44 |
+
|
45 |
+
# Events:
|
46 |
+
# inp.submit: Triggers the change function on input submission.
|
47 |
+
# but.click: Triggers the change function on button click.
|
48 |
+
# give_up.click: Triggers the change function on give up button click.
|
49 |
+
# radio.input: Triggers the change function on radio input.
|
50 |
+
# reload.click: Triggers the reset function on reload button click.
|
51 |
+
# demo.load: Triggers the reset function on demo load.
|
52 |
+
# lang[0].select: Triggers the reset function on language selection.
|
53 |
+
# model[0].select: Triggers the reset function on model selection.
|
54 |
+
# state.change: Triggers the update function on state change.
|
55 |
+
|
56 |
+
# Main:
|
57 |
+
# Launches the Gradio application if the script is run as the main module.
|
58 |
+
# """
|
59 |
+
|
60 |
+
import gradio as gr
|
61 |
+
import json
|
62 |
+
from datetime import datetime, date
|
63 |
+
from game import Semantrix, Model_class
|
64 |
+
from huggingface_hub import CommitScheduler
|
65 |
+
import os
|
66 |
+
import logging
|
67 |
+
|
68 |
+
# Configure logging
|
69 |
+
logging.basicConfig(
|
70 |
+
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
71 |
+
)
|
72 |
+
logger = logging.getLogger(__name__)
|
73 |
+
|
74 |
+
|
75 |
+
# File paths for configuration and images
|
76 |
+
base_path = os.path.dirname(os.path.abspath(__file__))
|
77 |
+
config_file_path = os.path.join(base_path, "config/lang.json")
|
78 |
+
logo_path = os.path.join(base_path, "config/images/logo.png")
|
79 |
+
logo_win_path = os.path.join(base_path, "config/images/logo_win.gif")
|
80 |
+
condition_config_path = os.path.join(base_path, "config/condition.json")
|
81 |
+
data_path = os.path.join(base_path, "data")
|
82 |
+
plays_path = os.path.join(data_path, "plays")
|
83 |
+
|
84 |
+
condition_name = "condition_3"
|
85 |
+
# Dynamically determine the condition name based on the folder name
|
86 |
+
# folder_name = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
|
87 |
+
# condition = folder_name.split("_")[-1][-1]
|
88 |
+
# condition_name = "condition_" + condition
|
89 |
+
|
90 |
+
|
91 |
+
with open(condition_config_path, "r") as file:
|
92 |
+
condition_config = json.load(file)
|
93 |
+
|
94 |
+
model = condition_config[condition_name]["model"]
|
95 |
+
hints_enabled = condition_config[condition_name]["hints"]
|
96 |
+
|
97 |
+
# Loading the configuration file
|
98 |
+
with open(config_file_path, "r") as file:
|
99 |
+
Config_full = json.load(file)
|
100 |
+
|
101 |
+
scheduler = CommitScheduler(
|
102 |
+
repo_id="Jsevisal/semantrix_data_" + condition_name[-1],
|
103 |
+
repo_type="dataset",
|
104 |
+
folder_path=plays_path,
|
105 |
+
path_in_repo="data",
|
106 |
+
every=10,
|
107 |
+
)
|
108 |
+
|
109 |
+
lang = 0 # Language configuration flag (0 for Spanish, 1 for English)
|
110 |
+
|
111 |
+
# Setting the configuration based on the language flag
|
112 |
+
if lang == 1:
|
113 |
+
Config = Config_full["ENG"]["Game"]
|
114 |
+
Menu = Config_full["ENG"]["Menu"]
|
115 |
+
else:
|
116 |
+
Config = Config_full["SPA"]["Game"]
|
117 |
+
Menu = Config_full["SPA"]["Menu"]
|
118 |
+
|
119 |
+
|
120 |
+
# Function to convert text to centered markdown format
|
121 |
+
def convert_to_markdown_centered(text):
|
122 |
+
lines = text.strip().split("\n")
|
123 |
+
|
124 |
+
if not lines:
|
125 |
+
return ""
|
126 |
+
|
127 |
+
last_attempt = lines[0]
|
128 |
+
history_attempts = lines[2:12]
|
129 |
+
markdown = '<div align="center">\n\n'
|
130 |
+
|
131 |
+
markdown += "## " + Menu["Best_tries"] + "\n"
|
132 |
+
markdown += "<table>\n"
|
133 |
+
markdown += " <tr>\n"
|
134 |
+
markdown += " <th>" + Menu["N_try"] + "</th>\n"
|
135 |
+
markdown += " <th>" + Menu["Word"] + "</th>\n"
|
136 |
+
markdown += " <th>" + Menu["Score"] + "</th>\n"
|
137 |
+
markdown += " </tr>\n"
|
138 |
+
|
139 |
+
for line in history_attempts:
|
140 |
+
items = eval(line.strip())
|
141 |
+
markdown += " <tr>\n"
|
142 |
+
markdown += f" <td><strong>{items[0]}</strong></td>\n"
|
143 |
+
markdown += f" <td>{items[1]}</td>\n"
|
144 |
+
markdown += f" <td>{items[2]}</td>\n"
|
145 |
+
markdown += " </tr>\n"
|
146 |
+
|
147 |
+
markdown += "</table>\n\n"
|
148 |
+
|
149 |
+
last_items = eval(last_attempt)
|
150 |
+
markdown += f"## " + Menu["Last_try"] + "\n"
|
151 |
+
markdown += (
|
152 |
+
f"**{last_items[0]}:** {last_items[1]} - "
|
153 |
+
+ Menu["Score"]
|
154 |
+
+ f": {last_items[2]}\n\n"
|
155 |
+
)
|
156 |
+
markdown += "---\n\n"
|
157 |
+
|
158 |
+
markdown += "</div>"
|
159 |
+
|
160 |
+
return markdown
|
161 |
+
|
162 |
+
|
163 |
+
#
|
164 |
+
with gr.Blocks(
|
165 |
+
theme="ocean",
|
166 |
+
) as demo:
|
167 |
+
# Initializing state variables to manage the internal state of the application
|
168 |
+
state = gr.State(-1) # State variable to track the current game state
|
169 |
+
difficulty = gr.State(-1) # State variable to track the difficulty level
|
170 |
+
hint = gr.State(False) # State variable to track if the hint is provided
|
171 |
+
|
172 |
+
secret_word_used = gr.BrowserState(
|
173 |
+
0
|
174 |
+
) # State variable to track the number of secret words used
|
175 |
+
|
176 |
+
# Initializing the game using the Semantrix class with default parameters
|
177 |
+
game_instances = {}
|
178 |
+
sessions_to_remove = {}
|
179 |
+
cooldown_time = 120 # Time in seconds to remove the session from the game_instances
|
180 |
+
model_class = Model_class(lang=0, model_type=model)
|
181 |
+
|
182 |
+
# Creating a Markdown component to display the header
|
183 |
+
header = gr.Markdown(
|
184 |
+
"""
|
185 |
+
<p style="text-align:center"> """
|
186 |
+
+ Menu["Header"]
|
187 |
+
+ """ </p>
|
188 |
+
"""
|
189 |
+
)
|
190 |
+
|
191 |
+
# Function to reset the game
|
192 |
+
def reset(reload, request: gr.Request):
|
193 |
+
global Config, game, Menu, model, lang # Declare global variables to modify them within the function
|
194 |
+
|
195 |
+
if reload:
|
196 |
+
game_instances[request.session_hash] = Semantrix(
|
197 |
+
lang=0, model_type=model, session_hash=request.session_hash
|
198 |
+
)
|
199 |
+
game_instances[request.session_hash].reset_game() # Reset the game state
|
200 |
+
|
201 |
+
logger.info("New session detected: %s", request.session_hash)
|
202 |
+
logger.info("Game instances: %s", game_instances)
|
203 |
+
|
204 |
+
# Define the initial output components for the UI
|
205 |
+
output = [
|
206 |
+
-1,
|
207 |
+
gr.Textbox(visible=False),
|
208 |
+
gr.Textbox(visible=False),
|
209 |
+
gr.Image(logo_path, visible=True, interactive=False),
|
210 |
+
gr.Button(Menu["Start"], visible=True, variant="secondary"),
|
211 |
+
gr.Radio(visible=False),
|
212 |
+
gr.Textbox(visible=False),
|
213 |
+
gr.Button(visible=False),
|
214 |
+
gr.Markdown(
|
215 |
+
"""
|
216 |
+
<p style="text-align:center"> """
|
217 |
+
+ Menu["Header"]
|
218 |
+
+ """ </p>
|
219 |
+
"""
|
220 |
+
),
|
221 |
+
gr.Button(visible=False),
|
222 |
+
]
|
223 |
+
|
224 |
+
# Return the initial output components
|
225 |
+
return output
|
226 |
+
|
227 |
+
def remove_game_instance(timer_tick=False, request: gr.Request | None = None):
|
228 |
+
request = None if timer_tick else request
|
229 |
+
|
230 |
+
if request is not None:
|
231 |
+
logger.info("Session on inactivity timer: %s", request.session_hash)
|
232 |
+
sessions_to_remove[request.session_hash] = datetime.now()
|
233 |
+
if len(sessions_to_remove.items()) > 0:
|
234 |
+
for session_hash, timestamp in list(sessions_to_remove.items()):
|
235 |
+
if (datetime.now() - timestamp).seconds > cooldown_time:
|
236 |
+
del sessions_to_remove[session_hash]
|
237 |
+
|
238 |
+
session_id = game_instances[session_hash].get_session_id()
|
239 |
+
del game_instances[session_hash]
|
240 |
+
|
241 |
+
# Delete ranking file if it exists
|
242 |
+
ranking_file = os.path.join(
|
243 |
+
data_path, f"rankings/ranking_{session_hash}.txt"
|
244 |
+
)
|
245 |
+
if os.path.exists(ranking_file):
|
246 |
+
os.remove(ranking_file)
|
247 |
+
|
248 |
+
# Delete plays files if it exists
|
249 |
+
session_files = [
|
250 |
+
f for f in os.listdir(plays_path) if f.startswith(session_id)
|
251 |
+
]
|
252 |
+
for file_name in session_files:
|
253 |
+
with open(os.path.join(plays_path, file_name), "r") as file:
|
254 |
+
os.remove(os.path.join(plays_path, file_name))
|
255 |
+
|
256 |
+
logger.info("Deleted session: %s", session_hash)
|
257 |
+
|
258 |
+
# Function to change the state of the game
|
259 |
+
def change(state, inp):
|
260 |
+
# Increment the state by 1
|
261 |
+
state = state + 1
|
262 |
+
|
263 |
+
# Return the updated state and input component
|
264 |
+
return [state, inp]
|
265 |
+
|
266 |
+
# Function to update the game state based on the current state of the game
|
267 |
+
def update(state, radio, inp, hint, secret_word_used, request: gr.Request):
|
268 |
+
global difficulty, hints_enabled
|
269 |
+
|
270 |
+
# Define the difficulty state
|
271 |
+
dif_state = 3
|
272 |
+
|
273 |
+
# Initialize the output component list with the current state
|
274 |
+
|
275 |
+
state_int = state
|
276 |
+
|
277 |
+
output = [state]
|
278 |
+
|
279 |
+
if request.session_hash in sessions_to_remove:
|
280 |
+
sessions_to_remove.pop(request.session_hash)
|
281 |
+
logger.info("Session saved: %s", request.session_hash)
|
282 |
+
|
283 |
+
# Define UI components for the initial state
|
284 |
+
if state_int == -1:
|
285 |
+
output.extend(
|
286 |
+
[
|
287 |
+
gr.Button(Menu["Start"], visible=True),
|
288 |
+
gr.Radio(label="", visible=False),
|
289 |
+
gr.Textbox(
|
290 |
+
Config[list(Config.keys())[state_int]], visible=False, label=""
|
291 |
+
),
|
292 |
+
gr.Button(Menu["Give_up"], visible=False),
|
293 |
+
gr.Textbox(visible=False),
|
294 |
+
gr.Image(interactive=False, visible=True),
|
295 |
+
gr.Textbox(visible=False),
|
296 |
+
gr.Button(visible=False),
|
297 |
+
gr.Markdown(visible=False),
|
298 |
+
gr.Button(visible=False),
|
299 |
+
secret_word_used,
|
300 |
+
]
|
301 |
+
)
|
302 |
+
|
303 |
+
# Define UI components for the first state, ask the user if they want to know the rules
|
304 |
+
elif state_int == 1:
|
305 |
+
output.extend(
|
306 |
+
[
|
307 |
+
gr.Button(visible=False),
|
308 |
+
gr.Radio(
|
309 |
+
[Menu["Yes"], Menu["No"]], value=None, label="", visible=True
|
310 |
+
),
|
311 |
+
gr.Textbox(
|
312 |
+
Config[list(Config.keys())[state_int]], visible=True, label=""
|
313 |
+
),
|
314 |
+
gr.Button(Menu["Give_up"], visible=False),
|
315 |
+
gr.Textbox(visible=False),
|
316 |
+
gr.Image(interactive=False, visible=False),
|
317 |
+
gr.Textbox(visible=False),
|
318 |
+
gr.Button(visible=False),
|
319 |
+
gr.Markdown(visible=False),
|
320 |
+
gr.Button(visible=False),
|
321 |
+
secret_word_used,
|
322 |
+
]
|
323 |
+
)
|
324 |
+
|
325 |
+
# Define UI components for the second state, Depending on the answer, show the rules or keep going
|
326 |
+
elif state_int == 2:
|
327 |
+
if radio == Menu["No"]:
|
328 |
+
output = [
|
329 |
+
dif_state,
|
330 |
+
gr.Button("Introducir", visible=True),
|
331 |
+
gr.Radio(visible=False),
|
332 |
+
gr.Textbox(
|
333 |
+
Config[list(Config.keys())[state_int]], visible=True, label=""
|
334 |
+
),
|
335 |
+
gr.Button(Menu["Give_up"], visible=False),
|
336 |
+
gr.Textbox(visible=False),
|
337 |
+
gr.Image(interactive=False, visible=False),
|
338 |
+
gr.Textbox(visible=False),
|
339 |
+
gr.Button(visible=False),
|
340 |
+
gr.Markdown(visible=False),
|
341 |
+
gr.Button(visible=False),
|
342 |
+
secret_word_used,
|
343 |
+
]
|
344 |
+
|
345 |
+
else:
|
346 |
+
output.extend(
|
347 |
+
[
|
348 |
+
gr.Button(Menu["Next"], visible=True),
|
349 |
+
gr.Radio(visible=False),
|
350 |
+
gr.Textbox(
|
351 |
+
Config[list(Config.keys())[state_int]],
|
352 |
+
visible=True,
|
353 |
+
label="",
|
354 |
+
),
|
355 |
+
gr.Button(Menu["Give_up"], visible=False),
|
356 |
+
gr.Textbox(visible=False),
|
357 |
+
gr.Image(interactive=False, visible=False),
|
358 |
+
gr.Textbox(visible=False),
|
359 |
+
gr.Button(visible=False),
|
360 |
+
gr.Markdown(visible=False),
|
361 |
+
gr.Button(visible=False),
|
362 |
+
secret_word_used,
|
363 |
+
]
|
364 |
+
)
|
365 |
+
|
366 |
+
# Define UI components for the difficulty state, ask the user to select the difficulty level
|
367 |
+
elif state_int == dif_state:
|
368 |
+
output.extend(
|
369 |
+
[
|
370 |
+
gr.Button(Menu["Start"], visible=True, variant="primary"),
|
371 |
+
gr.Radio(visible=False),
|
372 |
+
gr.Textbox(
|
373 |
+
Config[list(Config.keys())[state_int]], visible=True, label=""
|
374 |
+
),
|
375 |
+
gr.Button(Menu["Give_up"], visible=False),
|
376 |
+
gr.Textbox(visible=False),
|
377 |
+
gr.Image(interactive=False, visible=False),
|
378 |
+
gr.Textbox(visible=False),
|
379 |
+
gr.Button(visible=False),
|
380 |
+
gr.Markdown(visible=False),
|
381 |
+
gr.Button(visible=False),
|
382 |
+
secret_word_used,
|
383 |
+
]
|
384 |
+
)
|
385 |
+
|
386 |
+
# 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
|
387 |
+
elif state_int == dif_state + 1:
|
388 |
+
|
389 |
+
game_instances[request.session_hash].prepare_game(
|
390 |
+
secret_word_used, 2 if hints_enabled else 4
|
391 |
+
)
|
392 |
+
|
393 |
+
output.extend(
|
394 |
+
[
|
395 |
+
gr.Button(Menu["Send"], visible=True, variant="primary"),
|
396 |
+
gr.Radio(label="", visible=False),
|
397 |
+
gr.Textbox(visible=False, label=""),
|
398 |
+
gr.Button(visible=False, variant="stop"),
|
399 |
+
gr.Textbox(
|
400 |
+
value="",
|
401 |
+
visible=True,
|
402 |
+
autofocus=True,
|
403 |
+
placeholder=Menu["New_word"],
|
404 |
+
),
|
405 |
+
gr.Image(interactive=False, visible=False),
|
406 |
+
gr.Textbox(visible=False),
|
407 |
+
gr.Button(visible=False),
|
408 |
+
gr.Markdown(visible=False),
|
409 |
+
gr.Button(visible=False),
|
410 |
+
secret_word_used + 1,
|
411 |
+
]
|
412 |
+
)
|
413 |
+
|
414 |
+
# Define UI components for the state greater than the difficulty state + 2, play the game and provide feedback based on the user input
|
415 |
+
elif state_int > dif_state + 1:
|
416 |
+
|
417 |
+
# Send the user input to the game and get the feedback from the game
|
418 |
+
feed = game_instances[request.session_hash].play_game(inp, model_class)
|
419 |
+
|
420 |
+
# Check if the feedback contains the ranking information and process it
|
421 |
+
feedback_trim = feed.split("[rank]")
|
422 |
+
if len(feedback_trim) > 1:
|
423 |
+
ranking_vis = True
|
424 |
+
ranking_md = convert_to_markdown_centered(feedback_trim[1])
|
425 |
+
|
426 |
+
else:
|
427 |
+
ranking_vis = False
|
428 |
+
ranking_md = ""
|
429 |
+
|
430 |
+
# Check if the feedback contains a hint, win, or lose message
|
431 |
+
feedback = feedback_trim[0].split("[hint]")
|
432 |
+
win = feedback_trim[0].split("[win]")
|
433 |
+
lose = feedback_trim[0].split("[lose]")
|
434 |
+
|
435 |
+
# Check if the feedback contains a hint message
|
436 |
+
if len(feedback) > 1:
|
437 |
+
hint = True
|
438 |
+
hint_out = feedback[1]
|
439 |
+
feedback = feedback[0]
|
440 |
+
else:
|
441 |
+
hint = False
|
442 |
+
feedback = feedback[0]
|
443 |
+
|
444 |
+
# Check if the feedback contains a win or lose message and process it
|
445 |
+
if len(win) > 1 or len(lose) > 1:
|
446 |
+
|
447 |
+
# Check if the user won the game
|
448 |
+
won = True if len(win) > 1 else False
|
449 |
+
|
450 |
+
# Get the curiosity message from the game
|
451 |
+
curiosity = game_instances[request.session_hash].curiosity()
|
452 |
+
|
453 |
+
# Define the output components for the win or lose state
|
454 |
+
output.extend(
|
455 |
+
[
|
456 |
+
gr.Button(Menu["Send"], visible=False, variant="primary"),
|
457 |
+
gr.Radio(label="", visible=False),
|
458 |
+
gr.Textbox(win[1] if won else lose[1], visible=True, label=""),
|
459 |
+
gr.Button(visible=False, variant="stop"),
|
460 |
+
gr.Textbox(
|
461 |
+
value="", visible=False, placeholder=Menu["New_word"]
|
462 |
+
),
|
463 |
+
gr.Image(
|
464 |
+
logo_win_path if won else logo_path,
|
465 |
+
interactive=False,
|
466 |
+
visible=True,
|
467 |
+
),
|
468 |
+
gr.Textbox(curiosity, visible=True, label=Menu["Curiosity"]),
|
469 |
+
gr.Button(Menu["Play_again"], variant="primary", visible=True),
|
470 |
+
gr.Markdown(visible=False),
|
471 |
+
gr.Button(visible=True),
|
472 |
+
secret_word_used,
|
473 |
+
]
|
474 |
+
)
|
475 |
+
|
476 |
+
return output
|
477 |
+
|
478 |
+
# Define the output components for the feedback and keep playing
|
479 |
+
output.extend(
|
480 |
+
[
|
481 |
+
gr.Button(Menu["Send"], visible=True, variant="primary"),
|
482 |
+
gr.Radio(label="", visible=False),
|
483 |
+
gr.Textbox(feedback, visible=True, label=""),
|
484 |
+
gr.Button(visible=True, variant="stop"),
|
485 |
+
gr.Textbox(value="", visible=True, placeholder=Menu["New_word"]),
|
486 |
+
gr.Image(logo_path, interactive=False, visible=False),
|
487 |
+
gr.Textbox(hint_out if hint else "", visible=hint, label="Pista"),
|
488 |
+
gr.Button(visible=False),
|
489 |
+
gr.Markdown(ranking_md, visible=ranking_vis),
|
490 |
+
gr.Button(visible=False),
|
491 |
+
secret_word_used,
|
492 |
+
]
|
493 |
+
)
|
494 |
+
|
495 |
+
# Define UI components for the rest of the states, state for showing basic text to the user
|
496 |
+
else:
|
497 |
+
output.extend(
|
498 |
+
[
|
499 |
+
gr.Button(Menu["Next"], visible=True),
|
500 |
+
gr.Radio(label="", visible=False),
|
501 |
+
gr.Textbox(
|
502 |
+
Config[list(Config.keys())[state_int]], visible=True, label=""
|
503 |
+
),
|
504 |
+
gr.Button("Pista", visible=False),
|
505 |
+
gr.Textbox(visible=False),
|
506 |
+
gr.Image(interactive=False, visible=False),
|
507 |
+
gr.Textbox(visible=False),
|
508 |
+
gr.Button(visible=False),
|
509 |
+
gr.Markdown(visible=False),
|
510 |
+
gr.Button(visible=False),
|
511 |
+
secret_word_used,
|
512 |
+
]
|
513 |
+
)
|
514 |
+
|
515 |
+
# Return the output components
|
516 |
+
return output
|
517 |
+
|
518 |
+
def commit_data(request: gr.Request):
|
519 |
+
session_id = game_instances[request.session_hash].get_session_id()
|
520 |
+
session_files = [f for f in os.listdir(plays_path) if f.startswith(session_id)]
|
521 |
+
combined_data = []
|
522 |
+
|
523 |
+
for file_name in session_files:
|
524 |
+
with open(os.path.join(plays_path, file_name), "r") as file:
|
525 |
+
combined_data.append(json.load(file))
|
526 |
+
os.remove(os.path.join(plays_path, file_name))
|
527 |
+
|
528 |
+
combined_file_path = os.path.join(plays_path, f"{session_id}.json")
|
529 |
+
with open(combined_file_path, "w") as combined_file:
|
530 |
+
json.dump(combined_data, combined_file, indent=4)
|
531 |
+
|
532 |
+
scheduler.push_to_hub()
|
533 |
+
|
534 |
+
if os.path.exists(combined_file_path):
|
535 |
+
os.remove(combined_file_path)
|
536 |
+
|
537 |
+
return [
|
538 |
+
gr.Button(visible=False),
|
539 |
+
gr.Textbox(visible=False),
|
540 |
+
gr.Textbox(visible=False),
|
541 |
+
gr.Button(
|
542 |
+
"Rellenar cuestionario",
|
543 |
+
variant="primary",
|
544 |
+
link="https://docs.google.com/forms/d/e/1FAIpQLScqPES3cM6i7yS87_PZtD0T7Q0W-YynKBY_m-Frja3BL5t9Tw/viewform?usp=pp_url&entry.1903327713="
|
545 |
+
+ session_id,
|
546 |
+
visible=True,
|
547 |
+
),
|
548 |
+
]
|
549 |
+
|
550 |
+
# Define the UI layout for the gam
|
551 |
+
img = gr.Image(logo_path, height=430, interactive=False, visible=True)
|
552 |
+
ranking = gr.Markdown(visible=False)
|
553 |
+
|
554 |
+
with gr.Row():
|
555 |
+
out = gr.Textbox(visible=False, placeholder=Config[list(Config.keys())[0]])
|
556 |
+
hint_out = gr.Textbox(visible=False)
|
557 |
+
|
558 |
+
radio = gr.Radio(visible=False)
|
559 |
+
|
560 |
+
with gr.Row():
|
561 |
+
inp = gr.Textbox(visible=False, interactive=True, label="")
|
562 |
+
but = gr.Button(Menu["Start"])
|
563 |
+
give_up = gr.Button("Pista", visible=False)
|
564 |
+
reload = gr.Button(Menu["Play_again"], visible=False)
|
565 |
+
finish = gr.Button(
|
566 |
+
"Terminar",
|
567 |
+
variant="stop",
|
568 |
+
# link="https://docs.google.com/forms/d/e/1FAIpQLSd0z8nI4hhOSR83yPIw_bR3KkSt25Lsq0ZXG1pZnkldeoceqA/viewform?usp=pp_url&entry.327829192=Condici%C3%B3n+1",
|
569 |
+
visible=False,
|
570 |
+
)
|
571 |
+
|
572 |
+
timer_delete = gr.Timer(value=30)
|
573 |
+
|
574 |
+
# Define the UI events for the game
|
575 |
+
|
576 |
+
# Define events that trigger the game state change
|
577 |
+
timer_delete.tick(remove_game_instance, inputs=[gr.State(True)])
|
578 |
+
inp.submit(
|
579 |
+
change,
|
580 |
+
inputs=[state, inp],
|
581 |
+
outputs=[state, inp],
|
582 |
+
concurrency_limit=5,
|
583 |
+
)
|
584 |
+
but.click(
|
585 |
+
change,
|
586 |
+
inputs=[state, inp],
|
587 |
+
outputs=[state, inp],
|
588 |
+
concurrency_limit=5,
|
589 |
+
)
|
590 |
+
give_up.click(
|
591 |
+
change,
|
592 |
+
inputs=[
|
593 |
+
state,
|
594 |
+
gr.Textbox("give_up", visible=False, interactive=True, label=""),
|
595 |
+
],
|
596 |
+
outputs=[state, inp],
|
597 |
+
concurrency_limit=5,
|
598 |
+
)
|
599 |
+
radio.input(
|
600 |
+
change,
|
601 |
+
inputs=[state, inp],
|
602 |
+
outputs=[state, inp],
|
603 |
+
concurrency_limit=5,
|
604 |
+
)
|
605 |
+
finish.click(
|
606 |
+
commit_data, outputs=[reload, out, hint_out, finish]
|
607 |
+
) # Define events that trigger the game reset
|
608 |
+
reload.click(
|
609 |
+
reset,
|
610 |
+
inputs=[gr.State(False)],
|
611 |
+
outputs=[state, out, inp, img, but, radio, hint_out, reload, header, finish],
|
612 |
+
)
|
613 |
+
demo.load(
|
614 |
+
reset,
|
615 |
+
inputs=[gr.State(True)],
|
616 |
+
outputs=[state, out, inp, img, but, radio, hint_out, reload, header, finish],
|
617 |
+
)
|
618 |
+
demo.unload(remove_game_instance)
|
619 |
+
|
620 |
+
# Define events that trigger the game state update
|
621 |
+
state.change(
|
622 |
+
update,
|
623 |
+
inputs=[state, radio, inp, hint, secret_word_used],
|
624 |
+
outputs=[
|
625 |
+
state,
|
626 |
+
but,
|
627 |
+
radio,
|
628 |
+
out,
|
629 |
+
give_up,
|
630 |
+
inp,
|
631 |
+
img,
|
632 |
+
hint_out,
|
633 |
+
reload,
|
634 |
+
ranking,
|
635 |
+
finish,
|
636 |
+
secret_word_used,
|
637 |
+
],
|
638 |
+
)
|
639 |
+
|
640 |
+
if __name__ == "__main__":
|
641 |
+
demo.launch(debug=True)
|
config/condition.json
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"condition_0" : {
|
3 |
+
"model" : "SentenceTransformer",
|
4 |
+
"hints" : true
|
5 |
+
},
|
6 |
+
"condition_1" : {
|
7 |
+
"model" : "SentenceTransformer",
|
8 |
+
"hints" : false
|
9 |
+
},
|
10 |
+
"condition_2" : {
|
11 |
+
"model" : "word2vec",
|
12 |
+
"hints" : true
|
13 |
+
},
|
14 |
+
"condition_3" : {
|
15 |
+
"model" : "word2vec",
|
16 |
+
"hints" : false
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
config/images/logo.png
ADDED
![]() |
config/images/logo_win.gif
ADDED
![]() |
Git LFS Details
|
config/lang.json
ADDED
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"SPA": {
|
3 |
+
"Game": {
|
4 |
+
"Introduction_0": "Bienvenido a Semantrix, el emocionante Juego de la adivinanza semántica. \n\nTu misión es adivinar una palabra secreta que yo he escogido, pero no te preocupes, te ayudaré en el camino. \n\nLanza al aire la primera palabra que se te ocurra. Te daré pistas diciéndote si estás caliente, es decir muy cerca de adivinarla o frío, es decir, muy lejos de la palabra.",
|
5 |
+
"Introduction_1": "¿Quieres saber cómo se juega?",
|
6 |
+
"Rules": "Cada palabra que propongas recibirá una puntuación semántica entre 0 y 10, un puntaje alto significa que estás muy cerca de adivinar la palabra secreta\n\nSi veo que estás un poco perdido, estaré aquí para darte pistas que te ayudarán a acercarte a la palabra secreta.\n\nComo ayuda extra, mostraré un ranking de todas las palabras que has propuesto, ordenadas según su puntuación. Así podrás tener una idea mejor de qué tan cerca están y qué palabras funcionan mejor.\n\nGanarás cuando adivines correctamente la palabra secreta. ¡No desistas, lo tienes al alcance!",
|
7 |
+
"Begin": "¡Enciende tu mente, confía en tus ideas y sobre todo, pasa un buen rato! Este es un juego en el que cada palabra, cada puntuación y cada pista te acerca a tu victoria. ¡Mucha suerte!",
|
8 |
+
"New_word": "Nueva palabra: ",
|
9 |
+
"Feedback_0": "Helado, puntuación: ",
|
10 |
+
"Feedback_1": "Frío, puntuación: ",
|
11 |
+
"Feedback_2": "Templado, puntuación: ",
|
12 |
+
"Feedback_3": "Caliente, puntuación: ",
|
13 |
+
"Feedback_4": "Quemando, puntuación: ",
|
14 |
+
"Feedback_5": "Ardiendo, puntuación: ",
|
15 |
+
"Feedback_6": "Te estás acercando",
|
16 |
+
"Feedback_7": "Te estás alejando",
|
17 |
+
"Feedback_8": "¡Has ganado, ENHORABUENA!",
|
18 |
+
"Feedback_9": "La palabra secreta era: ",
|
19 |
+
"Feedback_10": "Aunque no fue una victoria esta vez, ¡no temas! ¡Cada intento es una nueva oportunidad para brillar! ¡Sigue adelante!",
|
20 |
+
"secret_word": "PALABRA SECRETA",
|
21 |
+
"hint_intro": [
|
22 |
+
"Parece que las palabras están jugando al escondite contigo. ¿Necesitas una ayudita? Aquí va una pista:",
|
23 |
+
"¡Vamos, estás tan cerca de descifrar el enigma semántico! Pero si sientes que te falta un empujón, aquí tienes una pista:",
|
24 |
+
"Tu mente está explorando este campo semántico como un detective, ¡pero incluso los detectives a veces necesitan pistas extra! Así que, aquí va una para ti:",
|
25 |
+
"El camino semántico puede volverse un poco sinuoso a veces. No te preocupes, estoy aquí para allanar el camino con una pista:",
|
26 |
+
"Las palabras son como piezas de un rompecabezas, y sé que estás cerca de completar la imagen. Aquí va una pista para encajar las piezas restantes:",
|
27 |
+
"Estás navegando por las aguas semánticas con destreza, ¡pero incluso los capitanes expertos pueden necesitar un faro de vez en cuando! Aquí está tu faro, tu pista:",
|
28 |
+
"La danza de las palabras puede ser complicada, pero no te preocupes, estoy aquí para ser tu guía de baile. Aquí tienes una pista para que sigas moviéndote con gracia:"
|
29 |
+
]
|
30 |
+
},
|
31 |
+
"Hint": {
|
32 |
+
"secret_word": "La palabra secreta",
|
33 |
+
"hint_0_0": "Define brevemente \"",
|
34 |
+
"hint_0_1": "\" sin incluir la propia palabra ni palabras de su familia léxica. En caso de usarla refiérete a ",
|
35 |
+
"hint_0_2": " como \"la palabra secreta\":",
|
36 |
+
"hint_0_3": "Podría definir la palabra secreta como:\n",
|
37 |
+
"hint_1_0": "Representa la palabra ",
|
38 |
+
"hint_1_1": " con emojis:",
|
39 |
+
"hint_1_2": "Voy a intentar representarte la palabra usando emojis:\n",
|
40 |
+
"hint_2_0": "Da una pista en forma de poema de 4 versos sobre ",
|
41 |
+
"hint_2_1": " que no incluya la propia palabra:",
|
42 |
+
"hint_2_2": "Me he inspirado un poco, voy a recitar un poema sobre la palabra secreta:\n",
|
43 |
+
"hint_3_0": "Dime tres palabras aleatorias separadas por coma:",
|
44 |
+
"hint_3_1": "Dime una palabra relacionada con",
|
45 |
+
"hint_3_2": " pero no puede ser la propia palabra ni de su familia léxica. No añadas texto adicional",
|
46 |
+
"hint_3_3": "Voy a pensar cuatro palabras y te muestro entre ellas cual se acerca más a la palabra secreta:\n",
|
47 |
+
"hint_4_0": "Dime el título de una película real donde aparece ",
|
48 |
+
"hint_4_1": ". Di únicamente el título, nada mas:",
|
49 |
+
"hint_4_2": "Representa la película ",
|
50 |
+
"hint_4_3": " únicamente con unos pocos emojis:",
|
51 |
+
"hint_4_4": "He pensado en una película relacionada con la palabra secreta y te la voy a representar con emojis, la película es:\n",
|
52 |
+
"curiosity": "Cuéntame una curiosidad sobre \""
|
53 |
+
},
|
54 |
+
"Menu": {
|
55 |
+
"Header": "SEMANTRIX: EL JUEGO DE LAS PALABRAS",
|
56 |
+
"Start": "Empezar",
|
57 |
+
"Next": "Siguiente",
|
58 |
+
"Yes": "Sí",
|
59 |
+
"No": "No",
|
60 |
+
"Easy": "Fácil",
|
61 |
+
"Normal": "Normal",
|
62 |
+
"Hard": "Difícil",
|
63 |
+
"Expert": "Experto",
|
64 |
+
"New_word": "Nueva palabra",
|
65 |
+
"Send": "Enviar",
|
66 |
+
"Give_up": "Rendirse",
|
67 |
+
"Language": "Idioma",
|
68 |
+
"Best_tries": "Mejores intentos",
|
69 |
+
"N_try": "Intento nº",
|
70 |
+
"Word": "Palabra",
|
71 |
+
"Score": "Puntuación",
|
72 |
+
"Last_try": "Último intento",
|
73 |
+
"Curiosity": "Curiosidad",
|
74 |
+
"Play_again": "Jugar de nuevo"
|
75 |
+
}
|
76 |
+
},
|
77 |
+
"ENG": {
|
78 |
+
"Game": {
|
79 |
+
|
80 |
+
"Introduction_0": "Welcome to Semantrix, the exciting semantic guessing game.\n\nYour mission is to guess a secret word I've chosen, but don't worry, I'll help you along the way.\n\nThrow out the first word that comes to your mind. I'll give you clues by telling you if you're hot, meaning very close to guessing it, or cold, meaning far from the word.",
|
81 |
+
"Introduction_1": "Do you want to know how to play?",
|
82 |
+
"Rules": "Each word you propose will receive a semantic score between 0 and 10. A high score means you're very close to guessing the secret word.\n\nIf I see you're a bit lost, I'll be here to give you hints to help you get closer to the secret word.\n\nAs extra help, I'll show a ranking of all the words you've suggested, ordered by their score. This way, you can have a better idea of how close they are and which words work best.\n\nYou'll be the winner when you correctly guess the secret word. Don't give up, it's within your reach!",
|
83 |
+
"Begin": "Turn on your mind, trust your ideas, and above all, have a good time! This is a game where each word, each score, and each clue brings you closer to your victory. Good luck!",
|
84 |
+
"New_word": "New word: ",
|
85 |
+
"Feedback_0": "Ice-cold, score: ",
|
86 |
+
"Feedback_1": "Cold, score: ",
|
87 |
+
"Feedback_2": "Warm, score: ",
|
88 |
+
"Feedback_3": "Hot, score: ",
|
89 |
+
"Feedback_4": "Burning, score: ",
|
90 |
+
"Feedback_5": "On fire, score: ",
|
91 |
+
"Feedback_6": "You're getting closer",
|
92 |
+
"Feedback_7": "You're moving away",
|
93 |
+
"Feedback_8": "You've won, CONGRATULATIONS!",
|
94 |
+
"Feedback_9": "The secret word was: ",
|
95 |
+
"Feedback_10": "Though it wasn't a win this time, fear not! Each try is a new chance to shine! Keep going!",
|
96 |
+
"secret_word": "SECRET WORD",
|
97 |
+
"hint_intro": [
|
98 |
+
"It seems like the words are playing hide-and-seek with you. Need a little help? Here's a hint:",
|
99 |
+
"Come on, you're so close to unraveling the semantic mystery! But if you feel like you need a push, here's a hint:",
|
100 |
+
"Your mind is exploring this semantic field like a detective, but even detectives sometimes need extra clues! So, here's one for you:",
|
101 |
+
"The semantic path can get a bit winding at times. Don't worry, I'm here to smooth the way with a hint:",
|
102 |
+
"Words are like pieces of a puzzle, and I know you're close to completing the picture. Here's a hint to fit the remaining pieces:",
|
103 |
+
"You're navigating the semantic waters skillfully, but even expert captains may need a lighthouse now and then! Here's your lighthouse, your hint:",
|
104 |
+
"The dance of words can be intricate, but don't worry, I'm here to be your dance guide. Here's a hint to keep you moving with grace:"
|
105 |
+
]
|
106 |
+
},
|
107 |
+
"Hint": {
|
108 |
+
"secret_word": "The secret word",
|
109 |
+
"hint_0_0": "Briefly define \"",
|
110 |
+
"hint_0_1": "\" without including the word itself or words from its lexical family. If using it, refer to ",
|
111 |
+
"hint_0_2": " as \"the secret word\":",
|
112 |
+
"hint_0_3": "I could define the secret word as:\n",
|
113 |
+
"hint_1_0": "Represent the word ",
|
114 |
+
"hint_1_1": " with emojis:",
|
115 |
+
"hint_1_2": "I'll try to represent the word using emojis:\n",
|
116 |
+
"hint_2_0": "Give a hint in the form of a 4-verse poem about ",
|
117 |
+
"hint_2_1": " that doesn't include the word itself:",
|
118 |
+
"hint_2_2": "I've been a bit inspired; I'll recite a poem about the secret word:\n",
|
119 |
+
"hint_3_0": "Tell me three random words separated by commas:",
|
120 |
+
"hint_3_1": "Tell me a word related to",
|
121 |
+
"hint_3_2": " but it can't be the word itself or from its lexical family. Don't add any additional text",
|
122 |
+
"hint_3_3": "I'll think of four words and show you which one comes closest to the secret word:\n",
|
123 |
+
"hint_4_0": "Tell me the title of a real movie where ",
|
124 |
+
"hint_4_1": ". Only say the title, nothing more:",
|
125 |
+
"hint_4_2": "Represent the movie ",
|
126 |
+
"hint_4_3": " only with a few emojis:",
|
127 |
+
"hint_4_4": "I've thought of a movie related to the secret word, and I'll represent it with emojis. The movie is:\n",
|
128 |
+
"curiosity": "Tell me an interesting fact about \""
|
129 |
+
},
|
130 |
+
"Menu": {
|
131 |
+
"Header": "SEMANTRIX: THE WORD GUESSING GAME",
|
132 |
+
"Start": "Start",
|
133 |
+
"Next": "Next",
|
134 |
+
"Yes": "Yes",
|
135 |
+
"No": "No",
|
136 |
+
"Easy": "Easy",
|
137 |
+
"Normal": "Normal",
|
138 |
+
"Hard": "Hard",
|
139 |
+
"Expert": "Expert",
|
140 |
+
"New_word": "New word",
|
141 |
+
"Send": "Send",
|
142 |
+
"Give_up": "Give up",
|
143 |
+
"Language": "Language",
|
144 |
+
"Best_tries": "Best tries",
|
145 |
+
"N_try": "Try nº",
|
146 |
+
"Word": "Word",
|
147 |
+
"Score": "Score",
|
148 |
+
"Last_try": "Last try",
|
149 |
+
"Curiosity": "Curiosity",
|
150 |
+
"Play_again": "Play again"
|
151 |
+
}
|
152 |
+
}
|
153 |
+
}
|
config/secret.json
ADDED
@@ -0,0 +1,1035 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"ENG": {
|
3 |
+
"basic": [
|
4 |
+
"ant",
|
5 |
+
"tree",
|
6 |
+
"glass",
|
7 |
+
"mountains",
|
8 |
+
"dress",
|
9 |
+
"rain",
|
10 |
+
"soap",
|
11 |
+
"finger",
|
12 |
+
"man",
|
13 |
+
"earth",
|
14 |
+
"stick",
|
15 |
+
"office",
|
16 |
+
"sadness",
|
17 |
+
"radio",
|
18 |
+
"keyboard",
|
19 |
+
"dirt",
|
20 |
+
"plum",
|
21 |
+
"message",
|
22 |
+
"eel",
|
23 |
+
"king",
|
24 |
+
"wild boar",
|
25 |
+
"nut",
|
26 |
+
"planet",
|
27 |
+
"party",
|
28 |
+
"son",
|
29 |
+
"cellphone",
|
30 |
+
"visit",
|
31 |
+
"tab",
|
32 |
+
"elegance",
|
33 |
+
"gloves",
|
34 |
+
"turtle",
|
35 |
+
"lock",
|
36 |
+
"plane",
|
37 |
+
"leaf",
|
38 |
+
"suitcase",
|
39 |
+
"cookie",
|
40 |
+
"hand",
|
41 |
+
"lamp",
|
42 |
+
"woman",
|
43 |
+
"pen",
|
44 |
+
"mountain",
|
45 |
+
"leaves",
|
46 |
+
"wind",
|
47 |
+
"helmet",
|
48 |
+
"spring",
|
49 |
+
"atom",
|
50 |
+
"dad",
|
51 |
+
"father-in-law",
|
52 |
+
"watch",
|
53 |
+
"cabin",
|
54 |
+
"map",
|
55 |
+
"cow",
|
56 |
+
"rectangle",
|
57 |
+
"shoe",
|
58 |
+
"parrot",
|
59 |
+
"insect",
|
60 |
+
"letter",
|
61 |
+
"baby",
|
62 |
+
"keys",
|
63 |
+
"toucan",
|
64 |
+
"wall",
|
65 |
+
"jacket",
|
66 |
+
"waterfall",
|
67 |
+
"seat",
|
68 |
+
"ear",
|
69 |
+
"team",
|
70 |
+
"luck",
|
71 |
+
"soul",
|
72 |
+
"guitar",
|
73 |
+
"choir",
|
74 |
+
"summer",
|
75 |
+
"dream",
|
76 |
+
"button",
|
77 |
+
"chair",
|
78 |
+
"lamb",
|
79 |
+
"ease",
|
80 |
+
"notebook",
|
81 |
+
"curiosity",
|
82 |
+
"coin",
|
83 |
+
"hanger",
|
84 |
+
"violin",
|
85 |
+
"singer",
|
86 |
+
"camera",
|
87 |
+
"snow",
|
88 |
+
"pool",
|
89 |
+
"container",
|
90 |
+
"photograph",
|
91 |
+
"shelter",
|
92 |
+
"animal",
|
93 |
+
"fear",
|
94 |
+
"rat",
|
95 |
+
"plastic",
|
96 |
+
"debate",
|
97 |
+
"duck",
|
98 |
+
"feather",
|
99 |
+
"monkey",
|
100 |
+
"people",
|
101 |
+
"door",
|
102 |
+
"blind",
|
103 |
+
"nose",
|
104 |
+
"wood",
|
105 |
+
"leg",
|
106 |
+
"car",
|
107 |
+
"hair",
|
108 |
+
"key",
|
109 |
+
"stewardess",
|
110 |
+
"living room",
|
111 |
+
"forest",
|
112 |
+
"castle",
|
113 |
+
"salad",
|
114 |
+
"lock",
|
115 |
+
"carnation",
|
116 |
+
"molar",
|
117 |
+
"bird",
|
118 |
+
"table",
|
119 |
+
"beach",
|
120 |
+
"screen",
|
121 |
+
"agenda",
|
122 |
+
"person",
|
123 |
+
"idea",
|
124 |
+
"clothing",
|
125 |
+
"wheel",
|
126 |
+
"tool",
|
127 |
+
"aluminium",
|
128 |
+
"house",
|
129 |
+
"cup",
|
130 |
+
"clouds",
|
131 |
+
"jug",
|
132 |
+
"circle",
|
133 |
+
"brother",
|
134 |
+
"farm",
|
135 |
+
"sorrow",
|
136 |
+
"hook",
|
137 |
+
"iron",
|
138 |
+
"hump",
|
139 |
+
"sound",
|
140 |
+
"zoo",
|
141 |
+
"photo",
|
142 |
+
"toy",
|
143 |
+
"crib",
|
144 |
+
"war",
|
145 |
+
"dressing room",
|
146 |
+
"sheep",
|
147 |
+
"hail",
|
148 |
+
"pilot",
|
149 |
+
"grass",
|
150 |
+
"spider",
|
151 |
+
"hammer",
|
152 |
+
"last name",
|
153 |
+
"boat",
|
154 |
+
"armchair",
|
155 |
+
"plate",
|
156 |
+
"seal",
|
157 |
+
"tiger",
|
158 |
+
"lawn",
|
159 |
+
"dog",
|
160 |
+
"oil",
|
161 |
+
"bow",
|
162 |
+
"helm",
|
163 |
+
"school",
|
164 |
+
"can opener",
|
165 |
+
"chimney",
|
166 |
+
"match",
|
167 |
+
"molecule",
|
168 |
+
"shirt",
|
169 |
+
"hut",
|
170 |
+
"calculator",
|
171 |
+
"horse",
|
172 |
+
"pine grove",
|
173 |
+
"paper",
|
174 |
+
"ornament",
|
175 |
+
"church",
|
176 |
+
"uncle",
|
177 |
+
"pencil",
|
178 |
+
"pig",
|
179 |
+
"cheese",
|
180 |
+
"cream",
|
181 |
+
"time",
|
182 |
+
"field",
|
183 |
+
"pants",
|
184 |
+
"pen",
|
185 |
+
"cloud",
|
186 |
+
"friend",
|
187 |
+
"purse",
|
188 |
+
"competition",
|
189 |
+
"light",
|
190 |
+
"program",
|
191 |
+
"cable",
|
192 |
+
"key",
|
193 |
+
"writing",
|
194 |
+
"floor",
|
195 |
+
"job",
|
196 |
+
"compass",
|
197 |
+
"coaster",
|
198 |
+
"sun",
|
199 |
+
"lights",
|
200 |
+
"furniture",
|
201 |
+
"dogs",
|
202 |
+
"eye",
|
203 |
+
"rope",
|
204 |
+
"noodles",
|
205 |
+
"kindness",
|
206 |
+
"sailboat",
|
207 |
+
"bed",
|
208 |
+
"sand",
|
209 |
+
"corner",
|
210 |
+
"cat",
|
211 |
+
"honey",
|
212 |
+
"chocolate",
|
213 |
+
"bell",
|
214 |
+
"noise",
|
215 |
+
"pillar",
|
216 |
+
"child",
|
217 |
+
"street",
|
218 |
+
"rocket",
|
219 |
+
"herd",
|
220 |
+
"godfather",
|
221 |
+
"headphones",
|
222 |
+
"nephew",
|
223 |
+
"hospital",
|
224 |
+
"July",
|
225 |
+
"bunch",
|
226 |
+
"speaker",
|
227 |
+
"brother-in-law",
|
228 |
+
"coffee",
|
229 |
+
"satellite",
|
230 |
+
"screw",
|
231 |
+
"books",
|
232 |
+
"letter",
|
233 |
+
"folder",
|
234 |
+
"train",
|
235 |
+
"briefcase",
|
236 |
+
"school",
|
237 |
+
"picture",
|
238 |
+
"eagle",
|
239 |
+
"love",
|
240 |
+
"room",
|
241 |
+
"truck",
|
242 |
+
"newspapers",
|
243 |
+
"tablecloth",
|
244 |
+
"notes",
|
245 |
+
"heron",
|
246 |
+
"painting",
|
247 |
+
"fluff",
|
248 |
+
"tie",
|
249 |
+
"letters",
|
250 |
+
"printer",
|
251 |
+
"sofa",
|
252 |
+
"flower",
|
253 |
+
"weasel",
|
254 |
+
"lemon",
|
255 |
+
"mason",
|
256 |
+
"glass",
|
257 |
+
"meat",
|
258 |
+
"city",
|
259 |
+
"designer",
|
260 |
+
"lagoon",
|
261 |
+
"darkness",
|
262 |
+
"meadow",
|
263 |
+
"puma",
|
264 |
+
"ship",
|
265 |
+
"troop",
|
266 |
+
"lime",
|
267 |
+
"cage",
|
268 |
+
"sport",
|
269 |
+
"casino",
|
270 |
+
"building",
|
271 |
+
"firefighter",
|
272 |
+
"plant"
|
273 |
+
],
|
274 |
+
"advanced": [
|
275 |
+
"accountant",
|
276 |
+
"hatred",
|
277 |
+
"engineer",
|
278 |
+
"viewer",
|
279 |
+
"window",
|
280 |
+
"substance",
|
281 |
+
"complaint",
|
282 |
+
"platform",
|
283 |
+
"toilet",
|
284 |
+
"Argentina",
|
285 |
+
"Guadeloupe",
|
286 |
+
"machinist",
|
287 |
+
"explosion",
|
288 |
+
"gas",
|
289 |
+
"presidency",
|
290 |
+
"lake",
|
291 |
+
"visit",
|
292 |
+
"speech",
|
293 |
+
"archipelago",
|
294 |
+
"entrepreneur",
|
295 |
+
"Temple",
|
296 |
+
"utensil",
|
297 |
+
"theater",
|
298 |
+
"classroom",
|
299 |
+
"television",
|
300 |
+
"glasses",
|
301 |
+
"reptile",
|
302 |
+
"whale",
|
303 |
+
"fauna",
|
304 |
+
"plane",
|
305 |
+
"stone",
|
306 |
+
"family",
|
307 |
+
"discotheque",
|
308 |
+
"bomb",
|
309 |
+
"waitress",
|
310 |
+
"candidate",
|
311 |
+
"triangle",
|
312 |
+
"famous",
|
313 |
+
"auction",
|
314 |
+
"belt",
|
315 |
+
"atom",
|
316 |
+
"bookstore",
|
317 |
+
"watch",
|
318 |
+
"music",
|
319 |
+
"Grandmother",
|
320 |
+
"candies",
|
321 |
+
"battery",
|
322 |
+
"brother",
|
323 |
+
"Colombia",
|
324 |
+
"hook",
|
325 |
+
"sound",
|
326 |
+
"government",
|
327 |
+
"note",
|
328 |
+
"hippopotamus",
|
329 |
+
"metal",
|
330 |
+
"happiness",
|
331 |
+
"baby",
|
332 |
+
"tiger",
|
333 |
+
"child",
|
334 |
+
"vocabulary",
|
335 |
+
"grapefruit",
|
336 |
+
"cloud",
|
337 |
+
"competition",
|
338 |
+
"cable",
|
339 |
+
"Mexico",
|
340 |
+
"program",
|
341 |
+
"writing",
|
342 |
+
"compass",
|
343 |
+
"coaster",
|
344 |
+
"program",
|
345 |
+
"writing",
|
346 |
+
"compass",
|
347 |
+
"coaster",
|
348 |
+
"lights",
|
349 |
+
"water",
|
350 |
+
"bottle",
|
351 |
+
"floor",
|
352 |
+
"rabbit",
|
353 |
+
"book",
|
354 |
+
"bridge",
|
355 |
+
"scarf",
|
356 |
+
"school",
|
357 |
+
"picture",
|
358 |
+
"notes",
|
359 |
+
"photo",
|
360 |
+
"toy",
|
361 |
+
"war",
|
362 |
+
"heron",
|
363 |
+
"carnation",
|
364 |
+
"enthusiasm",
|
365 |
+
"molar",
|
366 |
+
"bird",
|
367 |
+
"table",
|
368 |
+
"screen",
|
369 |
+
"agenda",
|
370 |
+
"person",
|
371 |
+
"idea",
|
372 |
+
"clothing",
|
373 |
+
"wheel",
|
374 |
+
"tool",
|
375 |
+
"aluminium",
|
376 |
+
"house",
|
377 |
+
"cup",
|
378 |
+
"clouds",
|
379 |
+
"jug",
|
380 |
+
"circle",
|
381 |
+
"Colombia",
|
382 |
+
"farm",
|
383 |
+
"sorrow",
|
384 |
+
"iron",
|
385 |
+
"hump",
|
386 |
+
"zoo",
|
387 |
+
"government",
|
388 |
+
"photo",
|
389 |
+
"toy",
|
390 |
+
"crib",
|
391 |
+
"war",
|
392 |
+
"dressing room",
|
393 |
+
"tornado",
|
394 |
+
"sheep",
|
395 |
+
"hail",
|
396 |
+
"pilot",
|
397 |
+
"grass",
|
398 |
+
"spider",
|
399 |
+
"anguish",
|
400 |
+
"hammer",
|
401 |
+
"statistics",
|
402 |
+
"last name",
|
403 |
+
"boat",
|
404 |
+
"armchair",
|
405 |
+
"plate",
|
406 |
+
"river",
|
407 |
+
"seal",
|
408 |
+
"rhinoceros",
|
409 |
+
"shirt",
|
410 |
+
"hut",
|
411 |
+
"calculator",
|
412 |
+
"horse",
|
413 |
+
"pine grove",
|
414 |
+
"paper",
|
415 |
+
"ornament",
|
416 |
+
"church",
|
417 |
+
"uncle",
|
418 |
+
"pencil",
|
419 |
+
"pig",
|
420 |
+
"cheese",
|
421 |
+
"cream",
|
422 |
+
"time",
|
423 |
+
"vocabulary",
|
424 |
+
"grapefruit",
|
425 |
+
"field",
|
426 |
+
"pants",
|
427 |
+
"pen",
|
428 |
+
"cloud",
|
429 |
+
"friend",
|
430 |
+
"purse",
|
431 |
+
"competition",
|
432 |
+
"light",
|
433 |
+
"program",
|
434 |
+
"cable",
|
435 |
+
"key",
|
436 |
+
"writing",
|
437 |
+
"Mexico",
|
438 |
+
"floor",
|
439 |
+
"job",
|
440 |
+
"compass",
|
441 |
+
"coaster",
|
442 |
+
"sun",
|
443 |
+
"lights",
|
444 |
+
"furniture",
|
445 |
+
"dogs",
|
446 |
+
"eye",
|
447 |
+
"rope",
|
448 |
+
"noodles",
|
449 |
+
"kindness",
|
450 |
+
"sailboat",
|
451 |
+
"bed",
|
452 |
+
"sand",
|
453 |
+
"corner",
|
454 |
+
"cat",
|
455 |
+
"honey",
|
456 |
+
"chocolate",
|
457 |
+
"bell",
|
458 |
+
"noise",
|
459 |
+
"pillar",
|
460 |
+
"child",
|
461 |
+
"street",
|
462 |
+
"rocket",
|
463 |
+
"herd",
|
464 |
+
"godfather",
|
465 |
+
"headphones",
|
466 |
+
"nephew",
|
467 |
+
"hospital",
|
468 |
+
"July",
|
469 |
+
"bunch",
|
470 |
+
"Chile",
|
471 |
+
"speaker",
|
472 |
+
"brother-in-law",
|
473 |
+
"coffee",
|
474 |
+
"satellite",
|
475 |
+
"screw",
|
476 |
+
"books",
|
477 |
+
"letter",
|
478 |
+
"folder",
|
479 |
+
"train",
|
480 |
+
"briefcase",
|
481 |
+
"school",
|
482 |
+
"picture",
|
483 |
+
"eagle",
|
484 |
+
"love",
|
485 |
+
"room",
|
486 |
+
"truck",
|
487 |
+
"newspapers",
|
488 |
+
"tablecloth",
|
489 |
+
"notes",
|
490 |
+
"heron",
|
491 |
+
"painting",
|
492 |
+
"fluff",
|
493 |
+
"tie",
|
494 |
+
"letters",
|
495 |
+
"printer",
|
496 |
+
"sofa",
|
497 |
+
"flower",
|
498 |
+
"weasel",
|
499 |
+
"lemon",
|
500 |
+
"mason",
|
501 |
+
"glass",
|
502 |
+
"meat",
|
503 |
+
"city",
|
504 |
+
"designer",
|
505 |
+
"lagoon",
|
506 |
+
"darkness",
|
507 |
+
"meadow",
|
508 |
+
"puma",
|
509 |
+
"ship",
|
510 |
+
"troop",
|
511 |
+
"lime",
|
512 |
+
"cage",
|
513 |
+
"sport",
|
514 |
+
"casino",
|
515 |
+
"building",
|
516 |
+
"firefighter",
|
517 |
+
"plant",
|
518 |
+
"clarity"
|
519 |
+
]
|
520 |
+
},
|
521 |
+
"SPA": {
|
522 |
+
"basic": [
|
523 |
+
"vestido",
|
524 |
+
"lluvia",
|
525 |
+
"jabón",
|
526 |
+
"dedo",
|
527 |
+
"hombre",
|
528 |
+
"tierra",
|
529 |
+
"palo",
|
530 |
+
"oficina",
|
531 |
+
"tristeza",
|
532 |
+
"radio",
|
533 |
+
"teclado",
|
534 |
+
"tierra",
|
535 |
+
"ciruela",
|
536 |
+
"mensaje",
|
537 |
+
"anguila",
|
538 |
+
"rey",
|
539 |
+
"jabalí",
|
540 |
+
"nuez",
|
541 |
+
"planeta",
|
542 |
+
"fiesta",
|
543 |
+
"hijo",
|
544 |
+
"teléfono",
|
545 |
+
"visita",
|
546 |
+
"pestaña",
|
547 |
+
"elegancia",
|
548 |
+
"guantes",
|
549 |
+
"tortuga",
|
550 |
+
"cerradura",
|
551 |
+
"avión",
|
552 |
+
"hoja",
|
553 |
+
"maleta",
|
554 |
+
"galleta",
|
555 |
+
"mano",
|
556 |
+
"lámpara",
|
557 |
+
"mujer",
|
558 |
+
"pluma",
|
559 |
+
"montaña",
|
560 |
+
"hojas",
|
561 |
+
"viento",
|
562 |
+
"casco",
|
563 |
+
"primavera",
|
564 |
+
"átomo",
|
565 |
+
"papá",
|
566 |
+
"suegro",
|
567 |
+
"reloj",
|
568 |
+
"cabaña",
|
569 |
+
"mapa",
|
570 |
+
"vaca",
|
571 |
+
"rectángulo",
|
572 |
+
"zapato",
|
573 |
+
"loro",
|
574 |
+
"insecto",
|
575 |
+
"carta",
|
576 |
+
"bebé",
|
577 |
+
"llaves",
|
578 |
+
"tucán",
|
579 |
+
"pared",
|
580 |
+
"chaqueta",
|
581 |
+
"cascada",
|
582 |
+
"asiento",
|
583 |
+
"oreja",
|
584 |
+
"equipo",
|
585 |
+
"suerte",
|
586 |
+
"alma",
|
587 |
+
"guitarra",
|
588 |
+
"coro",
|
589 |
+
"verano",
|
590 |
+
"sueño",
|
591 |
+
"botón",
|
592 |
+
"silla",
|
593 |
+
"cordero",
|
594 |
+
"facilidad",
|
595 |
+
"cuaderno",
|
596 |
+
"curiosidad",
|
597 |
+
"moneda",
|
598 |
+
"percha",
|
599 |
+
"violín",
|
600 |
+
"cantante",
|
601 |
+
"cámara",
|
602 |
+
"nieve",
|
603 |
+
"piscina",
|
604 |
+
"contenedor",
|
605 |
+
"fotografía",
|
606 |
+
"refugio",
|
607 |
+
"animal",
|
608 |
+
"miedo",
|
609 |
+
"rata",
|
610 |
+
"plástico",
|
611 |
+
"debate",
|
612 |
+
"pato",
|
613 |
+
"pluma",
|
614 |
+
"mono",
|
615 |
+
"gente",
|
616 |
+
"puerta",
|
617 |
+
"persiana",
|
618 |
+
"nariz",
|
619 |
+
"madera",
|
620 |
+
"pierna",
|
621 |
+
"coche",
|
622 |
+
"cabello",
|
623 |
+
"llave",
|
624 |
+
"azafata",
|
625 |
+
"sala de estar",
|
626 |
+
"bosque",
|
627 |
+
"castillo",
|
628 |
+
"ensalada",
|
629 |
+
"cerradura",
|
630 |
+
"clavel",
|
631 |
+
"molar",
|
632 |
+
"pájaro",
|
633 |
+
"mesa",
|
634 |
+
"playa",
|
635 |
+
"pantalla",
|
636 |
+
"agenda",
|
637 |
+
"persona",
|
638 |
+
"idea",
|
639 |
+
"ropa",
|
640 |
+
"rueda",
|
641 |
+
"herramienta",
|
642 |
+
"aluminio",
|
643 |
+
"casa",
|
644 |
+
"taza",
|
645 |
+
"nubes",
|
646 |
+
"jarra",
|
647 |
+
"círculo",
|
648 |
+
"hermano",
|
649 |
+
"granja",
|
650 |
+
"pena",
|
651 |
+
"gancho",
|
652 |
+
"hierro",
|
653 |
+
"joroba",
|
654 |
+
"sonido",
|
655 |
+
"zoológico",
|
656 |
+
"foto",
|
657 |
+
"juguete",
|
658 |
+
"cuna",
|
659 |
+
"guerra",
|
660 |
+
"vestuario",
|
661 |
+
"oveja",
|
662 |
+
"granizo",
|
663 |
+
"piloto",
|
664 |
+
"césped",
|
665 |
+
"araña",
|
666 |
+
"martillo",
|
667 |
+
"apellido",
|
668 |
+
"barco",
|
669 |
+
"sillón",
|
670 |
+
"plato",
|
671 |
+
"foca",
|
672 |
+
"tigre",
|
673 |
+
"césped",
|
674 |
+
"perro",
|
675 |
+
"aceite",
|
676 |
+
"arco",
|
677 |
+
"timón",
|
678 |
+
"escuela",
|
679 |
+
"abrelatas",
|
680 |
+
"chimenea",
|
681 |
+
"fósforo",
|
682 |
+
"molécula",
|
683 |
+
"camisa",
|
684 |
+
"choza",
|
685 |
+
"calculadora",
|
686 |
+
"caballo",
|
687 |
+
"pinar",
|
688 |
+
"papel",
|
689 |
+
"adorno",
|
690 |
+
"iglesia",
|
691 |
+
"tío",
|
692 |
+
"lápiz",
|
693 |
+
"cerdo",
|
694 |
+
"queso",
|
695 |
+
"crema",
|
696 |
+
"tiempo",
|
697 |
+
"campo",
|
698 |
+
"pantalones",
|
699 |
+
"pluma",
|
700 |
+
"nube",
|
701 |
+
"amigo",
|
702 |
+
"bolso",
|
703 |
+
"competencia",
|
704 |
+
"luz",
|
705 |
+
"programa",
|
706 |
+
"cable",
|
707 |
+
"llave",
|
708 |
+
"escritura",
|
709 |
+
"piso",
|
710 |
+
"trabajo",
|
711 |
+
"brújula",
|
712 |
+
"posavasos",
|
713 |
+
"sol",
|
714 |
+
"luces",
|
715 |
+
"muebles",
|
716 |
+
"perros",
|
717 |
+
"ojo",
|
718 |
+
"cuerda",
|
719 |
+
"fideos",
|
720 |
+
"bondad",
|
721 |
+
"velero",
|
722 |
+
"cama",
|
723 |
+
"arena",
|
724 |
+
"esquina",
|
725 |
+
"gato",
|
726 |
+
"miel",
|
727 |
+
"chocolate",
|
728 |
+
"campana",
|
729 |
+
"ruido",
|
730 |
+
"pilar",
|
731 |
+
"niño",
|
732 |
+
"calle",
|
733 |
+
"cohete",
|
734 |
+
"manada",
|
735 |
+
"padrino",
|
736 |
+
"auriculares",
|
737 |
+
"sobrino",
|
738 |
+
"hospital",
|
739 |
+
"julio",
|
740 |
+
"ramo",
|
741 |
+
"altavoz",
|
742 |
+
"cuñado",
|
743 |
+
"café",
|
744 |
+
"satélite",
|
745 |
+
"tornillo",
|
746 |
+
"libros",
|
747 |
+
"carta",
|
748 |
+
"carpeta",
|
749 |
+
"tren",
|
750 |
+
"maletín",
|
751 |
+
"escuela",
|
752 |
+
"imagen",
|
753 |
+
"águila",
|
754 |
+
"amor",
|
755 |
+
"habitación",
|
756 |
+
"camión",
|
757 |
+
"periódicos",
|
758 |
+
"mantel",
|
759 |
+
"notas",
|
760 |
+
"garza",
|
761 |
+
"pintura",
|
762 |
+
"pelusa",
|
763 |
+
"corbata",
|
764 |
+
"letras",
|
765 |
+
"impresora",
|
766 |
+
"sofá",
|
767 |
+
"flor",
|
768 |
+
"comadreja",
|
769 |
+
"limón",
|
770 |
+
"albañil",
|
771 |
+
"vidrio",
|
772 |
+
"carne",
|
773 |
+
"ciudad",
|
774 |
+
"diseñador",
|
775 |
+
"laguna",
|
776 |
+
"oscuridad",
|
777 |
+
"prado",
|
778 |
+
"puma",
|
779 |
+
"barco",
|
780 |
+
"tropa",
|
781 |
+
"lima",
|
782 |
+
"jaula",
|
783 |
+
"deporte",
|
784 |
+
"casino",
|
785 |
+
"edificio",
|
786 |
+
"bombero",
|
787 |
+
"planta"
|
788 |
+
],
|
789 |
+
"advanced": [
|
790 |
+
"contador",
|
791 |
+
"odio",
|
792 |
+
"ingeniero",
|
793 |
+
"espectador",
|
794 |
+
"ventana",
|
795 |
+
"sustancia",
|
796 |
+
"queja",
|
797 |
+
"plataforma",
|
798 |
+
"inodoro",
|
799 |
+
"Argentina",
|
800 |
+
"Guadalupe",
|
801 |
+
"mecánico",
|
802 |
+
"explosión",
|
803 |
+
"gas",
|
804 |
+
"presidencia",
|
805 |
+
"lago",
|
806 |
+
"visita",
|
807 |
+
"discurso",
|
808 |
+
"archipiélago",
|
809 |
+
"empresario",
|
810 |
+
"templo",
|
811 |
+
"utensilio",
|
812 |
+
"teatro",
|
813 |
+
"aula",
|
814 |
+
"televisión",
|
815 |
+
"gafas",
|
816 |
+
"reptil",
|
817 |
+
"ballena",
|
818 |
+
"fauna",
|
819 |
+
"avión",
|
820 |
+
"piedra",
|
821 |
+
"familia",
|
822 |
+
"discoteca",
|
823 |
+
"bomba",
|
824 |
+
"mesera",
|
825 |
+
"candidato",
|
826 |
+
"triángulo",
|
827 |
+
"famoso",
|
828 |
+
"subasta",
|
829 |
+
"cinturón",
|
830 |
+
"átomo",
|
831 |
+
"librería",
|
832 |
+
"reloj",
|
833 |
+
"música",
|
834 |
+
"abuela",
|
835 |
+
"caramelos",
|
836 |
+
"batería",
|
837 |
+
"hermano",
|
838 |
+
"Colombia",
|
839 |
+
"gancho",
|
840 |
+
"sonido",
|
841 |
+
"gobierno",
|
842 |
+
"nota",
|
843 |
+
"hipopótamo",
|
844 |
+
"metal",
|
845 |
+
"felicidad",
|
846 |
+
"bebé",
|
847 |
+
"tigre",
|
848 |
+
"niño",
|
849 |
+
"vocabulario",
|
850 |
+
"pomelo",
|
851 |
+
"nube",
|
852 |
+
"competencia",
|
853 |
+
"cable",
|
854 |
+
"México",
|
855 |
+
"programa",
|
856 |
+
"escritura",
|
857 |
+
"brújula",
|
858 |
+
"posavasos",
|
859 |
+
"programa",
|
860 |
+
"escritura",
|
861 |
+
"brújula",
|
862 |
+
"posavasos",
|
863 |
+
"luces",
|
864 |
+
"agua",
|
865 |
+
"botella",
|
866 |
+
"piso",
|
867 |
+
"conejo",
|
868 |
+
"libro",
|
869 |
+
"puente",
|
870 |
+
"bufanda",
|
871 |
+
"escuela",
|
872 |
+
"imagen",
|
873 |
+
"notas",
|
874 |
+
"foto",
|
875 |
+
"juguete",
|
876 |
+
"guerra",
|
877 |
+
"garza",
|
878 |
+
"clavel",
|
879 |
+
"entusiasmo",
|
880 |
+
"molar",
|
881 |
+
"pájaro",
|
882 |
+
"mesa",
|
883 |
+
"pantalla",
|
884 |
+
"agenda",
|
885 |
+
"persona",
|
886 |
+
"idea",
|
887 |
+
"ropa",
|
888 |
+
"rueda",
|
889 |
+
"herramienta",
|
890 |
+
"aluminio",
|
891 |
+
"casa",
|
892 |
+
"taza",
|
893 |
+
"nubes",
|
894 |
+
"jarra",
|
895 |
+
"círculo",
|
896 |
+
"Colombia",
|
897 |
+
"granja",
|
898 |
+
"pena",
|
899 |
+
"hierro",
|
900 |
+
"joroba",
|
901 |
+
"zoológico",
|
902 |
+
"gobierno",
|
903 |
+
"foto",
|
904 |
+
"juguete",
|
905 |
+
"cuna",
|
906 |
+
"guerra",
|
907 |
+
"vestuario",
|
908 |
+
"oveja",
|
909 |
+
"granizo",
|
910 |
+
"piloto",
|
911 |
+
"césped",
|
912 |
+
"araña",
|
913 |
+
"angustia",
|
914 |
+
"martillo",
|
915 |
+
"estadísticas",
|
916 |
+
"apellido",
|
917 |
+
"barco",
|
918 |
+
"sillón",
|
919 |
+
"plato",
|
920 |
+
"río",
|
921 |
+
"foca",
|
922 |
+
"rinoceronte",
|
923 |
+
"camisa",
|
924 |
+
"choza",
|
925 |
+
"calculadora",
|
926 |
+
"caballo",
|
927 |
+
"pinar",
|
928 |
+
"papel",
|
929 |
+
"adorno",
|
930 |
+
"iglesia",
|
931 |
+
"tío",
|
932 |
+
"lápiz",
|
933 |
+
"cerdo",
|
934 |
+
"queso",
|
935 |
+
"crema",
|
936 |
+
"tiempo",
|
937 |
+
"vocabulario",
|
938 |
+
"pomelo",
|
939 |
+
"campo",
|
940 |
+
"pantalones",
|
941 |
+
"pluma",
|
942 |
+
"nube",
|
943 |
+
"amigo",
|
944 |
+
"bolso",
|
945 |
+
"competencia",
|
946 |
+
"luz",
|
947 |
+
"programa",
|
948 |
+
"cable",
|
949 |
+
"llave",
|
950 |
+
"escritura",
|
951 |
+
"México",
|
952 |
+
"piso",
|
953 |
+
"trabajo",
|
954 |
+
"brújula",
|
955 |
+
"posavasos",
|
956 |
+
"sol",
|
957 |
+
"luces",
|
958 |
+
"muebles",
|
959 |
+
"perros",
|
960 |
+
"ojo",
|
961 |
+
"cuerda",
|
962 |
+
"fideos",
|
963 |
+
"bondad",
|
964 |
+
"velero",
|
965 |
+
"cama",
|
966 |
+
"arena",
|
967 |
+
"esquina",
|
968 |
+
"gato",
|
969 |
+
"miel",
|
970 |
+
"chocolate",
|
971 |
+
"campana",
|
972 |
+
"ruido",
|
973 |
+
"pilar",
|
974 |
+
"niño",
|
975 |
+
"calle",
|
976 |
+
"cohete",
|
977 |
+
"manada",
|
978 |
+
"padrino",
|
979 |
+
"auriculares",
|
980 |
+
"sobrino",
|
981 |
+
"hospital",
|
982 |
+
"julio",
|
983 |
+
"ramo",
|
984 |
+
"Chile",
|
985 |
+
"altavoz",
|
986 |
+
"cuñado",
|
987 |
+
"café",
|
988 |
+
"satélite",
|
989 |
+
"tornillo",
|
990 |
+
"libros",
|
991 |
+
"carta",
|
992 |
+
"carpeta",
|
993 |
+
"tren",
|
994 |
+
"maletín",
|
995 |
+
"escuela",
|
996 |
+
"imagen",
|
997 |
+
"águila",
|
998 |
+
"amor",
|
999 |
+
"habitación",
|
1000 |
+
"camión",
|
1001 |
+
"periódicos",
|
1002 |
+
"mantel",
|
1003 |
+
"notas",
|
1004 |
+
"garza",
|
1005 |
+
"pintura",
|
1006 |
+
"pelusa",
|
1007 |
+
"corbata",
|
1008 |
+
"letras",
|
1009 |
+
"impresora",
|
1010 |
+
"sofá",
|
1011 |
+
"flor",
|
1012 |
+
"comadreja",
|
1013 |
+
"limón",
|
1014 |
+
"albañil",
|
1015 |
+
"vidrio",
|
1016 |
+
"carne",
|
1017 |
+
"ciudad",
|
1018 |
+
"diseñador",
|
1019 |
+
"laguna",
|
1020 |
+
"oscuridad",
|
1021 |
+
"prado",
|
1022 |
+
"puma",
|
1023 |
+
"barco",
|
1024 |
+
"tropa",
|
1025 |
+
"lima",
|
1026 |
+
"jaula",
|
1027 |
+
"deporte",
|
1028 |
+
"casino",
|
1029 |
+
"edificio",
|
1030 |
+
"bombero",
|
1031 |
+
"planta",
|
1032 |
+
"claridad"
|
1033 |
+
]
|
1034 |
+
}
|
1035 |
+
}
|
data/plays/__init__.py
ADDED
File without changes
|
data/rankings/__init__.py
ADDED
File without changes
|
game.py
ADDED
@@ -0,0 +1,417 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3.10
|
2 |
+
|
3 |
+
# """
|
4 |
+
# Semantrix Game Module
|
5 |
+
|
6 |
+
# This module defines the Semantrix class, which implements a word guessing game using word embeddings. The game can be configured to use either a Word2Vec model or a SentenceTransformer model for word embeddings. The game supports multiple languages and difficulty levels.
|
7 |
+
|
8 |
+
# Classes:
|
9 |
+
# Semantrix: A class that implements the Semantrix word guessing game.
|
10 |
+
# Semantrix.DictWrapper: A helper class to wrap configuration dictionaries.
|
11 |
+
|
12 |
+
# Functions:
|
13 |
+
# __init__(self, lang=0, model_type="SentenceTransformer"): Initializes the Semantrix game with the specified language and model type.
|
14 |
+
# prepare_game(self, difficulty): Prepares the game with the selected difficulty level.
|
15 |
+
# gen_rank(self, repeated): Generates the ranking file based on the scores.
|
16 |
+
# play_game(self, word): Plays the game with the selected word and returns feedback.
|
17 |
+
# curiosity(self): Generates a curiosity hint about the secret word once the game is over.
|
18 |
+
|
19 |
+
# Attributes:
|
20 |
+
# model (KeyedVectors): The word embeddings model.
|
21 |
+
# config_file_path (str): Path to the configuration file.
|
22 |
+
# secret_file_path (str): Path to the secret words file.
|
23 |
+
# data_path (str): Path to the data directory.
|
24 |
+
# Config_full (dict): Full configuration data.
|
25 |
+
# secret (dict): Secret words data.
|
26 |
+
# lang (int): Language of the game (0 for Spanish, 1 for English).
|
27 |
+
# model_type (str): Type of the model ("word2vec" or "SentenceTransformer").
|
28 |
+
# Config (DictWrapper): Configuration data for the selected language.
|
29 |
+
# secret_dict (dict): Secret words for the selected language.
|
30 |
+
# secret_list (list): List of secret words for the selected difficulty.
|
31 |
+
# words (list): List of words guessed by the player.
|
32 |
+
# scores (list): List of scores for the guessed words.
|
33 |
+
# win (bool): Indicates if the player has won the game.
|
34 |
+
# n (int): Number of hints given.
|
35 |
+
# recent_hint (int): Counter for recent hints.
|
36 |
+
# f_dev_avg (float): Moving average of the tendency slope.
|
37 |
+
# last_hint (int): Index of the last hint given.
|
38 |
+
# difficulty (int): Difficulty level of the game.
|
39 |
+
# """
|
40 |
+
|
41 |
+
import os
|
42 |
+
import sys
|
43 |
+
import json
|
44 |
+
import uuid
|
45 |
+
import random
|
46 |
+
from datetime import datetime
|
47 |
+
import time
|
48 |
+
from tqdm import tqdm
|
49 |
+
import numpy as np
|
50 |
+
from gensim.models import KeyedVectors
|
51 |
+
from hints import curiosity, hint
|
52 |
+
from tracking import (
|
53 |
+
calculate_moving_average,
|
54 |
+
calculate_tendency_slope,
|
55 |
+
)
|
56 |
+
from sentence_transformers import SentenceTransformer, util
|
57 |
+
import warnings
|
58 |
+
from huggingface_hub import snapshot_download
|
59 |
+
|
60 |
+
|
61 |
+
warnings.filterwarnings(action="ignore", category=UserWarning, module="gensim")
|
62 |
+
|
63 |
+
|
64 |
+
class Model_class:
|
65 |
+
|
66 |
+
base_path = os.path.dirname(os.path.abspath(__file__))
|
67 |
+
|
68 |
+
def __init__(self, lang=0, model_type="SentenceTransformer"):
|
69 |
+
|
70 |
+
if model_type == "SentenceTransformer":
|
71 |
+
repo_url = "Jsevisal/strans_models"
|
72 |
+
|
73 |
+
else:
|
74 |
+
repo_url = "Jsevisal/w2v_models"
|
75 |
+
|
76 |
+
# Check if the model exists, clone it if it doesn't
|
77 |
+
if not os.path.exists(
|
78 |
+
os.path.join(self.base_path, "config/strans_models/")
|
79 |
+
) or not os.path.exists(os.path.join(self.base_path, "config/w2v_models/")):
|
80 |
+
model_path = snapshot_download(repo_id=repo_url)
|
81 |
+
|
82 |
+
if lang == 1:
|
83 |
+
if model_type == "word2vec":
|
84 |
+
self.model = KeyedVectors.load(
|
85 |
+
os.path.join(model_path, "eng_w2v_model"),
|
86 |
+
mmap="r",
|
87 |
+
)
|
88 |
+
elif model_type == "SentenceTransformer":
|
89 |
+
self.model = KeyedVectors.load(
|
90 |
+
os.path.join(model_path, "eng_strans_model"),
|
91 |
+
mmap="r",
|
92 |
+
)
|
93 |
+
|
94 |
+
else:
|
95 |
+
if model_type == "word2vec":
|
96 |
+
self.model = KeyedVectors.load(
|
97 |
+
os.path.join(model_path, "esp_w2v_model"),
|
98 |
+
mmap="r",
|
99 |
+
)
|
100 |
+
|
101 |
+
elif model_type == "SentenceTransformer":
|
102 |
+
self.model = KeyedVectors.load(
|
103 |
+
os.path.join(model_path, "esp_strans_model"),
|
104 |
+
mmap="r",
|
105 |
+
)
|
106 |
+
|
107 |
+
self.model_st = SentenceTransformer(
|
108 |
+
"sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
|
109 |
+
)
|
110 |
+
|
111 |
+
|
112 |
+
# Define the class Semantrix
|
113 |
+
class Semantrix:
|
114 |
+
|
115 |
+
# Define the paths for the configuration files and the data
|
116 |
+
base_path = os.path.dirname(os.path.abspath(__file__))
|
117 |
+
config_file_path = os.path.join(base_path, "config/")
|
118 |
+
config_file_path = os.path.join(base_path, "config/lang.json")
|
119 |
+
secret_file_path = os.path.join(base_path, "config/secret.json")
|
120 |
+
data_path = os.path.join(base_path, "data/")
|
121 |
+
|
122 |
+
# Define the class DictWrapper to store the configuration data
|
123 |
+
class DictWrapper:
|
124 |
+
def __init__(self, data_dict):
|
125 |
+
self.__dict__.update(data_dict)
|
126 |
+
|
127 |
+
# Define the constructor of the class which loads the configuration files and initializes the class variables depending on the language parameter and the model type
|
128 |
+
def __init__(self, lang=0, model_type="SentenceTransformer", session_hash=None):
|
129 |
+
|
130 |
+
# Load the configuration files
|
131 |
+
with open(self.config_file_path, "r") as file:
|
132 |
+
self.Config_full = json.load(file)
|
133 |
+
|
134 |
+
# Load the secret file where the secret words are stored
|
135 |
+
with open(self.secret_file_path, "r") as file:
|
136 |
+
self.secret = json.load(file)
|
137 |
+
|
138 |
+
# Set the language of the game
|
139 |
+
self.lang = lang
|
140 |
+
|
141 |
+
self.session_id = str(uuid.uuid4().hex)
|
142 |
+
|
143 |
+
# Set the model type
|
144 |
+
self.model_type = model_type
|
145 |
+
|
146 |
+
self.session_hash = session_hash
|
147 |
+
self.ranking_path = "rankings/ranking_" + str(self.session_hash) + ".txt"
|
148 |
+
|
149 |
+
self.ranking_data = []
|
150 |
+
self.ranking_msg = ""
|
151 |
+
|
152 |
+
if lang == 1:
|
153 |
+
self.Config = self.DictWrapper(self.Config_full["ENG"]["Game"])
|
154 |
+
self.secret_dict = self.secret["ENG"]
|
155 |
+
self.secret_list = self.secret_dict["basic"]
|
156 |
+
else:
|
157 |
+
self.Config = self.DictWrapper(self.Config_full["SPA"]["Game"])
|
158 |
+
self.secret_dict = self.secret["SPA"]
|
159 |
+
self.secret_list = self.secret_dict["basic"]
|
160 |
+
|
161 |
+
# Create the ranking file
|
162 |
+
with open(self.data_path + self.ranking_path, "w+") as file:
|
163 |
+
file.write("---------------------------")
|
164 |
+
|
165 |
+
def reset_game(self):
|
166 |
+
self.session_id = str(uuid.uuid4().hex)
|
167 |
+
# Load the secret file where the secret words are stored
|
168 |
+
with open(self.secret_file_path, "r") as file:
|
169 |
+
self.secret = json.load(file)
|
170 |
+
self.secret_dict = self.secret["SPA"]
|
171 |
+
self.secret_list = self.secret_dict["basic"]
|
172 |
+
|
173 |
+
def generate_gensim_model(self, model_class, batch_size=32):
|
174 |
+
from tqdm import tqdm
|
175 |
+
|
176 |
+
self.model_trans = KeyedVectors(768)
|
177 |
+
|
178 |
+
self.model_trans.init_sims(replace=True)
|
179 |
+
words = list(model_class.model.key_to_index.keys())
|
180 |
+
num_batches = (
|
181 |
+
len(words) + batch_size - 1
|
182 |
+
) // batch_size # Calculate the number of batches
|
183 |
+
|
184 |
+
for batch_index in tqdm(range(num_batches)):
|
185 |
+
# Get the batch of words
|
186 |
+
start_index = batch_index * batch_size
|
187 |
+
end_index = min(start_index + batch_size, len(words))
|
188 |
+
batch_words = words[start_index:end_index]
|
189 |
+
|
190 |
+
# Encode the batch of words
|
191 |
+
encoded_vectors = model_class.model_st.encode(
|
192 |
+
batch_words,
|
193 |
+
convert_to_tensor=True,
|
194 |
+
prompt="Encuentra el valor semántico de la palabra: ",
|
195 |
+
).tolist()
|
196 |
+
|
197 |
+
# # Add vectors to the model
|
198 |
+
self.model_trans.add_vectors(batch_words, encoded_vectors)
|
199 |
+
|
200 |
+
self.model_trans.save("config/strans_models/esp_strans_model_prompt")
|
201 |
+
|
202 |
+
# Define the function to prepare the game with the selected difficulty
|
203 |
+
def prepare_game(self, secret_word_used, difficulty):
|
204 |
+
|
205 |
+
# Set the secret list depending on the difficulty
|
206 |
+
self.secret = self.secret_list[secret_word_used]
|
207 |
+
self.secret = self.secret.lower()
|
208 |
+
|
209 |
+
self.init_time = time.time()
|
210 |
+
|
211 |
+
# Store the secret word in the words list
|
212 |
+
self.words = [self.Config.secret_word]
|
213 |
+
|
214 |
+
# Store the score in the scores list
|
215 |
+
self.scores = [10]
|
216 |
+
|
217 |
+
# Initialize the game variables
|
218 |
+
self.win = False
|
219 |
+
self.n = 0
|
220 |
+
self.recent_hint = 0
|
221 |
+
self.f_dev_avg = 0
|
222 |
+
self.last_hint = -1
|
223 |
+
self.difficulty = difficulty
|
224 |
+
|
225 |
+
# Set the number of hints depending on the difficulty
|
226 |
+
if self.difficulty == 1:
|
227 |
+
self.n = 3
|
228 |
+
|
229 |
+
# Define the function to generate the ranking file
|
230 |
+
def gen_rank(self, repeated):
|
231 |
+
ascending_indices = np.argsort(self.scores)
|
232 |
+
descending_indices = list(ascending_indices[::-1])
|
233 |
+
self.ranking_data.clear()
|
234 |
+
k = len(self.words) - 1
|
235 |
+
if repeated != -1:
|
236 |
+
k = repeated
|
237 |
+
|
238 |
+
self.ranking_data.append(["#" + str(k), self.words[k], self.scores[k]])
|
239 |
+
|
240 |
+
self.ranking_data.append("---------------------------")
|
241 |
+
for i in descending_indices:
|
242 |
+
if i == 0:
|
243 |
+
continue
|
244 |
+
self.ranking_data.append(["#" + str(i), self.words[i], self.scores[i]])
|
245 |
+
|
246 |
+
with open(self.data_path + self.ranking_path, "w+") as file:
|
247 |
+
for item in self.ranking_data:
|
248 |
+
file.write("%s\n" % item)
|
249 |
+
|
250 |
+
self.ranking_msg = ""
|
251 |
+
for item in self.ranking_data:
|
252 |
+
self.ranking_msg += f"{item}\n"
|
253 |
+
|
254 |
+
# Define the function to play the game with the selected word
|
255 |
+
def play_game(self, word, model_class):
|
256 |
+
|
257 |
+
# Convert the word to lowercase
|
258 |
+
word = word.lower().strip()
|
259 |
+
|
260 |
+
# Check if the user wants to give up
|
261 |
+
if word == "give_up":
|
262 |
+
text = (
|
263 |
+
"[lose]"
|
264 |
+
+ str(self.Config.Feedback_9)
|
265 |
+
+ self.secret
|
266 |
+
+ "\n\n"
|
267 |
+
+ self.Config.Feedback_10
|
268 |
+
)
|
269 |
+
return text
|
270 |
+
|
271 |
+
# Check if the word is repeated
|
272 |
+
if word in self.words:
|
273 |
+
repeated = self.words.index(word)
|
274 |
+
else:
|
275 |
+
repeated = -1
|
276 |
+
self.words.append(word)
|
277 |
+
|
278 |
+
# Check if the word is in the model already
|
279 |
+
if word not in model_class.model.key_to_index.keys():
|
280 |
+
# If the word is not in the model, remove it from the words list and provide feedback
|
281 |
+
self.words.pop(len(self.words) - 1)
|
282 |
+
feedback = (
|
283 |
+
"I don't know that word. Try another word."
|
284 |
+
if self.lang == 1
|
285 |
+
else "No conozco esa palabra. Prueba con otra palabra."
|
286 |
+
)
|
287 |
+
|
288 |
+
feedback += "[rank]" + self.ranking_msg if len(self.words) > 1 else "\n\n"
|
289 |
+
return feedback
|
290 |
+
|
291 |
+
similarity = model_class.model.similarity(self.secret, word)
|
292 |
+
|
293 |
+
if self.model_type == "word2vec":
|
294 |
+
score = np.round(similarity * 10, 2)
|
295 |
+
else:
|
296 |
+
# log_similarity = np.log10(similarity * 10) if np.any(similarity > 0) else 0
|
297 |
+
# score = np.round(
|
298 |
+
# np.interp(
|
299 |
+
# log_similarity,
|
300 |
+
# [0, np.log10(10)],
|
301 |
+
# [0, 10],
|
302 |
+
# ),
|
303 |
+
# 2,
|
304 |
+
# )
|
305 |
+
score = np.round(similarity * 10, 2)
|
306 |
+
|
307 |
+
# Remove the word from the score list if it is repeated
|
308 |
+
if repeated == -1:
|
309 |
+
self.scores.append(score)
|
310 |
+
|
311 |
+
# Generate the feedback message depending on the score
|
312 |
+
if score <= 2.5:
|
313 |
+
feedback = self.Config.Feedback_0 + str(score)
|
314 |
+
elif score > 2.5 and score <= 6.0:
|
315 |
+
feedback = self.Config.Feedback_1 + str(score)
|
316 |
+
elif score > 6.0 and score <= 7.0:
|
317 |
+
feedback = self.Config.Feedback_2 + str(score)
|
318 |
+
elif score > 7.0 and score <= 8:
|
319 |
+
feedback = self.Config.Feedback_3 + str(score)
|
320 |
+
elif score > 8 and score <= 9.0:
|
321 |
+
feedback = self.Config.Feedback_4 + str(score)
|
322 |
+
elif score > 9.0 and score < 10.0:
|
323 |
+
feedback = self.Config.Feedback_5 + str(score)
|
324 |
+
# If the score is 10, the user wins the game
|
325 |
+
else:
|
326 |
+
self.win = True
|
327 |
+
feedback = "[win]" + self.Config.Feedback_8
|
328 |
+
self.words[0] = self.secret
|
329 |
+
self.words.pop(len(self.words) - 1)
|
330 |
+
self.scores.pop(len(self.scores) - 1)
|
331 |
+
|
332 |
+
# Generate the feedback message depending on the score and the previous score
|
333 |
+
if score > self.scores[len(self.scores) - 2] and self.win == False:
|
334 |
+
feedback += "\n" + self.Config.Feedback_6
|
335 |
+
elif score < self.scores[len(self.scores) - 2] and self.win == False:
|
336 |
+
feedback += "\n" + self.Config.Feedback_7
|
337 |
+
|
338 |
+
## Hint generation
|
339 |
+
# If the difficulty is not 4, calculate the moving average of the scores and the tendency slope
|
340 |
+
if self.difficulty != 4 and len(self.scores) > 1:
|
341 |
+
mov_avg = calculate_moving_average(self.scores[1:], 5)
|
342 |
+
|
343 |
+
# If the moving average has more than one element and the user has not won yet, calculate the tendency slope and the moving average of the tendency slope
|
344 |
+
if len(mov_avg) > 1 and self.win == False:
|
345 |
+
f_dev = calculate_tendency_slope(mov_avg)
|
346 |
+
f_dev_avg = calculate_moving_average(f_dev, 3)
|
347 |
+
|
348 |
+
# If the tendency slope is negative and the hint has not been given recently (at least three rounds earlier), generate a hint
|
349 |
+
if f_dev_avg[len(f_dev_avg) - 1] < 0 and self.recent_hint == 0:
|
350 |
+
|
351 |
+
# Generate a random hint intro from the hint list
|
352 |
+
i = random.randint(0, len(self.Config.hint_intro) - 1)
|
353 |
+
feedback += "\n\n[hint]" + self.Config.hint_intro[i]
|
354 |
+
|
355 |
+
# Generate a dynamic hint
|
356 |
+
hint_text, self.n, self.last_hint = hint(
|
357 |
+
self.secret,
|
358 |
+
self.n,
|
359 |
+
model_class.model_st,
|
360 |
+
self.last_hint,
|
361 |
+
self.lang,
|
362 |
+
(
|
363 |
+
self.DictWrapper(self.Config_full["ENG"]["Hint"])
|
364 |
+
if self.lang == 1
|
365 |
+
else self.DictWrapper(self.Config_full["SPA"]["Hint"])
|
366 |
+
),
|
367 |
+
)
|
368 |
+
feedback += "\n" + hint_text
|
369 |
+
self.recent_hint = 3
|
370 |
+
|
371 |
+
if self.recent_hint != 0:
|
372 |
+
self.recent_hint -= 1
|
373 |
+
|
374 |
+
# Generate the ranking file
|
375 |
+
self.gen_rank(repeated)
|
376 |
+
|
377 |
+
# Add the ranking file to the feedback message
|
378 |
+
feedback += "[rank]" + self.ranking_msg if len(self.words) > 1 else "\n\n"
|
379 |
+
|
380 |
+
# Return the feedback message
|
381 |
+
return feedback
|
382 |
+
|
383 |
+
# Define the function to generate a curiosity hint once the game is over
|
384 |
+
def curiosity(self):
|
385 |
+
|
386 |
+
# Generate a curiosity aboyt the secret word
|
387 |
+
feedback = curiosity(
|
388 |
+
self.secret,
|
389 |
+
(
|
390 |
+
self.DictWrapper(self.Config_full["ENG"]["Hint"])
|
391 |
+
if self.lang == 1
|
392 |
+
else self.DictWrapper(self.Config_full["SPA"]["Hint"])
|
393 |
+
),
|
394 |
+
)
|
395 |
+
|
396 |
+
# Save the ranking file with the plays of the user if the user wins
|
397 |
+
with open(self.data_path + self.ranking_path, "r") as original_file:
|
398 |
+
file_content = original_file.readlines()[2:]
|
399 |
+
new_file_name = f"{self.session_id}-{self.secret}.json"
|
400 |
+
play_data = {
|
401 |
+
"session_id": self.session_id,
|
402 |
+
"datetime": str(datetime.now()),
|
403 |
+
"time": time.time() - self.init_time,
|
404 |
+
"data": file_content,
|
405 |
+
"win": self.win,
|
406 |
+
"secret": self.secret,
|
407 |
+
"number_of_hints": self.n,
|
408 |
+
}
|
409 |
+
|
410 |
+
with open(self.data_path + "plays/" + new_file_name, "w") as new_file:
|
411 |
+
json.dump(play_data, new_file, indent=4)
|
412 |
+
|
413 |
+
# Return the feedback message
|
414 |
+
return feedback
|
415 |
+
|
416 |
+
def get_session_id(self):
|
417 |
+
return self.session_id
|
gen_model.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# %%
|
2 |
+
from game import Semantrix, Model_class
|
3 |
+
|
4 |
+
# %%
|
5 |
+
model = "word2vec"
|
6 |
+
model_class = Model_class(lang=0, model_type=model)
|
7 |
+
|
8 |
+
sem = Semantrix(lang=1, model_type="SentenceTransformer")
|
9 |
+
|
10 |
+
# %%
|
11 |
+
sem.generate_gensim_model(batch_size=128, model_class=model_class)
|
12 |
+
|
13 |
+
# %%
|
hints.py
ADDED
@@ -0,0 +1,286 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# """
|
2 |
+
# This module provides functions to generate dynamic hints and curiosities about a secret word using llms.
|
3 |
+
|
4 |
+
# Functions:
|
5 |
+
# hint(secret, n, model, last_hint, lang, Config):
|
6 |
+
# Generates a dynamic hint based on the secret word and the number of hints given.
|
7 |
+
# Parameters:
|
8 |
+
# secret (str): The secret word.
|
9 |
+
# n (int): The number of hints already given.
|
10 |
+
# model: The sentence transformer model used for encoding.
|
11 |
+
# last_hint (int): The index of the last hint given.
|
12 |
+
# lang (int): The language code (0 for Spanish, 1 for English).
|
13 |
+
# Config: Configuration object containing hint templates.
|
14 |
+
# Returns:
|
15 |
+
# tuple: A tuple containing the generated hint (str), the updated number of hints (int), and the index of the last hint given (int).
|
16 |
+
|
17 |
+
# curiosity(secret, Config):
|
18 |
+
# Generates a curiosity about the secret word.
|
19 |
+
# Parameters:
|
20 |
+
# secret (str): The secret word.
|
21 |
+
# Config: Configuration object containing the curiosity template.
|
22 |
+
# Returns:
|
23 |
+
# str: The generated curiosity.
|
24 |
+
|
25 |
+
# ireplace(old, new, text):
|
26 |
+
# Replaces all occurrences of a substring in a string, case-insensitively.
|
27 |
+
# Parameters:
|
28 |
+
# old (str): The substring to be replaced.
|
29 |
+
# new (str): The substring to replace with.
|
30 |
+
# text (str): The original string.
|
31 |
+
# Returns:
|
32 |
+
# str: The modified string with all occurrences of the old substring replaced by the new substring.
|
33 |
+
# """
|
34 |
+
|
35 |
+
import random
|
36 |
+
import openai
|
37 |
+
from sentence_transformers import util
|
38 |
+
|
39 |
+
gpt_model = "gpt-3.5-turbo"
|
40 |
+
|
41 |
+
|
42 |
+
# Dynamic hint function that returns a hint based on the secret word and the number of hints given
|
43 |
+
def hint(secret, n, model, last_hint, lang, Config):
|
44 |
+
|
45 |
+
# Initialize hint variable
|
46 |
+
hint = ""
|
47 |
+
|
48 |
+
# Advanced hints
|
49 |
+
if n >= 3:
|
50 |
+
j = random.randint(0, 2)
|
51 |
+
while j == last_hint:
|
52 |
+
j = random.randint(0, 2)
|
53 |
+
|
54 |
+
# Advanced hint 1: Definition of the secret word
|
55 |
+
if j == 0:
|
56 |
+
response = openai.chat.completions.create(
|
57 |
+
model=gpt_model,
|
58 |
+
messages=[
|
59 |
+
{
|
60 |
+
"role": "user",
|
61 |
+
"content": Config.hint_0_0 # type: ignore
|
62 |
+
+ secret
|
63 |
+
+ Config.hint_0_1 # type: ignore
|
64 |
+
+ secret
|
65 |
+
+ Config.hint_0_2, # type: ignore
|
66 |
+
}
|
67 |
+
],
|
68 |
+
temperature=1,
|
69 |
+
max_tokens=256,
|
70 |
+
top_p=1,
|
71 |
+
frequency_penalty=0.5,
|
72 |
+
presence_penalty=0,
|
73 |
+
)
|
74 |
+
output = str(response.choices[0].message.content)
|
75 |
+
output = output.replace('"', "").replace("'", "")
|
76 |
+
|
77 |
+
# Replace the secret word with "La palabra secreta" or "The secret word" just in case the model uses the secret word in the definition
|
78 |
+
if lang == 0:
|
79 |
+
output = ireplace("la " + secret, "La palabra secreta", output)
|
80 |
+
output = ireplace("las " + secret, "La palabra secreta", output)
|
81 |
+
output = ireplace("el " + secret, "La palabra secreta", output)
|
82 |
+
output = ireplace("los " + secret, "La palabra secreta", output)
|
83 |
+
output = ireplace("un " + secret, "La palabra secreta", output)
|
84 |
+
output = ireplace("una " + secret, "La palabra secreta", output)
|
85 |
+
output = ireplace("unos " + secret, "La palabra secreta", output)
|
86 |
+
output = ireplace("unas " + secret, "La palabra secreta", output)
|
87 |
+
elif lang == 1:
|
88 |
+
output = ireplace("the " + secret, "The secret word", output)
|
89 |
+
output = ireplace("a " + secret, "The secret word", output)
|
90 |
+
|
91 |
+
hint += Config.hint_0_3 + output # type: ignore
|
92 |
+
last_hint = 0
|
93 |
+
|
94 |
+
# Advanced hint 2: Representation of the secret word with emojis
|
95 |
+
elif j == 1:
|
96 |
+
response = openai.chat.completions.create(
|
97 |
+
model=gpt_model,
|
98 |
+
messages=[
|
99 |
+
{
|
100 |
+
"role": "user",
|
101 |
+
"content": Config.hint_1_0 + secret + Config.hint_1_1, # type: ignore
|
102 |
+
}
|
103 |
+
],
|
104 |
+
temperature=1,
|
105 |
+
max_tokens=256,
|
106 |
+
top_p=1,
|
107 |
+
frequency_penalty=0,
|
108 |
+
presence_penalty=0,
|
109 |
+
)
|
110 |
+
output = str(response.choices[0].message.content)
|
111 |
+
hint += Config.hint_1_2 + output # type: ignore
|
112 |
+
last_hint = 1
|
113 |
+
|
114 |
+
# Advanced hint 3: Poem about the secret word
|
115 |
+
elif j == 2:
|
116 |
+
response = openai.chat.completions.create(
|
117 |
+
model=gpt_model,
|
118 |
+
messages=[
|
119 |
+
{
|
120 |
+
"role": "user",
|
121 |
+
"content": Config.hint_2_0 + secret + Config.hint_2_1, # type: ignore
|
122 |
+
}
|
123 |
+
],
|
124 |
+
temperature=1,
|
125 |
+
max_tokens=256,
|
126 |
+
top_p=1,
|
127 |
+
frequency_penalty=0,
|
128 |
+
presence_penalty=0,
|
129 |
+
)
|
130 |
+
output = str(response.choices[0].message.content)
|
131 |
+
|
132 |
+
hint += Config.hint_2_2 + output # type: ignore
|
133 |
+
|
134 |
+
last_hint = 2
|
135 |
+
# Initial hints
|
136 |
+
else:
|
137 |
+
j = random.randint(3, 4)
|
138 |
+
while j == last_hint:
|
139 |
+
j = random.randint(3, 4)
|
140 |
+
|
141 |
+
# Initial hint 1: Rank of four words related to the secret word
|
142 |
+
if j == 3:
|
143 |
+
words = []
|
144 |
+
response = openai.chat.completions.create(
|
145 |
+
model=gpt_model,
|
146 |
+
messages=[
|
147 |
+
{
|
148 |
+
"role": "user",
|
149 |
+
"content": Config.hint_3_0, # type: ignore
|
150 |
+
}
|
151 |
+
],
|
152 |
+
temperature=1.25,
|
153 |
+
max_tokens=256,
|
154 |
+
top_p=1,
|
155 |
+
frequency_penalty=0,
|
156 |
+
presence_penalty=0,
|
157 |
+
)
|
158 |
+
output = str(response.choices[0].message.content)
|
159 |
+
output = (output.replace(" ", "").replace(".", "")).lower()
|
160 |
+
words.extend(output.strip().split(","))
|
161 |
+
response = openai.chat.completions.create(
|
162 |
+
model=gpt_model,
|
163 |
+
messages=[
|
164 |
+
{
|
165 |
+
"role": "user",
|
166 |
+
"content": Config.hint_3_1 # type: ignore
|
167 |
+
+ secret
|
168 |
+
+ Config.hint_3_2, # type: ignore
|
169 |
+
}
|
170 |
+
],
|
171 |
+
temperature=1.1,
|
172 |
+
max_tokens=256,
|
173 |
+
top_p=1,
|
174 |
+
frequency_penalty=0,
|
175 |
+
presence_penalty=0,
|
176 |
+
)
|
177 |
+
output = str(response.choices[0].message.content)
|
178 |
+
output = (output.replace(".", "")).lower()
|
179 |
+
words.append(output) # type: ignore
|
180 |
+
random.shuffle(words)
|
181 |
+
sentences1 = [secret, secret, secret, secret]
|
182 |
+
sentences2 = words
|
183 |
+
embeddings1 = model.encode(sentences1, convert_to_tensor=True)
|
184 |
+
embeddings2 = model.encode(sentences2, convert_to_tensor=True)
|
185 |
+
|
186 |
+
cosine_scores = util.cos_sim(embeddings1, embeddings2)
|
187 |
+
scores = cosine_scores[0].tolist()
|
188 |
+
sum_scores = sum(scores)
|
189 |
+
normalized_scores = [round(score * 100 / sum_scores, 1) for score in scores]
|
190 |
+
|
191 |
+
hint += Config.hint_3_3 # type: ignore
|
192 |
+
|
193 |
+
max_len = -1
|
194 |
+
for ele in words:
|
195 |
+
if len(ele) > max_len:
|
196 |
+
max_len = len(ele)
|
197 |
+
longest_word = ele
|
198 |
+
|
199 |
+
for i in range(len(words)):
|
200 |
+
|
201 |
+
word_hint = words[i].ljust(len(longest_word) + 1)
|
202 |
+
hint += (
|
203 |
+
# word_hint[: len(longest_word)]
|
204 |
+
word_hint
|
205 |
+
+ "|"
|
206 |
+
+ ("🟩") * round(normalized_scores[i] * 0.2)
|
207 |
+
+ " "
|
208 |
+
+ str(normalized_scores[i])
|
209 |
+
+ "%\n"
|
210 |
+
)
|
211 |
+
last_hint = 3
|
212 |
+
|
213 |
+
# Initial hint 2: Film representation with emojis with the secret word
|
214 |
+
elif j == 4:
|
215 |
+
response = openai.chat.completions.create(
|
216 |
+
model=gpt_model,
|
217 |
+
messages=[
|
218 |
+
{
|
219 |
+
"role": "user",
|
220 |
+
"content": Config.hint_4_0 # type: ignore
|
221 |
+
+ secret
|
222 |
+
+ Config.hint_4_1, # type: ignore
|
223 |
+
}
|
224 |
+
],
|
225 |
+
temperature=1,
|
226 |
+
max_tokens=256,
|
227 |
+
top_p=1,
|
228 |
+
frequency_penalty=0,
|
229 |
+
presence_penalty=0,
|
230 |
+
)
|
231 |
+
film_title = str(response.choices[0].message.content).replace('"', "")
|
232 |
+
response = openai.chat.completions.create(
|
233 |
+
model=gpt_model,
|
234 |
+
messages=[
|
235 |
+
{
|
236 |
+
"role": "user",
|
237 |
+
"content": Config.hint_4_2 # type: ignore
|
238 |
+
+ film_title
|
239 |
+
+ Config.hint_4_3, # type: ignore
|
240 |
+
}
|
241 |
+
],
|
242 |
+
temperature=1,
|
243 |
+
max_tokens=256,
|
244 |
+
top_p=1,
|
245 |
+
frequency_penalty=0,
|
246 |
+
presence_penalty=0,
|
247 |
+
)
|
248 |
+
output = str(response.choices[0].message.content)
|
249 |
+
hint += Config.hint_4_4 + "\n" + output # type: ignore
|
250 |
+
last_hint = 4
|
251 |
+
|
252 |
+
return hint, n + 1, last_hint
|
253 |
+
|
254 |
+
|
255 |
+
# Tell a curiosity about the secret word
|
256 |
+
def curiosity(secret, Config):
|
257 |
+
|
258 |
+
response = openai.chat.completions.create(
|
259 |
+
model=gpt_model,
|
260 |
+
messages=[
|
261 |
+
{
|
262 |
+
"role": "user",
|
263 |
+
"content": Config.curiosity + secret + '".',
|
264 |
+
}
|
265 |
+
],
|
266 |
+
temperature=1,
|
267 |
+
max_tokens=256,
|
268 |
+
top_p=1,
|
269 |
+
frequency_penalty=0,
|
270 |
+
presence_penalty=0,
|
271 |
+
)
|
272 |
+
output = str(response.choices[0].message.content)
|
273 |
+
|
274 |
+
return output
|
275 |
+
|
276 |
+
|
277 |
+
# Replace all occurrences of a substring in a string
|
278 |
+
def ireplace(old, new, text):
|
279 |
+
idx = 0
|
280 |
+
while idx < len(text):
|
281 |
+
index_l = text.lower().find(old.lower(), idx)
|
282 |
+
if index_l == -1:
|
283 |
+
return text
|
284 |
+
text = text[:index_l] + new + text[index_l + len(old) :]
|
285 |
+
idx = index_l + len(new)
|
286 |
+
return text
|
requirements.txt
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gensim==4.3.0
|
2 |
+
sentence_transformers==3.4.1
|
3 |
+
openai==1.2.3
|
4 |
+
gradio==5.21.0
|
5 |
+
transformers==4.49.0
|
6 |
+
huggingface-hub==0.28.1
|
7 |
+
python-multipart==0.0.18
|
8 |
+
httpx==0.27.2
|
tracking.py
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
|
3 |
+
|
4 |
+
def calculate_moving_average(scores, window_size):
|
5 |
+
# Convert the scores list to a NumPy array for better performance
|
6 |
+
scores_array = np.array(scores)
|
7 |
+
|
8 |
+
# Create an array of rolling windows using the np.convolve function
|
9 |
+
moving_averages = np.around(
|
10 |
+
np.convolve(scores_array, np.ones(window_size) / window_size, mode="valid"), 2
|
11 |
+
)
|
12 |
+
|
13 |
+
return list(moving_averages)
|
14 |
+
|
15 |
+
|
16 |
+
def calculate_tendency_slope(scores):
|
17 |
+
# Convert the scores list to a NumPy array for better performance
|
18 |
+
scores_array = np.array(scores)
|
19 |
+
|
20 |
+
# Calculate the first derivative (slope) of the scores
|
21 |
+
derivative = np.around(np.gradient(scores_array), 2)
|
22 |
+
|
23 |
+
return list(derivative)
|