Click here to Skip to main content
15,867,453 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
For my cards game I have a lot of code like that:
If sHandCards IsNot Nothing Then

      If sHandCards.ToString.Contains("7") Then Return "7"
      If sHandCards.ToString.Contains("8") Then Return "8"
      If sHandCards.ToString.Contains("9") Then Return "9"
      If sHandCards.ToString.Contains("K") Then Return "K"

      If TrumpCardID = 4 Then
          If sHandCards.ToString.Contains("O") Then Return "O"
      End If

      If sHandCards.ToString.Contains("A") Then Return "A"
      If sHandCards.ToString.Contains("10") Then Return "10"

End If

If sNonTrumpCards IsNot Nothing Then

          If sNonTrumpCards.ToString.Contains("7") Then Return "7"
          If sNonTrumpCards.ToString.Contains("8") Then Return "8"
          If sNonTrumpCards.ToString.Contains("9") Then Return "9"

          If sNonTrumpCards.ToString.Contains("K") Then Return "K"

          If TrumpCardID = 4 Then
              If sNonTrumpCards.ToString.Contains("O") Then Return "O"
          End If

          If sNonTrumpCards.ToString.Contains("10") Then Return "10"
          If sNonTrumpCards.ToString.Contains("A") Then Return "A"

          If sNonTrumpCards.ToString.Contains("U") Then Return "U"
          If sNonTrumpCards.ToString.Contains("O") Then Return "O"

  End If


Could the code get shorter using a variable / array for strings (7,8,9,U,O ...) and then make a loop?

What I have tried:

I plan to use a variable and a loop instead of many separate strings but do not know how the array and the loop should look like.
Posted
Updated 26-Dec-22 4:16am

Do yourself a favour, and don't use strings at all.

Create a Card class, which has a Value and a Suit and then a Deck class which contains a List of Card objects. Populate the Deck with all 52 possible cards, and then write a Shuffle method that randomizes them.

Then create a Hand class, which has a collection of Card objects and add a Hand for each player. Deal into the hands, and work from that.

It'll almost certainly become a whole lot simpler to simulate any card game than the version you have at the moment - and a whole load more readable!
 
Share this answer
 
Comments
Jo_vb.net 26-Dec-22 9:13am    
I have a deck class and playingCard class.

Each player has a cardsPanel with the cards (playingCards) of only himself (handCards).

I get the strings like that:

' GetTrumpList
With CardsPanel.Children
If .Count > 0 Then
For n = 0 To .Count - 1
If .Item(n).CardType = TrumpCardID Then
sTrumpList = sTrumpList & " " & .Item(n).CardShortName & " " & .Item(n).CardSymbol & Environment.NewLine
End If
Next
End If
End With

So how to get same results shorter and without using strings?

P.S.:

Public Class PlayingCard
Inherits ToggleButton

Public Property CardSymbol As String
Public Property CardShortName As String
Public Property IsCallAce As Boolean
Public Property IsAlreadyPlayed As Boolean
Public Property IsPlayedTwice As Boolean
Public Property IsHighestCardInColor As Boolean
Public Property IsPlayableThrowMeAwayCard As Boolean ' Abspatzen
Public Property IsPlayableSchmearCard As Boolean ' Schmieren

Public Property IsUsefulAsFirstCardInTrick As Boolean ' Trumpf-As/10er = False !!!
Public Property IsHighestPlayableTrumpCard As Boolean
Public Property IsLowestPlayableTrumpCard As Boolean

Private Shared CardOwnerProperty As DependencyProperty = DependencyProperty.Register("CardOwner", GetType(CardOwner), GetType(PlayingCard), New PropertyMetadata(CardOwner.North))

Private Shared CardTypeProperty As DependencyProperty = DependencyProperty.Register("CardType", GetType(CardType), GetType(PlayingCard), New PropertyMetadata(CardType.Acorn))

Private Shared CardValueProperty As DependencyProperty = DependencyProperty.Register("CardValue", GetType(CardValue), GetType(PlayingCard), New PropertyMetadata(CardValue.Seven, AddressOf UpdateTemplate))

Private Shared CardTemplatesProperty As DependencyProperty = DependencyProperty.Register("CardTemplates", GetType(CardTemplateCollection), GetType(PlayingCard), New PropertyMetadata(New CardTemplateCollection(), AddressOf UpdateTemplate))
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
VB
Public Enum Suit
    Clubs
    Diamonds
    Hearts
    Spades
End Enum

2. Card Kinds (values)
VB
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
VB.NET
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
VB.NET
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)
VB.NET
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
VB.NET
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
VB
Public Enum CardSortType
    KindOnly
    SuitOnly
    SuitThenKind
    KindThenSuit
End Enum

b. The sorter
VB.NET
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.
VB
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)

        'Sample of shuffling and dealing cards for a player

        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:
VB
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:
VB
If myHand.HasCard(Kind.Eight, Suit.Clubs) Then
    'do something...
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:
VB
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

        'do testing here

        Return result

    End Function

End Module

Then, to check the results, you simply:
VB
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:
VB
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:
VB
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!
 
Share this answer
 
v5
Comments
Jo_vb.net 26-Dec-22 11:09am    
Thanks for your work - this looks like a variant for my already existing classes.

deck class
shuffle method
4 cardpanels with playingCards

are already there.

After shuffling the cards are distributed to the 4 cardpanels.
1 panel is controlled by the user, the pc controls 3 panels and plays against the user.

In my original question I asked for a better version of that:

If sHandCards IsNot Nothing Then

If sHandCards.ToString.Contains("7") Then Return "7"
If sHandCards.ToString.Contains("8") Then Return "8"
If sHandCards.ToString.Contains("9") Then Return "9"
If sHandCards.ToString.Contains("K") Then Return "K"

If TrumpCardID = 4 Then
If sHandCards.ToString.Contains("O") Then Return "O"
End If

If sHandCards.ToString.Contains("A") Then Return "A"
If sHandCards.ToString.Contains("10") Then Return "10"

End If

In this case the lowest card of a cardPanel should be given as return.

I do not understand how your approach should be helpful for that issue.
And I do do not want to re-write the complete project because of this issue.
Dave Kreskowiak 26-Dec-22 11:25am    
You're not understanding what's going on. You "optimize" the code by NOT USING STRINGS! Every time you use a string you are allocating more memory and fragmenting it when the strings are freed. Every time you use .ToString(), you are allocating memory and them doing character comparisons. This is the SLOWEST way to go about handling cards.

Unfortunately for you, getting rid of the strings also means completely rewriting your classes to avoid using them. You simply have no choice.
Jo_vb.net 26-Dec-22 11:37am    
I would re-write bigger parts of this fun project if that would have advantage for important code parts.

One is how the lowest (or the highest) card of a cardsPanel (with all the cards one player has in his hands) could be given as return without using my string method.

Other points are how such a framework would be helpful for creating game rules.
Graeme_Grant 26-Dec-22 11:46am    
See my update above. Also, as for the cards, I overload the ToString() to return the card values.
Jo_vb.net 27-Dec-22 3:55am    
Thanks for the updates.

I give your Hand class and the Extensions module a chance and combine them with my existing classes.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900