1

a programming beginner here! I was challenging myself to create a drawing game(card drawing or in this case number drawing) in order to sharpen my coding skills, but it seemed a little too challenging for me as I am just a beginner.

The program I'm making is actually very simple. You have a deck of 15 cards(or in this case numbers) and the program asks you to draw 5 cards. While I am able to do this, I noticed there would sometimes be duplicates of a card you already drew, and as for any normal deck there would only be 1 copy of each card.

As for my code it's fairly short:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CardGame
{
    class Program
    {
        static void Main(string[] args)
        {
            string ans = " ";
            int[] Deck = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; // the deck

            Console.WriteLine("Draw 5 cards? Y/N");


            while (!ans.Equals("N"))
            {

                ans = Console.ReadLine();

                if (ans.Equals("Y"))
                {
                    Draw(Deck);
                }

                else
                {
                    Console.WriteLine("Closing program");
                }
            }
        }

        static void Draw(int[] Deck) //Draw function
        {
            Random rand = new Random(); // randomizer
            int turncount = 1;

            while (turncount <= 5)
            {
                int pick = rand.Next(Deck.Count()); // random pick for each iteration
                Console.WriteLine(Deck[pick]);
                turncount++;
            }


        }


    }
}

So my expected output would be something like: 5,8,1,4,12 | 2,3,9,11,7 | 10,6,15,13,14

But what I did was spitting out something like: 1,5,5,3,7 | 15,12,1,15,3| 2,3,1,5,6

and I also an added challenge I wanted the deck to reset once you finish your 3 draws. So after 3 draws you can draw all of the cards again. But I have no idea on how i could do that at all.

As for the various methods that I tried, I tried implementing the Distinct function, so i could eliminate duplicates but, it was not really the result I was looking for as it would call my the elements in order like: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15.

That about sums up my question. Hope you all could help this beginner and enlighten me!

