【求助笔试题]】 找出VB.NET写的21点扑克牌程序中的问题 10C

Blackjack
Card
Deck
Game
Hand
Player
ProbabilityCalculator

``````''' <summary>
''' Emulates a black jack game between two different AI players. 模拟两个不同的AI玩家之间的黑杰克游戏。
''' </summary>
Public Class Blackjack

Public Shared ReadOnly AllCards As New List(Of Card)

''' <summary>
''' Static constructor.
''' Generates a list containing one of every possible card.生成一个包含所有可能卡片的列表
''' This simplified game assumes a single deck of 52 cards, one of every unique suit and rank combination.
''' 这个简化的游戏假设有一副52张的牌，每副牌都有自己独特的花色和等级组合。
''' </summary>
Shared Sub New()
For Each s As Card.Suit In [Enum].GetValues(GetType(Card.Suit))
For Each r As Card.Rank In [Enum].GetValues(GetType(Card.Rank))
Next
Next
End Sub

''' <summary>
''' Main method. Constructs the object, plays the game, prints the output.
''' Don't change this method!
''' </summary>
''' <param name="args">Command line args, unused.</param>
Public Shared Sub Main(args As String())
Dim deck As New Deck(AllCards)
Dim random As New Random()
Dim calculator As New ProbabilityCalculator(AllCards)
Dim player1 As New Player("Harry", calculator)
Dim player2 As New Player("Joe", calculator)
Dim game As New Game(deck, random, player1, player2)
Console.WriteLine(game.Play())
End Sub

End Class
``````

﻿Imports System
Imports System.Collections.Generic
Imports System.Text

'''
''' Represents a single playing card, the combination of a suit (Spades, hearts, clubs, diamonds)
''' and a rank (Ace, two, three... Jack, Queen, King).
''' 表示一张纸牌，是花色(黑桃、红心、梅花、方块)的组合
''' 还有一排(a，二，三……杰克,王后,国王)。
''' Any particular card is worth a number of points, according to its rank.
''' 任何一张特定的纸牌，根据它的等级，值若干点数。
''' In this simplified game, an Ace is always worth 1 points.
''' A two is with 2 points, three worth 3, and so on.
''' The face cards (Jack, Queen, King) are worth 10 points.
''' 在这个简化的游戏中，a总是值1分
'''2是2分，3是3分，以此类推
'''脸牌(杰克、皇后、国王)值10分。
'''
Public Class Card

``````Public Enum Suit
Hearts
Clubs
Diamonds
End Enum

Public Enum Rank
Ace = 1
Two = 2
Three = 3
Four = 4
Five = 5
Seven = 7
Eight = 8
Nine = 9
Ten = 10
Jack = 11
Queen = 12
King = 13
End Enum

Public Sub New(r As Rank, s As Suit)
mSuit = s
mRank = r
End Sub

''' <summary>
''' Returns a human readable name of the card, for example "Ace of Spades", suitable for inclusion in
''' the game's console output.
''' 返回人类可读的纸牌名称，例如“黑桃a”，适合包含在游戏控制台输出中
''' </summary>
''' <returns>Name of the card</returns>
Public ReadOnly Property Description As String
Get
'            Return mRank + " of" + mSuit
Return mRank.ToString + " of" + mSuit.ToString
End Get
End Property

''' <summary>
''' Returns the number of points that this card is worth, according to its rank.
''' 返回该牌值多少分，根据其分值。
''' </summary>
''' <returns>Point value of this card.</returns>
Public ReadOnly Property Points As Integer
Get
Return CType(mRank, Integer)
End Get
End Property

Public Overrides Function ToString() As String
Return Description
End Function
``````

End Class

``
'''
''' Represents a collection of playing cards that will be dealt in the game.
''' 表示将在游戏中处理的纸牌集合。
''' A fresh deck usually begins with the cards in an ordered sequence according to their suit and rank.
''' 一副新牌通常是按照牌的花色和等级顺序排列的。
''' Before dealing cards to the players, the dealer should shuffle the deck,
''' 发牌前，发牌人应洗牌
''' otherwise cards will be dealt in their original sequence.
''' 否则，牌将按原来顺序处理
''' During the game, the dealer deals one card at a time, removing it from the top
''' of the deck, and adding it to the player's hand.
''' 在游戏中，发牌人一次发一张牌，把它从牌顶移开 并将其添加到玩家手上。
'''
'''
Public Class Deck
'''
''' The cards remaining to be dealt
''' 剩下要处理的牌集合
'''
Private mCards As List(Of Card)

``````''' <summary>
''' Constructs a fresh deck with the specified cards in the given sequence
''' 按照给定的顺序用指定的牌组构造新的牌组
''' </summary>
''' <param name="cards">@param cards cards</param>
Public Sub New(cards As List(Of Card))
mCards = cards
End Sub

