Compare commits

...

178 Commits
v0.8 ... master

Author SHA1 Message Date
Rémi LAVERGNE a03ccb76e9 Ajout de la vidéo
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
11 months ago
Rémi LAVERGNE 4ea6ec094b Merge pull request 'v0.9 - Application MAUI' (#112) from dev into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
11 months ago
Rémi LAVERGNE bc26c9184d Mise à jour du README
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 6509cbcdd1 Bug de Rules.cs
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE e414771027 Bug de Rules.cs
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE aab53a614e Bug de Rules.cs
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE a5ea118e4a Bug de Rules.cs
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE cd2c35c481 Code Smells de Map.cs
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 170e5c5a66 Suppression d'un code smell
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 5a597d8323 Code Smells de Player.cs
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE e113ee2ac6 Code Smells de Rules.cs
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 0d3c541c4b Code Smells de Rules.cs
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 8b54be4818 Code Smells de Rules.cs
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 66cf3c37b2 Actualisation de LastPlayed
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 9d6b2af796 🛠️ Encore plus de tests
11 months ago
Remi NEVEU c11ccba67d Merge branch 'dev' of https://codefirst.iut.uca.fr/git/remi.lavergne/Trek-12 into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 68d29bc5a5 Suppression de code commenter et fonction EveryAdjacentCells mit nullable pour faire baisser les codes smells
11 months ago
Remi NEVEU 3104b66a39 Changement de la méthode AddToRopePath en enlever le second paramètre, qui était inutilisé
11 months ago
Rémi LAVERGNE b98b2172ce Merge SQLite into dev (#111)
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE d8962f4cb1 Suppression de deux tests qui ne servaient plus
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 60c8f76e01 Merge branch 'dev' of codefirst.iut.uca.fr:remi.lavergne/Trek-12 into dev
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 103fd02652 ⚙️ Correction de bugs dans le jeu
11 months ago
Remi NEVEU c95295575e Ajout d'un test et suppression de quelques codes smells
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 7fe5289475 Ajout de quelques tests
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU c562f9cbb5 suppression d'une mauvaise modification
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 38a9c8a8e7 Correction de tous les tests et fusion de ce qu'a fait Lucas
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 37d57b37fb première correction des tests mais je vais tout recommencer à mon git pull
11 months ago
Rémi LAVERGNE 62ff6c130f Merge PageBoard into dev (#110)
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 3fbe28accf Quelques modification pour continuer une partie non terminée
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 1f723b64f1 🚧 Correction de bugs et modification de la solution pour publication
continuous-integration/drone/push Build is failing Details
11 months ago
Lucas DUFLOT a9f4a82a5e Un debut de jeu, mais encore des problemes...
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE d63bc44634 Merge pull request 'ConsoleApp' (#109) from ConsoleApp into dev
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 70c4b9c3b2 Correction de tests
continuous-integration/drone/push Build is failing Details
11 months ago
Lucas DUFLOT 2685063b5e rien change
continuous-integration/drone/push Build was killed Details
continuous-integration/drone/pr Build was killed Details
11 months ago
Rémi LAVERGNE cfa4a18b93 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 631fae3711 💾 Persistance avec SQLite (création et bases)
continuous-integration/drone/push Build was killed Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Lucas DUFLOT 2595ac686f ✔ Chemins de corde
continuous-integration/drone/push Build was killed Details
11 months ago
Rémi LAVERGNE 56c47829e8 Ajout de la réflexion pour un test
continuous-integration/drone/push Build was killed Details
11 months ago
Rémi LAVERGNE ac940759a5 Merge GameLoop into dev (#108)
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 163012ec67 Suppression d'un using inutile
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 2067d24bc1 Suppression d'un using inutileÃ
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU 371948095e Suppression des choses non utilisées
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU e5fb6eba95 correction de deux tests de bestscore
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 1bd4021ec9 correction d'un test de bestscore
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 2f5085e39e Merge pull request 'bestScores et leaderboard' (#107) from bestScores into dev
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU 2392b8e0fa correction d'une erreur sur un test
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU ef49a6e81a Ajout de tests et de fonctions calculants les pénalités et les plaçant'
continuous-integration/drone/push Build is failing Details
11 months ago
Lucas DUFLOT d516b5898d 🚧 Console App
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU fb1676b335 Commit pour garder mais un test ne fonctionne pas du tout
11 months ago
Rémi LAVERGNE 4247d29b1a Déplacement de la fonction d'ajout du score final dans BestScore
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE ec92b15dcd Fonction pour l'ajout d'un score à la fin d'une partie
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE ccdc672033 BestScores
continuous-integration/drone/push Build is failing Details
11 months ago
Lucas DUFLOT 462e8dc744 🚧 Finalisation de la gameLoop
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU 49ee464ed2 Finition de deux tests pour gérer les pénalités sur les cases dangereuses ou ayant une valeure > 12
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU c499d279c8 commit de test
11 months ago
Remi NEVEU 7aa5f738c3 Titre de règles en blanc, suppression des espaces au début et à la fin des pseudo et ajout d'un popup quand l'on veut supprimer un pseudo qui n'existe pas
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 7d4343c2ad Modification du stub
11 months ago
Rémi LAVERGNE c4414e687e 🛠️ Ajout BestScore dans le Joueur
continuous-integration/drone/push Build is failing Details
11 months ago
Lucas DUFLOT 5d53cc73bf Correction du bug de la grille d'operation
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 606ee7d0dc Modification du nom d'une fonction et d'un test
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 0e06232715 Merge OperationGridBinding into dev #105
continuous-integration/drone/push Build is failing Details
11 months ago
Lucas DUFLOT eae8e70c8b DataBinding des cases cocher de la grille d'operation
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Lucas DUFLOT 184013c77f 🚧 Ajout de boutons de selection de l'operation
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE e95e7d290c Correction mineure pour faire passer les tests
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE fc8a7b7994 Merge branch 'MapEdits' into dev
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 99b9542cd4 Ajout de nom pour les Map
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE a34c5cc715 Merge pull request ' Navigation entre les pages, interactions et persistance' (#100) from navigation into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 8e9bca4b9a 🚧 Ajout de la navigation entre les pages, utilisation de la persistance et interactions
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE 0153fd24a7 🖼️ Modification du logo de l'application
11 months ago
Rémi LAVERGNE d802a29250 Modification des bases de navigation de l'application
11 months ago
Rémi LAVERGNE f1ff974661 Modification mineure du Stub
11 months ago
Rémi LAVERGNE f8e7dc1d24 Ajout des DataContract & DataMembers pour la persistance.
11 months ago
Remi NEVEU ce7d739d08 Rajout de la couleur blanche même en mode claire pour les règles
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 49ed6ac387 Ajout d'icones pour l'application
11 months ago
Remi NEVEU 941587fe4c Retour à la version de la semaine dernière pour modifier les joueurs
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU e70e826715 Réparation de deux tests et du stub
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE b7e386522b Ajout encore de tests
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 0855c28269 Ajout de tests supplémentaires
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE da8c0aeb73 Merge branch 'tests' into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 0665b2ca1b ⚙️ Correction de tests unitaires
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE d7c6afe388 Merge pull request 'Nouveaux tests et correction @remi.neveu' (#99) from tests into dev
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 0019bc7ad3 Changement d'un test vers les ObservableCollection
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE 642fa430bd Merge branch 'tests' of codefirst.iut.uca.fr:remi.lavergne/Trek-12 into tests
11 months ago
Remi NEVEU 5b7ccda4fd autre ajout
11 months ago
Remi NEVEU 00c7a46831 Ajout de quelques tests pour gametests
11 months ago
Lucas DUFLOT 74720d57b0 🚧 Binding des des avec l'evenement OndiceRolled
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU 4d9b1f2e67 Merge pull request 'Modification pageProfiles' (#98) from pageProfiles into dev
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU da44204a0f Merge pull request ' Navigation, Persistance, Binding et Actualisation' (#97) from navigation into dev
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 6c6dad65ca Ajout de la date de création et modification de la disposition
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE 3deafb234f Renommage et ajout de la date de création
11 months ago
Rémi LAVERGNE 12a18207ae Modification des chemins après une modification de nom de page
11 months ago
Rémi LAVERGNE 9d41d68597
Appel au traitement de conversion d'image pour récupérer l'image à partir du type converti (Base64)
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE 1bffc98af6
Implémentation du Manager Game de l'application dans la page, et refonte de l'ajout/suppression de Profils avec l'actualisation et vérification automatique. BindingContext -> Manager
11 months ago
Rémi LAVERGNE 39fb86e881
‼️ Ajout du nécessaire pour implémenter la persistance et son management au sein de toute les autres vues
11 months ago
Rémi LAVERGNE c4cbcf282f
Légère modification (ajout d'oublies dans la génération auto) des inclusions dans le .csproj
11 months ago
Rémi LAVERGNE a71a65ed1b
Retrait de l'implémentation de l'interface de changement de propriété
11 months ago
Rémi LAVERGNE 312067c84f
Ajout des balises de contrat de données, et initialisation de la date de création du profil dans les constructeurs
11 months ago
Rémi LAVERGNE 6755dd03a1
Retrait de la variable privée et de l'interface INotifyPropertyChanged, inutile ici
11 months ago
Rémi LAVERGNE b06f729ffd
Implémentation de l'interface INotifyPropertyChanged et des ObservableCollection pour l'actualisation des vues et l'encapsulation. Ajout d'une méthode de recherche et suppression d'un profil
11 months ago
Rémi LAVERGNE b84558e54e
Modification de l'interface en conséquence du passage en ObservableCollection
11 months ago
Rémi LAVERGNE b913214afc
🖼️ Ajout d'une méthode récupération de l'image depuis la chaine convertie
11 months ago
Rémi LAVERGNE ac794f5c16
Passage en ObservableCollection et changement du chemin d'accès de la persistance
11 months ago
Rémi LAVERGNE bf84bfa906 Merge branch 'navigation' of codefirst.iut.uca.fr:remi.lavergne/Trek-12 into navigation
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 57b4eba7fe Ajout des routes pour la navigation
11 months ago
Rémi LAVERGNE fcb586b474 Ajout des routes et manager (Persistance de Game)
continuous-integration/drone/push Build is failing Details
11 months ago
Lucas DUFLOT 531b45f159 🚧 Deroulement de la partie
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU d0fbda5eb7 autre ajout
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU f9ff46bdac Ajout de quelques tests pour gametests
continuous-integration/drone/push Build is passing Details
11 months ago
Lucas DUFLOT 32f7516c5a fixe bug
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 6aa9e768e9 Merge PageBoard into dev (#123)
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE ad934b1ec9 Ajout des routes pour la navigation
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE a21dd9c018 Merge PageSelectMap into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU feb662dd39 Merge pull request 'pageProfils' (#94) from pageProfils into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU e16133b425 Retour à l'état initial du leaderboard
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Lucas DUFLOT cd83fe85ac Merge pull request '⚙️ Tests pour Game.cs, Reflection, Mock et modif Game.cs' (#91) from tests into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Lucas DUFLOT a7214c03af Merge pull request ' Ajout de la persistance en JSON' (#93) from persistence into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Lucas DUFLOT 82482f609a Merge pull request 'Conversion d'image avec interface et base64' (#92) from converterToBase64 into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Lucas DUFLOT 5f1da34a3d � fix drone
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Lucas DUFLOT 99349370d7 � fix drone
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 5f3480bf98
🚧 Amélioration du binding, avec l'actualisation et l'interaction avec la grille d'opération
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 3d3677ea7f
Ajout de OnPropertyChanged et de l'interface INotifyPropertyChanged pour implémenter l'évenement de changement et d'actualisation
11 months ago
Rémi LAVERGNE c875061d18
🌫️ Modification (temporaire) de la visibilité de IsDangerous pour les vues
11 months ago
Rémi LAVERGNE f2ac649287
Création de cellules dangeureuses pour tester les vues
11 months ago
Rémi LAVERGNE bdef385b36 🖼️ Ajout d'images pour l'application
11 months ago
Rémi LAVERGNE b4aaff6dde
🔗 Ajout des directives d'assembly et des changements de Game pour corriger les erreurs
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE 4a3ab90bdc
🚧 Suppression d'un constructeur et développement de la méthode d'initialisation à la place
11 months ago
Rémi LAVERGNE 6bbf5c147a
⚙️ Ajout des tests restants pour Game.cs
11 months ago
Rémi LAVERGNE 756764f8ce
📝 Traduction de propriétés pour les dés et traduction des tests
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 170f002e14
⚙️ Ajout de Mock pour imiter l'interface de persistance et début des tests de Game
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 8c8ec49953 Merge branch 'tests' of codefirst.iut.uca.fr:remi.lavergne/Trek-12 into tests
continuous-integration/drone/push Build is failing Details
11 months ago
Rémi LAVERGNE 1c4125aadb
🛡️ Ajout du paquet NuGet pour les Mocks dans les tests unitaires
11 months ago
Remi NEVEU 45f1429886 Ajout d'ébauches de tests pour GameTests mais non fonctionnelles (check pour utiliser reflection)
11 months ago
Remi NEVEU 407ded9e14 Ajout de deux tests sur le calcul des zones + résolution d'un problème sur le code de cette même fonction
11 months ago
Rémi LAVERGNE 8f3468c863 🛠️ VSÃVérification si paramètres null
11 months ago
Rémi LAVERGNE 0dc74f9aa1 ⚙️ Test des dernières règles
11 months ago
Rémi LAVERGNE 7ecdedf561
🚧 Ajout en test de la convertion d'image avec interface et base64
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE bc86e16024 Ajout de la persistance en JSON et de commentaires de code
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Lucas DUFLOT 3fe358fce7 🚧 Avancement du databinding et du plateau de jeu
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU 0ff2da324f Boutons fonctionnels dans pageProfils
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 1ce607ac81 Merge pull request 'Débug de la boucle de jeu' (#87) from debugGameLoop into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 43877415f9 Binding du stub de plusieurs players via un datatemplate
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 5169d668b2 Ajout d'un titre à la page
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Lucas DUFLOT e8336afd10 ✔ DataBinding grille operation
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU 4733ed0a7e Ne fonctionne pas, je pars sur pageProfil puisqu'il n'y a pas de lien entre le player et le bestscore
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 3a2eee23b6 Départ de stub pour le leaderboard
continuous-integration/drone/push Build is passing Details
11 months ago
Lucas DUFLOT ae56f47b33 🚧 Binding de la table d'operation
continuous-integration/drone/push Build is failing Details
11 months ago
Lucas DUFLOT 2f8923cc0e Merge pull request ' Persistance en XML' (#86) from persistence into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE b0f608ff78
Persistance en XML
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE 27fdf24e90
Inclusion de la persistence à la solution
11 months ago
Rémi LAVERGNE 51eeee000a
Ajout du .csproj du DataContractPersistence
11 months ago
Rémi LAVERGNE 0e7c323e6c
Ajout des listes et méthodes de persistance avec le constructeur
11 months ago
Lucas DUFLOT b4f27e8b0b 🚧 Debut de la page Board
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU be61f9dd7e Ajout d'ébauches de tests pour GameTests mais non fonctionnelles (check pour utiliser reflection)
continuous-integration/drone/push Build is failing Details
11 months ago
Remi NEVEU 5d148e5352 Ajout de tests pour player
continuous-integration/drone/push Build is passing Details
11 months ago
Lucas DUFLOT af6e9ec71c 🚧 Modification de la page de seltion map
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU d999520a9a Ajout de deux tests sur le calcul des zones + résolution d'un problème sur le code de cette même fonction
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 636cc3b722 🛠️ VSÃVérification si paramètres null
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 005546e43b ⚙️ Test des dernières règles
11 months ago
Remi NEVEU 25ae73f4b5 L'ajout aux chemins de cordes est pas bon, donc le calcul final n'est pas bon🎅
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Remi NEVEU b58f363881 Boucle fonctionnelle, mais il manque les calculs de fin
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU e90f8cb185 Correction des opérations
11 months ago
Lucas DUFLOT de321a0726 🚧 databinding selection map
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU 8cc8435b3f Je réécris encore le code de l'autre Rémi
continuous-integration/drone/push Build is passing Details
11 months ago
Remi NEVEU fd019fb45a Un truc de plus fonctionne
11 months ago
Rémi LAVERGNE 9fae1d2702 Merge pull request 'pageRegles' (#80) from pageRegles into dev
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
11 months ago
Remi NEVEU 108f13e597 Suppression d'un héritage inutile
11 months ago
Lucas DUFLOT eed1dce359 Merge pull request '[PR] Départ tests des règles' (#74) from tests into dev
continuous-integration/drone/push Build is passing Details
continuous-integration/drone Build is passing Details
11 months ago
Remi NEVEU bdc1a9bb8f Merge pull request '🪚 Réduction de l'imbrication (nesting/Sonar)' (#73) from gameClass into dev
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE ef91eac896
🪚 Réduction de l'imbrication (nesting/Sonar)
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE a6ddee80af
⚙️ Ajout de tests supplémentaires pour d'autres règles
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
11 months ago
Rémi LAVERGNE 9dc789c3ec
⚙️ Ajout des tests pour les règles decheminsde corde
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 379c709589
⚙️ AjAjout de es tests sur les cellules adjacentes
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 98d7abd254
⚙️ Ajout du début des tests des Règles
continuous-integration/drone/push Build is passing Details
11 months ago
Rémi LAVERGNE 6d791f4737 Ajout de la LICENCE
continuous-integration/drone/push Build is passing Details
12 months ago
Rémi LAVERGNE 9fef1b7fff Supprimer 'LICENSE'
continuous-integration/drone/push Build is passing Details
12 months ago
Rémi LAVERGNE 1323dcc53f Ajout de la licence sur le dépôt
continuous-integration/drone/push Build is passing Details
12 months ago
Remi NEVEU 34a0c9278b Correction de Operation et de la console de l'autre Rémi
continuous-integration/drone/push Build is passing Details
12 months ago
Remi NEVEU 10008b236b Ajout d'un lien cliquable afin d'acheter le jeu👌
12 months ago

@ -0,0 +1,15 @@
**© 2024 - Tous droits réservés.**
Les éléments de conception et de documentation regroupent:
- Les diagrammes
- Les éléments de design
- Tout écrit réalisé dans le cadre de l'application et présent sur ce même [wiki](https://codefirst.iut.uca.fr/git/remi.lavergne/Trek-12/wiki).
Le terme "code" tel que définit constitue tout ou partie de texte permettant la formation, le fonctionnement et le lancement de l'application et présent dans le [dépôt CodeFirst](https://codefirst.iut.uca.fr/git/remi.lavergne/Trek-12).
Tous les éléments de conception et de documentation, ainsi que le code dans son intégralité sont la propriété des utilisateurs enregistrés sous les comptes CodeFirst/Gitea suivants: @remi.lavergne, @remi.neveu & @lucas.duflot.
Le jeu de société du nom "Trek 12" ainsi que tous ses logos associés sont la propriété de [🪓 LUMBERJACKS [Studio]](https://lumberjacks-studio.com/fr/trek12/)
**Utilisation et distribution :** Toute reproduction, distribution, ou utilisation non autorisée des éléments protégés est strictement interdite sans l'autorisation écrite préalable des propriétaires respectifs. Les utilisateurs peuvent demander des autorisations spécifiques en contactant les propriétaires via leurs comptes mentionnés.
**Avis de non-responsabilité :** Ce copyright ne s'étend pas aux éléments tiers utilisés sous licence. Toutes les marques commerciales et logos appartiennent à leurs propriétaires respectifs. Les propriétaires des comptes mentionnés ne sauraient être tenus responsables de l'utilisation abusive des éléments protégés par des tiers.

@ -1,4 +1,4 @@
![Static Badge](https://img.shields.io/badge/Trek12-En_Production-red)
![Static Badge](https://img.shields.io/badge/Trek12-Disponible_au_public-yellow)
<details>
<summary>Sonarcube Ratings</summary>
@ -27,4 +27,29 @@ Auteurs:
<br>
> ### **📕 Toute la documentation est présente sur le [Wiki](https://codefirst.iut.uca.fr/git/remi.lavergne/Trek-12/wiki) !**
> ### **📼 Vidéo de présentation sur [Opencast](https://opencast.dsi.uca.fr/paella/ui/watch.html?id=b08a0497-0fc3-420f-a98d-5f5a2b226252).**
> ### **📕 Toute la conception et le fonctionnement est présent sur le [Wiki](https://codefirst.iut.uca.fr/git/remi.lavergne/Trek-12/wiki) !**
> ### **📄 Documentation du code sur [CodeDoc](https://codefirst.iut.uca.fr/documentation/remi.lavergne/Trek-12/doxygen/index.html).**
<br>
*Persistance locale en XML ou JSON disponible. La persistance en SQLite est créée mais nécessite encore du développement pour fonctionner.*<br/>
*Pour changer de mode de persistance, se rendre dans le fichier App.xaml.cs*
<br><br>
## 📦 Générer un exécutable
Pour générer un exécutable, il suffit de lancer la commande suivante dans le terminal :
```bash
# Pour un exécutable MSIX (Windows Store):
dotnet publish -f net8.0-windows10.0.19041.0 -p:WindowsPackageType=MSIX
# Ou pour un exécutable "classique":
dotnet publish -c Release -r win-x64 --self-contained
```
<br>
***Avant de fork le projet ou autre, merci de lire attentivement la [LICENCE](LICENSE.md)***

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DataContractPersistence\DataContractPersistence.csproj" />
<ProjectReference Include="..\Models\Models.csproj" />
</ItemGroup>

@ -1,246 +1,416 @@
// See https://aka.ms/new-console-template for more information
using Models;
using Models.Events;
using Models.Exceptions;
using Models.Game;
namespace ConsoleApp;
class Program
{
/// <summary>
/// Main function of the console app
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Console.WriteLine("Enter your pseudo:");
string? pseudo = Console.ReadLine();
if (pseudo != null)
{
Player player = new Player(pseudo);
Map map = new Map("background");
Game game = new Game(player, map);
// Abonnement aux événements
game.GameStarted += OnGameStarted!;
game.GameEnded += OnGameEnded!;
game.BoardUpdated += OnBoardUpdated!;
game.DiceRolled += OnDiceRolled!;
game.OperationChosen += OnOperationChosen!;
game.CellChosen += OnCellChosen!;
// Initialisation
game.InitializeGame();
}
}
/// <summary>
/// Handles the event when the game has started.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnGameStarted(object sender, GameStartedEventArgs e)
{
Console.WriteLine($"The game has started! Player: {e.CurrentPlayer.Pseudo}");
}
/// <summary>
/// Handles the event when the game has ended.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnGameEnded(object sender, GameEndedEventArgs e)
{
Console.WriteLine($"The game has ended! Player: {e.CurrentPlayer.Pseudo}");
}
/// <summary>
/// Handles the event when the board is updated.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnBoardUpdated(object sender, EventArgs e)
{
DisplayBoard(((Game)sender).UsedMap);
DisplayOperationTable(((Game)sender).UsedMap.OperationGrid);
}
/// <summary>
/// Handles the event when the dice are rolled.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnDiceRolled(object sender, DiceRolledEventArgs e)
{
Console.WriteLine($"Dice 1: {e.Dice1Value} | Dice 2: {e.Dice2Value}");
Operation playerOperation = GetPlayerOperation();
((Game)sender).HandlePlayerOperation(playerOperation);
}
/// <summary>
/// Handles the event when an operation is chosen by the player.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnOperationChosen(object sender, OperationChosenEventArgs e)
{
Console.WriteLine($"Operation: {e.Operation}, Result: {e.Result}");
DisplayOperationTable(((Game)sender).UsedMap.OperationGrid);
Cell playerChoice = GetPlayerChoice();
try
{
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidCellCoordinatesException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidCellException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
}
/// <summary>
/// Handles the event when a cell is chosen by the player.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnCellChosen(object sender, CellChosenEventArgs e)
{
Console.WriteLine($"Cell chosen: ({e.Cell.X}, {e.Cell.Y}) with result: {e.Result}");
DisplayBoard(((Game)sender).UsedMap);
}
/// <summary>
/// Displays the game board.
/// </summary>
/// <param name="map">Used map to display</param>
static void DisplayBoard(Map map)
{
int cpt = 0;
for (int i = 0; i < map.Boards.Count; i++)
{
if (cpt % 6 == 0)
{
Console.Write("| ");
}
if (map.Boards[i].Value.HasValue)
{
Console.Write(map.Boards[i].Value?.ToString().PadLeft(2));
}
else
{
Console.Write(" O");
}
cpt++;
if (cpt % 6 == 0)
{
Console.Write(" |");
Console.WriteLine();
}
}
}
/// <summary>
/// Displays the operation table.
/// </summary>
/// <param name="operationTable">The operation table to display.</param>
static void DisplayOperationTable(List<OperationCell> operationTable)
{
Console.WriteLine("Operation Table:");
string[] operations = { "Addition (+)", "Subtraction (-)", "Multiplication (*)", "Lower Dice (less)", "Higher Dice (high)" };
for (int i = 0; i < operations.Length; i++)
{
Console.Write(operations[i].PadRight(18));
for (int j = 0; j < 4; j++)
{
int index = i * 4 + j;
if (index < operationTable.Count)
{
string status = operationTable[index].IsChecked ? "X" : " ";
Console.Write($" | {status}");
}
}
Console.WriteLine();
}
}
/// <summary>
/// Gets the cell chosen by the player.
/// </summary>
/// <returns>The cell chosen by the player.</returns>
static Cell GetPlayerChoice()
{
int row, column;
while (true)
{
Console.WriteLine("Enter the position of the cell you want to play");
Console.WriteLine("Enter the row number (0-5)");
if (!int.TryParse(Console.ReadLine(), out row) || row < 0 || row >= 6)
{
Console.WriteLine("Invalid row number. Please enter a number between 0 and 5.");
continue;
}
Console.WriteLine("Enter the column number (0-5)");
if (!int.TryParse(Console.ReadLine(), out column) || column < 0 || column >= 6)
{
Console.WriteLine("Invalid column number. Please enter a number between 0 and 5.");
continue;
}
return new Cell(row, column);
}
}
/// <summary>
/// Gets the operation chosen by the player.
/// </summary>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
static Operation GetPlayerOperation()
{
DisplayOperationOptions();
string? op = Console.ReadLine();
while (op != "1" && op != "2" && op != "3" && op != "4" && op != "5")
{
Console.WriteLine("Invalid operation. Please choose again.");
op = Console.ReadLine();
}
return op switch
{
"1" => Operation.ADDITION,
"2" => Operation.SUBTRACTION,
"3" => Operation.MULTIPLICATION,
"4" => Operation.LOWER,
"5" => Operation.HIGHER,
_ => throw new ArgumentOutOfRangeException()
};
}
/// <summary>
/// Displays the operation options for the player to choose from.
/// </summary>
static void DisplayOperationOptions()
{
Console.WriteLine("Choose an operation:");
Console.WriteLine("1: Addition (+)");
Console.WriteLine("2: Subtraction (-)");
Console.WriteLine("3: Multiplication (*)");
Console.WriteLine("4: Lower Dice (less)");
Console.WriteLine("5: Higher Dice (high)");
}
// See https://aka.ms/new-console-template for more information
using Models;
using Models.Events;
using Models.Game;
using Models.Interfaces;
using DataContractPersistence;
using Microsoft.Extensions.Logging;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace ConsoleApp;
class Program
{
static Game Game { get; set; }
/// <summary>
/// Main function of the console app
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Console.WriteLine("Enter your pseudo:");
string? pseudo = Console.ReadLine();
if (pseudo != null)
{
IPersistence persistence = new DataContractXml();
Player player = new Player(pseudo, "test.png");
Map map = new Map("Dunai","background");
Game = new Game(persistence);
// Abonnement aux événements
Game.GameStarted += OnGameStarted!;
Game.GameEnded += OnGameEnded!;
Game.BoardUpdated += OnBoardUpdated!;
Game.DiceRolled += OnDiceRolled!;
Game.OperationChosen += OnOperationChosen!;
Game.CellChosen += OnCellChosen!;
Game.PlayerChooseOp += OnPlayerSelectionOp!;
Game.PlayerOption += OnPlayerOption!;
Game.PlayerChooseCell += OnPlayerSelectionCell!;
// Initialisation
Game.InitializeGame(map, player);
Game.GameLoop();
}
}
static void OnPlayerSelectionCell(Object sender, PlayerChooseCellEventArgs e)
{
int row, column;
while (true)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Enter the position of the cell you want to play");
Console.WriteLine("Enter the row number (0-6)");
Console.ResetColor();
if (!int.TryParse(Console.ReadLine(), out row) || row < 0 || row >= 7)
{
Console.ForegroundColor= ConsoleColor.Red;
Console.WriteLine("Invalid row number. Please enter a number between 0 and 6.");
Console.ResetColor();
continue;
}
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Enter the column number (0-6)");
Console.ResetColor();
if (!int.TryParse(Console.ReadLine(), out column) || column < 0 || column >= 7)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Invalid column number. Please enter a number between 0 and 6.");
Console.ResetColor();
continue;
}
Game.PlayerCell = new Cell(row, column);
break;
}
}
static void OnPlayerOption(object sender, PlayerOptionEventArgs e)
{
Console.WriteLine();
if (e.Turn != 1)
{
IEnumerable<Cell> PlayedCellsQuery =
from cell in e.Board
where cell.Valid == true
where cell.Value != null
select cell;
foreach (var item in e.Board)
{
if (item.X == 6)
Console.WriteLine();
if (!item.Valid)
Console.Write(" ");
else if (item.Value != null)
Console.Write($"{item.Value}");
else
{
foreach (var item1 in PlayedCellsQuery)
{
if (Math.Abs(item.X - item1.X) <= 1 && Math.Abs(item.Y - item1.Y) <= 1)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write($"{e.Resultat}");
Console.ResetColor();
}
}
}
}
return;
}
foreach (var item in e.Board)
{
if (!item.Valid)
Console.Write(" ");
else
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write($"{e.Resultat}");
Console.ResetColor();
}
if (item.X == 6)
Console.WriteLine();
}
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Choose an Available cell.");
Console.ResetColor();
}
static void OnPlayerSelectionOp(object sender, PlayerChooseOperationEventArgs e)
{
Console.WriteLine();
DisplayOperationTable(((Game)sender).UsedMap.OperationGrid.ToList());
Console.WriteLine();
Console.WriteLine("0. Lower | 1. Higher | 2. Substraction | 3. Addition | 4. Multiplication");
string? op = Console.ReadLine();
while (op != "0" && op != "1" && op != "2" && op != "3" && op != "4")
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Invalid operation. Please choose again.");
Console.ResetColor();
op = Console.ReadLine();
}
int test = Convert.ToInt32(op);
Game.PlayerOperation = (Operation)test;
}
/// <summary>
/// Handles the event when the game has started.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnGameStarted(object sender, GameStartedEventArgs e)
{
Console.WriteLine($"The game has started! Player: {e.CurrentPlayer.Pseudo}");
}
/// <summary>
/// Handles the event when the game has ended.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnGameEnded(object sender, GameEndedEventArgs e)
{
Console.WriteLine($"The game has ended! Player: {e.CurrentPlayer.Pseudo}");
Console.WriteLine($"Points: {e.Point}");
}
/// <summary>
/// Handles the event when the board is updated.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnBoardUpdated(object sender, BoardsUpdateEventArgs e)
{
foreach (var item in e.Boards)
{
if (!item.Valid)
Console.Write(" ");
else if (item.Value != null)
Console.Write($"{item.Value}");
else Console.Write("O");
if (item.X == 6)
Console.WriteLine();
}
}
/// <summary>
/// Handles the event when the dice are rolled.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnDiceRolled(object sender, DiceRolledEventArgs e)
{
// Console.Clear();
Console.WriteLine($"Dice 1: {e.Dice1Value} | Dice 2: {e.Dice2Value}");
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Choose an operation.");
Console.ResetColor();
}
/// <summary>
/// Handles the event when an operation is chosen by the player.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnOperationChosen(object sender, OperationChosenEventArgs e)
{
Console.WriteLine($"Operation: {e.Operation}, Result: {e.Result}");
DisplayOperationTable(((Game)sender).UsedMap.OperationGrid.ToList());
Cell playerChoice = GetPlayerChoice();
bool test = ((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
if(!test)
{
Console.WriteLine("Invalid cell. Please choose again.");
Console.WriteLine();
Console.WriteLine();
DisplayBoard(((Game)sender).UsedMap);
OnOperationChosen(sender, e);
}
/*
try
{
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidCellCoordinatesException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidCellException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidPlaceResultException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
*/
}
/// <summary>
/// Handles the event when a cell is chosen by the player.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnCellChosen(object sender, CellChosenEventArgs e)
{
Console.WriteLine($"Cell chosen: ({e.Cell.X}, {e.Cell.Y}) with result: {e.Result}");
DisplayBoard(((Game)sender).UsedMap);
}
/// <summary>
/// Displays the game board.
/// </summary>
/// <param name="map">Used map to display</param>
static void DisplayBoard(Map map)
{
int cpt = 0;
for (int i = 0; i < map.Boards.Count; i++)
{
if (cpt % 6 == 0)
{
Console.Write("| ");
}
if (map.Boards[i].Value.HasValue)
{
Console.Write(map.Boards[i].Value?.ToString().PadLeft(2));
}
else
{
Console.Write(" O");
}
cpt++;
if (cpt % 6 == 0)
{
Console.Write(" |");
Console.WriteLine();
}
}
}
/// <summary>
/// Displays the operation table.
/// </summary>
/// <param name="operationTable">The operation table to display.</param>
static void DisplayOperationTable(List<OperationCell> operationTable)
{
Console.ForegroundColor = ConsoleColor.Yellow;
foreach ( var cell in operationTable)
{
if (cell.X == 0 && cell.Y == 0)
Console.Write("Lower => ");
if (cell.X == 0 && cell.Y == 1)
Console.Write("Higher => ");
if (cell.X == 0 && cell.Y == 2)
Console.Write("Substraction => ");
if (cell.X == 0 && cell.Y == 3)
Console.Write("Addition => ");
if (cell.X == 0 && cell.Y == 4)
Console.Write("Multiplication => ");
if (cell.IsChecked)
Console.Write("X | ");
if (!cell.IsChecked)
Console.Write(" | ");
if (cell.X == 3)
Console.WriteLine();
}
Console.ResetColor();
}
/// <summary>
/// Gets the cell chosen by the player.
/// </summary>
/// <returns>The cell chosen by the player.</returns>
static Cell GetPlayerChoice()
{
int row, column;
while (true)
{
Console.WriteLine("Enter the position of the cell you want to play");
Console.WriteLine("Enter the row number (0-5)");
if (!int.TryParse(Console.ReadLine(), out row) || row < 0 || row >= 6)
{
Console.WriteLine("Invalid row number. Please enter a number between 0 and 5.");
continue;
}
Console.WriteLine("Enter the column number (0-5)");
if (!int.TryParse(Console.ReadLine(), out column) || column < 0 || column >= 6)
{
Console.WriteLine("Invalid column number. Please enter a number between 0 and 5.");
continue;
}
return new Cell(row, column);
}
}
/// <summary>
/// Gets the operation chosen by the player.
/// </summary>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
static Operation GetPlayerOperation(object? sender)
{
DisplayOperationOptions();
string? op = Console.ReadLine();
while (op != "1" && op != "2" && op != "3" && op != "4" && op != "5")
{
Console.WriteLine("Invalid operation. Please choose again.");
op = Console.ReadLine();
}
int test = Convert.ToInt32(op);
while(((Game)sender).UsedMap.CheckOperationPossible(test-1))
{
Console.WriteLine("Invalid operation. Please choose again.");
Console.WriteLine();
op = Console.ReadLine();
while (op != "1" && op != "2" && op != "3" && op != "4" && op != "5")
{
Console.WriteLine("Invalid operation. Please choose again.");
op = Console.ReadLine();
}
test = Convert.ToInt32(op);
}
return op switch
{
"1" => Operation.ADDITION,
"2" => Operation.SUBTRACTION,
"3" => Operation.MULTIPLICATION,
"4" => Operation.LOWER,
"5" => Operation.HIGHER,
_ => throw new ArgumentOutOfRangeException()
};
}
/// <summary>
/// Displays the operation options for the player to choose from.
/// </summary>
static void DisplayOperationOptions()
{
Console.WriteLine("Choose an operation:");
Console.WriteLine("1: Addition (+)");
Console.WriteLine("2: Subtraction (-)");
Console.WriteLine("3: Multiplication (*)");
Console.WriteLine("4: Lower Dice (less)");
Console.WriteLine("5: Higher Dice (high)");
}
}

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Xml.Serialization;
using System.Collections.ObjectModel;
using System.Text.Json;
using Models.Game;
using Models.Interfaces;
namespace DataContractPersistence
{
public class DataContractJson : IPersistence
{
/// <summary>
/// Name of the file where the data will be saved
/// </summary>
public string FileName { get; set; } = "data.json";
/// <summary>
/// Path (relative to the project)
/// </summary>
public string FilePath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Trek_12");
/// <summary>
/// Load all the data from JSON file
/// </summary>
/// <returns>A tuple with the lists of players, games, maps and bestScores</returns>
public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData()
{
var JsonSerializer = new DataContractJsonSerializer(typeof(DataToPersist));
DataToPersist? data = new DataToPersist();
using (Stream s = File.OpenRead(Path.Combine(FilePath, FileName)))
{
data = JsonSerializer.ReadObject(s) as DataToPersist;
}
Console.WriteLine("Data loaded: " + Path.Combine(FilePath, FileName));
return (data.Players, data.Games, data.Maps, data.BestScores);
}
/// <summary>
/// Save all the data to a JSON file
/// </summary>
/// <param name="players"></param>
/// <param name="games"></param>
/// <param name="maps"></param>
/// <param name="bestScores"></param>
public void SaveData(ObservableCollection<Player> players, ObservableCollection<Game> games, ObservableCollection<Map> maps, ObservableCollection<BestScore> bestScores)
{
Debug.WriteLine("Saving data at " + Path.Combine(FilePath, FileName));
if (!Directory.Exists(FilePath))
{
Debug.WriteLine("Directory created");
Debug.WriteLine(Directory.GetDirectoryRoot(FilePath));
Debug.WriteLine(FilePath);
Directory.CreateDirectory(FilePath);
}
var JsonSerializer = new DataContractJsonSerializer(typeof(DataToPersist));
DataToPersist? data = new DataToPersist();
data.Players = players;
data.Games = games;
data.Maps = maps;
data.BestScores = bestScores;
var settings = new XmlWriterSettings() { Indent = true };
using (FileStream stream = File.Create(Path.Combine(FilePath, FileName)))
{
using (var writer = JsonReaderWriterFactory.CreateJsonWriter(
stream,
Encoding.UTF8,
false,
true))
{
JsonSerializer.WriteObject(writer, data);
writer.Flush();
}
}
}
}
}

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Models\Models.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using Models.Game;
using Models.Interfaces;
namespace DataContractPersistence
{
public class DataContractXml : IPersistence
{
/// <summary>
/// Name of the file where the data will be saved
/// </summary>
public string FileName { get; set; } = "data.xml";
/// <summary>
/// Path (relative to the project)
/// </summary>
public string FilePath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Trek_12");
/// <summary>
/// Load all the data from XML file
/// </summary>
/// <returns>A tuple with the lists of players, games, maps and bestScores</returns>
public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData()
{
var serializer = new DataContractSerializer(typeof(DataToPersist));
DataToPersist? data = new DataToPersist();
using (Stream s = File.OpenRead(Path.Combine(FilePath, FileName)))
{
data = serializer.ReadObject(s) as DataToPersist;
}
Console.WriteLine("Data loaded: " + Path.Combine(FilePath, FileName));
return (data.Players, data.Games, data.Maps, data.BestScores);
}
/// <summary>
/// Save all the data to a XML file
/// </summary>
/// <param name="players"></param>
/// <param name="games"></param>
/// <param name="maps"></param>
/// <param name="bestScores"></param>
public void SaveData(ObservableCollection<Player> players, ObservableCollection<Game> games, ObservableCollection<Map> maps, ObservableCollection<BestScore> bestScores)
{
if(!Directory.Exists(FilePath))
{
Debug.WriteLine("Directory created");
Debug.WriteLine(Directory.GetDirectoryRoot(FilePath));
Debug.WriteLine(FilePath);
Directory.CreateDirectory(FilePath);
}
var serializer = new DataContractSerializer(typeof(DataToPersist));
DataToPersist? data = new DataToPersist();
data.Players = players;
data.Games = games;
data.Maps = maps;
data.BestScores = bestScores;
var settings = new XmlWriterSettings() { Indent = true };
using (TextWriter tw = File.CreateText(Path.Combine(FilePath, FileName)))
{
using (XmlWriter writer = XmlWriter.Create(tw, settings))
{
serializer.WriteObject(writer, data);
Console.WriteLine("Data saved: " + Path.Combine(FilePath, FileName));
}
}
}
}
}

@ -0,0 +1,28 @@
using System.Collections.ObjectModel;
using Models.Game;
namespace DataContractPersistence
{
public class DataToPersist
{
/// <summary>
/// List of players
/// </summary>
public ObservableCollection<Player> Players { get; set; }
/// <summary>
/// List of games (with all the data in it to be able to recreate the game)
/// </summary>
public ObservableCollection<Game> Games { get; set; }
/// <summary>
/// List of maps with their boards
/// </summary>
public ObservableCollection<Map> Maps { get; set; }
/// <summary>
/// List of best scores
/// </summary>
public ObservableCollection<BestScore> BestScores { get; set; }
}
}

@ -0,0 +1,69 @@
using SQLite;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading.Tasks;
namespace DataContractPersistence
{
using Models.Game;
using Models.Interfaces;
public class SqLitePersistence : IPersistence
{
private readonly SQLiteConnection _database;
private readonly string _databasePath;
public SqLitePersistence()
{
_databasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Trek_12.db");
_database = new SQLiteConnection(_databasePath);
_database.CreateTable<Player>();
_database.CreateTable<Map>();
_database.CreateTable<Game>();
_database.CreateTable<BestScore>();
}
public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData()
{
var players = new ObservableCollection<Player>(_database.Table<Player>());
var games = new ObservableCollection<Game>(_database.Table<Game>());
var maps = new ObservableCollection<Map>(_database.Table<Map>());
var bestScores = new ObservableCollection<BestScore>(_database.Table<BestScore>());
return (players, games, maps, bestScores);
}
public void SaveData(ObservableCollection<Player> players, ObservableCollection<Game> games, ObservableCollection<Map> maps, ObservableCollection<BestScore> bestScores)
{
_database.RunInTransaction(() =>
{
_database.DeleteAll<Player>();
_database.DeleteAll<Game>();
_database.DeleteAll<Map>();
_database.DeleteAll<BestScore>();
});
foreach (var player in players)
{
_database.Insert(player);
}
foreach (var game in games)
{
_database.Insert(game);
}
foreach (var map in maps)
{
_database.Insert(map);
}
foreach (var bestScore in bestScores)
{
_database.Insert(bestScore);
}
}
}
}

@ -0,0 +1,17 @@
using Models.Game;
namespace Models.Events
{
/// <summary>
/// Event arguments for when the board change.
/// </summary>
public class BoardsUpdateEventArgs : EventArgs
{
public List<Cell> Boards { get; set; }
public BoardsUpdateEventArgs(List<Cell> board)
{
Boards = board;
}
}
}

@ -9,9 +9,12 @@ namespace Models.Events
{
public Player CurrentPlayer { get; }
public GameEndedEventArgs(Player winner)
public int? Point { get; }
public GameEndedEventArgs(Player winner, int? point)
{
CurrentPlayer = winner;
Point = point;
}
}
}

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Models.Game;
namespace Models.Events
{
public class PlayerChooseCellEventArgs : EventArgs
{
public Cell Cell { get; set; }
public PlayerChooseCellEventArgs(Cell cell)
{
Cell = cell;
}
}
}

@ -0,0 +1,19 @@
using Models.Game;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Models.Events
{
public class PlayerChooseOperationEventArgs : EventArgs
{
public Operation PlayerOp { get; set; }
public PlayerChooseOperationEventArgs(Operation op)
{
PlayerOp = op;
}
}
}

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Models.Game;
namespace Models.Events
{
public class PlayerOptionEventArgs : EventArgs
{
public List<Cell> Board { get; set; }
public int Resultat { get; set; }
public int Turn { get; set; }
public PlayerOptionEventArgs(List<Cell> boards, int resultat, int turn)
{
Board = boards;
Resultat = resultat;
Turn = turn;
}
}
}

@ -1,11 +0,0 @@
namespace Models.Exceptions
{
/// <summary>
/// Exception for when the cell coordinates are invalid.
/// </summary>
public class InvalidCellCoordinatesException : Exception
{
public InvalidCellCoordinatesException(string message) : base(message) { }
}
}

@ -1,11 +0,0 @@
namespace Models.Exceptions
{
/// <summary>
/// Exception for when the cell is invalid.
/// </summary>
public class InvalidCellException : Exception
{
public InvalidCellException(string message) : base(message) { }
}
}

@ -0,0 +1,41 @@
using System;
using Microsoft.Maui.Controls;
using Models.Interfaces;
namespace Models.Game
{
/// <summary>
/// Converter to Base64
/// </summary>
public class Base64Converter : IImageConverter
{
/// <summary>
/// Converts an image to a base64 string
/// </summary>
/// <param name="imagePath">The path to access the image</param>
/// <returns>The base64 string representation of the image</returns>
/// <exception cref="FileNotFoundException">Native .NET exception</exception>
public string ConvertImage(string imagePath)
{
if (!File.Exists(imagePath))
{
// native .NET exception
throw new FileNotFoundException("Image file not found", imagePath);
}
byte[] imageBytes = File.ReadAllBytes(imagePath);
return Convert.ToBase64String(imageBytes);
}
/// <summary>
/// Retrieve an image from a string encoded in base64
/// </summary>
/// <param name="imageString"></param>
/// <returns></returns>
public ImageSource RetrieveImage(string imageString)
{
byte[] imageBytes = Convert.FromBase64String(imageString);
return ImageSource.FromStream(() => new MemoryStream(imageBytes));
}
}
}

@ -2,53 +2,83 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using SQLite;
namespace Models.Game
{
/// <summary>
/// This class represents the best score of a player.
/// </summary>
[DataContract, SQLite.Table("BestScores")]
public class BestScore
{
/// <summary>
/// Initialize a new instance of the BestScore class.
/// </summary>
/// <param name="gamesPlayed">Number of games played by the new user</param>
/// <param name="score">Best score</param>
public BestScore(int gamesPlayed, int score)
{
GamesPlayed = gamesPlayed;
Score = score;
if (GamesPlayed < 0)
GamesPlayed = 0;
if (Score < 0)
Score = 0;
}
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
/// <summary>
/// Number of games played by the user.
/// Name of the map.
/// </summary>
public int GamesPlayed { get; private set; }
[DataMember]
public string MapName { get; private set; }
[DataMember]
public Player ThePlayer { get; private set; }
/// <summary>
/// Best score of the player.
/// Number of games played by the user (on a specific map).
/// </summary>
private int _score;
public int Score
{
get
private int _gamesPlayed;
[DataMember]
public int GamesPlayed
{
get => _gamesPlayed;
private set
{
return _score;
if (value >= 0)
_gamesPlayed = value;
else _gamesPlayed = 0;
}
}
/// <summary>
/// Best score of the player (on a specific map).
/// </summary>
private int _score;
[DataMember]
public int Score
{
get => _score;
private set
{
if (value > _score)
if (value >= 0)
_score = value;
else _score = 0;
}
}
/// <summary>
/// Initialize a new instance of the BestScore class.
/// </summary>
/// <param name="gamesPlayed">Number of games played by the new user</param>
/// <param name="score">Best score</param>
/// <param name="map">The name of the map</param>
public BestScore(string map, Player player, int gamesPlayed, int score)
{
MapName = map;
ThePlayer = player;
GamesPlayed = gamesPlayed;
Score = score;
}
/// <summary>
/// SQLite constructor
/// </summary>
public BestScore() { }
/// <summary>
/// Increment the number of games played by the user.
/// </summary>
@ -63,7 +93,31 @@ namespace Models.Game
/// <param name="newScore">New best score</param>
public void UpdateScore(int newScore)
{
Score = newScore;
Score += newScore;
}
/// <summary>
/// Redefine the equal operation between BestScore.
/// </summary>
/// <param name="obj">The object to compare with the current BestScore.</param>
/// <returns>true if the specified object is equal to the current BestScore; otherwise, false.</returns>
public override bool Equals(object? obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
BestScore c = (BestScore)obj;
return (MapName == c.MapName && ThePlayer == c.ThePlayer);
}
/// <summary>
/// Returns the hash code for the current BestScore, in order for the Equals operation to work
/// </summary>
/// <returns>The hash code for the current BestScore.</returns>
public override int GetHashCode()
{
return MapName.GetHashCode() ^ ThePlayer.GetHashCode();
}
}
}

@ -1,14 +1,20 @@
namespace Models.Game
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
namespace Models.Game
{
/// <summary>
/// The Cell class represents a cell in the application.
/// </summary>
public class Cell : Position, IEquatable<Cell>
[DataContract]
public class Cell : Position, IEquatable<Cell>, INotifyPropertyChanged
{
/// <summary>
/// The value of the cell.
/// </summary>
private int? _value;
[DataMember]
public int? Value {
get => _value;
set
@ -18,18 +24,31 @@
throw new Exception("La valeur doit être supérieure à 0");
}
this._value = value;
OnPropertyChanged();
}
}
/// <summary>
/// The fact that the cell is dangerous or not.
/// </summary>
private bool IsDangerous { get; set; }
[DataMember]
public bool IsDangerous { get; set; }
[DataMember]
public bool Valid { get; set; }
/// <summary>
/// Atribute to know if the cell is a penalty cell.
/// </summary>
private bool Penalty { get; set; }
[DataMember]
public bool Penalty { get; private set; }
public void SetPenalty()
{
Penalty = true;
}
/// <summary>
/// Constructor of the Cell class.
@ -41,6 +60,7 @@
{
IsDangerous = isDangerous;
Penalty = false;
Valid = false;
}
/// <summary>
@ -60,5 +80,13 @@
if (this.X == other.X && this.Y == other.Y) return true;
return false;
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

@ -14,12 +14,12 @@ namespace Models.Game
/// <summary>
/// Lowest number on the dice.
/// </summary>
public int NbMin { get; private set; }
public int MinVal { get; private set; }
/// <summary>
/// Highest number on the dice.
/// </summary>
public int NbMax { get; private set; }
public int MaxVal { get; private set; }
/// <summary>
/// Value of the dice.
@ -29,9 +29,9 @@ namespace Models.Game
get => _value;
private set
{
if (value < NbMin || value > NbMax)
if (value < MinVal || value > MaxVal)
{
value = NbMin;
value = MinVal;
}
_value = value;
}
@ -40,13 +40,13 @@ namespace Models.Game
/// <summary>
/// Initializes a new instance of the <see cref="Dice"/> class.
/// </summary>
/// <param name="nbmin">The lowest number on the dice, on which the highest number is set</param>
public Dice(int nbmin)
/// <param name="minval">The lowest number on the dice, on which the highest number is set</param>
public Dice(int minval)
{
if (nbmin < 0) nbmin = 0;
if (nbmin > 1) nbmin = 1;
NbMin = nbmin;
NbMax = nbmin + 5;
if (minval < 0) minval = 0;
if (minval > 1) minval = 1;
MinVal = minval;
MaxVal = minval + 5;
}
/// <summary>
@ -54,13 +54,8 @@ namespace Models.Game
/// </summary>
public Dice()
{
NbMin = 0;
NbMax = 5;
}
public override string ToString()
{
return $"Ce dé a pour valeur {Value} et est entre {NbMin} et {NbMax}";
MinVal = 0;
MaxVal = 5;
}
/// <summary>
@ -68,7 +63,7 @@ namespace Models.Game
/// </summary>
public void Roll()
{
Value = new Random().Next(NbMin, NbMax + 1);
Value = new Random().Next(MinVal, MaxVal + 1);
}
/// <summary>

@ -1,12 +1,19 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using Models.Events;
using Models.Exceptions;
using Models.Interfaces;
using Models.Rules;
using SQLite;
namespace Models.Game
{
@ -14,54 +21,326 @@ namespace Models.Game
/// The Game class represents a game session in the application.
/// It contains all the necessary properties and methods to manage a game, including the game loop, dice rolling, and use of the game rules.
/// </summary>
public class Game
[DataContract, SQLite.Table("Games")]
public class Game : INotifyPropertyChanged
{
public bool IsPreviousGameNotFinished { get; private set; }
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
/* Persistence Interface */
[Ignore]
public IPersistence PersistenceManager { get; set; }
/* List for the game and persistence */
private ObservableCollection<Player> _players;
[Ignore]
public ObservableCollection<Player> Players
{
get => _players;
set
{
_players = value;
OnPropertyChanged(nameof(Players));
}
}
private ObservableCollection<Game> _games;
[Ignore]
public ObservableCollection<Game> Games
{
get => _games;
set
{
_games = value;
OnPropertyChanged(nameof(Games));
}
}
private ObservableCollection<Map> _maps;
[Ignore]
public ObservableCollection<Map> Maps
{
get => _maps;
set
{
_maps = value;
OnPropertyChanged(nameof(Maps));
}
}
private ObservableCollection<BestScore> _bestScores;
[Ignore]
public ObservableCollection<BestScore> BestScores
{
get => _bestScores;
set
{
_bestScores = value;
OnPropertyChanged(nameof(BestScores));
}
}
private bool _isRunning;
public Player CurrentPlayer { get; private set; }
[DataMember]
public bool IsRunning
{
get => _isRunning;
private set => _isRunning = value;
}
public Map UsedMap { get; private set; }
[DataMember]
public Player? CurrentPlayer { get; private set; }
private Dice Dice1 { get; }
private Dice Dice2 { get; }
private Map? _usedMap;
[DataMember]
public Map? UsedMap
{
get => _usedMap;
set
{
_usedMap = value;
OnPropertyChanged(nameof(UsedMap));
}
}
[Ignore]
public Dice Dice1 { get; private set;}
[Ignore]
public Dice Dice2 { get; private set; }
[DataMember]
public int Turn { get; set; }
[Ignore]
public Operation PlayerOperation { get; set; }
private Cell _playerCell;
[Ignore]
public Cell PlayerCell {
get => _playerCell;
set
{
_playerCell = value;
OnPropertyChanged(nameof(PlayerCell));
}
}
private int _resultat;
public int Resultat {
get => _resultat;
set
{
_resultat = value;
OnPropertyChanged(nameof(Resultat));
}
}
private int Turn { get; set; }
public bool DiceRolledFlag { get; private set; }
public bool OperationChosenFlag { get; private set; }
[Ignore]
public Rules.Rules GameRules { get; }
// == Events ==
public event EventHandler<GameStartedEventArgs> GameStarted;
public event EventHandler<GameEndedEventArgs> GameEnded;
public event EventHandler BoardUpdated;
public event EventHandler<BoardsUpdateEventArgs> BoardUpdated;
public event EventHandler<DiceRolledEventArgs> DiceRolled;
public event EventHandler<OperationChosenEventArgs> OperationChosen;
public event EventHandler<CellChosenEventArgs> CellChosen;
public event EventHandler<PlayerChooseOperationEventArgs> PlayerChooseOp;
public event EventHandler<PlayerOptionEventArgs> PlayerOption;
public event EventHandler<PlayerChooseCellEventArgs> PlayerChooseCell;
public void AddPlayer(Player player)
{
Players.Add(player);
}
public void AddGame(Game game)
{
Games.Add(game);
}
public void AddMap(Map map)
{
Maps.Add(map);
}
/// <summary>
/// Initializes a new instance of the <see cref="Game"/> class.
/// Adds a new best score to the list of best scores. Or updates it if it already exists.
/// </summary>
/// <param name="player">The player who play the game.</param>
/// <param name="map">The map to be used in the game.</param>
public Game(Player player, Map map)
/// <param name="finalScore">The final score of the game.</param>
public void AddBestScore(int finalScore)
{
_isRunning = false;
UsedMap = map;
CurrentPlayer = player;
Dice1 = new Dice();
Dice2 = new Dice(1);
Turn = 1;
BestScore bs = new BestScore(UsedMap.Name, CurrentPlayer, 1, finalScore);
var existingScore = BestScores.FirstOrDefault(score => score.Equals(bs));
if (existingScore != null)
{
existingScore.IncrGamesPlayed();
existingScore.UpdateScore(finalScore);
}
else
{
BestScores.Add(bs);
}
// Sorting the best scores
List<BestScore> sortedScores = BestScores.OrderByDescending(score => score.Score).ToList();
for (int i = 0; i < sortedScores.Count; i++)
{
if (!BestScores[i].Equals(sortedScores[i]))
{
BestScores.Move(BestScores.IndexOf(sortedScores[i]), i);
}
}
}
/// <summary>
/// Removes a player from the list of players.
/// </summary>
/// <param name="playerName"></param>
/// <returns>True if the player was removed successfully, false otherwise.</returns>
public bool RemovePlayer(string playerName)
{
Player? player = Players.FirstOrDefault(p => p.Pseudo == playerName);
if (player == null)
{
return false;
}
Players.Remove(player);
CheckAndRemoveBestScoresDependencies(player.Pseudo);
return true;
}
/// <summary>
/// Modifies the pseudo of a player.
/// </summary>
/// <param name="pseudo"></param>
/// <param name="newpseudo"></param>
/// <returns></returns>
public bool ModifyPlayer(string pseudo, string newpseudo)
{
foreach (var index in Players)
{
if (index.Pseudo == pseudo)
{
CheckAndChangeBestScoresDependencies(index.Pseudo, newpseudo);
index.Pseudo = newpseudo;
return true;
}
}
return false;
}
/// <summary>
/// Removes a game from the list of games.
/// </summary>
/// <param name="playerName"></param>
public void CheckAndRemoveBestScoresDependencies(string playerName)
{
List<BestScore> bs = new List<BestScore>();
foreach (var bestScore in BestScores)
{
if (!bestScore.ThePlayer.Pseudo.Equals(playerName)) continue;
bs.Add(bestScore);
}
foreach (var score in bs)
{
BestScores.Remove(score);
}
}
/// <summary>
/// Modifies the pseudo of a player in the best scores.
/// </summary>
/// <param name="playerName"></param>
/// <param name="newPlayerName"></param>
public void CheckAndChangeBestScoresDependencies(string playerName, string newPlayerName)
{
foreach (var bestScore in BestScores)
{
if (!bestScore.ThePlayer.Pseudo.Equals(playerName)) continue;
bestScore.ThePlayer.Pseudo = newPlayerName;
}
}
public void LoadData()
{
var data = PersistenceManager.LoadData();
foreach (var player in data.Item1)
{
Players.Add(player);
}
foreach (var game in data.Item2)
{
if (game.IsRunning)
{
IsPreviousGameNotFinished = true;
}
Games.Add(game);
}
foreach (var map in data.Item3)
{
Maps.Add(map);
}
foreach (var bestScore in data.Item4)
{
BestScores.Add(bestScore);
}
}
public void SaveData() => PersistenceManager.SaveData(Players, Games, Maps, BestScores);
public Game(IPersistence persistenceManager)
{
PersistenceManager = persistenceManager;
Players = new ObservableCollection<Player>();
Games = new ObservableCollection<Game>();
Maps = new ObservableCollection<Map>();
BestScores = new ObservableCollection<BestScore>();
GameRules = new Rules.Rules();
IsRunning = false;
}
public Game()
{
Players = new ObservableCollection<Player>();
Games = new ObservableCollection<Game>();
Maps = new ObservableCollection<Map>();
BestScores = new ObservableCollection<BestScore>();
GameRules = new Rules.Rules();
UsedMap = new Map("temp","test");
IsRunning = false;
}
/// <summary>
/// Rolls all the dice.
/// </summary>
private void RollAllDice()
public void RollAllDice()
{
Dice1.Roll();
Dice2.Roll();
DiceRolledFlag = true;
OperationChosenFlag = false;
DiceRolled?.Invoke(this, new DiceRolledEventArgs(Dice1.Value, Dice2.Value));
OnPropertyChanged(nameof(Dice1));
OnPropertyChanged(nameof(Dice2));
}
/// <summary>
@ -71,13 +350,16 @@ namespace Models.Game
private void MarkOperationAsChecked(Operation operation)
{
int operationIndex = (int)operation;
int operationsPerType = 4; // Chaque type d'opération peut être fait 4 fois
IEnumerable<OperationCell> sortPaths =
from cell in UsedMap.OperationGrid
where cell.Y == operationIndex
select cell;
for (int i = operationIndex * operationsPerType; i < (operationIndex + 1) * operationsPerType; i++)
foreach (var item in sortPaths)
{
if (!UsedMap.OperationGrid[i].IsChecked)
if (!item.IsChecked)
{
UsedMap.OperationGrid[i].Check();
item.Check();
break;
}
}
@ -94,7 +376,7 @@ namespace Models.Game
/// If the operation is MULTIPLICATION, it returns the product of the values of the two dice.
/// If the operation is not one of the operations, it throws an ArgumentOutOfRangeException.
/// </returns>
private int ResultOperation(Operation o)
public int ResultOperation(Operation o)
{
int result = o switch
{
@ -105,8 +387,6 @@ namespace Models.Game
Operation.MULTIPLICATION => Dice2.Value * Dice1.Value,
_ => throw new ArgumentOutOfRangeException()
};
MarkOperationAsChecked(o);
return result;
}
@ -119,10 +399,26 @@ namespace Models.Game
/// <param name="result">The result of the dice operation to be placed in the cell.</param>
private void PlaceResult(Cell playerChoice, int result)
{
if (Turn == 1 || GameRules.NearCellIsValid(playerChoice, UsedMap.Boards))
IEnumerable<Cell> ValidCell =
from cell in UsedMap.Boards
where cell.Value == null
where cell.Valid == true
select cell;
foreach (var item in ValidCell)
{
playerChoice.Value = result;
BoardUpdated?.Invoke(this, EventArgs.Empty);
if (item.X == playerChoice.X && item.Y == playerChoice.Y)
{
if (result > 12 || (result > 6 && item.IsDangerous == true))
{
item.SetPenalty();
PlayerCell.SetPenalty();
}
item.Value = result;
OnPropertyChanged(nameof(UsedMap.Boards));
return;
}
}
}
@ -131,87 +427,126 @@ namespace Models.Game
/// </summary>
/// <param name="playerChoice"></param>
/// <param name="adjacentes"></param>
private void AddToRopePath(Cell playerChoice,List<Cell> adjacentes)
private void AddToRopePath(Cell playerChoice)
{
int index =0;
foreach (var cells in adjacentes)
{
// La cellule choisi peut creer/s'ajouter a un chemin de corde
if (cells.Value - playerChoice.Value == 1 || cells.Value - playerChoice.Value == -1)
{
// Le cas si il n'existe aucun chemin de corde
if (UsedMap.RopePaths.Count == 0)
{
// Creer un nouveau chemin de corde avec la cellule choisi par le joueur et celle adjacente
UsedMap.RopePaths.Add(new List<Cell> {playerChoice, cells});
}
if(Turn==1) return;
int index = 0;
// A modifier dans le cas ou il est possible de fusionner deux chemins de corde
if (GameRules.IsInRopePaths(playerChoice, UsedMap.RopePaths, index)) break;
IEnumerable<Cell> ValidCell =
from cell in UsedMap.Boards
where cell.Value != null && cell.Valid == true && cell != playerChoice
select cell;
// Le cas si il existe des chemins de corde
foreach (var item in ValidCell)
{
if (!GameRules.IsCellAdjacent(playerChoice, item))
continue;
if (!((playerChoice.Value - item.Value) == 1 || (playerChoice.Value - item.Value) == -1))
continue;
if (!GameRules.IsInRopePaths(item,UsedMap.RopePaths,index))
{
UsedMap.RopePaths.Add(new List<Cell> { playerChoice, item });
return;
}
if (!GameRules.AsValue(playerChoice, UsedMap.RopePaths, index))
{
UsedMap.RopePaths[index].Add(playerChoice);
return;
}
}
}
// Est-ce que la cellule adjacentes fait parti d'un chemin de corde
if (!GameRules.IsInRopePaths(cells,UsedMap.RopePaths,index))
{
UsedMap.RopePaths.Add(new List<Cell> { playerChoice, cells });
continue;
}
/// <summary>
/// Initializes the game.
/// </summary>
public void InitializeGame(Map map, Player player, bool startImmediately = true)
{
var runningGames = Games.Where(g => g.IsRunning).ToList();
foreach (var game in runningGames)
{
Games.Remove(game);
}
OnPropertyChanged(nameof(Games));
if (!GameRules.AsValue(playerChoice,UsedMap.RopePaths,index))
{
UsedMap.RopePaths[index].Add(playerChoice);
}
UsedMap = map;
CurrentPlayer = player;
Turn = 1;
Dice1 = new Dice();
Dice2 = new Dice(1);
// Si oui, est-ce que le chemin possede deja la valeur correspondante a la valeur de la cellule du joueur choisi
// {playerChoice.Value} n'est pas dans le chemin de corde
// Ajouter au chemin
// {playerChoice.Value} existe dans le chemin de corde pas possible
IsPreviousGameNotFinished = false;
OnPropertyChanged(nameof(UsedMap));
OnPropertyChanged(nameof(CurrentPlayer));
OnPropertyChanged(nameof(Turn));
}
if (startImmediately)
{
StartGame();
}
SaveData();
}
/// <summary>
/// Initializes the game.
/// Starts the game.
/// </summary>
public void InitializeGame()
private void StartGame()
{
_isRunning = true;
IsRunning = true;
GameStarted?.Invoke(this, new GameStartedEventArgs(CurrentPlayer));
GameLoop();
SaveData();
}
/// <summary>
/// Ends the game.
/// </summary>
private void EndGame()
private void EndGame(int? pts)
{
_isRunning = false;
GameEnded?.Invoke(this, new GameEndedEventArgs(CurrentPlayer));
IsRunning = false;
GameEnded?.Invoke(this, new GameEndedEventArgs(CurrentPlayer, pts));
}
/// <summary>
/// The main game loop that runs while the game is active.
/// </summary>
private void GameLoop()
public void GameLoop()
{
while (_isRunning)
while (IsRunning)
{
if (Turn == 20)
{
EndGame();
break;
}
RollAllDice();
Resultat = PlayerChooseOperation();
PlayerSelectionCell();
BoardUpdated?.Invoke(this, new BoardsUpdateEventArgs(UsedMap.Boards.ToList()));
Turn++;
}
}
public int PlayerChooseOperation()
{
PlayerChooseOp?.Invoke(this, new PlayerChooseOperationEventArgs(PlayerOperation));
while(!GameRules.OperationAvailable(PlayerOperation,UsedMap.OperationGrid.ToList()))
{
PlayerChooseOp?.Invoke(this, new PlayerChooseOperationEventArgs(PlayerOperation));
}
PlayerOption?.Invoke(this, new PlayerOptionEventArgs(UsedMap.Boards.ToList(), ResultOperation(PlayerOperation), Turn));
OperationChosenFlag = true;
return ResultOperation(PlayerOperation);
}
public void PlayerSelectionCell()
{
PlayerChooseCell?.Invoke(this,new PlayerChooseCellEventArgs(PlayerCell));
while(!GameRules.NearCellIsValid(PlayerCell,UsedMap.Boards.ToList()))
{
PlayerChooseCell?.Invoke(this, new PlayerChooseCellEventArgs(PlayerCell));
}
MarkOperationAsChecked(PlayerOperation);
PlaceResult(PlayerCell, Resultat);
DiceRolledFlag = false;
OperationChosenFlag = false;
}
/// <summary>
/// Handles the player's choice of an operation based on the dice values.s
@ -220,6 +555,7 @@ namespace Models.Game
public void HandlePlayerOperation(Operation operation)
{
int result = ResultOperation(operation);
OperationChosenFlag = true;
OperationChosen?.Invoke(this, new OperationChosenEventArgs(operation, result));
}
@ -230,23 +566,72 @@ namespace Models.Game
/// <param name="result"></param>
/// <exception cref="InvalidCellCoordinatesException"></exception>
/// <exception cref="InvalidCellException"></exception>
public void HandlePlayerChoice(Cell cell, int result)
public bool HandlePlayerChoice(Cell cell, int result)
{
if (cell.X < 0 || cell.X >= UsedMap.Boards.Count / 6 || cell.Y < 0 || cell.Y >= 6)
{
throw new InvalidCellCoordinatesException("Invalid cell coordinates. Please choose again.");
return false;
}
if (!GameRules.IsCellValid(cell, UsedMap.Boards))
if (!GameRules.IsCellValid(cell, UsedMap.Boards.ToList()))
{
throw new InvalidCellException("Cell is not valid. Please choose again.");
return false;
}
PlaceResult(cell, result);
GameRules.IsZoneValidAndAddToZones(cell, UsedMap);
AddToRopePath(cell, GameRules.EveryAdjacentCells(cell, UsedMap.Boards));
AddToRopePath(cell);
CellChosen?.Invoke(this, new CellChosenEventArgs(cell, result));
BoardUpdated?.Invoke(this, EventArgs.Empty);
return true;
}
/// <summary>
/// Event raised when a property is changed to notify the view.
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Trigger the PropertyChanged event for a specific property.
/// </summary>
/// <param name="propertyName">Name of the property that changed.</param>
public virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int CalculusOfPenalty(ReadOnlyCollection<Cell> Boards)
{
int result = 0;
foreach (var cells in Boards)
if (cells.Penalty)
{
if (!cells.Valid || cells.Value == null)
continue;
result += 3;
}
return result;
}
public void PutPenaltyForLostCells(ReadOnlyCollection<Cell> Boards)
{
foreach (var cells in Boards)
{
if (cells == null || cells.Value == null || !cells.Valid)
continue;
if (!UsedMap.IsCellInZones(cells) && !UsedMap.IsCellInRopePath(cells))
cells.SetPenalty();
}
}
public int FinalCalculusOfPoints()
{
int? points = GameRules.FinalCalculusOfZones(UsedMap.Zones);
for (int i = 0; i < UsedMap.RopePaths.Count; i++)
{
points += GameRules.ScoreRopePaths(UsedMap.RopePaths[i]);
}
points -= CalculusOfPenalty(UsedMap.Boards);
return points ?? 0;
}
}
}

@ -1,78 +1,152 @@
namespace Models.Game
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using SQLite;
namespace Models.Game
{
/// <summary>
/// The Map class is the representation of the game map with the board and the operations table.
/// </summary>
[DataContract, SQLite.Table("Maps")]
public class Map
{
/// <summary>
/// The displaying name of the map
/// </summary>
[DataMember, PrimaryKey]
public string Name { get; private set; }
/// <summary>
/// It is the list of cells on the map.
/// </summary>
public List<Cell> Boards { get; private set; }
[DataMember]
[Ignore]
public ReadOnlyObservableCollection<Cell> Boards { get; private set; }
private readonly ObservableCollection<Cell> _board = new ObservableCollection<Cell>();
/// <summary>
/// It is the backgrond image of the map
/// </summary>
[DataMember]
public string Background { get; private set; }
/// <summary>
/// It is the grid of the possible operation in the game
/// </summary>
public List<OperationCell> OperationGrid { get; private set; }
[DataMember]
public ReadOnlyObservableCollection<OperationCell> OperationGrid { get; private set; }
private readonly ObservableCollection<OperationCell> _operationGrid = new ObservableCollection<OperationCell>();
/// <summary>
/// It is a list of a list containing user's rope paths in the current game
/// </summary>
[DataMember]
public List<List<Cell>> RopePaths { get; private set; }
/// <summary>
/// It is a list of a list containing user's zones in the current game
/// </summary>
[DataMember]
public List<List<Cell>> Zones { get; private set; }
/// <summary>
/// Initializes a new instance of the Map class.
/// </summary>
/// <param name="name"></param>
/// <param name="background">The background of the map.</param>
public Map(string background)
public Map(string name, string background)
{
Boards = InitializeBoards();
Name = name;
Background = background;
OperationGrid = InitializeOperationGrid();
Boards = new ReadOnlyObservableCollection<Cell>(_board);
InitializeBoards(_board);
OperationGrid = new ReadOnlyObservableCollection<OperationCell>(_operationGrid);
InitializeOperationGrid(_operationGrid);
RopePaths = new List<List<Cell>>();
Zones = new List<List<Cell>>();
}
/// <summary>
/// SQLite constructor
/// </summary>
public Map() { }
/// <summary>
/// Clone the map to avoid reference problems.
/// </summary>
/// <returns></returns>
public Map Clone()
{
Map map = new Map(Name, Background);
return map;
}
/// <summary>
/// Initializes the boards of the map.
/// </summary>
/// <returns>Return the boards</returns>
private List<Cell> InitializeBoards()
private void InitializeBoards(ObservableCollection<Cell> board)
{
var boards = new List<Cell>();
for (int i = 0; i < 36; i++) // 6x6 board
for (int i = 0; i < 49; i++) // 7x7 board
{
boards.Add(new Cell(i / 6, i % 6));
board.Add(new Cell(i % 7, i / 7));
}
return boards;
board[1].Valid = true;
board[3].Valid = true;
board[4].Valid = true;
board[5].Valid = true;
board[9].Valid = true;
board[12].Valid = true;
board[15].Valid = true;
board[16].Valid = true;
board[17].Valid = true;
board[21].Valid = true;
board[24].Valid = true;
board[25].Valid = true;
board[28].Valid = true;
board[30].Valid = true;
board[31].Valid = true;
board[32].Valid = true;
board[40].Valid = true;
board[41].Valid = true;
board[48].Valid = true;
board[3].IsDangerous = true;
board[15].IsDangerous = true;
board[16].IsDangerous = true;
}
/// <summary>
/// Initializes the operation grid of the map.
/// </summary>
/// <returns>Return the operation grid</returns>
private List<OperationCell> InitializeOperationGrid()
private void InitializeOperationGrid(ObservableCollection<OperationCell>operationGrid)
{
var operationGrid = new List<OperationCell>();
for (int i = 0; i < 5; i++) // 5 operations
{
for (int j = 0; j < 4; j++) // 4 cells per operation
{
operationGrid.Add(new OperationCell(i, j));
operationGrid.Add(new OperationCell(j, i));
}
}
return operationGrid;
}
public bool CheckOperationPossible(int x)
{
return OperationGrid[x * 4 + 3].IsChecked;
}
public bool IsCellInZones(Cell cell)
{
return Zones.Any(zone => zone.Contains(cell));
}
public bool IsCellInRopePath(Cell cell)
{
return RopePaths.Any(path => path.Contains(cell));
}
}
}

@ -16,5 +16,7 @@ namespace Models.Game
SUBTRACTION,
ADDITION,
MULTIPLICATION
}
}

@ -1,14 +1,30 @@
using System.ComponentModel;
using System.Runtime.Serialization;
namespace Models.Game
{
/// <summary>
/// Represents a cell in the operation grid of the game.
/// </summary>
public class OperationCell : Position
[DataContract]
public class OperationCell : Position, INotifyPropertyChanged
{
/// <summary>
/// It tells if the operation is checked or not in the operation grid of the game.
/// </summary>
public bool IsChecked { get; private set; }
[DataMember]
private bool isChecked;
public bool IsChecked
{
get => isChecked;
set
{
if (isChecked == value)
return;
isChecked = value;
OnPropertyChanged(nameof(IsChecked));
}
}
/// <summary>
/// Constructor of the OperationCell class.
@ -17,6 +33,17 @@ namespace Models.Game
/// <param name="y"></param>
public OperationCell(int x, int y) : base(x, y)
{
IsChecked = false;
}
public event PropertyChangedEventHandler? PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
/// <summary>

@ -1,59 +1,77 @@
namespace Models.Game
using SQLite;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using Models.Interfaces;
namespace Models.Game
{
/// <summary>
/// Represents a player in the game.
/// </summary>
public class Player
[DataContract, SQLite.Table("Players")]
public class Player : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
void OnPropertyChanged([CallerMemberName] string? propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
/// <summary>
/// It is he pseudo of the player.
/// </summary>
public string Pseudo { get; private set; }
private string _pseudo;
[DataMember, PrimaryKey]
public string Pseudo
{
get => _pseudo;
set
{
if (_pseudo == value)
return;
_pseudo = value;
OnPropertyChanged();
}
}
/// <summary>
/// It is the profile picture of the player.
/// </summary>
[DataMember]
public string ProfilePicture { get; private set; }
/// <summary>
/// It is the creation date of the player.
/// </summary>
public string? CreationDate { get; private set; }
[DataMember]
public string CreationDate { get; private set; }
/// <summary>
/// It tells when was the last time the player played.
/// </summary>
[DataMember]
public string? LastPlayed { get; private set; }
/// <summary>
/// Constructor with default values.
/// </summary>
public Player()
{
Pseudo = "Player";
ProfilePicture = "DefaultProfilePicture";
}
/// <summary>
/// Construct a new instance of Player with specified pseudo and profile picture.
/// </summary>
/// <param name="pseudo">The pseudo of the player.</param>
/// <param name="profilePicture">The profile picture of the player.</param>
public Player(string pseudo, string profilePicture = "DefaultProfilePicture")
public Player(string pseudo, string profilePicture)
{
Pseudo = pseudo;
ProfilePicture = profilePicture;
CreationDate = DateTime.Now.ToString("dd/MM/yyyy");
}
/// <summary>
/// Chooses the operation for the player.
/// SQLite constructor
/// </summary>
/// <returns>The chosen operation.</returns>
public Operation ChooseOperation()
{
return Operation.LOWER;
}
public Player() { }
/// <summary>
/// Redefine the equal operation between player.
@ -78,5 +96,13 @@
{
return Pseudo.GetHashCode();
}
/// <summary>
/// Actualize the last time the player played.
/// </summary>
public void UpdateLastPlayed()
{
LastPlayed = DateTime.Now.ToString("dd/MM/yyyy");
}
}
}

@ -1,19 +1,26 @@
namespace Models.Game
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
namespace Models.Game
{
/// <summary>
/// The Position (x,y) of a cell in the game.
/// </summary>
[DataContract]
public class Position
{
/// <summary>
/// The X coordinate.
/// </summary>
[DataMember]
public int X { get; set; }
/// <summary>
/// The Y coordinate.
/// </summary>
[DataMember]
public int Y { get; set; }
/// <summary>

@ -0,0 +1,24 @@
using Microsoft.Maui.Controls;
namespace Models.Interfaces
{
/// <summary>
/// Interface for image converters
/// </summary>
public interface IImageConverter
{
/// <summary>
/// Converter to any type that can be converted to a string (ex: Base64)
/// </summary>
/// <param name="imagePath"></param>
/// <returns></returns>
string ConvertImage(string imagePath);
/// <summary>
/// Retrieve an image from a string encoded
/// </summary>
/// <param name="imageString"></param>
/// <returns></returns>
ImageSource RetrieveImage(string imageString);
}
}

@ -0,0 +1,12 @@
using System.Collections.ObjectModel;
namespace Models.Interfaces
{
using Models.Game;
public interface IPersistence
{
(ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData();
void SaveData(ObservableCollection<Player> players, ObservableCollection<Game> games, ObservableCollection<Map> maps, ObservableCollection<BestScore> bestScores);
}
}

@ -8,8 +8,6 @@ namespace Models.Interfaces
/// </summary>
public interface IRules
{
//public bool NearCellIsValid(Position playerChoicePosition, SortedDictionary<Position, Cell> cells);
/// <summary>
/// Return true if the cell is empty, otherwise false.
/// </summary>
@ -25,15 +23,6 @@ namespace Models.Interfaces
/// <returns>true if the cell is valid; otherwise, false</returns>
public bool IsCellValid(Cell playerChoicePosition, List<Cell> cells);
//public bool IsRopePath(Cell playerChoice, HashSet<RopePath> ropePaths);
//public bool IsAdjacent(Cell cell1, Cell cell2, List<Position, Cell> cells);
//public int HowMany(Cell playerChoice, List<Position, Cell> cells);
//public void SetValueAndPenalty(int valueChoice, Cell playerChoice, List<Position, Cell> cells);
/// <summary>
/// Check if the given cell is adjacent to the target cell.
/// </summary>

@ -7,9 +7,14 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="Game\" />
<Folder Include="Interfaces\" />
<Folder Include="Rules\" />
<Folder Include="Game\" />
<Folder Include="Interfaces\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls.Core" Version="8.0.3" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
</ItemGroup>
</Project>

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -24,40 +25,32 @@ namespace Models.Rules
{
if (!IsCellEmpty(playerChoicePosition)) return false;
if (!playerChoicePosition.Valid) return false;
if (EveryAdjacentCells(playerChoicePosition, cells).Count == 1) return false;
return true;
}
public bool IsCellAdjacent(Cell choosenCell, Cell targetCell)
{
if (Math.Abs(choosenCell.X - targetCell.X) > 1 || Math.Abs(choosenCell.Y - targetCell.Y) > 1)
return false;
if (Math.Abs(choosenCell.X - targetCell.X) > 1 && Math.Abs(choosenCell.Y - targetCell.Y) > 1)
return false;
if (choosenCell.X == 0 && targetCell.X == 4)
return false;
if (choosenCell.Y == 0 && targetCell.Y == 4)
return false;
if (choosenCell.X == 4 && targetCell.X == 0)
return false;
if (choosenCell.Y == 4 && targetCell.Y == 0)
return false;
if (choosenCell.X == targetCell.X && choosenCell.Y == targetCell.Y)
return false;
if (Math.Abs(choosenCell.X - targetCell.X) <= 1 && Math.Abs(choosenCell.Y - targetCell.Y) <= 1)
{
return true;
}
return false;
}
return true;
}
public bool IsInRopePaths (Cell adjacente,List<List<Cell>> ropePaths,int index)
public bool IsInRopePaths (Cell adjacente,List<List<Cell>> ropePaths, int index)
{
foreach (List<Cell> path in ropePaths)
{
if (path.Contains(adjacente))
{
index=ropePaths.IndexOf(path);
return true;
}
if (!path.Contains(adjacente)) continue;
index=ropePaths.IndexOf(path);
return true;
}
return false;
}
@ -70,32 +63,36 @@ namespace Models.Rules
}
return false;
}
public bool NearCellIsValid(Cell choosenCell, List<Cell> cells)
{
if (choosenCell == null || cells == null) return false;
IEnumerable<Cell> PlayedCellsQuery =
from cell in cells
where cell.Value != null
where cell.Valid
select cell;
foreach (var cell in PlayedCellsQuery)
{
if(!IsCellAdjacent(choosenCell, cell)) continue;
return true;
if (choosenCell.X == cell.X && choosenCell.Y == cell.Y && cell.Value == null)
return true;
if (IsCellAdjacent(choosenCell, cell))
return true;
}
return false;
}
public void IsZoneValidAndAddToZones(Cell chosenCell, Map map)
{
if (chosenCell == null ||chosenCell.Value == null) return;
List<Cell> adjacentCells = new List<Cell>();
List<Cell> adjacentCells;
adjacentCells = EveryAdjacentCells(chosenCell, map.Boards);
adjacentCells = EveryAdjacentCells(chosenCell, map.Boards.ToList());
foreach(var cells in adjacentCells)
{
@ -109,12 +106,8 @@ namespace Models.Rules
{
NewZoneIsCreated(chosenCell, cells, map);
}
//return true; // Il y a une cellule adjacente avec la même valeur donc une zone est créée si elle n'est pas déjà existante
// Si il return true, tout c'est bien passer
}
}
return;
}
public bool IsValueInZones(Cell chosenCell, List<List<Cell>> zones)
@ -159,7 +152,6 @@ namespace Models.Rules
return;
}
}
return;
}
public void NewZoneIsCreated(Cell firstCell, Cell secondCell, Map map)
@ -171,9 +163,11 @@ namespace Models.Rules
map.Zones.Add(newZone);
}
public List<Cell> EveryAdjacentCells(Cell choosenCell, List<Cell> cells)
public List<Cell> EveryAdjacentCells(Cell? choosenCell, List<Cell> cells)
{
List<Cell> adjacentCells = new List<Cell>();
if (choosenCell == null || cells == null) return adjacentCells;
foreach (var cell in cells)
{
@ -195,11 +189,42 @@ namespace Models.Rules
calculus += zones[i].Count - 1 + zones[i][0].Value;
if (zones[i].Count > 9)
{
calculus += (zones[i].Count - 9) * 5;
calculus += (zones[i].Count - 9) * 5 - (zones[i].Count - 9);
}
}
return calculus;
}
public int? ScoreRopePaths(List<Cell> paths)
{
int? score = 0;
IEnumerable<Cell> sortPaths =
from cell in paths
orderby cell.Value descending
select cell;
foreach (var item in sortPaths)
{
if (score == 0)
score += item.Value;
else
score++;
}
return score;
}
public bool OperationAvailable(Operation operation,List<OperationCell> operationGrid)
{
IEnumerable<OperationCell> row =
from cell in operationGrid
where cell.Y == (int)operation
select cell;
foreach (var item in row)
{
if (!item.IsChecked)
return true;
}
return false;
}
}
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Models\Models.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Models.Game;
using Models.Interfaces;
using Moq;
namespace Tests
{
public class Base64ConverterTests
{
/*
public interface IImageConverter
{
/// <summary>
/// Converter to any type that can be converted to a string (ex: Base64)
/// </summary>
/// <param name="imagePath"></param>
/// <returns></returns>
string ConvertImage(string imagePath);
/// <summary>
/// Retrieve an image from a string encoded
/// </summary>
/// <param name="imageString"></param>
/// <returns></returns>
ImageSource RetrieveImage(string imageString);
}
*/
/*
public class Base64Converter : IImageConverter
{
/// <summary>
/// Converts an image to a base64 string
/// </summary>
/// <param name="imagePath">The path to access the image</param>
/// <returns>The base64 string representation of the image</returns>
/// <exception cref="FileNotFoundException">Native .NET exception</exception>
public string ConvertImage(string imagePath)
{
if (!File.Exists(imagePath))
{
// native .NET exception
throw new FileNotFoundException("Image file not found", imagePath);
}
byte[] imageBytes = File.ReadAllBytes(imagePath);
return Convert.ToBase64String(imageBytes);
}
/// <summary>
/// Retrieve an image from a string encoded in base64
/// </summary>
/// <param name="imageString"></param>
/// <returns></returns>
public ImageSource RetrieveImage(string imageString)
{
byte[] imageBytes = Convert.FromBase64String(imageString);
return ImageSource.FromStream(() => new MemoryStream(imageBytes));
}
}
*/
// Create a mock
private Mock<IImageConverter> _imageConverterMock;
public Base64ConverterTests()
{
_imageConverterMock = new Mock<IImageConverter>();
}
[Fact]
public void ConvertImage_WithNonExistentPath_ThrowsFileNotFoundException()
{
var base64Converter = new Base64Converter();
Assert.Throws<FileNotFoundException>(() => base64Converter.ConvertImage("nonexistent.jpg"));
}
[Fact]
public void RetrieveImage_WithInvalidBase64_ThrowsFormatException()
{
var base64Converter = new Base64Converter();
Assert.Throws<FormatException>(() => base64Converter.RetrieveImage("////invalidBase64//!!///"));
}
}
}

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using Models.Game;
@ -12,7 +13,11 @@ namespace Tests
[Fact]
public void Constructor_WithNegativeInputs_SetsPropertiesToZero()
{
var bestScore = new BestScore(-5, -10);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, -5, -10);
Assert.Equal(0, bestScore.GamesPlayed);
Assert.Equal(0, bestScore.Score);
}
@ -20,7 +25,11 @@ namespace Tests
[Fact]
public void Constructor_WithPositiveInputs_SetsPropertiesCorrectly()
{
var bestScore = new BestScore(5, 10);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 5, 10);
Assert.Equal(5, bestScore.GamesPlayed);
Assert.Equal(10, bestScore.Score);
}
@ -28,25 +37,99 @@ namespace Tests
[Fact]
public void IncrGamesPlayed_IncrementsGamesPlayed()
{
var bestScore = new BestScore(0, 0);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 0, 0);
bestScore.IncrGamesPlayed();
Assert.Equal(1, bestScore.GamesPlayed);
}
[Fact]
public void ScoreSetter_WithLowerValue_DoesNotChangeScore()
{
var bestScore = new BestScore(0, 10);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 0, 10);
bestScore.UpdateScore(5);
Assert.Equal(10, bestScore.Score);
Assert.Equal(15, bestScore.Score);
}
[Fact]
public void ScoreSetter_WithHigherValue_ChangesScore()
{
var bestScore = new BestScore(0, 5);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 0, 5);
bestScore.UpdateScore(10);
Assert.Equal(10, bestScore.Score);
Assert.Equal(15, bestScore.Score);
}
[Fact]
public void IdGetter_ReturnsCorrectId()
{
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 5, 10);
bestScore.Id = 1;
Assert.Equal(1, bestScore.Id);
}
[Fact]
public void Equals_WithEqualBestScores_ReturnsTrue()
{
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore1 = new BestScore(myMap.Name, myPlayer, 5, 10);
var bestScore2 = new BestScore(myMap.Name, myPlayer, 5, 10);
Assert.True(bestScore1.Equals(bestScore2));
}
[Fact]
public void Equals_WithDifferentBestScores_ReturnsFalse()
{
var myMap1 = new Map("Dunai", "Dunai.png");
var myMap2 = new Map("Amazon", "Amazon.png");
var myPlayer = new Player("John", "pp.png");
var bestScore1 = new BestScore(myMap1.Name, myPlayer, 5, 10);
var bestScore2 = new BestScore(myMap2.Name, myPlayer, 5, 10);
Assert.False(bestScore1.Equals(bestScore2));
}
[Fact]
public void GetHashCode_WithEqualBestScores_ReturnsSameHashCode()
{
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore1 = new BestScore(myMap.Name, myPlayer, 5, 10);
var bestScore2 = new BestScore(myMap.Name, myPlayer, 5, 10);
Assert.Equal(bestScore1.GetHashCode(), bestScore2.GetHashCode());
}
[Fact]
public void GetHashCode_WithDifferentBestScores_ReturnsDifferentHashCodes()
{
var myMap1 = new Map("Dunai", "Dunai.png");
var myMap2 = new Map("Amazon", "Amazon.png");
var myPlayer = new Player("John", "pp.png");
var bestScore1 = new BestScore(myMap1.Name, myPlayer, 5, 10);
var bestScore2 = new BestScore(myMap2.Name, myPlayer, 5, 10);
Assert.NotEqual(bestScore1.GetHashCode(), bestScore2.GetHashCode());
}
}
}

@ -13,30 +13,37 @@ public class DiceTests
public void Constructor_WithNegativeNbMin_SetsNbMinToZero()
{
Dice dice = new Dice(-1);
Assert.Equal(0, dice.NbMin);
Assert.Equal(0, dice.MinVal);
}
[Fact]
public void Constructor_WithValueMoreThan5_SetsValueTo0()
{
Dice dice = new Dice(6);
Assert.Equal(0, dice.Value);
}
[Fact]
public void Constructor_WithNbMinGreaterThanOne_SetsNbMinToOne()
{
Dice dice = new Dice(2);
Assert.Equal(1, dice.NbMin);
Assert.Equal(1, dice.MinVal);
}
[Fact]
public void Constructor_WithValidNbMin_SetsNbMinAndNbMaxCorrectly()
{
Dice dice = new Dice(1);
Assert.Equal(1, dice.NbMin);
Assert.Equal(6, dice.NbMax);
Assert.Equal(1, dice.MinVal);
Assert.Equal(6, dice.MaxVal);
}
[Fact]
public void DefaultConstructor_SetsNbMinToZeroAndNbMaxToFive()
{
Dice dice = new Dice();
Assert.Equal(0, dice.NbMin);
Assert.Equal(5, dice.NbMax);
Assert.Equal(0, dice.MinVal);
Assert.Equal(5, dice.MaxVal);
}
[Fact]
@ -44,6 +51,6 @@ public class DiceTests
{
Dice dice = new Dice();
dice.Roll();
Assert.True(dice.Value >= dice.NbMin && dice.Value <= dice.NbMax);
Assert.True(dice.Value >= dice.MinVal && dice.Value <= dice.MaxVal);
}
}

@ -0,0 +1,694 @@
using System.Collections.ObjectModel;
using Moq;
using Models.Game;
using Models.Interfaces;
using System.Reflection;
using static System.Type;
using Xunit.Abstractions;
namespace Tests;
public class GameTests
{
private readonly Mock<IPersistence> _mockPersistence; // Mocking (faking) the persistence layer to allow for testing without a persistent connection
private readonly Game _game;
public GameTests()
{
_mockPersistence = new Mock<IPersistence>();
_game = new Game(_mockPersistence.Object);
}
private void SetDiceValues(Game game, int value1, int value2)
{
var dice1Field = typeof(Game).GetProperty("Dice1", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var dice2Field = typeof(Game).GetProperty("Dice2", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (dice1Field != null && dice2Field != null)
{
var dice1 = (Dice)dice1Field?.GetValue(game)!;
var dice2 = (Dice)dice2Field?.GetValue(game)!;
var valueField = typeof(Dice).GetField("_value", BindingFlags.Instance | BindingFlags.NonPublic);
valueField?.SetValue(dice1, value1);
valueField?.SetValue(dice2, value2);
}
}
[Fact]
public void DefaultConstructor_ShouldInitializeLists()
{
Assert.NotNull(_game.Players);
Assert.NotNull(_game.Games);
Assert.NotNull(_game.Maps);
Assert.NotNull(_game.BestScores);
Assert.NotNull(_game.GameRules);
Assert.False(_game.IsRunning);
}
[Fact]
public void AddPlayer_ShouldAddPlayerToList()
{
var player = new Player("test_player", "DefaultProfilePicture");
_game.AddPlayer(player);
Assert.Contains(player, _game.Players);
}
[Fact]
public void AddGame_ShouldAddGameToList()
{
var game = new Game(_mockPersistence.Object);
_game.AddGame(game);
Assert.Contains(game, _game.Games);
}
[Fact]
public void AddMap_ShouldAddMapToList()
{
var map = new Map("test_name", "test_background.png");
_game.AddMap(map);
Assert.Contains(map, _game.Maps);
}
[Fact]
public void AddBestScore_ShouldAddBestScoreToList()
{
var bestScore = new BestScore("test", new Player("John", "Picture.png"), 3, 127);
_game.BestScores.Add(bestScore);
Assert.Contains(bestScore, _game.BestScores);
}
[Fact]
public void LoadData_ShouldLoadDataFromPersistence()
{
var myGame = new Game(_mockPersistence.Object);
var myPlayer = new Player("test", "DefaultProfilePicture");
var myMap = new Map("test_name", "test_background.png");
var players = new ObservableCollection<Player> { myPlayer };
var games = new ObservableCollection<Game> { myGame };
var maps = new ObservableCollection<Map> { myMap };
var bestScores = new ObservableCollection<BestScore> { new BestScore(myMap.Name, myPlayer, 1, 45) };
_mockPersistence.Setup(p => p.LoadData()).Returns((players, games, maps, bestScores));
_game.LoadData();
Assert.Equal(players, _game.Players);
Assert.Equal(games, _game.Games);
Assert.Equal(maps, _game.Maps);
Assert.Equal(bestScores, _game.BestScores);
}
[Fact]
public void SaveData_ShouldCallPersistenceSaveData()
{
_game.SaveData();
_mockPersistence.Verify(p => p.SaveData(_game.Players, _game.Games, _game.Maps, _game.BestScores), Times.Once); // Times.Once is to verify if the method is called exactly one time
}
[Fact]
public void InitializeGame_ShouldInitializeGameAndNotTriggerEventWhenNotStarted()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
bool eventTriggered = false;
_game.GameStarted += (sender, args) =>
{
eventTriggered = true;
};
_game.InitializeGame(map, player, false);
Assert.False(eventTriggered);
Assert.False(_game.IsRunning);
Assert.Equal(map, _game.UsedMap);
Assert.Equal(player, _game.CurrentPlayer);
}
[Theory]
[InlineData(Operation.ADDITION, 3, 4, 7)]
[InlineData(Operation.SUBTRACTION, 6, 4, 2)]
[InlineData(Operation.MULTIPLICATION, 2, 3, 6)]
[InlineData(Operation.LOWER, 1, 4, 1)]
[InlineData(Operation.HIGHER, 2, 5, 5)]
public void HandlePlayerOperation_ShouldPerformCorrectOperationAndTriggerEvent(Operation operation, int value1, int value2, int expectedResult)
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
bool eventTriggered = false;
int actualResult = 0;
_game.OperationChosen += (sender, args) =>
{
eventTriggered = true;
actualResult = args.Result;
};
SetDiceValues(_game, value1, value2);
_game.HandlePlayerOperation(operation);
Assert.True(eventTriggered);
Assert.Equal(expectedResult, actualResult);
}
[Fact]
public void Game_Initialization_SetsMap()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
Assert.Equal(map, _game.UsedMap);
}
[Fact]
public void Game_Initialization_SetsPlayer()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
Assert.Equal(player, _game.CurrentPlayer);
}
[Fact]
public void Game_Initialization_SetsDice()
{
Player player = new Player("test_player", "DefaultProfilePicture");
Map map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
Assert.NotNull(_game.Dice1);
Assert.NotNull(_game.Dice2);
}
[Fact]
public void Game_Initialization_SetsGameRules()
{
var game = new Game(_mockPersistence.Object);
Assert.NotNull(game.GameRules);
}
[Fact]
public void MarkOperationAsChecked_Check_Well()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
// Use of reflection to call private method
var methodInfo = typeof(Game).GetMethod("MarkOperationAsChecked", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
var operation = Operation.ADDITION;
methodInfo.Invoke(_game, new object[] { operation });
int operationIndex = (int)operation;
int operationsPerType = 4;
bool isChecked = false;
for (int i = operationIndex * operationsPerType; i < (operationIndex + 1) * operationsPerType; i++)
{
if (_game.UsedMap.OperationGrid[i].IsChecked)
{
isChecked = true;
break;
}
}
Assert.True(isChecked);
}
[Fact]
public void IsPlaceOperationCorrect()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
Assert.NotNull(_game.GameRules);
_game.UsedMap.Boards[0].Value = 1;
_game.UsedMap.Boards[1].Value = 2;
_game.UsedMap.Boards[0].Valid = true;
_game.UsedMap.Boards[1].Valid = true;
_game.UsedMap.Boards[2].Valid = true;
var methodInfo = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
_game.PlayerCell = new Cell(2, 0);
_game.PlayerCell.Value = 3;
methodInfo.Invoke(_game, new object[] { _game.PlayerCell, 3 });
//_game.UsedMap.Boards[2].Value = _game.PlayerCell.Value;
Assert.Equal(3, _game.UsedMap.Boards[2].Value);
}
[Fact]
public void IsHandlePlayerChoice_Handling()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
var cell = new Cell(1, 0);
cell.Valid = true;
_game.UsedMap.Boards[0].Valid = true;
_game.UsedMap.Boards[0].Value = 1;
_game.UsedMap.Boards[1].Valid = true;
bool result = _game.HandlePlayerChoice(cell, 1);
Assert.True(result);
}
[Fact]
public void IsHandlePlayerChoice_InvalidCell()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
var cell = new Cell(0, 7);
cell.Value = 1;
bool result = _game.HandlePlayerChoice(cell, 1);
Assert.False(result);
}
[Fact]
public void IsHandlePlayerChoice_InvalidPlace()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
var cell = new Cell(0, 0);
cell.Value = 1;
var othercell = new Cell(3, 3);
bool result = _game.HandlePlayerChoice(othercell, 1);
Assert.False(result);
}
[Fact]
public void RemovePlayerTest_ShouldRemovePlayer()
{
Game game = new Game();
Player player = new Player("test", "DefaultProfilePicture");
game.AddPlayer(player);
Assert.True(game.RemovePlayer("test"));
}
[Fact]
public void RemovePlayerTest_ShouldNotRemovePlayer()
{
Game game = new Game();
Player player = new Player("test", "DefaultProfilePicture");
game.AddPlayer(player);
Assert.False(game.RemovePlayer("otherName"));
}
[Fact]
public void PutPenalty_ShouldPutPenalty()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[1].Valid = true;
_game.UsedMap.Boards[2].Valid = true;
_game.UsedMap.Boards[1].Value = 5;
var methodInfo = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
_game.PlayerCell = new Cell(2, 0);
_game.PlayerCell.Value = 14;
methodInfo.Invoke(_game, new object[] { _game.PlayerCell, 14 });
Assert.True(_game.UsedMap.Boards[2].Penalty);
}
[Fact]
public void PutPenalty_ShouldPutPenalty_ForDangerous()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[1].Valid = true;
_game.UsedMap.Boards[2].Valid = true;
_game.UsedMap.Boards[1].Value = 5;
_game.UsedMap.Boards[2].IsDangerous = true;
var methodInfo = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
_game.PlayerCell = new Cell(2, 0);
_game.PlayerCell.Value = 7;
methodInfo.Invoke(_game, new object[] { _game.PlayerCell, 7 });
Assert.True(_game.UsedMap.Boards[2].Penalty);
}
[Fact]
public void PutPenaltyForLostCells_ReallyPutPenalty_ForZones()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[0].Valid = true;
_game.UsedMap.Boards[3].Valid = true;
_game.UsedMap.Boards[2].Valid = true;
_game.UsedMap.Boards[1].Valid = true;
_game.UsedMap.Boards[1].Value = 5;
_game.UsedMap.Boards[2].Value = 5;
_game.UsedMap.Boards[3].Value = 5;
_game.UsedMap.Boards[0].Value = 0;
foreach (var cells in _game.UsedMap.Boards.ToList())
{
_game.GameRules.IsZoneValidAndAddToZones(cells, _game.UsedMap);
}
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[0].Penalty);
}
[Fact]
public void PutPenaltyForLostCells_ReallyPutPenalty_ForRopePathes()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[1].Value = 1;
_game.UsedMap.Boards[2].Value = 2;
_game.UsedMap.Boards[3].Value = 5;
_game.UsedMap.Boards[0].Value = 0;
var methodInfo = typeof(Game).GetMethod("AddToRopePath", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
foreach (var cells in _game.UsedMap.Boards.ToList())
{
methodInfo.Invoke(_game, new object[] { cells });
}
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[3].Penalty);
}
[Fact]
public void CalculusOfPenalty_ReallyCalculusPenalty_ForZonesAndDangerousCellsAndOverTwelve()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[7].Valid = true;
_game.UsedMap.Boards[8].Valid = true;
_game.UsedMap.Boards[9].Valid = true;
_game.UsedMap.Boards[10].Valid = true;
_game.UsedMap.Boards[11].Valid = true;
_game.UsedMap.Boards[12].Valid = true;
_game.UsedMap.Boards[10].Value = 2; // 1,3 // penalty
_game.UsedMap.Boards[7].Value = 5; // 1,0
_game.UsedMap.Boards[8].Value = 5; // 1,1
_game.UsedMap.Boards[9].Value = 5; // 1,2
var place = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(place);
_game.PlayerCell = new Cell(4, 1);
_game.PlayerCell.Value = 7;
_game.PlayerCell.Valid = true;
_game.PlayerCell.IsDangerous = true;
place.Invoke(_game, new object[] { _game.PlayerCell, 7 }); //One penalty
_game.PlayerCell = new Cell(5, 1);
_game.PlayerCell.Value = 14;
_game.PlayerCell.Valid = true;
place.Invoke(_game, new object[] { _game.PlayerCell, 14 });
foreach (var cells in _game.UsedMap.Boards.ToList())
{
_game.GameRules.IsZoneValidAndAddToZones(cells, _game.UsedMap);
}
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[11].Penalty);
Assert.Equal(9, _game.CalculusOfPenalty(_game.UsedMap.Boards));
}
[Fact]
public void CanIModifyAPlayer()
{
Game game = new Game();
Player player = new Player("test", "DefaultProfilePicture");
game.AddPlayer(player);
game.ModifyPlayer("test", "newName");
Assert.Equal("newName", game.Players[0].Pseudo);
}
[Fact]
public void CanIModifyANonExistentPlayer()
{
Game game = new Game();
var res = game.ModifyPlayer("nope", "newName");
Assert.False(res);
}
[Fact]
public void CanIRollDice()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
_game.RollAllDice();
Assert.NotNull(_game.Dice1);
Assert.NotNull(_game.Dice2);
Assert.True(_game.Dice1.Value >= 0 && _game.Dice1.Value <= 5 );
Assert.True(_game.Dice2.Value >= 1 && _game.Dice2.Value <= 6 );
}
[Fact]
public void CanIStartGame()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
var start = typeof(Game).GetMethod("StartGame", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(start);
start.Invoke(_game, null);
Assert.True(_game.IsRunning);
}
[Fact]
public void CanIEndGame()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
var start = typeof(Game).GetMethod("StartGame", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(start);
start.Invoke(_game, null);
var end = typeof(Game).GetMethod("EndGame", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(end);
end.Invoke(_game, new object[] { 14 } );
Assert.False(_game.IsRunning);
}
[Fact]
public void CalculusOfPointsWorksWellOrNot()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
_game.UsedMap.Boards[7].Valid = true;
_game.UsedMap.Boards[8].Valid = true;
_game.UsedMap.Boards[9].Valid = true;
_game.UsedMap.Boards[10].Valid = true;
_game.UsedMap.Boards[11].Valid = true;
_game.UsedMap.Boards[12].Valid = true;
_game.UsedMap.Boards[10].Value = 2; // penalty (- 3)
_game.UsedMap.Boards[7].Value = 5; //5 + 2 = 7
_game.UsedMap.Boards[8].Value = 5;
_game.UsedMap.Boards[9].Value = 5;
var place = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(place);
_game.PlayerCell = new Cell(4, 1);
_game.PlayerCell.Value = 7;
_game.PlayerCell.Valid = true;
_game.PlayerCell.IsDangerous = true;
place.Invoke(_game, new object[] { _game.PlayerCell, 7 }); //One penalty
foreach (var cells in _game.UsedMap.Boards.ToList())
{
_game.GameRules.IsZoneValidAndAddToZones(cells, _game.UsedMap);
}
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[11].Penalty);
Assert.Equal(1, _game.FinalCalculusOfPoints());
}
[Fact]
public void CalculusOfPointsWorksWellOrNotForRopePathes()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
var methodInfo = typeof(Game).GetMethod("AddToRopePath", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
_game.Turn = 2;
_game.UsedMap.Boards[7].Valid = true;
_game.UsedMap.Boards[8].Valid = true;
_game.UsedMap.Boards[9].Valid = true;
_game.UsedMap.Boards[10].Valid = true;
_game.UsedMap.Boards[7].Value = 5; //7 + 2 = 9
methodInfo.Invoke(_game, new object[] { _game.UsedMap.Boards[7] });
_game.UsedMap.Boards[8].Value = 6;
methodInfo.Invoke(_game, new object[] { _game.UsedMap.Boards[8] });
_game.UsedMap.Boards[9].Value = 7;
methodInfo.Invoke(_game, new object[] { _game.UsedMap.Boards[9] });
_game.UsedMap.Boards[10].Value = 2; // penalty (- 3)
methodInfo.Invoke(_game, new object[] { _game.UsedMap.Boards[10] });
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[10].Penalty);
Assert.Equal(6, _game.FinalCalculusOfPoints());
}
[Fact]
public void AddBestScore_AddsNewBestScoreToList()
{
var game = new Game(_mockPersistence.Object);
var player = new Player("John Doe", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player, false);
game.AddBestScore(100);
Assert.Single(game.BestScores);
Assert.Equal(100, game.BestScores[0].Score);
Assert.Equal(player, game.BestScores[0].ThePlayer);
Assert.Equal(map.Name, game.BestScores[0].MapName);
}
[Fact]
public void AddBestScore_UpdatesExistingBestScoreAndIncrementsGamesPlayed()
{
var game = new Game(_mockPersistence.Object);
var player = new Player("John Doe", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player, false);
game.AddBestScore(100);
game.AddBestScore(200);
Assert.Single(game.BestScores);
Assert.Equal(300, game.BestScores[0].Score);
Assert.Equal(2, game.BestScores[0].GamesPlayed);
Assert.Equal(player, game.BestScores[0].ThePlayer);
Assert.Equal(map.Name, game.BestScores[0].MapName);
}
[Fact]
public void AddBestScore_SortsBestScoresCorrectly()
{
var game = new Game(_mockPersistence.Object);
var player1 = new Player("John Doe", "DefaultProfilePicture");
var player2 = new Player("John Does", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player1, false);
game.AddBestScore(100);
game.InitializeGame(map, player2, false);
game.AddBestScore(200);
Assert.Equal(2, game.BestScores.Count);
Assert.Equal(200, game.BestScores[0].Score);
Assert.Equal(player2, game.BestScores[0].ThePlayer);
Assert.Equal(100, game.BestScores[1].Score);
Assert.Equal(player1, game.BestScores[1].ThePlayer);
}
[Fact]
public void CheckAndRemoveBestScoresDependencies_RemovesDependenciesCorrectly()
{
var game = new Game(_mockPersistence.Object);
var player = new Player("John Doe", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player, false);
game.AddBestScore(100);
game.CheckAndRemoveBestScoresDependencies(player.Pseudo);
Assert.DoesNotContain(game.BestScores, bs => bs.ThePlayer.Pseudo == player.Pseudo);
}
[Fact]
public void CheckAndChangeBestScoresDependencies_ChangesDependenciesCorrectly()
{
var game = new Game(_mockPersistence.Object);
var player = new Player("John Doe", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player, false);
game.AddBestScore(100);
game.CheckAndChangeBestScoresDependencies(player.Pseudo, "John Does");
Assert.All(game.BestScores, bs => Assert.NotEqual("John Doe", bs.ThePlayer.Pseudo));
Assert.Contains(game.BestScores, bs => bs.ThePlayer.Pseudo == "John Does");
}
}

