diff --git a/GameData/ApiClient.cs b/GameData/ApiClient.cs index 472f30e..bdb1c0a 100644 --- a/GameData/ApiClient.cs +++ b/GameData/ApiClient.cs @@ -1,5 +1,6 @@ using GameData.Repository; using GameModel.Settings; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -20,12 +21,13 @@ namespace GameData } private readonly HttpClient client; - + private readonly ILogger _logger; private readonly AppSettings _appSettings; private readonly string Api; - public ApiClient(AppSettings appSettings) + public ApiClient(AppSettings appSettings, ILogger logger) { + _logger = logger; _appSettings = appSettings; client = new HttpClient @@ -121,12 +123,12 @@ namespace GameData else { var errorResult = await response.Content.ReadAsStringAsync(); - //_logger.LogError("error downloading from service '{service}' with code '{statuscode}' - Result: '{result}', TargetFile: '{payload}'", serviceUrl, response.StatusCode, errorResult, targetFile); + _logger.LogError("error downloading from service '{service}' with code '{statuscode}' - Result: '{result}', TargetFile: '{payload}'", serviceUrl, response.StatusCode, errorResult, targetFile); } } catch (Exception ex) { - //_logger.LogError(ex, "error at DownloadFile"); + _logger.LogError(ex, "error at DownloadFile"); } return res; @@ -157,12 +159,12 @@ namespace GameData else { var errorResult = await response.Content.ReadAsStringAsync(); - //_logger.LogError("error posting to service '{service}' with code '{statuscode}' - Result: '{result}', Payload: '{payload}'", serviceUrl, response.StatusCode, errorResult, JsonConvert.SerializeObject(payload)); + _logger.LogError("error posting to service '{service}' with code '{statuscode}' - Result: '{result}', Payload: '{payload}'", serviceUrl, response.StatusCode, errorResult, JsonConvert.SerializeObject(payload)); } } catch (Exception ex) { - //_logger.LogError(ex, "error at post"); + _logger.LogError(ex, "error at post"); if (!suppressExceptions) { throw ex; @@ -197,12 +199,12 @@ namespace GameData else { var errorResult = await response.Content.ReadAsStringAsync(); - //_logger.LogError("error deleting to service '{service}' with code '{statuscode}' - Result: '{result}', Payload: '{payload}'", serviceUrl, response.StatusCode, errorResult, JsonConvert.SerializeObject(payload)); + _logger.LogError("error deleting to service '{service}' with code '{statuscode}' - Result: '{result}', Payload: '{payload}'", serviceUrl, response.StatusCode, errorResult, JsonConvert.SerializeObject(payload)); } } catch (Exception ex) { - //_logger.LogError(ex, "error at Delete"); + _logger.LogError(ex, "error at Delete"); if (!suppressExceptions) { throw ex; @@ -212,13 +214,13 @@ namespace GameData return res; } - public T[] Get(string serviceUrl, bool suppressExceptions = true) + public T[] Get(string serviceUrl, bool suppressExceptions = false) { Wrapper res = default; try { - //_logger.LogDebug($"calling service url {serviceUrl}"); + _logger.LogDebug($"calling service url {serviceUrl}"); var response = client.Send(GetHttpRequestMessage(HttpMethod.Get, serviceUrl)); if (response.IsSuccessStatusCode) @@ -238,7 +240,6 @@ namespace GameData using (var jsonTextReader = new JsonTextReader(streamReader)) { var serializer = new JsonSerializer(); - //serializer. res = serializer.Deserialize>(jsonTextReader); } } @@ -248,12 +249,12 @@ namespace GameData else { var errorResult = response.Content.ReadAsStringAsync(); - //_logger.LogError("error getting from service '{service}' with code '{statuscode}' - Result: '{result}'", serviceUrl, response.StatusCode, errorResult); + _logger.LogError("error getting from service '{service}' with code '{statuscode}' - Result: '{result}'", serviceUrl, response.StatusCode, errorResult); } } catch (Exception ex) { - //_logger.LogError(ex, "error at Get"); + _logger.LogError(ex, "error at Get"); if (!suppressExceptions) { throw ex; diff --git a/GameHandler.UnitTests/Extensions/GameServiceExtensionTests.cs b/GameHandler.UnitTests/Extensions/GameServiceExtensionTests.cs new file mode 100644 index 0000000..519d924 --- /dev/null +++ b/GameHandler.UnitTests/Extensions/GameServiceExtensionTests.cs @@ -0,0 +1,21 @@ +using GameHandler.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GameHandler.Extensions; + +namespace GameHandler.UnitTests.Extensions +{ + [TestFixture] + internal class GameServiceExtensionTests + { + [Test] + public void GetGameHandler_ThrowsExceptionWhenGamenameIsNotValid() + { + var gs = new GameService(); + Assert.That(() => gs.GetGameHandler("unknown"), Throws.TypeOf()); + } + } +} diff --git a/GameHandler/ExpenseHandler.cs b/GameHandler/ExpenseHandler.cs index 383e6cc..c0a1467 100644 --- a/GameHandler/ExpenseHandler.cs +++ b/GameHandler/ExpenseHandler.cs @@ -23,20 +23,44 @@ namespace GameHandler _expenseRepository = expenseRepository; } - //private ExpenseModel _expenseModel = new ExpenseModel(Array.Empty()); + public ExpenseModel HandleGameExenseEventArgs(GameExenseEventArgs gameExenseEventArgs, ExpenseModel expenseModelToAppend) + { + if (!gameExenseEventArgs.PlayerIds.Any(_ => _ == gameExenseEventArgs.PlayerId)) + { + throw new InvalidPinThrowException($"Player {gameExenseEventArgs.PlayerId} not found"); + } + + List memberExpenses = PrepareResult(ref expenseModelToAppend); + + var destExpenses = _expenseRepository.GetAll().Where(_ => _.ExpenseTriggers.Any(et => gameExenseEventArgs.Triggers.Any(et2 => et2 == et))); + + var inversePlayers = gameExenseEventArgs.PlayerIds.Where(_ => _ != gameExenseEventArgs.PlayerId); + foreach (var expense in destExpenses) + { + if (expense.IsInverse) + { + foreach (var player in inversePlayers) + { + memberExpenses.Add(MemberExpense.Create(player, expense)); + } + } + else + { + memberExpenses.Add(MemberExpense.Create(gameExenseEventArgs.PlayerId, expense)); + } + } + + return expenseModelToAppend with { MemberExpenses = memberExpenses.ToArray() }; + } public ExpenseModel CheckThrow(BoardState currentState, PinThrow pinThrow, int[] PlayerIds, ExpenseModel expenseModelToAppend = null) { if (!PlayerIds.Any(_ => _ == pinThrow.PlayerId)) { - throw new InvalidPinThrowException($"Player {pinThrow.PlayerId} not found"); + throw new InvalidPinThrowException($"Player {pinThrow.PlayerId} not found"); } - if (expenseModelToAppend == null) - { - expenseModelToAppend = new ExpenseModel(Array.Empty()); - } - var memberExpenses = new List(expenseModelToAppend.MemberExpenses); + List memberExpenses = PrepareResult(ref expenseModelToAppend); var triggers = pinThrow.GetExpenseTriggers(currentState); var destExpenses = _expenseRepository.GetAll().Where(_ => _.ExpenseTriggers.Any(et => triggers.Any(et2 => et2 == et))); @@ -56,8 +80,18 @@ namespace GameHandler memberExpenses.Add(MemberExpense.Create(pinThrow.PlayerId, expense)); } } - + return expenseModelToAppend with { MemberExpenses = memberExpenses.ToArray() }; } + + private static List PrepareResult(ref ExpenseModel expenseModelToAppend) + { + if (expenseModelToAppend == null) + { + expenseModelToAppend = new ExpenseModel(Array.Empty()); + } + var memberExpenses = new List(expenseModelToAppend.MemberExpenses); + return memberExpenses; + } } } diff --git a/GameHandler/GameExenseEventArgs.cs b/GameHandler/GameExenseEventArgs.cs new file mode 100644 index 0000000..5e98c91 --- /dev/null +++ b/GameHandler/GameExenseEventArgs.cs @@ -0,0 +1,24 @@ +using GameModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameHandler +{ + public class GameExenseEventArgs : EventArgs + { + public int[] PlayerIds { get; set; } + public int PlayerId { get; set; } + + public ExpenseTrigger[] Triggers { get; set; } + + public GameExenseEventArgs(int playerId, ExpenseTrigger[] triggers, int[] playerIds) + { + PlayerId = playerId; + Triggers = triggers; + PlayerIds = playerIds; + } + } +} diff --git a/GameHandler/GameHandler/DeathGameHandler.cs b/GameHandler/GameHandler/DeathGameHandler.cs index c39e4f8..e731a1a 100644 --- a/GameHandler/GameHandler/DeathGameHandler.cs +++ b/GameHandler/GameHandler/DeathGameHandler.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; namespace GameHandler.DeathGame { public delegate void CoffinCompleted(Coffin coffin); - public delegate void FirstThrowFailed(Coffin coffin); public class DeathGameHandler : IGameHandler { @@ -22,7 +21,7 @@ namespace GameHandler.DeathGame public const string GAMENAME_DEATHBOX = "Totenkiste"; public event CoffinCompleted CoffinCompleted; - public event FirstThrowFailed FirstThrowFailed; + public event EventHandler GameExpenseOccured; private static Random random = new Random(); @@ -80,7 +79,7 @@ namespace GameHandler.DeathGame return CalcNextModel(gameModel as DeathGameModel, pinThrow.PinCount, isTrowIntoAllPins, boardCleared); } - public DeathGameModel CalcNextModel(DeathGameModel gm, int pinCount, bool isTrowIntoAllPins, bool boardCleared) + private DeathGameModel CalcNextModel(DeathGameModel gm, int pinCount, bool isTrowIntoAllPins, bool boardCleared) { var current = gm.Coffins.First(); var next = gm.Coffins.Skip(1).First(); @@ -93,7 +92,7 @@ namespace GameHandler.DeathGame incX = true; if (pinCount < 3) { - FirstThrowFailed?.Invoke(current); + OnFirstThrowFailed(gm, current); incLine = true; } } @@ -192,6 +191,11 @@ namespace GameHandler.DeathGame return result; } + private void OnFirstThrowFailed(DeathGameModel gm, Coffin current) + { + OnGameExpenseOccured(new GameExenseEventArgs(current.PlayerId, new[] { ExpenseTrigger.FirstThrowFail }, gm.PlayerIds)); + } + public static string GameName() { return GAMENAME_DEATHBOX; @@ -209,6 +213,9 @@ namespace GameHandler.DeathGame public int ThrowsPerRount() => IGameHandler.INFINIT_THROWS; - + protected virtual void OnGameExpenseOccured(GameExenseEventArgs e) + { + GameExpenseOccured?.Invoke(this, e); + } } } diff --git a/GameHandler/GameHandler/FreeGameHandler.cs b/GameHandler/GameHandler/FreeGameHandler.cs index f8bfadb..cac9551 100644 --- a/GameHandler/GameHandler/FreeGameHandler.cs +++ b/GameHandler/GameHandler/FreeGameHandler.cs @@ -13,6 +13,9 @@ namespace GameHandler.GameHandler public class FreeGameHandler : IGameHandler { public const string GAMENAME_FREETRAINING = "Freies Spiel"; + + public event EventHandler GameExpenseOccured; + public static string GameName() { return GAMENAME_FREETRAINING; diff --git a/GameHandler/GameService.cs b/GameHandler/GameService.cs index b629a30..525287e 100644 --- a/GameHandler/GameService.cs +++ b/GameHandler/GameService.cs @@ -66,6 +66,7 @@ namespace GameHandler _scope = _rootContainer?.BeginLifetimeScope(); _gh = this.GetGameHandler(gameName); + _gh.GameExpenseOccured += _gh_GameExpenseOccured; var gm = _gh.InitGameModel(playerIds, gameSettings); _th = new ThrowHandler(); @@ -78,6 +79,12 @@ namespace GameHandler } + private void _gh_GameExpenseOccured(object? sender, EventArgs e) + { + var expenseModel = _eh.HandleGameExenseEventArgs(e as GameExenseEventArgs, _lastState.ExpenseModel); + _lastState = _lastState with { ExpenseModel = expenseModel }; + } + public GameState HandleThrow(PinThrow pinThrow) { if (!_isStarted) diff --git a/GameModel/Contracts/IGameHandler.cs b/GameModel/Contracts/IGameHandler.cs index 3d2a9b1..6f93bf7 100644 --- a/GameModel/Contracts/IGameHandler.cs +++ b/GameModel/Contracts/IGameHandler.cs @@ -23,6 +23,8 @@ namespace GameModel.Contract int GetCurrentPlayerId(IGameModel gameModel); IGameModel Update(PinThrow pinThrow, IGameModel gameModel, BoardState boardStateBeforeUpdate); + + event EventHandler GameExpenseOccured; } }