semantrix_cond2 / game.py
Javierss
Add comments to code,Update README.md, Sintezise game.py modules
7c2eafe
raw
history blame
13.8 kB
"""
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")
# Define the class Semantrix
class Semantrix:
# Create empty KeyedVectors model with predefined size where the embeddings will be stored
model = KeyedVectors(768)
# Define the paths for the configuration files and the data
config_file_path = "config/lang.json"
secret_file_path = "config/secret.json"
data_path = "data/"
# Define the class DictWrapper to store the configuration data
class DictWrapper:
def __init__(self, data_dict):
self.__dict__.update(data_dict)
# 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
def __init__(self, lang=0, model_type="SentenceTransformer"):
# Load the configuration files
with open(self.config_file_path, "r") as file:
self.Config_full = json.load(file)
# Load the secret file where the secret words are stored
with open(self.secret_file_path, "r") as file:
self.secret = json.load(file)
# Set the language of the game
self.lang = lang
# Set the model type
self.model_type = model_type
# Load word2vec model if needed
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"
)
# Set the configuration variables depending on the language
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"]
# Create the ranking file
with open(self.data_path + "ranking.txt", "w+") as file:
file.write("---------------------------")
# Define the function to prepare the game with the selected difficulty
def prepare_game(self, difficulty):
# Set the secret list depending on the difficulty
self.secret_list = (
self.secret_dict["basic"]
if difficulty <= 2
else self.secret_dict["advanced"]
)
# Select a random secret word from the secret list
self.secret = self.secret_list.pop(random.randint(0, len(self.secret_list) - 1))
self.secret = self.secret.lower()
# Store the secret word in the words list
self.words = [self.Config.secret_word]
# Store the score in the scores list
self.scores = [10]
# Store the embedding of the secret word in the embeddings dictionary
if self.secret not in self.model.key_to_index.keys():
# Add the secret word to the KeyedVectors model if the model type is SentenceTransformer
# If the model type is word2vec, the secret word is already in the model
if self.model_type == "SentenceTransformer":
self.model.add_vector(
self.secret,
self.model_st.encode(self.secret, convert_to_tensor=True).tolist(),
)
# Initialize the game variables
self.win = False
self.n = 0
self.recent_hint = 0
self.f_dev_avg = 0
self.last_hint = -1
self.difficulty = difficulty
# Set the number of hints depending on the difficulty
if self.difficulty == 1:
self.n = 3
# Define the function to generate the ranking file
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)
# Define the function to play the game with the selected word
def play_game(self, word):
# Convert the word to lowercase
word = word.lower()
# Check if the user wants to give up
if word == "give_up":
text = (
"[lose]"
+ self.Config.Feedback_9
+ self.secret
+ "\n\n"
+ self.Config.Feedback_10
)
return text
# Check if the word is repeated
if word in self.words:
repeated = self.words.index(word)
else:
repeated = -1
self.words.append(word)
# Check if the word is in the model already
if word not in self.model.key_to_index.keys():
# Add the word to the KeyedVectors model if the model type is SentenceTransformer
if self.model_type == "SentenceTransformer":
self.model.add_vector(
word, self.model_st.encode(word, convert_to_tensor=True).tolist()
)
else:
# If the word is not in the model when using word2vec, remove it from the words list and provide feedback
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
# Calculate the score of the word, apply logarithmic scaling, interpolate the score to a range from 0 to 10, and round it to two decimal places
score = round(
np.interp(
np.log(self.model.similarity(self.secret, word) * 10),
[0, np.log(10)],
[0, 10],
),
2,
)
# Remove the word from the score list if it is repeated
if repeated == -1:
self.scores.append(score)
# Generate the feedback message depending on the 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)
# If the score is 10, the user wins the game
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)
# Generate the feedback message depending on the score and the previous score
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
## Hint generation
# If the difficulty is not 4, calculate the moving average of the scores and the tendency slope
if self.difficulty != 4:
mov_avg = calculate_moving_average(self.scores[1:], 5)
# 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
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 the tendency slope is negative and the hint has not been given recently (at least three rounds earlier), generate a hint
if f_dev_avg[len(f_dev_avg) - 1] < 0 and self.recent_hint == 0:
# Generate a random hint intro from the hint list
i = random.randint(0, len(self.Config.hint_intro) - 1)
feedback += "\n\n[hint]" + self.Config.hint_intro[i]
# Generate a dynamic hint
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
# Generate the ranking file
self.gen_rank(repeated)
# Add the ranking file to the feedback message
feedback += "[rank]" + open(self.data_path + "ranking.txt", "r").read()
# Save the ranking file with the plays of the user if the user wins
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 the feedback message
return feedback
# Define the function to generate a curiosity hint once the game is over
def curiosity(self):
# Generate a curiosity aboyt the secret word
feedback = curiosity(
self.secret,
(
self.DictWrapper(self.Config_full["ENG"]["Hint"])
if self.lang == 1
else self.DictWrapper(self.Config_full["SPA"]["Hint"])
),
)
# Return the feedback message
return feedback