@ -8,37 +8,101 @@ public class MapTests
[Fact]
public void Map_Initialization_SetsBackground()
{
string name = "test_name";
string background = "test_background";
var map = new Map(background);
var map = new Map(name, background);
Assert.Equal(background, map.Background);
}
[Fact]
public void Map_Initialization_InitializesBoards()
{
string name = "test_name";
string background = "test_background";
var map = new Map(background);
Assert.Equal(36, map.Boards.Count);
var map = new Map(name, background);
Assert.Equal(49, map.Boards.Count);
for (int i = 0; i < 36; i++)
{
Assert.Equal(new Cell(i / 6, i % 6), map.Boards[i]);
Assert.Equal(new Cell(i % 7, i / 7), map.Boards[i]);
}
}
[Fact]
public void Map_Initialization_InitializesRopePathsAndZones()
{
string name = "test_name";
string background = "test_background";
var map = new Map(background);
var map = new Map(name, background);
Assert.NotNull(map.RopePaths);
Assert.NotNull(map.Zones);
Assert.Empty(map.RopePaths);
Assert.Empty(map.Zones);
}
[Fact]
public void IsCellInZones_FoundTheCell()
{
var map = new Map("test_name", "test_background.png");
var cell = new Cell(0, 0);
var zone = new List<Cell> { cell };
map.Zones.Add(zone);
Assert.True(map.IsCellInZones(cell));
}
[Fact]
public void IsCellInZones_DoNotFoundTheCell()
{
var map = new Map("test_name", "test_background.png");
var cell = new Cell(0, 0);
var othercell = new Cell(1, 1);
var zone = new List<Cell> { cell };
map.Zones.Add(zone);
Assert.False(map.IsCellInZones(othercell));
}
[Fact]
public void IsCellInRopePath_FoundTheCell()
{
var map = new Map("test_name", "test_background.png");
var cell = new Cell(0, 0);
var paths = new List<Cell> { cell };
map.RopePaths.Add(paths);
Assert.True(map.IsCellInRopePath(cell));
}
[Fact]
public void IsCellInRopePath_DoNotFoundTheCell()
{
var map = new Map("test_name", "test_background.png");
var cell = new Cell(0, 0);
var othercell = new Cell(1, 1);
var paths = new List<Cell> { cell };
map.RopePaths.Add(paths);
Assert.False(map.IsCellInRopePath(othercell));
}
[Fact]
public void Clone_ReturnsNewMap()
{
var map = new Map("test_name", "test_background.png");
var clone = map.Clone();
Assert.NotEqual(map, clone);
Assert.Equal(map.Name, clone.Name);
Assert.Equal(map.Background, clone.Background);
}
}

