refactorings
This commit is contained in:
parent
4d584e98af
commit
3f62b3c0e0
|
|
@ -17,6 +17,8 @@ The name for the App is KOOGLE. Koogle is an app for club management.
|
||||||
- Automatic or rule-based cost allocation
|
- Automatic or rule-based cost allocation
|
||||||
- Evaluation per person/day/game
|
- Evaluation per person/day/game
|
||||||
- User-related actions (CurrentUserService)
|
- User-related actions (CurrentUserService)
|
||||||
|
- cash book
|
||||||
|
- plugin system for different game logics
|
||||||
|
|
||||||
### Technical
|
### Technical
|
||||||
- ASP.NET Core backend
|
- ASP.NET Core backend
|
||||||
|
|
@ -29,7 +31,7 @@ The name for the App is KOOGLE. Koogle is an app for club management.
|
||||||
- maybe in future also for darts, soccer betting pools, Leisure groups, Sport clubs, etc.
|
- maybe in future also for darts, soccer betting pools, Leisure groups, Sport clubs, etc.
|
||||||
|
|
||||||
**Phase 1 MVP:** See [docs/IMPLEMENTATION_PLAN.md](./docs/IMPLEMENTATION_PLAN.md)
|
**Phase 1 MVP:** See [docs/IMPLEMENTATION_PLAN.md](./docs/IMPLEMENTATION_PLAN.md)
|
||||||
- 23 granular phases (A1-F3)
|
- granular phases (A1-K23)
|
||||||
- Foundation → Clubs → Users → Persons → Days → Dashboard
|
- Foundation → Clubs → Users → Persons → Days → Dashboard
|
||||||
- Track progress via checkboxes in plan
|
- Track progress via checkboxes in plan
|
||||||
- ~75 files total
|
- ~75 files total
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FoxHuntGameLogicService : IGameLogicService
|
public class FoxHuntGameLogicService : IGameLogicService
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public object CreateInitialModel(Guid[] playerIds, object? setupOptions)
|
public object CreateInitialModel(Guid[] playerIds, object? setupOptions)
|
||||||
{
|
{
|
||||||
var options = ParseSetupOptions(setupOptions);
|
var options = ParseSetupOptions(setupOptions);
|
||||||
|
|
@ -30,13 +31,9 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
NonFoxIndex = 0, // Index für Nicht-Fuchs-Spieler
|
NonFoxIndex = 0, // Index für Nicht-Fuchs-Spieler
|
||||||
FoxTurnsRemaining = options.LeadingThrows - 1, // -1 weil erster Wurf bereits aus PlayerOrder[FoxIndex]
|
FoxTurnsRemaining = options.LeadingThrows - 1, // -1 weil erster Wurf bereits aus PlayerOrder[FoxIndex]
|
||||||
FoxTurn = true, // ein fuchs fängt an
|
FoxTurn = true, // ein fuchs fängt an
|
||||||
|
|
||||||
LeadingThrows = options.LeadingThrows,
|
LeadingThrows = options.LeadingThrows,
|
||||||
PlayerStates = playerStates,
|
PlayerStates = playerStates,
|
||||||
PlayerOrder = playerOrder,
|
PlayerOrder = playerOrder,
|
||||||
//CurrentPlayerIndex = 0,
|
|
||||||
//FoxLeadingThrowCount = 0,
|
|
||||||
//CurrentFoxIndex = 0,
|
|
||||||
WinnerId = null,
|
WinnerId = null,
|
||||||
IsGameOver = false,
|
IsGameOver = false,
|
||||||
};
|
};
|
||||||
|
|
@ -44,6 +41,7 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public (object UpdatedModel, ThrowResult Result) ProcessThrow(object gameModel, AfterThrowState afterThrow)
|
public (object UpdatedModel, ThrowResult Result) ProcessThrow(object gameModel, AfterThrowState afterThrow)
|
||||||
{
|
{
|
||||||
var model = this.CastModel<FoxHuntGameModel>(gameModel);
|
var model = this.CastModel<FoxHuntGameModel>(gameModel);
|
||||||
|
|
@ -61,9 +59,7 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//var actualName = GetPlayerName(playerId).Result;
|
|
||||||
var playerStates = new Dictionary<Guid, FoxHuntPlayerState>(model.PlayerStates);
|
var playerStates = new Dictionary<Guid, FoxHuntPlayerState>(model.PlayerStates);
|
||||||
//var eliminatedPlayers = new List<Guid>(model.EliminatedPlayers);
|
|
||||||
var triggers = new List<TriggerEvent>();
|
var triggers = new List<TriggerEvent>();
|
||||||
|
|
||||||
var foxId = model.PlayerOrder[model.FoxIndex];
|
var foxId = model.PlayerOrder[model.FoxIndex];
|
||||||
|
|
@ -73,19 +69,28 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
if (model.FoxTurn)
|
if (model.FoxTurn)
|
||||||
{
|
{
|
||||||
playerStates[foxId].PinCountFox+= afterThrow.PinsKnocked;
|
playerStates[foxId].PinCountFox+= afterThrow.PinsKnocked;
|
||||||
|
if (afterThrow.IsCircle)
|
||||||
|
{
|
||||||
|
playerStates[foxId].PinCountFox += 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
playerStates[foxId].PinCountHunters+= afterThrow.PinsKnocked;
|
playerStates[foxId].PinCountHunters+= afterThrow.PinsKnocked;
|
||||||
|
if (afterThrow.IsCircle)
|
||||||
|
{
|
||||||
|
playerStates[foxId].PinCountHunters += 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (playerStates[foxId].PinCountHunters >= playerStates[foxId].PinCountFox )
|
if (playerStates[foxId].PinCountHunters >= playerStates[foxId].PinCountFox )
|
||||||
{
|
{
|
||||||
// fox has been caught
|
// fox has been caught
|
||||||
isLastHunter = true;
|
isLastHunter = true; // will cause selection of the next fox
|
||||||
playerStates[foxId].FoxCaught = true;
|
playerStates[foxId].FoxCaught = true;
|
||||||
|
|
||||||
|
|
||||||
// Calculate penalty (point difference the hunters achieved)
|
// Calculate penalty (point difference the hunters achieved)
|
||||||
int pointDifference = playerStates[foxId].PinCountHunters - playerStates[foxId].PinCountFox;
|
var pointDifference = playerStates[foxId].PinCountHunters - playerStates[foxId].PinCountFox;
|
||||||
if (pointDifference > 0)
|
if (pointDifference > 0)
|
||||||
{
|
{
|
||||||
triggers.Add(new TriggerEvent
|
triggers.Add(new TriggerEvent
|
||||||
|
|
@ -109,7 +114,7 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
var nextPlayerId = GetNextId(model, isLastHunter);
|
var nextPlayerId = GetNextId(model, isLastHunter);
|
||||||
|
|
||||||
// Check GAME END
|
// Check GAME END
|
||||||
bool isGameOver = isLastHunter && model.FoxIndex == 0;
|
var isGameOver = isLastHunter && model.FoxIndex == 0;
|
||||||
|
|
||||||
if (isGameOver)
|
if (isGameOver)
|
||||||
{
|
{
|
||||||
|
|
@ -178,7 +183,7 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
return model.PlayerOrder[model.FoxIndex];
|
return model.PlayerOrder[model.FoxIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1️⃣ Fuchs ist 2x hintereinander dran
|
// Fuchs ist 2x hintereinander dran
|
||||||
if (model.FoxTurnsRemaining > 0)
|
if (model.FoxTurnsRemaining > 0)
|
||||||
{
|
{
|
||||||
model.FoxTurnsRemaining--;
|
model.FoxTurnsRemaining--;
|
||||||
|
|
@ -188,7 +193,7 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
|
|
||||||
model.FoxTurn = !model.FoxTurn; // danach abwechselnd
|
model.FoxTurn = !model.FoxTurn; // danach abwechselnd
|
||||||
|
|
||||||
// 2️⃣ Abwechselnd Nicht-Fuchs → Fuchs
|
// Abwechselnd Nicht-Fuchs → Fuchs
|
||||||
if (!model.FoxTurn)
|
if (!model.FoxTurn)
|
||||||
{
|
{
|
||||||
var nextNonFoxIndex = model.GetNextIndex(model.PlayerOrder, model.NonFoxIndex);
|
var nextNonFoxIndex = model.GetNextIndex(model.PlayerOrder, model.NonFoxIndex);
|
||||||
|
|
@ -199,56 +204,26 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
}
|
}
|
||||||
model.NonFoxIndex = nextNonFoxIndex;
|
model.NonFoxIndex = nextNonFoxIndex;
|
||||||
return model.PlayerOrder[nextNonFoxIndex];
|
return model.PlayerOrder[nextNonFoxIndex];
|
||||||
|
|
||||||
// nächsten Nicht-Fuchs suchen
|
|
||||||
//while (model.NonFoxIndex == model.FoxIndex)
|
|
||||||
// model.NonFoxIndex++;
|
|
||||||
|
|
||||||
//if (model.NonFoxIndex < model.PlayerOrder.Length)
|
|
||||||
//{
|
|
||||||
// Guid next = model.PlayerOrder[model.NonFoxIndex];
|
|
||||||
// model.NonFoxIndex++;
|
|
||||||
// //model.FoxTurn = true;
|
|
||||||
// return next;
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// // Alle Nicht-Füchse durch → neuer Fuchs
|
|
||||||
// model.FoxIndex++;
|
|
||||||
// model.NonFoxIndex = 0;
|
|
||||||
// model.FoxTurnsRemaining = model.LeadingThrows - 1;
|
|
||||||
// model.FoxTurn = true;
|
|
||||||
// return GetNextId(model);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fuchs-Zug im Wechsel
|
||||||
|
|
||||||
|
|
||||||
// 3️⃣ Fuchs-Zug im Wechsel
|
|
||||||
//model.FoxTurn = false;
|
|
||||||
return foxId;
|
return foxId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetNextActivePlayerIndex(
|
/// <inheritdoc />
|
||||||
Guid[] playerOrder,
|
|
||||||
int currentIndex,
|
|
||||||
Dictionary<Guid, FoxHuntPlayerState> playerStates)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (bool IsGameOver, Guid? WinnerId) CheckGameEnd(object gameModel)
|
public (bool IsGameOver, Guid? WinnerId) CheckGameEnd(object gameModel)
|
||||||
{
|
{
|
||||||
var model = this.CastModel<FoxHuntGameModel>(gameModel);
|
var model = this.CastModel<FoxHuntGameModel>(gameModel);
|
||||||
return (model.IsGameOver, model.WinnerId);
|
return (model.IsGameOver, model.WinnerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public IReadOnlyDictionary<Guid, PlayerStatsSummary> GetPlayerStats(object gameModel)
|
public IReadOnlyDictionary<Guid, PlayerStatsSummary> GetPlayerStats(object gameModel)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public GameSetupValidationResult ValidateSetup(object? setupOptions)
|
public GameSetupValidationResult ValidateSetup(object? setupOptions)
|
||||||
{
|
{
|
||||||
if (setupOptions is null)
|
if (setupOptions is null)
|
||||||
|
|
@ -261,7 +236,7 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
|
|
||||||
if (options.LeadingThrows < 2 || options.LeadingThrows > 4)
|
if (options.LeadingThrows < 2 || options.LeadingThrows > 4)
|
||||||
{
|
{
|
||||||
errors.Add("Vorsprung muss zwischen 6 und 12 liegen.");
|
errors.Add("Vorsprung muss zwischen 2 und 4 liegen.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: validate player-count, at lease 2 players needed
|
//TODO: validate player-count, at lease 2 players needed
|
||||||
|
|
@ -271,12 +246,14 @@ namespace Koogle.Application.Games.FoxHunt
|
||||||
: GameSetupValidationResult.Valid();
|
: GameSetupValidationResult.Valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<GameActionDescriptor> GetAvailableActions(object gameModel, Guid currentPlayerId)
|
public IReadOnlyList<GameActionDescriptor> GetAvailableActions(object gameModel, Guid currentPlayerId)
|
||||||
{
|
{
|
||||||
// FoxHunt has no custom actions
|
// FoxHunt has no custom actions
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public GameActionResult ExecuteAction(object gameModel, string actionId, Guid currentPlayerId,
|
public GameActionResult ExecuteAction(object gameModel, string actionId, Guid currentPlayerId,
|
||||||
IReadOnlyDictionary<string, object>? parameters = null)
|
IReadOnlyDictionary<string, object>? parameters = null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
namespace Koogle.Application.Games.FoxHunt
|
namespace Koogle.Application.Games.FoxHunt
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Game model for Fuchsjagd-Spiel (Fox Hunt) game type.
|
/// Game model for Fox Hunt (Fuchsjagd-Spiel) game type.
|
||||||
/// One Player starts as fox, and all others try to hunt him.
|
/// One Player starts as fox, and all others try to hunt him.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record FoxHuntGameModel : IGameModel
|
public record FoxHuntGameModel : IGameModel
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// State for each player (details if fox survived or not).
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<Guid, FoxHuntPlayerState> PlayerStates { get; init; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
//public int LeadingThrows { get; set; }
|
/// Fixed player order (default order at game start).
|
||||||
//public int FoxLeadingThrowCount { get; set; }
|
/// </summary>
|
||||||
public Dictionary<Guid, FoxHuntPlayerState> PlayerStates { get; set; }
|
public Guid[] PlayerOrder { get; set; } = [];
|
||||||
|
|
||||||
public Guid[] PlayerOrder { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// count of throw the fox can start with
|
/// count of throw the fox can start with
|
||||||
|
|
@ -20,15 +22,25 @@
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int LeadingThrows { get; set; }
|
public int LeadingThrows { get; set; }
|
||||||
|
|
||||||
//public int CurrentPlayerIndex { get; set; }
|
/// <summary>
|
||||||
//public int CurrentFoxIndex { get; set; }
|
/// index pointing to the current fox (Index in PlayerOrder)
|
||||||
|
/// </summary>
|
||||||
|
public int FoxIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// index pointing to the current hunter (Index in PlayerOrder)
|
||||||
|
/// </summary>
|
||||||
|
public int NonFoxIndex { get; set; }
|
||||||
|
|
||||||
public int FoxIndex { get; set; } // aktueller Fuchs (Index in PlayerOrder)
|
/// <summary>
|
||||||
public int NonFoxIndex { get; set; } // Index für Nicht-Fuchs-Spieler
|
/// remaining leading throws a fox has left
|
||||||
public int FoxTurnsRemaining { get; set; } // verbleibende Fuchs-Züge
|
/// </summary>
|
||||||
public bool FoxTurn { get; set; } // true = Fuchs dran im Wechsel
|
public int FoxTurnsRemaining { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// true = a throw by fox, false = a throw by a hunter
|
||||||
|
/// </summary>
|
||||||
|
public bool FoxTurn { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Guid? WinnerId { get; set; }
|
public Guid? WinnerId { get; set; }
|
||||||
|
|
@ -37,12 +49,34 @@
|
||||||
public bool IsGameOver { get; set; }
|
public bool IsGameOver { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Info about the last throw for UI display.
|
||||||
|
/// </summary>
|
||||||
public record FoxHuntPlayerState
|
public record FoxHuntPlayerState
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// count of pins the fox has knocked.
|
||||||
|
/// </summary>
|
||||||
public int PinCountFox { get; set; }
|
public int PinCountFox { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// count of pins the hunters have knocked.
|
||||||
|
/// </summary>
|
||||||
public int PinCountHunters { get; set; }
|
public int PinCountHunters { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Weather the hunters have caught the fox.
|
||||||
|
/// </summary>
|
||||||
public bool FoxCaught { get; set; }
|
public bool FoxCaught { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Weather the fox survived with most point difference.
|
||||||
|
/// </summary>
|
||||||
public bool IsWinner { get; set; }
|
public bool IsWinner { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Weather the fox could escape the hunters.
|
||||||
|
/// </summary>
|
||||||
public bool FoxEscaped { get; set; }
|
public bool FoxEscaped { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,19 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Koogle.Application.Games
|
namespace Koogle.Application.Games
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper class for GameLogicServices
|
||||||
|
/// </summary>
|
||||||
public static class GameLogicServiceExtension
|
public static class GameLogicServiceExtension
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// casts an object to game model type
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of the game model</typeparam>
|
||||||
|
/// <param name="logicService"></param>
|
||||||
|
/// <param name="gameModel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="InvalidOperationException"></exception>
|
||||||
public static T CastModel<T>(this IGameLogicService logicService, object gameModel) where T : IGameModel
|
public static T CastModel<T>(this IGameLogicService logicService, object gameModel) where T : IGameModel
|
||||||
{
|
{
|
||||||
if (gameModel is T model)
|
if (gameModel is T model)
|
||||||
|
|
@ -29,8 +40,18 @@ namespace Koogle.Application.Games
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper methods for GameModel
|
||||||
|
/// </summary>
|
||||||
public static class GameModelExtensions
|
public static class GameModelExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get the previous Guid in array, considering loop of players.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameModel"></param>
|
||||||
|
/// <param name="guids"></param>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static Guid GetPrev(this IGameModel gameModel, Guid[] guids, int index)
|
public static Guid GetPrev(this IGameModel gameModel, Guid[] guids, int index)
|
||||||
{
|
{
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
|
|
@ -40,6 +61,13 @@ namespace Koogle.Application.Games
|
||||||
return guids[index - 1];
|
return guids[index - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next Guid in array, considering loop of players.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameModel"></param>
|
||||||
|
/// <param name="guids"></param>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static Guid GetNext(this IGameModel gameModel, Guid[] guids, int index)
|
public static Guid GetNext(this IGameModel gameModel, Guid[] guids, int index)
|
||||||
{
|
{
|
||||||
if (index == guids.Length - 1)
|
if (index == guids.Length - 1)
|
||||||
|
|
@ -49,6 +77,13 @@ namespace Koogle.Application.Games
|
||||||
return guids[index + 1];
|
return guids[index + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next index in array, considering loop of players.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameModel"></param>
|
||||||
|
/// <param name="guids"></param>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static int GetNextIndex(this IGameModel gameModel,Guid[] guids, int index)
|
public static int GetNextIndex(this IGameModel gameModel,Guid[] guids, int index)
|
||||||
{
|
{
|
||||||
if (index == guids.Length - 1)
|
if (index == guids.Length - 1)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue