This article is mainly to list some common lottery probability models in the game, and do a brief analysis and discussion of them (this article is the second in the series from simple books to nuggets).

Games often have random lotteries designed to increase gameplay and revenue points. Such as raffle cards, random gift packs, daily raffle opportunities and so on. They all fall into the following two categories:

1. Pure random probability calculation:

By pure randomness, I mean a random situation in which the random result is not affected by the outside world and the random model and random probability remain constant.

Introduction to the model: There will be N kinds of items in the random treasure chest, and the chance of obtaining each item has been fixed. The chance of obtaining these items will not be different because of the number of times you open the treasure chest or other external factors (such as the amount of player recharge) (no privilege, no guarantee, African people do it all). It doesn’t matter if you do it 10 times or 100 times; No matter big R or white fuck, all people are equal. Implementation: Implementation is simple, just make sure to use the right random function.

    int randomRewardId(a) {
        int rewardFactor = random(1.100);
        return getRewardIdByFactor(rewardFactor);
    }

    // Select a random number from min-max (min and Max)
    int random(int min, int max);

    // Get different rewards based on the rewardFactor
    int getRewardIdByFactor(int rewardFactor);
Copy the code

2. Pseudo-random probability calculation:

What I mean by pseudo-randomness is that the player’s actions or the consequences of those actions have an impact on subsequent random strategies and outcomes.

Pseudo-random model 1: the probabilistic model of bottom guarantee:

