This commit is contained in:
Christian Kauer 2023-12-31 13:21:40 +01:00
parent a69a8f450d
commit c451a67b8c
16 changed files with 159 additions and 64 deletions

View File

@ -13,29 +13,42 @@ namespace GameData.Repository
{
public class ExpenseRepository : IExpenseRepository
{
readonly ILogger<ExpenseRepository> _log;
private ApiClient _client;
readonly ILogger<ExpenseRepository>? _log;
private ApiClient _apiClient;
string UrlExpense => "items/expense";
string UrlPlayerExpense => "items/playerexpense";
List<PlayerExpense> _memberExpenses = new List<PlayerExpense>();
private List<Expense> _expenses = null;
public ExpenseRepository(ILogger<ExpenseRepository> log, ApiClient apiClient)
{
_log = log;
_client = apiClient;
_apiClient = apiClient;
_log?.LogDebug("creating ExpenseRepository");
}
public async Task<IEnumerable<Expense>> GetAll()
{
return await _client.Get<Expense>(UrlExpense);
if (_expenses == null)
{
_log?.LogDebug("loading expenses from api");
var res = await _apiClient.Get<Expense>(UrlExpense);
_expenses = new List<Expense> { };
_expenses.AddRange(res);
}
else
{
_log?.LogDebug("returning expenses from cache");
}
return _expenses;
}
public async Task<PlayerExpense> Save(PlayerExpense memberExpense)
{
return await _client.Post<PlayerExpense>(memberExpense, UrlPlayerExpense);
return await _apiClient.Post<PlayerExpense>(memberExpense, UrlPlayerExpense);
}
public async Task Delete(IEnumerable<PlayerExpense> items)
@ -43,7 +56,7 @@ namespace GameData.Repository
dynamic keys = new JObject();
keys.keys = new JArray(items.Select(_ => _.Id));
await _client.Delete(keys, UrlPlayerExpense);
await _apiClient.Delete(keys, UrlPlayerExpense);
}
}
}

View File

@ -55,11 +55,11 @@ namespace GameData.Repository
TypeNameHandling = TypeNameHandling.Auto
});
var gameStateDo = new GameStateDo(gameState.Id, gameState.GameId, gameState.GameName, str, gameState.Counter);
var res = await _client.Post<GameStateDo>(gameStateDo,UrlGameState);
var res = await _client.Post<GameStateDo>(gameStateDo,UrlGameState).ConfigureAwait(false);
if (res == null)
{
var msg = "error saviong gamestate - save rsult cannot be null - repo not online, or game deleted";
var msg = "error saving gamestate - save rsult cannot be null - repo not online, or game deleted";
_log.LogError(msg);
throw new InvalidOperationException(msg);
}
@ -72,19 +72,19 @@ namespace GameData.Repository
return obj;
}
public async Task Update(Game game)
public Task Update(Game game)
{
await _client.Put<Game>(game, UrlGame);
return _client.Put<Game>(game, UrlGame);
}
public async Task<Game> Create(Game game)
public Task<Game> Create(Game game)
{
return await _client.Post<Game>(game, UrlGame);
return _client.Post<Game>(game, UrlGame);
}
public async Task<Game> LoadGame(Guid gameId)
public Task<Game> LoadGame(Guid gameId)
{
return await _client.GetSingle<Game>(UrlGame + "/" + gameId);
return _client.GetSingle<Game>(UrlGame + "/" + gameId);
}
public async Task<Game> Create(string gameName)

View File

