From my perspective, you have to account for the fact that when the player has one or more aces, he or she has two possible scores at any given time. Trying to calculate the single value closest to 21 is a faulty abstraction of what it means to hold an ace in blackjack.
As a player, I don't want the program to tell me that I have 16 when I have ace & 5, because I might not take a hit with 16, but if I have ace & 5 I am absolutely going to take that hit. Conceptually, I really have 6 or 16.
I think the value of the hand should be representable as one or two values. Obviously, in the absence of an ace, there will be only one value. Likewise, when ace-as-eleven means a busted hand, there is only one value for the hand, because that ace must be counted as 1. You could also say that any ace + 10 combination has only one value, because that is instant blackjack.
Here is a partial implementation -- one of many, many ways to skin this cat. I wrote it this way to sort of get you thinking in a certain way; given that only aces can have more than one value and a given hand can have at most two possible values, though, this could be done more succinctly.
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;
}
}