Compare commits

..

10 Commits

Author SHA1 Message Date
Ismail TAHA JANAN d29126ab29 fix color die test 6
continuous-integration/drone/push Build is failing Details
2 years ago
Ismail TAHA JANAN 3ec359881e fix color die test 5
continuous-integration/drone/push Build is failing Details
2 years ago
Ismail TAHA JANAN 1f0a6b99de fix color die test 4
continuous-integration/drone/push Build is failing Details
2 years ago
Ismail TAHA JANAN 8d937614c6 fix color die test 3
continuous-integration/drone/push Build is failing Details
2 years ago
Ismail TAHA JANAN 17d6cfc46e fix color die test 2
continuous-integration/drone/push Build is failing Details
2 years ago
Ismail TAHA JANAN ffb0365f0b fix color die test
continuous-integration/drone/push Build is failing Details
2 years ago
Ismail TAHA JANAN 10204acb93 test updated
continuous-integration/drone/push Build is failing Details
2 years ago
Ismail TAHA JANAN fa3406e92d faces test added
continuous-integration/drone/push Build is failing Details
2 years ago
Alexis Drai 05ef14ccd4 🩹 Suggest a fix
continuous-integration/drone/push Build is passing Details
2 years ago
Ismail TAHA JANAN 583de4bb0b 🧪 my first test
continuous-integration/drone/push Build is failing Details
2 years ago

@ -9,9 +9,6 @@ trigger:
steps:
- name: build
image: mcr.microsoft.com/dotnet/sdk:6.0
volumes:
- name: docs
path: /docs
commands:
- cd Sources/
- dotnet restore DiceApp.sln
@ -44,25 +41,4 @@ steps:
# accessible en ligne de commande par $${PLUGIN_SONAR_TOKEN}
sonar_token:
from_secret: SECRET_SONAR_LOGIN
depends_on: [tests]
# La documentation Doxygen doit être dans le répertoire
# Documentation/doxygen
# avec le ficher
# Documentation/doxygen/Doxyfile contenant
# OUTPUT_DIRECTORY = /docs/doxygen
- name: generate-and-deploy-docs
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-docdeployer
volumes:
- name: docs
path: /docs
commands:
- /entrypoint.sh
when:
branch:
- master
depends_on: [ build ]
volumes:
- name: docs
temp: {}
depends_on: [tests]

7
.gitignore vendored

@ -4,13 +4,6 @@
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# Migrations and DB files
# useful while the DB is still fluid, in the early stages
[Mm]igrations/
*.db
*.db-wal
*.db-shm
# User-specific files
*.rsuser
*.suo

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

File diff suppressed because it is too large Load Diff

@ -1,8 +0,0 @@
<html><body>
<p>
<hr size="1"/><address style="text-align: right;"><small>Generated on $datetime with &nbsp;
<img src="CodeFirst.png" alt="Code#0" align="middle" border="0" height="40px"/>
by Doxygen version $doxygenversion</small></address>
</p>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

