---
title: "Jam 02 - Exercise 3 - Simulating Dice Rolls"
tags:
- 3 ๐งช in testing
- 4 ๐ฅณ done
- jam02
- objects
- methods
- classes
- random
---
<!-- markdownlint-disable line-length single-h1 no-inline-html -->
<!-- markdownlint-configure-file { "ul-indent": { "indent": 4 }, "link-fragments": {"ignore_case": true} } -->
# Exercise 3 - Simulating Dice Rolls
{%hackmd dJZ5TulxSDKme-3fSY4Lbw %}
## Overview - Exercise 3
Let's put our Java skills to work by creating a simulation of rolling dice. This exercise will help you:
- Learn how to create a proper Java class
- Use Java's Random Number Generator to simulate real-world events
- Practice working with objects and methods
- Apply what you've learned about performance measurement
## The Problem - Exercise 3
**Problem**: A single die is a 6-sided game piece commonly used in many basic games. When you roll it, the value obtained is whatever number is on top. A *fair* die has an equal probability of landing on any face value (1 through 6).
For example, if you roll a fair die 1,000,000 times:
- Each value should appear about 1/6 of the time
- The expected (mean) value would be 3.5
- Calculated as: (1 * 1/6 + 2 * 1/6 + 3 * 1/6 + ... + 6 * 1/6 = 3.5) <!-- markdownlint-disable-line -->
Your task is to:
1. Create a class that models a single fair die
2. Use it to explore probability by simulating many rolls
3. Compare your simulation results with theoretical probabilities
This has two purposes:
- Show how can we use the Random Number Generator to simulate real-world random events
- Offer a gentle introduction to class design in Java
## Required Steps - Creating the Die Class
:::danger
๐จ **Important**: This is your first real class definition! While we're providing the code to help you learn, you must **understand every line you type**. Don't blindly copy without understanding what each part does - that won't help you learn and will hurt you in the long run.
:::
1. Create a new file called `Die.java` in your `jam02` directory. This is going to be a class called `Die`. In Java, class names and their files must be named the same. The `Die` class is an *abstraction* that represents a single die - it will contain all the data and behaviors we need to simulate rolling a die.
2. IntelliJ should have already set up your package declaration based on the banner template we used in Jam01. However, make sure your file has this line after the banner comment:
```java
package jam02;
```
Remember: The root folder of all your source code is `src/main/java`, and every directory under this folder represents a **package**.
3. We need a way to simulate rolling a die. This means we need to generate random numbers! Take a look at the Java 21 API documentation for the Random class in the `java.util` package: [Random (Java SE 21 & JDK 21) (oracle.com)](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Random.html)
Can you find a method that returns a random integer within a specific range? We'll need this later in our `roll()` method to simulate rolling a die - it should give us a random number between 1 and however many sides our die has. Keep this method in mind - we'll use it soon!
For now, let's just import the Random class. Add these lines right after your package statement:
```java
import java.util.Random; // For generating random numbers
import java.util.Scanner; // For reading user input (we'll need this soon!)
```
4. The `Die` class will contain:
**Fields (aka Variables, Data Members, Attributes, and many other names - get used to hearing different names for the same thing! We will use these terms interchangeably):**
- Constants (primitive types) to define the range of possible values
- A static Random Number Generator (RNG) shared by all Die objects
- An instance variable (primitive type) to track the last roll
**Methods (Behaviors):**
- Constructor to create new Die objects
- Methods to roll the die and get its value
- A string representation of the die's state
5. Now let's add the **Data Members** to your `Die` class`. We'll do this step by step, adding and understanding each piece:
6. First, let's define our constants. Add these after the class declaration that was created by your banner template:
```java
/** Minimum value that can appear on any face */
private static final int MIN_FACE_VALUE = 1;
/** Maximum value that can appear on any face */
private static final int MAX_FACE_VALUE = 6;
```
- We're using constants instead of "magic numbers" - this is good programming practice!
- The `final` keyword means these values are immutable (a.k.a. they cannot be changed, a.k.a. constant).
- You'll notice the `static` keyword on several variables (above and below). For now, just understand that `static` means these values are shared by all Die objects rather than each Die having its own copy. We'll explore this concept more deeply soon!
- You'll also see the `private` keyword being used. Java has several ways to control access to variables and methods. For now, just know that we use `private` to help protect our data - this relates to important Object-Oriented Design principles like encapsulation and information hiding that we'll explore soon!
7. Next, we need a variable to track rolls:
```java
/** The last value rolled */
private int lastRollValue;
```
- Notice this variable doesn't have the `static` keyword - this means each Die object gets its own copy.
- Think about it: if you're rolling multiple dice, each die needs to remember its own last roll! But things like the number of sides (`MAX_FACE_VALUE`) can be shared - after all, every die in this game has the same number of sides and can use the same generator to determine its rolls.
8. Finally, we need our random number generator:
```java
/** Our random number generator - shared by all Die objects */
private static Random rng = new Random(1234);
```
Let's understand what's happening here:
- See that `Random()` with the parentheses? That's a constructor! Constructors are special methods that create new objects, and they always have the same name as their class. When we write `new Random(1234)`, we're calling the Random constructor to create a new random number generator.
- The `1234` we passed to the Random constructor is called a "seed" value. Hover over `Random(1234)` in IntelliJ to see what it does: "Creates a new random number generator using a single long seed. The seed is the initial value of the internal state of the random number generator." Using the same seed means we'll get the same sequence of random numbers each time we run our program - this is great for testing!