@ -26,9 +26,9 @@ namespace GameHandler.UnitTests
public void CheckThrow_SinkCausesMultipleSinkExpsenses()
{
var bs = BoardState.Create();
var model = _eh.CheckThrow(bs, PinThrow.Create(1, PinPicture.Create(), false, true), _player);
var expense1 = model.MemberExpenses.First();
var expense2 = model.MemberExpenses.Skip(1).First();
var model = _eh.CheckThrow(bs, PinThrow.Create(1, PinPicture.Create(), false, true), _player).Result;
var expense1 = model.First();
var expense2 = model.Skip(1).First();
Assert.That(expense1.Name, Is.EqualTo("Gosse"));
Assert.That(expense2.Name, Is.EqualTo("Gosse2"));
}
@ -37,20 +37,20 @@ namespace GameHandler.UnitTests
public void CheckThrow_NinePinsCausesExpenseForAllOthers()
{
var bs = BoardState.Create();
var model = _eh.CheckThrow(bs, PinThrow.Create(1, PinPicture.CreateAllPins(), false, false), _player);
Assert.That(model.MemberExpenses.Count, Is.EqualTo(_player.Count()-1));
var model = _eh.CheckThrow(bs, PinThrow.Create(1, PinPicture.CreateAllPins(), false, false), _player).Result;
Assert.That(model.Count, Is.EqualTo(_player.Count()-1));
}
[Test]
public void CheckThrow_NoWoodAndBellCausesSinkAndBellExpsense()
{
var bs = BoardState.Create();
var model = _eh.CheckThrow(bs, PinThrow.Create(1, true, true), _player);
var expense1 = model.MemberExpenses.First();
var expense2 = model.MemberExpenses.Skip(2).First(); // there are two triggers for sink in testdata
var model = _eh.CheckThrow(bs, PinThrow.Create(1, true, true), _player).Result;
var expense1 = model.First();
var expense2 = model.Skip(2).First(); // there are two triggers for sink in testdata
Assert.That(expense1.Name, Is.EqualTo("Gosse"));
Assert.That(expense2.Name, Is.EqualTo("Klingel"));
Assert.That(model.MemberExpenses.Count, Is.EqualTo(3));
Assert.That(model.Count, Is.EqualTo(3));
}
[Test]

View File

@ -14,7 +14,7 @@ namespace GameHandler.UnitTests.Extensions
[Test]
public void GetGameHandler_ThrowsExceptionWhenGamenameIsNotValid()
{
var gs = new GameService();
var gs = new GameService(null);
Assert.That(() => gs.GetGameHandler("unknown"), Throws.TypeOf<InvaildGameNameException>());
}
}

View File