@ -14,47 +14,9 @@
## To use the app
### Console prototype
TLDR: you can't really
Open the *DiceAppConsole.sln* solution and navigate to the *App* project. The *Program.cs* file has a `Main()` method that can be launched.
*If you simply load DiceApp.sln, Visual Studio will not load the App project...*
The console prototype loads a stub with a few small games that you can test, and you can create new everything (with a little patience).
### DiceApp DB context with stub
Still in *Program.cs*, we also now have a nacent data layer, using Entity Framework.
Open the *DiceAppConsole.sln* solution and navigate to the *App* project. The *Program.cs* file has a `Main()` method that can be launched.
The NuGet packages are managed in files that are versioned, so you shouldn't need to manage the dependencies yourself. *"The Line"* is taken care of too.
However, you do need to create the migrations and DB (and you probably should delete them everytime you want to reload).
First, in Visual Studio's terminal ("Developer PowerShell"), go to *DiceApp/Sources/Data*, and make sure Entity Framework is installed and / or updated.
```
cd Data
dotnet tool install --global dotnet-ef
dotnet tool update --global dotnet-ef
```
Now the migrations and DB. Since we have a `DiceAppDbContext` *and* and `DiceAppDbContextWithStub`, you will need to specify which one to use.
```
cd Data
dotnet ef migrations add dice_app_db --context DiceAppDbContextWithStub
dotnet ef database update --context DiceAppDbContextWithStub --startup-project ../App
```
Replace `DiceAppDbContextWithStub` with `DiceAppDbContext` if you want to launch an app with an empty DB.
You can now run the *App* program, and check out your local DB.
You may not want to read tables in the debug window -- in which case, just download [DB Brower for SQLite](https://sqlitebrowser.org/dl/) and open the *.db* file in it.
Ta-da.
#### Troubleshooting (VS vs .NET EF)
**If Visual Studio's embedded terminal refuses to recognize `dotnet ef`, try to fully close and reopen Visual Studio**
Open the *DiceApp* solution and navigate to the *App* project. The *Program.cs* file has a `Main()` method that can be launched. It will soon be able to load a stub. Then you will be able to play a console prototype / perform functional tests. But it isn't ready yet.
## To contribute (workflow)
@ -100,9 +62,4 @@ On [the repository's main page](https://codefirst.iut.uca.fr/git/alexis.drai/dic
It should then allow you to `merge into: ...:main` and `pull from: ...:new-feature`
Follow the platform's instructions, until you've made a "work in progress" (WIP) pull request. You can now assign reviewers among your colleagues. They will get familiar with your new code -- and will either accept the branch as it is, or help you arrange it.
## Known issues and limitations
### copies of games
As of now, this app does not allow making copies of a game. We're not trying to make a roguelike, it's just not considered to be a priority feature.
Follow the platform's instructions, until you've made a "work in progress" (WIP) pull request. You can now assign reviewers among your colleagues. They will get familiar with your new code -- and will either accept the branch as it is, or help you arrange it.

@ -1,28 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<Content Include="NLog.config">
<BuildAction>Content</BuildAction>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NLog" Version="5.0.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Data\Data.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Data\Data.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup>
</Project>

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
throwConfigExceptions="true">
<targets async="true">
<target name="logfile" xsi:type="File" fileName="log.txt" />
<target name="logdebug" xsi:type="Debug" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="logdebug"/>
<logger name="*" minlevel="Warn" writeTo="logfile"/>
</rules>
</nlog>

@ -1,457 +1,16 @@
using Data;
using Data.EF;
using Data.EF.Players;
using Model.Dice;
using Model.Dice.Faces;
using Model.Games;
using Model.Players;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace App
{
internal static class Program
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
static async Task Main(string[] args)
{
// MODEL stuff
ILoader loader = new Stub();
MasterOfCeremonies masterOfCeremonies;
try
{
masterOfCeremonies = await loader.LoadApp();
}
catch (Exception ex)
{
logger.Warn(ex);
masterOfCeremonies = new(new PlayerManager(), new DiceGroupManager(), new GameManager());
}
try
{
// DB stuff when the app opens
using (DiceAppDbContext db = new())
{
// Later, we'll use the DiceAppDbContext to get a GameDbRunner
// get all the players from the DB
PlayerDbManager playerDbManager = new(db);
IEnumerable<PlayerEntity> entities = await playerDbManager.GetAll();
foreach (PlayerEntity entity in entities)
{
try
{
// persist them as models !
await masterOfCeremonies.GlobalPlayerManager.Add(entity.ToModel());
}
catch (Exception ex) { Debug.WriteLine($"{ex.Message}\n... Never mind"); }
}
}
}
catch (Exception ex) { Console.WriteLine($"{ex.Message}\n... Couldn't use the database"); }
string menuChoice = "nothing";
while (menuChoice != "q")
{
Console.WriteLine(
"l... load a game\n" +
"n... start new game\n" +
"d... delete a game\n" +
"i... see all dice\n" +
"c... create a group of dice\n" +
"p... see all players\n" +
"y... create players\n" +
"q... QUIT\n" +
">"
);
menuChoice = Console.ReadLine();
switch (menuChoice)
{
case "q":
break;
case "l":
string loadName = await ChooseGame(masterOfCeremonies);
if (masterOfCeremonies.GameManager.GetOneByName(loadName) != null)
{
await Play(masterOfCeremonies, loadName);
}
break;
case "n":
if (!(await masterOfCeremonies.DiceGroupManager.GetAll()).Any())
{
Console.WriteLine("make at least one dice group first, then try again");
break;
}
Console.WriteLine("add dice to the game");
IEnumerable<Die> newGameDice = await PrepareDice(masterOfCeremonies);
string newGameName;
Console.WriteLine("give this new game a name\n>");
newGameName = Console.ReadLine();
Console.WriteLine("add players to the game");
PlayerManager playerManager = await PreparePlayers(masterOfCeremonies);
await masterOfCeremonies.StartNewGame(newGameName, playerManager, newGameDice);
await Play(masterOfCeremonies, newGameName);
break;
case "d":
string deleteName = await ChooseGame(masterOfCeremonies);
masterOfCeremonies.GameManager.Remove(await masterOfCeremonies.GameManager.GetOneByName(deleteName));
break;
case "c":
string newGroupName;
Console.WriteLine("give this new dice group a name");
newGroupName = Console.ReadLine();
List<Die> newGroupDice = new();
string menuChoiceNewDice = "";
while (!(menuChoiceNewDice.Equals("ok") && newGroupDice.Any()))
{
Die die = null;
Console.WriteLine("create a die you want to add (at least one), or enter 'ok' if you're finished");
Console.WriteLine("what type of die ?\n" +
"n... number\n" +
"c... color\n" +
"i... image");
menuChoiceNewDice = Console.ReadLine();
switch (menuChoiceNewDice)
{
case "n":
die = MakeNumberDie();
break;
case "c":
die = MakeColorDie();
break;
case "i":
die = MakeImageDie();
break;
}
// almost no checks, this is temporary
if (die is not null)
{
newGroupDice.Add(die);
}
}
await masterOfCeremonies.DiceGroupManager.Add(new DiceGroup(newGroupName, newGroupDice));
break;
case "p":
await ShowPlayers(masterOfCeremonies);
break;
case "i":
await ShowDice(masterOfCeremonies);
break;
case "y":
await PreparePlayers(masterOfCeremonies);
break;
default:
Console.WriteLine("u wot m8?");
break;
}
}
try
{
// DB stuff when the app closes
using (DiceAppDbContext db = new())
{
// get all the players from the app's memory
IEnumerable<Player> models = await masterOfCeremonies.GlobalPlayerManager.GetAll();
// create a PlayerDbManager (and inject it with the DB)
PlayerDbManager playerDbManager = new(db);
foreach (Player model in models)
{
try // to persist them
{ // as entities !
PlayerEntity entity = model.ToEntity();
await playerDbManager.Add(entity);
}
// what if there's already a player with that name? Exception (see PlayerEntity's annotations)
catch (ArgumentException ex) { Debug.WriteLine($"{ex.Message}\n... Never mind"); }
}
}
// flushing and closing NLog before quitting completely
NLog.LogManager.Shutdown();
}
catch (Exception ex) { Console.WriteLine($"{ex.Message}\n... Couldn't use the database"); }
}
private static async Task Play(MasterOfCeremonies masterOfCeremonies, string name)
{
string menuChoicePlay = "";
while (menuChoicePlay != "q")
{
Game game = await masterOfCeremonies.GameManager.GetOneByName(name);
Console.WriteLine($"{PlayerToString(await game.GetWhoPlaysNow())}'s turn\n" +
"q... quit\n" +
"h... show history\n" +
"s... save\n" +
"any other... throw");
menuChoicePlay = Console.ReadLine();
switch (menuChoicePlay)
{
case "q":
break;
case "h":
foreach (Turn turn in game.GetHistory()) { Console.WriteLine(TurnToString(turn)); }
break;
case "s":
await masterOfCeremonies.GameManager.Add(game);
break;
default:
await MasterOfCeremonies.PlayGame(game);
Console.WriteLine(TurnToString(game.GetHistory().Last()));
break;
}
}
}
private static async Task<string> ChooseGame(MasterOfCeremonies masterOfCeremonies)
{
string name;
Console.WriteLine("which of these games?\n(choose by name)\n>");
foreach (Game game in await masterOfCeremonies.GameManager.GetAll())
{
Console.WriteLine(GameToString(game));
}
name = Console.ReadLine();
return name;
}
private static async Task ShowPlayers(MasterOfCeremonies masterOfCeremonies)
{
Console.WriteLine("Look at all them players!");
foreach (Player player in await masterOfCeremonies.GlobalPlayerManager.GetAll())
{
Console.WriteLine(PlayerToString(player));
}
}
private static async Task ShowDice(MasterOfCeremonies masterOfCeremonies)
{
foreach ((string name, ReadOnlyCollection<Die> dice) in await masterOfCeremonies.DiceGroupManager.GetAll())
{
Console.WriteLine($"{name} -- {dice}"); // maybe code a quick and dirty DieToString()
}
}
private static NumberDie MakeNumberDie()
{
NumberDie die;
List<NumberFace> faces = new();
string menuChoiceNewFaces = "";
while (menuChoiceNewFaces != "ok")
{
Console.WriteLine("create a face with a number, or enter 'ok' if you're finished");
menuChoiceNewFaces = Console.ReadLine();
PreventEmptyDieCreation(ref menuChoiceNewFaces, faces.Count);
if (!menuChoiceNewFaces.Equals("ok") && int.TryParse(menuChoiceNewFaces, out int num))
{
faces.Add(new(num));
}
}
NumberFace[] facesArr = faces.ToArray();
die = new NumberDie(facesArr[0], facesArr[1..]);
return die;
}
private static ColorDie MakeColorDie()
{
ColorDie die;
List<ColorFace> faces = new();
string menuChoiceNewFaces = "";
while (!menuChoiceNewFaces.Equals("ok"))
{
Console.WriteLine("create a face with an color name, or enter 'ok' if you're finished");
menuChoiceNewFaces = Console.ReadLine();
PreventEmptyDieCreation(ref menuChoiceNewFaces, faces.Count);
if (menuChoiceNewFaces != "ok") faces.Add(new(Color.FromName(menuChoiceNewFaces)));
}
ColorFace[] facesArr = faces.ToArray();
die = new ColorDie(facesArr[0], facesArr[1..]);
return die;
}
private static ImageDie MakeImageDie()
{
ImageDie die;
List<ImageFace> faces = new();
string menuChoiceNewFaces = "";
while (!menuChoiceNewFaces.Equals("ok"))
{
Console.WriteLine("create a face with an image uri, or enter 'ok' if you're finished");
menuChoiceNewFaces = Console.ReadLine();
PreventEmptyDieCreation(ref menuChoiceNewFaces, faces.Count);
if (menuChoiceNewFaces != "ok")
{
try
{
faces.Add(new(new Uri(menuChoiceNewFaces)));
}
catch (ArgumentNullException ex)
{
Console.WriteLine(ex.Message);
logger.Warn(ex);
}
catch (UriFormatException ex)
{
Console.WriteLine("that URI was not valid");
Console.WriteLine(ex.Message);
logger.Warn(ex);
}
}
}
ImageFace[] facesArr = faces.ToArray();
die = new ImageDie(facesArr[0], facesArr[1..]);
return die;
}
private static void PreventEmptyDieCreation(ref string menuChoice, int count)
{
if (menuChoice.Equals("ok") && count == 0)
{
Console.WriteLine("create at least one valid face");
menuChoice = ""; // persists outside the scope of this function
}
}
private async static Task<IEnumerable<Die>> PrepareDice(MasterOfCeremonies masterOfCeremonies)
{
List<Die> result = new();
Console.WriteLine("all known dice or groups of dice:");
await ShowDice(masterOfCeremonies);
string menuChoiceDice = "";
while (!(menuChoiceDice.Equals("ok") && result.Any()))
{
Console.WriteLine("write the name of a dice group you want to add (at least one), or 'ok' if you're finished");
menuChoiceDice = Console.ReadLine();
if (!menuChoiceDice.Equals("ok"))
{
IEnumerable<Die> chosenDice = (await masterOfCeremonies.DiceGroupManager.GetOneByName(menuChoiceDice)).Dice;
foreach (Die die in chosenDice)
{
result.Add(die);
}
}
}
return result.AsEnumerable();
}
private async static Task<PlayerManager> PreparePlayers(MasterOfCeremonies masterOfCeremonies)
{
PlayerManager result = new();
Console.WriteLine("all known players:");
await ShowPlayers(masterOfCeremonies);
string menuChoicePlayers = "";
while (!(menuChoicePlayers.Equals("ok") && (await result.GetAll()).Any()))
{
Console.WriteLine("write the name of a player you want to add (at least one), or 'ok' if you're finished");
menuChoicePlayers = Console.ReadLine();
if (!menuChoicePlayers.Equals("ok"))
{
Player player = new(menuChoicePlayers);
if (!(await masterOfCeremonies.GlobalPlayerManager.GetAll()).Contains(player))
{
// if the player didn't exist, now it does...
await masterOfCeremonies.GlobalPlayerManager.Add(player);
}
// almost no checks, this is temporary
try
{
await result.Add(player);
}
catch (ArgumentException ex) { Console.WriteLine($"{ex.Message}\n... Never mind"); }
}
}
return result;
}
private static string TurnToString(Turn turn)
{
string[] datetime = turn.When.ToString("s", System.Globalization.CultureInfo.InvariantCulture).Split("T");
string date = datetime[0];
string time = datetime[1];
StringBuilder sb = new();
sb.AppendFormat("{0} {1} -- {2} rolled:",
date,
time,
PlayerToString(turn.Player));
foreach (KeyValuePair<Die, Face> kvp in turn.DiceNFaces)
{
sb.Append(" " + kvp.Value.StringValue);
}
return sb.ToString();
}
private async static Task<string> GameToString(Game game)
{
StringBuilder sb = new();
sb.Append($"Game: {game.Name}");
sb.Append("\nPlayers:");
foreach (Player player in game.PlayerManager.GetAll()?.Result)
{
sb.Append($" {PlayerToString(player)}");
}
sb.Append($"\nNext: {PlayerToString(await game.GetWhoPlaysNow())}");
sb.Append("\nLog:\n");
foreach (Turn turn in game.GetHistory())
{
sb.Append($"\t{TurnToString(turn)}\n");
}
return sb.ToString();
}
private static string PlayerToString(Player player)
{
return player.Name;
}
}
}
using Data;
using Model.Games;
using System.Diagnostics;
namespace App
{
internal static class Program
{
static void Main(string[] args)
{
ILoader loader = new Stub();
GameRunner gameRunner = loader.LoadApp();
// use gameRunner to play
}
}
}

@ -1,24 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NLog" Version="5.0.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\Utils\Utils\Utils.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup>
</Project>

@ -1,9 +0,0 @@
using Data.EF.Dice.Faces;
namespace Data.EF.Dice
{
public class ColorDieEntity : DieEntity
{
public new ICollection<ColorFaceEntity> Faces { get; set; } = new List<ColorFaceEntity>();
}
}

@ -1,35 +0,0 @@
using Data.EF.Dice.Faces;
using Model.Dice;
using Model.Dice.Faces;
namespace Data.EF.Dice
{
public static class ColorDieExtensions
{
public static ColorDie ToModel(this ColorDieEntity dieEntity)
{
/*
* creating an array of faces model
*/
ColorFace[] faces = dieEntity.Faces.ToModels().ToArray();
/*
* creating the die
*/
ColorDie die = new(faces[0], faces[1..]);
return die;
}
public static IEnumerable<ColorDie> ToModels(this IEnumerable<ColorDieEntity> entities) => entities.Select(entity => entity.ToModel());
public static ColorDieEntity ToEntity(this ColorDie model)
{
var entity = new ColorDieEntity();
foreach (var face in model.Faces) { entity.Faces.Add(((ColorFace)face).ToEntity()); }
return entity;
}
public static IEnumerable<ColorDieEntity> ToEntities(this IEnumerable<ColorDie> models) => models.Select(model => model.ToEntity());
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data.EF.Dice
{
public class DiceGroupDbManager
{
}
}

@ -1,32 +0,0 @@
using Data.EF.Dice.Faces;
using Data.EF.Games;
using Data.EF.Joins;
using System.Diagnostics.CodeAnalysis;
namespace Data.EF.Dice
{
/// <summary>
/// not designed to be instantiated, but not abstract in order to allow extensions
/// </summary>
///
public class DieEntity : IEqualityComparer<DieEntity>
{
public Guid ID { get; set; }
public ICollection<FaceEntity> Faces { get; set; } = new List<FaceEntity>(); // one to many
public ICollection<TurnEntity> Turns { get; set; } = new List<TurnEntity>(); // many to many
public List<DieTurn> DieTurns { get; set; } = new();
public bool Equals(DieEntity x, DieEntity y)
{
return x is not null
&& y is not null
&& x.ID.Equals(y.ID)
&& x.Faces.Equals(y.Faces);
}
public int GetHashCode([DisallowNull] DieEntity obj)
{
return HashCode.Combine(ID, Faces);
}
}
}

@ -1,23 +0,0 @@
using System.Drawing;
namespace Data.EF.Dice.Faces
{
public class ColorFaceEntity : FaceEntity
{
public byte A { get; set; }
public byte R { get; set; }
public byte G { get; set; }
public byte B { get; set; }
public Guid ColorDieEntityID { get; set; }
public ColorDieEntity ColorDieEntity { get; set; }
public void SetValue(Color c)
{
A = c.A;
R = c.R;
G = c.G;
B = c.B;
}
}
}

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Model.Dice.Faces;
using System.Drawing;
namespace Data.EF.Dice.Faces
{
public static class ColorFaceExtensions
{
public static ColorFace ToModel(this ColorFaceEntity clrFaceEntity) => new(Color.FromArgb(clrFaceEntity.A, clrFaceEntity.R, clrFaceEntity.G, clrFaceEntity.B));
public static IEnumerable<ColorFace> ToModels(this IEnumerable<ColorFaceEntity> entities) => entities.Select(entity => entity.ToModel());
public static ColorFaceEntity ToEntity(this ColorFace model) => new() { A = model.Value.A, R = model.Value.R, G = model.Value.G, B = model.Value.B };
public static IEnumerable<ColorFaceEntity> ToEntities(this IEnumerable<ColorFace> models) => models.Select(model => model.ToEntity());
}
}

@ -1,28 +0,0 @@
using Data.EF.Games;
using Data.EF.Joins;
using System.Diagnostics.CodeAnalysis;
namespace Data.EF.Dice.Faces
{
/// <summary>
/// not designed to be instantiated, but not abstract in order to allow extensions
/// </summary>
public class FaceEntity : IEqualityComparer<FaceEntity>
{
public Guid ID { get; set; }
public ICollection<TurnEntity> Turns { get; set; } // many to many
public List<FaceTurn> FaceTurns { get; set; }
public bool Equals(FaceEntity x, FaceEntity y)
{
return x is not null
&& y is not null
&& x.ID.Equals(y.ID);
}
public int GetHashCode([DisallowNull] FaceEntity obj)
{
return ID.GetHashCode();
}
}
}

@ -1,9 +0,0 @@
namespace Data.EF.Dice.Faces
{
public class ImageFaceEntity : FaceEntity
{
public string Value { get; set; }
public Guid ImageDieEntityID { get; set; }
public ImageDieEntity ImageDieEntity { get; set; }
}
}

@ -1,22 +0,0 @@
using Data.EF.Players;
using Model.Dice.Faces;
using Model.Players;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data.EF.Dice.Faces
{
public static class ImageFaceExtensions
{
public static ImageFace ToModel(this ImageFaceEntity entity) => new(new Uri(entity.Value));
public static IEnumerable<ImageFace> ToModels(this IEnumerable<ImageFaceEntity> entities) => entities.Select(entity => entity.ToModel());
public static ImageFaceEntity ToEntity(this ImageFace model) => new() { Value = model.StringValue };
public static IEnumerable<ImageFaceEntity> ToEntities(this IEnumerable<ImageFace> models) => models.Select(model => model.ToEntity());
}
}

@ -1,9 +0,0 @@
namespace Data.EF.Dice.Faces
{
public class NumberFaceEntity : FaceEntity
{
public int Value { get; set; }
public Guid NumberDieEntityID { get; set; }
public NumberDieEntity NumberDieEntity { get; set; }
}
}

@ -1,20 +0,0 @@
using Model.Dice.Faces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data.EF.Dice.Faces
{
public static class NumberFaceExtensions
{
public static NumberFace ToModel(this NumberFaceEntity entity) => new(entity.Value);
public static IEnumerable<NumberFace> ToModels(this IEnumerable<NumberFaceEntity> entities) => entities.Select(entity => entity.ToModel());
public static NumberFaceEntity ToEntity(this NumberFace model) => new() { Value = model.Value };
public static IEnumerable<NumberFaceEntity> ToEntities(this IEnumerable<NumberFace> models) => models.Select(model => model.ToEntity());
}
}

@ -1,9 +0,0 @@
using Data.EF.Dice.Faces;
namespace Data.EF.Dice
{
public class ImageDieEntity : DieEntity
{
public new ICollection<ImageFaceEntity> Faces { get; set; } = new List<ImageFaceEntity>();
}
}

@ -1,35 +0,0 @@
using Data.EF.Dice.Faces;
using Model.Dice;
using Model.Dice.Faces;
namespace Data.EF.Dice
{
public static class ImageDieExtensions
{
public static ImageDie ToModel(this ImageDieEntity dieEntity)
{
/*
* creating an array of faces model
*/
ImageFace[] faces = dieEntity.Faces.ToModels().ToArray();
/*
* creating the die
*/
ImageDie die = new(faces[0], faces[1..]);
return die;
}
public static IEnumerable<ImageDie> ToModels(this IEnumerable<ImageDieEntity> entities) => entities.Select(entity => entity.ToModel());
public static ImageDieEntity ToEntity(this ImageDie model)
{
var entity = new ImageDieEntity();
foreach (var face in model.Faces) { entity.Faces.Add(((ImageFace)face).ToEntity()); }
return entity;
}
public static IEnumerable<ImageDieEntity> ToEntities(this IEnumerable<ImageDie> models) => models.Select(model => model.ToEntity());
}
}

@ -1,9 +0,0 @@
using Data.EF.Dice.Faces;
namespace Data.EF.Dice
{
public class NumberDieEntity : DieEntity
{
public new ICollection<NumberFaceEntity> Faces { get; set; } = new List<NumberFaceEntity>();
}
}

@ -1,35 +0,0 @@
using Data.EF.Dice.Faces;
using Model.Dice;
using Model.Dice.Faces;
namespace Data.EF.Dice
{
public static class NumberDieExtensions
{
public static NumberDie ToModel(this NumberDieEntity dieEntity)
{
/*
* creating an array of faces model
*/
NumberFace[] faces = dieEntity.Faces.ToModels().ToArray();
/*
* creating the die
*/
NumberDie die = new(faces[0], faces[1..]);
return die;
}
public static IEnumerable<NumberDie> ToModels(this IEnumerable<NumberDieEntity> entities) => entities.Select(entity => ToModel(entity));
public static NumberDieEntity ToEntity(this NumberDie model)
{
var entity = new NumberDieEntity();
foreach (var face in model.Faces) { entity.Faces.Add(((NumberFace)face).ToEntity()); }
return entity;
}
public static IEnumerable<NumberDieEntity> ToEntities(this IEnumerable<NumberDie> models) => models.Select(model => model.ToEntity());
}
}

@ -1,82 +0,0 @@
using Data.EF.Dice;
using Data.EF.Dice.Faces;
using Data.EF.Games;
using Data.EF.Joins;
using Data.EF.Players;
using Microsoft.EntityFrameworkCore;
using Model.Games;
namespace Data.EF
{
public class DiceAppDbContext : DbContext, ILoader
{
// will be async!
public virtual Task<MasterOfCeremonies> LoadApp() { throw new NotImplementedException(); }
public DbSet<PlayerEntity> PlayerEntity { get; set; }
public DbSet<TurnEntity> TurnEntity { get; set; }
public DbSet<NumberDieEntity> NumberDieEntity { get; set; }
public DbSet<NumberFaceEntity> NumberFaceEntity { get; set; }
public DbSet<ImageDieEntity> ImageDieEntity { get; set; }
public DbSet<ImageFaceEntity> ImageFaceEntity { get; set; }
public DbSet<ColorDieEntity> ColorDieEntity { get; set; }
public DbSet<ColorFaceEntity> ColorFaceEntity { get; set; }
public DiceAppDbContext() { }
public DiceAppDbContext(DbContextOptions<DiceAppDbContext> options)
: base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured) optionsBuilder.UseSqlite("Data Source=EFDice.DiceApp.db").EnableSensitiveDataLogging();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// thanks to https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration
// many to many TurnEntity <-> FaceEntity
modelBuilder.Entity<FaceEntity>()
.HasMany(face => face.Turns)
.WithMany(turn => turn.Faces)
.UsingEntity<FaceTurn>(
join => join
// FaceTurn --> TurnEntity
.HasOne(faceturn => faceturn.TurnEntity)
.WithMany(turn => turn.FaceTurns)
.HasForeignKey(faceturn => faceturn.TurnEntityID),
join => join
// FaceTurn --> FaceEntity
.HasOne(faceturn => faceturn.FaceEntity)
.WithMany(face => face.FaceTurns)
.HasForeignKey(faceturn => faceturn.FaceEntityID),
// FaceTurn.PK = (ID1, ID2)
join => join.HasKey(faceturn => new { faceturn.FaceEntityID, faceturn.TurnEntityID })
);
// many to many TurnEntity <-> DieEntity
modelBuilder.Entity<DieEntity>()
.HasMany(die => die.Turns)
.WithMany(turn => turn.Dice)
.UsingEntity<DieTurn>(
join => join
// DieTurn --> TurnEntity
.HasOne(dieturn => dieturn.TurnEntity)
.WithMany(turn => turn.DieTurns)
.HasForeignKey(dieturn => dieturn.TurnEntityID),
join => join
// DieTurn --> DieEntity
.HasOne(dieturn => dieturn.DieEntity)
.WithMany(die => die.DieTurns)
.HasForeignKey(dieturn => dieturn.DieEntityID),
// DieTurn.PK = (ID1, ID2)
join => join.HasKey(dieturn => new { dieturn.DieEntityID, dieturn.TurnEntityID })
);
}
}
}

@ -1,112 +0,0 @@
using Data.EF.Dice;
using Data.EF.Dice.Faces;
using Data.EF.Games;
using Data.EF.Joins;
using Data.EF.Players;
using Microsoft.EntityFrameworkCore;
using Model.Games;
using System.Drawing;
using System.Linq.Expressions;
using System.Security.Cryptography.X509Certificates;
namespace Data.EF
{
public class DiceAppDbContextWithStub : DiceAppDbContext
{
// will be async
public override Task<MasterOfCeremonies> LoadApp() { throw new NotImplementedException(); }
public DiceAppDbContextWithStub() { }
public DiceAppDbContextWithStub(DbContextOptions<DiceAppDbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
Guid playerID_1 = Guid.NewGuid();
Guid playerID_2 = new("6e856818-92f1-4d7d-b35c-f9c6687ef8e1");
Guid playerID_3 = Guid.NewGuid();
Guid playerID_4 = Guid.NewGuid();
PlayerEntity player_1 = new() { ID = playerID_1, Name = "Alice" };
PlayerEntity player_2 = new() { ID = playerID_2, Name = "Bob" };
PlayerEntity player_3 = new() { ID = playerID_3, Name = "Clyde" };
PlayerEntity player_4 = new() { ID = playerID_4, Name = "Dahlia" };
Guid turnID_1 = Guid.NewGuid();
Guid turnID_2 = Guid.NewGuid();
DateTime datetime_1 = new(2017, 1, 6, 17, 30, 0, DateTimeKind.Utc);
TurnEntity turn_1 = new() { ID = turnID_1, When = datetime_1, PlayerEntityID = playerID_1 };
TurnEntity turn_2 = new() { ID = turnID_2, When = DateTime.UtcNow, PlayerEntityID = playerID_2 };
Guid dieID_1 = Guid.NewGuid();
Guid dieID_2 = Guid.NewGuid();
Guid dieID_3 = Guid.NewGuid();
NumberDieEntity die_1 = new() { ID = dieID_1 };
ImageDieEntity die_2 = new() { ID = dieID_2 };
ColorDieEntity die_3 = new() { ID = dieID_3 };
Guid faceID_1 = Guid.NewGuid();
Guid faceID_2 = Guid.NewGuid();
Guid faceID_3 = Guid.NewGuid();
Guid faceID_4 = Guid.NewGuid();
Guid faceID_5 = Guid.NewGuid();
Guid faceID_6 = Guid.NewGuid();
NumberFaceEntity face_1 = new() { ID = faceID_1, Value = 1, NumberDieEntityID = dieID_1 };
NumberFaceEntity face_2 = new() { ID = faceID_2, Value = 2, NumberDieEntityID = dieID_1 };
ImageFaceEntity face_3 = new() { ID = faceID_3, Value = "https://1", ImageDieEntityID = dieID_2 };
ImageFaceEntity face_4 = new() { ID = faceID_4, Value = "https://2", ImageDieEntityID = dieID_2 };
ColorFaceEntity face_5 = new() { ID = faceID_5, ColorDieEntityID = dieID_3 };
face_5.SetValue(Color.FromArgb(255, 255, 0, 0));
ColorFaceEntity face_6 = new() { ID = faceID_6, ColorDieEntityID = dieID_3 };
face_6.SetValue(Color.FromName("green"));
modelBuilder.Entity<PlayerEntity>().HasData(player_1, player_2, player_3, player_4);
modelBuilder.Entity<TurnEntity>().HasData(turn_1, turn_2);
modelBuilder.Entity<NumberDieEntity>().HasData(die_1);
modelBuilder.Entity<NumberFaceEntity>().HasData(face_1, face_2);
modelBuilder.Entity<ImageDieEntity>().HasData(die_2);
modelBuilder.Entity<ImageFaceEntity>().HasData(face_3, face_4);
modelBuilder.Entity<ColorDieEntity>().HasData(die_3);
modelBuilder.Entity<ColorFaceEntity>().HasData(face_5, face_6);
// die 1 die 2 die 3
// turn 1 : num->2, img->https://2, clr->red
// turn 2 : num->1, clr->green
modelBuilder
.Entity<TurnEntity>()
.HasMany(turn => turn.Faces)
.WithMany(face => face.Turns)
.UsingEntity<FaceTurn>(join => join.HasData(
new { TurnEntityID = turnID_1, FaceEntityID = faceID_2 },
new { TurnEntityID = turnID_1, FaceEntityID = faceID_4 },
new { TurnEntityID = turnID_1, FaceEntityID = faceID_5 },
new { TurnEntityID = turnID_2, FaceEntityID = faceID_1 },
new { TurnEntityID = turnID_2, FaceEntityID = faceID_6 }));
modelBuilder
.Entity<TurnEntity>()
.HasMany(turn => turn.Dice)
.WithMany(die => die.Turns)
.UsingEntity<DieTurn>(join => join.HasData(
new { TurnEntityID = turnID_1, DieEntityID = dieID_1 },
new { TurnEntityID = turnID_1, DieEntityID = dieID_2 },
new { TurnEntityID = turnID_1, DieEntityID = dieID_3 },
new { TurnEntityID = turnID_2, DieEntityID = dieID_1 },
new { TurnEntityID = turnID_2, DieEntityID = dieID_3 }));
}
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data.EF.Games
{
public class GameDbManager
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data.EF.Games
{
public class GameEntity
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data.EF.Games
{
public static class GameExtensions
{
}
}

@ -1,88 +0,0 @@
using Data.EF.Dice;
using Data.EF.Dice.Faces;
using Data.EF.Joins;
using Data.EF.Players;
using Model.Players;
namespace Data.EF.Games
{
public sealed class TurnEntity : IEquatable<TurnEntity>
{
public Guid ID { get; set; }
public DateTime When { get; set; }
public PlayerEntity PlayerEntity { get; set; }
public Guid PlayerEntityID { get; set; }
public ICollection<DieEntity> Dice { get; set; } = new List<DieEntity>(); // many to many
public List<DieTurn> DieTurns { get; set; } = new();
public ICollection<FaceEntity> Faces { get; set; } = new List<FaceEntity>(); // many to many
public List<FaceTurn> FaceTurns { get; set; } = new();
public override bool Equals(object obj)
{
if (obj is not TurnEntity)
{
return false;
}
return Equals(obj as TurnEntity);
}
public bool Equals(TurnEntity other)
{
if (other is null
||
!(PlayerEntity.Equals(other.PlayerEntity)
&& When.Equals(other.When)
&& ID.Equals(other.ID)
&& Dice.Count == other.Dice.Count
&& Faces.Count == other.Faces.Count))
{
return false;
}
for (int i = 0; i < Faces.Count; i++)
{
if (Dice.ElementAt(i).Faces.Count
!= other.Dice.ElementAt(i).Faces.Count)
{
return false;
}
if (!other.Faces.ElementAt(i).ID
.Equals(Faces.ElementAt(i).ID))
{
return false;
}
for (int j = 0; j < Dice.ElementAt(i).Faces.Count; j++)
{
if (!other.Dice.ElementAt(i).Faces.ElementAt(j).ID
.Equals(Dice.ElementAt(i).Faces.ElementAt(j).ID))
{
return false;
}
}
}
return true;
}
public override int GetHashCode()
{
int result = HashCode.Combine(
ID,
When,
PlayerEntity);
foreach (DieEntity die in Dice)
{
result += die.GetHashCode();
}
foreach (FaceEntity face in Faces)
{
result += face.GetHashCode();
}
return result;
}
}
}

@ -1,68 +0,0 @@
using Data.EF.Dice;
using Data.EF.Dice.Faces;
using Data.EF.Players;
using Model.Dice;
using Model.Dice.Faces;
using Model.Games;
namespace Data.EF.Games
{
public static class TurnExtensions
{
private static (List<Die>, List<Face>) ToModelsByTypes(ICollection<DieEntity> diceEntities, ICollection<FaceEntity> faceEntities)
{
List<Die> dice = new();
List<Face> faces = new();
foreach (DieEntity dieEntity in diceEntities)
{
if (dieEntity.GetType() == typeof(NumberDieEntity)) { dice.Add((dieEntity as NumberDieEntity).ToModel()); }
if (dieEntity.GetType() == typeof(ColorDieEntity)) { dice.Add((dieEntity as ColorDieEntity).ToModel()); }
if (dieEntity.GetType() == typeof(ImageDieEntity)) { dice.Add((dieEntity as ImageDieEntity).ToModel()); }
}
foreach (FaceEntity faceEntity in faceEntities)
{
if (faceEntity.GetType() == typeof(NumberFaceEntity)) { faces.Add((faceEntity as NumberFaceEntity).ToModel()); }
if (faceEntity.GetType() == typeof(ColorFaceEntity)) { faces.Add((faceEntity as ColorFaceEntity).ToModel()); }
if (faceEntity.GetType() == typeof(ImageFaceEntity)) { faces.Add((faceEntity as ImageFaceEntity).ToModel()); }
}
return (dice, faces);
}
public static Turn ToModel(this TurnEntity entity)
{
Dictionary<Die, Face> DiceNFaces = new();
List<Die> keysList;
List<Face> valuesList;
(keysList, valuesList) = ToModelsByTypes(entity.Dice, entity.Faces);
DiceNFaces = Utils.Enumerables.FeedListsToDict(DiceNFaces, keysList, valuesList);
return Turn.CreateWithSpecifiedTime(when: entity.When, player: entity.PlayerEntity.ToModel(), diceNFaces: DiceNFaces);
}
public static IEnumerable<Turn> ToModels(this IEnumerable<TurnEntity> entities) => entities.Select(entity => entity.ToModel());
public static TurnEntity ToEntity(this Turn model)
{
List<DieEntity> DiceEntities = new();
List<FaceEntity> FaceEntities = new();
foreach (KeyValuePair<Die, Face> kvp in model.DiceNFaces)
{
if (kvp.Key.GetType() == typeof(NumberDie)) { DiceEntities.Add((kvp.Key as NumberDie).ToEntity()); FaceEntities.Add((kvp.Value as NumberFace).ToEntity()); }
if (kvp.Key.GetType() == typeof(ImageDie)) { DiceEntities.Add((kvp.Key as ImageDie).ToEntity()); FaceEntities.Add((kvp.Value as ImageFace).ToEntity()); }
if (kvp.Key.GetType() == typeof(ColorDie)) { DiceEntities.Add((kvp.Key as ColorDie).ToEntity()); FaceEntities.Add((kvp.Value as ColorFace).ToEntity()); }
}
return new TurnEntity() { When = model.When, PlayerEntity = model.Player.ToEntity(), Dice = DiceEntities, Faces = FaceEntities };
}
public static IEnumerable<TurnEntity> ToEntities(this IEnumerable<Turn> models) => models.Select(model => model.ToEntity());
}
}

@ -1,15 +0,0 @@
using Data.EF.Dice;
using Data.EF.Dice.Faces;
using Data.EF.Games;
namespace Data.EF.Joins
{
public class DieTurn
{
public Guid DieEntityID { get; set; }
public DieEntity DieEntity { get; set; }
public Guid TurnEntityID { get; set; }
public TurnEntity TurnEntity { get; set; }
}
}

@ -1,19 +0,0 @@
using Data.EF.Dice.Faces;
using Data.EF.Games;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data.EF.Joins
{
public class FaceTurn
{
public Guid FaceEntityID { get; set; }
public FaceEntity FaceEntity { get; set; }
public Guid TurnEntityID { get; set; }
public TurnEntity TurnEntity { get; set; }
}
}

@ -1,186 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Model;
using System.Collections.ObjectModel;
namespace Data.EF.Players
{
public sealed class PlayerDbManager : IManager<PlayerEntity>
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private readonly DiceAppDbContext db;
public PlayerDbManager(DiceAppDbContext db)
{
if (db is null)
{
ArgumentNullException ex = new(nameof(db), "param should not be null");
logger.Error(ex, "attempted to construct PlayerDbManager with a null context");
throw ex;
}
this.db = db;
}
/// <summary>
/// side effect: entity's name is trimmed.
/// </summary>
/// <param name="entity"></param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
private static void CleanPlayerEntity(PlayerEntity entity)
{
if (entity is null)
{
ArgumentNullException ex = new(nameof(entity), "param should not be null");
logger.Warn(ex);
throw ex;
}
if (string.IsNullOrWhiteSpace(entity.Name))
{
ArgumentException ex = new("Name property should not be null or whitespace", nameof(entity));
logger.Warn(ex);
throw ex;
}
entity.Name = entity.Name.Trim();
}
/// <summary>
/// adds a non-null PlayerEntity with a valid name to this mgr's context
/// </summary>
/// <param name="toAdd">the entity to add</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public Task<PlayerEntity> Add(PlayerEntity toAdd)
{
CleanPlayerEntity(toAdd);
if (db.PlayerEntity.Where(entity => entity.Name == toAdd.Name).Any())
{
ArgumentException ex = new("this username is already taken", nameof(toAdd));
logger.Warn(ex);
throw ex;
}
return InternalAdd(toAdd);
}
private async Task<PlayerEntity> InternalAdd(PlayerEntity toAdd)
{
EntityEntry ee = await db.PlayerEntity.AddAsync(toAdd);
await db.SaveChangesAsync();
logger.Info("Added {0}", ee.Entity.ToString());
return (PlayerEntity)ee.Entity;
}
public async Task<ReadOnlyCollection<PlayerEntity>> GetAll()
{
List<PlayerEntity> players = new();
await Task.Run(() => players.AddRange(db.PlayerEntity));
return new ReadOnlyCollection<PlayerEntity>(players);
}
/// <summary>
/// This will throw an exception if no player with such name exists.
/// If you want to know whether any player with that name exists, call IsPresentByName()
/// </summary>
/// <param name="name"></param>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="InvalidOperationException"></exception>
/// <returns></returns>
public Task<PlayerEntity> GetOneByName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Name property should not be null or whitespace", nameof(name));
}
name = name.Trim();
return InternalGetOneByName(name);
}
private async Task<PlayerEntity> InternalGetOneByName(string name)
{
return await db.PlayerEntity.Where(p => p.Name == name).FirstAsync();
}
public async Task<bool> IsPresentByName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return false;
}
name = name.Trim();
return await db.PlayerEntity.Where(p => p.Name == name).FirstOrDefaultAsync() is not null;
}
/// <summary>
/// removes a non-null PlayerEntity with a valid name from this mgr's context
/// </summary>
/// <param name="toRemove">the entity to remove</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public void Remove(PlayerEntity toRemove)
{
CleanPlayerEntity(toRemove);
bool isPresent = IsPresentByID(toRemove.ID).Result;
if (isPresent)
{
db.PlayerEntity.Remove(toRemove);
logger.Info("Removed {0}", toRemove.ToString());
db.SaveChanges();
}
}
/// <summary>
/// updates a non-null PlayerEntity with a valid name in this mgr's context. This cannot update an ID
/// </summary>
/// <param name="before">the entity to update</param>
/// <param name="after">the entity to replace 'before'</param>
/// <returns>the updated entity</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public Task<PlayerEntity> Update(PlayerEntity before, PlayerEntity after)
{
PlayerEntity[] args = { before, after };
foreach (PlayerEntity entity in args)
{
CleanPlayerEntity(entity);
}
if (before.ID != after.ID)
{
ArgumentException ex = new("ID cannot be updated", nameof(after));
logger.Warn(ex);
throw ex;
}
return InternalUpdate(before, after);
}
private async Task<PlayerEntity> InternalUpdate(PlayerEntity before, PlayerEntity after)
{
Remove(before);
return await Add(after);
}
/// <summary>
/// This will throw an exception if no player with such ID exists.
/// If you want to know whether any player with that ID exists, call IsPresentByID()
/// </summary>
/// <param name="ID">the ID to look for</param>
/// <returns>PlayerEntity with that ID</returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<PlayerEntity> GetOneByID(Guid ID)
{
return await db.PlayerEntity.FirstAsync(p => p.ID == ID);
}
public async Task<bool> IsPresentByID(Guid ID)
{
return await db.PlayerEntity.FirstOrDefaultAsync(p => p.ID == ID) is not null;
}
}
}