:::info
๐ **Design for Flexibility**
Think about this: What if your program becomes super popular and game designers want to use it for different types of dice? A regular six-sided die is great for Yahtzee, but Dungeons & Dragons players might want d20s (20-sided dice), and other games might need d4s, d8s, or d12s!
If we hard-coded the number 6 throughout our code, we'd have to hunt down and change every instance to support these different dice. That's both error-prone and time-consuming. You've learned in previous classes that hard-coding numbers like this (called *magic numbers*) is a bad practice - and now you're seeing why! A good developer writes code that's flexible and easy to modify when requirements change.
:::
9. Now let's add the **Methods** to your `Die` class`. We'll do this step by step, adding and understanding each piece:
:::warning
๐ง **Watch Your Curly Braces!**
When adding these methods, make sure you:
- Add them inside the `Die` class (between the outer curly braces)
- Have matching opening and closing curly braces for each method
- End up with exactly one closing curly brace at the end of the file (closing the class)
A mismatched curly brace is a common error that can be tricky to spot!
:::
10. First, let's add the constructor. This creates new Die objects (in Python, this was called `__init__`). In Java, constructors must follow a strict naming rule - they must have exactly the same name as the class, including the same capitalization:
```java
/**
* Construct a new die, initializing it to not rolled yet
*/
public Die() {
this.lastRollValue = 0;
}
```
The constructor initializes our die with `lastRollValue = 0` to indicate it hasn't been rolled yet.
11. Next, we need a way to get the last roll value:
```java
/**
* @return the last value rolled
*/
public int getLastRollValue() {
return this.lastRollValue;
}
```
This is a "getter" method - it simply returns the value of our instance variable.
:::info
๐ **Code Style Matters!**
You'll notice that, unlike Python, Java lets you structure your code however you want. As long as your code adheres to the proper syntax, you could technically place your entire class on one line! (And, if you did that, you will fail the class! Don't.)
Best practices in Java strive to create:
- Clean, structured code that is easy to read
- Good variable and method names that are self-documenting
- Consistent indentation for visual formatting
- Clear organization of related code elements
The only time we allow methods to be written on one line is usually for getter and setter methods, when the body of the method itself contains only one statement. For example, we could have written our getter method as:
```java
public int getLastRollValue() { return this.lastRollValue; }
```
But for learning purposes, we're showing it with full formatting first.
Remember: Good formatting isn't just about making your code pretty - it's about making it understandable and maintainable!
:::
12. Now for the most important method - simulating a die roll. We need a method to actually simulate rolling our die, which is nothing more than using the random number generator to return a random number between `MIN_FACE_VALUE` and `MAX_FACE_VALUE`. The `nextInt(int bound)` method of a Random object is what we need. How does it work? Get used to referencing the API! We'll copy it here:

