Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nerlim #1

Merged
merged 11 commits into from
Feb 15, 2024
Merged
2 changes: 1 addition & 1 deletion tools/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
### Data loading ###

# scale for experience awarded to the user
XP_SCALE: int = 10
XP_PER_LEVEL: int = 10

# Create the user data json if it does not exist
if not os.path.exists(PATH_USER_DATA):
Expand Down
91 changes: 39 additions & 52 deletions tools/linconym.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
ENGLISH_WORDS_DICTS,
GAMEPLAY_DICT,
USER_DATA,
XP_SCALE
XP_PER_LEVEL
)
from tools.basic_tools import (
dichotomy,
Expand Down Expand Up @@ -151,7 +151,7 @@ def find_all_next_words(current_word: str, english_words):
return next_words


def convert_position_to_wordlist(position: tuple, position_to_word_id, words_found):
def convert_position_to_wordlist(position: str, position_to_word_id, words_found):
wordlist = []
for i in range(1, len(position) + 1):
word = words_found[position_to_word_id[position[:i]]]
Expand Down Expand Up @@ -307,18 +307,18 @@ def __init__(self, start_word: str, end_word: str, quest_word: str = None, act_n
self.quest_word: str = quest_word
self.act_name = act_name
self.lvl_name = lvl_name
self.current_position: tuple[int] = (0,) # (0,) is a one-element tuple while (0) is just an int
self.position_to_word_id = {self.current_position: 0} # A dictionary to map a tuple[int] position to the index of its word in the words_found list[str]
self.current_position: str = "0" # Positions are strings of comma-separated integers which indicate a path in the nodes
self.position_to_word_id = {self.current_position: 0} # A dictionary to map a (str) position to the index of its word in the words_found list[str]
self.words_found: list[str] = [start_word]
self.current_word: str = start_word

def get_word(self, position: tuple[int]) -> str:
def get_word(self, position: str) -> str:
"""
Get the word at given position.

Parameters
----------
position : tuple[int]
position : str
Position to use.

Returns
Expand All @@ -329,13 +329,13 @@ def get_word(self, position: tuple[int]) -> str:

return self.words_found[self.position_to_word_id[position]]

def get_word_path(self, position: tuple[int]) -> list[str]:
def get_word_path(self, position: str) -> list[str]:
"""
Get a list of the words forming the path to the given position.

Parameters
----------
position : tuple[int]
position : str
Position to use.

Returns
Expand All @@ -346,18 +346,18 @@ def get_word_path(self, position: tuple[int]) -> list[str]:

word_path: list[str] = []
for i in range(len(position)):
previous_pos: tuple[int] = position[:(i+1)]
previous_pos: str = position[:(i+1)]
LupaDevStudio marked this conversation as resolved.
Show resolved Hide resolved
word_path += [self.get_word(previous_pos)]
return word_path

def get_nb_next_words(self, position: tuple[int]) -> int:
def get_nb_next_words(self, position: str) -> int:
"""
Get the number of words deriving directly from the given position.
In tree jargon, get the number of children of the node corresponding to the input position.

Parameters
----------
position : tuple[int]
position : str
Position to use.

Returns
Expand All @@ -377,46 +377,19 @@ def get_nb_next_words(self, position: tuple[int]) -> int:

return nb_next_words

def change_position(self, new_position: tuple[int]) -> None:
def change_position(self, new_position: str) -> None:
"""
Change the position of the cursor from one word (node) to another.

Parameters
----------
new_position : tuple[int]
new_position : str
New position to set.
"""

self.current_position = new_position
self.current_word = self.get_word(self.current_position)

def is_valid_and_new(self, new_word: str, skip_dictionary_check: bool = False) -> bool:
"""
Checks whether a new word is absent from the WHOLE tree and is a valid successor to the current word.
This is probably not the right method to use to check user input, because if a word is used as a part of a very long path,
then it cannot be used to create a shorter path anymore.

Example: in a level asking to go from "sea" to "shell", the path "sea, seal, sell, shell" is the shortest. However, if the user already
built the path "sea, tea, teal, seal", then they won't be able to create the shortest path anymore: all they can do is pick up from the
word "seal" in their already-way-too-long current path.

Parameters
----------
new_word: str
New word to be checked.
skip_dictionary_check: bool = False
If True, new_word can be valid even if it isn't in any dictionary.

Returns
-------
bool
True if new_word wasn't previously found and is valid, False otherwise.
"""

word_is_valid: bool = is_valid(new_word, self.current_word, skip_dictionary_check)
word_is_new: bool = not(new_word in self.words_found)
return word_is_valid and word_is_new

def is_valid_and_new_in_path(self, new_word: str, skip_dictionary_check: bool = False) -> bool:
LupaDevStudio marked this conversation as resolved.
Show resolved Hide resolved
"""
Checks whether a new word is absent from the path leading to the current word and is a valid successor to the current word.
Expand Down Expand Up @@ -468,27 +441,39 @@ def submit_word(self, new_word: str) -> None:
else:
print("Word not valid")
LupaDevStudio marked this conversation as resolved.
Show resolved Hide resolved

def award_stars_xp(self, nb_words_10k: int, nb_words_300k: int) -> None:
def award_stars_xp(self, solution_10k_exists: bool, nb_words_10k: int, nb_words_300k: int) -> None:
# Only receive stars and xp if the level was completed
if (self.current_word == self.end_word):
solution_found: list[str] = self.get_word_path(self.current_position)

# Stars: one for finishing the level, one for doing as good as the 10k dictionary solution, and one for doing better
nb_stars: int = 1
# Stars
nb_stars: int = 1 # first star is awarded for finishing the level
nb_words_found: int = len(solution_found)
if (nb_words_found >= nb_words_10k):
nb_stars += 1
if (nb_words_found > nb_words_10k):
nb_stars += 1
if (solution_10k_exists): # easy levels have a solution in the 10k dictionary
LupaDevStudio marked this conversation as resolved.
Show resolved Hide resolved
if (nb_words_found >= (1.25)*nb_words_10k):
nb_stars += 1 # second star for doing as well as the 10k dictionary + 25%
if (nb_words_found >= nb_words_10k):
nb_stars += 1 # third star for doing as well as the 10k dictionary
else: # harder levels don't have a solution in the 10k dictionary, and their stars are defined differently
if (nb_words_found >= (1.30)*nb_words_300k):
nb_stars += 1 # second star for doing as well as the 300k dictionary + 30%
if (nb_words_found >= (1.10)*nb_words_300k):
nb_stars += 1 # third star for doing as well as the 300k dictionary + 10%

# xp: get a percentage of a certain constant amount depending on proximity to the 300k dictionary solution...
xp_fraction: float = nb_words_found / nb_words_300k
xp_fraction: float = nb_words_300k / nb_words_found
if (xp_fraction > 1):
xp_fraction = 1.0
# ... and get a bonus for passing through the quest word
quest_word_done: bool = ((self.quest_word != None) and (self.quest_word in solution_found))

# save level progress (TEMP: only for classic mode, should probably make subclasses for classic and daily mode)
### save level progress (TEMP: only for classic mode, should probably make subclasses for classic and daily mode)
# check that the current act has save data
if (not(self.act_name in USER_DATA.classic_mode)):
USER_DATA.classic_mode[self.act_name] = {}
# check that current level has save data
if (not(self.lvl_name in USER_DATA.classic_mode[self.act_name])):
USER_DATA.classic_mode[self.act_name][self.lvl_name] = {}
# save stars
STARS_KEY: str = "nb_stars"
if (STARS_KEY in USER_DATA.classic_mode[self.act_name][self.lvl_name]):
Expand All @@ -505,7 +490,9 @@ def award_stars_xp(self, nb_words_10k: int, nb_words_300k: int) -> None:
USER_DATA.classic_mode[self.act_name][self.lvl_name][NB_WORDS_KEY] = nb_words_found
else:
USER_DATA.classic_mode[self.act_name][self.lvl_name][NB_WORDS_KEY] = nb_words_found
previous_xp_fraction: float = nb_words_previous_best / nb_words_300k
previous_xp_fraction: float = 0.0
if (nb_words_previous_best > 0):
previous_xp_fraction = nb_words_300k / nb_words_previous_best
# save quest word
QUEST_WORD_KEY: str = "quest_word_done"
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Il faudra ajouter ce fameux quest word dans le json contenant les données des levels.

award_quest_word_xp: bool = False
Expand All @@ -518,9 +505,9 @@ def award_stars_xp(self, nb_words_10k: int, nb_words_300k: int) -> None:

# award newly acquired xp
XP_KEY: str = "experience"
USER_DATA.user_profile[XP_KEY] += int((xp_fraction - previous_xp_fraction) * XP_SCALE)
USER_DATA.user_profile[XP_KEY] += int((xp_fraction - previous_xp_fraction) * XP_PER_LEVEL)
if (award_quest_word_xp):
USER_DATA.user_profile[XP_KEY] += XP_SCALE
USER_DATA.user_profile[XP_KEY] += XP_PER_LEVEL

# save changes
USER_DATA.save_changes()
Expand Down
Loading