Professional HMI project design

Image by katemangostar on Freepik

Image by katemangostar on Freepik

Explained in all steps

In the beginning, there is always an idea for a new project. The worst you can do at this very moment is to launch the Nextion editor and, if needed, the Arduino IDE, and start immediately coding… Before you do the first step, you have to know exactly where you want to go, says an old proverb. In this blog and the following over the next weeks, we’ll look at how to define this “exactly where you want to go”. The proceeding is quite similar to what you can see in cooking shows at the TV: They first think about what they’ll cook, then they make sure that they have all required ingredients at hands, prepare and pre-process them, the famous “mise en place” before everything is added consecutively in an optimal order to the pot or pan, on the stove or in the oven, which is called the “assemblage“.

Cooking (=successfully preparing a meal) is mostly about strategy, availability and timing. And developing a combined hardware and software project is about… Do you see it?

The example project

As you might already have guessed it from the picture above, we’ll realize a virtual slot machine on our Nextion HMI. I decided for that because it is entertaining and ludicrous in the first place, and second, because this gives us the occasion to walk through many aspects: Gaming theory (which is basically mathematics), then translating this into an algorithm first, and later into code, and designing a dynamically animated GUI which interacts in a bidirectional way with our algorithm. So, there is at least a bit of everything which makes developing interesting. In order to “virtualize” a slot machine, we’ll first have to analyze how it works. If we didn’t understand all details of its operation, how could we simulate one on our Nextion HMI?

The principle of operation

Basically, in its simplest form, it’s about inserting a coin which triggers the start of three wheels with different symbols painted on their circumference, spinning at relatively high speed. These will continue to spin until there is a stop condition, either a button press (or leaver action on older mechanical versions), or a timeout. When the stop condition is given, the wheels’ spinning speed will slow down until they stop. In case all three wheels show the same symbol at the front window, the player will get a reward which is not immediately paid out but added to their credits to incite them to continue gambling. That’s an important move by the owner of the machine, because he earns the more the longer the players are using it (see the gaming theory paragraph below). In all other cases, the inserted coin is lost for the player and remains in the machine as a stock for rewarding future gains or as a benefit for the owner.

The game theory

Each of the three wheels has the same n symbols on its outside. If these finally stop at purely random positions, there are thus n * n * n or n^3 possible patterns. But only n of these, from 0  0  0 to (n-1)  (n-1)  (n-1) fulfill the condition to have three identical symbols in a line. To better visualize this, let’s imagine that there are 10 different symbols on each wheel, the numbers from 0 to 9. Thus, n = 10. Now, it should be easy to see that there are 10^3 = 1000 possible outcomes, all numbers from 000 to 999. But only 10 of them, 000, 111, 222, 333, 444, 555, 666, 777, 888, and 999 will give a reward. The chance to get a reward is thus n / n^3 which simplifies to 1 / 10^2 or 1 / 100 or 1%. Now, if a player plays 1000 (n^3) rounds, he will pay one coin for each round which makes 1000 coins available for rewards. Since during these 1000 rounds, the player will see (on statistic average) 10 (n) times a reward situation, the reward could be 1000 / 10 = 100 (n^2) coins if all win situations give the same reward.

But unfortunately, real life isn’t simple mathematics like that. First, because the owner of the machine wants also to get their part of the inserted coins to pay back the investment for the machine, and its costs of maintenance and operation (direct costs). And second, because the owner of the machine needs also to have some interest rate for their investment and for creating a buffer in case the statistical balance does not happen during exactly the first n^3 rounds but perhaps later because a fortunate player could theoretically win a reward in the first round and immediately stop playing without “paying back” with 99 consecutive mishaps…

That’s why, for example, the state lotteries in Germany and France pay out only about half the sum of the stakes as winnings. Part of the retained sum still goes to welfare, arts and sports promotion, but the vast majority of the rest is used to cover all risks and costs.

Now you might wonder how a casino makes its money with French roulette. At first glance, it looks as if the stakes are mathematically correct and completely paid out again as winnings. With red or black, or with even or odd, there seems to be a 50% chance of winning, and you actually get your bet doubled. If you bet on one of the 36 numbers, you also get 36 times your stake if you win. How does the casino pay its employees, heating and lighting? Even in roulette, you can’t beat the math and the old saying “the bank always wins” applies. The answer? In addition to the 36 pits for the numbers from 1 to 36, there is another pit for the zero and sometimes another for the double zero in the roulette wheel. Since the zero and, if present, the double zero are neither red nor black, and both are considered neither even nor odd, this means that the operator collects 1/37 (2.7%) or, in the variant with double zero, even 2/38 (5.2%) of the stakes. And nobody thinks anything bad about it…

The game psychology

