Home Lawrence's Labyrinth Deconstructing B/X D&D Adventure Generator! B/X D&D Character Generator Other Contact
 

First PagePrevious PageBack to overviewNext PageLast Page

Rolling Dice

So! Where should we start?

I guess the first thing we do when we sit down to play a tabletop RPG is create a character. And the first thing we need to do to create a character is pull out some dice and roll 3d6 six times for our stats.

But we don't have any dice! Let's create a class that will act as our virtual dice bag!

Implementation

Create a Utility Package

First, let's create a new package, “net.dizzydragon.rustybox.util”, to hold our utility classes, like this bag o' dice. To do this, right-click on your “src” directory and select New→Package. Enter “net.dizzydragon.rustybox.util” in the Name field, and press Finish. Your new package should magically appear under the “src” directory.

Create the Dice Class

Then, create the Dice class by right-clicking on the util package that you just created, and select New→Class. Enter “Dice” in the Name field, and press Finish.

You should now have something that looks like this:

package net.dizzydragon.rustybox.util; 
 
public class Dice 
{ 
 
}

If you have something that looks different, edit it to look the same as the above code. Next time, make sure you don't have anything weird like “generate main” or “abstract” or “final” selected in the dialog where you create a new class.

Create an Instance of a Random Number Generator

We'll use Random, java's built-in Pseudorandom Number Generator to simulate the throw of a die. So let's add that to our class.

package net.dizzydragon.rustybox.util; 
 
import java.util.Random; 
 
public class Dice 
{ 
	final private static Random	random	= new Random(); 
 
}
  • import java.util.Random; - This tells the compiler what system package it should look for the Random class in. If the external classes you use in your code are in a different package than the one your source file is in, then you must import them like this.
  • final - This keyword tells the compiler that the object contained in the random field will never be changed. That doesn't mean that the random values being generated will never change, but that the random number generator object that is actually generating the random numbers will never be replaced with a different one. When a field is marked as final like this, the compiler can do some optimization to make the code run a little faster.
  • private - This keyword tells the compiler that the random field is to only be accessable from within this file. This is to encapsulate the internal implementation of the dice bag away from its external interface. This way, we could replace the Java builtin random number generator with, say, hamsters drawing pingpong balls out of a jelly jar at a later date, and the rest of your code can just go on working without worrying that the internal implementation of the dice bag has changed.
  • static - This keyword makes the random field global to the whole class, instead of a separate instance for each Dice object that is instantiated. We'll actually be making all of the methods of the Dice class static as well, and adding a Private Void Constructor to prevent instances of the class from actually being instantiated anywhere. Computer Science Professors like to call this a Singleton Pattern, and yell at us for using them. But in the real world, it is much less of a headache (not to mention more efficient) to just “use one global bag of dice” (singleton), rather than “creating a new bag of dice” (normal instantiated object with non-static fields and methods) every time we want to roll something. In fairness to the CS Professors, it can be dangerous to use singleton patterns in multithreaded code, but ours is (1) not multithreaded (at least outside of the SimpleIO library, which is written to ensure that the game code only runs in a single thread) and (2) Java's Random class has been thread-safe since Java5 anyway.
  • random - This is the name of the field containing the instance of the random number generator. We can generate random numbers in our code by calling its methods, ie random.nextInt().
  • new Random() - This creates an instance of Random and places it (or more accurately, a reference to it, but more on that later) into the random field.

Write a Method to Roll a Die

Now let's write a method to roll a die.

package net.dizzydragon.rustybox.util; 
 
import java.util.Random; 
 
public class Dice 
{ 
	final private static Random	random	= new Random(); 
 
	final public static int d( int sides ) 
	{ 
		// Input validation 
		if( sides <= 0 ) 
			sides = 1; 
		// Generate and return a result 
		return random.nextInt( sides ) + 1; 
	} 
}

Now we can roll a d6 by calling Dice.d(6)!

Notice that the method is final and static. Technically, all static methods are by nature final, but we're anally retentive about such things at DDG!

Note that the d() method is public instead of private, though. This means that any code, anywhere in our game, call call the method to roll a die.

The method returns an int, aka integer, a nonfractional number.

And it takes an int sides as an argument, specifying the number of sides that the die has. Unlike real dice, we can even make a d7 that has a linear result distribution!

This part of the method…

		if( sides <= 0 ) 
			sides = 1;

… performs input validation, ensuring that nobody tries to roll a die with zero or a negative number of sides, because that would be silly! (And it would cause the random number generator to throw an exception and crash the game!)

Then we just call random.nextInt( sides ), which generates a random number between 0 (inclusive) and the value of sides (exclusive). That is, if sides equals 6, then the generated number is 0-5. We add 1 to the random number (for a value of 1-6 in the previous example), and return it, so that whatever code wanted to roll a die can do something useful with the value.

Add a Method to Roll Several Dice and Total Them