@ -11,14 +11,7 @@ public class PlayerTests
var player = new Player(pseudo, profilePicture);
Assert.Equal(pseudo, player.Pseudo);
Assert.Equal(profilePicture, player.ProfilePicture);
}
[Fact]
public void Constructor_WithPseudoAndWithoutProfilePicture_SetsPseudoAndDefaultProfilePicture()
{
var player = new Player("MyPlayer");
Assert.Equal("MyPlayer", player.Pseudo);
Assert.Equal("DefaultProfilePicture", player.ProfilePicture);
Assert.Null(player.LastPlayed);
}
[Theory]
@ -26,8 +19,8 @@ public class PlayerTests
[InlineData("John Doe", "Jane Doe", false)]
public void Equals_WithSameOrDifferentPseudo_ReturnsExpectedResult(string pseudo1, string pseudo2, bool expectedResult)
{
var player1 = new Player(pseudo1);
var player2 = new Player(pseudo2);
var player1 = new Player(pseudo1, "DefaultProfilePicture");
var player2 = new Player(pseudo2, "DefaultProfilePicture");
Assert.Equal(expectedResult, player1.Equals(player2));
}
@ -36,8 +29,33 @@ public class PlayerTests
[InlineData("John Doe", "Jane Doe", false)]
public void GetHashCode_ReturnsSameOrDifferentHashCodeForPseudo(string pseudo1, string pseudo2, bool expectedResult)
{
var player1 = new Player(pseudo1);
var player2 = new Player(pseudo2);
var player1 = new Player(pseudo1, "DefaultProfilePicture");
var player2 = new Player(pseudo2, "DefaultProfilePicture");
Assert.Equal(expectedResult, player1.GetHashCode() == player2.GetHashCode());
}
[Fact]
public void Equals_WithNull_ReturnsFalse()
{
Player player = new Player(null, null);
Assert.False(player.Equals(null));
}
[Fact]
public void Equals_WithDifferentType_ReturnsFalse()
{
Player player = new Player("test_pseudo", "DefaultProfilePicture");
Assert.False(player.Equals(new Cell(0, 0)));
}
[Fact]
public void UpdateLastPlayed_UpdatesLastPlayedDate()
{
var player = new Player("John Doe", "DefaultProfilePicture");
player.UpdateLastPlayed();
Assert.NotNull(player.LastPlayed);
Assert.Equal(DateTime.Now.ToString("dd/MM/yyyy"), player.LastPlayed);
}
}

