diff --git a/GameData/Repository/ExpenseRepository.cs b/GameData/Repository/ExpenseRepository.cs index 87df232..287d043 100644 --- a/GameData/Repository/ExpenseRepository.cs +++ b/GameData/Repository/ExpenseRepository.cs @@ -13,29 +13,42 @@ namespace GameData.Repository { public class ExpenseRepository : IExpenseRepository { - readonly ILogger _log; - private ApiClient _client; + readonly ILogger? _log; + private ApiClient _apiClient; string UrlExpense => "items/expense"; string UrlPlayerExpense => "items/playerexpense"; List _memberExpenses = new List(); + private List _expenses = null; + public ExpenseRepository(ILogger log, ApiClient apiClient) { _log = log; - _client = apiClient; + _apiClient = apiClient; _log?.LogDebug("creating ExpenseRepository"); } public async Task> GetAll() { - return await _client.Get(UrlExpense); + if (_expenses == null) + { + _log?.LogDebug("loading expenses from api"); + var res = await _apiClient.Get(UrlExpense); + _expenses = new List { }; + _expenses.AddRange(res); + } + else + { + _log?.LogDebug("returning expenses from cache"); + } + return _expenses; } public async Task Save(PlayerExpense memberExpense) { - return await _client.Post(memberExpense, UrlPlayerExpense); + return await _apiClient.Post(memberExpense, UrlPlayerExpense); } public async Task Delete(IEnumerable 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); } } } diff --git a/GameData/Repository/GameRepository.cs b/GameData/Repository/GameRepository.cs index d089381..f3f443b 100644 --- a/GameData/Repository/GameRepository.cs +++ b/GameData/Repository/GameRepository.cs @@ -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,UrlGameState); + var res = await _client.Post(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, UrlGame); + return _client.Put(game, UrlGame); } - public async Task Create(Game game) + public Task Create(Game game) { - return await _client.Post(game, UrlGame); + return _client.Post(game, UrlGame); } - public async Task LoadGame(Guid gameId) + public Task LoadGame(Guid gameId) { - return await _client.GetSingle(UrlGame + "/" + gameId); + return _client.GetSingle(UrlGame + "/" + gameId); } public async Task Create(string gameName) diff --git a/GameHandler.UnitTests/ExpenseHandlerTests.cs b/GameHandler.UnitTests/ExpenseHandlerTests.cs index 9b06e4a..c90f1d9 100644 --- a/GameHandler.UnitTests/ExpenseHandlerTests.cs +++ b/GameHandler.UnitTests/ExpenseHandlerTests.cs @@ -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] diff --git a/GameHandler.UnitTests/Extensions/GameServiceExtensionTests.cs b/GameHandler.UnitTests/Extensions/GameServiceExtensionTests.cs index 519d924..66ee463 100644 --- a/GameHandler.UnitTests/Extensions/GameServiceExtensionTests.cs +++ b/GameHandler.UnitTests/Extensions/GameServiceExtensionTests.cs @@ -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()); } } diff --git a/GameHandler.UnitTests/GameHandler/DeathGameHandlerTests.cs b/GameHandler.UnitTests/GameHandler/DeathGameHandlerTests.cs index b8d45f5..350348a 100644 --- a/GameHandler.UnitTests/GameHandler/DeathGameHandlerTests.cs +++ b/GameHandler.UnitTests/GameHandler/DeathGameHandlerTests.cs @@ -76,7 +76,7 @@ namespace GameHandler.UnitTests.DeathGame var gm = _gh.InitGameModel(_players, _settings); int failedCoffinPlayerId = 0; ExpenseTrigger[] failed = Array.Empty(); - _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(); - _gh.GameExpenseOccured += (sender, arg) => + _gh.GameExpenseOccured += async (sender, arg) => { failed = ((GameExenseEventArgs)arg).Triggers; failedCoffinPlayerId = ((GameExenseEventArgs)arg).PlayerId; diff --git a/GameHandler.UnitTests/Mocks/FakeExpenseRepository.cs b/GameHandler.UnitTests/Mocks/FakeExpenseRepository.cs index a576e0a..72a61a6 100644 --- a/GameHandler.UnitTests/Mocks/FakeExpenseRepository.cs +++ b/GameHandler.UnitTests/Mocks/FakeExpenseRepository.cs @@ -10,16 +10,21 @@ namespace GameHandler.UnitTests.Mocks { public class FakeExpenseRepository : IExpenseRepository { - - - public IEnumerable GetAll() + public Task Delete(IEnumerable enumerable) { - return IExpenseRepository.TestData; + throw new NotImplementedException(); } public Task Save(PlayerExpense data) { return Task.FromResult(data); } + + public Task> GetAll() + { + IEnumerable res = IExpenseRepository.TestData; + return Task.FromResult(res); + } + } } diff --git a/GameHandler.UnitTests/Mocks/FakeGameRepository.cs b/GameHandler.UnitTests/Mocks/FakeGameRepository.cs index a48c552..df96911 100644 --- a/GameHandler.UnitTests/Mocks/FakeGameRepository.cs +++ b/GameHandler.UnitTests/Mocks/FakeGameRepository.cs @@ -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 Create(Game game) { - return Task.FromResult(Game.Create()); + return Task.FromResult(Game.Create(FreeGameHandler.GAMENAME_FREETRAINING)); + } + + public Task Create(string gameName) + { + return Task.FromResult(Game.Create(gameName)); + } + + public Task Delete(IEnumerable gameStates) + { + throw new NotImplementedException(); } public GameState Load(Guid gameId) @@ -20,6 +31,16 @@ namespace GameHandler.UnitTests.Mocks throw new NotImplementedException(); } + public Task LoadGame(Guid gameId) + { + throw new NotImplementedException(); + } + + public Task> LoadStates(Guid gameId) + { + throw new NotImplementedException(); + } + public Task Save(GameState gameState) { return Task.FromResult(gameState); diff --git a/GameHandler/GameHandler.csproj b/GameHandler/GameHandler.csproj index 8d810f8..dafde05 100644 --- a/GameHandler/GameHandler.csproj +++ b/GameHandler/GameHandler.csproj @@ -7,6 +7,7 @@ + diff --git a/GameHandler/GameHandler/DeathGameHandler.cs b/GameHandler/GameHandler/DeathGameHandler.cs index 133b7cf..644024f 100644 --- a/GameHandler/GameHandler/DeathGameHandler.cs +++ b/GameHandler/GameHandler/DeathGameHandler.cs @@ -21,7 +21,7 @@ namespace GameHandler.DeathGame public const string GAMENAME_DEATHBOX = "Totenkiste"; public event CoffinCompleted CoffinCompleted; - public event EventHandler GameExpenseOccured; + public event AsyncEventHandler? 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); + } } } } diff --git a/GameHandler/GameHandler/FreeGameHandler.cs b/GameHandler/GameHandler/FreeGameHandler.cs index 16c9ec0..5c9699e 100644 --- a/GameHandler/GameHandler/FreeGameHandler.cs +++ b/GameHandler/GameHandler/FreeGameHandler.cs @@ -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? GameExpenseOccured; + public static string GameName() { diff --git a/GameHandler/GameService.cs b/GameHandler/GameService.cs index bbc632c..f7f8758 100644 --- a/GameHandler/GameService.cs +++ b/GameHandler/GameService.cs @@ -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 Start(string gameName = FreeGameHandler.GAMENAME_FREETRAINING) + public Task Start(string gameName = FreeGameHandler.GAMENAME_FREETRAINING) { - return await Start(defaultPlayerIds, new DeathGameSettings(6), gameName); + return Start(defaultPlayerIds, new DeathGameSettings(6), gameName); } public async Task 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()); _gameStateHandler = new GameStateHandler(_scope.Resolve(), _scope.Resolve()); } - 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 }; } diff --git a/GameModel.UnitTests/PinPictureTests.cs b/GameModel.UnitTests/PinPictureTests.cs index b71a2fc..e01dc5f 100644 --- a/GameModel.UnitTests/PinPictureTests.cs +++ b/GameModel.UnitTests/PinPictureTests.cs @@ -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()); + Assert.That(() => p.Index(pinNumber),Throws.TypeOf()); } //[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().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().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] diff --git a/GameModel/Contracts/IGameHandler.cs b/GameModel/Contracts/IGameHandler.cs index 47fe1b0..fef63b3 100644 --- a/GameModel/Contracts/IGameHandler.cs +++ b/GameModel/Contracts/IGameHandler.cs @@ -8,6 +8,8 @@ using System.Threading.Tasks; namespace GameModel.Contract { + public delegate Task AsyncEventHandler(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? GameExpenseOccured; } } diff --git a/GameModel/PinPicture.cs b/GameModel/PinPicture.cs index 4a582ba..1f4fd10 100644 --- a/GameModel/PinPicture.cs +++ b/GameModel/PinPicture.cs @@ -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 Take() + { + return Take(9); + } + + public IEnumerable Take(int count) + { + if (count <= 1 || count > 9) + { + throw new InvalidPinIndexException(); + } + var res = new List + { + PinState1, + PinState2, + PinState3, + PinState4, + PinState5, + PinState6, + PinState7, + PinState8, + PinState9 + }; + + return res.Take(count); + } + + /// + /// + /// + /// one-based number of a pin + /// + public PinState Index(int index) + { + if (index < 1 || index > 9) + { + throw new InvalidPinIndexException(); + } + return Take(9).ToArray()[index-1]; + } } public record PinPictureEnum : PinPicture, IEnumerable, IEnumerable diff --git a/KoogleCli/KoogleCli.csproj b/KoogleCli/KoogleCli.csproj index 57f0999..79f2981 100644 --- a/KoogleCli/KoogleCli.csproj +++ b/KoogleCli/KoogleCli.csproj @@ -18,6 +18,7 @@ + diff --git a/KoogleCli/Program.cs b/KoogleCli/Program.cs index 8d170bf..0d696fc 100644 --- a/KoogleCli/Program.cs +++ b/KoogleCli/Program.cs @@ -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()