@ -76,7 +76,7 @@ namespace GameHandler.UnitTests.DeathGame
var gm = _gh.InitGameModel(_players, _settings);
int failedCoffinPlayerId = 0;
ExpenseTrigger[] failed = Array.Empty<ExpenseTrigger>();
_gh.GameExpenseOccured += (sender, arg) =>
_gh.GameExpenseOccured += async (sender, arg) =>
{
failed = ((GameExenseEventArgs)arg).Triggers;
failedCoffinPlayerId = ((GameExenseEventArgs)arg).PlayerId;
@ -92,7 +92,7 @@ namespace GameHandler.UnitTests.DeathGame
var gm = _gh.InitGameModel(_players, _settings);
int failedCoffinPlayerId = 0;
ExpenseTrigger[] failed = Array.Empty<ExpenseTrigger>();
_gh.GameExpenseOccured += (sender, arg) =>
_gh.GameExpenseOccured += async (sender, arg) =>
{
failed = ((GameExenseEventArgs)arg).Triggers;
failedCoffinPlayerId = ((GameExenseEventArgs)arg).PlayerId;

View File

@ -10,16 +10,21 @@ namespace GameHandler.UnitTests.Mocks
{
public class FakeExpenseRepository : IExpenseRepository
{
public IEnumerable<Expense> GetAll()
public Task Delete(IEnumerable<PlayerExpense> enumerable)
{
return IExpenseRepository.TestData;
throw new NotImplementedException();
}
public Task<PlayerExpense> Save(PlayerExpense data)
{
return Task.FromResult(data);
}
public Task<IEnumerable<Expense>> GetAll()
{
IEnumerable<Expense> res = IExpenseRepository.TestData;
return Task.FromResult(res);
}
}
}

View File

@ -1,4 +1,5 @@
using GameModel;
using GameHandler.GameHandler;
using GameModel;
using GameModel.Contracts;
using System;
using System.Collections.Generic;
@ -12,7 +13,17 @@ namespace GameHandler.UnitTests.Mocks
{
public Task<Game> Create(Game game)
{
return Task.FromResult(Game.Create());
return Task.FromResult(Game.Create(FreeGameHandler.GAMENAME_FREETRAINING));
}
public Task<Game> Create(string gameName)
{
return Task.FromResult(Game.Create(gameName));
}
public Task Delete(IEnumerable<GameState> gameStates)
{
throw new NotImplementedException();
}
public GameState Load(Guid gameId)
@ -20,6 +31,16 @@ namespace GameHandler.UnitTests.Mocks
throw new NotImplementedException();
}
public Task<Game> LoadGame(Guid gameId)
{
throw new NotImplementedException();
}
public Task<IEnumerable<GameState>> LoadStates(Guid gameId)
{
throw new NotImplementedException();
}
public Task<GameState> Save(GameState gameState)
{
return Task.FromResult(gameState);

View File

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AsyncAwaitBestPractices" Version="7.0.0" />
<PackageReference Include="Autofac" Version="7.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

View File

@ -21,7 +21,7 @@ namespace GameHandler.DeathGame
public const string GAMENAME_DEATHBOX = "Totenkiste";
public event CoffinCompleted CoffinCompleted;
public event EventHandler GameExpenseOccured;
public event AsyncEventHandler<EventArgs>? GameExpenseOccured;
private static Random random = new Random();
@ -50,6 +50,7 @@ namespace GameHandler.DeathGame
{
}
public IGameModel InitGameModel(int[] playerIds, IGameSettings gameSettings)
{
return InitGameModel(playerIds, gameSettings as DeathGameSettings);
@ -217,9 +218,12 @@ namespace GameHandler.DeathGame
public int ThrowsPerRount() => IGameHandler.INFINIT_THROWS;
protected virtual void OnGameExpenseOccured(GameExenseEventArgs e)
protected async virtual Task OnGameExpenseOccured(GameExenseEventArgs e)
{
GameExpenseOccured?.Invoke(this, e);
if (GameExpenseOccured is not null)
{
await GameExpenseOccured(this, e);
}
}
}
}

View File

@ -13,8 +13,9 @@ namespace GameHandler.GameHandler
public class FreeGameHandler : IGameHandler
{
public const string GAMENAME_FREETRAINING = "Freies Spiel";
public event EventHandler GameExpenseOccured;
public event AsyncEventHandler<EventArgs>? GameExpenseOccured;
public static string GameName()
{

View File

@ -16,25 +16,26 @@ using System.Data;
using Autofac.Core;
using Autofac;
using static System.Formats.Asn1.AsnWriter;
using AsyncAwaitBestPractices;
namespace GameHandler
{
public class GameService
{
private readonly IContainer? _rootContainer;
private readonly IContainer _rootContainer;
private bool _isStarted = false;
private ILifetimeScope? _scope;
private IGameHandler? _gh;
private ThrowHandler? _th;
private ExpenseHandler? _eh;
private GameStateHandler? _gameStateHandler;
private ILifetimeScope _scope;
private IGameHandler _gh;
private ThrowHandler _th;
private ExpenseHandler _eh;
private GameStateHandler _gameStateHandler;
private Game? _game;
public GameState? GameModel { get => _gameStateHandler?.GameState; }
public bool FreePlayerSelection => _gh != null && _gh.FreePlayerSelection();
public GameService(IContainer? rootContainer = null)
public GameService(IContainer rootContainer)
{
this._rootContainer = rootContainer;
}
@ -53,9 +54,9 @@ namespace GameHandler
static int[] defaultPlayerIds => new[] {1,2,3,4};
public async Task<GameState> Start(string gameName = FreeGameHandler.GAMENAME_FREETRAINING)
public Task<GameState> Start(string gameName = FreeGameHandler.GAMENAME_FREETRAINING)
{
return await Start(defaultPlayerIds, new DeathGameSettings(6), gameName);
return Start(defaultPlayerIds, new DeathGameSettings(6), gameName);
}
public async Task<GameState> Start(int[] playerIds, IGameSettings gameSettings, string gameName = FreeGameHandler.GAMENAME_FREETRAINING)
@ -92,15 +93,15 @@ namespace GameHandler
{
_scope = _rootContainer?.BeginLifetimeScope();
_gh = this.GetGameHandler(gameName);
_gh.GameExpenseOccured += _gh_GameExpenseOccured;
_gh.GameExpenseOccured += _gh_GameExpenseOccured1; ;
_th = new ThrowHandler();
_eh = new ExpenseHandler(_scope.Resolve<IExpenseRepository>());
_gameStateHandler = new GameStateHandler(_scope.Resolve<IGameRepository>(), _scope.Resolve<IExpenseRepository>());
}
private void _gh_GameExpenseOccured(object? sender, EventArgs e)
private async Task _gh_GameExpenseOccured1(object? sender, EventArgs e)
{
var items = _eh.HandleGameExenseEventArgs(e as GameExenseEventArgs).Result;
var items = await _eh.HandleGameExenseEventArgs(e as GameExenseEventArgs);
_gameStateHandler.Add(items);
//_lastState = _lastState with { ExpenseModel = expenseModel };
}

View File

@ -24,7 +24,7 @@ namespace GameModel.UnitTests
public void PinPicture_IndexTest(int pinNumber, PinState pinState)
{
var p = PinPicture.Create(pinNumber, pinState);
Assert.That(p[pinNumber],Is.EqualTo(PinState.Down));
Assert.That(p.Index(pinNumber),Is.EqualTo(PinState.Down));
}
[Test]
@ -35,7 +35,7 @@ namespace GameModel.UnitTests
public void Get_Invalid_PinNumber_ThrowsException(int pinNumber)
{
var p = PinPicture.Create();
Assert.That(() => p[pinNumber],Throws.TypeOf<InvalidPinIndexException>());
Assert.That(() => p.Index(pinNumber),Throws.TypeOf<InvalidPinIndexException>());
}
//[Test]
@ -79,10 +79,10 @@ namespace GameModel.UnitTests
{
var p = PinPicture.Create();
var i = 0;
foreach (var item in p)
foreach (var item in p.Take())
{
i++;
Assert.That(item, Is.EqualTo(p[i]));
Assert.That(item, Is.EqualTo(p.Index(i)));
}
}
@ -92,21 +92,21 @@ namespace GameModel.UnitTests
{
var p = new PinPicture(PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down);
Assert.That(p.First, Is.EqualTo(PinState.Down));
Assert.That(p.Skip(1).First, Is.EqualTo(PinState.Up));
Assert.That(p.PinState2, Is.EqualTo(PinState.Up));
Assert.That(p.Last, Is.EqualTo(PinState.Down));
var items = p.Take(9).ToArray();
CollectionAssert.AreEqual(items, new[] { PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down });
var enumerator = p.GetEnumerator();
enumerator.MoveNext();
enumerator.MoveNext();
Assert.That(enumerator.Current, Is.EqualTo(PinState.Up));
//var enumerator = p.GetEnumerator();
//enumerator.MoveNext();
//enumerator.MoveNext();
//Assert.That(enumerator.Current, Is.EqualTo(PinState.Up));
// cover GetEnumerator method https://stackoverflow.com/questions/1510031/how-do-you-test-the-ienumerable-getenumerator-method
IEnumerable weak = p.AsWeakEnumerable();
var sequence = weak.Cast<PinState>().Take(9).ToArray();
CollectionAssert.AreEqual(sequence, new[] { PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down });
//// cover GetEnumerator method https://stackoverflow.com/questions/1510031/how-do-you-test-the-ienumerable-getenumerator-method
//IEnumerable weak = p.AsWeakEnumerable();
//var sequence = weak.Cast<PinState>().Take(9).ToArray();
//CollectionAssert.AreEqual(sequence, new[] { PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down, PinState.Up, PinState.Down });
}
[Test]

View File

@ -8,6 +8,8 @@ using System.Threading.Tasks;
namespace GameModel.Contract
{
public delegate Task AsyncEventHandler<TEventArgs>(object? sender, TEventArgs e);
public interface IGameHandler
{
public const int INFINIT_THROWS = 9999;
@ -25,7 +27,7 @@ namespace GameModel.Contract
IGameModel Update(PinThrow pinThrow, IGameModel gameModel, BoardState boardStateBeforeUpdate,
ThrowState throwStateAfterUpdate);
event EventHandler GameExpenseOccured;
event AsyncEventHandler<EventArgs>? GameExpenseOccured;
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Numerics;
using System.Reflection;
using System.Reflection.Metadata.Ecma335;
using System.Threading.Tasks;
@ -145,6 +146,51 @@ namespace GameModel
return DownCount == 0;
}
}
public PinState First => PinState1;
public PinState Last => PinState9;
public IEnumerable<PinState> Take()
{
return Take(9);
}
public IEnumerable<PinState> Take(int count)
{
if (count <= 1 || count > 9)
{
throw new InvalidPinIndexException();
}
var res = new List<PinState>
{
PinState1,
PinState2,
PinState3,
PinState4,
PinState5,
PinState6,
PinState7,
PinState8,
PinState9
};
return res.Take(count);
}
/// <summary>
///
/// </summary>
/// <param name="index">one-based number of a pin</param>
/// <returns></returns>
public PinState Index(int index)
{
if (index < 1 || index > 9)
{
throw new InvalidPinIndexException();
}
return Take(9).ToArray()[index-1];
}
}
public record PinPictureEnum : PinPicture, IEnumerable<PinState>, IEnumerable

View File

@ -18,6 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AsyncAwaitBestPractices" Version="7.0.0" />
<PackageReference Include="AutofacSerilogIntegration" Version="5.0.0" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />

View File

@ -88,7 +88,7 @@ async Task ShowMainMenu()
void ContinueGame()
{
StartGameAction("", new Guid("33869054-811f-49cb-940b-be582ee5d8a5"));
StartGameAction("", new Guid("4dd49b5e-7060-467a-8f79-a2e96b1b4c32"));
}
void MasterDataAction()