I recently completed my first Java programming project: a simple command line application built to provide random character ideas for the 5th edition of Dungeons and Dragons. Some of the final program’s outputs can be seen above. What follows will be a deep dive into the code that makes up the application, detailing the techniques used and choices made in its development.
What’s the best place to start with a random generator application? Probably at the random generation utility itself. To begin with, I imported the java.util.Random class and created a new Random instance that I named rand. Random.nextInt() is a handy function that generates a random integer between -2147483648 and 2147483647 by default, or between 0 (inclusive) and a designated integer (exclusive) if that integer is entered as an argument.
In this case, I wanted my function to choose a random string element in an array and return that string. The return statement above randomly generates an integer with 0 as a minimum and the length of the entered array minus one as a maximum, and then returns the element in the array at that randomly generated index. This randomize function was at the heart of almost all of the more complex functions in the program.
Welcome to the easiest, and yet most time-consuming, part of the program! I manually entered arrays of all of the races, classes, and backgrounds available to characters in Dungeons and Dragons. There was very little technical skill to this part; just a whole lot of digging through my D&D books. These arrays had to be made so that the randomize function I’d already created could be called to randomly choose an element of each.
This was the actual main function, which just called randomize on each of the four arrays above to choose one of each for any given character. After creating this basic utility, you can see that I added two more complex functions to the generator: a function to assign an archetype based on class, and a function to assign a name based on race and gender.
In Dungeons and Dragons, an archetype or subclass is a specific specialization within a character’s class. For instance, a fighter can choose to specialize in archery, mounted combat, dueling, and so on and so forth. Step one in implementing archetypes into my program was to create a new set of arrays for each class, detailing their available subclasses. Again, this was tedious but simple.
Since a character’s choice of archetype depends on their class, I couldn’t just use the base randomize function for this the same way that I had done for gender, race, class, and background. Instead, I created a new function that accepted an argument of the character’s class and used a case statement to designate which array needed to be randomly chosen from.
Implementing names was by far the most tedious part of this project and, in hindsight, I’m not sure it was worth it. At the very least, though, it allowed me to get some practice in using a different data type than the arrays I had grown so accustomed to: the hash map.
While I did initially enter in the available names as several arrays of race- and gender-specific example names, I found myself in a bit of a bind when it came down to implementing the actual name-choosing function. There are far more races in D&D than there are classes. Did I really want to create a case statement with options for each race/gender combination? That would get unwieldy fast, not to mention repetitive when it came to races that used exclusively unisex names or races that had irregular name lists. (Half-elves, for instance, don’t come with their own list of names but rather are specified as using either elven or human names. Creating three new arrays that only contain a mix of elements from two existing arrays seemed like such a waste!) Ultimately, after some research, I decided to forego the arrays of names and create one big hash map of names instead.
Each key/value pair in the hash map included a string key that specified race and gender (if applicable) and an array of possible names in the value. It was now much easier to call a specific array of names just by inputting the race and gender of the character being generated.
Here, the giveName function accepts race and gender as arguments, and then uses an if/else statement to determine whether or not the provided race has gender specific names. If the race has unisex names, then the randomize function is called on that race’s name array. If not, then another if/else statement exists to choose the correct list of gendered names. In the specific case of non-binary characters, giveName recursively calls itself to randomly choose a name from either the male or female name arrays of the given race. This worked perfectly for races with regular naming schemes! For irregular races (like half-elves, as I mentioned above), I had to get a little less elegant and a little more specific.
Note the use of the pickFromTwo function, which is another randomizing function I created to randomly choose one of two strings. Each code block here recursively calls giveName, but with references to the name array of a different race. Aasimar, for instance, use human names, so in the case of aasimar characters, I just had the function look for a name of the appropriate gender for a human character. Some of these are messy — note the genasi case, which uses my pickFromTwo function to actually pick from five different racial name options: human, elf (both thanks to the half-elf case below it), gnome, dwarf, and halfling. Were this a larger project that I was working with multiple people on, I would definitely want to add in more code comments to make sure it was clear what was happening in each block.
The full code — and, by extension, the program, if you want to randomly generate some character ideas yourself — is available here at Github. Hopefully this has provided some food for thought for those interested in making a similar Java program!