Merge pull request 'bestScores et leaderboard' (#107) from bestScores into dev
continuous-integration/drone/push Build is failing Details

Reviewed-on: #107
Reviewed-by: Remi NEVEU <remi.neveu@etu.uca.fr>
pull/111/head
Rémi LAVERGNE 11 months ago
commit 2f5085e39e

@ -31,7 +31,7 @@ namespace DataContractPersistence
/// <summary> /// <summary>
/// Load all the data from JSON file /// Load all the data from JSON file
/// </summary> /// </summary>
/// <returns>A tuple with the lists of players, games, maps and best scores</returns> /// <returns>A tuple with the lists of players, games, maps and bestScores</returns>
public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData() public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData()
{ {
var JsonSerializer = new DataContractJsonSerializer(typeof(DataToPersist)); var JsonSerializer = new DataContractJsonSerializer(typeof(DataToPersist));

@ -29,7 +29,7 @@ namespace DataContractPersistence
/// <summary> /// <summary>
/// Load all the data from XML file /// Load all the data from XML file
/// </summary> /// </summary>
/// <returns>A tuple with the lists of players, games, maps and best scores</returns> /// <returns>A tuple with the lists of players, games, maps and bestScores</returns>
public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData() public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData()
{ {
var serializer = new DataContractSerializer(typeof(DataToPersist)); var serializer = new DataContractSerializer(typeof(DataToPersist));

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Runtime.Serialization;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -10,45 +11,64 @@ namespace Models.Game
/// <summary> /// <summary>
/// This class represents the best score of a player. /// This class represents the best score of a player.
/// </summary> /// </summary>
[DataContract]
public class BestScore public class BestScore
{ {
/// <summary> /// <summary>
/// Initialize a new instance of the BestScore class. /// Name of the map.
/// </summary> /// </summary>
/// <param name="gamesPlayed">Number of games played by the new user</param> [DataMember]
/// <param name="score">Best score</param> public string MapName { get; private set; }
public BestScore(int gamesPlayed, int score)
{ [DataMember]
GamesPlayed = gamesPlayed; public Player ThePlayer { get; private set; }
Score = score;
if (GamesPlayed < 0)
GamesPlayed = 0;
if (Score < 0)
Score = 0;
}
/// <summary> /// <summary>
/// Number of games played by the user. /// Number of games played by the user (on a specific map).
/// </summary> /// </summary>
public int GamesPlayed { get; private set; } private int _gamesPlayed;
[DataMember]
public int GamesPlayed
{
get => _gamesPlayed;
private set
{
if (value >= 0)
_gamesPlayed = value;
else _gamesPlayed = 0;
}
}
/// <summary> /// <summary>
/// Best score of the player. /// Best score of the player (on a specific map).
/// </summary> /// </summary>
private int _score; private int _score;
[DataMember]
public int Score public int Score
{ {
get get => _score;
{
return _score;
}
private set private set
{ {
if (value > _score) if (value >= 0)
_score = value; _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> /// <summary>
/// Increment the number of games played by the user. /// Increment the number of games played by the user.
/// </summary> /// </summary>
@ -63,7 +83,31 @@ namespace Models.Game
/// <param name="newScore">New best score</param> /// <param name="newScore">New best score</param>
public void UpdateScore(int newScore) 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();
} }
} }
} }

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Serialization; using System.Runtime.Serialization;
@ -23,12 +24,11 @@ namespace Models.Game
[DataContract] [DataContract]
public class Game : INotifyPropertyChanged public class Game : INotifyPropertyChanged
{ {
/* Persistence */ /* Persistence Interface */
public IPersistence PersistenceManager { get; set; } public IPersistence PersistenceManager { get; set; }
/* List for the game and persistence */ /* List for the game and persistence */
private ObservableCollection<Player> _players; private ObservableCollection<Player> _players;
public ObservableCollection<Player> Players public ObservableCollection<Player> Players
{ {
get => _players; get => _players;
@ -40,7 +40,6 @@ namespace Models.Game
} }
private ObservableCollection<Game> _games; private ObservableCollection<Game> _games;
public ObservableCollection<Game> Games public ObservableCollection<Game> Games
{ {
get => _games; get => _games;
@ -52,7 +51,6 @@ namespace Models.Game
} }
private ObservableCollection<Map> _maps; private ObservableCollection<Map> _maps;
public ObservableCollection<Map> Maps public ObservableCollection<Map> Maps
{ {
get => _maps; get => _maps;
@ -64,7 +62,6 @@ namespace Models.Game
} }
private ObservableCollection<BestScore> _bestScores; private ObservableCollection<BestScore> _bestScores;
public ObservableCollection<BestScore> BestScores public ObservableCollection<BestScore> BestScores
{ {
get => _bestScores; get => _bestScores;
@ -131,9 +128,20 @@ namespace Models.Game
Maps.Add(map); Maps.Add(map);
} }
public void AddBestScore(BestScore bestScore) public void AddBestScore(int finalScore)
{ {
BestScores.Add(bestScore); BestScore bs = new BestScore(UsedMap.Name, CurrentPlayer, 1, finalScore);
foreach (var score in BestScores)
{
if (!bs.Equals(score)) continue;
score.IncrGamesPlayed();
score.UpdateScore(finalScore);
return;
}
BestScores.Add(bs);
BestScores.OrderByDescending(p => p.Score);
} }
public bool RemovePlayer(string playerName) public bool RemovePlayer(string playerName)
@ -144,6 +152,7 @@ namespace Models.Game
return false; return false;
} }
Players.Remove(player); Players.Remove(player);
CheckAndRemoveBestScoresDependencies(player.Pseudo);
return true; return true;
} }
@ -153,6 +162,7 @@ namespace Models.Game
{ {
if (index.Pseudo == pseudo) if (index.Pseudo == pseudo)
{ {
CheckAndChangeBestScoresDependencies(index.Pseudo, newpseudo);
index.Pseudo = newpseudo; index.Pseudo = newpseudo;
return true; return true;
} }
@ -161,6 +171,32 @@ namespace Models.Game
return false; return false;
} }
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);
}
}
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() public void LoadData()
{ {
var data = PersistenceManager.LoadData(); var data = PersistenceManager.LoadData();

@ -1,4 +1,5 @@
using System.ComponentModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Models.Interfaces; using Models.Interfaces;
@ -17,6 +18,7 @@ namespace Models.Game
void OnPropertyChanged([CallerMemberName] string propertyName = null) void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
/// <summary> /// <summary>
/// It is he pseudo of the player. /// It is he pseudo of the player.
/// </summary> /// </summary>
@ -90,5 +92,13 @@ namespace Models.Game
{ {
return Pseudo.GetHashCode(); return Pseudo.GetHashCode();
} }
/// <summary>
/// Actualize the last time the player played.
/// </summary>
public void UpdateLastPlayed()
{
LastPlayed = DateTime.Now.ToString("dd/MM/yyyy");
}
} }
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection.Emit;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Models.Game; using Models.Game;
@ -12,7 +13,11 @@ namespace Tests
[Fact] [Fact]
public void Constructor_WithNegativeInputs_SetsPropertiesToZero() 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.GamesPlayed);
Assert.Equal(0, bestScore.Score); Assert.Equal(0, bestScore.Score);
} }
@ -20,7 +25,11 @@ namespace Tests
[Fact] [Fact]
public void Constructor_WithPositiveInputs_SetsPropertiesCorrectly() 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(5, bestScore.GamesPlayed);
Assert.Equal(10, bestScore.Score); Assert.Equal(10, bestScore.Score);
} }
@ -28,24 +37,36 @@ namespace Tests
[Fact] [Fact]
public void IncrGamesPlayed_IncrementsGamesPlayed() 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(); bestScore.IncrGamesPlayed();
Assert.Equal(1, bestScore.GamesPlayed); Assert.Equal(1, bestScore.GamesPlayed);
} }
[Fact] [Fact]
public void ScoreSetter_WithLowerValue_DoesNotChangeScore() 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); bestScore.UpdateScore(5);
Assert.Equal(10, bestScore.Score);
Assert.Equal(5, bestScore.Score);
} }
[Fact] [Fact]
public void ScoreSetter_WithHigherValue_ChangesScore() 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); bestScore.UpdateScore(10);
Assert.Equal(10, bestScore.Score); Assert.Equal(10, bestScore.Score);
} }
} }

