from flask import Flask, request, jsonify, session from gensim.models import KeyedVectors import numpy as np import random import os from flask import send_from_directory app = Flask(__name__) app.secret_key = os.urandom(24) # Utilitaires globaux (en mémoire pour chaque session) game_state = {} TOLERANCE = 1e-4 # Calculer la similarité cosinus entre deux vecteurs def cos(a, b): return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))) def is_valid_word(word, lang, valid_words): 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 @app.route('/init', methods=['POST']) def init_game(): data = request.json first_word = data['first_word'].lower() first_score = float(data['first_score']) / 100 lang = data.get('lang', 'en') if lang == "fr": tol = 1e-2 with open("french_words.txt") as f: valid_words = set(w.strip() for w in f) model_path = "frWac_non_lem_no_postag_no_phrase_200_cbow_cut0.bin" model = KeyedVectors.load_word2vec_format(model_path, binary=True) else: tol = TOLERANCE with open("english_words.txt") as f: valid_words = set(w.strip() for w in f) model_path = "google_news_reduced.kv" model = KeyedVectors.load(model_path) if first_word not in model: return jsonify({'error': 'Mot inconnu du modèle'}), 400 vec1 = model[first_word] all_words = [w for w in model.index_to_key if w != first_word and is_valid_word(w, lang, valid_words)] words = [w for w in all_words if abs(cos(vec1, model[w]) - first_score) < tol] word_indexes = [all_words.index(w) for w in words] session['lang'] = lang session['first_word'] = first_word session['tol'] = tol session['model_path'] = model_path session['word_indexes'] = word_indexes session['history'] = [] # Liste des {word, score/pass, remaining} return jsonify({'count': len(words), 'history': session['history']}) @app.route('/next', methods=['POST']) def next_word(): word_indexes = session.get('word_indexes', []) if not word_indexes: return jsonify({'error': 'Plus de mots'}), 400 # Recalculer all_words à partir du modèle et du mot de départ lang = session.get('lang', 'en') model_path = session.get('model_path') first_word = session.get('first_word') if model_path.endswith('.kv'): model = KeyedVectors.load(model_path) else: model = KeyedVectors.load_word2vec_format(model_path, binary=True) if lang == 'fr': with open("french_words.txt") as f: valid_words = set(w.strip() for w in f) else: with open("english_words.txt") as f: valid_words = set(w.strip() for w in f) all_words = [w for w in model.index_to_key if w != first_word and is_valid_word(w, lang, valid_words)] idx = random.choice(word_indexes) guess = all_words[idx] session['current_guess_idx'] = idx return jsonify({'guess': guess, 'remaining': len(word_indexes), 'history': session.get('history', [])}) @app.route('/score', methods=['POST']) def submit_score(): data = request.json score = data.get('score') if score is None: return jsonify({'error': 'Score manquant'}), 400 try: score = float(score) / 100 except ValueError: return jsonify({'error': 'Score invalide'}), 400 lang = session.get('lang', 'en') tol = session.get('tol', TOLERANCE) model_path = session.get('model_path') word_indexes = session.get('word_indexes', []) current_guess_idx = session.get('current_guess_idx') if current_guess_idx is None: return jsonify({'error': 'Aucun mot courant'}), 400 if model_path.endswith('.kv'): model = KeyedVectors.load(model_path) else: model = KeyedVectors.load_word2vec_format(model_path, binary=True) first_word = session.get('first_word') if lang == 'fr': with open("french_words.txt") as f: valid_words = set(w.strip() for w in f) else: with open("english_words.txt") as f: valid_words = set(w.strip() for w in f) all_words = [w for w in model.index_to_key if w != first_word and is_valid_word(w, lang, valid_words)] guess_word = all_words[current_guess_idx] vec_guess = model[guess_word] new_word_indexes = [i for i in word_indexes if abs(cos(vec_guess, model[all_words[i]]) - score) < tol] session['word_indexes'] = new_word_indexes history = session.get('history', []) history.append({'word': guess_word, 'score': score, 'remaining': len(new_word_indexes)}) session['history'] = history if not new_word_indexes: return jsonify({'result': 'Plus aucun mot possible. Terminé.', 'history': history}) if len(new_word_indexes) == 1: return jsonify({'result': f"Plus qu'un seul mot possible: {all_words[new_word_indexes[0]]}", 'history': history}) return jsonify({'remaining': len(new_word_indexes), 'history': history}) @app.route('/skip', methods=['POST']) def skip_word(): word_indexes = session.get('word_indexes', []) current_guess_idx = session.get('current_guess_idx') if current_guess_idx is None or not word_indexes: return jsonify({'error': 'Aucun mot à passer'}), 400 # Recalculer all_words à partir du modèle et du mot de départ lang = session.get('lang', 'en') model_path = session.get('model_path') first_word = session.get('first_word') if model_path.endswith('.kv'): model = KeyedVectors.load(model_path) else: model = KeyedVectors.load_word2vec_format(model_path, binary=True) if lang == 'fr': with open("french_words.txt") as f: valid_words = set(w.strip() for w in f) else: with open("english_words.txt") as f: valid_words = set(w.strip() for w in f) all_words = [w for w in model.index_to_key if w != first_word and is_valid_word(w, lang, valid_words)] try: word_indexes.remove(current_guess_idx) except ValueError: pass session['word_indexes'] = word_indexes history = session.get('history', []) history.append({'word': all_words[current_guess_idx], 'score': 'pass', 'remaining': len(word_indexes)}) session['history'] = history if not word_indexes: return jsonify({'result': 'Plus aucun mot possible. Terminé.', 'history': history}) return jsonify({'remaining': len(word_indexes), 'history': history}) # Servir index.html à la racine @app.route('/') def root(): return send_from_directory('.', 'index.html') # Servir les fichiers statiques (ex: .txt, .js, .css) @app.route('/') def static_files(filename): return send_from_directory('.', filename) if __name__ == '__main__': app.run(debug=True)