''' <summary>
''' Randomizes the sequence of the cards within the deck.
''' 将牌堆中的牌的顺序随机化
''' </summary>
Public Sub Shuffle(random As Random)

' Keep a reference to the existing cards, then build a new list and copy
' 保持对现有卡片的引用，然后建立一个新的列表并复制
' the cards over in a random sequence.
' 这些卡片是随机排列的。
Dim originalCards As List(Of Card) = mCards
Dim shuffledCards As New List(Of Card)

Dim numberOfCards As Integer = originalCards.Count
For i As Integer = 0 To numberOfCards
Dim nextCardIndex As Integer = random.Next(originalCards.Count)
Dim nextCard As Card = originalCards(nextCardIndex)
Next

mCards = shuffledCards
End Sub

''' <summary>
''' Draws a card from the top of the deck, so that it can be added to a player's hand.
''' 从牌堆顶部抽一张牌，这样它就可以加到玩家手上。
''' </summary>
''' <returns>Dealt card</returns>
Public Function Deal() As Card
Return mCards(0)
End Function

''' <summary>
''' Returns the list of cards in the deck, in the sequence that they are going to be dealt.
''' 返回牌堆中纸牌的列表，按将要处理它们的顺序排列。
''' </summary>
''' <returns>Cards cards in the deck</returns>
Public ReadOnly Property Cards As List(Of Card)
Get
Return mCards
End Get
End Property
``````

End Class

``````
''' <summary>
''' Controller responsible for overall game loop.
''' 负责整个游戏循环的控制器
''' </summary>
Public Class Game
Private mDeck As Deck
Private mRandom As Random
Private mPlayer1 As Player
Private mPlayer2 As Player

Public Sub New(deck As Deck, random As Random, player1 As Player, player2 As Player)
mDeck = deck
mRandom = random
mPlayer1 = player1
mPlayer2 = player2
End Sub

Public ReadOnly Property Deck As Deck
Get
Return mDeck
End Get
End Property

Public ReadOnly Property Random As Random
Get
Return mRandom
End Get
End Property

Public ReadOnly Property Player1 As Player
Get
Return mPlayer1
End Get
End Property

Public ReadOnly Property Player2 As Player
Get
Return mPlayer2
End Get
End Property

''' <summary>
''' Main control loop. You an imagine this routine to reflect the actions of the dealer, who
''' coordinates gameplay.
''' 主要控制回路。你可以想象这个程序反映了协调游戏玩法的商人的行动。
''' This simple game is played by two players.
''' The dealer is given a single fresh deck of cards, which he then shuffles.
''' Each player is dealt two cards.
''' Then each player takes their turn.
''' During their turn, a player can choose to "hit", which means they want to be dealt another card,
''' or to "stand", which means they will end their turn.
''' The player may "hit" as many times as they wish before ending their turn.
''' Once both players have taken their turn, the winner is determined.
''' The winner is the player with the greatest number of points without exceeding 21.
''' 这个简单的游戏是两个玩家玩的。发牌人拿到一副新的牌，然后洗牌。每人发两张牌。然后每个玩家轮流上场
'''在他们的回合中，玩家可以选择“hit”，这意味着他们想要发另一张牌，或者选择“stand”，这意味着他们将束他们的回合。
'''玩家可以在回合结束前按自己的意愿“命中”多少次。一旦两名选手轮流上场，胜者就已确定。得分最多且不超过21分者为胜者。
''' </summary>
''' <returns>Output from the game</returns>
Public Function Play() As String
Dim output As New StringBuilder()

' The game begins...
Dim hand1 = New Hand(mPlayer1)

Dim hand2 = New Hand(mPlayer2)

output.AppendFormat("{0} starts with {1}{2}", hand1.Player.Name, hand1.Description, Environment.NewLine)
output.AppendFormat("{0} starts with {1}{2}", hand2.Player.Name, hand2.Description, Environment.NewLine)

' Players take their turns
Dim hands As New List(Of Hand)({hand1, hand2})
For Each hand As Hand In hands
Dim name As String = hand.Player.Name
output.AppendFormat("{0}'s turn...{1}", name, Environment.NewLine)
While (hand.Player.WantsToHit(hand.TotalPoints))
Dim dealt As Card = mDeck.Deal()
output.AppendFormat("{0} hits: {1}{2}", name, dealt.Description, Environment.NewLine)
End While
If hand.TotalPoints > 21 Then
output.AppendFormat("{0} bursts.{1}", name, Environment.NewLine)
Else
output.AppendFormat("{0} stands.{1}", name, Environment.NewLine)
End If
Next

