So, I play an RPG called Earthdawn; it’s a lot like D&D, but for real nerds.
One of the things we all do in my gaming group is write our own dice rollers; rolling actual dice is SO passé–and there’s an ongoing argument about whether or not a seeded random is more or less random than the natural flaws in dice and rolling surfaces. Java is the language in which I learned to write math, so I somewhat naturally write algorithms in Java without thinking. It’s easy enough to translate this into C# or whatever.
Ok, so, here’s the algorithm. In Earthdawn, dice rolls are predicated on the step level of the difficulty. You may have an attack roll at step 18 and a damage roll at step 22. In 3rd edition Earthdawn, that translates to rolling d12+d10+d8 to attack, and 2d12+2d6 for damage. Here’s the chart (click to embiggen):
As you can see, there’s some kind of progression here; it turns out that the algorithm is a simple infinite series. There’s a jump in the number of dice every seven steps. Hence, the algorithm has a few simple steps:
(1) Divide the step number by 7.
(2) Determine and store the floor and the modulus.
(3) Roll a number of d12s equal to (floor – 1).
(4) Roll dice equal to the corresponding modulus (the first addition of dice past the 7 threshold will be 2d6, so if the modulus is 1, 2d6 are rolled and added).
That is the step algorithm such that no lookup is now necessary; Earthdawn has exploding dice and epic fails, however, so two things are necessary. Look at the exploding dice method; if you roll the maximum value of a die, you can roll it again. You can keep rolling that die until a value shows that is less than the maximum value, such that a d6 rolled with a result of 6 can be rerolled. On the second roll, 6 results. On the third roll, 2 results, so the total value of that die roll is 14. For epic fails, if you roll more than one die and all dice show ones, you have epically failed (similar to a fumble in D&D, and with equivalent disastrous results).
Here’s my dice roller; click to embiggen:
So, here’s the code. It’s intended to be self-contained (you can see that I use a pic of Captain Malcolm Reynolds; just drop a pic in your file structure and reference it in the code if you want a background). Obviously this is bare-bones; you can adapt it at your leisure and to whatever GUI you desire. Any suggestions are welcome; I am always debugging this (and the JavaScript I’m using to display syntax highlighting is a little cranky, so forgive any indentation issues). If you can think of a way to optimize this algorithm, let me know; I always need bragging rights over the guys
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | package dice; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; @SuppressWarnings("serial") public class ImagePanel extends JPanel implements ActionListener{ int six = 6; int eight = 8; int ten = 10; int twelve = 12; int dice = 0; //counter for total number of dice public int middle; //step value entered by me. public boolean fail; //whether or not an epic fail happened. public JFrame frame; public Random r; Image img = new ImageIcon("img/MalcolmReynolds13.jpg").getImage(); SpinnerModel stepEntry = new SpinnerNumberModel(1, 1, 300, 1); SpinnerModel karmaCounter = new SpinnerNumberModel(25, 0, 25, 1); JSpinner stepSpinner = new JSpinner(stepEntry); JSpinner karmaSpinner = new JSpinner(karmaCounter); private JTextField diceResult; private JButton myButton; private JCheckBox myCheck; private JLabel enterStep = new JLabel("Enter Step Here."); public static void main(String[] args) { ImagePanel panel = new ImagePanel(new ImageIcon("img/MalcolmReynolds13.jpg").getImage()); JFrame frame = new JFrame("Tarah's Dice Roller Of Awesomeness"); frame.getContentPane().add(panel); frame.pack(); frame.setVisible(true); } public ImagePanel(String img) { this(new ImageIcon(img).getImage()); } public ImagePanel(Image img){ this.img = img; Dimension size = new Dimension(img.getWidth(null), img.getHeight(null)); setPreferredSize(size); setMinimumSize(size); setMaximumSize(size); setSize(size); JPanel panel = new JPanel(); add(panel, "Center"); myButton = new JButton("Roll The Dice."); myButton.addActionListener(this); myButton.setBorder(BorderFactory.createLineBorder(Color.black)); diceResult = new JTextField("Roll Result", 9); diceResult.setBorder(BorderFactory.createLineBorder(Color.black)); myCheck = new JCheckBox("Use Karma.", false); myCheck.setBorder(BorderFactory.createLineBorder(Color.black)); GridLayout myGrid = new GridLayout(3, 2); panel.setLayout(myGrid); panel.setBorder(BorderFactory.createLineBorder(Color.black)); panel.add(enterStep); panel.add(stepSpinner); panel.add(myButton); panel.add(diceResult); panel.add(myCheck); panel.add(karmaSpinner); setVisible(true); } public void paintComponent(Graphics g) { g.drawImage(img, 0, 0, null); } public void actionPerformed(ActionEvent e) { boolean useKarma = false; middle = (Integer)stepSpinner.getValue(); System.out.println("actionPerformed() thinks the step number is: " + middle); if (myCheck.isSelected() == true) { System.out.println("Using Karma."); useKarma = true; int decrease = ((Integer)karmaCounter.getValue()) - 1; karmaCounter.setValue(decrease); } String s = Integer.toString(rollTheDice(useKarma, middle, fail)); diceResult.setText(s); } ///////////////////////////////////////////// /////////////////MATHYNESS/////////////////// ///////////////////////////////////////////// //This is the Earthdawn Exploding Dice Method. public int d (int die){ int sides = die; int result = 0; int roll; do { r = new Random(); roll = r.nextInt(sides) + 1; result = result + roll; System.out.println("This is a d" + sides + " roll with result: " + result); } while (roll == sides); return result; } public int oneToSeven (int o) { int result = 0; if (o == 1) { result = d(six) - 3; if (result < 1) { result = 1; } } if (o == 2) { result = d(six) - 2; if (result < 1) { result = 1; } } if (o == 3) { result = d(six) - 1; if (result < 1) { result = 1; } } if (o == 4) { result = d(six); } if (o == 5) { result = d(eight); } if (o == 6) { result = d(ten); } if (o == 7) { result = d(twelve); } return result; } public int prefix (int p) { int prefixTotal = 0; for (int i=1; i<p; i++) { prefixTotal = prefixTotal + d(twelve); dice++; } return prefixTotal; } public int suffix (int s){ int suffixTotal = 0;; if (s == 1){ suffixTotal = d(six) + d(six); } if (s == 2){ suffixTotal = d(eight) + d(six); } if (s == 3){ suffixTotal = d(eight) + d(eight); } if (s == 4){ suffixTotal = d(ten) + d(eight); } if (s == 5){ suffixTotal = d(ten) + d(ten); } if (s == 6){ suffixTotal = d(twelve) + d(ten); } if (s == 0){ suffixTotal = d(twelve); } return suffixTotal; } public int step (int sn){ int result = 0; fail = false; int full = sn / 7; //will yield the d12s int mod = sn % 7; //will yield the additive dice if (sn < 8) { int total = oneToSeven(sn); result = total; } if (sn >= 8) { int d12s = prefix(full); int rest = suffix(mod); result = d12s + rest; if (result == full+1) { fail = true; } } return result; } public int rollTheDice(boolean addKarma, int stepValue, boolean epicFail) { boolean addK = addKarma; dice = 0; int result = 0; result = step(stepValue); epicFail = fail; if (addK == true) { int dK = d(six); result = result + dK; if (epicFail == true && dK == 1) { JOptionPane.showMessageDialog(frame, "Epic FAIL."); } } else if (addK != true && epicFail == true) { JOptionPane.showMessageDialog(frame, "Epic FAIL."); } return result; } } |









