Перестановка карт в C # - PullRequest
13 голосов
/ 19 июля 2009

Я пытаюсь написать код для проекта, который перечисляет содержимое колоды карт, спрашивает, сколько раз человек хочет перетасовать колоду, а затем тасует их. Он должен использовать метод для создания двух случайных целых чисел, используя класс System.Random.

Это мои занятия:

Program.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?");

        }
    }
}

Deck.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;


            }
        }
    }  
}

Enums.cs:

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

Card.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);
        }
    }
 }

Пожалуйста, скажите мне, как сделать так, чтобы карты перемешивались столько, сколько хочет человек, а затем перечислите перемешанные карты.

Ответы [ 8 ]

48 голосов
/ 19 июля 2009

Использование 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;
        }
    }
}
17 голосов
/ 19 июля 2009

Перетасовка колоды карт поначалу кажется тривиальной, но обычно алгоритм, который придумывает большинство людей, неверен.

Джефф Этвуд ( Coding Horror ) написал несколько очень хороших статей на эту тему:

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

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

(особенно второй, который нужно прочитать)

5 голосов
/ 19 июля 2009

Я думаю, что это тот случай, когда вы просто слишком увлекаетесь абстракцией.

Перетасовка колоды карт в программном обеспечении - это вопрос предоставления колоды пользователю в случайном порядке. На самом деле это не требует, чтобы вы перетасовали их заранее.

Инициируйте свою колоду. (Я обычно использую число от 1 до 52 для представления карты и математически вычисляю, какая карта.)

  1. Сдайте карту, используя генератор случайных чисел, чтобы выбрать карту из колоды доступных карт.
  2. Поменяйте эту карту на карту в конце колоды.
  3. Уменьшите счетчик, указывающий на конец колоды, чтобы удалить эту карту из колоды.
  4. Переходите к шагу 1, пока не закончите рисовать карты.

Редактировать : И вообще, если у вас есть хороший генератор случайных чисел, ничего не получится, если его "перемешать" несколько раз.

Это должно быть возможно при использовании структур данных, которые вы показали. Вам просто нужно добавить метод Draw и переменную-член, чтобы отслеживать конец колоды. Если вы чертовски настроены на то, чтобы на самом деле выполнить «перемешивание» раньше времени, то A ваш профессор - придурок, B каждый раз, когда вы берете 52 карты, колода будет перетасовываться. После того, как вы вытянули все карты, вам нужно предоставить метод «DeckEmpty» и метод для сброса конца колоды, чтобы снова включить все карты.

3 голосов
/ 16 июля 2013

для правильного перетасования колоды, вы НЕ должны использовать ТОЛЬКО класс Random, начальное число составляет всего 2 ^ 32, что означает, что ваш случайный объект может дать вам только 2 ^ 32 (предполагаемый) другой порядок, где 52! (факториал 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);
            }
        }
    }
2 голосов
/ 04 ноября 2012

Ваш 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
  }
 }
}

Таким образом, вы можете получить более реалистичное перемешивание с меньшим количеством итераций

0 голосов
/ 25 июля 2018
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());
        }
    }
0 голосов
/ 23 февраля 2014

В целом, я бы сказал, что каждая колода рассматривается как объект, который содержит массив объектов Card, каждый из которых содержит каждый объект Card value и свойство int int, которые можно применять к Enum значений и наборов для сбора названная версия согласно типу колоды, которую вы используете. (Это позволит сделать этот фрагмент кода более универсальным и упростить сравнение значений. 3 <11 ​​(jack)! ~) Ваш стиль будет работать для школьного проекта, я просто получаю OCD с ним! </p>

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++;
            }
        }
    }
}
0 голосов
/ 21 июля 2009

Перестановка должна работать следующим образом:

Вы берете две случайные карты в колоде (индекс карты в колоде - случайные числа) И поменяйте местами две карты. Например, возьмите карточку с индексом 2 и карточку с индексом 9 и попросите их поменять место.

И это может повторяться определенное количество раз.

Алгоритм должен выглядеть примерно так:

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

Card tempCard = MyCards[firstNum];
MyCards[firstNum] = MyCards[secondNum];
MyCards[secondNum] = tempCard;
...