' Determine the winner
If hand1.Beats(hand2) Then
output.AppendFormat("{0} WINS!{1}", hand1.Player.Name, Environment.NewLine)
ElseIf hand2.Beats(hand1) Then
output.AppendFormat("{0} WINS!{1}", hand2.Player.Name, Environment.NewLine)
Else
output.AppendFormat("It's a DRAW!{0}", Environment.NewLine)
End If

Return output.ToString()
End Function
End Class
``````

'''
''' Represents the collection of cards that have been dealt to a player.
''' 表示已分发给玩家的纸牌集合
'''
Public Class Hand

``````''' <summary>
''' The cards currently held in this hand
''' </summary>
Private mCards As List(Of Card)

''' <summary>
''' The player to whom this hand belongs
''' </summary>
Private mPlayer As Player

''' <summary>
''' Constructs a new hand for the specified player.
''' </summary>
''' <param name="player">The player to whom this hand belongs</param>
Public Sub New(player As Player)
mPlayer = player
mCards = New List(Of Card)()
End Sub

''' <summary>
''' Returns the player that this hand belongs to.
''' </summary>
''' <returns>player</returns>
Public ReadOnly Property Player As Player
Get
Return mPlayer
End Get
End Property

''' <summary>
''' Adds a card to the hand
''' </summary>
''' <param name="card">the card to be added</param>
End Sub

''' <summary>
''' Returns the total points for this hand by adding up the points of each card.
''' </summary>
''' <returns>total points</returns>
Public ReadOnly Property TotalPoints As Integer
Get
Dim points As Integer = 0
For i As Integer = 0 To (mCards.Count - 1)
points += mCards(i).Points
Next
Return points
End Get
End Property

''' <summary>
''' Determines whether this hand is better than the other player's hand.
''' 确定这手牌是否比其他玩家的那手牌好
''' In general, the winning hand is the hand with the greatest number of points.
''' 一般来说，获胜的手是得分最多的手
''' But, if the hand exceeds 21 then it is a "bust" - the other player wins.
''' 但是，如果这只手超过21，那么它就是一个“半身像”——另一个玩家赢了
''' If both players bust, or if their total points are the same, then it is a draw.
''' 如果双方球员都失败了，或者他们的总得分相同，那么就是平局
''' </summary>
''' <param name="other">the hand to compare against</param>
''' <returns>True, if this is a better hand than the specified other hand</returns>
Public Function Beats(other As Hand) As Boolean
Dim myScore As Integer = TotalPoints
If myScore > 21 Then
Return False
End If

Dim otherScore As Integer = other.TotalPoints
If otherScore > myScore Then
Return False
End If

Return True
End Function

''' <summary>
''' Returns a formatted description of the cards in the hand, suitable for screen output
''' </summary>
''' <returns>Description of the hand</returns>
Public ReadOnly Property Description As String
Get
Dim desc As New StringBuilder()
desc.Append(mCards.Count)
desc.Append(" cards: ")
Dim first As Boolean = True
For Each card As Card In mCards
If Not first Then
desc.Append(", ")
End If
desc.Append(card.Description)
first = False
Next
desc.Append(".")
Return desc.ToString()
End Get
End Property
``````

End Class

``````''' <summary>
''' Represents a player, and their decision making logic for whether to
''' "hit" or "stand"
''' </summary>
Public Class Player

''' <summary>
''' A friendly name that identifies the player.
''' 一个识别玩家的友好名字
''' </summary>
Private mName As String

''' <summary>
''' Used for calculating probability of bust
''' 用于计算破产概率
''' </summary>
Private mProbabilityCalculator As ProbabilityCalculator

''' <summary>
''' Constructor.
''' </summary>
''' <param name="name">player's name</param>
''' <param name="probabilityCalculator">for calculating probability of bust</param>
''' <remarks></remarks>
Public Sub New(name As String, probabilityCalculator As ProbabilityCalculator)
mName = name
mProbabilityCalculator = probabilityCalculator
End Sub

''' <summary>
''' Returns the player's name
''' </summary>
''' <returns>name</returns>
Public ReadOnly Property Name As String
Get
Return mName
End Get
End Property