@ -0,0 +1,571 @@
namespace Tests;
using Models.Game;
using Models.Rules;
using System.Diagnostics;
public class RulesTests
{
[Fact]
public void IsCellEmpty_ReturnsTrue_WhenCellIsNull()
{
Rules rules = new Rules();
Assert.True(rules.IsCellEmpty(null));
}
[Fact]
public void IsCellEmpty_ReturnsTrue_WhenCellValueIsNull()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
Assert.True(rules.IsCellEmpty(cell));
}
[Fact]
public void IsCellEmpty_ReturnsFalse_WhenCellValueIsNotNull()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 1;
Assert.False(rules.IsCellEmpty(cell));
}
[Fact]
public void IsCellValid_ReturnsFalse_WhenCellIsNotEmptyAndHasAdjacentCells()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
Cell selectedCell = map.Boards[0];
selectedCell.Value = 5;
Assert.False(rules.IsCellValid(selectedCell, map.Boards.ToList()));
}
[Fact]
public void IsCellValid_ReturnsTrue_WhenCellIsEmptyAndHasAdjacentCells()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
map.Boards[0].Valid = true;
Cell selectedCell = map.Boards[0];
Assert.True(rules.IsCellValid(selectedCell, map.Boards.ToList()));
}
[Fact]
public void IsCellAdjacent_ReturnsFalse_WhenCellsAreNotAdjacent()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(2, 2);
Assert.False(rules.IsCellAdjacent(cell1, cell2));
}
[Fact]
public void IsCellAdjacent_ReturnsTrue_WhenCellsAreSideBySide()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
Assert.True(rules.IsCellAdjacent(cell1, cell2));
}
[Fact]
public void IsCellAdjacent_ReturnsTrue_WhenCellsAreDiagonal()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(1, 1);
Assert.True(rules.IsCellAdjacent(cell1, cell2));
}
[Fact]
public void IsInRopePaths_ReturnsTrue_WhenCellsAreInRopePaths()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1, cell2 });
Assert.True(rules.IsInRopePaths(cell2, map.RopePaths, 0));
}
[Fact]
public void IsInRopePaths_ReturnsFalse_WhenCellsAreNotInRopePaths()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1 });
Assert.False(rules.IsInRopePaths(cell2, map.RopePaths, 0));
}
[Fact]
public void IsInRopePaths_ReturnsTrue_WhenCellsAreInRopePathsButNotInTheSamePath()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1 });
map.RopePaths.Add(new List<Cell> { cell2 });
Assert.True(rules.IsInRopePaths(cell2, map.RopePaths, 0));
}
[Fact]
public void AsValue_ReturnsTrue_WhenCellHasTheSameValue()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
cell1.Value = 5;
cell2.Value = 5;
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1, cell2 });
Assert.True(rules.AsValue(cell2, map.RopePaths, 0));
}
[Fact]
public void AsValue_ReturnsTrue_WhenCellValueIsAlreadyInRopePaths()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
cell1.Value = 5;
cell2.Value = 5;
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1 });
Assert.True(rules.AsValue(cell2, map.RopePaths, 0));
}
[Fact]
public void AsValue_ReturnsFalse_WhenCellHasDifferentValue()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
cell1.Value = 5;
cell2.Value = 6;
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1 });
Assert.False(rules.AsValue(cell2, map.RopePaths, 0));
}
[Fact]
public void NearCellIsValid_ReturnsFalse_WhenChosenCellIsNull()
{
Rules rules = new Rules();
Assert.False(rules.NearCellIsValid(null, new List<Cell>()));
}
[Fact]
public void NearCellIsValid_ReturnsFalse_WhenCellsIsNull()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
Assert.False(rules.NearCellIsValid(cell, null));
}
[Fact]
public void NearCellIsValid_ReturnsFalse_WhenNoAdjacentCells()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<Cell> cells = new List<Cell> { new Cell(2, 0) };
Assert.False(rules.NearCellIsValid(cell, cells));
}
[Fact]
public void NearCellIsValid_ReturnsTrue_WhenAdjacentCellExists()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
cell2.Value = 12;
cell2.Valid = true;
List<Cell> cells = new List<Cell> { cell2 };
Assert.True(rules.NearCellIsValid(cell1, cells));
}
[Fact]
public void IsZoneValidAndAddToZones_DoesNothing_WhenCellIsNull()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
rules.IsZoneValidAndAddToZones(null, map);
Assert.Empty(map.Zones);
}
[Fact]
public void IsZoneValidAndAddToZones_DoesNothing_WhenCellValueIsNull()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
Cell cell = new Cell(0, 0);
rules.IsZoneValidAndAddToZones(cell, map);
Assert.Empty(map.Zones);
}
[Fact]
public void IsZoneValidAndAddToZones_AddsToExistingZone_WhenAdjacentCellWithSameValueExists()
{
Rules rules = new Rules();
Map map = new Map("Dunai", "background");
Cell cell = new Cell(0, 0);
cell.Value = 1;
Cell adjacentCell = new Cell(0, 1);
adjacentCell.Value = 1;
Cell finalCell = new Cell(0, 2);
map.Boards.ToList().Add(cell);
map.Boards.ToList().Add(adjacentCell);
rules.NewZoneIsCreated(cell, adjacentCell, map);
rules.IsZoneValidAndAddToZones(finalCell, map);
Assert.Contains(cell, map.Zones[0]);
}
[Fact]
public void IsZoneValidAndAddToZones_CreatesNewZone_WhenAdjacentCellWithSameValueExistsButNoExistingZone()
{
Rules rules = new Rules();
Map map = new Map("Dunai", "background");
Cell cell = new Cell(0, 0);
cell.Value = 1;
Cell adjacentCell = new Cell(0, 1);
adjacentCell.Value = 1;
map.Boards[0].Value = 1;
map.Boards[1].Value = 1;
rules.IsZoneValidAndAddToZones(cell, map);
Assert.Contains(cell, map.Zones[0]);
}
[Fact]
public void IsValueInZones_ReturnsFalse_WhenCellIsNull()
{
Rules rules = new Rules();
Assert.False(rules.IsValueInZones(null, new List<List<Cell>>()));
}
[Fact]
public void IsValueInZones_ReturnsFalse_WhenZonesAreEmpty()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 1;
Assert.False(rules.IsValueInZones(cell, new List<List<Cell>>()));
}
[Fact]
public void IsValueInZones_ReturnsFalse_WhenNoZoneContainsCellWithSameValue()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 1;
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { new Cell(0, 1) { Value = 2 } },
new List<Cell> { new Cell(1, 0) { Value = 3 } }
};
Assert.False(rules.IsValueInZones(cell, zones));
}
[Fact]
public void IsValueInZones_ReturnsTrue_WhenZoneContainsCellWithSameValue()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 1;
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { new Cell(0, 1) { Value = 1 } },
new List<Cell> { new Cell(1, 0) { Value = 3 } }
};
Assert.True(rules.IsValueInZones(cell, zones));
}
[Fact]
public void IsCellInZone_ReturnsFalse_WhenCellIsNull()
{
Rules rules = new Rules();
Assert.False(rules.IsCellInZone(null, new List<List<Cell>>()));
}
[Fact]
public void IsCellInZone_ReturnsFalse_WhenZonesAreEmpty()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
Assert.False(rules.IsCellInZone(cell, new List<List<Cell>>()));
}
[Fact]
public void IsCellInZone_ReturnsFalse_WhenNoZoneContainsCell()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { new Cell(0, 1) },
new List<Cell> { new Cell(1, 0) }
};
Assert.False(rules.IsCellInZone(cell, zones));
}
[Fact]
public void IsCellInZone_ReturnsTrue_WhenZoneContainsCell()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { new Cell(0, 1) },
new List<Cell> { cell }
};
Assert.True(rules.IsCellInZone(cell, zones));
}
[Fact]
public void AddToZone_DoesNothing_WhenCellIsNull()
{
Rules rules = new Rules();
List<List<Cell>> zones = new List<List<Cell>>();
rules.AddToZone(null, zones);
Assert.Empty(zones);
}
[Fact]
public void AddToZone_DoesNothing_WhenCellValueIsNull()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<List<Cell>> zones = new List<List<Cell>>();
rules.AddToZone(cell, zones);
Assert.Empty(zones);
}
[Fact]
public void AddToZone_DoesNothing_WhenCellIsAlreadyInZone()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 1;
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { cell }
};
rules.AddToZone(cell, zones);
Assert.Single(zones[0]); // Verify if the List has still only one element
}
[Fact]
public void AddToZone_AddsCellToExistingZone_WhenCellHasSameValueAsZone()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 1;
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { new Cell(0, 1) { Value = 1 } }
};
rules.AddToZone(cell, zones);
Assert.Contains(cell, zones[0]);
}
[Fact]
public void NewZoneIsCreated_DoesNothing_WhenFirstCellIsNull()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
rules.NewZoneIsCreated(null, new Cell(0, 0), map);
Assert.Empty(map.Zones);
}
[Fact]
public void NewZoneIsCreated_DoesNothing_WhenSecondCellIsNull()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
rules.NewZoneIsCreated(new Cell(0, 0), null, map);
Assert.Empty(map.Zones);
}
[Fact]
public void NewZoneIsCreated_DoesNothing_WhenFirstCellValueIsNull()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
Cell firstCell = new Cell(0, 0);
rules.NewZoneIsCreated(firstCell, new Cell(0, 1), map);
Assert.Empty(map.Zones);
}
[Fact]
public void NewZoneIsCreated_DoesNothing_WhenSecondCellValueIsNull()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
Cell secondCell = new Cell(0, 0);
rules.NewZoneIsCreated(new Cell(0, 1), secondCell, map);
Assert.Empty(map.Zones);
}
[Fact]
public void NewZoneIsCreated_CreatesNewZone_WhenBothCellsAreNotNullAndHaveValues()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
Cell firstCell = new Cell(0, 0);
firstCell.Value = 1;
Cell secondCell = new Cell(0, 1);
secondCell.Value = 1;
rules.NewZoneIsCreated(firstCell, secondCell, map);
Assert.Single(map.Zones);
Assert.Contains(firstCell, map.Zones[0]);
Assert.Contains(secondCell, map.Zones[0]);
}
[Fact]
public void EveryAdjacentCells_ReturnsEmptyList_WhenCellIsNull()
{
Rules rules = new Rules();
List<Cell> cells = new List<Cell> { new Cell(0, 0), new Cell(0, 1) };
Assert.Empty(rules.EveryAdjacentCells(null, cells));
}
[Fact]
public void EveryAdjacentCells_ReturnsEmptyList_WhenCellsAreEmpty()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
Assert.Empty(rules.EveryAdjacentCells(cell, new List<Cell>()));
}
[Fact]
public void EveryAdjacentCells_ReturnsEmptyList_WhenNoAdjacentCells()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<Cell> cells = new List<Cell> { new Cell(2, 2), new Cell(3, 3) };
Assert.Empty(rules.EveryAdjacentCells(cell, cells));
}
[Fact]
public void EveryAdjacentCells_ReturnsListWithAdjacentCells_WhenAdjacentCellsExist()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<Cell> cells = new List<Cell> { new Cell(0, 1), new Cell(1, 0), new Cell(2, 2) };
List<Cell> result = rules.EveryAdjacentCells(cell, cells);
Assert.Equal(2, result.Count);
Assert.Contains(new Cell(0, 1), result);
Assert.Contains(new Cell(1, 0), result);
}
[Fact]
public void FinalCalculusOfZones_ReturnsZero_WhenZonesAreEmpty()
{
Rules rules = new Rules();
Assert.Equal(0, rules.FinalCalculusOfZones(new List<List<Cell>>()));
}
[Fact]
public void FinalCalculusOfZones_ReturnsCorrectValue_WhenZonesAreNotEmpty()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 1;
Cell cell1 = new Cell(0, 1);
cell1.Value = 1;
Cell cell2 = new Cell(0, 2);
cell2.Value = 1;
Cell uncell = new Cell(0, 0);
uncell.Value = 4;
Cell deuxcell = new Cell(0, 1);
deuxcell.Value = 4;
Cell troiscell = new Cell(0, 2);
troiscell.Value = 4;
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { cell, cell1, cell2 },
new List<Cell> { uncell, deuxcell, troiscell }
};
Assert.Equal(9, rules.FinalCalculusOfZones(zones));
}
[Fact]
public void FinalCalculusOfZones_ReturnsCorrectValue_WhenZoneCountIsGreaterThanNine()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 5;
Cell cell1 = new Cell(0, 1);
cell1.Value = 5;
Cell cell2 = new Cell(0, 2);
cell2.Value = 5;
Cell cell3 = new Cell(0, 3);
cell3.Value = 5;
Cell cell4 = new Cell(0, 4);
cell4.Value = 5;
Cell cell5 = new Cell(0, 5);
cell5.Value = 5;
Cell cell6 = new Cell(1, 0);
cell6.Value = 5;
Cell cell7 = new Cell(1, 1);
cell7.Value = 5;
Cell cell8 = new Cell(1, 2);
cell8.Value = 5;
Cell cell9 = new Cell(1, 3);
cell9.Value = 5;
Cell cell10 = new Cell(1, 4);
cell10.Value = 5;
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { cell, cell1, cell2, cell3, cell4, cell5, cell6, cell7, cell8, cell9, cell10 }
};
Assert.Equal(23, rules.FinalCalculusOfZones(zones));
}
[Fact]
public void ScoreRopePaths_ReturnsZero_WhenPathsAreEmpty()
{
Rules rules = new Rules();
Assert.Equal(0, rules.ScoreRopePaths(new List<Cell>()));
}
[Fact]
public void ScoreRopePaths_ReturnsCorrectValue_WhenPathsAreNotEmpty()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 5;
Cell cell1 = new Cell(0, 1);
cell1.Value = 6;
Cell cell2 = new Cell(0, 2);
cell2.Value = 4;
List<Cell> ropePath = new List<Cell> { cell, cell1, cell2 };
Assert.Equal(8, rules.ScoreRopePaths(ropePath));
}
[Fact]
public void ScoreRopePaths_ReturnsCorrectValue_WhenPathsAreNotEmptyAndSorted()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 5;
Cell cell1 = new Cell(0, 1);
cell1.Value = 6;
Cell cell2 = new Cell(0, 2);
cell2.Value = 4;
List<Cell> ropePath = new List<Cell> { cell1, cell, cell2 };
Assert.Equal(8, rules.ScoreRopePaths(ropePath));
}
}

