16

我正在尝试为一个列出一副纸牌内容的项目编写代码,询问该人想要洗牌多少次,然后将它们洗牌。它必须使用一种方法来使用 System.Random 类创建两个随机整数。

这些是我的课:

程序.cs:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Deck mydeck = new Deck();
            foreach (Card c in mydeck.Cards)
            {
                Console.WriteLine(c);
            }
            Console.WriteLine("How Many Times Do You Want To Shuffle?");

        }
    }
}

甲板.cs:

namespace ConsoleApplication1
{
    class Deck
    {    
        Card[] cards = new Card[52];
        string[] numbers = new string[] { "2", "3", "4", "5", "6", "7", "8", "9", "J", "Q", "K" };
        public Deck()
        {
            int i = 0;
            foreach(string s in numbers)
            {
                cards[i] = new Card(Suits.Clubs, s);
                i++;

            }
            foreach (string s in numbers)
            {
                cards[i] = new Card(Suits.Spades, s);
                i++;

            }
            foreach (string s in numbers)
            {
                cards[i] = new Card(Suits.Hearts, s);
                i++;

            }
            foreach (string s in numbers)
            {
                cards[i] = new Card(Suits.Diamonds, s);
                i++;

            }
        }

        public Card[] Cards
        {
            get
            {
                return cards;


            }
        }
    }  
}

枚举.cs:

namespace ConsoleApplication1
{        
    enum Suits 
    {
        Hearts,
        Diamonds,
        Spades,
        Clubs
    }
}

卡片.cs:

namespace ConsoleApplication1
{
    class Card
    {
        protected Suits suit;
        protected string cardvalue;
        public Card()
        {
        }
        public Card(Suits suit2, string cardvalue2)
        {
            suit = suit2;
            cardvalue = cardvalue2;
        }
        public override string ToString()
        {
            return string.Format("{0} of {1}", cardvalue, suit);
        }
    }
 }

请告诉我如何让卡片随机洗牌,然后列出洗好的牌。

4

9 回答 9

52

使用Fisher-Yates shuffle

您的 C# 代码应如下所示:

static public class FisherYates
{
    static Random r = new Random();
    //  Based on Java code from wikipedia:
    //  http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
    static public void Shuffle(int[] deck)
    {
        for (int n = deck.Length - 1; n > 0; --n)
        {
            int k = r.Next(n+1);
            int temp = deck[n];
            deck[n] = deck[k];
            deck[k] = temp;
        }
    }
}
于 2009-07-19T19:41:14.767 回答
17

洗牌一开始似乎微不足道,但通常大多数人提出的算法是不正确的。

Jeff Atwood ( Coding Horror ) 写了几篇关于这个主题的非常好的文章:

http://www.codinghorror.com/blog/archives/001008.html

http://www.codinghorror.com/blog/archives/001015.html

(尤其是第二本必读)

于 2009-07-19T19:29:33.510 回答
5

我认为这是一个你可能过于沉迷于抽象的情况。

在软件中洗牌一副牌是以随机顺序向用户提供一副牌的问题。这实际上并不需要你提前洗牌。

初始化你的牌组。(我通常使用 1 到 52 之间的数字来表示卡片,并从数学上计算出哪张卡片是。)

  1. 通过使用随机数生成器从可用牌组中挑选一张牌来发牌。
  2. 将该牌与牌库末端的那张交换。
  3. 减少指向牌组末端的计数器,以从牌组中移除该牌。
  4. 转到第 1 步,直到您完成抽牌。

编辑:一般来说,如果你有一个好的随机数生成器,多次“洗牌”它不会获得任何好处。

使用您显示的数据结构应该可以做到这一点。您只需要添加一个“Draw”方法和成员变量来跟踪牌组的结束。如果您一心想提前实际进行“洗牌”,那么您的教授A就是个混蛋,B只要您抽到52张牌,就会洗牌。抽完所有牌后,您需要提供“DeckEmpty”方法,以及重置牌组结束以重新包含所有牌的方法。

于 2009-07-19T19:58:57.820 回答
3

要正确洗牌,你不应该只使用 Random 类,种子只有 2^32,这意味着你的 Random 对象只能给你 2^32(假设)不同的顺序,其中有 52!(factorial 52) 现实生活中甲板的方法。

我正在使用 2 个 guid 创建 32 字节的随机数据-> 8 个 4 字节的种子,我用 8 个不同的种子洗牌

