A predictable (game) random generator May 28, 2009
Posted by pageman in Gaming / GameDev News & Articles.Tags: game frustration, game mechanics, Java, predictable random generator, randomness, Ruby, shufflebag, StackOverflow
trackback
I just ran across this question in StackOverflow:
“I’m a web-game developer and I got a problem with random numbers. Let’s say that a player has 20% chance to get a critical hit with his sword. That means, 1 out of 5 hits should be critical. The problem is I got very bad real life results — sometimes players get 3 crits in 5 hits, sometimes none in 15 hits. Battles are rather short (3-10 hits) so it’s important to get good random distribution.”
Sounds familiar? Here’s a “game mechanics” situation from GameDev.net as suggested by a commenter:
“A blade spider is at your throat. It hits and you miss. It hits again and you miss again. And again and again, until there’s nothing left of you to hit. You’re dead and there’s a two-ton arachnid gloating over your corpse. Impossible? No. Improbable? Yes. But given enough players and given enough time, the improbable becomes almost certain. It wasn’t that the blade spider was hard, it was just bad luck. How frustrating. It’s enough to make a player want to quit.“
Obviously, there has to be a way to do it. One suggestion was to use a shufflebag:
“In games or educational programs you often don’t want real randomness. Unsurprisingly random it’s often a bit too random. You may get some items several times in a row and others too rarely. It’s either too easy or too hard. Balancing the weights is hard as well, because each run is too different. And if that wouldn’t be already bad enough; it’s also difficult to verify the results.
Here’s the code of the commenter based from his own java code: import java.util.ArrayList; /** private final Random random; /** public ShuffleBag() { public ShuffleBag(Random random) { public void add(T value) { public T get() { private int randomUnused() { private void markAsUsed(int indexOfUsed) { private static void swap(List list, int x, int y) { public void addMany(T value, int quantity) { public List getMany(int quantity) {
package net.orfjackal.puzzlewarrior;
import java.util.List;
import java.util.Random;
* @author Esko Luontola
* @since 25.2.2008
* @originally here: http://bit.ly/9vgIV
*/
public class ShuffleBag {
* Unused values are in the range {@code 0 <= index < cursor}.
* Used values are in the range {@code cursor <= index < values.size()}.
*/
private final List values = new ArrayList();
private int cursor = 0;
this(new Random());
}
this.random = random;
}
values.add(value);
}
if (values.size() == 0) {
throw new IllegalStateException("bag is empty");
}
int grab = randomUnused();
T value = values.get(grab);
markAsUsed(grab);
return value;
}
if (cursor <= 0) {
cursor = values.size();
}
return random.nextInt(cursor);
}
cursor--;
swap(values, indexOfUsed, cursor);
}
T tmp = list.get(x);
list.set(x, list.get(y));
list.set(y, tmp);
}
for (int i = 0; i < quantity; i++) {
add(value);
}
}
List results = new ArrayList(quantity);
for (int i = 0; i < quantity; i++) {
results.add(get());
}
return results;
}
}
If you’re not into Java, here’s ShuffleBag code in Ruby:
|
class Fairish def initialize(probability, unfair_low, unfair_high, min_rolls) @probability, @unfair_low, @unfair_high, @min_rolls = probability, unfair_low, unfair_high, min_rolls @rolls, @hits = 0, 0 end def fire! private class Random def fire! trials = 100000 probability = 0.2 trials.times do |n| trial_size.times do |x| fairish_out_of_bounds_low += 1 if fairish_hits.to_f / trial_size upper_bound puts "Fairish was out of bounds #{(fairish_out_of_bounds_low + fairish_out_of_bounds_high).to_f/trials * 100}% of the time (#{fairish_out_of_bounds_low.to_f/trials * 100}% low and #{fairish_out_of_bounds_high.to_f/trials * 100}% high)." |
Happy ShuffleBagging!
posted by Paul “The Pageman” Pajo




Comments»
No comments yet — be the first.