@ -10,8 +10,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

@ -5,11 +5,15 @@ VisualStudioVersion = 17.8.34408.163
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trek-12", "Trek-12\Trek-12.csproj", "{41EE7BF8-DDE6-4B00-9434-076589C0B419}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Models", "Models\Models.csproj", "{807AB723-7AD3-42DD-9DA6-7AA5B0A9AAB4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Models", "Models\Models.csproj", "{807AB723-7AD3-42DD-9DA6-7AA5B0A9AAB4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{795F2C88-3C43-4795-9764-E52F7330888D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{795F2C88-3C43-4795-9764-E52F7330888D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{383C4215-C680-4C2E-BC7E-B62F0B164370}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{383C4215-C680-4C2E-BC7E-B62F0B164370}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataContractPersistence", "DataContractPersistence\DataContractPersistence.csproj", "{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stub", "Stub\Stub.csproj", "{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -35,6 +39,14 @@ Global
{383C4215-C680-4C2E-BC7E-B62F0B164370}.Debug|Any CPU.Build.0 = Debug|Any CPU
{383C4215-C680-4C2E-BC7E-B62F0B164370}.Release|Any CPU.ActiveCfg = Release|Any CPU
{383C4215-C680-4C2E-BC7E-B62F0B164370}.Release|Any CPU.Build.0 = Release|Any CPU
{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}.Release|Any CPU.Build.0 = Release|Any CPU
{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -1,12 +1,69 @@
namespace Trek_12
using System.Diagnostics;
using System.Runtime.Serialization.DataContracts;
namespace Trek_12
{
using Stub;
using Models.Game;
using Models.Interfaces;
using DataContractPersistence;
public partial class App : Application
{
public string FileName { get; set; } = "data.json";
public string FilePath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Trek_12");
public Game Manager { get; private set; }
public App()
{
InitializeComponent();
Manager = new Game(new DataContractJson());
//Manager = new Game(new Stub());
if (!Directory.Exists(FilePath))
{
Directory.CreateDirectory(FilePath);
}
//File.Delete(Path.Combine(FilePath, FileName));
string fullPath = Path.Combine(FilePath, FileName);
if (File.Exists(fullPath))
{
Debug.WriteLine("Data loaded from " + fullPath);
Manager.LoadData();
}
/* Add the permanent maps if they are not already in the game */
if (Manager.Maps.Count == 0)
{
Manager.AddMap(new Map("Dunai","montagne2.png"));
Manager.AddMap(new Map("Kagkot", "montagne3.png"));
Manager.AddMap(new Map("Dhaulagiri", "montagne4.png"));
}
MainPage = new AppShell();
// If the MainPage is closed, we save the data
MainPage.Disappearing += (sender, e) =>
{
Debug.WriteLine("Saving data...");
Manager.SaveData();
};
}
/// <summary>
/// Save the data when the app is in background
/// </summary>
protected override void OnSleep()
{
Debug.WriteLine("Zzz Secure save...");
Manager.SaveData();
}
}
}

@ -5,38 +5,11 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Trek_12"
xmlns:views="clr-namespace:Trek_12.Views"
Shell.FlyoutBehavior="Flyout"
Shell.FlyoutBehavior="Disabled"
Title="Trek_12"
Shell.NavBarIsVisible="False">
<ShellContent
Title="Home"
ContentTemplate="{DataTemplate views:PageMenuPrincipal}"
Route="MenuPrincipal" />
<ShellContent
Title="LeaderBoard"
ContentTemplate="{DataTemplate views:PageLeaderBoard}"
Route="LeaderBoard"/>
<ShellContent
Title="Profils"
ContentTemplate="{DataTemplate views:PageProfils}"
Route="Profils"/>
<ShellContent
Title="Règles"
ContentTemplate="{DataTemplate views:PageRegles}"
Route="Regles"/>
<!--<ShellContent
Title="Map Select"
ContentTemplate="{DataTemplate views:PageMapSelect}"
Route="MapSelect"/>-->
<!--<ShellContent
Title="Plateau"
ContentTemplate="{DataTemplate views:PagePlateau}"
Route="Plateau"/>-->
Shell.NavBarIsVisible="False"
Shell.FlyoutItemIsVisible="False"
Shell.TabBarIsVisible="False">
<ShellContent ContentTemplate="{DataTemplate views:PageMenuPrincipal}" />
</Shell>

@ -1,10 +1,22 @@
namespace Trek_12
using Trek_12.Views;
namespace Trek_12
{
/// <summary>
/// Class for the route of the views and the navigation of the app.
/// </summary>
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(PageMenuPrincipal), typeof(PageMenuPrincipal));
Routing.RegisterRoute(nameof(PageProfiles), typeof(PageProfiles));
Routing.RegisterRoute(nameof(PageSelectMap), typeof(PageSelectMap));
Routing.RegisterRoute(nameof(PageRegles), typeof(PageRegles));
Routing.RegisterRoute(nameof(PageLeaderBoard), typeof(PageLeaderBoard));
Routing.RegisterRoute(nameof(PageBoard), typeof(PageBoard));
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="456" height="456" fill="#512BD4" />
</svg>

Before

Width:  |  Height:  |  Size: 228 B

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<!--<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>-->
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
@ -38,16 +38,23 @@
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('windows'))">
<TargetFrameworks>$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<WindowsPackageType>MSIX</WindowsPackageType>
<PackageCertificateThumbprint>404032fa5d4dc4c8bbf036505d2409963f355ebd</PackageCertificateThumbprint>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
<MauiIcon Include="Resources\AppIcon\app_icon.png" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185" />
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />
@ -56,6 +63,17 @@
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<None Remove="Resources\Images\app_icon.png" />
<None Remove="Resources\Images\back_arrow.png" />
<None Remove="Resources\Images\checked.png" />
<None Remove="Resources\Images\maptest.png" />
<None Remove="Resources\Images\montagne2.png" />
<None Remove="Resources\Images\montagne3.png" />
<None Remove="Resources\Images\montagne4.png" />
<None Remove="Resources\Images\user.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Maui" Version="7.0.1" />
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
@ -63,26 +81,32 @@
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Views\PageLeaderBoard.xaml.cs">
<DependentUpon>PageLeaderBoard.xaml</DependentUpon>
</Compile>
<Compile Update="Views\pageProfils.xaml.cs">
<DependentUpon>PageProfils.xaml</DependentUpon>
</Compile>
<Compile Update="Views\pageRegles.xaml.cs">
<DependentUpon>PageRegles.xaml</DependentUpon>
</Compile>
<ItemGroup>
<ProjectReference Include="..\DataContractPersistence\DataContractPersistence.csproj" />
<ProjectReference Include="..\Models\Models.csproj" />
<ProjectReference Include="..\Stub\Stub.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Views\PageLeaderBoard.xaml.cs">
<DependentUpon>PageLeaderBoard.xaml</DependentUpon>
</Compile>
<Compile Update="Views\pageProfiles.xaml.cs">
<DependentUpon>pageProfiles.xaml</DependentUpon>
</Compile>
<Compile Update="Views\pageRegles.xaml.cs">
<DependentUpon>PageRegles.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<MauiXaml Update="Views\Components\ContentLeaderBoard.xaml">
<Generator>MSBuild:Compile</Generator>
<MauiXaml Update="Views\Components\ContentLeaderBoard.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Views\PageLeaderBoard.xaml">
<Generator>MSBuild:Compile</Generator>
<MauiXaml Update="Views\PageBoard.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Views\PageConnexion.xaml">
<MauiXaml Update="Views\PageLeaderBoard.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.Components.ContentLeaderBoard">
x:Class="Trek_12.Views.Components.ContentLeaderBoard"
x:Name="this">
<Grid ColumnDefinitions="auto,*,*,*,*"
RowDefinitions="*,*"
Margin="0,20">
@ -12,31 +14,31 @@
WidthRequest="60"
IsClippedToBounds="True"
HasShadow="True">
<Image Source="profile.jpg"
<Image Source="{Binding ProfilePicture, Source={x:Reference this}}"
Aspect="AspectFill"
Margin="-20"/>
</Frame>
<Label Text="Kiwi6026"
Grid.Column="1"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"
Margin="10,0"/>
<Label Text="nbParties"
Grid.Column="2"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="map"
Grid.Column="3"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="bestScore"
Grid.Column="4"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="{Binding Pseudo, Source={x:Reference this}}"
Grid.Column="1"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"
Margin="10,0"/>
<Label Text="{Binding NbGames, Source={x:Reference this}}"
Grid.Column="2"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="{Binding BestScore, Source={x:Reference this}}"
Grid.Column="3"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="{Binding Map, Source={x:Reference this}}"
Grid.Column="4"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<BoxView Color="DarkSalmon"
Grid.Row="1"
HeightRequest="1"

File diff suppressed because one or more lines are too long

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.Components.viewsProfils">
x:Class="Trek_12.Views.Components.viewsProfils"
x:Name="this">
<VerticalStackLayout>
<Grid ColumnDefinitions="*,*,*,*" >
<Grid ColumnDefinitions="*,*,*">
<Frame Margin="10"
Grid.Column="1"
Grid.Column="0"
BorderColor="Black"
CornerRadius="50"
HeightRequest="60"
@ -13,11 +15,12 @@
IsClippedToBounds="True"
HasShadow="True"
HorizontalOptions="CenterAndExpand">
<Image Source="profile.jpg"
<Image Source="{Binding ProfilePicture, Source={x:Reference this}}"
Aspect="AspectFill"
Margin="-20"/>
</Frame>
<Label Text="Profile n°*" Grid.Column="2" TextColor="Black" FontSize="Large" HorizontalTextAlignment="Center" Margin="100"/>
<Label Text="{Binding Pseudo, Source={x:Reference this}}" Grid.Column="1" TextColor="Black" FontSize="Large" HorizontalTextAlignment="Center"/>
<Label Text="{Binding CreationDate, Source={x:Reference this}}" Grid.Column="2" TextColor="Black" FontSize="Large" HorizontalTextAlignment="Center"/>
</Grid>
</VerticalStackLayout>
</ContentView>

File diff suppressed because one or more lines are too long

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.PageBoard"
Title="Board"
BackgroundColor="Bisque">
<Grid RowDefinitions="*,auto" ColumnDefinitions="auto,*,auto">
<Grid RowDefinitions="auto,auto"
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
Margin="50,0,0,0">
<HorizontalStackLayout HorizontalOptions="Center"
Margin="0,20,0,0">
<Frame BorderColor="DarkGray"
HeightRequest="50"
WidthRequest="50"
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
Padding="0"
BackgroundColor="Yellow"
IsVisible="Hidden"
x:Name="YellowDice">
<Label Text="{Binding Dice1.Value}"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="Black"
x:Name="Dice1"
FontAttributes="Bold"/>
</Frame>
<Frame BorderColor="DarkGray"
HeightRequest="50"
WidthRequest="50"
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
Padding="0"
BackgroundColor="Red"
IsVisible="Hidden"
x:Name="RedDice">
<Label Text="{Binding Dice2.Value}"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="Black"
x:Name="Dice2"
FontAttributes="Bold"/>
</Frame>
</HorizontalStackLayout>
<Button Text="Roll"
HeightRequest="25"
WidthRequest="100"
Clicked="DiceButton_Clicked"
Grid.Row="1"
Margin="0,50,0,0"
x:Name="RollButton"/>
</Grid>
<CollectionView ItemsSource="{Binding UsedMap.Boards}"
Grid.Column="1"
SelectionMode="Single"
WidthRequest="350"
HeightRequest="350"
ItemsLayout="VerticalGrid,7"
x:Name="Board"
SelectionChanged="OnCellSelected">
<CollectionView.ItemTemplate>
<DataTemplate x:Name="Cellule">
<Grid VerticalOptions="Center" HorizontalOptions="Center" HeightRequest="50" WidthRequest="50">
<Frame
IsVisible="{Binding Valid}"
BorderColor="DarkGray"
CornerRadius="25"
HeightRequest="50"
WidthRequest="50"
BackgroundColor="White"
Opacity="0.7"
VerticalOptions="Center"
HorizontalOptions="Center"
Padding="0"
x:Name="CellValid">
<Label Text="{Binding Value}"
x:Name="CellValue"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="Black"
FontAttributes="Bold"/>
<Frame.Triggers>
<DataTrigger TargetType="Frame" Binding="{Binding IsDangerous}" Value="True">
<Setter Property="BorderColor" Value="Black"/>
</DataTrigger>
<DataTrigger TargetType="Frame" Binding="{Binding IsDangerous}" Value="False">
<Setter Property="BorderColor" Value="Transparent"/>
</DataTrigger>
</Frame.Triggers>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- Operation Grid -->
<Grid Grid.Row="0" Grid.Column="2"
ColumnDefinitions="auto,*"
RowDefinitions="auto,auto">
<!--Images des operations -->
<!--Grille de la partie-->
<CollectionView Grid.Column="1"
ItemsSource="{Binding UsedMap.OperationGrid}"
WidthRequest="200"
HeightRequest="250"
BackgroundColor="Transparent"
SelectionChanged="OnOperationCellSelected"
Margin="50"
ItemsLayout="VerticalGrid,4">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="True"
BorderColor="Black"
BackgroundColor="Transparent"
CornerRadius="0"
HorizontalOptions="Center"
VerticalOptions="Center"
HeightRequest="50"
WidthRequest="50"
Padding="0">
<Image Source="checked.png"
IsVisible="{Binding IsChecked}"
HorizontalOptions="Center"
VerticalOptions="Center"/>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!--Les bouttons de selection d'operation-->
<HorizontalStackLayout Grid.Row="1"
Grid.ColumnSpan="2"
VerticalOptions="Center">
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Lower"
Clicked="LowerClicked"
IsVisible="False"/>
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Higher"
Clicked="HigherClicked"
IsVisible="False"/>
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Substraction"
Clicked="SubstractionClicked"
IsVisible="False"/>
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Addition"
Clicked="AdditionClicked"
IsVisible="False"/>
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Multiplication"
Clicked="MultiplicationClicked"
IsVisible="False"/>
</HorizontalStackLayout>
</Grid>
</Grid>
</ContentPage>

@ -0,0 +1,216 @@
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.VisualBasic;
namespace Trek_12.Views;
using Models.Events;
using Models.Game;
public partial class PageBoard : ContentPage
{
public Game GameManager => (App.Current as App).Manager;
public int Result { get; set; }
public Cell ChoosenCell { get; set; }
public PageBoard()
{
InitializeComponent();
BindingContext = GameManager;
GameManager.CurrentPlayer.UpdateLastPlayed();
GameManager.DiceRolled += TheGame_DiceRolled;
GameManager.DiceRolled += ResultAddition;
GameManager.DiceRolled += ResultLower;
GameManager.DiceRolled += ResultHigher;
GameManager.DiceRolled += ResultSubstraction;
GameManager.DiceRolled += ResultMultiplication;
GameManager.PlayerOption += GameManager_PlayerOption;
GameManager.CellChosen += HandleCellChosen;
GameManager.AddGame(GameManager);
GameManager.OnPropertyChanged(nameof(GameManager.Games));
GameManager.SaveData();
}
private void HandleCellChosen(object sender, CellChosenEventArgs e)
{
YellowDice.IsVisible = false;
RedDice.IsVisible = false;
RollButton.IsEnabled = true;
}
private void ResetOperationButtonsAndDice()
{
Lower.IsVisible = false;
Higher.IsVisible = false;
Substraction.IsVisible = false;
Addition.IsVisible = false;
Multiplication.IsVisible = false;
RollButton.IsEnabled = true;
YellowDice.IsVisible = false;
RedDice.IsVisible = false;
}
private void SetOperationButtonState(Button selectedButton)
{
// Deselect all buttons
Lower.BackgroundColor = Colors.DarkSalmon;
Higher.BackgroundColor = Colors.DarkSalmon;
Substraction.BackgroundColor = Colors.DarkSalmon;
Addition.BackgroundColor = Colors.DarkSalmon;
Multiplication.BackgroundColor = Colors.DarkSalmon;
// Select the clicked button
selectedButton.BackgroundColor = Colors.LightCoral;
}
private void GameManager_PlayerOption(object? sender, PlayerOptionEventArgs e)
{
/* IEnumerable<Cell> PlayedCellsQuery =
from cell in e.Board
where cell.Valid == true
where cell.Value != null
select cell;*/
// prévisualisation des zone disponible, Je ne sais pas comment ca marche... 😵
}
private void ResultMultiplication(object? sender, DiceRolledEventArgs e)
{
Multiplication.IsVisible = true;
Multiplication.Text = $"Mult {e.Dice1Value*e.Dice2Value}";
}
private void ResultSubstraction(object? sender, DiceRolledEventArgs e)
{
Substraction.IsVisible = true;
if (GameManager.Dice1.IsLower(GameManager.Dice2))
Substraction.Text = $"Sub {e.Dice2Value - e.Dice1Value}";
else Substraction.Text = $"Sub {e.Dice1Value - e.Dice2Value}";
}
private void ResultHigher(object? sender, DiceRolledEventArgs e)
{
Higher.IsVisible = true;
if (GameManager.Dice1.IsLower(GameManager.Dice2))
Higher.Text = $"Higher {e.Dice2Value}";
else Higher.Text = $"Higher {e.Dice1Value}";
}
private void ResultLower(object? sender, DiceRolledEventArgs e)
{
Lower.IsVisible = true;
if(GameManager.Dice1.IsLower(GameManager.Dice2))
Lower.Text = $"Lower {e.Dice1Value}";
else Lower.Text = $"Lower {e.Dice2Value}";
}
private void ResultAddition(object? sender, DiceRolledEventArgs e)
{
Addition.IsVisible = true;
Addition.Text = $"Add {e.Dice1Value+e.Dice2Value}";
}
private void TheGame_DiceRolled(object? sender, Models.Events.DiceRolledEventArgs e)
{
YellowDice.IsVisible = true;
RedDice.IsVisible = true;
Dice1.Text = $"{e.Dice1Value}";
Dice2.Text = $"{e.Dice2Value}";
RollButton.IsEnabled = false;
}
private void OnOperationCellSelected(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.Count > 0) // Si un élément est sélectionné
{
var selectedCell = (OperationCell)e.CurrentSelection[0];
if (selectedCell != null && !selectedCell.IsChecked)
{
selectedCell.Check();
Debug.WriteLine("OperationCell at ({0}, {1}) is checked", selectedCell.X, selectedCell.Y); // Debug
}
((CollectionView)sender).SelectedItem = null; // Déselectionne l'élément pour la CollectionView
}
}
private void HigherClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.HIGHER;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.HIGHER);
GameManager.HandlePlayerOperation(Operation.HIGHER);
}
private void LowerClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.LOWER;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.LOWER);
GameManager.HandlePlayerOperation(Operation.LOWER);
}
private void AdditionClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.ADDITION;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.ADDITION);
GameManager.HandlePlayerOperation(Operation.ADDITION);
}
private void SubstractionClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.SUBTRACTION;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.SUBTRACTION);
GameManager.HandlePlayerOperation(Operation.SUBTRACTION);
}
private void MultiplicationClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.MULTIPLICATION;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.MULTIPLICATION);
GameManager.HandlePlayerOperation(Operation.MULTIPLICATION);
}
private void DiceButton_Clicked(object sender, EventArgs e)
{
GameManager.RollAllDice();
}
private async void OnCellSelected(object sender, SelectionChangedEventArgs e)
{
if (!GameManager.DiceRolledFlag)
{
await DisplayAlert("Action Required", "You must roll the dice first.", "OK");
return;
}
if (!GameManager.OperationChosenFlag)
{
await DisplayAlert("Action Required", "You must choose an operation first.", "OK");
return;
}
if (e.CurrentSelection.Count > 0)
{
ChoosenCell = (Cell)e.CurrentSelection[0];
GameManager.PlayerCell = ChoosenCell;
GameManager.Resultat = Result;
OnPropertyChanged(nameof(GameManager.PlayerCell));
OnPropertyChanged(nameof(GameManager.Resultat));
GameManager.PlayerSelectionCell();
((CollectionView)sender).SelectedItem = null;
ResetOperationButtonsAndDice();
}
}
}

