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
|
||||
- Evaluation per person/day/game
|
||||
- User-related actions (CurrentUserService)
|
||||
- cash book
|
||||
- plugin system for different game logics
|
||||
|
||||
### Technical
|
||||
- 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.
|
||||
|
||||
**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
|
||||
- Track progress via checkboxes in plan
|
||||
- ~75 files total
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
/// </summary>
|
||||
public class FoxHuntGameLogicService : IGameLogicService
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object CreateInitialModel(Guid[] playerIds, object? setupOptions)
|
||||
{
|
||||
var options = ParseSetupOptions(setupOptions);
|
||||
|
|
@ -30,13 +31,9 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
NonFoxIndex = 0, // Index für Nicht-Fuchs-Spieler
|
||||
FoxTurnsRemaining = options.LeadingThrows - 1, // -1 weil erster Wurf bereits aus PlayerOrder[FoxIndex]
|
||||
FoxTurn = true, // ein fuchs fängt an
|
||||
|
||||
LeadingThrows = options.LeadingThrows,
|
||||
PlayerStates = playerStates,
|
||||
PlayerOrder = playerOrder,
|
||||
//CurrentPlayerIndex = 0,
|
||||
//FoxLeadingThrowCount = 0,
|
||||
//CurrentFoxIndex = 0,
|
||||
WinnerId = null,
|
||||
IsGameOver = false,
|
||||
};
|
||||
|
|
@ -44,6 +41,7 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
return model;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public (object UpdatedModel, ThrowResult Result) ProcessThrow(object gameModel, AfterThrowState afterThrow)
|
||||
{
|
||||
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 eliminatedPlayers = new List<Guid>(model.EliminatedPlayers);
|
||||
var triggers = new List<TriggerEvent>();
|
||||
|
||||
var foxId = model.PlayerOrder[model.FoxIndex];
|
||||
|
|
@ -73,19 +69,28 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
if (model.FoxTurn)
|
||||
{
|
||||
playerStates[foxId].PinCountFox+= afterThrow.PinsKnocked;
|
||||
if (afterThrow.IsCircle)
|
||||
{
|
||||
playerStates[foxId].PinCountFox += 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
playerStates[foxId].PinCountHunters+= afterThrow.PinsKnocked;
|
||||
if (afterThrow.IsCircle)
|
||||
{
|
||||
playerStates[foxId].PinCountHunters += 2;
|
||||
}
|
||||
|
||||
if (playerStates[foxId].PinCountHunters >= playerStates[foxId].PinCountFox )
|
||||
{
|
||||
// fox has been caught
|
||||
isLastHunter = true;
|
||||
isLastHunter = true; // will cause selection of the next fox
|
||||
playerStates[foxId].FoxCaught = true;
|
||||
|
||||
|
||||
// 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)
|
||||
{
|
||||
triggers.Add(new TriggerEvent
|
||||
|
|
@ -109,7 +114,7 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
var nextPlayerId = GetNextId(model, isLastHunter);
|
||||
|
||||
// Check GAME END
|
||||
bool isGameOver = isLastHunter && model.FoxIndex == 0;
|
||||
var isGameOver = isLastHunter && model.FoxIndex == 0;
|
||||
|
||||
if (isGameOver)
|
||||
{
|
||||
|
|
@ -178,7 +183,7 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
return model.PlayerOrder[model.FoxIndex];
|
||||
}
|
||||
|
||||
// 1️⃣ Fuchs ist 2x hintereinander dran
|
||||
// Fuchs ist 2x hintereinander dran
|
||||
if (model.FoxTurnsRemaining > 0)
|
||||
{
|
||||
model.FoxTurnsRemaining--;
|
||||
|
|
@ -188,7 +193,7 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
|
||||
model.FoxTurn = !model.FoxTurn; // danach abwechselnd
|
||||
|
||||
// 2️⃣ Abwechselnd Nicht-Fuchs → Fuchs
|
||||
// Abwechselnd Nicht-Fuchs → Fuchs
|
||||
if (!model.FoxTurn)
|
||||
{
|
||||
var nextNonFoxIndex = model.GetNextIndex(model.PlayerOrder, model.NonFoxIndex);
|
||||
|
|
@ -199,56 +204,26 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
}
|
||||
model.NonFoxIndex = 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);
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 3️⃣ Fuchs-Zug im Wechsel
|
||||
//model.FoxTurn = false;
|
||||
// Fuchs-Zug im Wechsel
|
||||
return foxId;
|
||||
}
|
||||
|
||||
private static int GetNextActivePlayerIndex(
|
||||
Guid[] playerOrder,
|
||||
int currentIndex,
|
||||
Dictionary<Guid, FoxHuntPlayerState> playerStates)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public (bool IsGameOver, Guid? WinnerId) CheckGameEnd(object gameModel)
|
||||
{
|
||||
var model = this.CastModel<FoxHuntGameModel>(gameModel);
|
||||
return (model.IsGameOver, model.WinnerId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyDictionary<Guid, PlayerStatsSummary> GetPlayerStats(object gameModel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public GameSetupValidationResult ValidateSetup(object? setupOptions)
|
||||
{
|
||||
if (setupOptions is null)
|
||||
|
|
@ -261,7 +236,7 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
|
||||
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
|
||||
|
|
@ -271,12 +246,14 @@ namespace Koogle.Application.Games.FoxHunt
|
|||
: GameSetupValidationResult.Valid();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<GameActionDescriptor> GetAvailableActions(object gameModel, Guid currentPlayerId)
|
||||
{
|
||||
// FoxHunt has no custom actions
|
||||
return [];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public GameActionResult ExecuteAction(object gameModel, string actionId, Guid currentPlayerId,
|
||||
IReadOnlyDictionary<string, object>? parameters = null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,34 +1,46 @@
|
|||
namespace Koogle.Application.Games.FoxHunt
|
||||
{
|
||||
/// <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.
|
||||
/// </summary>
|
||||
public record FoxHuntGameModel : IGameModel
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// State for each player (details if fox survived or not).
|
||||
/// </summary>
|
||||
public Dictionary<Guid, FoxHuntPlayerState> PlayerStates { get; init; } = new();
|
||||
|
||||
//public int LeadingThrows { get; set; }
|
||||
//public int FoxLeadingThrowCount { get; set; }
|
||||
public Dictionary<Guid, FoxHuntPlayerState> PlayerStates { get; set; }
|
||||
|
||||
public Guid[] PlayerOrder { get; set; }
|
||||
/// <summary>
|
||||
/// Fixed player order (default order at game start).
|
||||
/// </summary>
|
||||
public Guid[] PlayerOrder { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// count of throw the fox can start with
|
||||
/// copy from setup model
|
||||
/// </summary>
|
||||
public int LeadingThrows { get; set; }
|
||||
public int LeadingThrows { get; set; }
|
||||
|
||||
//public int CurrentPlayerIndex { get; set; }
|
||||
//public int CurrentFoxIndex { get; set; }
|
||||
/// <summary>
|
||||
/// 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)
|
||||
public int NonFoxIndex { get; set; } // Index für Nicht-Fuchs-Spieler
|
||||
public int FoxTurnsRemaining { get; set; } // verbleibende Fuchs-Züge
|
||||
public bool FoxTurn { get; set; } // true = Fuchs dran im Wechsel
|
||||
/// <summary>
|
||||
/// remaining leading throws a fox has left
|
||||
/// </summary>
|
||||
public int FoxTurnsRemaining { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// true = a throw by fox, false = a throw by a hunter
|
||||
/// </summary>
|
||||
public bool FoxTurn { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Guid? WinnerId { get; set; }
|
||||
|
|
@ -37,12 +49,34 @@
|
|||
public bool IsGameOver { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Info about the last throw for UI display.
|
||||
/// </summary>
|
||||
public record FoxHuntPlayerState
|
||||
{
|
||||
/// <summary>
|
||||
/// count of pins the fox has knocked.
|
||||
/// </summary>
|
||||
public int PinCountFox { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// count of pins the hunters have knocked.
|
||||
/// </summary>
|
||||
public int PinCountHunters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Weather the hunters have caught the fox.
|
||||
/// </summary>
|
||||
public bool FoxCaught { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Weather the fox survived with most point difference.
|
||||
/// </summary>
|
||||
public bool IsWinner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Weather the fox could escape the hunters.
|
||||
/// </summary>
|
||||
public bool FoxEscaped { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,19 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Koogle.Application.Games
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for GameLogicServices
|
||||
/// </summary>
|
||||
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
|
||||
{
|
||||
if (gameModel is T model)
|
||||
|
|
@ -29,8 +40,18 @@ namespace Koogle.Application.Games
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper methods for GameModel
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
if (index == 0)
|
||||
|
|
@ -40,6 +61,13 @@ namespace Koogle.Application.Games
|
|||
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)
|
||||
{
|
||||
if (index == guids.Length - 1)
|
||||
|
|
@ -49,6 +77,13 @@ namespace Koogle.Application.Games
|
|||
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)
|
||||
{
|
||||
if (index == guids.Length - 1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue