cemantle_solver/cemantle.py
2026-02-04 08:41:17 +01:00

83 lines
2.7 KiB
Python

import sys
import numpy as np
from gensim.models import KeyedVectors
import random
TOLERANCE = 1e-4 # tolérance sur la similarité cosinus
if len(sys.argv) < 3:
print("Usage: python cemantle.py <mot> <score>")
sys.exit(1)
first_word = sys.argv[1].lower()
first_score = float(sys.argv[2]) / 100
# Détection de la langue
lang = sys.argv[3] if len(sys.argv) > 3 else "en"
if lang == "fr":
TOLERANCE = 1e-2
with open("french_words.txt") as f:
valid_words = set(w.strip() for w in f)
print("Mode français : dictionnaire français chargé.")
model_path = "frWac_non_lem_no_postag_no_phrase_200_cbow_cut0.bin"
print("Chargement du modèle français (frWac cut100)...")
model = KeyedVectors.load_word2vec_format(model_path, binary=True)
else:
with open("english_words.txt") as f:
valid_words = set(w.strip() for w in f)
print("Mode anglais : dictionnaire anglais chargé.")
model_path = "google_news_reduced.kv"
print("Chargement du modèle anglais...")
model = KeyedVectors.load(model_path)
if first_word not in model:
print(f"Mot inconnu du modèle: {first_word}")
sys.exit(1)
# Calculer la similarité cosinus entre deux vecteurs
cos = lambda a, b: np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
def is_valid_word(word):
if lang == "fr":
return word.islower() and all(c.isalpha() for c in word) and word in valid_words and 3 <= len(word) <= 15
else:
return word.islower() and word.isalpha() and word in valid_words and 3 <= len(word) <= 15
# Filtrer les mots à la bonne distance du mot donné, et qui sont valides
vec1 = model[first_word]
words = []
for w in model.index_to_key:
if w == first_word:
continue
if not is_valid_word(w):
continue
sim = cos(vec1, model[w])
if abs(sim - first_score) < TOLERANCE:
words.append(w)
print(f"{len(words)} mots à la distance {first_score*100:.2f} de '{first_word}' (valides)")
while words:
guess = random.choice(words)
print(f"Try: {guess}")
s = input("Score: ")
if s == "n":
words.remove(guess)
print(f"Mot refusé, {len(words)} restants.")
continue
try:
score = float(s) / 100
except ValueError:
print("Entrée invalide, recommencez.")
continue
vec_guess = model[guess]
# Filtrer les mots à la bonne distance du guess
words = [w for w in words if abs(cos(vec_guess, model[w]) - score) < TOLERANCE]
print(f"{len(words)} mots restants à la distance {score*100:.2f} de '{guess}'")
if not words:
print("Plus aucun mot possible. Terminé.")
break
if len(words) == 1:
print(f"Plus qu'un seul mot possible: {words[0]}")
break