@ -1,43 +1,60 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Trek_12.Views.Components"
x:Class="Trek_12.Views.PageLeaderBoard"
Title="PageLeaderBoard">
<Grid BackgroundColor="BlanchedAlmond"
RowDefinitions="auto,6*,*">
<VerticalStackLayout>
<Label
Text="Leader board"
VerticalOptions="Center"
HorizontalOptions="Center"
FontSize="Title"/>
<BoxView
Color="DarkSalmon"
HeightRequest="1"
WidthRequest="125"/>
</VerticalStackLayout>
<ScrollView Grid.Row="1"
VerticalOptions="FillAndExpand"
VerticalScrollBarVisibility="Never"
Margin="0,10">
<VerticalStackLayout>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
</VerticalStackLayout>
</ScrollView>
<Button Text="Back"
BackgroundColor="OliveDrab"
FontSize="Title"
Grid.Row="2"
HorizontalOptions="Start"
CornerRadius="20"
WidthRequest="150"
HeightRequest="75"
Margin="10"/>
</Grid>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Trek_12.Views.Components"
x:Class="Trek_12.Views.PageLeaderBoard"
Title="PageLeaderBoard">
<ContentPage.Content>
<Grid BackgroundColor="BlanchedAlmond"
RowDefinitions="auto,6*,*">
<Frame Grid.Row="0" BackgroundColor="Transparent" BorderColor="Transparent" Padding="0" Margin="15">
<Image Source="back_arrow.png"
Margin="0"
HeightRequest="50"
WidthRequest="50"
VerticalOptions="Center"
HorizontalOptions="Start"/>
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBackArrow_Tapped"/>
</Frame.GestureRecognizers>
</Frame>
<VerticalStackLayout VerticalOptions="Center" HorizontalOptions="Center">
<Label
Text="Leaderboard"
VerticalOptions="Center"
HorizontalOptions="Center"
FontSize="Title"/>
<BoxView
Color="DarkSalmon"
HeightRequest="1"
WidthRequest="125"/>
</VerticalStackLayout>
<ScrollView Grid.Row="1"
VerticalOptions="FillAndExpand"
VerticalScrollBarVisibility="Never"
Margin="0,10">
<VerticalStackLayout HorizontalOptions="Center">
<CollectionView ItemsSource="{Binding BestScores}"
ItemsLayout="VerticalList"
VerticalOptions="Center">
<CollectionView.ItemTemplate>
<DataTemplate>
<views:ContentLeaderBoard
Pseudo="{Binding ThePlayer.Pseudo}"
ProfilePicture="{Binding ThePlayer.ProfilePicture}"
NbGames="{Binding GamesPlayed}"
BestScore="{Binding Score}"
Map="{Binding MapName}"
/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</Grid>
</ContentPage.Content>
</ContentPage>

