improvements

This commit is contained in:
Michal Vrba
2026-09-03 17:54:36 +02:00
parent 00aa94ea7c
commit 035da18584
14 changed files with 1006 additions and 52 deletions
+4 -2
View File
@@ -124,11 +124,13 @@ public class FaraonAI : IFaraonAI
private Card ChooseBestCardSmart(List<Card> playable, Player player, GameState state)
{
// 1. Ak čelíme sedme, prioritne zahráme obyčajnú sedmu pred Červenou sedmou (ak je niekto vonku, šetríme ju)
// 1. Ak čelíme sedme, zahráme obyčajnú sedmu, Zeleného dolníka na zrušenie trestu, alebo Červenú sedmu
if (state.PendingDrawCount > 0)
{
var regularSeven = playable.FirstOrDefault(c => c.IsSeven && !c.IsRedSeven);
if (regularSeven != null) return regularSeven;
var greenUnderKnave = playable.FirstOrDefault(c => c.IsGreenUnderKnave);
if (greenUnderKnave != null) return greenUnderKnave;
var redSeven = playable.FirstOrDefault(c => c.IsRedSeven);
if (redSeven != null) return redSeven;
}
@@ -147,7 +149,7 @@ public class FaraonAI : IFaraonAI
}
// 4. Ak má Červenú sedmu a na stole je niekto na oživenie (LastFinishedPlayer), oživíme ho!
if (state.Rules.RedSevenRevives && state.LastFinishedPlayer != null)
if (state.Rules.RedSevenRevives && state.LastFinishedPlayer != null && state.ReviveTurnsRemaining > 0)
{
var redSeven = playable.FirstOrDefault(c => c.IsRedSeven);
if (redSeven != null) return redSeven;
+41 -1
View File
@@ -12,9 +12,15 @@ public static class ActionValidator
bool pendingSkip,
RuleOptions rules)
{
// 1. Ak je na stole trest zo sedmy (napr. +2, +4, +6)
// 1. Ak je na stole trest zo sedmy (napr. +3, +6, +9)
if (pendingDrawCount > 0)
{
// Zelený dolník ruší sedmičky
if (rules.GreenUnderKnaveCancelsSevens && card.IsGreenUnderKnave)
{
return true;
}
// Ak je povolené prebíjanie sediem, musí zahrať sedmu
return rules.StackSevens && card.IsSeven;
}
@@ -38,6 +44,21 @@ public static class ActionValidator
return true;
}
// 4b. Červená sedma v režime AnyCardOneRound sa môže zahrať na ľubovoľnú kartu
if (card.IsRedSeven && rules.RedSevenMode == RedSevenMode.AnyCardOneRound)
{
return true;
}
// 4c. Zelený dolník v režime UniversalWildcard ide na čokoľvek a naňho ide čokoľvek
if (rules.GreenUnderKnaveMode == GreenUnderKnaveMode.UniversalWildcard)
{
if (card.IsGreenUnderKnave || topCard.IsGreenUnderKnave)
{
return true;
}
}
// 5. Ak bola zmenená farba Horníkom
if (activeSuit.HasValue)
{
@@ -66,6 +87,10 @@ public static class ActionValidator
// 1. Ak je na stole trest zo sedmy
if (pendingDrawCount > 0)
{
if (rules.GreenUnderKnaveCancelsSevens && cards.Any(c => c.IsGreenUnderKnave))
{
return true;
}
return rules.StackSevens && cards.All(c => c.IsSeven);
}
@@ -87,6 +112,21 @@ public static class ActionValidator
return true;
}
// 4b. Combo so Srdcovou/Červenou sedmou v režime AnyCardOneRound sa dá vyložiť na čokoľvek
if (firstRank == Rank.Seven && cards.Any(c => c.IsRedSeven) && rules.RedSevenMode == RedSevenMode.AnyCardOneRound)
{
return true;
}
// 4c. Zelený dolník v režime UniversalWildcard
if (rules.GreenUnderKnaveMode == GreenUnderKnaveMode.UniversalWildcard)
{
if (cards.Any(c => c.IsGreenUnderKnave) || topCard.IsGreenUnderKnave)
{
return true;
}
}
// 5. Ak bola zmenená farba Horníkom
if (activeSuit.HasValue)
{
+68 -3
View File
@@ -31,6 +31,9 @@ public class GameEngine
State.RoundLoser = null;
State.RoundNumber = 1;
State.LastFinishedPlayer = null;
State.ReviveTurnsRemaining = 0;
State.ReviveRoundsRemaining = 0;
State.ReviveWindowInitialTurns = 0;
State.TurnCount = 1;
State.PendingDrawCount = 0;
State.PendingSkip = false;
@@ -205,11 +208,13 @@ public class GameEngine
State.PendingDrawCount += addedPenalty;
var redSeven = cards.FirstOrDefault(c => c.IsRedSeven);
if (redSeven != null && State.Rules.RedSevenRevives && State.LastFinishedPlayer != null && State.LastFinishedPlayer != player)
if (redSeven != null && State.Rules.RedSevenRevives && State.LastFinishedPlayer != null && State.LastFinishedPlayer != player && State.ReviveTurnsRemaining > 0)
{
var revived = State.LastFinishedPlayer;
revived.FinishedPosition = null;
State.LastFinishedPlayer = null;
State.ReviveTurnsRemaining = 0;
State.ReviveRoundsRemaining = 0;
DrawCardsForPlayer(revived, 3);
EmitEvent(new GameEvent(
@@ -257,6 +262,20 @@ public class GameEngine
));
}
}
// Efekt Zeleného dolníka (rušenie sediem)
else if (firstRank == Rank.UnderKnave && cards.Any(c => c.IsGreenUnderKnave) && State.Rules.GreenUnderKnaveCancelsSevens && State.PendingDrawCount > 0)
{
int canceled = State.PendingDrawCount;
State.PendingDrawCount = 0;
EmitEvent(new GameEvent(
GameEventType.SevenCanceled,
player,
Cards: cards,
ExtraData: canceled,
Message: $"🍃 {player.Name} zahral Zeleného dolníka a ZRUŠIL trest {canceled} kariet!"
));
}
// Ostatné karty
else
{
@@ -328,11 +347,13 @@ public class GameEngine
State.PendingDrawCount += 3;
// Červená sedma vracia do hry
if (card.IsRedSeven && State.Rules.RedSevenRevives && State.LastFinishedPlayer != null && State.LastFinishedPlayer != player)
if (card.IsRedSeven && State.Rules.RedSevenRevives && State.LastFinishedPlayer != null && State.LastFinishedPlayer != player && State.ReviveTurnsRemaining > 0)
{
var revived = State.LastFinishedPlayer;
revived.FinishedPosition = null;
State.LastFinishedPlayer = null;
State.ReviveTurnsRemaining = 0;
State.ReviveRoundsRemaining = 0;
// Doberie 3 karty
DrawCardsForPlayer(revived, 3);
@@ -382,6 +403,21 @@ public class GameEngine
State.PendingSkip = true;
}
// Efekt Zeleného dolníka (rušenie sediem)
if (card.IsGreenUnderKnave && State.Rules.GreenUnderKnaveCancelsSevens && State.PendingDrawCount > 0)
{
int canceled = State.PendingDrawCount;
State.PendingDrawCount = 0;
EmitEvent(new GameEvent(
GameEventType.SevenCanceled,
player,
Card: card,
ExtraData: canceled,
Message: $"🍃 {player.Name} zahral Zeleného dolníka a ZRUŠIL trest {canceled} kariet!"
));
}
// Kontrola, či hráč vyhral / skončil
CheckPlayerFinish(player);
@@ -538,7 +574,22 @@ public class GameEngine
{
int nextPos = State.Players.Count(p => p.HasFinished) + 1;
player.FinishedPosition = nextPos;
State.LastFinishedPlayer = player;
if (State.Rules.RedSevenRevives)
{
State.LastFinishedPlayer = player;
int maxRounds = State.Rules.RedSevenMode == RedSevenMode.ColorOrSevenThreeRounds ? 3 : 1;
int activeCount = Math.Max(1, State.ActivePlayersCount);
State.ReviveRoundsRemaining = maxRounds;
State.ReviveTurnsRemaining = maxRounds * activeCount;
State.ReviveWindowInitialTurns = State.ReviveTurnsRemaining;
}
else
{
State.LastFinishedPlayer = null;
State.ReviveRoundsRemaining = 0;
State.ReviveTurnsRemaining = 0;
}
EmitEvent(new GameEvent(
GameEventType.PlayerFinished,
@@ -682,6 +733,20 @@ public class GameEngine
{
if (State.IsGameOver || State.IsRoundOver) return;
// Odpočet lehoty na oživenie
if (State.LastFinishedPlayer != null && State.ReviveTurnsRemaining > 0)
{
State.ReviveTurnsRemaining--;
int activeCount = Math.Max(1, State.ActivePlayersCount);
State.ReviveRoundsRemaining = (int)Math.Ceiling((double)State.ReviveTurnsRemaining / activeCount);
if (State.ReviveTurnsRemaining <= 0)
{
State.LastFinishedPlayer = null;
State.ReviveRoundsRemaining = 0;
}
}
int attempts = 0;
do
{
+1
View File
@@ -126,4 +126,5 @@ public record Card(Suit Suit, Rank Rank)
public bool IsAce => Rank == Rank.Ace;
public bool IsHornik => Rank == Rank.OverKnave;
public bool IsRedSeven => Rank == Rank.Seven && Suit == Suit.Hearts;
public bool IsGreenUnderKnave => Rank == Rank.UnderKnave && Suit == Suit.Leaves;
}
+1
View File
@@ -7,6 +7,7 @@ public enum GameEventType
CardsBurned,
CardDrawn,
SevenStacked,
SevenCanceled,
AceSkipped,
AceCountered,
SuitChanged,
+3
View File
@@ -21,6 +21,9 @@ public class GameState
public RuleOptions Rules { get; set; } = new();
public bool IsGameOver { get; set; } = false;
public Player? LastFinishedPlayer { get; set; }
public int ReviveTurnsRemaining { get; set; } = 0;
public int ReviveRoundsRemaining { get; set; } = 0;
public int ReviveWindowInitialTurns { get; set; } = 0;
public int TurnCount { get; set; } = 0;
public TurnState TurnState { get; set; } = TurnState.Playing;
public Player? PendingHornikPlayer { get; set; }
+57 -4
View File
@@ -1,5 +1,19 @@
namespace Faraon.Core.Models;
public enum RedSevenMode
{
Disabled,
AnyCardOneRound,
ColorOrSevenThreeRounds
}
public enum GreenUnderKnaveMode
{
Disabled,
OnlyOnSevenOrUnderKnave, // Ruší sedmy, bežné pravidlá (Zeleň / Dolník)
UniversalWildcard // Ruší sedmy, ide na čokoľvek a súper naňho môže dať čokoľvek
}
public class RuleOptions
{
/// <summary>
@@ -18,9 +32,32 @@ public class RuleOptions
public bool CounterAceWithAce { get; set; } = true;
/// <summary>
/// Červená sedma vracia do hry hráča, ktorý sa už zbavil všetkých kariet (posledného víťaza).
/// Režim oživovania Červenou sedmou (Voľná do 1 kola, Klasická do 3 kôl, Vypnuté).
/// </summary>
public bool RedSevenRevives { get; set; } = true;
public RedSevenMode RedSevenMode { get; set; } = RedSevenMode.ColorOrSevenThreeRounds;
/// <summary>
/// Či je oživovanie Červenou sedmou povolené.
/// </summary>
public bool RedSevenRevives
{
get => RedSevenMode != RedSevenMode.Disabled;
set => RedSevenMode = value ? (RedSevenMode == RedSevenMode.Disabled ? RedSevenMode.ColorOrSevenThreeRounds : RedSevenMode) : RedSevenMode.Disabled;
}
/// <summary>
/// Režim pre Zeleného dolníka (rušenie sediem a univerzálne vykladanie).
/// </summary>
public GreenUnderKnaveMode GreenUnderKnaveMode { get; set; } = GreenUnderKnaveMode.OnlyOnSevenOrUnderKnave;
/// <summary>
/// Či Zelený dolník ruší trest zo sediem.
/// </summary>
public bool GreenUnderKnaveCancelsSevens
{
get => GreenUnderKnaveMode != GreenUnderKnaveMode.Disabled;
set => GreenUnderKnaveMode = value ? (GreenUnderKnaveMode == GreenUnderKnaveMode.Disabled ? GreenUnderKnaveMode.OnlyOnSevenOrUnderKnave : GreenUnderKnaveMode) : GreenUnderKnaveMode.Disabled;
}
/// <summary>
/// Zahranie 4 rovnakých kariet naraz ("Spálenie"). Spáli kôpku a hráč hrá znova.
@@ -33,9 +70,25 @@ public class RuleOptions
public bool PassAndPlayPrivacyScreen { get; set; } = false;
/// <summary>
/// Rýchlosť AI ťahov v milisekundách (pre plynulé sledovanie animácií).
/// Úroveň rýchlosti hry (1 = 0.5x, 2 = 0.75x, 3 = 1x Normálna, 4 = 1.5x Rýchla, 5 = 2.5x Turbo).
/// </summary>
public int AIDelayMs { get; set; } = 900;
public int SpeedLevel { get; set; } = 3;
/// <summary>
/// Rýchlosť AI ťahov v milisekundách podľa zvolenej úrovne rýchlosti.
/// </summary>
public int AIDelayMs
{
get => SpeedLevel switch
{
1 => 1600,
2 => 1200,
4 => 500,
5 => 200,
_ => 900
};
set { }
}
/// <summary>
/// Zvolený vizuálny štýl balíčka ("original" alebo "spongebob").