Subscribe to Martyr2's Programming Underground        RSS Feed
-----

Rolling Dice In Java, C# And PHP

Icon 2 Comments
Some of the very first programs we are introduced to in programming is the idea of random numbers. This is often introduced using the idea of the die or dice. We have all seen them and have probably even thrown a set of dice at some point in our life. A die is pretty much a representation of a random number generator with a fixed range of values. The traditional die has 6 faces and what many of us don't know is that there are die that go from having only 1 face to die that have as many as 144 faces. Either way, dice are used in all sorts of games of probability and because of that I thought I would show you how we could model a Die and its ability to "roll". While we are at it, why not also build in an extra feature of allowing us to view a die's roll history and present a histogram of its rolls. I will show you the same setup in Java, C# and PHP. Feel free to take this code and do what you like with it.

Let's get to the code

We will start off with wrapping all this functionality up into a general class called "Die" (or in the case of PHP "Dice" since PHP has a function called Die). The class will start by having some private internal variables to keep track of state as related to the number of faces we tell the Die it has. By default we will assume we have a 6 faced die, but you could easily add more or less as required by specifying the number of sides in the constructor. We will first explore the class using some Java.

// Define a single die
public class Die {
    private int value = 1;
    private int sides = 6;
    private Random randomGen = new Random();
    private int[] history;

    // Constructor
    public Die(int sides) {
        if (sides >= 1) {
            this.sides = sides;
        }

        history = new int[this.sides];
    }

    // Implement a roll method to generate a number for the value
    public void Roll() {
        value = randomGen.nextInt(sides) + 1;
        history[value-1]++;
    }

    // Gets the value of the die
    public int getValue() {
        return value;
    }

    // Tell us how many sides the die has
    public int getSides() {
        return sides;
    }

    // Reset the history of this die
    public void resetHistory() {
        history = new int[sides];
    }

    public int getHistoryValue(int side) {
        if (side >= 1 && side <= this.sides) {
            return history[side-1];
        }
        throw new IllegalArgumentException("Invalid side: This die doesn't have that side.");
    }

    // Prints out the histogram results of the die's roll history
    public String getHistogram() {
        StringBuilder builder = new StringBuilder();
        builder.append(String.format("%s\n%s\n\n", "Histogram of Die", "----------------"));

        for (int i = 0; i < sides; i++) {
            builder.append(String.format("%-4d", i + 1));

            for (int j = 0; j < history[i]; j++) {
                builder.append("*");
            }

            builder.append("\n");
        }

        return builder.toString();
    }
}



In the constructor of this class we take in the number of sides or "faces" we would like the die to have. With this value we will set the number of sides and establish an array of counts we will call "history" for our histogram. Each time a roll is done, its private value is set and that particular roll value is incremented in this history array. For example, if we roll the die 10 times and of those 10 rolls 3 of them were the number 6, the value at index 5 (remember arrays start at zero) will be "3".

The class offers a set of properties for getting the current value of the roll using getValue(), the number of sides using getSides(), the count for any side rolled using getHistoryValue(int side) and a resetHistory() function for resetting all the history values. We also setup a method to get a string containing a histogram so we can then print it as we see fit. The class is pretty straight forward and easy to use in various ways. One way we can use this is to setup a die instance, call its roll method and then interrogate the instance for data it has seen. Need 5 dice to use in your next D&D game? Create one die, roll it five times or create five dice and roll them all once. It is up to you. You can even mix and match dice types by creating 3 instances with 4 faces and 2 with 8 faces.

Let's see how this might look in C# as well...

// Define a single die
public class Die {
    private int value = 1;
    private int sides = 6;
    private Random randomGen = new Random();
    private int[] history;

    // Constructor
    public Die(int sides) {
        if (sides >= 1) {
            this.sides = sides;
        }

        history = new int[this.sides];
    }