@ -1,9 +1,20 @@
namespace Trek_12.Views;
public partial class PageLeaderBoard : ContentPage
{
public PageLeaderBoard()
{
InitializeComponent();
}
using Models.Game;
using System.Diagnostics;
namespace Trek_12.Views;
public partial class PageLeaderBoard : ContentPage
{
public Game LeaderboardManager => (App.Current as App).Manager;
public PageLeaderBoard()
{
InitializeComponent();
BindingContext = LeaderboardManager;
}
private async void OnBackArrow_Tapped(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
}

@ -17,11 +17,19 @@
HorizontalOptions="Center"
MinimumHeightRequest="50"
MaximumHeightRequest="150"/>
<Frame Grid.Row="0" BackgroundColor="Transparent" BorderColor="Transparent" Padding="0" Margin="20" HorizontalOptions="End" VerticalOptions="Start">
<Image Source="user.png" HeightRequest="50" WidthRequest="50" Aspect="AspectFit" VerticalOptions="Center" HorizontalOptions="Center" Margin="0" />
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnProfilesButton_Tapped"/>
</Frame.GestureRecognizers>
</Frame>
<Grid Grid.Row="1" ColumnDefinitions="*,2*,*" RowDefinitions="*,*" HorizontalOptions="Fill" VerticalOptions="Center" ColumnSpacing="50" RowSpacing="25" Margin="50,0">
<Button Grid.Column="0" Grid.Row="1" Text="Leaderboard" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6"/>
<Button Grid.Column="1" Grid.RowSpan="2" Text="JOUER" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6"/>
<Button Grid.Column="2" Grid.Row="1" Text="Règles" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6"/>
<Button Grid.Column="0" Grid.Row="1" Text="Leaderboard" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6" Clicked="OnLeaderBoardButton_Clicked"/>
<Button Grid.Column="1" Grid.RowSpan="2" Text="JOUER" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6" Clicked="OnPlayButton_Clicked"/>
<Button Grid.Column="2" Grid.Row="1" Text="Règles" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6" Clicked="OnRulesButton_Clicked"/>
</Grid>
</Grid>
</ContentPage.Content>

@ -1,8 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Maui.Alerts;
using CommunityToolkit.Maui.Core;
using Font = Microsoft.Maui.Font;
namespace Trek_12.Views;
@ -12,4 +17,24 @@ public partial class PageMenuPrincipal : ContentPage
{
InitializeComponent();
}
private async void OnRulesButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(PageRegles));
}
private async void OnLeaderBoardButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(PageLeaderBoard));
}
private async void OnPlayButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(PageSelectMap));
}
private async void OnProfilesButton_Tapped(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(PageProfiles));
}
}

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.PageSelectMap"
Title="PageSelectMap">
<Grid RowDefinitions="auto,auto,auto">
<Label Grid.Row="0" Text="Sélection de la Carte" HorizontalOptions="Center" FontSize="Header"/>
<CollectionView Grid.Row="1"
ItemsSource="{Binding Maps}"
ItemsLayout="HorizontalList"
HorizontalOptions="Center"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="True"
BorderColor="DarkGray"
CornerRadius="5"
Margin="20"
HeightRequest="300"
WidthRequest="200"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
x:Name="Frame">
<Grid RowDefinitions="auto,*">
<Label Text="{Binding Name}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<Image Source="{Binding Background}"
Aspect="AspectFill"
HeightRequest="150"
WidthRequest="150"
HorizontalOptions="Center"
Grid.Row="1"/>
</Grid>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Reprendre"
TextColor="White"
BackgroundColor="DarkRed"
FontAttributes="Bold"
FontSize="Large"
Grid.Row="2"
Margin="80"
HorizontalOptions="Center"
Clicked="ResumeButton_Clicked"
IsVisible="{Binding IsPreviousGameNotFinished}"/>
<Button Text="Retour"
FontAttributes="Bold"
FontSize="Large"
Grid.Row="2"
HorizontalOptions="Start"
Margin="100"
Clicked="BackButton_Clicked"/>
<Button Text="Jouer"
FontAttributes="Bold"
FontSize="Large"
Grid.Row="2"
HorizontalOptions="End"
Margin="100"
Clicked="PlayButton_Clicked"/>
</Grid>
</ContentPage>

