From 69411ede1024b519465de4bdac98fb2ecb5cb552 Mon Sep 17 00:00:00 2001 From: feldenr <135638674+feldenr@users.noreply.github.com> Date: Sat, 17 Jan 2026 01:35:37 +0100 Subject: [PATCH] Jeu de test, optimisation correction de bug --- .claude/settings.local.json | 49 +++++- .idea/deploymentTargetSelector.xml | 8 + app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 2 +- app/src/main/assets/question.json | 162 +++++++++++++++++- .../BoideloClassicGameActivity.java | 46 ++++- .../boidelov3/hub/GameSelectionActivity.java | 6 +- .../example/boidelov3/utils/SecureConfig.java | 22 ++- app/src/main/res/values/strings.xml | 2 +- .../games/papelito/PapelitoGameTest.java | 35 +++- 10 files changed, 310 insertions(+), 23 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 2a435d8..7f0c156 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,7 +4,54 @@ "Bash(tree:*)", "Bash(./gradlew:*)", "Bash(dir:*)", - "Bash(timeout:*)" + "Bash(timeout:*)", + "WebSearch", + "Bash(git rm:*)", + "Bash(python:*)", + "Bash(gradlew.bat build:*)", + "Bash(gradlew.bat assembleDebug:*)", + "Bash(cmd.exe /c \"gradlew.bat assembleDebug 2>&1\")", + "Bash(cmd.exe:*)", + "Bash(./gradlew.bat assembleDebug:*)", + "Bash(git add:*)", + "Bash(git commit:*)", + "Bash(git push:*)", + "mcp__plugin_serena_serena__initial_instructions", + "mcp__plugin_serena_serena__list_dir", + "mcp__plugin_serena_serena__activate_project", + "mcp__plugin_serena_serena__get_symbols_overview", + "mcp__plugin_serena_serena__find_symbol", + "mcp__plugin_serena_serena__read_file", + "mcp__plugin_serena_serena__create_text_file", + "mcp__plugin_serena_serena__replace_content", + "Bash(adb:*)", + "mcp__plugin_serena_serena__search_for_pattern", + "Bash(mkdir:*)", + "Bash(.\\\\gradlew.bat:*)", + "Bash(Select-String -Pattern \"error|Error|BUILD|FAILED|SUCCESS\")", + "Bash(Select-Object:*)", + "Bash(rm:*)", + "Bash(findstr /i \"\\\\.java$\")", + "Bash(findstr:*)", + "Bash(.\\\\gradlew.bat test:*)", + "Bash(./gradlew.bat test)", + "Bash(./gradlew.bat test --tests \"*QuestionCategoryTest*\")", + "Bash(./gradlew.bat test:*)", + "Bash(wc:*)", + "Bash(./gradlew.bat:*)", + "mcp__plugin_serena_serena__replace_symbol_body", + "mcp__plugin_serena_serena__think_about_collected_information", + "mcp__plugin_serena_serena__find_file", + "Bash(gradlew test:*)", + "Bash(cat app/build/reports/tests/testDebugUnitTest/index.html)", + "Bash(Select-String:*)", + "Bash(gh pr view:*)", + "Bash(cmd:*)", + "Bash(git restore:*)", + "Bash(git config:*)", + "mcp__plugin_serena_serena__check_onboarding_performed", + "Bash(powershell:*)", + "mcp__plugin_serena_serena__execute_shell_command" ] } } diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index 849be02..3cc0cd3 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,6 +4,14 @@ diff --git a/app/build.gradle b/app/build.gradle index 0da9676..0f7b3b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -52,6 +52,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.2.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.7.0' + testImplementation 'org.robolectric:robolectric:4.11.1' androidTestImplementation 'androidx.test.ext:junit:1.2.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' implementation 'com.squareup.okhttp3:okhttp:4.12.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1196a3a..4c817d4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -72,7 +72,7 @@ android:screenOrientation="portrait" /> - $1 + doit parler avec un pendant manches", + "gorger": 0, + "variante": [ + "accent allemand", + "accent belge", + "accent suisse", + "accent canadien" + ], + "arret": "Plus d'accent !" + }, + { + "id": 168, + "question": "Le/La plus 'marabout' du groupe (celui/celle qui a toujours des remèdes)", + "gorger": 2, + "distribution": false, + "recois": true + }, + { + "id": 169, + "question": "Ceux qui ont déjà été bloqués sur les réseaux sociaux", + "gorger": 3, + "distribution": true, + "recois": true + }, + { + "id": 170, + "question": "Ceux qui ont leur permis de conduire", + "gorger": 1, + "distribution": true, + "recois": true + }, + { + "id": 171, + "question": " doit faire rire en 30 secondes. Si échec : 5 gorgées", + "gorger": 0, + "recois": true + }, + { + "id": 172, + "question": "Les fans de K-Pop", + "gorger": 3, + "distribution": true, + "recois": true + }, + { + "id": 173, + "question": "Ceux qui ont déjà fait du covoiturage avec des inconnus", + "gorger": 2, + "distribution": true, + "recois": true + }, + { + "id": 174, + "question": " doit mimer pendant que les autres devinent. 30 secondes, 3 gorgées par échec", + "gorger": 0, + "variante": [ + "un chat", + "un chien", + "une voiture", + "un téléphone", + "un avion" + ], + "arret": "Fin du mime !" + }, + { + "id": 175, + "question": "Le/La plus 'intello' du groupe (vote à main levée)", + "gorger": 3, + "distribution": false, + "recois": true + }, + { + "id": 176, + "question": "Ceux qui ont déjà participé à un marathon ou semi-marathon", + "gorger": 2, + "distribution": true, + "recois": true + }, + { + "id": 177, + "question": "Jeu du NI : Tous ceux qui ont un sur eux doivent boire 2 gorgées", + "gorger": 0, + "variante": [ + "stylo", + "post-it", + "mèche", + "alliance", + "bracelet" + ], + "arret": "Fin du jeu du NI !" + }, + { + "id": 178, + "question": "Ceux qui ont déjà fait une surprise-party à quelqu'un", + "gorger": 2, + "distribution": true, + "recois": true + }, + { + "id": 179, + "question": " doit compléter chaque phrase de par 'oui chef' pendant manches", + "gorger": 0, + "arret": "Plus de 'oui chef' !" + }, + { + "id": 180, + "question": "Le/La plus 'sportif/sportive' du groupe", + "gorger": 2, + "distribution": false, + "recois": true + }, + { + "id": 181, + "question": "Ceux qui ont un compte sur onlyfans", + "gorger": 4, + "distribution": true, + "recois": true + }, + { + "id": 182, + "question": " doit choisir entre . Le perdant boit 3 gorgées", + "gorger": 0, + "variante": [ + "Instagram et TikTok", + "Netflix et Disney+", + "Pizza et Burger", + "Chat et Chien", + "Montagne et Mer" + ] + }, + { + "id": 183, + "question": "Ceux qui ont déjà fait un regretté post-soirée (message à un ex)", + "gorger": 4, + "distribution": true, + "recois": true + }, + { + "id": 184, + "question": "Le/La plus 'organisé' du groupe", + "gorger": 2, + "distribution": false, + "recois": true + }, + { + "id": 185, + "question": "Ceux qui ont déjà été coincés dans un ascenseur", + "gorger": 3, + "distribution": true, + "recois": true } ] } \ No newline at end of file diff --git a/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicGameActivity.java b/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicGameActivity.java index 2c6bd51..fe36c1d 100644 --- a/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicGameActivity.java +++ b/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicGameActivity.java @@ -22,6 +22,7 @@ import com.example.boidelov3.R; import com.example.boidelov3.data.PlayerStats; import com.example.boidelov3.data.QuestionCategory; import com.example.boidelov3.data.QuestionRepository; +import com.example.boidelov3.game.GameEngine; import com.example.boidelov3.Question; import com.example.boidelov3.Questions; import com.example.boidelov3.utils.ErrorHandler; @@ -65,8 +66,8 @@ public class BoideloClassicGameActivity extends AppCompatActivity { private Map playerStatsMap; // Services - // private SoundManager soundManager; // TODO: Fix SoundManager constructor - // private GameEngine gameEngine; // TODO: Fix GameEngine + private com.example.boidelov3.utils.SoundManager soundManager; + private GameEngine gameEngine; private QuestionRepository questionRepository; // Random @@ -165,8 +166,8 @@ public class BoideloClassicGameActivity extends AppCompatActivity { * Initialise les services */ private void initServices() { - // soundManager = new SoundManager(this); // TODO: Fix SoundManager - // gameEngine = new GameEngine(); // TODO: Fix GameEngine + soundManager = com.example.boidelov3.utils.SoundManager.getInstance(this); + gameEngine = new GameEngine(); questionRepository = new QuestionRepository(this); } @@ -179,7 +180,16 @@ public class BoideloClassicGameActivity extends AppCompatActivity { Questions q = result.getData(); questions = q != null && q.getQuestions() != null ? q.getQuestions() : new ArrayList<>(); } else { + android.util.Log.e("BoideloClassicGame", "Erreur lors du chargement des questions: " + result.getError()); questions = new ArrayList<>(); + + // Afficher une erreur à l'utilisateur + new androidx.appcompat.app.AlertDialog.Builder(this) + .setTitle("Erreur de chargement") + .setMessage("Impossible de charger les questions. Veuillez réinstaller l'application.") + .setPositiveButton("Fermer", (dialog, which) -> finish()) + .setCancelable(false) + .show(); } questionsAvecManches = new ArrayList<>(); } @@ -260,6 +270,9 @@ public class BoideloClassicGameActivity extends AppCompatActivity { */ private void handleGameEnd() { isFinishingGame = true; + + // Sauvegarder les stats avant de terminer + saveGameStats(); // Prépare les stats pour l'activité de fin Intent intent = new Intent(this, EndGameActivity.class); @@ -329,6 +342,8 @@ public class BoideloClassicGameActivity extends AppCompatActivity { */ private void displayNewQuestion() { if (questions == null || questions.isEmpty()) { + android.util.Log.e("BoideloClassicGame", "Aucune question disponible - fin du jeu"); + handleGameEnd(); return; } @@ -372,7 +387,15 @@ public class BoideloClassicGameActivity extends AppCompatActivity { * Affiche la notification de fin de manche */ private void showMancheEndNotification() { - // TODO: Implémenter la notification de fin de manche + if (soundManager != null) { + soundManager.playManche(); + } + + // Vibration pour feedback haptique + BoideloAnimationUtils.triggerErrorHaptic(this); + + // Afficher un message toast pour informer les joueurs + android.widget.Toast.makeText(this, "⏱️ Manche terminée!", android.widget.Toast.LENGTH_SHORT).show(); } /** @@ -958,7 +981,18 @@ public class BoideloClassicGameActivity extends AppCompatActivity { * Sauvegarde les stats du jeu */ private void saveGameStats() { - // TODO: Implémenter la sauvegarde des stats + if (questionRepository != null && playerStatsMap != null) { + // Calculer le total des questions posées + int totalQuestionsPlayed = totalQuestionsAsked; + int playersCount = toutlesjoueurs != null ? toutlesjoueurs.size() : 0; + + // Sauvegarder dans SharedPreferences via le repository + questionRepository.saveGameStats(totalQuestionsPlayed, playersCount); + + android.util.Log.d("BoideloClassicGame", + "Stats sauvegardées: " + totalQuestionsPlayed + " questions, " + + playersCount + " joueurs"); + } } /** diff --git a/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java b/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java index 6f8bd0a..623b17a 100644 --- a/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java +++ b/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java @@ -101,6 +101,8 @@ public class GameSelectionActivity extends AppCompatActivity implements GameAdap public void onItemClick(GameInfo gameInfo) { if (!gameInfo.isAvailable()) { // Afficher un message "Coming soon" + android.widget.Toast.makeText(this, gameInfo.getName() + " - Bientôt disponible!", + android.widget.Toast.LENGTH_SHORT).show(); return; } @@ -115,7 +117,9 @@ public class GameSelectionActivity extends AppCompatActivity implements GameAdap startActivity(new Intent(this, com.example.boidelov3.games.papelito.PapelitoSetupActivity.class)); break; case RULES: - // TODO: Implémenter RulesListActivity + // À implémenter: RulesListActivity + android.widget.Toast.makeText(this, "Règles de jeux - Bientôt disponible!", + android.widget.Toast.LENGTH_SHORT).show(); break; } } diff --git a/app/src/main/java/com/example/boidelov3/utils/SecureConfig.java b/app/src/main/java/com/example/boidelov3/utils/SecureConfig.java index 50c886d..81f8c2d 100644 --- a/app/src/main/java/com/example/boidelov3/utils/SecureConfig.java +++ b/app/src/main/java/com/example/boidelov3/utils/SecureConfig.java @@ -6,7 +6,6 @@ import android.util.Log; import java.security.MessageDigest; import java.security.SecureRandom; -import java.util.Base64; /** * Classe utilitaire pour la gestion sécurisée des clés API et configuration sensible. @@ -188,12 +187,25 @@ public class SecureConfig { * @return Le hash de la clé */ public String hashApiKey(String apiKey) { + if (apiKey == null) { + Log.w(TAG, "Tentative de hasher une clé API null"); + return null; + } + + if (apiKey.trim().isEmpty()) { + Log.w(TAG, "Tentative de hasher une clé API vide"); + return null; + } + try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(apiKey.getBytes("UTF-8")); - return Base64.getEncoder().encodeToString(hash); - } catch (Exception e) { - Log.e(TAG, "Erreur lors du hash de la clé", e); + return android.util.Base64.encodeToString(hash, android.util.Base64.NO_WRAP); + } catch (java.security.NoSuchAlgorithmException e) { + Log.e(TAG, "SHA-256 non disponible sur cet appareil - hash impossible", e); + return null; + } catch (java.io.UnsupportedEncodingException e) { + Log.e(TAG, "UTF-8 encoding non supporté - impossible", e); return null; } } @@ -207,7 +219,7 @@ public class SecureConfig { public String generateSecureToken(int length) { byte[] token = new byte[length]; secureRandom.nextBytes(token); - return Base64.getEncoder().encodeToString(token); + return android.util.Base64.encodeToString(token, android.util.Base64.NO_WRAP); } /** diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 37734e7..9ac5f1f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,7 +4,7 @@ Pseudo Nom C\'est qui - comment il s\'appelle + Comment il s\'appelle who Joueur Prénom diff --git a/app/src/test/java/com/example/boidelov3/games/papelito/PapelitoGameTest.java b/app/src/test/java/com/example/boidelov3/games/papelito/PapelitoGameTest.java index ac12971..6120c86 100644 --- a/app/src/test/java/com/example/boidelov3/games/papelito/PapelitoGameTest.java +++ b/app/src/test/java/com/example/boidelov3/games/papelito/PapelitoGameTest.java @@ -340,21 +340,42 @@ public class PapelitoGameTest { List playerNames = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve"); game.setupGame(playerNames, 3); - // Kill 2 civils using game method + // Set game state to voting game.setGameState(PapelitoGame.GameState.VOTING); - PapelitoPlayer civil1 = game.getAlivePlayers().get(0); - PapelitoPlayer civil2 = game.getAlivePlayers().get(1); - PapelitoPlayer civil3 = game.getAlivePlayers().get(2); - PapelitoPlayer undercover1 = game.getAlivePlayers().get(3); - PapelitoPlayer undercover2 = game.getAlivePlayers().get(4); + // Find actual civils and undercovers (roles are assigned randomly) + PapelitoPlayer civil1 = null; + PapelitoPlayer civil2 = null; + PapelitoPlayer undercover1 = null; + PapelitoPlayer undercover2 = null; + PapelitoPlayer undercover3 = null; + + for (PapelitoPlayer player : game.getAlivePlayers()) { + if (player.isCivil()) { + if (civil1 == null) civil1 = player; + else if (civil2 == null) civil2 = player; + } else if (player.isUndercover()) { + if (undercover1 == null) undercover1 = player; + else if (undercover2 == null) undercover2 = player; + else if (undercover3 == null) undercover3 = player; + } + } + + // We should have 2 civils and 3 undercovers + assertNotNull("Should have at least 1 civil", civil1); + assertNotNull("Should have at least 2 civils", civil2); + assertNotNull("Should have at least 1 undercover", undercover1); + assertNotNull("Should have at least 2 undercovers", undercover2); + assertNotNull("Should have at least 3 undercovers", undercover3); + + // Eliminate both civils game.vote(undercover1, civil1); game.eliminateMostVoted(); game.vote(undercover2, civil2); game.eliminateMostVoted(); - // Act + // Act - Now we have 0 civils and 3 undercovers boolean result = game.checkGameOver(); // Assert