|
""" |
|
Semantrix Game Module |
|
|
|
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. |
|
|
|
Classes: |
|
Semantrix: A class that implements the Semantrix word guessing game. |
|
Semantrix.DictWrapper: A helper class to wrap configuration dictionaries. |
|
|
|
Functions: |
|
__init__(self, lang=0, model_type="SentenceTransformer"): Initializes the Semantrix game with the specified language and model type. |
|
prepare_game(self, difficulty): Prepares the game with the selected difficulty level. |
|
gen_rank(self, repeated): Generates the ranking file based on the scores. |
|
play_game(self, word): Plays the game with the selected word and returns feedback. |
|
curiosity(self): Generates a curiosity hint about the secret word once the game is over. |
|
|
|
Attributes: |
|
model (KeyedVectors): The word embeddings model. |
|
config_file_path (str): Path to the configuration file. |
|
secret_file_path (str): Path to the secret words file. |
|
data_path (str): Path to the data directory. |
|
Config_full (dict): Full configuration data. |
|
secret (dict): Secret words data. |
|
lang (int): Language of the game (0 for Spanish, 1 for English). |
|
model_type (str): Type of the model ("word2vec" or "SentenceTransformer"). |
|
Config (DictWrapper): Configuration data for the selected language. |
|
secret_dict (dict): Secret words for the selected language. |
|
secret_list (list): List of secret words for the selected difficulty. |
|
words (list): List of words guessed by the player. |
|
scores (list): List of scores for the guessed words. |
|
win (bool): Indicates if the player has won the game. |
|
n (int): Number of hints given. |
|
recent_hint (int): Counter for recent hints. |
|
f_dev_avg (float): Moving average of the tendency slope. |
|
last_hint (int): Index of the last hint given. |
|
difficulty (int): Difficulty level of the game. |
|
""" |
|
|
|
import json |
|
import random |
|
from datetime import datetime |
|
import numpy as np |
|
from gensim.models import KeyedVectors |
|
from hints import curiosity, hint |
|
from tracking import ( |
|
calculate_moving_average, |
|
calculate_tendency_slope, |
|
) |
|
from sentence_transformers import SentenceTransformer |
|
import warnings |
|
|
|
warnings.filterwarnings(action="ignore", category=UserWarning, module="gensim") |
|
|
|
|
|
|
|
class Semantrix: |
|
|
|
|
|
model = KeyedVectors(768) |
|
|
|
|
|
config_file_path = "config/lang.json" |
|
secret_file_path = "config/secret.json" |
|
data_path = "data/" |
|
|
|
|
|
class DictWrapper: |
|
def __init__(self, data_dict): |
|
self.__dict__.update(data_dict) |
|
|
|
|
|
def __init__(self, lang=0, model_type="SentenceTransformer"): |
|
|
|
|
|
with open(self.config_file_path, "r") as file: |
|
self.Config_full = json.load(file) |
|
|
|
|
|
with open(self.secret_file_path, "r") as file: |
|
self.secret = json.load(file) |
|
|
|
|
|
self.lang = lang |
|
|
|
|
|
self.model_type = model_type |
|
|
|
|
|
if self.model_type == "word2vec": |
|
if self.lang == 1: |
|
self.model = KeyedVectors.load( |
|
"config/w2v_models/eng_w2v_model", mmap="r" |
|
) |
|
self.Config = self.DictWrapper(self.Config_full["ENG"]["Game"]) |
|
self.secret_dict = self.secret["ENG"] |
|
else: |
|
self.model = KeyedVectors.load( |
|
"config/w2v_models/esp_w2v_model", mmap="r" |
|
) |
|
self.Config = self.DictWrapper(self.Config_full["SPA"]["Game"]) |
|
self.secret_dict = self.secret["SPA"] |
|
else: |
|
self.model_st = SentenceTransformer( |
|
"sentence-transformers/paraphrase-multilingual-mpnet-base-v2" |
|
) |
|
|
|
|
|
if self.lang == 1: |
|
self.Config = self.DictWrapper(self.Config_full["ENG"]["Game"]) |
|
self.secret_dict = self.secret["ENG"] |
|
else: |
|
self.Config = self.DictWrapper(self.Config_full["SPA"]["Game"]) |
|
self.secret_dict = self.secret["SPA"] |
|
|
|
|
|
with open(self.data_path + "ranking.txt", "w+") as file: |
|
file.write("---------------------------") |
|
|
|
|
|
def prepare_game(self, difficulty): |
|
|
|
|
|
self.secret_list = ( |
|
self.secret_dict["basic"] |
|
if difficulty <= 2 |
|
else self.secret_dict["advanced"] |
|
) |
|
|
|
|
|
self.secret = self.secret_list.pop(random.randint(0, len(self.secret_list) - 1)) |
|
self.secret = self.secret.lower() |
|
|
|
|
|
self.words = [self.Config.secret_word] |
|
|
|
|
|
self.scores = [10] |
|
|
|
|
|
if self.secret not in self.model.key_to_index.keys(): |
|
|
|
|
|
|
|
if self.model_type == "SentenceTransformer": |
|
self.model.add_vector( |
|
self.secret, |
|
self.model_st.encode(self.secret, convert_to_tensor=True).tolist(), |
|
) |
|
|
|
|
|
self.win = False |
|
self.n = 0 |
|
self.recent_hint = 0 |
|
self.f_dev_avg = 0 |
|
self.last_hint = -1 |
|
self.difficulty = difficulty |
|
|
|
|
|
if self.difficulty == 1: |
|
self.n = 3 |
|
|
|
|
|
def gen_rank(self, repeated): |
|
ascending_indices = np.argsort(self.scores) |
|
descending_indices = list(ascending_indices[::-1]) |
|
ranking_data = [] |
|
k = len(self.words) - 1 |
|
if repeated != -1: |
|
k = repeated |
|
|
|
ranking_data.append(["#" + str(k), self.words[k], self.scores[k]]) |
|
|
|
ranking_data.append("---------------------------") |
|
for i in descending_indices: |
|
if i == 0: |
|
continue |
|
ranking_data.append(["#" + str(i), self.words[i], self.scores[i]]) |
|
|
|
with open(self.data_path + "ranking.txt", "w+") as file: |
|
for item in ranking_data: |
|
file.write("%s\n" % item) |
|
|
|
|
|
def play_game(self, word): |
|
|
|
|
|
word = word.lower() |
|
|
|
|
|
if word == "give_up": |
|
text = ( |
|
"[lose]" |
|
+ self.Config.Feedback_9 |
|
+ self.secret |
|
+ "\n\n" |
|
+ self.Config.Feedback_10 |
|
) |
|
return text |
|
|
|
|
|
if word in self.words: |
|
repeated = self.words.index(word) |
|
else: |
|
repeated = -1 |
|
self.words.append(word) |
|
|
|
|
|
if word not in self.model.key_to_index.keys(): |
|
|
|
if self.model_type == "SentenceTransformer": |
|
self.model.add_vector( |
|
word, self.model_st.encode(word, convert_to_tensor=True).tolist() |
|
) |
|
else: |
|
|
|
self.words.pop(len(self.words) - 1) |
|
feedback = ( |
|
"I don't know that word. Try again." |
|
if self.lang == 1 |
|
else "No conozco esa palabra. Inténtalo de nuevo." |
|
) |
|
feedback += ( |
|
"[rank]" + open(self.data_path + "ranking.txt", "r").read() |
|
if len(self.words) > 1 |
|
else "\n\n" |
|
) |
|
return feedback |
|
|
|
|
|
score = round( |
|
np.interp( |
|
np.log(self.model.similarity(self.secret, word) * 10), |
|
[0, np.log(10)], |
|
[0, 10], |
|
), |
|
2, |
|
) |
|
|
|
|
|
if repeated == -1: |
|
self.scores.append(score) |
|
|
|
|
|
if score <= 2.5: |
|
feedback = self.Config.Feedback_0 + str(score) |
|
elif score > 2.5 and score <= 4.0: |
|
feedback = self.Config.Feedback_1 + str(score) |
|
elif score > 4.0 and score <= 6.0: |
|
feedback = self.Config.Feedback_2 + str(score) |
|
elif score > 6.0 and score <= 7.5: |
|
feedback = self.Config.Feedback_3 + str(score) |
|
elif score > 7.5 and score <= 8.0: |
|
feedback = self.Config.Feedback_4 + str(score) |
|
elif score > 8.0 and score < 10.0: |
|
feedback = self.Config.Feedback_5 + str(score) |
|
|
|
else: |
|
self.win = True |
|
feedback = "[win]" + self.Config.Feedback_8 |
|
self.words[0] = self.secret |
|
self.words.pop(len(self.words) - 1) |
|
self.scores.pop(len(self.scores) - 1) |
|
|
|
|
|
if score > self.scores[len(self.scores) - 2] and self.win == False: |
|
feedback += "\n" + self.Config.Feedback_6 |
|
elif score < self.scores[len(self.scores) - 2] and self.win == False: |
|
feedback += "\n" + self.Config.Feedback_7 |
|
|
|
|
|
|
|
if self.difficulty != 4: |
|
mov_avg = calculate_moving_average(self.scores[1:], 5) |
|
|
|
|
|
if len(mov_avg) > 1 and self.win == False: |
|
f_dev = calculate_tendency_slope(mov_avg) |
|
f_dev_avg = calculate_moving_average(f_dev, 3) |
|
|
|
|
|
if f_dev_avg[len(f_dev_avg) - 1] < 0 and self.recent_hint == 0: |
|
|
|
|
|
i = random.randint(0, len(self.Config.hint_intro) - 1) |
|
feedback += "\n\n[hint]" + self.Config.hint_intro[i] |
|
|
|
|
|
hint_text, self.n, self.last_hint = hint( |
|
self.secret, |
|
self.n, |
|
self.model_st, |
|
self.last_hint, |
|
self.lang, |
|
( |
|
self.DictWrapper(self.Config_full["ENG"]["Hint"]) |
|
if self.lang == 1 |
|
else self.DictWrapper(self.Config_full["SPA"]["Hint"]) |
|
), |
|
) |
|
feedback += "\n" + hint_text |
|
self.recent_hint = 3 |
|
|
|
if self.recent_hint != 0: |
|
self.recent_hint -= 1 |
|
|
|
|
|
self.gen_rank(repeated) |
|
|
|
|
|
feedback += "[rank]" + open(self.data_path + "ranking.txt", "r").read() |
|
|
|
|
|
if self.win: |
|
|
|
with open(self.data_path + "ranking.txt", "r") as original_file: |
|
file_content = original_file.readlines() |
|
|
|
new_file_name = self.secret + "_" + str(datetime.now()) + ".txt" |
|
|
|
with open(self.data_path + "plays/" + new_file_name, "w+") as new_file: |
|
new_file.writelines(file_content[2:]) |
|
|
|
|
|
return feedback |
|
|
|
|
|
def curiosity(self): |
|
|
|
|
|
feedback = curiosity( |
|
self.secret, |
|
( |
|
self.DictWrapper(self.Config_full["ENG"]["Hint"]) |
|
if self.lang == 1 |
|
else self.DictWrapper(self.Config_full["SPA"]["Hint"]) |
|
), |
|
) |
|
|
|
|
|
return feedback |
|
|