I am participating in the Mid-Autumn Festival Creative Submission contest, please see: Mid-Autumn Festival Creative Submission Contest for details

preface

Some time ago to see a lot of digging friends in soha worked hard to sign the ore, want to draw the nuggets of “Hilton moon cakes”, even at the speed of the code to accelerate the lottery, the result is naturally –Smoked a lonely! I wonder if any of the people who offer automatic lottery scripts are nuggetstora(Big guys beg to pass 😜). It is not easy to sign in every day and save tens of thousands of ores. It seems to be free, but it also takes time and persistence! This article, island code farmers simulation raffle to see yours20000Take a look at what you’ve picked and why you’re lonely from a scientific point of view.

Draw the interface

The first is interface implementation, nine grid layout, we use GridView to do. Here we fixed the number to nine, with the middle bar being the button. The prize before the middle has the same index, and the prize after the middle has to be subtracted by 1 because it is one space apart.

GridView.count(
  crossAxisCount: 3,
  crossAxisSpacing: 10.0,
  mainAxisSpacing: 20.0,
  childAspectRatio: 0.8,
  shrinkWrap: true,
  physics: NeverScrollableScrollPhysics(),
  children: List<Widget>.generate(
    9,
    (index) {
      int lotteryIndex =
          index < (lotteryController.lotteryCardNumber ~/ 2)? index : index -1;
      LotteryEntity lottery = lotteryController.lotteres[lotteryIndex];
      returnindex ! = (lotteryController.lotteryCardNumber ~/2)? LotteryCard( name: lottery.name, cardAssetName: lottery.assetName, color: index == lotteryController.lotteryOrder[ lotteryController.currentIndex] && lotteryController.isRunning ? Colors.green[200]!
                  : Colors.white,
            )
          : LotteryButton(
              name: 'lottery',
              onPressed: lotteryController.isRunning
                  ? null: () { lotteryController.startLottery(); }); },),),Copy the code

LotteryEntity is a prize entity class with only three properties, the picture file name, the prize name, and the probability of winning. Here the probability of winning is an integer, and we will talk later about how to calculate the actual probability from this integer.

class LotteryEntity {
  final String assetName;
  final String name;
  final int probability;

  LotteryEntity({
    required this.name,
    required this.assetName,
    required this.probability,
  });
}
Copy the code

By changing the background color of the card clockwise in order to make it appear to be constantly moving, So when the subscript index = = lotteryController. LotteryOrder [lotteryController. CurrentIndex] and has launched the lottery (when set to true), change the color card, or use white. LotteryController lotteryOrder is state management scratchable latex mobile order table, actually can also be calculated by the program, but we have a lazy, write directly an array [0, 1, 2, 5, 8, 7, 6, 3] said the order of the mobile. We won’t post the code for the card and button, you can go to the source code here: lottery source code.

Lottery business logic

For card movements, we use a timer implementation that moves one space at a certain interval (calling the _loopIndex method). After winning the prize, it slowly slows down (_timerStep += 1, you know) until it reaches the winning position, after which it is reminded of the winning prize.