@ -79,7 +79,7 @@ public class GameTests
[Fact] [Fact]
public void AddBestScore_ShouldAddBestScoreToList() public void AddBestScore_ShouldAddBestScoreToList()
{ {
var bestScore = new BestScore(3, 125); var bestScore = new BestScore("test", new Player("John", "Picture.png"), 3, 127);
_game.AddBestScore(bestScore); _game.AddBestScore(bestScore);
@ -89,10 +89,14 @@ public class GameTests
[Fact] [Fact]
public void LoadData_ShouldLoadDataFromPersistence() public void LoadData_ShouldLoadDataFromPersistence()
{ {
var players = new ObservableCollection<Player> { new Player("test", "DefaultProfilePicture") }; var myGame = new Game(_mockPersistence.Object);
var games = new ObservableCollection<Game> { new Game(_mockPersistence.Object) }; var myPlayer = new Player("test", "DefaultProfilePicture");
var maps = new ObservableCollection<Map> { new Map("test_name", "test_background.png") }; var myMap = new Map("test_name", "test_background.png");
var bestScores = new ObservableCollection<BestScore> { new BestScore(1, 26) };
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)); _mockPersistence.Setup(p => p.LoadData()).Returns((players, games, maps, bestScores));

@ -29,7 +29,7 @@ namespace Trek_12
Directory.CreateDirectory(FilePath); Directory.CreateDirectory(FilePath);
} }
//File.Delete(Path.Combine(FilePath, FileName)); File.Delete(Path.Combine(FilePath, FileName));
string fullPath = Path.Combine(FilePath, FileName); string fullPath = Path.Combine(FilePath, FileName);
if (File.Exists(fullPath)) if (File.Exists(fullPath))

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