@ -1,186 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Model;
using System.Collections.ObjectModel;
namespace Data.EF.Players
{
public sealed class PlayerDbManager : IManager<PlayerEntity>
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private readonly DiceAppDbContext db;
public PlayerDbManager(DiceAppDbContext db)
{
if (db is null)
{
ArgumentNullException ex = new(nameof(db), "param should not be null");
logger.Error(ex, "attempted to construct PlayerDbManager with a null context");
throw ex;
}
this.db = db;
}
/// <summary>
/// side effect: entity's name is trimmed.
/// </summary>
/// <param name="entity"></param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
private static void CleanPlayerEntity(PlayerEntity entity)
{
if (entity is null)
{
ArgumentNullException ex = new(nameof(entity), "param should not be null");
logger.Warn(ex);
throw ex;
}
if (string.IsNullOrWhiteSpace(entity.Name))
{
ArgumentException ex = new("Name property should not be null or whitespace", nameof(entity));
logger.Warn(ex);
throw ex;
}
entity.Name = entity.Name.Trim();
}
/// <summary>
/// adds a non-null PlayerEntity with a valid name to this mgr's context
/// </summary>
/// <param name="toAdd">the entity to add</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public Task<PlayerEntity> Add(PlayerEntity toAdd)
{
CleanPlayerEntity(toAdd);
if (db.PlayerEntity.Where(entity => entity.Name == toAdd.Name).Any())
{
ArgumentException ex = new("this username is already taken", nameof(toAdd));
logger.Warn(ex);
throw ex;
}
return InternalAdd(toAdd);
}
private async Task<PlayerEntity> InternalAdd(PlayerEntity toAdd)
{
EntityEntry ee = await db.PlayerEntity.AddAsync(toAdd);
await db.SaveChangesAsync();
logger.Info("Added {0}", ee.Entity.ToString());
return (PlayerEntity)ee.Entity;
}
public async Task<ReadOnlyCollection<PlayerEntity>> GetAll()
{
List<PlayerEntity> players = new();
await Task.Run(() => players.AddRange(db.PlayerEntity));
return new ReadOnlyCollection<PlayerEntity>(players);
}
/// <summary>
/// This will throw an exception if no player with such name exists.
/// If you want to know whether any player with that name exists, call IsPresentByName()
/// </summary>
/// <param name="name"></param>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="InvalidOperationException"></exception>
/// <returns></returns>
public Task<PlayerEntity> GetOneByName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Name property should not be null or whitespace", nameof(name));
}
name = name.Trim();
return InternalGetOneByName(name);
}
private async Task<PlayerEntity> InternalGetOneByName(string name)
{
return await db.PlayerEntity.Where(p => p.Name == name).FirstAsync();
}
public async Task<bool> IsPresentByName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return false;
}
name = name.Trim();
return await db.PlayerEntity.Where(p => p.Name == name).FirstOrDefaultAsync() is not null;
}
/// <summary>
/// removes a non-null PlayerEntity with a valid name from this mgr's context
/// </summary>
/// <param name="toRemove">the entity to remove</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public void Remove(PlayerEntity toRemove)
{
CleanPlayerEntity(toRemove);
bool isPresent = IsPresentByID(toRemove.ID).Result;
if (isPresent)
{
db.PlayerEntity.Remove(toRemove);
logger.Info("Removed {0}", toRemove.ToString());
db.SaveChanges();
}
}
/// <summary>
/// updates a non-null PlayerEntity with a valid name in this mgr's context. This cannot update an ID
/// </summary>
/// <param name="before">the entity to update</param>
/// <param name="after">the entity to replace 'before'</param>
/// <returns>the updated entity</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public Task<PlayerEntity> Update(PlayerEntity before, PlayerEntity after)
{
PlayerEntity[] args = { before, after };
foreach (PlayerEntity entity in args)
{
CleanPlayerEntity(entity);
}
if (before.ID != after.ID)
{
ArgumentException ex = new("ID cannot be updated", nameof(after));
logger.Warn(ex);
throw ex;
}
return InternalUpdate(before, after);
}
private async Task<PlayerEntity> InternalUpdate(PlayerEntity before, PlayerEntity after)
{
Remove(before);
return await Add(after);
}
/// <summary>
/// This will throw an exception if no player with such ID exists.
/// If you want to know whether any player with that ID exists, call IsPresentByID()
/// </summary>
/// <param name="ID">the ID to look for</param>
/// <returns>PlayerEntity with that ID</returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<PlayerEntity> GetOneByID(Guid ID)
{
return await db.PlayerEntity.FirstAsync(p => p.ID == ID);
}
public async Task<bool> IsPresentByID(Guid ID)
{
return await db.PlayerEntity.FirstOrDefaultAsync(p => p.ID == ID) is not null;
}
}
}

@ -1,41 +0,0 @@
using Data.EF.Games;
using Microsoft.EntityFrameworkCore;
namespace Data.EF.Players
{
[Index(nameof(Name), IsUnique = true)]
public sealed class PlayerEntity : IEquatable<PlayerEntity>
{
public Guid ID { get; set; }
public string Name { get; set; }
public ICollection<TurnEntity> Turns { get; set; } = new List<TurnEntity>();
public override bool Equals(object obj)
{
if (obj is not PlayerEntity)
{
return false;
}
return Equals(obj as PlayerEntity);
}
public bool Equals(PlayerEntity other)
{
return other is not null && this.ID.Equals(other.ID) && this.Name.Equals(other.Name);
}
public override int GetHashCode()
{
return HashCode.Combine(ID, Name);
}
public override string ToString()
{
return $"{ID.ToString().ToUpper()} -- {Name}";
}
}
}

@ -1,15 +0,0 @@
using Model.Players;
namespace Data.EF.Players
{
public static class PlayerExtensions
{
public static Player ToModel(this PlayerEntity entity) => new(name: entity.Name);
public static IEnumerable<Player> ToModels(this IEnumerable<PlayerEntity> entities) => entities.Select(entity => entity.ToModel());
public static PlayerEntity ToEntity(this Player model) => new() { Name = model.Name };
public static IEnumerable<PlayerEntity> ToEntities(this IEnumerable<Player> models) => models.Select(model => model.ToEntity());
}
}

@ -1,9 +1,14 @@
using Model.Games;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Model.Games;
namespace Data
{
public interface ILoader
{
public Task<MasterOfCeremonies> LoadApp();
public GameRunner LoadApp();
}
}

