Click here to Skip to main content
15,903,744 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
I'm trying to develop a game as an exercise.

On runtime, I need the program to start with a total of 63 buttons (with the generated problem as a priority), randomly arranged.

So far, with the code I'm trying, I get a StackOverflowException.

Here are some bits of my code:

private void TimedMode_Load(object sender, EventArgs e)
{
    totalButtons = 0;
    nTotal = 63;

    while (totalButtons < nTotal)
    {
        createRandom();
    }

    tmr3sec.Enabled = true;
    tmr3sec.Start();
}

C#
void createRandom()
        {
            //nProb initialized to 0
            //problem initialized to ""
            nProb = Convert.ToInt32(rnd_nProb.Next(7));
            problem = items[Convert.ToInt32(rnd_prob.Next(1))];

            createProblem();
        }

        void setItems(int val)
        {
            switch (problem)
            {
                case "Donut":
                    donutCount += val;
                    break;

                case "Pencil":
                    pencilCount += val;
                    break;
            }
        }
        void createProblem()
        {
            //check if creating the new buttons will
            //result in an excess number of buttons
            if ((totalButtons + nProb) <= nTotal)      
                                                     
            {
                Random random = new Random();

                switch (problem)
                {
                    case "Donut":
                        donut[0] = Image.FromFile(GUILoc + "ChocoDonut_Hori.png");
                        donut[1] = Image.FromFile(GUILoc + "ChocoDonut_Verti.png");
                        donut[2] = Image.FromFile(GUILoc + "PinkDonut_Hori.png");
                        donut[3] = Image.FromFile(GUILoc + "PinkDonut_Verti.png");

                        //set value of probCount to the value of donutCount 
                        //to check later if 
                        //there are enough donuts
                        probCount = donutCount;
                        returnPic = donut[Convert.ToInt32(random.Next(donut.Length - 1))];
                        break;


                    case "Pencil":
                        pencil[0] = Image.FromFile(GUILoc + "GreenPencil_Down.png");
                        pencil[1] = Image.FromFile(GUILoc + "GreenPencil_Up.png");
                        pencil[2] = Image.FromFile(GUILoc + "PinkPencil_Down.png");
                        pencil[3] = Image.FromFile(GUILoc + "PinkPencil_Up.png");
                        probCount = pencilCount;
                        returnPic = pencil[Convert.ToInt32(random.Next(pencil.Length - 1))];
                        break;

                }

                //If for example the problem is donut, the nProb is 5, 
                //but there are 6 donuts already in the panel, 
                //then just create a new problem
                //Note that I am still deliberating this part, 
                //maybe you can share your thoughts?
                if (nProb <= probCount)
                {
                    createRandom();
                }
 
                //If buttons are insufficient, and will not be excess, 
                //create the buttons
                else 
                {
                    createButton();
                    totalButtons += nProb;
                }


            }

            else //If there are excess buttons
            {
                //For example, totalButtons = 59. Random # is 6. 59 + 6 = 65
                //It should not exceed 63.
                int total2 = nTotal - totalButtons;
                //nProb = (63 - 59) = 4
                nProb = total2;
                totalButtons += nProb;
                setItems(nProb); //increase the count of the item
                createButton();

            }

            dispProb = problem;
            dispProbNo = nProb;

        }

void createButton()
 {

     int k = 0;

       while (k < nProb)
       {
         btn = new Button();

         setPic = returnPic;

         btn.Image = setPic;
         btn.Name = "btn_" + problem + k.ToString();
         btn.Tag = problem;
         Size size = new Size(70, 70);
         btn.FlatStyle = FlatStyle.Flat;
         btn.BackColor = Color.Transparent;
         btn.FlatAppearance.BorderSize = 0;
         btn.FlatAppearance.BorderColor = Color.FromArgb(0, 255, 255, 255);
         btn.FlatAppearance.MouseOverBackColor = Color.Transparent;
         btn.FlatAppearance.MouseDownBackColor = Color.Transparent;
         btn.Size = size;
         setLoc(btn);
         k++;

     }
 }


C#
        void setLoc(Button bttn)
        {
            //if there is no need to generate a new random location
            //if true, it means there is already a position
            //no need to generate a new number

            //cellPosition initialized to false
            if (cellPosition == false) 
            {
                createRandomPos();              
            }
        
            else
            {

            }

            if (panDoodles.GetControlFromPosition(randcol, randrow) == null) //if cell is available
            {
                cellPos = new TableLayoutPanelCellPosition(randcol, randrow);
                panDoodles.SetCellPosition(bttn, cellPos);
                cellPosition = false;
            }

            else //if cell is occupied
            {
                createRandomPos();
                setLoc(bttn);
            }
            
            panDoodles.Controls.Add(bttn);
        }