然后通过种子我得到一定数量的卡片 [5,5,6,6,6,7,8,9]

这是我使用的代码

    public void Shuffle(Guid guid1, Guid guid2)
    {
        int[] cardsToGet = new int[] { 5, 5, 6, 6, 6, 7, 8, 9 };
        byte[] b1 = guid1.ToByteArray();
        byte[] b2 = guid2.ToByteArray();

        byte[] all = new byte[b1.Length + b2.Length];
        Array.Copy(b1, all, b1.Length);
        Array.Copy(b2, 0, all, b1.Length, b2.Length);

        List<Card> cards = new List<Card>(this);
        Clear();

        for (int c = 0; c < cardsToGet.Length; c++)
        {
            int seed = BitConverter.ToInt32(all, c * 4);
            Random random = new Random(seed);
            for (int d = 0; d < cardsToGet[c]; d++)
            {
                int index = random.Next(cards.Count);
                Add(cards[index]);
                cards.RemoveAt(index);
            }
        }
    }
于 2013-07-16T10:07:04.460 回答
2

您的 Shuffle 可能会起作用,但它不是真正有效且不逼真。你应该这样尝试:

//The shuffle goes like this: you take a portion of the deck, then put them in random places
private void Shuffle()
{
 int length = DeckofCards.Count;
 int level = 20; //number of shuffle iterations

 List<Card> Shuffleing; //the part of the deck were putting back
 Random rnd = new Random();
 int PickedCount, BackPortion; //the last used random number

 for (int _i = 0; _i < level; _i++)
 {
  PickedCount = rnd.Next(10, 30); //number of cards we pick out
  Shuffleing = DeckofCards.GetRange(0, PickedCount);
  DeckofCards.RemoveRange(0, PickedCount);

  while (Shuffleing.Count != 0)
  {
   PickedCount = rnd.Next(10, DeckofCards.Count - 1); //where we place a range of cards
   BackPortion = rnd.Next(1, Shuffleing.Count / 3 + 1); //the number of cards we but back in one step
   DeckofCards.InsertRange(PickedCount, Shuffleing.GetRange(0, BackPortion)); //instering a range of cards
   Shuffleing.RemoveRange(0, BackPortion); //we remove what we just placed back
  }
 }
}

这样,您可能会以更少的迭代获得更逼真的随机播放

于 2012-11-04T19:44:02.993 回答
0

总的来说,我会说将每个卡片组视为一个包含一组 Card 对象的对象,每个 Card 对象都包含一个 value 和 suite int 属性,可以将其应用于值和套件的枚举以收集命名版本为根据您使用的甲板类型。(这将使这段代码更加通用,并且更容易进行值比较 3 < 11 (jack) !~)你的风格适用于学校项目,我只是用它来强迫症!

class Card
{
    public int value
    { get; set; }

    public int suite
    { get; set; }
}


abstract class Deck
{
    public Card[] cards
    { get; set; }

    public void ShuffleCards(int timesToShuffle)
    {
        Card temp;
        Random random = new Random();
         // int timesToShuffle = random.Next(300, 600); #Had it setup for random shuffle
        int cardToShuffle1, cardToShuffle2; 

        for (int x = 0; x < timesToShuffle; x++)
        {
            cardToShuffle1 = random.Next(this.cards.Length);
            cardToShuffle2 = random.Next(this.cards.Length);
            temp = this.cards[cardToShuffle1];

            this.cards[cardToShuffle1] = this.cards[cardToShuffle2];
            this.cards[cardToShuffle2] = temp;
        }
    }
}

这是假设您使用了一个基本的 Deck 类,然后将其继承到您想要的甲板类型(这样您就可以将相同的代码应用到 Uno 甲板或其他任何东西。)普通类型甲板类的代码。

class NormalDeck : Deck
{
    // This would go in the NormalGame class to apply the enumerators to the values as a cipher.
    // Need int values for logic reasons (easier to work with numbers than J or K !!!
    // Also allows for most other methods to work with other deck<Type> (ex: Uno, Go Fish, Normal cards)
    public enum Suites
    {
        Hearts,
        Diamonds,
        Spades,
        Clover
    };

    // Same comment as above. 
    public enum Values
    { Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King };