    // Implement a roll method to generate a number for the value
    public void Roll() {
        value = randomGen.Next(1,sides+1);
        history[value - 1]++;
    }

    // Gets the value of the die
    public int getValue() {
        return value;
    }

    // Tell us how many sides the die has
    public int getSides() {
        return sides;
    }

    // Reset the history of this die
    public void resetHistory() {
        history = new int[sides];
    }

    public int getHistoryValue(int side) {
        if (side >= 1 && side <= this.sides) {
            return history[side - 1];
        }
        throw new ArgumentOutOfRangeException("Invalid side: This die doesn't have that side.");
    }

    // Prints out the histogram results of the die's roll history
    public String getHistogram() {
        StringBuilder builder = new StringBuilder();
            
        builder.Append(String.Format("{0}\n{1}\n\n", "Histogram of Die", "----------------"));

        for (int i = 0; i < sides; i++) {
            builder.Append(String.Format("{0,-4}", i + 1));

            for (int j = 0; j < history[i]; j++) {
                builder.Append("*");
            }

            builder.Append("\n");
        }

        return builder.ToString();
    }
}



Here we can see a lot of the similarities to the Java version of the class. However, here we have a few changes to some of the formatting in the histogram and how we generate an exception for when the number of sides doesn't make sense. Again we use a StringBuilder class to make the string building process a little more efficient for larger data sets and format it to be presentable. Below is the same example done in PHP.

// Define a single die
class Dice {
    private $value = 1;
    private $sides = 6;
    private $history = [];

    // Constructor
    function __construct($sides) {
        if ($sides >= 1) {
            $this->sides = $sides;
        }

        $this->history = array_fill(0, $this->sides, 0);
    }

    // Implement a roll method to generate a number for the value
    public function roll() {
        $this->value = random_int(1, $this->sides);
        $this->history[$this->value - 1]++;
    }

    // Gets the value of the die
    public function getValue() {
        return $this->value;
    }

    // Tell us how many sides the die has
    public function getSides() {
        return $this->sides;
    }

    // Reset the history of this die
    public function resetHistory() {
        $this->history = array_fill(0, $this->sides, 0);
    }

    public function getHistoryValue($side) {
        if ($side >= 1 && $side <= $this->sides) {
            return $this->history[$side - 1];
        }
        throw new Exception("Invalid side: This die doesn't have that side.");
    }

    // Prints out the histogram results of the die's roll history
    public function getHistogram() {
        
        $str = sprintf("%s<br/>%s<br/><br/>", "Histogram of Die", "----------------");

        for ($i = 0; $i < $this->sides; $i++) {
            $str .= ($i + 1) . " ";

            for ($j = 0; $j < $this->history[$i]; $j++) {
                $str .= "*";
            }

            $str .= "<br/>";
        }

        return $str;
    }
}



Again this class is named "Dice" to get around the fact that PHP has a reserved function already called "die". PHP accesses its members through the arrow functionality as well as all their variables being prefixed with dollar signs and no strong typing. If we are using PHP 7 we could introduce a little bit stronger typing hints and such if so desired. But the use of the class is the same as the others.

Dandy, but do you have an example of how to use it?

Sure. We can use this class relatively easily. One tip I always use to gauge whether or not a class is well formed is how easy it is to use it and call on its various functions. Is each function pretty simple to understand and call on? Does it take a little or a lot of parameters? Does the name make sense for what it does? Does it seem to do only one thing? All of these ideas are why we build things with OOP best practices and principles in mind. Here is an example of how we can roll a die 25 times and then print out its histogram in PHP...

$d = new Dice(6);

for ($i = 0; $i < 25; $i++) {
	$d->roll();
	echo "Die roll value is: " . $d->getValue() . "<br/>"; 
}

echo "<br/><br/>";

echo $d->getHistogram();



We create our die, saying it has 6 faces. We then launch into a loop that will run through 25 rolls. We print out the value of each roll after it is rolled and at the end we call on its getHistogram() method to print us a nice little chart that may look something like this...