void createRandomPos()
{
    randrow = Convert.ToInt32(rand_row.Next(0, 9));
    randcol = Convert.ToInt32(rand_col.Next(0, 7));

}



Pardon the structure of my code! :) I'm still finding my way around C#.
I have a 7x9 grid, so that's a total of 63 buttons. A big number, I know, but that's why I came here to ask how I will be able to do that efficiently.


What would be the best approach to do this?
Posted
Updated 6-Sep-14 13:46pm
v8
Comments
George Jonsson 6-Sep-14 12:09pm    
You have a recursive call inside void setLoc(Button bttn). That is always a risk of stack overflow.
Also, shouldn't you consider the number of already created buttons?
kmllev 6-Sep-14 19:32pm    
Actually I'm not sure how I will do that because my goal in that part of the code is if the position is already occupied, then generate new random numbers again and return to the begnning of the method to go through the conditions again.

totalButtons counts the number of already created buttons (all of them, regardless of the type of item)
[no name] 6-Sep-14 21:58pm    
And that is exactly your problem. Think about what you are doing logically for just a second. Just for the sake of argument lets say that the first 62 of your buttons were placed the first time. The last button gets a random location assigned to a filled location so it keeps calling the same routine over and over and over until it gets a 63 OR runs out of memory. What do you think the chances are that you will get the grid filled by using random numbers and calling the same routine over and over and over? The more buttons you try and create the lesser chance you have to finding an open cell to put it in.
kmllev 6-Sep-14 22:29pm    
Thank you for this, I realized the error. What do you suggest I do to fix it? I'm thinking right now of storing the indices of the rows and cols in 2 arrays, shuffle the arrays, and just take the values from there. At least, for the beginning of the game.
BillWoodruff 7-Sep-14 2:15am    
You are off to a good start, but, I believe there is a way you can greatly simplify this design using the WinForms TableLayoutControl, and I'm willing to help you learn how to use it.

You might get some ideas from an answer I posted today to a question that involves a similar task: creating a row-column matrix of Controls at run-time:

http://www.codeproject.com/Answers/816027/Hi-Everyone-My-Question-Is-About-Csharp-Windows-Fo

My sense is that you need to start from scratch and re-design this application. I, and I am sure other people here, will be happy to help you, but you need to make clear:

1. what is the initial visual structure of the game: are there always 63 buttons visible, organized in 9 rows of 7 columns ? Do these buttons always fall into two categories, 'donut, and 'pencil ?

2. what are the rules of the "game" ?

3. can the buttons be moved ?

4. in what case does a button status change from: donut horizontal to donut vertical; or pencil up or down ?

There is absolutely no problem manipulating 63 buttons in a WinForms application !

cheers, Bill

Best approach? Don't...that's a lot of buttons...

But if you must, I'd start by looking at the actual value of totalButtons using the debugger, given that you update it in two places:
C#
totalButtons += totalProb;
...
totalButtons += nProb;
And since it's your loop termination condition...you need to know what is happening to it.

So use the debugger, and put a breakpoint in the while loop. Step over the method calls and see what has happened to the value. Because if that doesn't go up at any time in the loop, you will get a stack overflow - just as you are.

"Thank you for your help! Actually it's a hidden object game of sorts, where the program will ask you to look for something in the grid. :) Like for example, look for 5 apples, etc.
I started over and so far it's working, but I still have to work on how there should only be an exact or a not too big number of the item being asked for in the grid, and I can't seem to do that without the endless recursion"


OK, so dump the randomness! :laugh:

Instead, set up a list of locations, and "shuffle" them randomly:
C#
List<Item> ordered = new List<Item>();
for (int i = 0; i < 63; i++)
    {
    Item item = new Item(i);
    ...
    ordered.Add(item);
    }
List<Item> shuffled = new List<Item>();
while (ordered.Count > 0)
    {
    int remove = rand.Next(ordered.Count);
    Item item = ordered[remove];
    ordered.Remove(item);
    shuffled.Add(item);
    }

You then get a list of Items or locations or whatever in a random order and no need to recurse.
See what I mean?
 
Share this answer
 
v2
Comments
kmllev 6-Sep-14 11:24am    
You're right about the part where I update it in two places, my bad! Must have overlooked it while I was editing my code.

I know, 63 buttons, that's a lot of buttons, but it's a 7x9 grid that must be filled, so I'm not really sure how to proceed without blowing the call stack or memory.

