1

I have files on my computer containing all cards within a deck. What I want to do is generate all 54 numbers and give 7 of them to the user in the form of buttons so they can play these cards, another 7 cards to the computer, and the remaining cards stored in a queue for either of them to press to get more if needed. But the error I am facing is generating the 7 random numbers that need to be stored for the user to get.

This is the code I am referring to:

I believe this part of the code calls the SetButtonImage to begin to compile on load.

public FmClassic()
        {
            InitializeComponent();

            SetButtonImage();
        }

This is SetButtonImage gathering the images from within my computer as well as calling another function to generate the random numbers.

private void SetButtonImage()
        {
            Stack<int> ShuffleOnTime = ShuffleOneTime();
            string Cards = String.Join("\n", ShuffleOnTime);
            button1.Image = Image.FromFile($"C:\\filepath\\{Cards.Substring(0, Cards.IndexOf(Environment.NewLine))}.png");
            button2.Image = Image.FromFile($"C:\\filepath\\{Cards.Substring(1, Cards.IndexOf(Environment.NewLine))}.png");
            button3.Image = Image.FromFile($"C:\\filepath\\{Cards.Substring(2, Cards.IndexOf(Environment.NewLine))}.png");
            button4.Image = Image.FromFile($"C:\\filepath\\{Cards.Substring(3, Cards.IndexOf(Environment.NewLine))}.png");
            button5.Image = Image.FromFile($"C:\\filepath\\{Cards.Substring(4, Cards.IndexOf(Environment.NewLine))}.png");
            button6.Image = Image.FromFile($"C:\\filepath\\{Cards.Substring(5, Cards.IndexOf(Environment.NewLine))}.png");
            button7.Image = Image.FromFile($"C:\\filepath\\{Cards.Substring(6, Cards.IndexOf(Environment.NewLine))}.png");

        }

This is then called to ensure they are random and not called twice with the hash table as well as converting to a string which I think is better for when searching for files.

public Stack<int> ShuffleOneTime()
        {
            Random random = new Random();
            Stack<int> generatednumbers = new Stack<int>();
            Stack<int> randomNumbers = GenerateRandomNumber(random, generatednumbers);

            return randomNumbers;
        }

This helps generate the numbers and return it back to the other function.

Stack<int> GenerateRandomNumber(Random random, Stack<int> generatednumbers)
        {
            //Queue<int> Cards = new Queue<int>();
            //Queue<string> realNumbers = new Queue<string>();
            int randomNumber;
            do
            {
                randomNumber = random.Next(1, 54);
                generatednumbers.Push(randomNumber);
                if (generatednumbers.Count >= 2)
                {
                    int Compare1 = generatednumbers.Pop();
                    int Compare2 = generatednumbers.Pop();
                    if (Compare1 == Compare2)
                    {
                        generatednumbers.Push(Compare1);
                    }
                }

            } while (generatednumbers.Count != 53);
            //string number = generatednumbers.ToString();
            //realNumbers.Enqueue(number);
            return generatednumbers;
        }

I have tried making it a string rather than an integer to help find it but it won't work. The filenames for the cards are all 1.jpg, 2.jpg etc so I don't know why there is an error. Everything compiles but then my screen freezes for 60 seconds and them inputs this error:

Managed Debugging Assistant 'ContextSwitchDeadlock' : 'The CLR has been unable to transition from COM context 0xa73c68 to COM context 0xa73bb0 for 60 seconds.

  • Answered here for Visual Studio 2019. https://stackoverflow.com/a/13586532/3585500 – ourmandave Jul 17 '23 at 11:46
  • Done that now my code is just frozen. – contingiouscrayon Jul 17 '23 at 11:55
  • Use the [debugger](https://learn.microsoft.com/en-us/visualstudio/get-started/csharp/tutorial-debugger?view=vs-2022) to find out what's happening. Your app freezes because the `do ... while` loop doesn't break when the `generatednumbers` contains all the `1-53` numbers. Also note: 1) `HashSet` does not allow duplicate elements, its `.Add` method returns `false` if an element is already in the collection. 2) Don't create the `Random` in methods, declare and instantiate it as a _class_ field. 3) What is the use of `List numbers = new List();` ? Nothing in that block scope. – dr.null Jul 17 '23 at 17:26
  • 4) **DO NOT** follow what the link in the first comment suggests. Exception must not be ignored nor neglected. You MUST catch and handle them. – dr.null Jul 17 '23 at 17:31
  • I have just tried that and you were right so thank you! But the problem I am trying to resolve is that the buttons do not output the image I want to display on them, do you know how I would do that? – contingiouscrayon Jul 18 '23 at 14:17

