As OriginalGriff pointed out, it's much easier to use objects to represent The deck, cards, etc. If you think about it, it is no different from the actual objects in real life.
A card has a suit and value. A deck has a number of cards. A dealer gives cards to players. Each of these is an object. So you should create classes for each of these objects to hold their values (properties). Action like shuffling, dealing, etc are methods for specific objects.
I'll build the Cards, Deck, Hand (Player), and Dealer + include a sorter.
Let us break it down:
1. Card Suits
Public Enum Suit
Clubs
Diamonds
Hearts
Spades
End Enum
2. Card Kinds (values)
Public Enum Kind
Two
Three
Four
Five
Six
Seven
Eight
Nine
Ten
Jack
Queen
King
Ace
End Enum
Note: Card values are sorted based on kinds: Value Cards, Picture Cards, and Ace (highest value card). I have not included wilds, I will leave that up to you.
3. Cards - Suit + Kind + custom ordering
Public Class Card
Implements IComparable(Of Card)
Public Kind As Kind
Public Suit As Suit
Public Sub New()
End Sub
Public Sub New(kind As Kind, suit As Suit)
Me.Kind = kind
Me.Suit = suit
End Sub
Public Function CompareTo(other As Card) As Integer _
Implements IComparable(Of Card).CompareTo
Return If(other Is Nothing, 1, If(Suit <> other.Suit, Suit.CompareTo(other.Suit), Kind.CompareTo(other.Kind)))
End Function
Public Overrides Function ToString() As String
Return $"{Kind} of {Suit}"
End Function
End Class
4. Card Deck
Public Class CardDeck
Public Sub New()
Cards = [Enum].GetValues(GetType(Suit)) _
.Cast(Of Suit)() _
.SelectMany(Function(__) [Enum].GetValues(GetType(Kind)) _
.Cast(Of Kind)(),
Function(suit, kind) New With {suit, kind}) _
.Select(Function(card) New Card(card.kind, card.suit)) _
.ToList()
End Sub
Public Property Cards As List(Of Card)
Public ReadOnly Property Count As Integer
Get
Return Cards.Count
End Get
End Property
Public Function DrawCardAt(index As Integer) As Card
If index < 0 OrElse index >= Count Then
Throw New ArgumentOutOfRangeException(NameOf(index))
End If
Dim card As Card = Cards(index)
Cards.RemoveAt(index)
Return card
End Function
Public Function DrawTopCard() As Card
Return DrawCardAt(0)
End Function
Public Function DrawBottomCard() As Card
Return DrawCardAt(Count - 1)
End Function
Public Function DrawRandomCard() As Card
Dim random As Random = New Random()
Dim index As Integer = random.[Next](Count)
Return DrawCardAt(index)
End Function
Public Sub AddCardOnTop(card As Card)
If Cards.Contains(card) Then
Throw New InvalidOperationException($"Deck already contains card {card}.")
End If
Cards.Insert(0, card)
End Sub
Public Sub AddCardOnBottom(card As Card)
If Not Cards.Contains(card) Then
Cards.Add(card)
Return
End If
Throw New InvalidOperationException($"Deck already contains card {card}.")
End Sub
Public Sub Shuffle()
Cards = Cards.OrderBy(Function(x) Guid.NewGuid()).ToList()
End Sub
Public Sub Sort()
Cards.Sort()
End Sub
Public Sub Sort(comparer As IComparer(Of Card))
Cards.Sort(comparer)
End Sub
End Class
5. Hand (Player)
Public Class Hand
Public Sub New(name As String)
Me.Name = name
Me.Cards = New List(Of Card)
End Sub
Public Property Cards As List(Of Card)
Public Property Name As String
Public Sub AddCard(card As Card)
If Cards.Contains(card) Then
Throw New InvalidOperationException($"Hand already contains card {card}.")
End If
Cards.Add(card)
End Sub
Public Sub RemoveCard(card As Card)
If Not Cards.Contains(card) Then
Throw New InvalidOperationException($"Hand does not contain card {card}.")
End If
Cards.Add(card)
End Sub
End Class
6. Dealer
Public Class Dealer
Public Sub New(deck As CardDeck)
Me.Deck = deck
End Sub
Public ReadOnly Property Deck As CardDeck
Public Sub DealCard(hand As Hand)
hand.AddCard(Deck.DrawTopCard())
End Sub
End Class
7. For the card sorting, I have supported a number of different orders:
7a. Sorting Types
Public Enum CardSortType
KindOnly
SuitOnly
SuitThenKind
KindThenSuit
End Enum
b. The sorter
Public Class CardSorter
Implements IComparer(Of Card)
Public Sub New(Optional sortBy As CardSortType = CardSortType.SuitThenKind)
Me.SortBy = sortBy
End Sub
Property SortBy As CardSortType
Public Function Compare(x As Card, y As Card) As Integer Implements IComparer(Of Card).Compare
Select Case Me.SortBy
Case CardSortType.KindOnly
Return x.Kind.CompareTo(y.Kind)
Case CardSortType.SuitOnly
Return x.Suit.CompareTo(y.Suit)
Case CardSortType.SuitThenKind
If x.Suit <> y.Suit Then
Return x.Suit.CompareTo(y.Suit)
End If
Return x.Kind.CompareTo(y.Kind)
Case CardSortType.KindThenSuit
If x.Kind <> y.Kind Then
Return x.Kind.CompareTo(y.Kind)
End If
Return x.Suit.CompareTo(y.Suit)
Case Else
Throw New NotImplementedException($"CardOrderMethod {SortBy} is not implemented.")
End Select
End Function
End Class
Below I have included examples of shuffling, sorting, and using the dealer to deal hands.
Module Program
Private cardDeck As CardDeck
Sub Main(args As String())
cardDeck = New CardDeck()
DumpDeck("Created Deck")
Shuffle()
TestSortBy()
Shuffle()
TestSortBy(CardSortType.SuitThenKind)
Shuffle()
TestSortBy(CardSortType.KindThenSuit)
Shuffle()
TestSortBy(CardSortType.SuitOnly)
Shuffle()
TestSortBy(CardSortType.KindOnly)
Shuffle()
Dim myHand = New Hand("Player 1")
Dim theDealer = New Dealer(cardDeck)
For i As Integer = 0 To 5
theDealer.DealCard(myHand)
Next
DumpHand(myHand)
End Sub
Private Sub DumpHand(hand As Hand)
Console.WriteLine($"--- Player: {hand.Name} ---")
For Each card As Card In hand.Cards
Console.WriteLine(card)
Next
Console.WriteLine()
End Sub
Private Sub Shuffle()
CardDeck.Shuffle()
DumpDeck("Shuffled Deck")
End Sub
Private Sub TestSortBy(ByVal Optional sortType As CardSortType? = Nothing)
If sortType Is Nothing Then
CardDeck.Sort()
Else
CardDeck.Sort(New CardSorter With {
.SortBy = CType(sortType, CardSortType)
})
End If
DumpDeck($"Sorted By: {(If(sortType IsNot Nothing, sortType.ToString(), "default"))}")
End Sub
Private Sub DumpDeck(title As String)
Console.WriteLine($"--- {title} ---")
For Each card As Card In CardDeck.Cards
Console.WriteLine(card)
Next
Console.WriteLine()
End Sub
End Module
SAmple output of the Dealer dealing cards:
--- Player: Player 1 ---
Eight of Spades
Five of Clubs
Nine of Clubs
Ace of Diamonds
Eight of Clubs
Six of Clubs
I've created a card object-orientated framework for you. Now you can build your game around it.
UPDATE
As Dave was pointing out in the comments below, you missed the point of the code above. I gave you a bare framework that you can build upon eliminating the use of strings and optimiszing your code.
Let me give you an example. Say you want to check if a card is in your hand. You could write a custom method for the hand class, or ue an extension method. Here I will use the latter:
Public Module HandExtensions
<Extension>
Public Function HasCard(hand As Hand, kind As Kind, suit As Suit) As Boolean
Return hand.Cards.Any(Function(card) card.Kind = kind And card.Suit = suit)
End Function
End Module
Now, to check if a hand has a card, the code is simple and clean:
If myHand.HasCard(Kind.Eight, Suit.Clubs) Then
End If
Update #2
Evaluating the hand. Here is a mock of evaluating a poker hand (without the testing). Note, by using an enum, the value of each enum is incremental, so the highest hand has the highest enum value:
Public Enum HandRules
HighCard
OnePair
TwoPair
ThreeOfAKind
Straight
Flush
FullHouse
FourOfAKind
StraightFlush
RoyalFlush
End Enum
Public Module GenericPokerEvaluator
Public Function Evaluate(hand As Hand) As HandRules
Dim result As HandRules = HandRules.HighCard
Return result
End Function
End Module
Then, to check the results, you simply:
Dim result = GenericPokerEvaluator.Evaluate(myHand)
UPDATE #3
Looking at your code above and your comments, if you are looking for an optimized version of finding the
Lowest Card in the Hand, using the code above, we could write the following extension method:
Public Module HandExtensions
<Extension>
Public Function GetLowestCard(hand As Hand) As Card
Return hand.Cards _
.OrderBy(Function(card) card.Kind) _
.ThenBy(Function(card) card.Suit) _
.FirstOrDefault()
End Function
<Extension>
Public Function GetLowestCard(hand As Hand, suit As Suit) As Card
Return hand.Cards _
.Where(Function(card) card.Suit = suit) _
.OrderBy(Function(card) card.Kind) _
.FirstOrDefault()
End Function
End Module
Now to check, here is the code:
Dim lowestCard = myHand.GetLowestCard(Suit.Hearts)
Console.WriteLine($"The lowest Card of Hearts is: {If(lowestCard Is Nothing, "no hearts", lowestCard)}")
and sample output:
--- Player: Player 1 ---
Queen of Clubs
Ten of Hearts
Seven of Clubs
Six of Clubs
Three of Spades
Ace of Hearts
The lowest Card of Hearts is: Ten of Hearts
That is a lot cleaner and far less code ... no strings required!