Search code examples
c#arraysloopsdice

How to progress my Five Dice game


I am learning to program, in particular starting with C# where I have been experimenting with dice games to become more familiar and to improve myself. I am now trying to create a basic game in windows forms, where two players roll 5 dice and keep record of their score.

Rules:

  • Begin with 5 dice

  • If a 1 or 4 appears, player scores no points and those dice are removed. Otherwise add all the dice to total

  • Continue with remaining dice until there a no dice left over

So far I have an Image array which stores all my dice images in my resources and a button which will roll the dice. What I need help with specifically is being able to remove the penalized dice (setting that particular one back to blank) and allowing the player to continue rolling their remaining dice until none are left.

At the moment I am unsure as to where I can further this and maybe I am biting off more than I can chew. I am loving coding and any help would be much appreciated.

Here's an image of the interface:

enter image description here

enter image description here

public partial class Form1 : Form
{

    Image[] diceImages;
    int[] dice;
    int[] diceResults;
    Random random;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        diceImages = new Image[7];
        diceImages[0] = Properties.Resources.blank;
        diceImages[1] = Properties.Resources.one;
        diceImages[2] = Properties.Resources.two;
        diceImages[3] = Properties.Resources.three;
        diceImages[4] = Properties.Resources.four;
        diceImages[5] = Properties.Resources.five;
        diceImages[6] = Properties.Resources.six;

        dice = new int[5] { 0, 0, 0, 0, 0 };

        random = new Random();

        diceResults = new int[6] { 0, 0, 0, 0, 0, 0 };

    }

    private void btn_rollDice_Click(object sender, EventArgs e)
    {
        RollDice();

        GetResults();

        ResetResults();
    }

    private void RollDice()
    { 
        for (int i = 0; i < dice.Length; i++)
        {
            dice[i] = random.Next(1, 7);

            switch (dice[i])
            { 
                case 1:
                    diceResults[0]++;
                    break;
                case 2:
                    diceResults[1]++;
                    break;
                case 3:
                    diceResults[2]++;
                    break;
                case 4:
                    diceResults[3]++;
                    break;
                case 5:
                    diceResults[4]++;
                    break;
                case 6:
                    diceResults[5]++;
                    break;

            }

        }

        lbl_dice1.Image = diceImages[dice[0]];
        lbl_dice2.Image = diceImages[dice[1]];
        lbl_dice3.Image = diceImages[dice[2]];
        lbl_dice4.Image = diceImages[dice[3]];
        lbl_dice5.Image = diceImages[dice[4]];
    }

    private void GetResults()
    {
        bool oneRoll = false, fourRoll = false;

        for (int i = 0; i < diceResults.Length; i++)
        {
            if (diceResults[i] == 1 && diceResults[i] == 4)
            {
                oneRoll = true;
                fourRoll = true;
            }
        }
    }

    private void ResetResults()
    {

    }

}

