semantrix_cond2 / game.py
Javierss
Add strip to input
c8fdcfa
# """
# 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 uuid
import random
from datetime import datetime
import time
from tqdm import tqdm
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:
# 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
self.session_id = str(uuid.uuid4().hex)
# Set the model type
self.model_type = model_type
if self.lang == 1:
if self.model_type == "word2vec":
self.model = KeyedVectors.load(
"config/w2v_models/eng_w2v_model", mmap="r"
)
elif self.model_type == "SentenceTransformer":
self.model = KeyedVectors.load(
"config/strans_models/eng_strans_model", mmap="r"
)
self.Config = self.DictWrapper(self.Config_full["ENG"]["Game"])
self.secret_dict = self.secret["ENG"]
self.secret_dict = self.secret["ENG"]
self.secret_list = self.secret_dict["basic"]
else:
if self.model_type == "word2vec":
self.model = KeyedVectors.load(
"config/w2v_models/esp_w2v_model", mmap="r"
)
elif self.model_type == "SentenceTransformer":
self.model = KeyedVectors.load(
"config/strans_models/esp_strans_model", mmap="r"
)
self.Config = self.DictWrapper(self.Config_full["SPA"]["Game"])
self.secret_dict = self.secret["SPA"]
self.secret_list = self.secret_dict["basic"]
self.model_st = SentenceTransformer(
"sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
)
# Create the ranking file
with open(self.data_path + "ranking.txt", "w+") as file:
file.write("---------------------------")
def reset_game(self):
self.session_id = str(uuid.uuid4().hex)
# Load the secret file where the secret words are stored
with open(self.secret_file_path, "r") as file:
self.secret = json.load(file)
self.secret_dict = self.secret["SPA"]
self.secret_list = self.secret_dict["basic"]
# 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 = self.secret_list.pop(0)
self.secret = self.secret.lower()
self.init_time = time.time()
# Store the secret word in the words list
self.words = [self.Config.secret_word]
# Store the score in the scores list
self.scores = [10]
# 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().strip()
# 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():
# If the word is not in the model, 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
similarity = self.model.similarity(self.secret, word)
if self.model_type == "word2vec":
score = round(similarity * 10, 2)
else:
log_similarity = np.log10(similarity * 10) if similarity > 0 else 0
score = round(
np.interp(
log_similarity,
[0, np.log10(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()
# 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"])
),
)
# Save the ranking file with the plays of the user if the user wins
with open(self.data_path + "ranking.txt", "r") as original_file:
file_content = original_file.readlines()[2:]
new_file_name = f"{self.session_id}-{self.secret}.json"
play_data = {
"session_id": self.session_id,
"datetime": str(datetime.now()),
"time": time.time() - self.init_time,
"data": file_content,
"win": self.win,
"secret": self.secret,
"number_of_hints": self.n,
}
with open(self.data_path + "plays/" + new_file_name, "w") as new_file:
json.dump(play_data, new_file, indent=4)
# Return the feedback message
return feedback
def get_session_id(self):
return self.session_id