Holden
  • 17
  • 6
  • basically your Random instance should not be in the Draw function you can read more about it [here](https://stackoverflow.com/questions/5264412/why-does-random-next-always-return-the-same-number) – styx Nov 25 '19 at 13:00
  • You never remove the cards already drawn from your deck. Either remove the drawn cards from your deck or add a condition to your while loop that checks if the card is already drawn so it can draw a new one. – lordvlad30 Nov 25 '19 at 13:04

5 Answers5

2

The issue is the random number generator can generate the same number 2, 3 or more times in a row. The two obvious ways to get around this is to either remove drawn cards from the Deck or to simply check you haven't drawn the same card twice.

The method I've put together below simply checks its not a card that has already been drawn. Removing the cards each time would be more efficient however.

        static void Draw(int[] Deck) //Draw function
        {
            Random rand = new Random(); // randomizer

            List<int> drawnCards = new List<int>();
            while (drawnCards.Count < 5)
            {
                int pick = rand.Next(Deck.Count());
                int card = Deck[pick];

                // Check card has not already been drawn
                if(!drawnCards.Contains(card))
                {
                    drawnCards.Add(card);
                }
            }

            foreach(int card in drawnCards)
            {
                Console.WriteLine(card);
            }
        }
Ben
  • 21
  • 1
1

I noticed there would sometimes be duplicates of a card you already drew

Of course, because you never "take" the cards from your deck. You ask repeatedly for a random number between 0 and 14, so it's perfectly possible for that to return the same number multiple times.

A solution could be to shuffle the array, and keep a counter of the current position:

static int[] GetShuffledDeck()
{
    int[] deck = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

    var rnd = new Random();
    var shuffledDeck = deck.OrderBy(x => rnd.Next()).ToArray();

    return shuffledDeck;
}

static void Main(string[] args)
{   
    var shuffledDeck = GetShuffledDeck();

    Console.WriteLine("Draw 5 cards? Y/N");

    string ans = "";
    int counter = 0;

    while (!ans.Equals("N"))
    {
        ans = Console.ReadLine();

        if (ans.Equals("Y"))
        {
            Draw(shuffledDeck, ref counter);

            if (counter == 15)
            {
                // re-shuffle deck
            }
        }
        else
        {
            Console.WriteLine("Closing program");
        }
    }
}

static void Draw(int[] deck, ref int counter)
{
    int turncount = 0;
    while (turncount < 5)
    {
        int pick = counter++;
        Console.WriteLine(Deck[pick]);
        turncount++;
    }
}
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Works like a charm! I didn't even know var shuffledDeck = deck.OrderBy(x => rnd.Next()).ToArray(); was a thing – Holden Nov 25 '19 at 13:27
  • but it has an out of bounds error on the 4th draw, is what i noticed. – Holden Nov 25 '19 at 13:27
  • Yes, that's because I left the shuffling and resetting of the counter as an exercise for you, as this smells like homework. – CodeCaster Nov 25 '19 at 13:28
  • ah gotcha, and this isn't homework btw(haha) just a little coding challenge i thought of, thanks for the help anyways ill be sure to complete this on my own, thanks again! – Holden Nov 25 '19 at 13:31
1

Slightly modified your code:

One possible solution is to remove the cards from your deck once they are drawn. This simulates a real card deck scenario. Also when they are finished you need to reinit the deck itself.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CardGame
{
    class Program
    {
        static List<int> cleanDeck = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; // the deck
        static void Main(string[] args)
        {
            string ans = " ";
            List<int> Deck = cleanDeck;

            Console.WriteLine("Draw 5 cards? Y/N");


            while (!ans.Equals("N"))
            {

                ans = Console.ReadLine();

                if (ans.Equals("Y"))
                {
                    Draw(Deck);
                }

                else
                {
                    Console.WriteLine("Closing program");
                }
            }
        }

        static void Draw(int[] Deck) //Draw function
        {
            Random rand = new Random(); // randomizer
            int turncount = 1;
            if (Deck.Count() == 0) Deck = cleanDeck; // reinit the deck if empty
            while (turncount <= 5)
            {
                int pick = rand.Next(Deck.Count()); // random pick for each iteration
                Console.WriteLine(Deck[pick]);
                Deck.removeAt(pick); // remove element
                turncount++;
            }
        }
    }
}
farbiondriven
  • 2,450
  • 2
  • 15
  • 31
1

In another post, I answered creating a full standard deck of cards.

When trying to display a hand of cards from a shuffled deck, it fills every hand with the Ace of Clubs

The two important things (IMO) I tried to do was create a master list of all the cards. Then, during shuffling them, on each card, I have a place-holder for a random number. I assign all cards a random number and return the deck sorted by that random number. So at the end, I have a full deck once and never have to ask for the next number, just deal the next card, next card, etc. After each reshuffle, it starts with the master deck and just keeps reassigning new randoms and returns sorted deck. Might be a jump start for you while learning too.

DRapp
  • 47,638
  • 12
  • 72
  • 142
1

When you draw a card form Deck you should Remove the card taken; since we want to modify Deck let it be List<T>, not an array T[]:

//DONE: do not re-create Random
static Random rand = new Random();

// It's quite natural to return the card taken (that's why we return something, not void)  
// I guess I'll want to return a card, not int; let's add generic T for this
static T Draw(List<T> Deck) {
  if (Deck.Count <= 0) 
    return 0; // or throw exception

  int index = rand.Next(0, Deck.Count);

  T card = Deck[index];

  Deck.RemoveAt(index); // <- this card is taken from the Deck 

  return Card; 
}

Now, let's use it

  private static List<int> CreateDeck() {
    return new List<int>() { 
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };   
  }

  private void Main(string[] args) {
    // Deck to draw cards from  
    List<int> Deck = CreateDeck(); 

    // how many cards to draw
    int cardsToTake = 5;

    //Keep asking user while he/she enters "Y"
    while (true) {
      Console.WriteLine($"Draw {cardsToTake} cards? Y/N");

      // Trim().Upper() - let's be nice and tolerate spaces, e.g. "N  ", cases i.e. "n"
      if (Console.ReadLine().Trim().ToUpper() != "Y") 
        break;

      // Do we have enough cards? If not, recreate the Deck
      if (Deck.Count < cardsToTake) 
        Deck = CreateDeck();  

      // Let's take cards (cardsToTake of them):
      int[] takenCards = new int[cardsToTake];

      for (int i = 0; i < takenCards.Length; ++i) 
        takenCards[i] = Draw(Deck);

      // Time to print the cards:
      Console.WriteLine(string.Join(" | ", takenCards));
    }

    Console.WriteLine("Closing program");
 }
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215