Introduction to the model: The bottom guarantee model mainly considers the game experience of African players. Such as a large R African players, for the sake of a winning probability of 5% gold smoking for 50 consecutive (the probability is around 7%), then the player hit is supposed to be great is likely to lead to swim back even call 12315, one hundred gold main players may appear seven this kind of situation, I estimate is a boss can’t accept it. In order to avoid the occurrence of this situation, there is a guarantee of this design. Africans don’t care. If you draw 20 times and don’t win, I’ll give you a gold card. Implementation method:

  • A simple way to do this: ———— keeps track of how many times a player has not won a gold card in a row (the value is reset upon obtaining the gold card), and if this value equals 20, a gold card is awarded in the next drawing.
    int continuedMissTimes; // The number of gold card draws in a row

    // Random cards
    int randomCardId(a) {
        // 20 represents the number of guarantees, and 5 represents the probability of winning the gold card
        if (continuedMissTimes >= 20 || random(1.100) < =5) {
            continuedMissTimes = 0;
            return 1; / gold/card id
        } else {
            continuedMissTimes++;
            return 2; // Common card ID}}// Select a random number from min-max (min and Max)
    int random(int min, int max);
Copy the code
  • If you want to make this guarantee a bit smoother ———— also record the number of times that the player has not drawn the gold card for several times in a row (reset the value if you get the gold card), according to this number of times calculate the probability of drawing the gold card (the greater the probability is), and ensure that the number of times is 20 when the probability of winning the gold card is 100%.
    int continuedMissTimes; // The number of gold card draws in a row

    // Random cards
    int randomCardId(a) {
        // 20 represents the number of guarantees, and 5 represents the probability of winning the gold card
        if (continuedMissTimes >= 20 || random(1.100) <= calGoldCardProbabilityByContinuedMissTimes()) {
            continuedMissTimes = 0;
            return 1; / gold/card id
        } else {
            continuedMissTimes++;
            return 2; // Common card ID}}// Select a random number from min-max (min and Max)
    int random(int min, int max);

    // Calculate the probability of winning the gold card according to the number of consecutive gold card draws
    int calGoldCardProbabilityByContinuedMissTimes(a) {
        return continuedMissTimes * 95 / 20;
    }

Copy the code

Additional reference: probabilistic Model of 10 Consecutive Draw guarantees

Pseudo random model 2: the probabilistic model of quantity prize of fixed proportion according to the number of sweepstakes:

Model introduction: Assume that only one person can own an item A if it is 100 people, and this proportion must be guaranteed. Implementation: the backend records the total number of lottery, the player will immediately calculate the server at this time of the total number of lottery (counting his lottery), the number of times of the lottery mod 100 is equal to their specified number, then he is in the lottery. The solution to large concurrency is to separate ———— winning, claiming and snatching processes. AtomicInteger is used to determine whether the user wins the lottery.

    AtomicInteger totalPlayerNum = new AtomicInteger(0); // Total number of lucky draws

    boolean randomIsReward(a) {
        int i = totalPlayerNum.incrementAndGet();
        if (i % 100= =50) {
            return true;
        } else {
            return false; }}Copy the code

Pseudo-random model 3: model with increasing probability of lottery failure:

Model introduction: Different from Model 1 (probability model of 10 consecutive draw guarantees), this is not a guarantee scheme. For example, there are rare items, class A items, common items, class B items. Ensure that there is only one chance of winning class A items in every 4 times. The probability of winning Class A items in the 1st, 2nd, 3rd and 4th times is 10%, 30%, 50% and 70% respectively. And as long as you win class A items in 4 times, the probability of winning Class A items in the following 4 times is 0. Make a new cycle every four times. Implementation method: calculate the probability according to The Times, and reset if A is obtained

    int rewardTimes;
    boolean isHit;

    // Random cards
    int randomCardId(a) {
        rewardTimes++;
        int res = 0;
        if(! isHit && random(1.100) <= calGoldCardProbabilityByRewardTimes()) {
            isHit = true;
            res = 1; // A object id
        } else {
            res = 2; // Common item id
        }
        if (rewardTimes >= 4) {
            rewardTimes = 0;
            isHit = false;
        }
        return res;
    }

    // Select a random number from min-max (min and Max)
    int random(int min, int max);

    int calGoldCardProbabilityByRewardTimes(a);

Copy the code

Pseudo-random model 4: Ensure that rare items are drawn last.

As shown above, players can only draw eight times. Prizes are divided into ordinary categories: Ordinary Rune gift box, Gold category: Golden Rune gift box, rare category: Holy Dragon/Odin Hero. In order to improve the payment rate of players, a pseudo-random lottery is designed:

  1. The rare prizes are not drawn until the end (one of them must be drawn for the last time, and the other may be drawn for the sixth or seventh time).
  2. The chance weight of the golden rune pack in the first four draws is 1/10 of that of the regular rune pack.
  3. The winning combination of 8 raffles must be 4 ordinary rune boxes, 2 gold rune boxes, and 2 rare heroes.

The following table shows the weight relationship between the number of times of drawing and the prize (if the player has reached the upper limit of the prize, the weight will be changed to 0)Correlation probability model implementation:

For pure random probability model, there are many implementation schemes. Here I have written a MutexRandomUtil that uses TreeMap’s floorEntry method to achieve pure random probability extraction. The code is as follows:

public class MutexRandomUtil<T> {
    private TreeMap<Double, T> map = new TreeMap<Double, T>();
    private double endRate = 0;

    public MutexRandomUtil(a) {}// Register random item t (random probability is rate)
    public void registerData(double rate, T t) {
        if (rate <= 0 || rate > 1) {
            throw new IllegalArgumentException("ERROR RATE!!!");
        }
        map.put(endRate, t);
        endRate += rate;
    }

    public T random(a) {
        // 0-endRate = 0-endRate = 0-endRate = 0
        double r = MutexRemoveRandom.avgRandom(0, endRate);
        Map.Entry<Double, T> e = map.floorEntry(r);
        returne ! =null ? e.getValue() : null; }}Copy the code

In the case of pseudo-random probability model 1,3, and 4, where once drawn means that there is no chance for a second time, using MutexRandomUtil as a reference, my implementation encapsulates all the items to be drawn and their extraction probability values into MutexRemoveRandom. Each time the random items are drawn from this class, the items drawn are removed, so as to ensure the continuity of the probability and ensure that the prizes won are not repeated. The code is as follows:

public class MutexRemoveRandom<T> {
    private TreeMap<Double, Entry<T>> map = new TreeMap<Double, Entry<T>>();
    private double endRate = 0;
    private static Random r = new Random();

    // Register random item t (random probability is rate)
    public void registerData(double rate, T t) {
        if (rate <= 0 || rate > 1) {
            throw new IllegalArgumentException("ERROR RATE!!!");
        }
        map.put(endRate, new Entry<T>(t, rate));
        endRate += rate;
    }
    
    // Extract the item and remove it
    public T randomAndRemove(a) {
        double r = avgRandom(0, endRate);
        Double key = map.floorKey(r);
        if(key ! =null) {
            T t = map.get(key).t;
            reorganizeMap(key);
            return t;
        } else {
            return null; }}private void reorganizeMap(Double key) {
        TreeMap<Double, Entry<T>> tempMap = new TreeMap<>();
        map.remove(key);
        double tempEndRate = 0;
        for (Map.Entry<Double, Entry<T>> entry : map.entrySet()) {
            tempMap.put(tempEndRate, entry.getValue());
            tempEndRate += entry.getValue().rate;
        }
        this.endRate = tempEndRate;
        this.map = tempMap;
    }

    public static double avgRandom(double min, double max) {
        if (min > max) {
            double temp = max;
            max = min;
            min = temp;
        }
        double rNum = r.nextDouble() * (max - min);
        return rNum + min;
    }

    private static class Entry<T> {
        final T t;
        final double rate;

        Entry(T t, double rate) {
            this.t = t;
            this.rate = rate; }}}Copy the code

There are so many kinds of sweepstakes that it’s impossible to list them all here. When writing about lottery logic, you need to be aware of the following common problems in your code:

  • Correctness: Write several more test cases for testing to ensure that the random results are consistent with the requirements of planning. This problem of testing and planning is not easily detected, so the development of the correctness of the main personnel.
  • Thread safety: an important criterion for this question is ———— whether there are multiple users accessing and modifying common resources (such as item probability query, random number retrieval). If this is the case, you need to ensure that the common resource is thread-safe.
  • Performance issues: Performance issues are also a common concern for many inexperienced developers. For example, I have seen performance problems caused by people creating Random objects in a for loop, asking to pick one of thousands of situations, and then randomly creating thousands of objects each time to pick one of them, and so on, but fortunately this was caught in time and not left online.
  • Clarity of code/comments: The clearer the code is, the better the implementation is and the less likely bugs are to occur. Our random routines are likely to be reused by others, and annotations will help them quickly determine if they are appropriate for the needs they face.