Solution

  • The code you posted has at least a couple of oddities that don't seem to fit with your description:

    • The code simply increments an element in an array (diceResults) when a given die value is rolled (i.e. the element corresponds to the die value, not the die's order in the game). From your description, I would have expected the code to simply add the die value to a single sum variable.
    • In your GetResults() method, your code compares the individual element values in the diceResults to the values 2 and 5. In other words, for each possible die value, if that value comes up twice or five times, you set both flags. There are a number of reasons this is strange, but the biggest, most obvious one is that a single variable (i.e. the element diceResults[i]) can never have two different values at the same time. I.e. that array element will never be both 2 and 5, as the if statement requires.

    Given those issues, I'm more inclined to focus on the original specification than to trust the code too much in terms of trying to understand what your intended behavior of the code actually is. :)

    It seems that the basic question is how best to remove die from play. The suggestion (in the comments above) to use a list to track the dice is certainly a workable one. In that approach, one would iterate through the list to set each element, and if the roll for a given element ever comes up as 1 or 4, remove that element before moving on.

    Having done that, one would just iterate through the list again to set the die value images, using the "blank" image for any die beyond the length of the list.

    But there is a simpler way, and based on your statement "setting that particular one back to blank", which seems to imply that each blank die should appear in the same position in which it was rolled, it seems like that simpler way might be preferable to you.

    Specifically, after rolling the dice, just scan through the dice array and reset any 1 and 4 values to 0, and use this 0 value as a special "sentinel" value to indicate that die is now blank.

    Note that however you do this (with a list, or just setting values to 0), there is also the question of whether to show the user the actual 1 and 4 rolls, or to immediately set those rolls to a blank die. I'm going to assume the former, but it would be very easy to implement it the other way instead. (As always, the beginning of good code is a good specification…right now, your specification is a bit light on detail, and thus is vague and ambiguous).

    Taking that approach, your code might look something more like this:

    public partial class Form1 : Form
    {
        #region Declaration
        Image[] diceImages;
        Label[] labels;
        int[] dice;
        int diceTotal;
        bool checkOnesAndFours;
        Random random;
        #endregion
    
        #region Initialiazation;
        public Form1()
        {
            InitializeComponent();
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
            // Initializing an array this way eliminates the chance of having
            // a typo in the array index for the assignment.
            diceImages = new Image[]
            {
                Properties.Resources.blank,
                Properties.Resources.one,
                Properties.Resources.two,
                Properties.Resources.three,
                Properties.Resources.four,
                Properties.Resources.five,
                Properties.Resources.six
            };
    
            // Arrays are always initialized with the elements having their default
            // values, so there's no need to specify `0` values for `int` arrays explicitly
            dice = new int[5];
    
            random = new Random();
    
            diceTotal = 0;
    
            // For the purposes of setting the dice images, it will be helpful
            // to keep the control references in an array. This is both convenient
            // and, again, helps guard against typographical errors
            labels = new Label[]
            {
                lbl_dice1,
                lbl_dice2,
                lbl_dice3,
                lbl_dice4,
                lbl_dice5
            };
        }
    
        #endregion
    
        #region Private Methods
    
        private void btn_rollDice_Click(object sender, EventArgs e)
        {
            RollDice();
        }
    
        private void RollDice()
        {
            bool rolledOneOrFour = false;
            int rollTotal = 0;
    
            for (int i = 0; i < dice.Length; i++)
            {
                if (checkOnesAndFours)
                {
                    // First, clear any 1 or 4 from the previous roll
                    if (dice[i] == 1 || dice[i] == 4)
                    {
                        dice[i] = 0;
                    }
    
                    // Then, ignore any blank die
                    if (dice[i] == 0)
                    {
                        continue;
                    }
                }
    
                dice[i] = random.Next(1, 7);
                if (dice[i] == 1 || dice[i] == 4)
                {
                    rolledOneOrFour = true;
                }
                rollTotal += dice[i];
            }
    
            if (!rolledOneOrFour)
            {
                diceTotal += rollTotal;
            }
    
            checkOnesAndFours = true;
    
            for (int i = 0; i < labels.Length; i++)
            {
                labels[i].Image = diceImages[dice[i]];
            }
        }
    
        #endregion
    }
    

    NOTE: it was not entirely clear to me what you mean to do when a 1 or 4 comes up. Taking what you wrote literally, I understand it to mean that if any die shows a 1 or 4 on a roll, that none of the dice count for that roll. The above code is implemented with that understanding in mind.

    It occurs to me that you might have instead meant that only the dice that show 1 or 4 are not counted for the roll, and that the other dice for that roll are still included. It would not be hard to change the above to accommodate that alternative specification.

    NOTE: you'll also notice that I have made other changes to the code not technically required in order to address the immediate question. I added comments in the code itself to try to explain why I made those changes, and why I feel they make the code better.


    Just for grins, here's a version that does use a list instead:

    public partial class Form1 : Form
    {
        #region Declaration
        Image[] diceImages;
        Label[] labels;
        List<int> dice;
        int diceTotal;
        bool checkOnesAndFours;
        Random random;
        #endregion
    
        #region Initialiazation;
        public Form1()
        {
            InitializeComponent();
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
            // Initializing an array this way eliminates the chance of having
            // a typo in the array index for the assignment.
            diceImages = new Image[]
            {
                Properties.Resources.blank,
                Properties.Resources.one,
                Properties.Resources.two,
                Properties.Resources.three,
                Properties.Resources.four,
                Properties.Resources.five,
                Properties.Resources.six
            };
    
            // Lists must be initialized explicitly with their initial values, as by default
            // they are initially empty.
            dice = new List<int>(Enumerable.Repeat(0, 5));
    
            random = new Random();
    
            diceTotal = 0;
    
            // For the purposes of setting the dice images, it will be helpful
            // to keep the control references in an array. This is both convenient
            // and, again, helps guard against typographical errors
            labels = new Label[]
            {
                lbl_dice1,
                lbl_dice2,
                lbl_dice3,
                lbl_dice4,
                lbl_dice5
            };
        }
    
        #endregion
    
        #region Private Methods
    
        private void btn_rollDice_Click(object sender, EventArgs e)
        {
            RollDice();
        }
    
        private void RollDice()
        {
            bool rolledOneOrFour = false;
            int rollTotal = 0;
    
            for (int i = 0; i < dice.Count; i++)
            {
                // Clear any 1 or 4 from the previous roll
                if (checkOnesAndFours && (dice[i] == 1 || dice[i] == 4))
                {
                    // Remove this die from play
                    dice.RemoveAt(i);
    
                    // The next list element to examine is now at the current i value
                    // and the for loop is going to increment i when the continue
                    // is executed, so decrement i in anticipation of that
                    // so that the loop moves on to the correct next element
                    i--;
    
                    continue;
                }
    
                dice[i] = random.Next(1, 7);
                if (dice[i] == 1 || dice[i] == 4)
                {
                    rolledOneOrFour = true;
                }
                rollTotal += dice[i];
            }
    
            if (!rolledOneOrFour)
            {
                diceTotal += rollTotal;
            }
    
            checkOnesAndFours = true;
    
            for (int i = 0; i < labels.Length; i++)
            {
                labels[i].Image = i < dice.Count ? diceImages[dice[i]] : diceImages[0];
            }
        }
    
        #endregion
    }
    


    Finally, note that neither of the above addresses a couple of other issues with the code:

    • Initializing the dice labels at the beginning of a game.
    • Resetting the entire game once the game has ended (i.e. there are no more dice left).

    I leave these two items as an exercise for the reader (of course, if you run into problems with those specific issues, you may always post another question on Stack Overflow asking about each specifically).