How do I shuffle and deal cards one at a time to players?

后端 未结 2 1364
忘掉有多难
忘掉有多难 2020-11-27 08:27

This is what I got so far and find myself stuck.=/

Private Sub Dealbtn_Click(sender As Object, e As EventArgs) Handles Dealbtn.Click  
     Dim Suits() As S         


        
相关标签:
2条回答
  • 2020-11-27 08:57

    This is the wrong way to go about it:

    Dim rand As New Random  
    Dim rand1 As Integer = rand.Next(12)  
    Dim rand2 As Integer = rand.Next(3) 
    

    You can easily end up with duplicate cards because you are picking THIS card without knowing if it has already been dealt (even in this click!). You also want one Random used per game/app, not per card. Representing a Card as suit & face will work, but it glues 2 important pieces of information together - in most games you will later have to parse it to get that info.

    A deck is made up of 52 cards; each Card is made of a Suit and a Rank. Lets build a simple class or two which mimics that:

    Public Class Card
        Public Property Suit As String
        Public Property Rank As Integer
    
        ' card images from
        ' http://www.jfitz.com/cards/
        Public Property Img As Image
    
        Private Faces() As String = {"Jack", "Queen", "King", "Ace"}
    
        ' for text version of the game
        Public Function CardText() As String
            Dim tmp As String = Rank.ToString
            If Rank = 1 Then
                tmp = "Ace"
            ElseIf Rank >= 11 Then
                tmp = Faces(Rank - 11)
            End If
            Return String.Format("{0} of {1}", tmp, Suit)
    
        End Function
    
        ' iDeck class will assign Rank, Suit and img to an "empty" card
        Public Sub New(strSuit As String, nRank As Integer, i As Image)
            Suit = strSuit
            Rank = nRank
            Img = i
        End Sub
    
        Public Overrides Function ToString() As String
            Return CardText()
        End Function
    End Class
    

    In reality, you'd also want a Value property since in most games it is not the same as the Rank.

    With the Rank and Suit as individual properties you can test the cards of one player versus another to see who has the best card. This is easy in some games like BlackJack since all you will care about is the Rank and the sum. Hand evaluation is more complex in other games since you can have combos like FullHouse and Flush. Now the deck (with both shuffle methods there for illustrative purposes):

    Public Class Deck
        Dim rand As Random
    
        ' the deck will be built in the same order a real deck comes in
        Private Suits() As String = {"Spades", "Diamonds", "Clubs", "Hearts"}
        Private Rank() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
    
        ' useful for blackjack
        Private Const Ace As Integer = 1
    
        ' freshly opened pack where they are in order.  this is reused rather 
        ' than building a new deck each time
        Private freshDeck As List(Of Card)
    
        ' shuffled deck; Stack prevents any bugs from a botched counter var
        Private shoe As Stack(Of Card)
    
        ' using an imagelist but My.Resources could work depending on card names
        Private imglist As ImageList
    
        ' the GAME object passes us the imagelist holding the card pics
        Public Sub New(imgs As ImageList)     ' ctor
            ' new random ONCE
            rand = New Random
            imglist = imgs
            NewDeck()
        End Sub
    
        ' create a new deck (done ONCE) but could be called again 
        Private Sub NewDeck()
            freshDeck = New List(Of Card)      ' new object
    
            For Each s As String In Suits
                For Each n As Integer In Rank
                    Dim key As String = CardKey(s, n)
    
                    freshDeck.Add(New Card(s, n, imglist.Images(key)))
                Next
            Next
        End Sub
    
        Private keys() As String = {"J", "Q", "K"}
    
        Private Function CardKey(suit As String, rank As Integer) As String
            ' convert Suit / Key to the key used in the imglist
            ' (e.g C1.JPG for Clubs, Ace)
            ' cards come from http://www.jfitz.com/cards/
            ' use the windows set (or rename them all)
    
            Dim key As String = suit.Substring(0, 1)   ' => C, H, D, S
            If rank < 11 Then
                key &= rank.ToString
            Else
                key &= keys(rank - 11)      ' cvt 11, 12, 13 => J, Q, K
            End If
    
            Return key & ".png"
        End Function
    
        ' Shuffle deck using Fisher-Yates; sub optimal here since we "use up"
        ' the shoe each hand and are not reshuffling a deck
        Public Sub Shuffle()
            ' new temp deck preserves the new deck starting point
            Dim thisDeck As New List(Of Card)(freshDeck.ToArray)
            Dim tmp As Card
    
            Dim j As Integer
            ' hi to low, so the rand pick result is meaningful
            ' lo to hi introduces a definite bias
            For i As Integer = thisDeck.Count - 1 To 0 Step -1
                j = rand.Next(0, i + 1)        ' NB max param is EXCLUSIVE
    
                tmp = thisDeck(j)
                ' swap Card j and Card i 
                thisDeck(j) = thisDeck(i)
                thisDeck(i) = tmp
            Next
    
            ' using a stack for the actual deck in use; copy shuffled deck to the Shoe
            shoe = New Stack(Of Card)(thisDeck.ToArray)
    
        End Sub
    
        ' shuffle using random and LINQ (neo's answer)
        Public Sub ShuffleLinq()
            ' using the same rand per app run may be random enough
            ' but would not suffice for most 'serious' games or standards
            shoe = New Stack(Of Card)(freshDeck.OrderBy(Function() rand.Next))
    
        End Sub
    
        Public Function DealCard() As Card
            ' get a card
            If shoe.Count = 0 Then
                ' ToDo: out of cards
                ' happens with 9+ handed, 7 card games and many hi-lo games...
                ' usually mucked and burn cards are reshuffled
                ' some games use shared cards at the end
                ' (muck/burn list not implemented)
            End If
            Return shoe.Pop
        End Function
    
    End Class
    

    Rather than simply looking for code to paste, you should start trying to learn concepts presented (even if it is just the learning the names of concepts you want/need to learn more about: Classes, Enum, Collections, Objects, Methods...). The above is much more involved than a simple array, but if you study it you'll see Card and Deck mimic the real world versions. The deck builds itself so elsewhere we just have to use it.

    Next is a Player Class to hold the cards and a Game Class to implement the rules for the game, deal cards and control the order (these are left for the student to complete). This results in almost no code in the form, just some calls to Game (and only Game) which in turn uses Deck and Player, controls turns, give cards to players etc. e.g.:

    Private poker As Game
    ...
    New Game(theImgList, 3)     ' 3 == the human player
    

    Shuffle button:

        poker.ShuffleDeck()
        poker.NewHand()
        thisRound = Game.Rounds.HoleCards
    

    Deal button:

        Select Case thisRound
            Case Game.Rounds.HoleCards
                poker.NewHand()                ' clears the display etc
                poker.DealRound(thisRound)     ' deal cards
                thisRound = Game.Rounds.Flop   ' change round indicator
    
            Case Game.Rounds.Flop             ' even this could be internal to Game(poker)
                poker.DealRound(thisRound)     
                thisRound = Game.Rounds.Turn
    

    In Game.DealRound:

        Case Rounds.Flop
            myDeck.DealCard()            ' burn card
            players(0).AddCard(myDeck.DealCard)  ' Player(0) is the house or community
            players(0).AddCard(myDeck.DealCard)
            players(0).AddCard(myDeck.DealCard)
    

    Using classes, the form doesnt know or care HOW anything happens (like which suffle method), just that it happens when requested. Texas HoldEm Game, where the Community Cards are held by Player(0) who has an IsHouse property and some other player who IsHuman (basically so their cards always show):

    enter image description here

    .o0( Yea, "Jon" go all in, PLEASE go all in.)
    And I definitely want to see a '6' come out for "Neo". Definitely a '6' for "Neo".

    0 讨论(0)
  • 2020-11-27 09:00

    You need to generate the whole deck upfront (52 cards), store it in a List/Stack/Queue, and when required, deal one to the player.

    A double loop should be good enough to generate cards sequentially, then sort by random number:

    Dim Suits() As String = {"S", "D", "C", "H"}
    Dim Faces() As String = {"2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"}
    
    Dim cards As New List(Of String)
    For Each s As String In Suits
      For Each f As String In Faces
        cards.Add(s & f)
      Next
    Next
    
    Dim r As New Random
    Dim cardsShuffled = cards.OrderBy(Function() r.Next)
    

    EDIT: Here is how you can populate your labels (just one way of doing it):

    Dim deck As New Stack(Of String)(cardsShuffled)
    For Each lbl As Label in {Label1, Label2, Label3, ...} 'you need to write all
      Try
        lbl.Text = deck.Pop()
      Catch ex As InvalidOperationException
        MessageBox.Show("No more cards.")
      End Try      
    Next
    

    Reference:

    • Stack(Of T).Pop @ MSDN.

    A proper solution would be to create labels dynamically, but first make sure you can get this to work. Refactoring is usually done after you have a working product.

    0 讨论(0)
提交回复
热议问题