С моей точки зрения, вы должны учитывать тот факт, что, когда у игрока есть один или несколько тузов, он или она имеет два возможных результата в любой момент времени. Попытка вычислить единственное значение, наиболее близкое к 21 - ошибочная абстракция того, что значит держать туза в блэкджеке.
Как игрок, я не хочу, чтобы программа говорила мне, что у меня 16, когда у меня туз и 5, потому что я не могу получить удар с 16, но если у меня туз и 5, я абсолютно принять этот удар. Концептуально у меня действительно 6 или 16.
Я думаю, что ценность руки должна быть представлена как одно или два значения. Очевидно, что при отсутствии туза будет только одно значение. Точно так же, когда туз-как-одиннадцать означает раздаченную руку, для этой руки есть только одно значение, потому что туз должен считаться равным 1. Вы также можете сказать, что любая комбинация туз + 10 имеет только одну значение, потому что это мгновенный блэкджек.
Вот частичная реализация - один из многих, многих способов снять шкуру с этой кошки. Я написал это таким образом, чтобы заставить вас думать определенным образом; учитывая, что только тузы могут иметь более одного значения, а данная рука может иметь не более двух возможных значений, однако это можно сделать более кратко.
public interface Hand
{
IEnumerable<int> PossibleValues { get; set; }
}
public interface Card
{
CardValues PossibleValues { get; set; }
}
public interface CardValues
{
int Value1 { get; }
int Value2 { get; }
}
public class BlackjackHand : Hand
{
IList<Card> cards;
public IEnumerable<int> PossibleValues
{
IList<int> possible_values = new List<int>();
int initial_hand_value = cards.Sum(c => c.Value1);
if(initial_hand_value <= 21)
{
possible_values.Add(initial_hand_value);
yield return initial_hand_value;
}
foreach(Card card in cards.Where(c => c.Value2 > 0))
{
IList<int> new_possible_values = new List<int>();
foreach(int value in possible_values)
{
var alternate_value = value + card.Value2;
if(alternate_value <= 21)
{
new_possible_values.Add(alternate_value);
yield return alternate_value;
}
}
possible_values.AddRange(new_possible_values);
}
yield break;
}
}