    public void NewNormalDeck()
    {
        // Clear the deck of cards
        if (this.cards != null)
        {
            Array.Clear(this.cards, 0, this.cards.Length);
        }

        //Set Value to length of Normal deck of Cards without Jokers 
        cards = new Card[52];

        // to keep count of which card we are.  
        int curNumofCards = 0;

        // Cycle through all of the suites listed in "suites" then all the values of     that suite
        for (int x = 0; x < Enum.GetValues(typeof(Suites)).GetLength(0); x++)
        {
            for (int y = 0; y < Enum.GetValues(typeof(Values)).GetLength(0); y++)
            {
                Card newCard = new Card();
                newCard.suite = x;
                newCard.value = y;
                this.cards[curNumofCards] = newCard;
                curNumofCards++;
            }
        }
    }
}
于 2014-02-23T05:47:58.897 回答
0

在您的卡片类中使用枚举。

public class Card
{
    public Suit Suit { get; set; }
    public CardValue CardValue { get; set; }
}

public enum CardValue 
{
    Ace = 1,
    Two = 2,
    Three = 3,
    Four = 4,
    Five = 5,
    Six = 6,
    Seven = 7,
    Eight = 8,
    Nine = 9,
    Ten = 10,
    Jack = 11,
    Queen = 12,
    King = 13
}

public enum Suit
{ 
    Clubs,
    Hearts,
    Spades,
    Dimonds
}

一旦你有了它,你就可以创建一个 PlayersHand 类

public class PlayersHand
{
    public List<Card> Cards { get; set; }
}

现在您已经创建了 Enum 并支持了两个类,只需从牌组中为每个玩家提供一只手,这只是一张卡片列表。

List<Card> deck = new List<Card>();

        // 52 cards in a deck, 13 cards per suit.
        foreach (Suit suit in Enum.GetValues(typeof(Suit))) 
        {
            foreach (CardValue cardValue in Enum.GetValues(typeof(CardValue))) 
            {
                Card card = new Card
                {
                    Suit = suit,
                    CardValue = cardValue
                };
                deck.Add(card);
            }
        }

// Shuffle using random replace.
        for (var i = deck.Count-1; i > 0; i--)
        {
            Random rnd = new Random();
            var j = rnd.Next(0, i);
            var temp = deck[i];
            deck[i] = deck[j];
            deck[j] = temp;
        }
            
        // THESE ARE THE SETTINGS
        int NumberOfPlayers = 4;
        int NumberOfCardsPerPlayer = 5;

        // NEW UP A LIST OF PLAYER HANDS
        List<PlayersHand> hands = new List<PlayersHand>();
        for (var i = 1; i <= NumberOfPlayers; i++)
        {
            PlayersHand newHand = new PlayersHand()
            {
                Cards = new List<Card>()
            };
            hands.Add(newHand);
        }

        // HERE IS THE TRICK TO DEALING OUT THE TOP CARD
        int deal = 0;

        // GOING AROUND THE TABLE 5 TIMES BECAUSE EACH PLAYER HAS 5 CARDS
        for (var i = 0; i < NumberOfCardsPerPlayer ; i++)
        {
            // FOR EACH PLAYER GET THE TOP CARD USING THE 'DEAL' INT
            for (var p = 0; p < NumberOfPlayers; p++) 
            {
                var player = hands[p];
                player.Cards.Add(deck[deal]);
                // INCREMENT DEAL
                deal++;
            }
        }

这种方法对我有用。

于 2021-09-30T00:52:37.603 回答
-1

改组应该以这种方式工作:

你在牌组中随机抽取两张牌(牌组中牌的索引是随机数)并交换两张牌的位置。例如,在索引 2 处获取卡片,在索引 9 处获取卡片并让它们更换位置。

并且可以重复一定次数。

该算法应如下所示:

int firstNum = rnd.Next(52);
int secondNum = rnd.Next(52);

Card tempCard = MyCards[firstNum];
MyCards[firstNum] = MyCards[secondNum];
MyCards[secondNum] = tempCard;
于 2009-07-21T18:52:48.003 回答
-1
static void Shuffle(List<int> cards)
    {
        Console.WriteLine("");
        Console.WriteLine("Shuffling");
        Console.WriteLine("---------");

        cards = cards.OrderBy(x => Guid.NewGuid()).ToList();

        foreach (var card in cards)
        {
            Console.WriteLine(card.ToString());
        }
    }
于 2018-07-25T14:53:52.973 回答