''' <summary>
''' Determines whether the player would like to "hit" (have another card dealt to their hand),
''' or "stand" (end their turn).
''' In this simple implementation, the player's strategy is to hit, so long as they are more likely
''' to increase their points than they are to bust.
''' If their current points are at 21, they should always stay
''' If their current points are at 10 or below, they can never bust, so they should always hit
''' For points in between, they will hit so long as the probability of the next draw causing a bust is less
''' than 50%.
''' 决定玩家是想“击中”(手上有另一张牌)，还是“站着”(结束他们的回合)。在这个简单的实现中，玩家的策略是命中，
''' 只要他们更有可能增加他们的点数而不是失败。如果他们现在的分数是21分，他们应该一直保持下去
'''如果他们现在的点数是10点或以下，他们永远不会破产，所以他们应该总是击中中间的点数，只要下一次平局导致破产的概率小于50%，他们就会击中。
''' </summary>
''' <param name="currentPoints">The total of the cards currently held by this player</param>
''' <remarks>True if the players wants to "hit", false if they want to "stand".</remarks>
Public Function WantsToHit(currentPoints As Integer)
If currentPoints >= 21 Then
Return False
ElseIf currentPoints <= 10 Then
Return True
Else
Return mProbabilityCalculator.CalculateProbability(currentPoints) < 0.5
End If
End Function

End Class
``````

'''
''' Used by the player's hit/stand decision making, to determine the probability of busting if
''' they deal another card.
''' 用于玩家的命中/立场决策，以确定如果他们发另一张牌失败的概率
'''
Public Class ProbabilityCalculator

``````''' <summary>
''' All possible cards
''' </summary>
Private mFullDeck As List(Of Card)

Public Sub New(fullDeck As List(Of Card))
mFullDeck = fullDeck
End Sub

''' <summary>
''' Determines the probability of a "bust" if the players deals one more card into their hand.
''' A "bust" occurs if the next card takes their total over 21.
''' 确定一个“破产”的概率，如果玩家在他们的手上多出一张牌。如果下一张牌的总数超过21，就会发生“破产”
'''
''' The probability is simulated as follows:
''' Consider all possible cards that might be dealt next if the player chooses to "hit".
''' (Since "card counting" is a no-no in Blackjack, we imagine that *any* of the cards from a
''' normal full deck could be dealt next. ie. don't exclude cards that have already been dealt).
''' For each possibility, determine the total points that would be achieved if that card
''' were added to the player's hand, and decide whether or not it is a bust.
''' The probability of a bust is the percentage of all the combinations tried that resulted in a bust.
''' 概率模拟如下: 考虑一下，如果玩家选择“命中”，接下来可能要发的所有牌。
'''(由于“数牌”在21点游戏中是不允许的，我们认为接下来可以处理任何一张普通的整副牌中的牌。ie。不要排除已经发过的牌)。
'''对于每一种可能性，确定如果那张牌可以获得的总积分   被添加到玩家手上，并决定它是否是一个破产。
'''破产的概率是所有尝试过的导致破产的组合的百分比。
'''
''' For example, if the player's current total is 12, then dealing any card worth 10 points
''' will result in a bust. Out of the 52 cards in a deck, 16 of them are worth 10 points (the Tens, Jacks,
''' Queens and Kings of Spades, Hearts, Clubs and Diamonds). So, the probability of a bust
''' is 0.3077 (30.77%), which is 16 divided by 52.
''' Similarly, if the player's current total is 19, there are 44 possible cards that will result in
''' a bust (everything except the aces and twos), and so the probability is 0.8462.
''' 例如，如果玩家当前的总数是12，那么处理任何值10分的牌 会导致破产。在一副牌中的52张牌中，16张值10分(10,j，黑桃、红桃、梅花和方块的王后和国王)。
''' 因此，破产的概率是0.3077(30.77%)也就是16除以52。同样的，如果玩家当前的总数是19，那么有44张可能导致失败的牌(除了a和2以外的所有牌)，所以概率是0.8462。
''' </summary>
''' <param name="currentPoints">the total points for the player's current hand</param>
''' <returns>probability of a bust if one more card is dealt (0.5f = 50%)</returns>
Public Function CalculateProbability(currentPoints As Integer) As Decimal
' Problem: we can't calculate the probability if we don't have any cards to simulate with.
' This shouldn't happen, but to prevent potential crashes, we'll return 50%
' 问题:如果我们没有卡片来模拟，我们就无法计算概率。这不应该发生，但为了防止潜在的崩溃，我们将返回50%

If mFullDeck.Count = 0 Then
Return 0.5
End If

Dim numberOfBusts As Integer = 0
Dim numberOfNonBusts As Integer = 0

For Each card As Card In mFullDeck
Dim potentialPoints = currentPoints + card.Points
If (potentialPoints > 21) Then
numberOfBusts += 1
Else
numberOfNonBusts += 1
End If
Next

Return numberOfBusts / CType(numberOfNonBusts, Decimal)

End Function
``````

End Class

``````
``````