And I just fixed the update so it only updates in one place now, and it's frozen still :/
OriginalGriff 6-Sep-14 11:39am    
So what does the debugger say is going on?
kmllev 6-Sep-14 12:09pm    
Can I get back to you sir? I'm currently trying to fix my code; I think I'm getting to the real error, if I still need help I'll go back right here, thank you!
kmllev 6-Sep-14 19:35pm    
Hello, I have updated my code. I still get the StackOverFlowException but I just can't seem to find the reason why. Would you mind looking over my code again? I know it's logically wrong but it still seems to make sense to me, which it shouldn't
OriginalGriff 7-Sep-14 3:15am    
I think that you need to rethink what you are doing: there are better ways to do that.
But first - I'm just guessing that this is a "slide puzzle" type game, where you "slide" pictures around to put it back together? Similar to this kind of thing?
http://a413.phobos.apple.com/us/r1000/072/Purple/v4/b4/fe/56/b4fe565e-7c61-65e1-4e6d-79de195a2d50/mzl.eylgjhju.png

If so, then random generation may be a poor idea - can you solve any puzzle from a "random" state? I don't know - I do know that you can make unsolvable rubics cubes by randomly assembling them.

If that is what you are doing, then I'd do it differently: generate the "working" image as a complete ordered, and "pretty" page, then apply random moves to it to shuffle it. That way, you are both guaranteed that the puzzle is solvable, and you can provide a "difficulty level" by changing the number of shuffles you do.

Do note that randomness is probably at the heart of your problem here: If a location you try to fill is occupied you will add the control at least twice because you are recursing in the setLoc method.
If I understand your code correctly, you have a fixed number of buttons and you want to assign a problem and image to each button randomly from a pool of problems.

If this is the case, I think you should create your buttons first and align them into your grid.
C#
// As you have a fixed number of problems, you can create an enum
private enum ProblemTypes
{
    Undefined = -1,    // Used to show an unintialized value
    Pencil = 0,
    Donut = 1
}

// Also only load your images once
private void LoadImages()
{
    // I have no idea where you create the variables donut and pencil
    donut[0] = Image.FromFile(GUILoc + "ChocoDonut_Hori.png");
    donut[1] = Image.FromFile(GUILoc + "ChocoDonut_Verti.png");
    donut[2] = Image.FromFile(GUILoc + "PinkDonut_Hori.png");
    donut[3] = Image.FromFile(GUILoc + "PinkDonut_Verti.png");}
    
    pencil[0] = Image.FromFile(GUILoc + "GreenPencil_Down.png");
    pencil[1] = Image.FromFile(GUILoc + "GreenPencil_Up.png");
    pencil[2] = Image.FromFile(GUILoc + "PinkPencil_Down.png");
    pencil[3] = Image.FromFile(GUILoc + "PinkPencil_Up.png");
}

private void CreateButtons()
{
    int numberOfButtons = 63;        // This can be configurable later on
    int positionX = 0;
    int positionY = 0;
    int horizontalSpace = 25;
    int verticalSpace = 20;
    for (int i=0; i<numberofbuttons;>    {
        Button btn = new Button();
        btn.Name = String.Format("button{0}", i+1);
        btn.Tag = ProblemTypes.Undefined;
        btn.X = positionX;
        btn.Y = positionY;
        btn.Size = new Size(70, 70);
        btn.FlatStyle = FlatStyle.Flat;
        btn.BackColor = Color.Transparent;
        btn.FlatAppearance.BorderSize = 0;
        btn.FlatAppearance.BorderColor = Color.FromArgb(0, 255, 255, 255);
        btn.FlatAppearance.MouseOverBackColor = Color.Transparent;
        btn.FlatAppearance.MouseDownBackColor = Color.Transparent;
        
        // Reset the x-position and increment the y-position every 7 column
        if (i % 7 == 0)
        {
            positionX = 0;
            positionY += verticalSpace;
        }
        else
        {
            positionX += (btn.Size.X + horizontalSpace);
        }
     
        // I have not taken into account your TableLayoutPanel
        // If you want to use it you have to figure that part out yourself
        panDoodles.Controls.Add(btn);
    }
}

// This is where you have to put in some work
public void AssignProblemToButtons()
{
    foreach (Button btn in panDoodles.Controls)
    {
        // Not sure how you want to assign problem type and image
        // That part of your code is a bit confusing so this is just the principal functionality
        btn.Tag = GetProblem(problemRandomNumber);
        btn.Image = GetImage((ProblemTypes)btn.Tag, imageRandomNumber);
    }
}


This just a start for you and not the entire code.
It just shows how you can change the logic a bit in order to avoid recursive functions
(Recursive functions are good in the right context, but not here)

Good luck and I hope this was a bit helpful.
 
Share this answer
 
Comments
kmllev 9-Sep-14 8:26am    
Thank you, George! I know, even now that I'm looking at the code I've posted here, I'm confused myself. But I started over and now it's working fine so far, but I'll still need to add a few more control structures. But thank you still for this.
My game is actually a hidden object game of sorts, where a problem will be posed, like look for 10 apples in the grid. :)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900