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>
/// Load all the data from JSON file
/// </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()
{
var JsonSerializer = new DataContractJsonSerializer(typeof(DataToPersist));

@ -29,7 +29,7 @@ namespace DataContractPersistence
/// <summary>
/// Load all the data from XML file
/// </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()
{
var serializer = new DataContractSerializer(typeof(DataToPersist));

@ -19,7 +19,7 @@ namespace DataContractPersistence
/// List of maps with their boards
/// </summary>
public ObservableCollection<Map> Maps { get; set; }
/// <summary>
/// List of best scores
/// </summary>

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
@ -10,45 +11,64 @@ namespace Models.Game
/// <summary>
/// This class represents the best score of a player.
/// </summary>
[DataContract]
public class BestScore
{
/// <summary>
/// Initialize a new instance of the BestScore class.
/// Name of the map.
/// </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;
}
[DataMember]
public string MapName { get; private set; }
/// <summary>
/// Number of games played by the user.
/// </summary>
public int GamesPlayed { 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>
/// Increment the number of games played by the user.
/// </summary>
@ -63,7 +83,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();
}
}
}

@ -3,6 +3,7 @@ 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;
@ -23,12 +24,11 @@ namespace Models.Game
[DataContract]
public class Game : INotifyPropertyChanged
{
/* Persistence */
/* Persistence Interface */
public IPersistence PersistenceManager { get; set; }
/* List for the game and persistence */
private ObservableCollection<Player> _players;
public ObservableCollection<Player> Players
{
get => _players;
@ -40,7 +40,6 @@ namespace Models.Game
}
private ObservableCollection<Game> _games;
public ObservableCollection<Game> Games
{
get => _games;
@ -52,7 +51,6 @@ namespace Models.Game
}
private ObservableCollection<Map> _maps;
public ObservableCollection<Map> Maps
{
get => _maps;
@ -64,7 +62,6 @@ namespace Models.Game
}
private ObservableCollection<BestScore> _bestScores;
public ObservableCollection<BestScore> BestScores
{
get => _bestScores;
@ -74,7 +71,7 @@ namespace Models.Game
OnPropertyChanged(nameof(BestScores));
}
}
private bool _isRunning;
[DataMember]
public bool IsRunning
@ -131,9 +128,20 @@ namespace Models.Game
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)
@ -144,6 +152,7 @@ namespace Models.Game
return false;
}
Players.Remove(player);
CheckAndRemoveBestScoresDependencies(player.Pseudo);
return true;
}
@ -153,6 +162,7 @@ namespace Models.Game
{
if (index.Pseudo == pseudo)
{
CheckAndChangeBestScoresDependencies(index.Pseudo, newpseudo);
index.Pseudo = newpseudo;
return true;
}
@ -161,6 +171,32 @@ namespace Models.Game
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()
{
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.Serialization;
using Models.Interfaces;
@ -17,6 +18,7 @@ namespace Models.Game
void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
/// <summary>
/// It is he pseudo of the player.
/// </summary>
@ -90,5 +92,13 @@ namespace Models.Game
{
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.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,24 +37,36 @@ 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(5, 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);
}
}

@ -79,7 +79,7 @@ public class GameTests
[Fact]
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);
@ -89,10 +89,14 @@ public class GameTests
[Fact]
public void LoadData_ShouldLoadDataFromPersistence()
{
var players = new ObservableCollection<Player> { new Player("test", "DefaultProfilePicture") };
var games = new ObservableCollection<Game> { new Game(_mockPersistence.Object) };
var maps = new ObservableCollection<Map> { new Map("test_name", "test_background.png") };
var bestScores = new ObservableCollection<BestScore> { new BestScore(1, 26) };
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));

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

@ -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

@ -4,45 +4,57 @@
xmlns:views="clr-namespace:Trek_12.Views.Components"
x:Class="Trek_12.Views.PageLeaderBoard"
Title="PageLeaderBoard">
<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>
<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">
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<VerticalStackLayout VerticalOptions="Center" HorizontalOptions="Center">
<Label
Text="Leaderboard"
VerticalOptions="Center"
HorizontalOptions="Center"
FontSize="Title"/>
<BoxView
Color="DarkSalmon"
HeightRequest="1"
WidthRequest="125"/>
</VerticalStackLayout>
</ScrollView>
</Grid>
<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,10 +1,16 @@
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)

Loading…
Cancel
Save