@ -1,98 +1,129 @@
using Model.Dice;
using Model;
using Model.Dice;
using Model.Dice.Faces;
using Model.Games;
using Model.Players;
using System.Drawing;
using System.Collections.Generic;
namespace Data
{
public class Stub : ILoader
{
public async Task<MasterOfCeremonies> LoadApp()
// when the other classes are ready
// the Stub should just make and return a GameRunner, and the GameRunner should have
// a PlayerManager, a collection of Games, a FavGroupManager, etc. (see diagram)
public GameRunner LoadApp()
{
MasterOfCeremonies mc = new(new PlayerManager(), new DiceGroupManager(), new GameManager());
string g1 = "game1", g2 = "game2", g3 = "game3";
Player player1 = new("Alice(Old Stub)"), player2 = new("Bob(Old Stub)"), player3 = new("Clyde(Old Stub)");
Player player1 = new("Alice"), player2 = new("Bob"), player3 = new("Clyde");
await mc.GlobalPlayerManager.Add(player1);
await mc.GlobalPlayerManager.Add(player2);
await mc.GlobalPlayerManager.Add(player3);
IManager<KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>>> globalDieManager = new DieManager();
// create at least one group in there
// ...
IEnumerable<AbstractDie<AbstractDieFace>> dice1 = globalDieManager.GetAll().First().Value;
IEnumerable<AbstractDie<AbstractDieFace>> dice2 = globalDieManager.GetAll().Last().Value;
List<Die> monopolyDice = new();
List<Die> dndDice = new();
Game game1 = new(name: g1, playerManager: new PlayerManager(), dice: dice1);
Game game2 = new(name: g2, playerManager: new PlayerManager(), dice: dice2);
Game game3 = new(name: g3, playerManager: new PlayerManager(), dice: dice1);
string monopolyName = "Monopoly", dndName = "DnD";
List<Game> games = new() { game1, game2, game3 };
NumberFace[] d6Faces = new NumberFace[] { new(1), new(2), new(3), new(4), new(5), new(6) };
PlayerManager globalPlayerManager = new();
globalPlayerManager.Add(player1);
globalPlayerManager.Add(player2);
globalPlayerManager.Add(player3);
monopolyDice.Add(new NumberDie(new NumberFace(1), new NumberFace(1), new NumberFace(1), new NumberFace(1)));
monopolyDice.Add(new NumberDie(d6Faces[0], d6Faces[1..]));
monopolyDice.Add(new NumberDie(d6Faces[0], d6Faces[1..]));
GameRunner gameRunner = new(globalPlayerManager, globalDieManager, games);
ColorFace[] colorFaces = new ColorFace[]
{
new(Color.FromName("blue")),
new(Color.FromName("red")),
new(Color.FromName("yellow")),
new(Color.FromName("green")),
new(Color.FromName("black")),
new(Color.FromName("white"))
};
game1.AddPlayerToGame(player1);
game1.AddPlayerToGame(player2);
monopolyDice.Add(new ColorDie(colorFaces[0], colorFaces[1..]));
game2.AddPlayerToGame(player1);
game2.AddPlayerToGame(player2);
game2.AddPlayerToGame(player3);
string rootPath = "https://unsplash.com/photos/";
game3.AddPlayerToGame(player1);
game3.AddPlayerToGame(player3);
ImageFace[] imageFaces = new ImageFace[]
foreach (Game game in games)
{
new(new Uri(rootPath + "TLD6iCOlyb0")),
new(new Uri(rootPath + "rTZW4f02zY8")),
new(new Uri(rootPath + "Hyu76loQLdk")),
new(new Uri(rootPath + "A_Ncbi-RH6s")),
};
for (int i = 0; i < 10; i++)
{
Player currentPlayer = game.GetWhoPlaysNow();
game.PerformTurn(currentPlayer);
game.PrepareNextPlayer(currentPlayer);
}
}
monopolyDice.Add(new ImageDie(imageFaces[0], imageFaces[1..]));
return gameRunner;
}
NumberFace[] d20Faces = new NumberFace[] {
new(1), new(2), new(3), new(4), new(5),
new(6), new(7), new(8), new(9), new(10),
new(11), new(12), new(13), new(14), new(15),
new(16), new(17), new(18), new(19), new(20)
public static List<Player> LoadPlayers()
{
List<Player> list = new()
{
new Player("name 1"),
new Player("name 2"),
new Player("name 3"),
new Player("name 4"),
new Player("name 5"),
new Player("name 6")
};
return list;
}
dndDice.Add(new NumberDie(d20Faces[0], d20Faces[1..]));
await mc.DiceGroupManager.Add(new DiceGroup(dndName, dndDice));
await mc.DiceGroupManager.Add(new DiceGroup(monopolyName, monopolyDice));
string game1 = "Forgotten Realms", game2 = "4e", game3 = "The Coopers";
await mc.GameManager.Add(new(game1, new PlayerManager(), dndDice.AsEnumerable()));
await mc.GameManager.Add(new(game2, new PlayerManager(), dndDice.AsEnumerable()));
await mc.GameManager.Add(new(game3, new PlayerManager(), monopolyDice.AsEnumerable()));
public static List<NumberDieFace> LoadNumFaces()
{
List<NumberDieFace> list = new()
{
new NumberDieFace(1),
new NumberDieFace(2),
new NumberDieFace(3),
new NumberDieFace(4),
new NumberDieFace(5),
new NumberDieFace(6),
new NumberDieFace(7)
};
await (await mc.GameManager.GetOneByName(game1)).PlayerManager.Add(player1);
await (await mc.GameManager.GetOneByName(game1)).PlayerManager.Add(player2);
return list;
}
await (await mc.GameManager.GetOneByName(game2)).PlayerManager.Add(player1);
await (await mc.GameManager.GetOneByName(game2)).PlayerManager.Add(player2);
await (await mc.GameManager.GetOneByName(game2)).PlayerManager.Add(player3);
public static List<ColorDieFace> LoadClrFaces()
{
List<ColorDieFace> list = new()
{
new ColorDieFace("ffffff"),
new ColorDieFace("ffff66"),
new ColorDieFace("ffff11"),
new ColorDieFace("ffff22"),
new ColorDieFace("ffff33"),
new ColorDieFace("ffff44"),
new ColorDieFace("ffff55")
};
await (await mc.GameManager.GetOneByName(game3)).PlayerManager.Add(player1);
await (await mc.GameManager.GetOneByName(game3)).PlayerManager.Add(player3);
return list;
}
foreach (Game game in mc.GameManager.GetAll()?.Result)
public static List<ImageDieFace> LoadImgFaces()
{
string urlBase = "baseUrl/img/";
List<ImageDieFace> list = new()
{
for (int i = 0; i < 10; i++)
{
Player currentPlayer = await game.GetWhoPlaysNow();
game.PerformTurn(currentPlayer);
await game.PrepareNextPlayer(currentPlayer);
}
}
new ImageDieFace( urlBase + 1),
new ImageDieFace( urlBase + 2),
new ImageDieFace( urlBase + 3),
new ImageDieFace( urlBase + 4),
new ImageDieFace( urlBase + 5),
new ImageDieFace( urlBase + 6),
new ImageDieFace( urlBase + 7),
};
return mc;
return list;
}
}
}

@ -5,13 +5,16 @@ VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{11BDDDA8-CBED-46EE-A224-144C3CD545A7}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "App\App.csproj", "{81443A61-4C32-4863-90B5-7548C7385059}"
ProjectSection(ProjectDependencies) = postProject
{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6} = {4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{953D2D67-BCE7-412C-B7BB-7C63B5592359}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{11BDDDA8-CBED-46EE-A224-144C3CD545A7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Data", "Data\Data.csproj", "{E9683741-E603-4ED3-8088-4099D67FCA6D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Data", "Data\Data.csproj", "{3047BFD8-EF44-4095-9E54-45D47C7AB212}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "Utils\Utils\Utils.csproj", "{9300910D-9D32-4C79-8868-67D0ED56E2F3}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{953D2D67-BCE7-412C-B7BB-7C63B5592359}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -23,18 +26,18 @@ Global
{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}.Release|Any CPU.Build.0 = Release|Any CPU
{81443A61-4C32-4863-90B5-7548C7385059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81443A61-4C32-4863-90B5-7548C7385059}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81443A61-4C32-4863-90B5-7548C7385059}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81443A61-4C32-4863-90B5-7548C7385059}.Release|Any CPU.Build.0 = Release|Any CPU
{11BDDDA8-CBED-46EE-A224-144C3CD545A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11BDDDA8-CBED-46EE-A224-144C3CD545A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11BDDDA8-CBED-46EE-A224-144C3CD545A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11BDDDA8-CBED-46EE-A224-144C3CD545A7}.Release|Any CPU.Build.0 = Release|Any CPU
{E9683741-E603-4ED3-8088-4099D67FCA6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9683741-E603-4ED3-8088-4099D67FCA6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9683741-E603-4ED3-8088-4099D67FCA6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9683741-E603-4ED3-8088-4099D67FCA6D}.Release|Any CPU.Build.0 = Release|Any CPU
{9300910D-9D32-4C79-8868-67D0ED56E2F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9300910D-9D32-4C79-8868-67D0ED56E2F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9300910D-9D32-4C79-8868-67D0ED56E2F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9300910D-9D32-4C79-8868-67D0ED56E2F3}.Release|Any CPU.Build.0 = Release|Any CPU
{3047BFD8-EF44-4095-9E54-45D47C7AB212}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3047BFD8-EF44-4095-9E54-45D47C7AB212}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3047BFD8-EF44-4095-9E54-45D47C7AB212}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3047BFD8-EF44-4095-9E54-45D47C7AB212}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -1,54 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "App\App.csproj", "{81443A61-4C32-4863-90B5-7548C7385059}"
ProjectSection(ProjectDependencies) = postProject
{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6} = {4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{11BDDDA8-CBED-46EE-A224-144C3CD545A7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{953D2D67-BCE7-412C-B7BB-7C63B5592359}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Data", "Data\Data.csproj", "{E9683741-E603-4ED3-8088-4099D67FCA6D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "Utils\Utils\Utils.csproj", "{9300910D-9D32-4C79-8868-67D0ED56E2F3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F3B0337-8019-4988-ADFE-FD7AE5BCDBF6}.Release|Any CPU.Build.0 = Release|Any CPU
{81443A61-4C32-4863-90B5-7548C7385059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81443A61-4C32-4863-90B5-7548C7385059}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81443A61-4C32-4863-90B5-7548C7385059}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81443A61-4C32-4863-90B5-7548C7385059}.Release|Any CPU.Build.0 = Release|Any CPU
{11BDDDA8-CBED-46EE-A224-144C3CD545A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11BDDDA8-CBED-46EE-A224-144C3CD545A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11BDDDA8-CBED-46EE-A224-144C3CD545A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11BDDDA8-CBED-46EE-A224-144C3CD545A7}.Release|Any CPU.Build.0 = Release|Any CPU
{E9683741-E603-4ED3-8088-4099D67FCA6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9683741-E603-4ED3-8088-4099D67FCA6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9683741-E603-4ED3-8088-4099D67FCA6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9683741-E603-4ED3-8088-4099D67FCA6D}.Release|Any CPU.Build.0 = Release|Any CPU
{9300910D-9D32-4C79-8868-67D0ED56E2F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9300910D-9D32-4C79-8868-67D0ED56E2F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9300910D-9D32-4C79-8868-67D0ED56E2F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9300910D-9D32-4C79-8868-67D0ED56E2F3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F1DFFC48-0814-4BD5-A8E6-9F757CEB8725}
EndGlobalSection
EndGlobal

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Model.Dice.Faces;
namespace Model.Dice
{
public abstract class AbstractDie<T> : RandomnessHaver where T : AbstractDieFace
{
public IEnumerable<T> ListFaces => listFaces;
private readonly List<T> listFaces = new();
protected AbstractDie(params T[] faces)
{
listFaces.AddRange(faces);
}
public T GetRandomFace()
{
int faceIndex = rnd.Next(0, ListFaces.Count());
return ListFaces.ElementAt(faceIndex);
}
}
}

@ -1,12 +1,17 @@
using Model.Dice.Faces;
using System.Drawing;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Dice
{
public class ColorDie : HomogeneousDie<Color>
public class ColorDie : AbstractDie<AbstractDieFace>
{
public ColorDie(ColorFace first, params ColorFace[] faces)
: base(first, faces)
public ColorDie(params ColorDieFace[] faces) : base(faces)
{
}
}

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace Model.Dice
{
public sealed class DiceGroup : IEquatable<DiceGroup>
{
public string Name { get; private set; }
public ReadOnlyCollection<Die> Dice => new(dice);
private readonly List<Die> dice = new();
public DiceGroup(string name, IEnumerable<Die> dice)
{
Name = name;
this.dice.AddRange(dice);
}
public bool Equals(DiceGroup other)
{
return Name == other.Name && Dice.SequenceEqual(other.Dice);
}
public override bool Equals(object obj)
{
if (obj is null) return false; // is null
if (ReferenceEquals(obj, this)) return true; // is me
if (!obj.GetType().Equals(GetType())) return false; // is different type
return Equals(obj as DiceGroup); // is not me, is not null, is same type : send up
}
public override int GetHashCode()
{
return HashCode.Combine(Name, dice);
}
public void Deconstruct(out string name, out ReadOnlyCollection<Die> dice)
{
dice = Dice;
name = Name;
}
}
}

@ -1,86 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace Model.Dice
{
public class DiceGroupManager : IManager<DiceGroup>
{
private readonly List<DiceGroup> diceGroups = new();
public Task<DiceGroup> Add(DiceGroup toAdd)
{
if (string.IsNullOrWhiteSpace(toAdd.Name))
{
throw new ArgumentNullException(nameof(toAdd), "param should not be null or empty");
}
if (diceGroups.Contains(toAdd))
{
throw new ArgumentException("this dice group already exists", nameof(toAdd));
}
diceGroups.Add(new(toAdd.Name.Trim(), toAdd.Dice));
return Task.FromResult(toAdd);
}
public Task<ReadOnlyCollection<DiceGroup>> GetAll()
{
return Task.FromResult(new ReadOnlyCollection<DiceGroup>(diceGroups));
}
public Task<DiceGroup> GetOneByID(Guid ID)
{
throw new NotImplementedException();
}
public Task<DiceGroup> GetOneByName(string name)
{
// les groupes de dés nommés :
// ils sont case-sensistive, mais "mon jeu" == "mon jeu " == " mon jeu"
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name), "param should not be null or empty");
}
return Task.FromResult(diceGroups.First(diceGroup => diceGroup.Name.Equals(name.Trim())));
}
public void Remove(DiceGroup toRemove)
{
if (toRemove.Name is null)
{
throw new ArgumentNullException(nameof(toRemove), "param should not be null");
}
else
{
diceGroups.Remove(toRemove);
}
}
/// <summary>
/// updates a (string, ReadOnlyCollection-of-Die) couple. only the name can be updated
/// </summary>
/// <param name="before">original couple</param>
/// <param name="after">new couple</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentNullException"></exception>
public Task<DiceGroup> Update(DiceGroup before, DiceGroup after)
{
// pas autorisé de changer les dés, juste le nom
if (!before.Dice.SequenceEqual(after.Dice))
{
throw new ArgumentException("the group of dice cannot be updated, only the name", nameof(before));
}
if (string.IsNullOrWhiteSpace(before.Name) || string.IsNullOrWhiteSpace(after.Name))
{
throw new ArgumentNullException(nameof(before), "dice group name should not be null or empty");
}
Remove(before);
Add(after);
return Task.FromResult(after);
}
}
}

@ -1,28 +0,0 @@
using Model.Dice.Faces;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace Model.Dice
{
public abstract class Die
{
public ReadOnlyCollection<Face> Faces => new(faces);
protected static readonly Random rnd = new();
private readonly List<Face> faces = new();
protected Die(Face first, params Face[] faces)
{
this.faces.AddRange(faces.Append(first));
}
public virtual Face GetRandomFace()
{
int faceIndex = rnd.Next(0, Faces.Count);
return Faces.ElementAt(faceIndex);
}
}
}

@ -0,0 +1,41 @@
using Model.Dice.Faces;
using System.Collections.Generic;
using System.Linq;
namespace Model.Dice
{
public class DieManager : IManager<KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>>>
{
private readonly Dictionary<string, IEnumerable<AbstractDie<AbstractDieFace>>> diceGroups = new();
public KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>> Add(KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>> toAdd)
{
diceGroups.Add(toAdd.Key, toAdd.Value);
return toAdd;
}
public IEnumerable<KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>>> GetAll()
{
return diceGroups.AsEnumerable();
}
public KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>> GetOneByName(string name)
{
return new KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>>(name, diceGroups[name]);
}
public void Remove(KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>> toRemove)
{
diceGroups.Remove(toRemove.Key);
}
public KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>> Update(KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>> before, KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>> after)
{
// check if key 1 exists
// check if both keys same
diceGroups.Remove(before.Key);
diceGroups.Add(after.Key, after.Value);
return after;
}
}
}

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Dice.Faces
{
public abstract class AbstractDieFace
{
/// <summary>
/// every die face has a value, and they can all be represented by an int,
/// even if they're not litterally a decimal number
/// <br/>
/// USE GetPracticalValue for a Value specific to face type
/// </summary>
protected abstract int Value { get; }
public abstract object GetPracticalValue();
public override string ToString()
{
return GetPracticalValue().ToString();
}
}
}

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Dice.Faces
{
public class ColorDieFace : AbstractDieFace
{
/// <summary>
/// a decimal representation of the hex (...representation of the color)
/// </summary>
protected override int Value { get; }
/// <summary>
/// accepts hex strings like "ffbb84" ([RRGGBB])
/// </summary>
/// <param name="hexValueString">hex string</param>
public ColorDieFace(string hexValueString)
{
// https://stackoverflow.com/questions/1139957/convert-integer-to-hexadecimal-and-back-again
// if style is ("f0b"), this constructor can develop it to "ff00bb" before doing the job
Value = int.Parse(hexValueString, System.Globalization.NumberStyles.HexNumber);
}
/// <summary>
/// accepts a decimal value that represents a color hex (0 is black, 65280 is green...)
/// </summary>
/// <param name="decimalValue"></param>
public ColorDieFace(int decimalValue)
{
Value = decimalValue;
}
public override object GetPracticalValue()
{
// https://stackoverflow.com/questions/1139957/convert-integer-to-hexadecimal-and-back-again
// maybe prepend it with a "#"...
return Value.ToString("X6").Insert(0, "#");
}
}
}