@ -0,0 +1,114 @@
using System.Diagnostics;
namespace Trek_12.Views;
using Stub;
using Models.Game;
public partial class PageSelectMap : ContentPage
{
public Game SelectMapManager => (App.Current as App).Manager;
private Map? _selectedMap;
private bool isVisibleContinueButton = false;
protected override async void OnAppearing()
{
base.OnAppearing();
if (SelectMapManager.Games.Any(g => g.IsRunning))
{
isVisibleContinueButton = true;
await DisplayAlert("Warning", "You've previously quit in the middle of a game.\nIf you start a new game, this one will be permanently lost.", "I understand");
}
}
public PageSelectMap()
{
InitializeComponent();
BindingContext = SelectMapManager;
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_selectedMap = e.CurrentSelection.FirstOrDefault() as Map;
}
private async void BackButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
private async void PlayButton_Clicked(object sender, EventArgs e)
{
if (_selectedMap == null)
{
await DisplayAlert("Selection Required", "Please select a map you want to play to continue.", "OK");
return;
}
if (SelectMapManager.Players.Count == 0)
{
await DisplayAlert("No player found", "Please add a player in the profile page.", "OK");
return;
}
string[] profiles = GetProfiles().ToArray();
string choosenPlayerName = await DisplayActionSheet("Choose a player", "Cancel", null, profiles);
if (choosenPlayerName == null || choosenPlayerName == "Cancel") return;
Player chosenPlayer = GetProfileByName(choosenPlayerName);
var runningGames = SelectMapManager.Games.Where(g => g.IsRunning).ToList();
bool delete = false;
foreach (var game in runningGames)
{
SelectMapManager.Games.Remove(game);
delete = true;
}
if (delete)
{
await DisplayAlert("Game deleted", "The previous game has been deleted because you started a new one.", "OK");
SelectMapManager.OnPropertyChanged(nameof(SelectMapManager.Games));
SelectMapManager.SaveData();
}
SelectMapManager.InitializeGame(_selectedMap.Clone(), chosenPlayer);
if (SelectMapManager.UsedMap != null && Equals(SelectMapManager.CurrentPlayer, chosenPlayer))
{
await Shell.Current.GoToAsync(nameof(PageBoard));
}
else
{
await DisplayAlert("Error", "An error occured while initializing the game. Please try again.", "OK");
}
}
private List<string> GetProfiles()
{
return SelectMapManager.Players.Select(p => p.Pseudo).ToList();
}
private Player GetProfileByName(string pseudo)
{
return SelectMapManager.Players.FirstOrDefault(p => p.Pseudo == pseudo);
}
private async void ResumeButton_Clicked(object sender, EventArgs e)
{
Game game = SelectMapManager.Games.FirstOrDefault(g => g.IsRunning);
if (game == null)
{
await DisplayAlert("No game found", "No game found to resume. Please start a new game.", "OK");
return;
}
SelectMapManager.InitializeGame(game.UsedMap, game.CurrentPlayer, false);
await Shell.Current.GoToAsync(nameof(PageBoard));
}
}

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.PageProfiles"
xmlns:views="clr-namespace:Trek_12.Views.Components"
Title="PageProfiles">
<ContentPage.Content>
<Grid RowDefinitions="auto,3*,*">
<Image Source="bg_profils.jpg" Grid.RowSpan="3" Aspect="AspectFill"/>
<Frame Grid.Row="0" BackgroundColor="Transparent" BorderColor="Transparent" Padding="0" Margin="15">
<Image Source="back_arrow.png"
Margin="0"
HeightRequest="50"
WidthRequest="50"
VerticalOptions="Center"
HorizontalOptions="Start"/>
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBackArrow_Tapped"/>
</Frame.GestureRecognizers>
</Frame>
<Frame BackgroundColor="WhiteSmoke" Opacity="0.5" Grid.Row="1" />
<Label Grid.Row="0" Text="Profils" TextColor="black" HorizontalTextAlignment="Center" FontSize="Header" Margin="30"/>
<Grid Grid.Row="1" ColumnDefinitions="*,*,*">
<Label Text="Pseudo" TextColor="black" FontSize="Large" HorizontalTextAlignment="Center" Grid.Column="1"/>
<Label Text="Date de Création" TextColor="black" FontSize="Large" HorizontalTextAlignment="Center" Grid.Column="2"/>
</Grid>
<CollectionView Grid.Row="1" ItemsSource="{Binding Players}"
ItemsLayout="VerticalList"
VerticalOptions="Center">
<CollectionView.ItemTemplate>
<DataTemplate>
<views:viewsProfils
Pseudo="{Binding Pseudo}"
ProfilePicture="{Binding ProfilePicture}"
CreationDate="{Binding CreationDate}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<HorizontalStackLayout Grid.Row="2" HorizontalOptions="Center" Spacing="50">
<Button Text="Modifier" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="Button_ClickedModify"/>
<Button Text="Créer" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="Button_ClickedAdd"/>
<Button Text="Supprimer" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="Button_ClickedPop"/>
</HorizontalStackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>

@ -0,0 +1,113 @@
using Models.Game;
using Stub;
using System.Diagnostics;
using CommunityToolkit.Maui.Alerts;
using CommunityToolkit.Maui.Core;
using Models.Interfaces;
namespace Trek_12.Views;
public partial class PageProfiles : ContentPage
{
public Game ProfileManager => (App.Current as App).Manager;
public PageProfiles()
{
InitializeComponent();
BindingContext = ProfileManager;
}
private async void OnBackArrow_Tapped(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
async void Button_ClickedAdd(System.Object sender, System.EventArgs e)
{
string pseudo = await DisplayPromptAsync("Info", $"Choose a name : ", "Ok");
char[] trim = { ' ', '\n', '\t' };
pseudo = pseudo.TrimEnd(trim);
pseudo = pseudo.TrimStart(trim);
if (pseudo == null) return;
if (ProfileManager.Players.Any(p => p.Pseudo == pseudo))
{
await DisplayAlert("Info", "This name is already taken", "Ok");
return;
}
var profilePicture = await MediaPicker.PickPhotoAsync();
if (profilePicture == null) return;
IImageConverter converter = new Base64Converter();
string convertedProfilePicture = converter.ConvertImage(profilePicture.FullPath);
Player player = new Player(pseudo, convertedProfilePicture);
ProfileManager.AddPlayer(player);
Debug.WriteLine("Player " + pseudo + " added with profile picture " + convertedProfilePicture);
Debug.WriteLine("It's the number" + ProfileManager.Players.Count + " player");
ProfileManager.OnPropertyChanged(nameof(ProfileManager.Players));
ProfileManager.SaveData();
}
async void Button_ClickedPop(System.Object sender, System.EventArgs e)
{
if (ProfileManager.Players.Count == 0)
{
await DisplayAlert("Info", "There is no player actually\nPlease add one", "Ok");
return;
}
string result = await DisplayPromptAsync("Info", $"Choose a pseudo to delete : ", "Ok");
if (result == null) return;
Debug.WriteLine("Answer: " + result);
if (ProfileManager.RemovePlayer(result))
{
Debug.WriteLine("bam, deleted");
OnPropertyChanged(nameof(ProfileManager));
}
else
{
await DisplayAlert("Info", "This name do not exist", "Ok");
Debug.WriteLine("Player not found");
return;
}
}
async void Button_ClickedModify(System.Object sender, System.EventArgs e)
{
if (ProfileManager.Players.Count == 0)
{
await DisplayAlert("Info", "There is no player actually\nPlease add one", "Ok");
return;
}
string result = await DisplayPromptAsync("Info", $"Choose a name to modify : ", "Ok");
Debug.WriteLine("Answer: " + result);
if (result == null)
{
Debug.WriteLine("Did not found");
return;
}
string tomodify = await DisplayPromptAsync("Info", $"How will you rename it ?: ", "Ok");
Debug.WriteLine("Answer: " + tomodify);
if (tomodify == null)
{
Debug.WriteLine("Did not found");
return;
}
Debug.WriteLine("bam, modified");
bool ismodified = ProfileManager.ModifyPlayer(result, tomodify);
if (ismodified)
{
Debug.WriteLine("Modified");
ProfileManager.SaveData();
}
else Debug.WriteLine("Player not found");
}
}

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.PageProfils"
xmlns:views="clr-namespace:Trek_12.Views.Components"
Title="pageProfils">
<ContentPage.Content>
<Grid RowDefinitions="2*,3*,*">
<Image Source="bg_profils.jpg" Grid.RowSpan="3" Aspect="AspectFill"/>
<Label Text="Profils" TextColor="black" HorizontalTextAlignment="Center" FontSize="Header" Margin="30"/>
<ScrollView Grid.Row="1">
<Grid RowDefinitions="*,*,*,*,*,*">
<views:viewsProfils />
<views:viewsProfils Grid.Row="1" />
<views:viewsProfils Grid.Row="2"/>
<views:viewsProfils Grid.Row="3"/>
<views:viewsProfils Grid.Row="4"/>
<views:viewsProfils Grid.Row="5"/>
</Grid>
</ScrollView>
<HorizontalStackLayout Grid.Row="2" HorizontalOptions="Center" Spacing="50">
<Button Text="Modifier" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
<Button Text="Créer" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
<Button Text="Supprimer" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
</HorizontalStackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>

@ -1,9 +0,0 @@
namespace Trek_12.Views;
public partial class PageProfils : ContentPage
{
public PageProfils()
{
InitializeComponent();
}
}

@ -12,7 +12,7 @@
<Image Source="bg_regles.jpg" Grid.RowSpan="3" Aspect="AspectFill"/>
<Label Text="Règles" TextColor="black" HorizontalTextAlignment="Center" FontSize="Header" Margin="30"/>
<Label Text="Règles" TextColor="white" HorizontalTextAlignment="Center" FontSize="Header" Margin="30"/>
<ScrollView Grid.Row="1">
<Grid RowDefinitions="*,*,*,*,*,*,*,*,*">
@ -25,11 +25,11 @@
<Label Text="But du jeu &#10;&#10;
Obtenir le score le plus élevé en remplissant astucieusement les cases circulaires afin de créer des chemins de corde et de cartographier des zones, au fur à mesure de votre ascension.
&#10;
Sélectionner votre carte pour débuter votre ascension.Pour une toute première partie, nous vous conseillons d'utiliser la carte DUNAI &#10;" Grid.Row="0"/>
Sélectionner votre carte pour débuter votre ascension.Pour une toute première partie, nous vous conseillons d'utiliser la carte DUNAI &#10;" TextColor="white" Grid.Row="0"/>
<Label Text=" &#10;Comment Jouer :&#10;
1. Lancer les deux dés.&#10;
Ici 1 et 4&#10;" Grid.Row="1" />
Ici 1 et 4&#10;" Grid.Row="1" TextColor="white"/>
<Label Text="2. Choisissez votre résultat&#10;
a. Pour choisir votre résultat, vous devez choisir une seule opération suivante :&#10;
@ -37,30 +37,30 @@
⬆Le résultat du plus grand dé (ici 4)&#10;
- La différence entre les deux dés (ici 3 - Une différence peut être nulle mais jamais négative)&#10;
+ La somme des deux dés (ici 5)&#10;
x Le produit des deux dés (ici 4)&#10;" Grid.Row="2" />
x Le produit des deux dés (ici 4)&#10;" Grid.Row="2" TextColor="white"/>
<Label Text=" &#10;b. Sélectionnez une case libre dans la ligne correspondant à votre choix&#10;
Lorsque tous les emplacements d'une même opération sont cochés,ce choix ne vous est plus accessible.&#10;" Grid.Row="3" />
Lorsque tous les emplacements d'une même opération sont cochés,ce choix ne vous est plus accessible.&#10;" Grid.Row="3" TextColor="white" />
<Label Text=" &#10;c. Sélectionnez la case circulaire de votre choix, pour y placer votre résultat.En respectant les règles suivantes&#10;
Placez votre 1er résultat dans la case de votre choix&#10;
Tous les résultats suivants doivent être placés adjacents a une case déjà remplie.&#10;
Les conséquences de votre choix sont appliquez immédiatement : Chemin de corde et/ou Zone&#10;" Grid.Row="4" />
Les conséquences de votre choix sont appliquez immédiatement : Chemin de corde et/ou Zone&#10;" Grid.Row="4" TextColor="white" />
<Label Text=" &#10;Les chemins de corde&#10;
Un chemin de corde est une suite de nombres qui se suivent exactement.&#10;
Au moment ou vous placez un nombre, s'il crée ou continue une suite avec un nombre voisin (croissant ou décroissant), un chemin de corde sera alors crée représenté par un trait reliant deux nombres &#10;
Si votre résultat peut être relié à plusieurs nombres voisins, vous devrez donc en choisir un seul. &#10;" Grid.Row="5" />
Si votre résultat peut être relié à plusieurs nombres voisins, vous devrez donc en choisir un seul. &#10;" Grid.Row="5" TextColor="white" />
<Label Text="&#10;Deux types de cases &#10;
Cases de Base : 12 MAX Dans ces cases, vous ne pouvez avoir un nombre supérieur a 12. Si vous choisissez, ou êtes oblige, d'y mettre un résultat supérieur a 12, un symbole y sera mit a la place&#10;
Cases Dangereuses : 6 MAX Dans ces cases, il est INTERDIT d'y avoir un nombre strictement supérieur a 6. Si vous choisissez, ou êtes oblige, d'inscrire un résultat supérieur a 6 sur une case dangereuse un symbole y sera mit a la place&#10;
Un symbole donne un Malus en fin de partie. &#10;" Grid.Row="6" />
Un symbole donne un Malus en fin de partie. &#10;" Grid.Row="6" TextColor="white" />
<Label Text=" &#10;Cartographier des zones&#10;
Une zone cartographiée est un groupe d'au moins 2 nombres contigus de même valeur.&#10;
Au moment ou vous placez un nombre, s'il est identique à un nombre voisin, il crée ou agrandit une zone.&#10;
Un motif sera alors représentez dans chaque case constituant la zone.&#10;" Grid.Row="7" />
Un motif sera alors représentez dans chaque case constituant la zone.&#10;" Grid.Row="7" TextColor="white" />
<Label Text=" &#10;Fin de partie&#10;
Une fois que toutes les cases de la surface de jeu sont remplies la partie est alors terminé.La valeur des chemins de corde, des zones, des bonus et malus sont additionné.&#10;
@ -68,14 +68,14 @@
Zones : Chaque Zone cartographiée rapporte autant de points que le nombre de l'une de ses cases,+1 point par autre case qui compose cette zone.&#10;
Bonus : Votre plus long Chemin de corde ET votre plus grande Zone rapportent un bonus dépendant de leur taille.&#10;
Malus : Un symbole est placé dans chaque case orpheline (contenant un nombre qui n'appartient ni à une zone ni à un Chemin de corde)&#10;
Chaque symbole donne un malus de 3 points." Grid.Row="8"/>
Chaque symbole donne un malus de 3 points." Grid.Row="8" TextColor="white" />
</Grid>
</ScrollView>
<HorizontalStackLayout Grid.Row="2" HorizontalOptions="Center" Spacing="50">
<Button Text="Retour" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
<Button Text="Acheter" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
<Button Text="Retour" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="BackButton_Clicked"/>
<Button Text="Acheter" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="BrowserOpen_Clicked"/>
</HorizontalStackLayout>
</Grid>

@ -1,3 +1,5 @@
using System.ComponentModel;
namespace Trek_12.Views;
public partial class PageRegles : ContentPage
@ -6,4 +8,23 @@ public partial class PageRegles : ContentPage
{
InitializeComponent();
}
}
private async void BackButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
private async void BrowserOpen_Clicked(System.Object sender, System.EventArgs e)
{
try
{
Uri uri = new Uri("https://www.philibertnet.com/fr/lumberjacks-studio/89702-trek-12-3760268310293.html");
await Browser.Default.OpenAsync(uri, BrowserLaunchMode.SystemPreferred);
}
catch (Exception)
{
// An unexpected error occurred. No browser may be installed on the device.
throw new NotImplementedException();
}
}
}

Loading…
Cancel
Save