void _rotateCard(Timer timer) {
  if (_rewardedIndex.value == - 1) {
    _loopIndex();
  } else {
    if (_stepCounter++ > _timerStep) {
      _loopIndex();
      _stepCounter = 0;
      _timerStep += 1;
      if (_lotteryOrder[_currentIndex.value] == _rewardedIndex.value) {
        timer.cancel();
        _isRunning.value = false; _showReward(); }}}}void _loopIndex() {
  _currentIndex.value++;
  if (_currentIndex.value == _lotteryOrder.length) {
    _currentIndex.value = 0; }}void _showReward() {
  Get.defaultDialog(
    title: 'Congratulations! ',
    titleStyle: TextStyle(color: Colors.orange[400]),
    content: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Image.asset(_lotteries[_rewardedIndex.value].assetName,
            width: 60, height: 60),
        Text(
          _lotteries[_rewardedIndex.value].name,
          style: TextStyle(
            color: Colors.orange[400[, [, [, [, [, [, [, [ }Copy the code

We need to pay attention to the subscript conversion here. The subscript _rewardedIndex of the winning prize is the subscript of the prize array, and the subscript of the move is the order of the move in the nine grids. Therefore, whether the nine grids need to be converted to the subscript of the actual prize array is judged, namely: _lotteryOrder [_currentIndex value] = = _rewardedIndex. Value.

Draw logic

The business code is done using the GetX+ timer again. According to the spirit of soha, we can know that the middle probability is about: 66 ore: 70%; Bug: 29.99%. That is to say, the probability of other prizes is less than 1/10,000, we evenly give other prizes here, reduce the Bug, 29.94%, the rest are 0.01% (1/10,000, hit the heart 😂). Integer values give the following array of prizes:

_lotteries = [
    LotteryEntity(
      name: '66 ore',
      assetName: 'images/rock.png',
      probability: 7000,
    ),
    LotteryEntity(
      name: 'Nuggets limited Badge',
      assetName: 'images/medals.png',
      probability: 1,
    ),
    LotteryEntity(
      name: 'Starbucks Mooncakes',
      assetName: 'images/starbuck_mooncake.png',
      probability: 1,
    ),
    LotteryEntity(
      name: 'Bug',
      assetName: 'images/bug.png',
      probability: 2994,
    ),
    LotteryEntity(
      name: 'Byte Mid-Autumn Festival Gift Box',
      assetName: 'images/bytedance_mooncake.png',
      probability: 1,
    ),
    LotteryEntity(
      name: 'Douyin Mooncake',
      assetName: 'images/tiktok_fly.png',
      probability: 1,
    ),
    LotteryEntity(
        name: 'Tik Tok Mid Autumn Moon Cake',
        assetName: 'images/tiktok_mooncake.png',
        probability: 1),
    LotteryEntity(
      name: 'Hilton Moon Cake',
      assetName: 'images/hilton_mooncake.png',
      probability: 1,)];Copy the code

The next step is to calculate the winning prize by probability. Here, we use a numerical interval to realize probability, that is, to compare the randomly obtained value with the interval. If it falls in the numerical interval of a prize, it is considered that the prize has won the prize, and the length of this numerical interval is the value of integer probability value, as shown in the figure below. The value interval array constructed is actually starting from two elements. The value interval value of the same subscript is the probability value of the prize plus the probability value of the prize. The value interval array obtained is [7000, 7001, 7002, 9996, 9997, 9998, 9999, 10000]. If none of them hit the jackpot first.

In order to ensure that the interval is not judged repeatedly, we judge from the largest value interval to the smallest value interval, that is, judge from the value interval array backwards, and start from the second-to-last prize (if the value is greater than the second-to-last prize, it means that the last prize wins). In this way, you can see that the interval between the two adjacent values can actually reflect the probability of winning each prize. The code for calculating the winning prize is as follows:

void _updateRewardIndex(int rewardedValue) {
  var orderedLotteries = _lotteries.map((e) => e.probability).toList();
  // Build incrementing numeric ranges
  for (var i = 1; i < orderedLotteries.length; i++) {
    orderedLotteries[i] += orderedLotteries[i - 1];
  }
  // By default, the first winner
  var orderIndex = 0;
  for (var i = orderedLotteries.length - 2; i >= 0; --i) {
    // If the random value falls higher than the starting value of the current value range, it means that the winning prize falls into the next prize of the corresponding prize in the range
    if (rewardedValue >= orderedLotteries[i]) {
      orderIndex = i + 1;
      break;
    }
  }

  _rewardedIndex.value = orderIndex;
}

Copy the code

The code for drawing prizes is as follows:

Future.delayed(Duration(seconds: 3), () {
  // Take the maximum value of the interval.
  int maxRandomNum = _lotteries
      .map((e) => e.probability)
      .toList()
      .reduce((value, element) => value + element);
  var rewardedValue = Random().nextInt(maxRandomNum);
  _updateRewardIndex(rewardedValue);
});
Copy the code

Disclosure: A game of probability

Ok, now let’s calculate the probability that we will be able to draw prizes other than bugs and minerals with the above probability. Each drawing is independent, so if we draw N times, we should actually calculate the probability of not winning the lottery, and then at the end subtract 1 from the probability of winning the lottery. The probability of not winning the big prize every time is 1-A, where A is the probability of winning small prizes such as bugs and ores, i.e. 99.94%. The formula is as follows:

If you have 20,000 ores, you can draw 100 ores for every 200 ores, and then you compensate by drawing 66 ores. If you can draw 150 ores, the intermediate probability is:

So it’s going to take 20,000 ores, 150 draws, and it’s actually less than a 9% chance of winning the jackpot, so let’s simulate it.

void simulate(int times) {
  int bigReward = 0;
  var orderedLotteries = _lotteries.map((e) => e.probability).toList();

  for (var i = 1; i < orderedLotteries.length; i++) {
    orderedLotteries[i] += orderedLotteries[i - 1];
  }
  int maxRandomNum = _lotteries
      .map((e) => e.probability)
      .toList()
      .reduce((value, element) => value + element);
  for (int i = 0; i < times; i++) {
    var rewardedValue = Random().nextInt(maxRandomNum);
    var orderIndex = 0;
    for (var i = orderedLotteries.length - 2; i >= 0; --i) {
      if (rewardedValue >= orderedLotteries[i]) {
        orderIndex = i + 1;
        break; }}if(orderIndex ! =0&& orderIndex ! =3) {
      bigReward++;
    }
    print('the first$iSecond lucky draw, prizes:${_lotteries[orderIndex].name}');
  }

  print(
      'Number of jackpots:$bigReward, the probability of winning the lottery${(bigReward / times * 100).toPrecision(2)}%. ');
}
Copy the code

So let’s just loop 150 times. As long as we don’t win minerals or bugs, we increment the number of winnings by one, and then we look at the number of winnings.

So why do people still win the lottery? It shows that the probability of winning the lottery is wrong, and the probability of winning the lottery is not exactly equal. So let’s adjust the probability, let’s increase the probability of each lottery to 1% (100 times more likely) and see what happens.

According to the results of our soha, the actual probability should be less than 1%, but should be higher than 0.01%, but the actual probability of winning, only the nuggets operating executives know.

conclusion

This article through the simulation of the lottery “revealed” the probability of the nuggets lottery game, only for reference, we can decide whether to continue to draw, anyway, I will not draw 😎!

I am dao Code Farmer with the same name as my wechat official account. This is a column about the introduction and practice of Flutter, providing systematic learning articles about Flutter. See the corresponding source code here: The source code of Flutter Introduction and Practical column. If you have any questions, please add me to the wechat account: island-coder.

👍🏻 : feel the harvest please point a praise to encourage!

🌟 : Collect articles, easy to look back!

💬 : Comment exchange, mutual progress!