We can see that `nextInt` returns a number between 0 (inclusive) and `bound` (exclusive)! Here's how we'll use it:
```java
/**
* Simulate rolling the die
* @return the value rolled
*/
public int roll() {
int spread = MAX_FACE_VALUE - MIN_FACE_VALUE + 1;
this.lastRollValue = Die.rng.nextInt(spread) + MIN_FACE_VALUE;
return this.lastRollValue;
}
```
Let's understand what's happening here:
- First we calculate the range of possible values (`spread`)
- Then we get a random number in that range and shift it to start at our minimum value
- We store the result in our instance variable using `this`
- We qualify the RNG with the class name (`Die.rng`) because it's a class variable
Instance variables are qualified by using the `this` keyword. It serves a very similar purpose to `self` in Python.
Class variables, in contrast, are qualified by using the name of the class where the variable is declared. (i.e. `Die.rng` is the random number generator object.) It makes sense because class variables are NOT associated with any particular instance of the class. They are shared by ALL objects of the class.
13. Finally, let's add a nice string representation of our die (in Python, this was called `__str__`):
```java
/**
* @return string representation of the die
*/
@Override
public String toString() {
return String.format("Die: last roll = %d", this.lastRollValue);
}
```
Just like Python's `__str__` method, this method is called automatically when Java needs to convert our Die object to a string (like when printing it). The `@Override` tells Java we're intentionally redefining this method from the parent class - similar to how Python knows to use your `__str__` instead of the default one.
At this point, your `Die.java` file should be complete except for the `main` method. In Java, every program needs a `main` method - it's where execution begins (similar to how Python runs code from top to bottom). Let's add a simple test program that creates a die, rolls it, and shows the result.
Add this static method at the bottom of your `Die` class:
```java
/**
* A simple main program to test the Die object
*
* @param args the command line arguments (not used)
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Die die = new Die(); // In Python, this would be: die = Die()
// Ask how many times to roll
System.out.println("How many times should I roll the die?");
int rolls = scanner.nextInt();
// Make sure they enter a positive number
while (rolls <= 0) {
System.out.println("Please enter a positive number:");
rolls = scanner.nextInt();
}
// Roll the die that many times
for (int i = 0; i < rolls; i++) {
die.roll();
System.out.println("Roll " + (i+1) + " was a " + die.getLastRollValue());
}
System.out.println("Goodbye!");
}
```
Notice that `new` keyword? That's how we create (instantiate) new objects in Java. In Python, we just called the class like a function, but Java requires the `new` keyword to make it clear we're creating an object.
Save your file and run it! You should see output like this:
```text
How many times should I roll the die?
-1
Please enter a positive number:
0
Please enter a positive number:
2
Roll 1 was a 3
Roll 2 was a 6
Goodbye!
```
You might be wondering why we didn't see the `toString` method called. That's because Java automatically calls the `toString` method when we print an object.
You also might be asking yourself, *"`self`, how is it my output was also a `3` for the first roll? Aren't we using a random number generator? And even the `6` matches?!"*
Remember that seed value of `1234` we used when creating our random number generator? That's why! Using the same seed means we'll get the same sequence of "random" numbers each time we run our program.
At this point, you've completed the basic implementation of the `Die` class. This is a good time to save your work! Feel free to commit your changes:
```bash
git add src/main/java/jam02/Die.java
git commit -m "jam02: Implement basic Die class with constructor and methods"
```
> ๐ **Checkpoint**: Before moving on, verify:
>
> - You completed the JShell exploration
> - You understand the difference between primitive types and wrapper classes
> - You've documented your findings in `answers.txt` for questions 1.1-1.3
> - Your working directory is clean (no uncommitted changes)
:::info
๐ **Key Java Concepts**
- `static` variables are shared by all instances of a class
- `private` helps with encapsulation by hiding implementation details
- The `@Override` annotation tells Java we're intentionally redefining a method from the parent class
- `this` refers to the current object instance (similar to Python's `self`)
:::
Great! You've just created your first complete Java class. While we provided the code to help you learn the structure and syntax, the real learning comes from understanding each component and how they work together. Now, let's put your understanding to the test with a more challenging exercise that will have you use this `Die` class in a probability simulation!
## Required Steps - Dice Rolling Simulation
Let's explore probability with our `Die` class! We'll simulate rolling two dice many times to see how often specific sums occur. For example, rolling a sum of 2 requires both dice showing 1 - with 36 possible combinations (6 ร 6) but only one way to make 2, that's a 2.78% chance. Will our simulation match this theoretical probability?
Now that we've tested our basic `Die` class functionality, let's modify our `main` method to do something more interesting. We'll build on our simple test program to create a probability simulation that rolls the dice 1,000,000 times. You can keep the Scanner setup and input validation pattern from our test program - they'll come in handy! Here's what we need to do:
Break this down into steps (and this time you'll write most of the code yourself!):
1. Instantiate two Die objects (we're rolling two dice!)
2. Ask the user what sum they want to check for
3. Validate their input (must be 2-12)
4. Roll both dice 1,000,000 times
5. Count and report how often the specified sum occurs
6. Measure and report the execution time
For timing measurement:
- Use `System.nanoTime()` before and after your simulation loop
- Convert the final time from nanoseconds to milliseconds
- Display the time with exactly 3 decimal places
For the simulation results:
- Display the count of how many times the sum occurred
- Calculate and show the percentage (with 3 decimal places)
- Format your output to match this example:
```text
Welcome to the dice simulator!
I'm going to roll 2 dice 1000000 times
What dice sum do you want to check for?
1
ERROR! Invalid value entered! 2-12 only. Try again.
What dice sum do you want to check for?
5
The roll value 5 appeared 111030 times, or 11.103% of all rolls.
1000000 rolls took 28.704 ms.
Goodbye.
```
> ๐ **Checkpoint**: Before moving on, verify:
>
> - Your `Die.java` file compiles and runs without errors
> - Your simulation correctly counts and reports sum occurrences
> - Your timing measurements are displayed with 3 decimal places
> - Your output matches the expected format
> - Your working directory is clean (no uncommitted changes)
## Save Your Work - Exercise 3
Verify what files are uncommited:
```bash
git status
```
Stage your changes (This should be the file shown in `git status` as modified)
Feel free to use a different message as long as it's descriptive
```bash
git add src/main/java/jam02/Die.java
```
Commit your work
```bash
git commit -m "jam02: Complete dice rolling simulation"
```
Your working directory should be now be clean.