diff --git a/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameLogicService.cs b/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameLogicService.cs index 7f21801..a646b6f 100644 --- a/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameLogicService.cs +++ b/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameLogicService.cs @@ -48,6 +48,7 @@ public class ChristmasTreeGameLogicService : IGameLogicService CurrentTeamIndex = 0, CurrentPlayerIndexInTeam = 0, PlayerIndexPerTeam = playerIndexPerTeam, + EnableSumOnce = options.EnableSumOnce, EnableContinueOnMiss = options.EnableContinueOnMiss, MaxContinueThrows = options.MaxContinueThrows, ContinueThrowsRemaining = 0, @@ -118,6 +119,50 @@ public class ChristmasTreeGameLogicService : IGameLogicService return RotateToNextPlayer(model, triggers); } + // Sum mode: second throw — try to cross first+second + if (model.IsInSumMode) + { + int sum = model.FirstThrowValue + pinsKnocked; + model = model with { IsInSumMode = false, FirstThrowValue = 0 }; + + bool crossedOwnSum = TryCrossNumber(teamTrees, playerTeamIndex, sum); + if (crossedOwnSum) + { + lastThrow = lastThrow with { CrossedOwnTree = true, PinsKnocked = sum }; + teamTrees[playerTeamIndex] = teamTrees[playerTeamIndex] with + { + CrossedOutCount = teamTrees[playerTeamIndex].CrossedOutCount + 1 + }; + bool gameEndedSum = CheckGameEndCondition(teamTrees, model.EndCondition); + if (gameEndedSum) + { + lastThrow = lastThrow with { TriggeredGameEnd = true }; + return HandleGameEnd(model, teamTrees, lastThrow, triggers); + } + model = model with { TeamTrees = teamTrees, LastThrow = lastThrow, IsInContinueMode = false, ContinueThrowsRemaining = 0 }; + return RotateToNextPlayer(model, triggers); + } + + var crossedOpponentsSum = TryCrossFromOpponents(teamTrees, playerTeamIndex, sum); + if (crossedOpponentsSum.Count > 0) + { + lastThrow = lastThrow with { CrossedOpponentTrees = true, OpponentTeamsCrossed = crossedOpponentsSum.ToArray(), PinsKnocked = sum }; + bool gameEndedSum = CheckGameEndCondition(teamTrees, model.EndCondition); + if (gameEndedSum) + { + lastThrow = lastThrow with { TriggeredGameEnd = true }; + return HandleGameEnd(model, teamTrees, lastThrow, triggers); + } + model = model with { TeamTrees = teamTrees, LastThrow = lastThrow, IsInContinueMode = false, ContinueThrowsRemaining = 0 }; + return RotateToNextPlayer(model, triggers); + } + + // Sum also not available → miss, rotate + lastThrow = lastThrow with { WasMiss = true }; + model = model with { TeamTrees = teamTrees, LastThrow = lastThrow }; + return RotateToNextPlayer(model, triggers); + } + // Try to cross number from own tree first bool crossedOwn = TryCrossNumber(teamTrees, playerTeamIndex, pinsKnocked); @@ -181,6 +226,26 @@ public class ChristmasTreeGameLogicService : IGameLogicService // Number not available anywhere = miss lastThrow = lastThrow with { WasMiss = true }; + // Sum-once: enter sum mode on first miss (non-gutter) + if (model.EnableSumOnce) + { + model = model with + { + TeamTrees = teamTrees, + LastThrow = lastThrow, + IsInSumMode = true, + FirstThrowValue = pinsKnocked + }; + return (model, new ThrowResult + { + PointsScored = pinsKnocked, + ShouldRotatePlayer = false, + IsGameOver = false, + WinnerId = null, + Triggers = [] + }); + } + if (model.EnableContinueOnMiss) { return HandleContinueModeThrow(model, teamTrees, lastThrow, pinsKnocked, playerTeamIndex, true); diff --git a/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameModel.cs b/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameModel.cs index 4219041..74cf40a 100644 --- a/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameModel.cs +++ b/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameModel.cs @@ -40,6 +40,21 @@ public record ChristmasTreeGameModel : IGameModel /// public Dictionary PlayerIndexPerTeam { get; init; } = new(); + /// + /// Whether sum-once variant is enabled. + /// + public bool EnableSumOnce { get; init; } + + /// + /// Whether the current player is in sum mode (waiting for second throw to sum). + /// + public bool IsInSumMode { get; set; } + + /// + /// Value of the first throw in sum mode. + /// + public int FirstThrowValue { get; set; } + /// /// Whether continue-on-miss variant is enabled. /// diff --git a/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameSetup.cs b/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameSetup.cs index 8f914eb..11a33d8 100644 --- a/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameSetup.cs +++ b/src/GoodWood.Application/Games/ChristmasTree/ChristmasTreeGameSetup.cs @@ -63,10 +63,15 @@ public record ChristmasTreeGameSetup : GameSetupModelBase /// public int MaxContinueThrows { get; init; } = 3; + /// + /// When enabled, a miss (not gutter) allows one additional throw; the sum is tried. + /// + public bool EnableSumOnce { get; init; } = true; + /// /// Condition that triggers game end. /// - public GameEndCondition EndCondition { get; init; } = GameEndCondition.AllFivesGone; + public GameEndCondition EndCondition { get; init; } = GameEndCondition.TreeCleared; /// /// Creates a new setup with default values. @@ -77,7 +82,8 @@ public record ChristmasTreeGameSetup : GameSetupModelBase TreeVariant variant = TreeVariant.Beginner, bool enableContinueOnMiss = false, int maxContinueThrows = 3, - GameEndCondition endCondition = GameEndCondition.AllFivesGone, + bool enableSumOnce = true, + GameEndCondition endCondition = GameEndCondition.TreeCleared, IReadOnlyList? teams = null) => new() { ThrowMode = throwMode, @@ -86,6 +92,7 @@ public record ChristmasTreeGameSetup : GameSetupModelBase Variant = variant, EnableContinueOnMiss = enableContinueOnMiss, MaxContinueThrows = maxContinueThrows, + EnableSumOnce = enableSumOnce, EndCondition = endCondition, Teams = teams }; diff --git a/src/GoodWood.Web/Components/Game/ChristmasTree/ChristmasTreeSetup.razor b/src/GoodWood.Web/Components/Game/ChristmasTree/ChristmasTreeSetup.razor index ab88edd..fd6d7ab 100644 --- a/src/GoodWood.Web/Components/Game/ChristmasTree/ChristmasTreeSetup.razor +++ b/src/GoodWood.Web/Components/Game/ChristmasTree/ChristmasTreeSetup.razor @@ -30,6 +30,11 @@ Ein Baum komplett leer + +