187 lines
5.9 KiB
C#
187 lines
5.9 KiB
C#
namespace Faraon.Core.AI;
|
|
|
|
using Faraon.Core.Models;
|
|
using Faraon.Core.Engine;
|
|
|
|
public enum AIDecisionType
|
|
{
|
|
PlayCard,
|
|
BurnCards,
|
|
DrawOrAcknowledge
|
|
}
|
|
|
|
public record AIDecision(
|
|
AIDecisionType Type,
|
|
Card? Card = null,
|
|
List<Card>? Cards = null,
|
|
Suit? ChosenSuit = null
|
|
);
|
|
|
|
public interface IFaraonAI
|
|
{
|
|
AIDecision MakeDecision(GameState state, Player player);
|
|
}
|
|
|
|
public class FaraonAI : IFaraonAI
|
|
{
|
|
private readonly Random _random = new();
|
|
|
|
public AIDecision MakeDecision(GameState state, Player player)
|
|
{
|
|
// 1. Kontrola Spálenia (4 rovnaké karty)
|
|
if (state.Rules.FourOfAKindBurns && state.PendingDrawCount == 0 && !state.PendingSkip)
|
|
{
|
|
var fourOfAKindGroup = player.Hand
|
|
.GroupBy(c => c.Rank)
|
|
.FirstOrDefault(g => g.Count() == 4);
|
|
|
|
if (fourOfAKindGroup != null)
|
|
{
|
|
var burnList = fourOfAKindGroup.ToList();
|
|
if (ActionValidator.CanBurnCards(burnList, state.PendingDrawCount, state.PendingSkip, state.Rules))
|
|
{
|
|
return new AIDecision(AIDecisionType.BurnCards, Cards: burnList);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Nájdi všetky zahrateľné karty
|
|
var playableCards = player.Hand
|
|
.Where(c => ActionValidator.CanPlayCard(
|
|
c,
|
|
state.TopCard,
|
|
state.ActiveSuit,
|
|
state.PendingDrawCount,
|
|
state.PendingSkip,
|
|
state.Rules))
|
|
.ToList();
|
|
|
|
// Ak nemá čo zahrať -> musí si potiahnuť alebo akceptovať trest
|
|
if (playableCards.Count == 0)
|
|
{
|
|
return new AIDecision(AIDecisionType.DrawOrAcknowledge);
|
|
}
|
|
|
|
// 3. Rozhodovanie podľa obtiažnosti
|
|
Card selectedCard;
|
|
if (player.Difficulty == AIDifficulty.Easy)
|
|
{
|
|
selectedCard = playableCards[_random.Next(playableCards.Count)];
|
|
}
|
|
else if (player.Difficulty == AIDifficulty.Hard)
|
|
{
|
|
selectedCard = ChooseBestCardSmart(playableCards, player, state);
|
|
}
|
|
else // Medium
|
|
{
|
|
selectedCard = ChooseBestCardMedium(playableCards, player, state);
|
|
}
|
|
|
|
// Zozbierame všetky karty rovnakej hodnoty v ruke (combo vyloženie)
|
|
var multiCards = player.Hand.Where(c => c.Rank == selectedCard.Rank).ToList();
|
|
|
|
// 4. Ak je vybraná karta Horník, vyber najvýhodnejšiu farbu
|
|
Suit? chosenSuit = null;
|
|
if (selectedCard.IsHornik)
|
|
{
|
|
chosenSuit = PickBestSuit(player.Hand.Where(c => !multiCards.Contains(c)).ToList());
|
|
}
|
|
|
|
return new AIDecision(AIDecisionType.PlayCard, Card: selectedCard, Cards: multiCards, ChosenSuit: chosenSuit);
|
|
}
|
|
|
|
private Card ChooseBestCardMedium(List<Card> playable, Player player, GameState state)
|
|
{
|
|
// Ak sa musí brániť proti sedme, zahrá sedmu
|
|
if (state.PendingDrawCount > 0)
|
|
{
|
|
var seven = playable.FirstOrDefault(c => c.IsSeven);
|
|
if (seven != null) return seven;
|
|
}
|
|
|
|
// Ak sa musí brániť proti esu, zahrá eso
|
|
if (state.PendingSkip)
|
|
{
|
|
var ace = playable.FirstOrDefault(c => c.IsAce);
|
|
if (ace != null) return ace;
|
|
}
|
|
|
|
// Ak má v ruke len 1 kartu navyše a môže vyhrať
|
|
if (player.Hand.Count <= 2)
|
|
{
|
|
return playable[0];
|
|
}
|
|
|
|
// Radšej šetrí Horníka ako žolíka, ak má inú normálnu kartu
|
|
var nonHornik = playable.Where(c => !c.IsHornik).ToList();
|
|
if (nonHornik.Count > 0)
|
|
{
|
|
return nonHornik[_random.Next(nonHornik.Count)];
|
|
}
|
|
|
|
return playable[0];
|
|
}
|
|
|
|
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)
|
|
if (state.PendingDrawCount > 0)
|
|
{
|
|
var regularSeven = playable.FirstOrDefault(c => c.IsSeven && !c.IsRedSeven);
|
|
if (regularSeven != null) return regularSeven;
|
|
var redSeven = playable.FirstOrDefault(c => c.IsRedSeven);
|
|
if (redSeven != null) return redSeven;
|
|
}
|
|
|
|
// 2. Ak čelíme esu, prebijeme esom
|
|
if (state.PendingSkip)
|
|
{
|
|
var ace = playable.FirstOrDefault(c => c.IsAce);
|
|
if (ace != null) return ace;
|
|
}
|
|
|
|
// 3. Ak môžeme touto kartou hru ukončiť (posledná karta v ruke)
|
|
if (player.Hand.Count == 1)
|
|
{
|
|
return playable[0];
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
var redSeven = playable.FirstOrDefault(c => c.IsRedSeven);
|
|
if (redSeven != null) return redSeven;
|
|
}
|
|
|
|
// 5. Ak máme iné možnosti, šetríme si Horníka na núdzu
|
|
var nonHornikCards = playable.Where(c => !c.IsHornik).ToList();
|
|
if (nonHornikCards.Count > 0)
|
|
{
|
|
// Zvoľ kartu farby, z ktorej máme najviac kariet
|
|
var suitCounts = player.Hand
|
|
.GroupBy(c => c.Suit)
|
|
.ToDictionary(g => g.Key, g => g.Count());
|
|
|
|
return nonHornikCards
|
|
.OrderByDescending(c => suitCounts.GetValueOrDefault(c.Suit, 0))
|
|
.First();
|
|
}
|
|
|
|
return playable[0];
|
|
}
|
|
|
|
public static Suit PickBestSuit(List<Card> remainingHand)
|
|
{
|
|
if (remainingHand.Count == 0) return Suit.Hearts;
|
|
|
|
// Nájdi farbu, ktorej máme najviac v ruke
|
|
var mostFrequentSuit = remainingHand
|
|
.GroupBy(c => c.Suit)
|
|
.OrderByDescending(g => g.Count())
|
|
.First()
|
|
.Key;
|
|
|
|
return mostFrequentSuit;
|
|
}
|
|
}
|