That's all fine and good, but what if we want to roll, say, 3d6 for stats? Should we just generate a number between 3 and 18, using our random object?

No!!! If we do that, the results will be linear, instead of bell shaped, and the results won't accurately reflect rolling a real 3d6!

So instead, let's write a method to roll a handful of the same sized die!

	final public static int d( int num, int sides ) 
	{ 
		// Input validation 
		if( num <= 0 ) 
			num = 1; 
		if( sides <= 0 ) 
			sides = 1; 
		// Generate and return a result 
		int total = 0; 
		for( ; num > 0; num-- ) 
			total += d( sides ); 
		return total; 
	}

This method takes two arguments, the number of dice to roll and the number of sides that the dice have. So to roll 3d6, we'd call Dice.d(3,6).

After the input validation, the method runs a loop. It rolls a die each time through the loop, using the d() method that we wrote before, and adds the result to a total.

Each time through the loop, it decrements (subtracts one) from the number of dice being rolled. When the number of dice being rolled is no longer greater than zero, the loop exits, and the total is returned.

You can visualize it like this: Hold up three fingers. Roll one d6 add it to your total (which starts at zero). Put down one finger. Do you still have any fingers held up? Yes? Ok, do it again, and put another finger down. Do you still have any fingers held up? Yes? Ok, do it again, and put yet another finger down. No fingers this time? Ok, there's your 3d6 result.

Add a Method to Roll Several Dice and Total Them, then Apply a Modifier

Often, we need to roll a result and apply a modifier to it, and treat any results less than one as one. Rolling combat damage, for example. Let's write a method to do that, too!

	final public static int d( int num, int sides, int bonus ) 
	{ 
		// Input validation 
		if( num <= 0 ) 
			num = 1; 
		if( sides <= 0 ) 
			sides = 1; 
		// Generate a result 
		int total = d( num, sides ) + bonus; 
		// Output validation 
		if( total < 1 ) 
			total = 1; 
		// Return the result 
		return total; 
	}

We do our input validation as usual, then we generate a result by calling the d(n,s) method we just wrote, and adding the modifier (which may be negative) to the result, and storing it in the total variable.

Then we do something new, output validation. In this case, if the total is less than one, then we set it to one, ensuring that the method always generates a number equal to at least 1.

Then we return the result, as usual.

Uhhhh, you didn't actually need to do some of that input validation!

That is true, the d(sides) method could just be relied upon to do some of the validation for d(num,sides), and d(num,sides,mod) doesn't need any input validation at all!

Indeed, in the so-called real world, I probably wouldn't have done the unneeded validation. But I'm trying to be kind to our readers who are new to programming. They'll be producing more bugs than you or I, who have been at it a little longer. So I want them to get in the habit of trying to write almost stupidly robust code, because that will make it easier for them to find their mistakes and fix them!

Bear with me. We'll be doing things that are more Fun™ soon. ;P

Cleanup!

Our implementation isn't quite done yet!

You may see that eclipse is still generating some warnings. This is probably due to silly things like unused imports, unused local variables and methods, and other such things.

But some warnings can be indications that there is something more serious wrong! Something that might compile, but not function properly.

Be pedantic! Quash all those warnings! Make your code compile perfectly cleanly! If nothing else, you'll save time later by not having to sort through an aggravatingly long warning list to find the actual errors that you're looking for!

Document It!

Documenting your code is very important! Java defines a standard way of formatting comments, called JavaDoc. You don't have to use javadoc formatted comments, but if you do, there are tools to magically generate separate HTML documentation from it, which is handy! Also, Eclipse reads the stuff that it displays in the context-sensitive help (when you hover the mouse over various things) from the javadocs.

JAutoDoc is an Eclipse plugin that makes is easier to keep up with the javadoc markups. I usually write the whole class/interface first, then add the javadoc when I am done.

Here is our Dice class after documenting it, with a zLib style open-source license.

FIXME In progress….

Dice.java (Class)

/* 
 * Copyright (C) 2012 L. Adamson 
 *  
 * This software is provided 'as-is', without any express or implied warranty. 
 * In no event will the authors be held liable for any damages arising from the 
 * use of this software. 
 *  
 * Permission is granted to anyone to use this software for any purpose, 
 * including commercial applications, and to alter it and redistribute it 
 * freely, subject to the following restrictions: 
 *  
 * 1. The origin of this software must not be misrepresented; you must not claim 
 * that you wrote the original software. If you use this software in a product, 
 * an acknowledgment in the product documentation would be appreciated but is 
 * not required. 
 *  
 * 2. Altered source versions must be plainly marked as such, and must not be 
 * misrepresented as being the original software. 
 *  
 * 3. This notice may not be removed or altered from any source distribution. 
 *  
 * L. Adamson leaf@dizzydragon.net 
 */ 
 
package net.dizzydragon.rustybox.util; 
 
import java.util.Random; 
 