@ -1,11 +0,0 @@
using System.Drawing;
namespace Model.Dice.Faces
{
public class ColorFace : Face<Color>
{
public ColorFace(Color value) : base(value)
{
}
}
}

@ -1,18 +0,0 @@
namespace Model.Dice.Faces
{
public abstract class Face
{
public string StringValue { get; protected set; }
}
public abstract class Face<T> : Face
{
public T Value { get; protected set; }
protected Face(T value)
{
Value = value;
StringValue = value.ToString();
}
}
}

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Dice.Faces
{
public class ImageDieFace : AbstractDieFace
{
/// <summary>
/// an image URL code, to find the image URL (to find the image)
/// </summary>
protected override int Value { get; }
public ImageDieFace(string uri)
{
/*parse an int after the last occurrence of "/" ? */
string resultString = uri[(uri.LastIndexOf('/') + 1)..];
/* !! here we should make sure to remove any ".jpg" etc, if there was one in the uri*/
int result = int.Parse(resultString);
Value = result;
}
public ImageDieFace(int code)
{
Value = code;
}
public override object GetPracticalValue()
{
return string.Format("Assets/images/{0}", Value);
}
}
}

@ -1,11 +0,0 @@
using System;
namespace Model.Dice.Faces
{
public class ImageFace : Face<Uri>
{
public ImageFace(Uri value) : base(value)
{
}
}
}

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Dice.Faces
{
public class NumberDieFace : AbstractDieFace
{
protected override int Value { get; }
public NumberDieFace(int value)
{
Value = value;
}
public override object GetPracticalValue()
{
return Value;
}
}
}

@ -1,9 +0,0 @@
namespace Model.Dice.Faces
{
public class NumberFace : Face<int>
{
public NumberFace(int value) : base(value)
{
}
}
}

@ -1,17 +0,0 @@
using Model.Dice.Faces;
namespace Model.Dice
{
public abstract class HomogeneousDie<T> : Die
{
protected HomogeneousDie(Face<T> first, params Face<T>[] faces)
: base(first, faces)
{
}
public override Face<T> GetRandomFace()
{
return (Face<T>)base.GetRandomFace();
}
}
}

@ -1,12 +1,15 @@
using Model.Dice.Faces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Dice
{
public class ImageDie : HomogeneousDie<Uri>
public class ImageDie : AbstractDie<AbstractDieFace>
{
public ImageDie(ImageFace first, params ImageFace[] faces)
: base(first, faces)
public ImageDie(params ImageDieFace[] faces) : base(faces)
{
}
}

@ -1,11 +1,15 @@
using Model.Dice.Faces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Dice
{
public class NumberDie : HomogeneousDie<int>
public class NumberDie : AbstractDie<AbstractDieFace>
{
public NumberDie(NumberFace first, params NumberFace[] faces)
: base(first, faces)
public NumberDie(params NumberDieFace[] faces) : base(faces)
{
}
}

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model.Dice
{
public class RandomnessHaver
{
protected RandomnessHaver()
{
}
protected static readonly Random rnd = new();
}
}

@ -1,165 +1,206 @@
using Model.Dice;
using Model.Dice.Faces;
using Model.Players;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace Model.Games
{
public class Game
{
/// <summary>
/// the name of the game 😎
/// </summary>
public string Name
{
get
{
return name;
}
set // MasterOfCeremonies will need to take care of forbidding
// (or allowing) having two Games with the same name etc.
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("param should not be null or blank", nameof(value));
}
name = value;
}
}
private string name;
/// <summary>
/// references the position in list of the current player, for a given game.
/// </summary>
private int nextIndex = 0;
/// <summary>
/// the turns that have been done so far
/// </summary>
private readonly List<Turn> turns = new();
/// </summary>
/// get a READ ONLY enumerable of all turns belonging to this game
/// </summary>
/// <returns>a readonly enumerable of all this game's turns</returns>
public ReadOnlyCollection<Turn> GetHistory() => new(turns);
/// <summary>
/// the game's player manager, doing CRUD on players and switching whose turn it is
/// </summary>
public IManager<Player> PlayerManager { get; private set; }
/// <summary>
/// the group of dice used for this game
/// </summary>
public ReadOnlyCollection<Die> Dice => new(dice);
private readonly List<Die> dice = new();
/// <summary>
/// constructs a Game with its own history of Turns.
/// If <paramref name="turns"/> is null, starts a new history
/// </summary>
/// <param name="name">the name of the game 😎</param>
/// <param name="turns">the turns that have been done so far</param>
/// <param name="playerManager">the game's player manager, doing CRUD on players and switching whose turn it is</param>
/// <param name="favGroup">the group of dice used for this game</param>
public Game(string name, IManager<Player> playerManager, IEnumerable<Die> dice, IEnumerable<Turn> turns)
{
Name = name;
PlayerManager = playerManager;
this.dice.AddRange(dice);
this.turns.AddRange(turns);
}
/// <summary>
/// constructs a Game with no history of turns.
///
/// </summary>
/// <param name="name">the name of the game 😎</param>
/// <param name="playerManager">the game's player manager, doing CRUD on players and switching whose turn it is</param>
/// <param name="favGroup">the group of dice used for this game</param>
public Game(string name, IManager<Player> playerManager, IEnumerable<Die> dice)
: this(name, playerManager, dice, new List<Turn>())
{ }
/// <summary>
/// performs a Turn, marks this Game as "started", and logs that Turn
/// </summary>
/// <param name="player">the player whose turn it is</param>
public void PerformTurn(Player player)
{
Turn turn = Turn.CreateWithDefaultTime(
player,
ThrowAll()
);
AddTurn(turn);
}
private void AddTurn(Turn turn)
{
if (!(turns.Contains(turn)))
{
turns.Add(turn);
}
}
/// <summary>
/// finds and returns the player whose turn it is
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Player> GetWhoPlaysNow()
{
if (!(await PlayerManager.GetAll()).Any())
{
throw new MemberAccessException("you are exploring an empty collection\nthis should not have happened");
}
return (await PlayerManager.GetAll()).ElementAt(nextIndex);
}
/// <summary>
/// this feels very dirty
/// </summary>
/// <param name="current">the current player</param>
/// <exception cref="MemberAccessException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public async Task PrepareNextPlayer(Player current)
{
IEnumerable<Player> players = await PlayerManager.GetAll();
if (!players.Any())
{
throw new MemberAccessException("you are exploring an empty collection\nthis should not have happened");
}
if (current == null)
{
throw new ArgumentNullException(nameof(current), "param should not be null");
}
if (!players.Contains(current))
{
throw new ArgumentException("param could not be found in this collection\n did you forget to add it?", nameof(current));
}
nextIndex = (nextIndex + 1) % players.Count();
}
/// <summary>
/// throws all the Dice in FavGroup and returns a list of their Faces
/// </summary>
/// <returns>list of AbstractDieFaces after a throw</returns>
private Dictionary<Die, Face> ThrowAll()
{
Dictionary<Die, Face> faces = new();
foreach (Die die in dice)
{
faces.Add(die, die.GetRandomFace());
}
return faces;
}
}
}
using Model.Dice;
using Model.Dice.Faces;
using Model.Players;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
namespace Model.Games
{
public class Game
{
/// <summary>
/// the name of the game 😎
/// </summary>
public string Name
{
get
{
return name;
}
set // GameRunner will need to take care of forbidding
// (or allowing) having two Games with the same name etc.
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("param should not be null or blank", nameof(value));
}
name = value;
}
}
private string name;
/// <summary>
/// references the position in list of the current player, for a given game.
/// </summary>
private int nextIndex = 0;
/// <summary>
/// the turns that have been done so far
/// </summary>
private readonly List<Turn> turns;
/// </summary>
/// get a READ ONLY enumerable of all turns belonging to this game
/// </summary>
/// <returns>a readonly enumerable of all this game's turns</returns>
public IEnumerable<Turn> GetHistory() => turns.AsEnumerable();
/// <summary>
/// the game's player manager, doing CRUD on players and switching whose turn it is
/// </summary>
private readonly IManager<Player> playerManager;
/// <summary>
/// the group of dice used for this game
/// </summary>
private readonly IEnumerable<AbstractDie<AbstractDieFace>> dice;
/// <summary>
/// constructs a Game with its own history of Turns.
/// If <paramref name="turns"/> is null, starts a new history
/// </summary>
/// <param name="name">the name of the game 😎</param>
/// <param name="turns">the turns that have been done so far</param>
/// <param name="playerManager">the game's player manager, doing CRUD on players and switching whose turn it is</param>
/// <param name="favGroup">the group of dice used for this game</param>
public Game(string name, IManager<Player> playerManager, IEnumerable<AbstractDie<AbstractDieFace>> dice, IEnumerable<Turn> turns)
{
Name = name;
this.turns = turns.ToList() ?? new List<Turn>();
this.playerManager = playerManager;
this.dice = dice;
}
/// <summary>
/// constructs a Game with no history of turns.
/// </summary>
/// <param name="name">the name of the game 😎</param>
/// <param name="playerManager">the game's player manager, doing CRUD on players and switching whose turn it is</param>
/// <param name="favGroup">the group of dice used for this game</param>
public Game(string name, IManager<Player> playerManager, IEnumerable<AbstractDie<AbstractDieFace>> dice)
: this(name, playerManager, dice, null)
{ }
/// <summary>
/// performs a Turn, marks this Game as "started", and logs that Turn
/// </summary>
/// <param name="player">the player whose turn it is</param>
public void PerformTurn(Player player)
{
Turn turn = Turn.CreateWithDefaultTime(
new Player(player), //using a copy so that next lines can't "change history"
ThrowAll()
);
turns.Add(turn);
nextIndex++;
}
/// <summary>
/// finds and returns the player whose turn it is
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public Player GetWhoPlaysNow()
{
if (!playerManager.GetAll().Any())
{
throw new MemberAccessException("you are exploring an empty collection\nthis should not have happened");
}
return playerManager.GetAll().ElementAt(nextIndex);
}
/// <summary>
/// this feels very dirty
/// </summary>
/// <param name="current">the current player</param>
/// <exception cref="MemberAccessException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public void PrepareNextPlayer(Player current)
{
if (!playerManager.GetAll().Any())
{
throw new MemberAccessException("you are exploring an empty collection\nthis should not have happened");
}
if (current == null)
{
throw new ArgumentNullException(nameof(current), "param should not be null");
}
if (!playerManager.GetAll().Contains(current))
{
throw new ArgumentException("param could not be found in this collection\n did you forget to add it?", nameof(current));
}
if (playerManager.GetAll().Last() == current)
{
// if we've reached the last player, we need the index to loop back around
nextIndex = 0;
}
else
{
// else we can just move up by one from the current index
nextIndex++;
}
}
/// <summary>
/// throws all the Dice in FavGroup and returns a list of their Faces
/// </summary>
/// <returns>list of AbstractDieFaces after a throw</returns>
private Dictionary<AbstractDie<AbstractDieFace>, AbstractDieFace> ThrowAll()
{
Dictionary<AbstractDie<AbstractDieFace>, AbstractDieFace> faces = new();
foreach (AbstractDie<AbstractDieFace> die in dice)
{
faces.Add(die, die.GetRandomFace());
}
return faces;
}
public Player AddPlayerToGame(Player player)
{
return playerManager.Add(player);
}
public IEnumerable<Player> GetPlayersFromGame()
{
return playerManager.GetAll();
}
public Player UpdatePlayerInGame(Player oldPlayer, Player newPlayer)
{
return playerManager.Update(oldPlayer, newPlayer);
}
public void RemovePlayerFromGame(Player player)
{
playerManager.Remove(player);
}
/// <summary>
/// represents a Game in string format
/// </summary>
/// <returns>a Game in string format</returns>
public override string ToString()
{
StringBuilder sb = new();
sb.AppendFormat("Game: {0}===========\n" +
"{1} are playing. {2} is next.\n" +
"Log:\n",
Name,
playerManager.GetAll().ToString(),
GetWhoPlaysNow());
foreach (Turn turn in this.turns)
{
sb.Append("\t" + turn.ToString());
}
return sb.ToString();
}
}
}

@ -1,106 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace Model.Games
{
public class GameManager : IManager<Game>
{
/// <summary>
/// the games managed by this instance
/// </summary>
private readonly List<Game> games = new();
/// <summary>
/// gets an unmodifiable collection of the games
/// </summary>
/// <returns>unmodifiable collection of the games</returns>
public Task<ReadOnlyCollection<Game>> GetAll() => Task.FromResult(new ReadOnlyCollection<Game>(games));
/// <summary>
/// finds the game with that name and returns it
/// <br/>
/// that copy does not belong to this manager's games, so it should not be modified
/// </summary>
/// <param name="name">a games's name</param>
/// <returns>game with said name, <em>or null</em> if no such game was found</returns>
public Task<Game> GetOneByName(string name)
{
if (!string.IsNullOrWhiteSpace(name))
{
return Task.FromResult(games.FirstOrDefault(g => g.Name == name)); // may return null
}
throw new ArgumentException("param should not be null or blank", nameof(name));
}
/// <summary>
/// not implemented in the model
/// </summary>
/// <param name="ID"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public Task<Game> GetOneByID(Guid ID)
{
throw new NotImplementedException();
}
/// <summary>
/// saves a given game -- does not allow copies yet: if a game with the same name exists, it is overwritten
/// </summary>
/// <param name="toAdd">a game to save</param>
public Task<Game> Add(Game toAdd)
{
if (toAdd is null)
{
throw new ArgumentNullException(nameof(toAdd), "param should not be null");
}
games.Remove(games.FirstOrDefault(g => g.Name == toAdd.Name));
// will often be an update: if game with that name exists, it is removed, else, nothing happens above
games.Add(toAdd);
return Task.FromResult(toAdd);
}
/// <summary>
/// removes a game. does nothing if the game doesn't exist
/// </summary>
/// <param name="toRemove">game to remove</param>
/// <exception cref="ArgumentNullException"></exception>
public void Remove(Game toRemove)
{
if (toRemove is null)
{
throw new ArgumentNullException(nameof(toRemove), "param should not be null");
}
games.Remove(toRemove);
}
/// <summary>
/// updates a game
/// </summary>
/// <param name="before">original game</param>
/// <param name="after">new game</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public Task<Game> Update(Game before, Game after)
{
Game[] args = { before, after };
foreach (Game game in args)
{
if (game is null)
{
throw new ArgumentNullException(nameof(after), "param should not be null");
// could also be because of before, but one param had to be chosen as an example
// and putting "player" there was raising a major code smell
}
}
Remove(before);
return (Add(after));
}
}
}

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Model.Dice;
using Model.Dice.Faces;
using Model.Players;
namespace Model.Games
{
public class GameRunner
{
private readonly IManager<Player> globalPlayerManager;
private readonly IManager<KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>>> globalDieManager;
private readonly List<Game> games;
public GameRunner(IManager<Player> globalPlayerManager, IManager<KeyValuePair<string, IEnumerable<AbstractDie<AbstractDieFace>>>> globalDieManager, List<Game> games)
{
this.globalPlayerManager = globalPlayerManager;
this.globalDieManager = globalDieManager;
this.games = games ?? new();
}
public IEnumerable<Game> GetAllGames() => games.AsEnumerable();
/// <summary>
/// finds the game with that name and returns it
/// <br/>
/// that copy does not belong to this gamerunner's games, so it should not be modified
/// </summary>
/// <param name="name">a games's name</param>
/// <returns>game with said name, <em>or null</em> if no such game was found</returns>
public Game GetOneGameByName(string name)
{
if (!string.IsNullOrWhiteSpace(name))
{
Game result = games.FirstOrDefault(g => g.Name == name);
return result; // may return null
}
throw new ArgumentException("param should not be null or blank", nameof(name));
}
public void SaveGame(Game game)
{
throw new NotSupportedException();
}
public Game LoadGame(string name)
{
throw new NotSupportedException();
}
public void StartNewGame()
{
throw new NotSupportedException();
}
public void DeleteGame(Game game)
{
throw new NotSupportedException();
}
}
}

@ -1,46 +0,0 @@
using Model.Dice;
using Model.Players;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Model.Games
{
public class MasterOfCeremonies
{
public IManager<Player> GlobalPlayerManager { get; private set; }
public IManager<DiceGroup> DiceGroupManager { get; private set; }
public IManager<Game> GameManager { get; private set; }
public MasterOfCeremonies(IManager<Player> globalPlayerManager, IManager<DiceGroup> globalDiceGroupManager, IManager<Game> gameManager)
{
GlobalPlayerManager = globalPlayerManager;
DiceGroupManager = globalDiceGroupManager;
GameManager = gameManager;
}
/// <summary>
/// creates a new game
/// </summary>
/// <param name="name"></param>
/// <param name="playerManager"></param>
/// <param name="dice"></param>
/// <returns></returns>
public async Task<Game> StartNewGame(string name, IManager<Player> playerManager, IEnumerable<Die> dice)
{
Game game = new(name, playerManager, dice);
return await GameManager.Add(game);
}
/// <summary>
/// plays one turn of the game
/// </summary>
/// <param name="game">the game from which a turn will be played</param>
public static async Task PlayGame(Game game)
{
Player current = await game.GetWhoPlaysNow();
game.PerformTurn(current);
await game.PrepareNextPlayer(current);
}
}
}