Histogram of Die
----------------

1 ****
2 ***
3 **********
4 ***
5 ****
6 *



Conclusion
You just got to love simple little classes that make complex tasks more palatable. By using this class you can quickly get a Die object that will take care of its own rolling and history tracking to boot. This class would be of great use in games where you need random number generation that acts like a Die being rolled or even showing a simple example of how you might go about keeping track of counts and building histograms. Games like craps and yahtzee are just two examples. Either way, feel free to take advantage of our sterling 5 finger discount and use these classes as you see fit. Hopefully you see ways to expand upon it and make the next smart die. :)

If you found this project interesting and wanted to know where you can find additional project ideas, be sure to check out our official ebook "The Programmers Idea Book" which features 200 project ideas, tips, difficulty ratings and more than span 10 different topic areas from games to file handling to networking. Thank you again for reading and don't forget to share!

2 Comments On This Entry

Page 1 of 1

Skydiver Icon

07 August 2017 - 09:25 PM
Here's a version of the C# code without a Java accent.
public class Die
{
    private Random randomGen = new Random();
    private int[] history;

    public int Value { get; private set; }
    public int Sides { get; private set; }

    public Die(int sides)
    {
        if (sides <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(sides), "Number of sides must be greater than or equal to 1.");
        }

        Sides = sides;
        Value = 1;
        history = new int[Sides];
    }

    public void Roll()
    {
        Value = randomGen.Next(1, Sides + 1);
        history[Value - 1]++;
    }

    public void ResetHistory()
    {
        for (int i = 0; i < Sides; i++)
            history[i] = 0;
    }

    public int GetHistoryValue(int side)
    {
        if (side < 0 ||  side > Sides)
        {
            throw new ArgumentOutOfRangeException(nameof(side), "Invalid side: This die doesn't have that side.");
        }
        return history[side - 1];
    }

    // Prints out the histogram results of the die's roll history
    public String GetHistogram()
    {
        StringBuilder builder = new StringBuilder();

        builder.AppendLine("Histogram of Die");
        builder.AppendLine("----------------");
        builder.AppendLine();

        for (int i = 0; i < Sides; i++)
        {
            builder.AppendFormat("{0,-4}", i + 1);
            builder.Append('*', history[i]);
            builder.AppendLine();
        }

        return builder.ToString();
    }
}

0

Skydiver Icon

07 August 2017 - 09:50 PM
And a little more refactoring to make the GetHistogram() a bit more flexible.
public class Die
{
    private Random randomGen = new Random();
    private int[] history;

    public int Value { get; private set; }
    public int Sides { get; private set; }

    public Die(int sides)
    {
        if (sides <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(sides), "Number of sides must be greater than or equal to 1.");
        }

        Sides = sides;
        Value = 1;
        history = new int[Sides];
    }

    public void Roll()
    {
        Value = randomGen.Next(1, Sides + 1);
        history[Value - 1]++;
    }

    public void ResetHistory()
    {
        for (int i = 0; i < Sides; i++)
        {
            history[i] = 0;
        }
    }

    public int GetHistoryValue(int side)
    {
        if (side < 0 ||  side > Sides)
        {
            throw new ArgumentOutOfRangeException(nameof(side), "Invalid side: This die doesn't have that side.");
        }
        return history[side - 1];
    }

    public void WriteHistogram(TextWriter writer,
                               char histogramChar = '*',
                               string title = "Histogram of Die", char titleChar = '-')
    {
        if (!string.IsNullOrEmpty(title))
        {
            writer.WriteLine(title);
            writer.WriteLine(String.Empty.PadRight(title.Length, titleChar));
            writer.WriteLine();
        }

        for (int i = 0; i < Sides; i++)
        {
            writer.WriteLine("{0,-4}{1}", i + 1, String.Empty.PadRight(history[i], histogramChar));
        }
    }
}

0
Page 1 of 1