As we have seen above, the chance for whatever win on our slot machine is 1 / n^2 and we could thus basically give a reward of somewhat less than n^2 coins for each win. But with this simple formula, the player can easily find out how high the benefit for the operator is and risks to be demotivated to continue. With n=10, it is easily seen that each win should give 100 coins, but if the operator only redistributes for example 75, you won’t feel motivated to play multiple rounds. Thus, a few more things are possible: The first and easiest is to increase the number of symbols on the wheels. With n=16 instead of 10, the theoretically possible reward becomes 256 coins. When retaining again 25% of the stakes, the reward would be 192 coins which is more than the double of the 75 we’ve seen before! Who would not be amazed but start over doubting the formula and find out that the operator’s part will increase by the same proportion?

The second thing is the lack of incentives to continue after the first win if all possible outcomes which happen on average every n^2 rounds give the same reward? Thus, we should try to modify the reward repartition, so that the n different outcomes give different rewards, so that our human player, after earning a first reward which might be lower that the expected n^2 coins, will continue to obtain a higher reward. Greed is an excellent motivator! 😉

The winning plan

Now that we have understood how much reward can be theoretically distributed, we have to decide how much we’ll distribute in reality, and how this will be distributed over the different win situations. For our project, we define the payout rate = 75%, so that we (as the owner and operator) may keep 25% of all stakes.

For the distribution, we define that the x x x outcome will give a so called reference reward m, multiplied by x. But to prevent the 0 0 0 outcome from being finally not rewarded, we’ll define that it’s reward will be 0.5m.

Thus, 0 0 0 will give 0.5m, 1 1 1 will give m, 2 2 2 will give 2m, and so on. Thus, all n win situations which happen statistically all once in a n^3 playing cycle, will need (0.5 + 1 + 2 + 3 + … + (n-1)) * m coins. On the other side, all stakes of such a n^3 playing cycle are n^3 coins. In our project, we’ll work with n=16, which will generate 4096 coins of stakes of which 75%, 3072 coins will be redistributed. Now, to find m, we’ll have to divide these 3072 by the sum of 0.5 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 = 120.5 which makes m = 25.4938 which we may round down to 24 to allow an integer reward for the 0 0 0 outcome.

The winning plan can now be established:

0 0 0 => 12
1 1 1 => 24
2 2 2 => 48
3 3 3 => 72
4 4 4 => 96
5 5 5 => 120
6 6 6 => 144
7 7 7 => 168
8 8 8 => 192
9 9 9 => 216
10 10 10 =>240
11 11 11 => 264
12 12 12 => 288
13 13 13 => 312
14 14 14 => 336
15 15 15 => 360 which is almost the double of the average 192 calculated earlier. If that is not an incentive for the player!

The checksum over all possible outcomes is 2892, which is logically lower than the planned 3072, due to the rounding down for m, so that the real payout quote is not 75% but 70.6%. For the coding of our project, it has the advantage that the reward for whatever x x x win situation can now easily be calculated by the simple one-liner (2 * x + 1) * m / 2

Simulating the wheels

Basically, the generation of a 3 digit number in the base n number system would be sufficient to simulate the end result wheels for spinning quickly first, then slowing down, and finally each stopping at a random position. But that’s not ludicrous! That’s why we’ll add a beautiful dynamic speed animation later, when we look deeper into the GUI. For now, we have a 3 digit number r s t as the end position of the wheels. How to check (with the Nextion’s restricted mathematical capabilities) if it’s a win situation, and if yes, how much reward has to be credited?

It’s for a specific reason that I decided to select n = 16. Each of the three digits takes 4 bits in a 32bit integer structure which reduces some computing to simple bit shift operations. First, we’ll look if r = s = t. Making several operations to separate the number r s t into its digits, r, s, and t and using if(r==s && s==t) is a working approach. But just using the modulo function with 1 1 1 in the base n (which is in our case hexadecimal) number system and checking if the result is zero, is a much quicker proceeding. The factor x for our reward formula can then be obtained easily with a bit mask, isolating the last 4 bits of r s t. Here a code snippet, the first of our project:

tmp=result%273 // 273 is hex 111
if(tmp==0)
{
  // this is a win situation, let's compute the reward
  x=result&0x0000000F // isolate the last digit
  reward=x<<1+1*m>>1 // left and right shifts by 1 bit replace multiplication and division by 2
}else
{
  // loss situation, no reward
  reward=0
}

And with this, we have done the “mise en place” of the first ingredient of our menu. I admit, this one was very loaded with theory and lengthy. But it was worth the effort because it allowed us finally to understand and to implement the most complex core function of our slot machine with only a handful of easy to read and to maintain code lines. This proves that thinking and scribbling with a pencil on a paper before coding gives a ways more efficient result!

We’ll add most other ingredients to our menu next week, but their “mise en place” will most probably be simpler and shorter because from now on, everything will be grouped around the central core element above.

Thank you for reading and happy Nextioning!

Comments, critics, suggestions? Just send me an email to thierry (at) itead (dot) cc! 🙂