@ -1,18 +1,23 @@
using Model.Dice;
using Model.Dice.Faces;
using Model.Players;
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Model.Dice;
using Model.Dice.Faces;
using Model.Players;
namespace Model.Games
{
/// <summary>
/// a Turn consists of a Player, a DateTime, and a IEnumerable of AbstractDieFace
/// Like a turn in some game.
/// <br/>
/// Two turns are equal if they are litterally the same instance in RAM
/// (default behaviors Equals() and GetHashCode())
/// </summary>
public sealed class Turn : IEquatable<Turn>
public class Turn
{
/// <summary>
@ -28,8 +33,8 @@ namespace Model.Games
/// <summary>
/// the collection of Face that were rolled
/// </summary>
public ReadOnlyDictionary<Die, Face> DiceNFaces => new(diceNFaces);
private readonly Dictionary<Die, Face> diceNFaces;
public IEnumerable<KeyValuePair<AbstractDie<AbstractDieFace>, AbstractDieFace>> DiceNFaces => diceNFaces.AsEnumerable();
private readonly Dictionary<AbstractDie<AbstractDieFace>, AbstractDieFace> diceNFaces;
/// <summary>
/// this private constructor is to be used only by factories
@ -37,7 +42,24 @@ namespace Model.Games
/// <param name="when">date and time of the turn</param>
/// <param name="player">player who played the turn</param>
/// <param name="faces">faces that were rolled</param>
private Turn(DateTime when, Player player, IEnumerable<KeyValuePair<Die, Face>> diceNFaces)
private Turn(DateTime when, Player player, Dictionary<AbstractDie<AbstractDieFace>, AbstractDieFace> diceNFaces)
{
When = when;
Player = player;
this.diceNFaces = diceNFaces;
}
/// <summary>
/// creates a Turn with a specified time, passed as a parameter.
/// <br/>
/// whatever the DateTimeKind of <paramref name="when"/> might be,
/// it will become UTC during construction
/// </summary>
/// <param name="when">date and time of the turn</param>
/// <param name="player">player who played the turn</param>
/// <param name="faces">faces that were rolled</param>
/// <returns>a new Turn object</returns>
public static Turn CreateWithSpecifiedTime(DateTime when, Player player, Dictionary<AbstractDie<AbstractDieFace>, AbstractDieFace> diceNFaces)
{
if (player is null)
{
@ -47,7 +69,7 @@ namespace Model.Games
{
throw new ArgumentNullException(nameof(diceNFaces), "param should not be null");
}
if (!diceNFaces.Any())
if (diceNFaces.Count == 0)
{
throw new ArgumentException("param should not be null", nameof(diceNFaces));
}
@ -56,23 +78,6 @@ namespace Model.Games
when = when.ToUniversalTime();
}
When = when;
Player = player;
this.diceNFaces = diceNFaces.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
/// <summary>
/// creates a Turn with a specified time, passed as a parameter.
/// <br/>
/// whatever the DateTimeKind of <paramref name="when"/> might be,
/// it will become UTC during construction
/// </summary>
/// <param name="when">date and time of the turn</param>
/// <param name="player">player who played the turn</param>
/// <param name="faces">faces that were rolled</param>
/// <returns>a new Turn object</returns>
public static Turn CreateWithSpecifiedTime(DateTime when, Player player, IEnumerable<KeyValuePair<Die, Face>> diceNFaces)
{
return new Turn(when, player, diceNFaces);
}
@ -82,61 +87,33 @@ namespace Model.Games
/// <param name="player">player who played the turn</param>
/// <param name="faces">faces that were rolled</param>
/// <returns>a new Turn object</returns>
public static Turn CreateWithDefaultTime(Player player, IEnumerable<KeyValuePair<Die, Face>> diceNFaces)
public static Turn CreateWithDefaultTime(Player player, Dictionary<AbstractDie<AbstractDieFace>, AbstractDieFace> diceNFaces)
{
return CreateWithSpecifiedTime(DateTime.UtcNow, player, diceNFaces);
}
public bool Equals(Turn other)
/// <summary>
/// represents a turn in string format
/// </summary>
/// <returns>a turn in string format</returns>
public override string ToString()
{
if (other is null
||
!(Player.Equals(other.Player)
&& When.Equals(other.When)
&& DiceNFaces.Count == other.DiceNFaces.Count))
{
return false;
}
string[] datetime = When.ToString("s", System.Globalization.CultureInfo.InvariantCulture).Split("T");
string date = datetime[0];
string time = datetime[1];
// 🤮
for (int i = 0; i < DiceNFaces.Count; i++)
{
if (DiceNFaces.ElementAt(i).Key.Faces.Count
!= other.DiceNFaces.ElementAt(i).Key.Faces.Count)
{
return false;
}
if (!other.DiceNFaces.ElementAt(i).Value.StringValue
.Equals(DiceNFaces.ElementAt(i).Value.StringValue))
{
return false;
}
StringBuilder sb = new();
for (int j = 0; j < DiceNFaces.ElementAt(i).Key.Faces.Count; j++)
{
if (!other.DiceNFaces.ElementAt(i).Key.Faces.ElementAt(j).StringValue
.Equals(DiceNFaces.ElementAt(i).Key.Faces.ElementAt(j).StringValue))
{
return false;
}
}
}
return true;
}
public override bool Equals(object obj)
{
if (obj is not Turn)
sb.AppendFormat("{0} {1} -- {2} rolled:",
date,
time,
Player.ToString());
foreach (AbstractDieFace face in this.diceNFaces.Values)
{
return false;
sb.Append(" " + face.ToString());
}
return Equals(obj as Turn);
}
public override int GetHashCode()
{
return When.GetHashCode();
return sb.ToString();
}
}
}

@ -1,21 +1,12 @@
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Model
{
public interface IManager<T>
{
public Task<T> Add(T toAdd);
public Task<T> GetOneByName(string name);
public Task<T> GetOneByID(Guid ID);
public Task<ReadOnlyCollection<T>> GetAll();
public Task<T> Update(T before, T after);
public T Add(T toAdd);
public T GetOneByName(string name);
public IEnumerable<T> GetAll();
public T Update(T before, T after);
public void Remove(T toRemove);
}
}

@ -4,8 +4,4 @@
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.0.4" />
</ItemGroup>
</Project>

@ -30,9 +30,14 @@ namespace Model.Players
: this(player?.Name) // passes the player's name if player exists, else null
{ }
public override string ToString()
{
return Name;
}
public bool Equals(Player other)
{
return other is not null && Name.ToUpper() == other.Name.ToUpper(); // equality is case insensitive
return Name.ToUpper() == other.Name.ToUpper(); // equality is case insensitive
}
public override bool Equals(object obj)

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace Model.Players
{
@ -11,14 +9,18 @@ namespace Model.Players
/// <summary>
/// a collection of the players that this manager is in charge of
/// </summary>
private readonly List<Player> players = new();
private readonly List<Player> players;
public PlayerManager()
{
players = new();
}
/// <summary>
/// add a new player
/// </summary>
/// <param name="toAdd">player to be added</param>
/// <returns>added player</returns>
public Task<Player> Add(Player toAdd)
/// <returns>added player, or null if <paramref name="toAdd"/> was null</returns>
public Player Add(Player toAdd)
{
if (toAdd is null)
{
@ -29,27 +31,25 @@ namespace Model.Players
throw new ArgumentException("this username is already taken", nameof(toAdd));
}
players.Add(toAdd);
return Task.FromResult(toAdd);
return toAdd;
}
/// <summary>
/// finds the player with that name and returns it
/// finds the player with that name and returns A COPY OF IT
/// <br/>
/// that copy does not belong to this manager's players, so it should not be modified
/// </summary>
/// <param name="name">a player's unique name</param>
/// <returns>player with said name, <em>or null</em> if no such player was found</returns>
public Task<Player> GetOneByName(string name)
public Player GetOneByName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("param should not be null or blank", nameof(name));
}
Player result = players.FirstOrDefault(p => p.Name.ToUpper().Equals(name.ToUpper().Trim()));
if (result == null)
if (!string.IsNullOrWhiteSpace(name))
{
return Task.FromResult<Player>(null);
Player wanted = new(name);
Player result = players.FirstOrDefault(p => p.Equals(wanted));
return result is null ? null : new Player(result); // THIS IS A COPY (using a copy constructor)
}
return Task.FromResult(result);
throw new ArgumentException("param should not be null or blank", nameof(name));
}
/// </summary>
@ -57,7 +57,7 @@ namespace Model.Players
/// so that the only way to modify the collection of players is to use this class's methods
/// </summary>
/// <returns>a readonly enumerable of all this manager's players</returns>
public Task<ReadOnlyCollection<Player>> GetAll() => Task.FromResult(new ReadOnlyCollection<Player>(players));
public IEnumerable<Player> GetAll() => players.AsEnumerable();
/// <summary>
/// update a player from <paramref name="before"/> to <paramref name="after"/>
@ -65,7 +65,7 @@ namespace Model.Players
/// <param name="before">player to be updated</param>
/// <param name="after">player in the state that it needs to be in after the update</param>
/// <returns>updated player</returns>
public Task<Player> Update(Player before, Player after)
public Player Update(Player before, Player after)
{
Player[] args = { before, after };
@ -95,10 +95,5 @@ namespace Model.Players
// the built-in Remove() method will use our redefined Equals(), using Name only
players.Remove(toRemove);
}
public Task<Player> GetOneByID(Guid ID)
{
throw new NotImplementedException();
}
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice
{
public class ColorDieEntityTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice
{
public class ColorDieExtensionsTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice
{
public class DiceGroupDbManagerTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice.Faces
{
public class ColorFaceEntityTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice.Faces
{
public class ColorFaceExtensionsTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice.Faces
{
public class ImageFaceEntityTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice.Faces
{
public class ImageFaceExtensionsTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice.Faces
{
public class NumberFaceEntityTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice.Faces
{
public class NumberFaceExtensionsTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice
{
public class ImageDieEntityTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice
{
public class ImageDieExtensionsTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice
{
public class NumberDieEntityTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Dice
{
public class NumberDieExtensionsTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs
{
public class DiceAppDbContextTest
{
}
}

@ -1,49 +0,0 @@
using Data.EF;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Tests.Data_UTs
{
public class DiceAppDbContextWithStubTest
{
private readonly SqliteConnection connection = new("DataSource=:memory:");
private readonly DbContextOptions<DiceAppDbContext> options;
public DiceAppDbContextWithStubTest()
{
connection.Open();
options = new DbContextOptionsBuilder<DiceAppDbContext>()
.UseSqlite(connection)
.EnableSensitiveDataLogging()
.Options;
}
[Theory]
[InlineData("Alice")]
[InlineData("Bob")]
[InlineData("Clyde")]
[InlineData("Dahlia")]
public void TestDbStubContainsAll(string name)
{
// Arrange
using (DiceAppDbContextWithStub db = new(options))
{
db.Database.EnsureCreated();
// Assert
Assert.True(db.PlayerEntity.Where(p => p.Name.Equals(name)).Any());
}
}
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Games
{
public class GameDbManagerTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Games
{
public class GameEntityTest
{
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Data_UTs.Games
{
public class GameExtensionsTest
{
}
}

@ -1,407 +0,0 @@
using Data.EF.Dice;
using Data.EF.Dice.Faces;
using Data.EF.Games;
using Data.EF.Joins;
using Data.EF.Players;
using System;
using System.Collections.Generic;
using System.Drawing;
using Xunit;
namespace Tests.Data_UTs.Games
{
public class TurnEntityTest
{
private readonly DieEntity numDie;
private readonly FaceEntity numFace1 = new NumberFaceEntity() { ID = Guid.NewGuid(), Value = 7 };
private readonly FaceEntity numFace2 = new NumberFaceEntity() { ID = Guid.NewGuid(), Value = 8 };
private readonly DieEntity clrDie;
private readonly FaceEntity clrFace1 = new ColorFaceEntity() { ID = Guid.NewGuid(), A = 255, R = 255, G = 255, B = 255 };
private readonly FaceEntity clrFace2 = new ColorFaceEntity() { ID = Guid.NewGuid() };
private readonly DieEntity imgDie;
private readonly FaceEntity imgFace1 = new ImageFaceEntity() { ID = Guid.NewGuid(), Value = "https://a" };
private readonly FaceEntity imgFace2 = new ImageFaceEntity() { ID = Guid.NewGuid(), Value = "https://b" };
private readonly PlayerEntity player1 = new() { ID = Guid.NewGuid(), Name = "Marvin" };
private readonly PlayerEntity player2 = new() { ID = Guid.NewGuid(), Name = "Barbara" };
private readonly DateTime datetime1 = new(2020, 6, 15, 12, 15, 3, DateTimeKind.Utc);
private readonly DateTime datetime2 = new(2016, 12, 13, 14, 15, 16, DateTimeKind.Utc);
private readonly DieTurn dieTurn1;
private readonly DieTurn dieTurn2;
private readonly DieTurn dieTurn3;
private readonly DieTurn dieTurn4;
private readonly DieTurn dieTurn5;
private readonly FaceTurn faceTurn1;
private readonly FaceTurn faceTurn2;
private readonly FaceTurn faceTurn3;
private readonly FaceTurn faceTurn4;
private readonly FaceTurn faceTurn5;
private readonly TurnEntity turn1;
private readonly TurnEntity turn2;
private readonly TurnEntity turn3;
public TurnEntityTest()
{
numDie = new NumberDieEntity() { ID = Guid.NewGuid(), Faces = new List<NumberFaceEntity>() { numFace1 as NumberFaceEntity, numFace2 as NumberFaceEntity } };
(numFace1 as NumberFaceEntity).NumberDieEntity = (NumberDieEntity)numDie;
(numFace2 as NumberFaceEntity).NumberDieEntity = (NumberDieEntity)numDie;
(clrFace2 as ColorFaceEntity).SetValue(Color.FromName("blue"));
clrDie = new ColorDieEntity() { ID = Guid.NewGuid(), Faces = new List<ColorFaceEntity>() { clrFace1 as ColorFaceEntity, clrFace2 as ColorFaceEntity } };
(clrFace1 as ColorFaceEntity).ColorDieEntity = (ColorDieEntity)clrDie;
(clrFace2 as ColorFaceEntity).ColorDieEntity = (ColorDieEntity)clrDie;
imgDie = new ImageDieEntity() { ID = Guid.NewGuid(), Faces = new List<ImageFaceEntity>() { imgFace1 as ImageFaceEntity, imgFace2 as ImageFaceEntity } };
(imgFace1 as ImageFaceEntity).ImageDieEntity = (ImageDieEntity)imgDie;
(imgFace2 as ImageFaceEntity).ImageDieEntity = (ImageDieEntity)imgDie;
turn1 = new()
{
ID = Guid.NewGuid(),
When = datetime1,
PlayerEntity = player1,
PlayerEntityID = player1.ID,
Dice = new List<DieEntity>
{
numDie,
clrDie,
imgDie
},
Faces = new List<FaceEntity>
{
numFace1,
clrFace2,
imgFace2
},
};
dieTurn1 = new() { DieEntityID = numDie.ID, DieEntity = numDie, TurnEntityID = turn1.ID, TurnEntity = turn1 };
dieTurn2 = new() { DieEntityID = clrDie.ID, DieEntity = clrDie, TurnEntityID = turn1.ID, TurnEntity = turn1 };
dieTurn3 = new() { DieEntityID = imgDie.ID, DieEntity = imgDie, TurnEntityID = turn1.ID, TurnEntity = turn1 };
turn1.DieTurns = new() { dieTurn1, dieTurn2, dieTurn3 };
numDie.DieTurns = new() { dieTurn1 };
clrDie.DieTurns = new() { dieTurn2 };
imgDie.DieTurns = new() { dieTurn3 };
faceTurn1 = new() { FaceEntityID = numFace1.ID, FaceEntity = numFace1, TurnEntityID = turn1.ID, TurnEntity = turn1 };
faceTurn2 = new() { FaceEntityID = clrFace2.ID, FaceEntity = clrFace2, TurnEntityID = turn1.ID, TurnEntity = turn1 };
faceTurn3 = new() { FaceEntityID = imgFace2.ID, FaceEntity = imgFace2, TurnEntityID = turn1.ID, TurnEntity = turn1 };
turn1.FaceTurns = new() { faceTurn1, faceTurn2, faceTurn3 };
numFace1.FaceTurns = new() { faceTurn1 };
clrFace2.FaceTurns = new() { faceTurn2 };
imgFace2.FaceTurns = new() { faceTurn3 };
Guid turn2ID = Guid.NewGuid();
turn2 = new()
{
ID = turn2ID,
When = datetime2,
PlayerEntity = player2,
PlayerEntityID = player2.ID,
Dice = new List<DieEntity>
{
numDie,
clrDie
},
Faces = new List<FaceEntity>
{
numFace2,
clrFace2
},
};
dieTurn4 = new() { DieEntityID = numDie.ID, DieEntity = numDie, TurnEntityID = turn2.ID, TurnEntity = turn2 };
dieTurn5 = new() { DieEntityID = clrDie.ID, DieEntity = clrDie, TurnEntityID = turn2.ID, TurnEntity = turn2 };
turn2.DieTurns = new() { dieTurn4, dieTurn5 };
numDie.DieTurns = new() { dieTurn4 };
clrDie.DieTurns = new() { dieTurn5 };
faceTurn4 = new() { FaceEntityID = numFace2.ID, FaceEntity = numFace2, TurnEntityID = turn2.ID, TurnEntity = turn2 };
faceTurn5 = new() { FaceEntityID = clrFace2.ID, FaceEntity = clrFace2, TurnEntityID = turn2.ID, TurnEntity = turn2 };
turn2.FaceTurns = new() { faceTurn4, faceTurn5 };
numFace2.FaceTurns = new() { faceTurn4 };
clrFace2.FaceTurns = new() { faceTurn5 };
turn3 = new()
{
ID = turn2ID,
When = datetime2,
PlayerEntity = player2,
PlayerEntityID = player2.ID,
Dice = new List<DieEntity>
{
numDie,
clrDie
},
Faces = new List<FaceEntity>
{
numFace2,
clrFace2
},
};
}
[Fact]
public void TestGetSetID()
{
// Arrange
TurnEntity turn = new();
Guid expected = Guid.NewGuid();
// Act
turn.ID = expected;
Guid actual = turn.ID;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestGetSetWhen()
{
// Arrange
TurnEntity turn = new();
DateTime expected = datetime1;
// Act
turn.When = expected;
DateTime actual = turn.When;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestGetSetPlayer()
{
// Arrange
TurnEntity turn = new();
PlayerEntity expected = player1;
// Act
turn.PlayerEntity = expected;
PlayerEntity actual = turn.PlayerEntity;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestGetSetPlayerID()
{
// Arrange
TurnEntity turn = new();
Guid expected = player1.ID;
// Act
turn.PlayerEntityID = expected;
Guid actual = turn.PlayerEntityID;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestGetSetDice()
{
// Arrange
TurnEntity turn = new();
ICollection<DieEntity> expected = new List<DieEntity>
{
numDie,
clrDie,
imgDie
};
// Act
turn.Dice = expected;
ICollection<DieEntity> actual = turn.Dice;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestGetSetDieTurns()
{
// Arrange
TurnEntity turn = new();
List<DieTurn> expected = new() { dieTurn1, dieTurn2, dieTurn3 };
// Act
turn.DieTurns = expected;
List<DieTurn> actual = turn.DieTurns;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestGetSetFaces()
{
// Arrange
TurnEntity turn = new();
ICollection<FaceEntity> expected = new List<FaceEntity>
{
numFace1,
clrFace1,
imgFace1
};
// Act
turn.Faces = expected;
ICollection<FaceEntity> actual = turn.Faces;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestGetSetFaceTurns()
{
// Arrange
TurnEntity turn = new();
List<FaceTurn> expected = new() { faceTurn1, faceTurn2 };
// Act
turn.FaceTurns = expected;
List<FaceTurn> actual = turn.FaceTurns;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestEqualsWhenNotTurnEntityThenFalse()
{
// Arrange
Point point;
TurnEntity entity;
// Act
point = new(1, 2);
entity = turn1;
// Assert
Assert.False(point.Equals(entity));
Assert.False(entity.Equals(point));
}
[Fact]
public void TestEqualsWhenNullThenFalse()
{
// Arrange
TurnEntity entity;
// Act
entity = turn2;
// Assert
Assert.False(entity.Equals(null));
}
[Fact]
public void TestGoesThruToSecondMethodIfObjIsTypeTurnEntity()
{
// Arrange
object t1;
TurnEntity t2;
// Act
t1 = turn1;
t2 = turn2;
// Assert
Assert.False(t1.Equals(t2));
Assert.False(t2.Equals(t1));
}
[Fact]
public void TestEqualsFalseIfNotSame()
{
// Arrange
TurnEntity t1;
TurnEntity t2;
// Act
t1 = turn1;
t2 = turn2;
// Assert
Assert.False(t1.Equals(t2));
Assert.False(t2.Equals(t1));
}
[Fact]
public void TestEqualsTrueIfSame()
{
// Arrange
TurnEntity t1;
TurnEntity t2;
// Act
t1 = turn2;
t2 = turn3; // turns 2 and 3 should be same as far as Equals is concerned
// Assert
Assert.True(t1.Equals(t2));
Assert.True(t2.Equals(t1));
}
[Fact]
public void TestSameHashFalseIfNotSame()
{
// Arrange
TurnEntity t1;
TurnEntity t2;
// Act
t1 = turn1;
t2 = turn2;
// Assert
Assert.False(t1.GetHashCode().Equals(t2.GetHashCode()));
Assert.False(t2.GetHashCode().Equals(t1.GetHashCode()));
}
[Fact]
public void TestSameHashTrueIfSame()
{
// Arrange
TurnEntity t1;
TurnEntity t2;
// Act
t1 = turn2;
t2 = turn3;
// Assert
Assert.True(t1.GetHashCode().Equals(t2.GetHashCode()));
Assert.True(t2.GetHashCode().Equals(t1.GetHashCode()));
}
}
}

@ -1,274 +0,0 @@
using Data.EF.Dice.Faces;
using Data.EF.Dice;
using Data.EF.Players;
using Model.Dice;
using Model.Players;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Data.EF.Games;
using System.Drawing;
using Model.Games;
using Newtonsoft.Json.Linq;
using Model.Dice.Faces;
using System.Diagnostics;
namespace Tests.Data_UTs.Games
{
public class TurnExtensionsTest
{
private readonly DateTime datetime1 = new(2013, 2, 1, 1, 19, 4, DateTimeKind.Utc);
private readonly DateTime datetime2 = new(2014, 8, 1, 23, 12, 4, DateTimeKind.Utc);
private readonly PlayerEntity playerEntity = new() { Name = "Paula" };
private readonly DieEntity numDieEntity;
private readonly FaceEntity numFace1Entity = new NumberFaceEntity() { Value = 7 };
private readonly FaceEntity numFace2Entity = new NumberFaceEntity() { Value = 8 };
private readonly DieEntity clrDieEntity;
private readonly FaceEntity clrFace1Entity = new ColorFaceEntity() { A = 255, R = 255, G = 255, B = 255 };
private readonly FaceEntity clrFace2Entity = new ColorFaceEntity() { A = 255, R = 0, G = 0, B = 128 };
private readonly DieEntity imgDieEntity;
private readonly FaceEntity imgFace1Entity = new ImageFaceEntity() { Value = "https://a" };
private readonly FaceEntity imgFace2Entity = new ImageFaceEntity() { Value = "https://b" };
public TurnExtensionsTest()
{
numDieEntity = new NumberDieEntity() { Faces = new List<NumberFaceEntity>() { numFace1Entity as NumberFaceEntity, numFace2Entity as NumberFaceEntity } };
(numFace1Entity as NumberFaceEntity).NumberDieEntity = (NumberDieEntity)numDieEntity;
(numFace2Entity as NumberFaceEntity).NumberDieEntity = (NumberDieEntity)numDieEntity;
clrDieEntity = new ColorDieEntity() { Faces = new List<ColorFaceEntity>() { clrFace1Entity as ColorFaceEntity, clrFace2Entity as ColorFaceEntity } };
(clrFace1Entity as ColorFaceEntity).ColorDieEntity = (ColorDieEntity)clrDieEntity;
(clrFace2Entity as ColorFaceEntity).ColorDieEntity = (ColorDieEntity)clrDieEntity;
imgDieEntity = new ImageDieEntity() { Faces = new List<ImageFaceEntity>() { imgFace1Entity as ImageFaceEntity, imgFace2Entity as ImageFaceEntity } };
(imgFace1Entity as ImageFaceEntity).ImageDieEntity = (ImageDieEntity)imgDieEntity;
(imgFace2Entity as ImageFaceEntity).ImageDieEntity = (ImageDieEntity)imgDieEntity;
}
[Fact]
public void TestToModel()
{
// Arrange
TurnEntity entity = new()
{
When = datetime1,
PlayerEntity = playerEntity,
Dice = new List<DieEntity>
{
numDieEntity,
clrDieEntity,
imgDieEntity
},
Faces = new List<FaceEntity>
{
numFace1Entity,
clrFace2Entity,
imgFace2Entity
}
};
Turn expected = Turn.CreateWithSpecifiedTime(
datetime1,
playerEntity.ToModel(),
new Dictionary<Die, Face>()
{
{(numDieEntity as NumberDieEntity).ToModel(), (numFace1Entity as NumberFaceEntity).ToModel() },
{(clrDieEntity as ColorDieEntity).ToModel(), (clrFace2Entity as ColorFaceEntity).ToModel() },
{(imgDieEntity as ImageDieEntity).ToModel(), (imgFace2Entity as ImageFaceEntity).ToModel() }
});
// Act
Turn actual = entity.ToModel();
// Assert
Assert.True(expected.Equals(actual));
}
[Fact]
public void TestToModels()
{
// Arrange
TurnEntity[] entities = new TurnEntity[]
{
new TurnEntity()
{
When = datetime1,
PlayerEntity = new PlayerEntity() {Name = "Aardvark"},
Dice = new List<DieEntity>
{
numDieEntity,
clrDieEntity
},
Faces = new List<FaceEntity>
{
numFace2Entity,
clrFace2Entity
}
},
new TurnEntity()
{
When = datetime2,
PlayerEntity = new PlayerEntity() {Name = "Chloe"},
Dice = new List<DieEntity>
{
clrDieEntity,
imgDieEntity
},
Faces = new List<FaceEntity>
{
clrFace1Entity,
imgFace1Entity
}
}
};
IEnumerable<Turn> expected = new Turn[]
{
Turn.CreateWithSpecifiedTime(
datetime1,
new("Aardvark"),
new Dictionary<Die, Face>()
{
{(numDieEntity as NumberDieEntity).ToModel(), (numFace2Entity as NumberFaceEntity).ToModel() },
{(clrDieEntity as ColorDieEntity).ToModel(), (clrFace2Entity as ColorFaceEntity).ToModel() },
}),
Turn.CreateWithSpecifiedTime(
datetime2,
new("Chloe"),
new Dictionary<Die, Face>()
{
{(clrDieEntity as ColorDieEntity).ToModel(), (clrFace1Entity as ColorFaceEntity).ToModel() },
{(imgDieEntity as ImageDieEntity).ToModel(), (imgFace1Entity as ImageFaceEntity).ToModel() }
})
}.AsEnumerable();
// Act
IEnumerable<Turn> actual = entities.ToModels();
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestToEntity()
{
// Arrange
Turn model = Turn.CreateWithSpecifiedTime(
datetime1,
playerEntity.ToModel(),
new Dictionary<Die, Face>()
{
{(numDieEntity as NumberDieEntity).ToModel(), (numFace2Entity as NumberFaceEntity).ToModel() },
{(clrDieEntity as ColorDieEntity).ToModel(), (clrFace2Entity as ColorFaceEntity).ToModel() },
{(imgDieEntity as ImageDieEntity).ToModel(), (imgFace1Entity as ImageFaceEntity).ToModel() }
});
TurnEntity expected = new()
{
When = datetime1,
PlayerEntity = playerEntity,
Dice = new List<DieEntity>
{
numDieEntity,
clrDieEntity,
imgDieEntity
},
Faces = new List<FaceEntity>
{
numFace2Entity,
clrFace2Entity,
imgFace1Entity
}
};
// Act
TurnEntity actual = model.ToEntity();
// Assert
Assert.True(expected.Equals(actual));
}
[Fact]
public void TestToEntities()
{
// Arrange
Turn[] models = new Turn[]
{
Turn.CreateWithSpecifiedTime(
datetime2,
new("Mimi"),
new Dictionary<Die, Face>()
{
{(numDieEntity as NumberDieEntity).ToModel(), (numFace2Entity as NumberFaceEntity).ToModel() },
{(clrDieEntity as ColorDieEntity).ToModel(), (clrFace2Entity as ColorFaceEntity).ToModel() },
}),
Turn.CreateWithSpecifiedTime(
datetime1,
new("blaaargh"),
new Dictionary<Die, Face>()
{
{(clrDieEntity as ColorDieEntity).ToModel(), (clrFace1Entity as ColorFaceEntity).ToModel() },
{(imgDieEntity as ImageDieEntity).ToModel(), (imgFace1Entity as ImageFaceEntity).ToModel() }
})
};
IEnumerable<TurnEntity> expected = new TurnEntity[]
{
new TurnEntity()
{
When = datetime2,
PlayerEntity = new PlayerEntity() {Name = "Mimi"},
Dice = new List<DieEntity>
{
numDieEntity,
clrDieEntity
},
Faces = new List<FaceEntity>
{
numFace2Entity,
clrFace2Entity
}
},
new TurnEntity()
{
When = datetime1,
PlayerEntity = new PlayerEntity() {Name = "blaaargh"},
Dice = new List<DieEntity>
{
clrDieEntity,
imgDieEntity
},
Faces = new List<FaceEntity>
{
clrFace1Entity,
imgFace1Entity
}
}
}.AsEnumerable();
// Act
IEnumerable<TurnEntity> actual = models.ToEntities();
// Assert
Assert.Equal(expected, actual);
}
}
}

@ -1,688 +0,0 @@
using Data.EF;
using Data.EF.Players;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace Tests.Data_UTs.Players
{
public class PlayerDbManagerTest
{
private readonly SqliteConnection connection = new("DataSource=:memory:");
private readonly DbContextOptions<DiceAppDbContext> options;
public PlayerDbManagerTest()
{
connection.Open();
options = new DbContextOptionsBuilder<DiceAppDbContext>()
.UseSqlite(connection)
.EnableSensitiveDataLogging()
.Options;
}
[Fact]
public async Task TestConstructorWhenGivenContextThenConstructs()
{
// Arrange
PlayerDbManager mgr;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
// Assert
Assert.Equal(new Collection<PlayerEntity>(), await mgr.GetAll());
}
}
[Fact]
public void TestConstructorWhenGivenNullThrowsException()
{
// Arrange
PlayerDbManager mgr;
// Act
void action() => mgr = new PlayerDbManager(null);
// Assert
Assert.Throws<ArgumentNullException>(action);
}
[Fact]
public async Task TestAddWhenValidThenValid()
{
// Arrange
string expectedName = "Jeff";
int expectedCount = 2;
PlayerDbManager mgr;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(new PlayerEntity() { Name = expectedName });
await mgr.Add(new PlayerEntity() { Name = "whatever" });
// mgr takes care of the SaveChange() calls internally
// we might use Units of Work later, to optimize our calls to DB
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
Assert.Equal(expectedName, (await mgr.GetOneByName(expectedName)).Name);
Assert.Equal(expectedCount, (await mgr.GetAll()).Count);
}
}
[Fact]
public async Task TestAddWhenPreExistentThenException()
{
// Arrange
string name = "Flynt";
PlayerDbManager mgr;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(new PlayerEntity() { Name = name });
async Task actionAsync() => await mgr.Add(new PlayerEntity() { Name = name });
// Assert
await Assert.ThrowsAsync<ArgumentException>(actionAsync);
}
}
[Fact]
public void TestAddWhenNullThenException()
{
// Arrange
PlayerDbManager mgr;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
async Task actionAsync() => await mgr.Add(null);
// Assert
Assert.ThrowsAsync<ArgumentNullException>(actionAsync);
}
}
[Theory]
[InlineData(" ")]
[InlineData(null)]
[InlineData("")]
public void TestAddWhenInvalidNameThenException(string name)
{
// Arrange
PlayerDbManager mgr;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
async Task actionAsync() => await mgr.Add(new PlayerEntity() { Name = name });
// Assert
Assert.ThrowsAsync<ArgumentException>(actionAsync);
}
}
[Theory]
[InlineData(" ")]
[InlineData(null)]
[InlineData("")]
public async Task TestGetOneByNameWhenInvalidThenException(string name)
{
// Arrange
PlayerDbManager mgr;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(new() { Name = "Ernesto" });
await mgr.Add(new() { Name = "Basil" });
async Task actionAsync() => await mgr.GetOneByName(name);
// Assert
await Assert.ThrowsAsync<ArgumentException>(actionAsync);
}
}
[Theory]
[InlineData("Caroline")]
[InlineData("Caroline ")]
[InlineData(" Caroline")]
public async Task TestGetOneByNameWhenValidAndExistsThenGetsIt(string name)
{
// Arrange
PlayerDbManager mgr;
PlayerEntity actual;
PlayerEntity expected = new() { Name = name.Trim() };
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(expected);
await mgr.Add(new() { Name = "Philip" });
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
actual = await mgr.GetOneByName(name);
Assert.Equal(expected, actual);
}
}
[Fact]
public async Task TestGetOneByNameWhenValidAndNotExistsThenException()
{
// Arrange
PlayerDbManager mgr;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
//mgr.Add(expected);
await mgr.Add(new() { Name = "Brett" });
await mgr.Add(new() { Name = "Noah" });
async Task actionAsync() => await mgr.GetOneByName("*r^a*éàru é^à");
// Assert
await Assert.ThrowsAsync<InvalidOperationException>(actionAsync);
}
}
[Fact]
public async Task TestIsPresentByNameWhenValidAndExistsThenTrue()
{
// Arrange
PlayerDbManager mgr;
string name = "Gerald";
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(new() { Name = "Philip" });
await mgr.Add(new() { Name = name });
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
Assert.True(await mgr.IsPresentByName(name));
}
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData(null)]
[InlineData("nowaythatthisnameisalreadyinourdatabase")]
public async Task TestIsPresentByNameWhenInvalidOrNonExistentThenFalse(string name)
{
// Arrange
PlayerDbManager mgr;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(new() { Name = "Herman" });
await mgr.Add(new() { Name = "Paulo" });
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
Assert.False(await mgr.IsPresentByName(name));
}
}
[Fact]
public void TestRemoveWhenNullThenException()
{
// Arrange
PlayerDbManager mgr;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
void action() => mgr.Remove(null);
// Assert
Assert.Throws<ArgumentNullException>(action);
}
}
[Fact]
public async Task TestRemoveWhenPreExistentThenRemoves()
{
// Arrange
PlayerDbManager mgr;
PlayerEntity toRemove = new() { ID = Guid.NewGuid(), Name = "Please!" };
// Act
using (DiceAppDbContextWithStub db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(toRemove); // calls SaveChangesAsync()
mgr.Remove(toRemove);
}
// Assert
using (DiceAppDbContextWithStub db = new(options))
{
db.Database.EnsureCreated();
Assert.DoesNotContain(toRemove, db.PlayerEntity);
}
}
[Fact]
public async Task TestRemoveWhenNonExistentThenStillNonExistent()
{
// Arrange
PlayerDbManager mgr;
PlayerEntity toRemove = new() { ID = Guid.NewGuid(), Name = "Filibert" };
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(new() { ID = Guid.NewGuid(), Name = "Bert" });
mgr.Remove(toRemove);
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
Assert.DoesNotContain(toRemove, await mgr.GetAll());
}
}
[Theory]
[InlineData("filiBert")]
[InlineData("Bertrand")]
public async Task TestUpdateWhenValidThenUpdates(string name)
{
// Arrange
PlayerDbManager mgr;
Guid idBefore = Guid.NewGuid();
PlayerEntity before = new() { ID = idBefore, Name = "Filibert" };
PlayerEntity after = new() { ID = idBefore, Name = name };
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(before);
await mgr.Update(before, after);
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
Assert.DoesNotContain(before, await mgr.GetAll());
Assert.Contains(after, await mgr.GetAll());
}
}
[Theory]
[InlineData("Valerie")]
[InlineData("Valerie ")]
[InlineData(" Valerie")]
public async Task TestUpdateWhenSameThenKeepsAndWorks(string name)
{
// Arrange
PlayerDbManager mgr;
string nameBefore = "Valerie";
Guid idBefore = Guid.NewGuid();
PlayerEntity before = new() { ID = idBefore, Name = nameBefore };
PlayerEntity after = new() { ID = idBefore, Name = name };
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(before);
await mgr.Update(before, after);
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
Assert.Contains(before, await mgr.GetAll());
Assert.Contains(after, await mgr.GetAll());
}
}
[Fact]
public async Task TestUpdateWhenNewIDThenException()
{
// Arrange
PlayerDbManager mgr;
PlayerEntity before = new() { ID = Guid.NewGuid(), Name = "Nova" };
PlayerEntity after = new() { ID = Guid.NewGuid(), Name = "Jacquie" };
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(before);
async Task actionAsync() => await mgr.Update(before, after);
// Assert
await Assert.ThrowsAsync<ArgumentException>(actionAsync);
}
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData(null)]
public async Task TestUpdateWhenInvalidThenException(string name)
{
// Arrange
PlayerDbManager mgr;
Guid id = Guid.NewGuid();
PlayerEntity before = new() { ID = id, Name = "Llanfair­pwll­gwyn­gyll­go­gery­chwyrn­drobwll­llan­tysilio­gogo­goch" };
PlayerEntity after = new() { ID = id, Name = name };
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(before);
async Task actionAsync() => await mgr.Update(before, after);
// Assert
await Assert.ThrowsAsync<ArgumentException>(actionAsync);
}
}
[Fact]
public async Task TestUpdateWhenNullThenException()
{
// Arrange
PlayerDbManager mgr;
PlayerEntity before = new() { ID = Guid.NewGuid(), Name = "Dwight" };
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(before);
async Task actionAsync() => await mgr.Update(before, null);
// Assert
await Assert.ThrowsAsync<ArgumentNullException>(actionAsync);
}
}
[Fact]
public async Task TestGetOneByIDWhenExistsThenGetsIt()
{
// Arrange
PlayerDbManager mgr;
Guid id = Guid.NewGuid();
PlayerEntity actual;
PlayerEntity expected = new() { ID = id, Name = "Hugh" };
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(expected);
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
actual = await mgr.GetOneByID(id);
Assert.Equal(expected, actual);
}
}
[Fact]
public async Task TestGetOneByIDWhenNotExistsThenExceptionAsync()
{
// Arrange
PlayerDbManager mgr;
Guid id = Guid.NewGuid();
PlayerEntity expected = new() { ID = id, Name = "Kyle" };
Guid otherId = Guid.NewGuid();
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(expected);
async Task actionAsync() => await mgr.GetOneByID(otherId);
// Assert
await Assert.ThrowsAsync<InvalidOperationException>(actionAsync);
}
}
[Fact]
public async Task TestIsPresentbyIdWhenExistsThenTrue()
{
// Arrange
PlayerDbManager mgr;
Guid id = Guid.NewGuid();
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
await mgr.Add(new() { ID = id, Name = "Bobby" });
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
Assert.True(await mgr.IsPresentByID(id));
}
}
[Fact]
public async Task TestIsPresentbyIdWhenNotExistsThenFalse()
{
// Arrange
PlayerDbManager mgr;
Guid id = Guid.NewGuid();
Guid otherId;
PlayerEntity presentEntity;
// Act
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
presentEntity = new() { ID = id, Name = "Victor" };
await mgr.Add(presentEntity);
otherId = Guid.NewGuid();
// not added
}
// Assert
using (DiceAppDbContext db = new(options))
{
db.Database.EnsureCreated();
mgr = new(db);
Assert.False(await mgr.IsPresentByID(otherId));
}
}
}
}

@ -1,193 +0,0 @@
using Data.EF.Players;
using System;
using Xunit;
namespace Tests.Data_UTs.Players
{
public class PlayerEntityTest
{
[Fact]
public void TestGetSetName()
{
// Arrange
PlayerEntity player = new();
string expected = "Alice";
// Act
player.Name = expected;
string actual = player.Name;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestGetSetID()
{
// Arrange
PlayerEntity player = new();
Guid expected = Guid.NewGuid();
// Act
player.ID = expected;
Guid actual = player.ID;
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestToString()
{
// Arrange
PlayerEntity player = new();
string IDString = "c8f60957-dd36-4e47-a7ce-1281f4f8bea4";
string nameString = "Bob";
player.ID = new Guid(IDString);
player.Name = nameString;
// Act
string expected = $"{IDString.ToUpper()} -- {nameString}";
// Assert
Assert.Equal(expected, player.ToString());
}
[Fact]
public void TestEqualsWhenNotPlayerEntityThenFalse()
{
// Arrange
Point point;
PlayerEntity entity;
// Act
point = new(1, 2);
entity = new() { Name = "Clyde" };
// Assert
Assert.False(point.Equals(entity));
Assert.False(entity.Equals(point));
}
[Fact]
public void TestEqualsWhenNullThenFalse()
{
// Arrange
PlayerEntity entity;
// Act
entity = new() { Name = "Clyde" };
// Assert
Assert.False(entity.Equals(null));
}
[Fact]
public void TestGoesThruToSecondMethodIfObjIsTypePlayerEntity()
{
// Arrange
object p1;
PlayerEntity p2;
// Act
p1 = new PlayerEntity() { Name = "Marvin" };
p2 = new() { Name = "Clyde" };
// Assert
Assert.False(p1.Equals(p2));
Assert.False(p2.Equals(p1));
}
[Fact]
public void TestEqualsFalseIfNotSameNameOrID()
{
// Arrange
PlayerEntity p1;
PlayerEntity p2;
PlayerEntity p3;
Guid id1 = Guid.NewGuid();
Guid id2 = Guid.NewGuid();
string name1 = "Panama";
string name2 = "Clyde";
// Act
p1 = new() { ID = id1, Name = name1 };
p2 = new() { ID = id1, Name = name2 };
p3 = new() { ID = id2, Name = name2 };
// Assert
Assert.False(p1.Equals(p2));
Assert.False(p1.Equals(p3));
Assert.False(p2.Equals(p1));
Assert.False(p2.Equals(p3));
Assert.False(p3.Equals(p1));
Assert.False(p3.Equals(p2));
}
[Fact]
public void TestEqualsTrueIfSameIDAndName()
{
// Arrange
PlayerEntity p1;
PlayerEntity p2;
Guid id = Guid.NewGuid();
string name = "Marley";
// Act
p1 = new() { ID = id, Name = name };
p2 = new() { ID = id, Name = name };
// Assert
Assert.True(p1.Equals(p2));
Assert.True(p2.Equals(p1));
}
[Fact]
public void TestSameHashFalseIfNotSameNameOrID()
{
// Arrange
PlayerEntity p1;
PlayerEntity p2;
PlayerEntity p3;
Guid id1 = Guid.NewGuid();
Guid id2 = Guid.NewGuid();
string name1 = "Panama";
string name2 = "Clyde";
// Act
p1 = new() { ID = id1, Name = name1 };
p2 = new() { ID = id1, Name = name2 };
p3 = new() { ID = id2, Name = name2 };
// Assert
Assert.False(p1.GetHashCode().Equals(p2.GetHashCode()));
Assert.False(p1.GetHashCode().Equals(p3.GetHashCode()));
Assert.False(p2.GetHashCode().Equals(p1.GetHashCode()));
Assert.False(p2.GetHashCode().Equals(p3.GetHashCode()));
Assert.False(p3.GetHashCode().Equals(p1.GetHashCode()));
Assert.False(p3.GetHashCode().Equals(p2.GetHashCode()));
}
[Fact]
public void TestSameHashTrueIfSame()
{
// Arrange
PlayerEntity p1;
PlayerEntity p2;
Guid id = Guid.NewGuid();
string name = "Marley";
// Act
p1 = new() { ID = id, Name = name };
p2 = new() { ID = id, Name = name };
// Assert
Assert.True(p1.GetHashCode().Equals(p2.GetHashCode()));
Assert.True(p2.GetHashCode().Equals(p1.GetHashCode()));
}
}
}

@ -1,90 +0,0 @@
using Data.EF.Players;
using Model.Players;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace Tests.Data_UTs.Players
{
public class PlayerExtensionsTest
{
[Fact]
public void TestToModel()
{
// Arrange
string name = "Alice";
PlayerEntity entity = new() { Name = name };
Player expected = new(name);
// Act
Player actual = entity.ToModel();
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestToEntity()
{
// Arrange
string name = "Bob";
Player model = new(name);
PlayerEntity expected = new() { Name = name };
// Act
PlayerEntity actual = model.ToEntity();
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestToModels()
{
// Arrange
string n1 = "Alice", n2 = "Bob", n3 = "Clyde";
PlayerEntity[] entities = new PlayerEntity[] {
new() {Name = n1 },
new() {Name = n2 },
new() {Name = n3 },
};
IEnumerable<Player> expected = new Player[] {
new(n1),
new(n2),
new(n3)
}.AsEnumerable();
// Act
IEnumerable<Player> actual = entities.ToModels();
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void TestToEntities()
{
// Arrange
string n1 = "Alice", n2 = "Bob", n3 = "Clyde";
Player[] models = new Player[] {
new(n1),
new(n2),
new(n3)
};
IEnumerable<PlayerEntity> expected = new PlayerEntity[] {
new() {Name = n1 },
new() {Name = n2 },
new() {Name = n3 },
}.AsEnumerable();
// Act
IEnumerable<PlayerEntity> actual = models.ToEntities();
// Assert
Assert.Equal(expected, actual);
}
}
}

@ -0,0 +1,61 @@
using Model.Dice.Faces;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Tests.Model_UTs
{
public class ColorDieFaceTest
{
[Fact]
public void TestCreatColorFace()
{
//Arrange
ColorDieFace face;
int expected = 11;
//Act
face = new ColorDieFace(expected);
int actuel = (int)face.GetPracticalValue();
//Assert
Assert.Equal(expected, actuel);
}
[Fact]
public void TestGetPracticalValueColorFace()
{
//Arrange
ColorDieFace face;
int expected = 11;
//Act
face = new ColorDieFace(expected);
int actuel = (int)face.GetPracticalValue();
//Assert
Assert.Equal(expected, actuel);
}
[Fact]
public void TestColorFaceToString()
{
//Arrange
ColorDieFace face;
string expected = "ff00bb";
//Act
face = new ColorDieFace(expected);
int val = (int)face.GetPracticalValue();
string actuel = val.ToString("X6").Insert(0, "#");
//Assert
Assert.Equal(expected.ToString(), actuel);
}
}
}

@ -1,51 +0,0 @@
using Model.Dice.Faces;
using Model.Dice;
using System.Collections.Generic;
using System.Drawing;
using Xunit;
namespace Tests.Model_UTs.Dice
{
public class ColorDieTest
{
public static IEnumerable<object[]> Data_Uri()
{
yield return new object[]
{
new ColorFace(Color.FromName("Chocolate")),
new ColorFace(Color.FromName("Aqua")),
new ColorFace(Color.FromName("Beige")),
new ColorFace(Color.FromName("Black")),
new ColorFace(Color.FromName("BurlyWood")),
};
}
[Theory]
[MemberData(nameof(Data_Uri))]
public void RndmFaceTest(ColorFace f1, ColorFace f2, ColorFace f3, ColorFace f4, ColorFace f5)
{
//Arrange
List<ColorFace> listFaces = new() {
f1,f2,f3,f4,f5
};
ColorDie die = new(
listFaces[1],
listFaces[2],
listFaces[3],
listFaces[4]
);
//Act
ColorFace actual = (ColorFace)die.GetRandomFace();
//Assert
Assert.Contains(listFaces, face => face == actual);
}
}
}

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tests.Model_UTs.Dice
{
public class DiceGroupManagerTest
{
}
}

@ -1,70 +0,0 @@
using Model.Dice.Faces;
using System.Collections.Generic;
using System.Drawing;
using Xunit;
namespace Tests.Model_UTs.Dice.Faces
{
public class ColorFaceTest
{
public static IEnumerable<object[]> Data_Colors()
{
yield return new object[]
{
Color.FromName("Chocolate"),
Color.FromArgb(144, 255, 78, 240),
};
yield return new object[]
{
Color.FromName("Chocolate"),
Color.FromArgb(144, 255, 78, 240),
};
}
[Theory]
[MemberData(nameof(Data_Colors))]
public void ColorFaceValueTest(Color clrA, Color clrB)
{
//Arrage
ColorFace face1 = new(clrA);
ColorFace face2 = new(clrB);
//Act
Color expected1 = clrA;
Color actual1 = face1.Value;
Color expected2 = clrB;
Color actual2 = face2.Value;
//Assert
Assert.Equal(expected1, actual1);
Assert.Equal(expected2, actual2);
}
[Theory]
[MemberData(nameof(Data_Colors))]
public void ColorFaceValueToStringTest(Color clrA, Color clrB)
{
//Arrage
ColorFace face1 = new(clrA);
ColorFace face2 = new(clrB);
//Act
string expected1 = clrA.ToString();
string actual1 = face1.StringValue;
string expected2 = clrB.ToString();
string actual2 = face2.StringValue;
//Assert
Assert.Equal(expected1, actual1);
Assert.Equal(expected2, actual2);
}
}
}

@ -1,70 +0,0 @@
using Model.Dice.Faces;
using System;
using System.Collections.Generic;
using Xunit;
namespace Tests.Model_UTs.Dice.Faces
{
public class ImageFaceTest
{
public static IEnumerable<object[]> Data_Colors()
{
yield return new object[]
{
new Uri("http://www.contoso.com/"),
new Uri("https://www.pedagojeux.fr/wp-content/uploads/2019/11/1280x720_LoL.jpg"),
};
yield return new object[]
{
new Uri("https://www.lacremedugaming.fr/wp-content/uploads/creme-gaming/2022/02/media-13411.jpg"),
new Uri("https://i1.moyens.net/io/images/2022/01/1642321015_Mises-a-jour-de-VALORANT-en-melee-a-venir-dans.jpg"),
};
}
[Theory]
[MemberData(nameof(Data_Colors))]
public void ImageFaceValueTest(Uri uriA, Uri uriB)
{
//Arrage
ImageFace face1 = new(uriA);
ImageFace face2 = new(uriB);
//Act
Uri expected1 = uriA;
Uri actual1 = face1.Value;
Uri expected2 = uriB;
Uri actual2 = face2.Value;
//Assert
Assert.Equal(expected1, actual1);
Assert.Equal(expected2, actual2);
}
[Theory]
[MemberData(nameof(Data_Colors))]
public void ImageFaceValueToStringTest(Uri uriA, Uri uriB)
{
//Arrage
ImageFace face1 = new(uriA);
ImageFace face2 = new(uriB);
//Act
string expected1 = uriA.ToString();
string actual1 = face1.StringValue;
string expected2 = uriB.ToString();
string actual2 = face2.StringValue;
//Assert
Assert.Equal(expected1, actual1);
Assert.Equal(expected2, actual2);
}
}
}

@ -1,51 +0,0 @@
using Model.Dice.Faces;
using Xunit;
namespace Tests.Model_UTs.Dice.Faces
{
public class NumberFaceTest
{
[Fact]
public void NumberFaceValueTest()
{
//Arrage
NumberFace face1 = new NumberFace(3);
NumberFace face2 = new NumberFace(5);
//Act
int expected1 = 3;
int actual1 = face1.Value;
int expected2 = 5;
int actual2 = face2.Value;
//Assert
Assert.Equal(expected1, actual1);
Assert.Equal(expected2, actual2);
}
[Fact]
public void NumberFaceValueToStringTest()
{
//Arrage
NumberFace face1 = new(3);
NumberFace face2 = new(5);
//Act
string expected1 = 3.ToString();
string actual1 = face1.StringValue;
string expected2 = 5.ToString();
string actual2 = face2.StringValue;
//Assert
Assert.Equal(expected1, actual1);
Assert.Equal(expected2, actual2);
}
}
}

@ -1,53 +0,0 @@
using Model.Dice.Faces;
using Model.Dice;
using System;
using System.Collections.Generic;
using Xunit;
namespace Tests.Model_UTs.Dice
{
public class ImageDieTest
{
public static IEnumerable<object[]> Data_Uri()
{
yield return new object[]
{
new ImageFace(new Uri("https://nothing1/")),
new ImageFace(new Uri("https://nothing2/")),
new ImageFace(new Uri("https://nothing3/")),
new ImageFace(new Uri("https://nothing4/")),
new ImageFace(new Uri("https://nothing5/")),
};
}
[Theory]
[MemberData(nameof(Data_Uri))]
public void RndmFaceTest(ImageFace f1, ImageFace f2, ImageFace f3, ImageFace f4, ImageFace f5)
{
//Arrange
List<ImageFace> listFaces = new() {
f1,f2,f3,f4,f5
};
ImageDie die = new(
listFaces[1],
listFaces[2],
listFaces[3],
listFaces[4]
);
//Act
ImageFace actual = (ImageFace)die.GetRandomFace();
//Assert
Assert.Contains(listFaces, face => face == actual);
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save