1 Answers1

1

So, you have 53 images in some folder, and you want to shuffle and pick. You can simplify this task since the file names are the numbers range that you want to generate and shuffle.

Create a method that generates shuffled numbers of a given range.

private readonly Random rnd = new Random();

// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
private int[] GenerateShuffledNumbers(int start, int count)
{
    var numbers = Enumerable.Range(start, count).ToArray();

    for (int i = 0; i < numbers.Length; i++)
    {
        var j = i + (int)(rnd.NextDouble() * (numbers.Length - i));
        (numbers[i], numbers[j]) = (numbers[j], numbers[i]);
    }

    return numbers;
}

All what you need to have in the SetButtonImage method is:

private void SetButtonImage()
{
    var dir = @"The Images Directory Path";
    var buttons = new[]
    {
        button1, button2, button3, button4,
        button5, button6, button7
    };
    var numbers = GenerateShuffledNumbers(1, 53);

    for (int i = 0; i < buttons.Length; i++)
    {
        // jpg or png? Append the right extension.
        var imgFile = Path.Combine(dir, $"{numbers[i]}.jpg");
        buttons[i].Image?.Dispose();
        buttons[i].Image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imgFile)), true, false);
    }
}

Note, the numbers array contains all the 53 numbers, you can take a range:

var someNumbers = numbers.Take(7);

... you can skip and take:

// Take the first 7...
var set1 = numbers.Take(7);
// Take the next 7...
var set2 = numbers.Skip(7).Take(7);
// Skip the first 14 and take the rest...
var set3 = numbers.Skip(14);

All of which return IEnumerable<int>. Call .ToList() or .ToArray() if needed.

If you need to create a Stack<int> or Queue<int>, both have a constructor overload that takes IEnumerable<T>.

var stack = new Stack<int>(numbers);
var queue = new Queue<int>(numbers);
dr.null
  • 4,032
  • 3
  • 9
  • 12
  • Thank you I appreciate it a lot! I was wondering do you know if there is any way to store those numbers inside of a queue? Only because I dont want to be reshuffling every time I call the form and it's needed for the project I am making. The var stacks and queues gave me the IEnumerable error even when I tried to convert to array. Thank you again for the help I really needed it. – contingiouscrayon Jul 20 '23 at 12:38
  • @contingiouscrayon Glad to help. To keep the shuffled numbers, the easiest way is an entry in the project's setting. `Project` -> `YourProject Properties` -> `Settings`. Add and name a new property and follow [this](https://stackoverflow.com/a/4267845/14171304) trick since the list of the types does not contain the type `int[]` by default. Then you can access the property through `Properties.Settings.Default.WhatEverTheNameIs`. Make sure to call `Properties.Settings.Default.Save()` method in the `FormClosing` event. As for the error, what is the exception message? – dr.null Jul 20 '23 at 19:03
  • Sorry for the slow reply, I have done the properties thing and it worked so thank you again! The error is "CS1503: Argument 1: cannot convert. from 'System.Collections.Generic.IEnumerable'' to 'int'" – contingiouscrayon Jul 23 '23 at 11:48
  • @contingiouscrayon You need to pass `int[]` array or `List` or `IEnumerable` to a `Queue` or `Stack` constructor as the type of the `numbers` variable in the code snippet above. None of which provides a constructor that takes `int`. Use their methods (Enqueue/Push) to add items or create arrays in their constructors to pass an `int` value. i.e. `var stack = new Stack(new[] { intValueOrVariable });` – dr.null Jul 23 '23 at 23:00