/** 
 * A singleton class containing static methods that emulate a box of dice. 
 *  
 * It uses a static instance of java.util.Random, and as such may not be  
 * thread-safe on pre-Java5 virtual machines. 
 */ 
public class Dice 
{ 
	final private static Random	random	= new Random(); 
 
	/** 
	 * Roll a single die of the specified size. 
	 *  
	 * @param sides 
	 *            the size of the die to roll, ie the number of sides 
	 * @return a random value between 1 and the value of sides, inclusive. 
	 */ 
	final public static int d( int sides ) 
	{ 
		// Input validation 
		if( sides <= 0 ) 
			sides = 1; 
		// Generate and return a result 
		return random.nextInt( sides ) + 1; 
	} 
 
	/** 
	 * Roll a number of dice of the specified size and return the total. 
	 *  
	 * @param num 
	 *            the number of dice to roll. 
	 * @param sides 
	 *            the size of the dice to roll, ie the number of sides 
	 * @return the total of the values generated by rolling the specified number 
	 *         of dice of the specified size. 
	 */ 
	final public static int d( int num, int sides ) 
	{ 
		// Input validation 
		if( num <= 0 ) 
			num = 1; 
		if( sides <= 0 ) 
			sides = 1; 
		// Generate and return a result 
		int total = 0; 
		for( ; num > 0; num-- ) 
			total += d( sides ); 
		return total; 
	} 
 
	/** 
	 * Roll a number of dice of the specified size and apply the given modifier. 
	 * If the result is less than 1, then 1 is returned. 
	 *  
	 * @param num 
	 *            the number of dice to roll. 
	 * @param sides 
	 *            the size of the dice to roll, ie the number of sides 
	 * @param bonus 
	 *            the bonus/penalty to apply to the total of the roll. 
	 * @return the total of the values generated by rolling the specified number 
	 *         of dice of the specified size, plus the bonus, but not less than 
	 *         1. 
	 */ 
	final public static int d( int num, int sides, int bonus ) 
	{ 
		// Input validation 
		if( num <= 0 ) 
			num = 1; 
		if( sides <= 0 ) 
			sides = 1; 
		// Generate a result 
		int total = d( num, sides ) + bonus; 
		// Output validation 
		if( total < 1 ) 
			total = 1; 
		// Return the result 
		return total; 
	} 
}

Run It!

Now let's open up Test.java, remove the “Hello, Adventurer” line, and add some code to test our dice bag!

/* 
 * Copyright (C) 2012 L. Adamson 
 *  
 * This software is provided 'as-is', without any express or implied warranty. 
 * In no event will the authors be held liable for any damages arising from the 
 * use of this software. 
 *  
 * Permission is granted to anyone to use this software for any purpose, 
 * including commercial applications, and to alter it and redistribute it 
 * freely, subject to the following restrictions: 
 *  
 * 1. The origin of this software must not be misrepresented; you must not claim 
 * that you wrote the original software. If you use this software in a product, 
 * an acknowledgment in the product documentation would be appreciated but is 
 * not required. 
 *  
 * 2. Altered source versions must be plainly marked as such, and must not be 
 * misrepresented as being the original software. 
 *  
 * 3. This notice may not be removed or altered from any source distribution. 
 *  
 * L. Adamson leaf@dizzydragon.net 
 */ 
 
package test; 
 
import net.dizzydragon.rustybox.simpleio.SimpleIO; 
import net.dizzydragon.rustybox.simpleio.SimpleRunnable; 
import net.dizzydragon.rustybox.util.Dice; 
 
public class Test extends SimpleRunnable 
{ 
	private static final long	serialVersionUID	= 1L; 
 
	public static void main( String[] args ) 
	{ 
		runLocal( new Test() ); 
	} 
 
	@Override 
	public void run( SimpleIO io ) 
	{ 
		io.println( "d10: "+Dice.d(10) ); 
		io.println( "3d6: "+Dice.d(3,6) ); 
		io.println( "d100: "+Dice.d(100) ); 
		io.println( "2d4-3: "+Dice.d(2,4,-3) ); 
	} 
}

Save it, and then right-click on Test.java and select Run As→Java Application.

Tada!

What's Next?

In the next chapter, we'll create a barebones PlayerCharacter class, and then use our spanky new bag o' dice to roll up some stats!

Exciting!

Source Code

The source code for this chapter can be found in the Subversion Repository.

If you are interested in changing/modifying the code, you can download it into your Eclipse workspace using Subclipse as detailed here: TUTORIAL: Using Subclipse.

First PagePrevious PageBack to overviewNext PageLast Page

rustybox/book/003_rolling_dice.txt · Last modified: 2012/09/09 00:28 (external edit)

Copyright © 2009, 2012-2014 by L. Adamson, unless otherwise stated.
If you see something here that you like, and find it useful or learn something, please consider making a quick, easy, and secure donation via PayPal.
Your support is what keeps this whole thing going!