File diff suppressed because one or more lines are too long

@ -4,6 +4,8 @@
xmlns:views="clr-namespace:Trek_12.Views.Components" xmlns:views="clr-namespace:Trek_12.Views.Components"
x:Class="Trek_12.Views.PageLeaderBoard" x:Class="Trek_12.Views.PageLeaderBoard"
Title="PageLeaderBoard"> Title="PageLeaderBoard">
<ContentPage.Content>
<Grid BackgroundColor="BlanchedAlmond" <Grid BackgroundColor="BlanchedAlmond"
RowDefinitions="auto,6*,*"> RowDefinitions="auto,6*,*">
@ -36,13 +38,23 @@
VerticalScrollBarVisibility="Never" VerticalScrollBarVisibility="Never"
Margin="0,10"> Margin="0,10">
<VerticalStackLayout HorizontalOptions="Center"> <VerticalStackLayout HorizontalOptions="Center">
<views:ContentLeaderBoard/> <CollectionView ItemsSource="{Binding BestScores}"
<views:ContentLeaderBoard/> ItemsLayout="VerticalList"
<views:ContentLeaderBoard/> VerticalOptions="Center">
<views:ContentLeaderBoard/> <CollectionView.ItemTemplate>
<views:ContentLeaderBoard/> <DataTemplate>
<views:ContentLeaderBoard/> <views:ContentLeaderBoard
Pseudo="{Binding ThePlayer.Pseudo}"
ProfilePicture="{Binding ThePlayer.ProfilePicture}"
NbGames="{Binding GamesPlayed}"
BestScore="{Binding Score}"
Map="{Binding MapName}"
/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout> </VerticalStackLayout>
</ScrollView> </ScrollView>
</Grid> </Grid>
</ContentPage.Content>
</ContentPage> </ContentPage>

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

Loading…
Cancel
Save