-1

I have pored over C# documentation and I have not found an actual method called Shuffle(), but I wanted an implementation that at least came close to looking and feeling like a built-in method called Shuffle() and also I knew I was going to have to manually put together the actual logic to do the shuffling or randomizing.

This is where I learned about the Fisher-Yates approach and that lead me to this particular post with a solution that I favor by grenade:

Randomize a List<T>

Unfortunately, I cannot seem to get this solution working for me. No matter how I configure it, I always end up with this error:

error CS1501: No overload for method 'Shuffle' takes '0' arguments if I want to maintain the solution in this manner:

using System;
using System.Collections.Generic;

class Program
{
    public static void Main()
    {
        // List<Deck> deck = new List<Deck>();
        Deck deck = new Deck();
        deck.Shuffle();
        System.Console.WriteLine(deck);
    }
}

public class Deck {
   public List<Card> Cards = new List<Card>();

public Deck() {
  string[] ranks = { "Ace", "Two", "Three", "Four", "Five" };
  string[] suits = { "Diamonds", "Hearts", "Clubs", "Spades" };

  foreach (string suit in suits) {
      foreach (string rank in ranks) {
          Card card = new Card(rank, suit);
          Cards.Add(card);
      }
  }
}

  public override string ToString()
  {
      string s = "[";
      foreach (var card in Cards) {
        s += card.ToString() + ", ";
      }
      s += "]";
      return s;
  }

    private static Random rng = new Random();

    public static void Shuffle<T>(IList<T> list)  
    {  
        int n = list.Count;  
        while (n > 1) {  
            n--;  
            int k = rng.Next(n + 1);  
            T value = list[k];  
            list[k] = list[n];  
            list[n] = value;  
        }  
    }
}

public class Card {
    // properties
    public string suit { get; set; }
    public string rank { get; set; }

    public override string ToString()
    {
      return $"{rank} of {suit}"; 
    }

    public Card(string rank, string suit){
       //initializations
       this.rank = rank;
       this.suit = suit;
    }

}

It has the look and feel of working with a built-in method, but that error is telling me I need to pass something into Shuffle() because I declared it like so: public static void Shuffle<T>(IList<T> list), and no matter what I attempt to pass into it, it just leads to another error.

So then if I go with this one:

class Program
{
    public static void Main()
    {
        List<Deck> deck = new List<Deck>();
        // Deck deck = new Deck();
        deck.Shuffle();
        System.Console.WriteLine(deck);
    }
}

I am told Shuffle is not a method: error CS1061: TypeSystem.Collections.Generic.List' does not contain a definition for Shuffle' and no extension methodShuffle' of type System.Collections.Generic.List<Deck>' could be found.

and I know this, but then how is this working for grenade? What am I missing aside from some years of experience with this?

Bogdan Doicin
  • 2,342
  • 5
  • 25
  • 34
Daniel
  • 14,004
  • 16
  • 96
  • 156
  • 2
    The shuffle method is supposed to be an extension method. Move it into its own static class. You also need to put the `this` keyword back in. Have another look at the source answer. The method is static, inside a separate static class.. and it has the `this` keyword prepended to the input argument. – Simon Whitehead Jun 14 '19 at 15:44
  • Rather than random, you could try creating a new GUID for each and then sorting by guid. – AntDC Jun 14 '19 at 15:46
  • Particularly for a deck of cards, a `Queue` or `Stack` is a bit better choice than a List since they would "use up" the deck just like in real life. A list means you have to keep track of an index or also remove it manually from the deck. You can create one deck of cards as a list, shuffle it and create a deck (shoe) from that using the `Stack`. Reshuffling means just reusing the created deck rather than recreating it. – Ňɏssa Pøngjǣrdenlarp Jun 14 '19 at 15:55
  • Not answering the question, because the answer has already been provided, but I've seen a very neat way of shuffling of lists: `collection.OrderBy(x => Guid.NewGuid()).ToList()`. `Guid` could probably be replaced by `Random.Next()` for performance. – Dmitri Trofimov Jun 14 '19 at 15:56
  • 2
    @DmitriTrofimov It may look good, but it doesn't perform as well. See the anser to: [is using random and orderby a good shuffle algorithm](https://stackoverflow.com/questions/1287567/is-using-random-and-orderby-a-good-shuffle-algorithm) – Rufus L Jun 14 '19 at 16:03
  • 1
    Guids are a source of uniqueness, not randomness. They are different things. Don't use Guids for this. – Mike Jun 14 '19 at 16:07
  • FYI, your `ToString` method could be simplified to: `return $"[{string.Join(", ", Cards)}]";` – Rufus L Jun 14 '19 at 16:19
  • @RufusL, yes another colleague assisted me with that solution you just shared, but as a newbie I went with the one I have now because its easier for me to understand it if I go back and look at it a couple of weeks from now. – Daniel Jun 14 '19 at 16:22
  • 1
    Makes sense. Although the beauty of `string.Join` is that it doesn't leave a trailing `", "` at the end of the list of items. – Rufus L Jun 14 '19 at 16:25

2 Answers2

1

The prototype for Shuffle

public static void Shuffle<T>(IList<T> list)

is different from how you call in in main

deck.Shuffle();

This is why you get the CS1051 error. Fix that error first, then it will work for you aswell.

Bogdan Doicin
  • 2,342
  • 5
  • 25
  • 34
0

The solution you referred to was for an extension method for an object that implements IList. Extension methods must reside in a separate class, so all you need to do is add a new class in your namespace to hold the extension methods.:

public class Deck
{
    // Implementation omitted
}

public static class Extensions
{
    private static Random rng = new Random();  

    // This extension method is now available to any class that implements 'IList'
    public static void Shuffle<T>(this IList<T> list)  
    {  
        var currentIndex = list.Count;

        while (currentIndex > 1)
        {
            var swapIndex = rnd.Next(currentIndex--);

            var value = list[swapIndex];
            list[swapIndex] = list[currentIndex];
            list[currentIndex] = value;
        } 
    }
}

Now you can use this method in your Deck class, in the Shuffle method. Since Cards is a List<Card>, and List<T> implements IList, when you type Cards., Shuffle will now appear in the intellisense:

public class Deck 
{
    public void Shuffle() 
    { 
        Cards.Shuffle();  // This is using the extension method we created above
    }  

    // Rest of Deck class code omitted
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • Rufus, so defining this extension method on my `Deck` class was my first mistake, all extension methods have to be in their own class in C# – Daniel Jun 14 '19 at 16:18
  • 1
    Yeah, they have to be defined in a `static` class. And while the class *can* contain other, non-extension, `static` methods, typically they don't. – Rufus L Jun 14 '19 at 16:23
  • Also note the use of the `this` keyword, which defines the type that the extension applies to (and that argument represents the instance of that type that's calling the method). See [this page](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods) for more info. – Rufus L Jun 14 '19 at 16:28