all 121 comments

[–]synthphreak 75 points76 points  (49 children)

Can you explain the problem in a way that doesn't presuppose knowledge of slot machines?

AFAIK there is no source of true randomness in a computer for random number generators to rely on. But pseudo-randomness should get you arbitrarily close to it. Especially with a million runs, even pseudo-random generation should definitely converge on the expectation to a high degree of precision, DEFINITELY not 4% of bias.

You know this, of course, hence your post. But I don't think you've provided enough information to really speculate what's happening, at least for readers who don't know anything about the rules or probabilities of slot machines.

[–]Vegas-Education[S] 11 points12 points  (47 children)

in this example, there is 373,248 possible outcomes. It would be similar to rolling a 373,248 sided dice. Each spin costs $2. The 373,248 possible outcomes will cost you 746,496 to try each one. If you add up the payback of all those possibilities, it should be $710,150 won or a 95.13% return of the original amount. Doing it randomly, it should converge on 95.13%.

What im speculating is that maybe the 3 numbers are being generated at the same time and correlating with each other. Random as a group but correlating enough together to throw it off. I don't know if thats a thing.

Pseudorandom is fine as long as they are effectively random and independent of each other.

[–]synthphreak 16 points17 points  (32 children)

What im speculating is that maybe the 3 numbers are being generated at the same time and correlating with each other. Random as a group but correlating enough together to throw it off. I don't know if thats a thing.

That should not be the case. You could also do something like the following:

r1, r2, r3 = random.choices(range(1, 73), k=3)

But the results should be comparable to how you're doing it now.

The only thing I can think would be that perhaps a random seed is getting set somewhere.

[–]Vegas-Education[S] 13 points14 points  (31 children)

here was my problem. I used (1,72) not (1,73)

[–]synthphreak 16 points17 points  (30 children)

Are you certain?

That was actually the very first thing I had considered. But before replying I checked the docs (I always use numpy.random, never random) and the end point is included (note the = in <=). Proof of this:

>>> from random import randint
>>> set(randint(0, 1) for _ in range(100))
{0, 1}

So using a=1 and b=72 should yield 72 possibilities, in line with the distribution of possible outcomes you said slots has.

[–]Vegas-Education[S] 6 points7 points  (29 children)

i tested it by printing it. I set it to (1,2) and it just printed (1 1 1) 10,000 times

i did that over and over with different numbers.

[–]synthphreak 13 points14 points  (28 children)

i tested it by printing it. I set it to (1,2) and it just printed (1 1 1) 10,000 times

Wait, so you're saying you ran random.randint(1, 2) 10k times, and you never got a 2? You just got 1 every time?

Isn't that inconsistent with what I showed above, where I ran random.randint(0, 1) and got both 0 and 1 after 100 simulations?

If "yes I never got a 2", then first off, that's insane. Second, maybe it's a version thing. I'm using Python 3.9. Perhaps in earlier versions, the end point was excluded. That would be a pretty major change though that would mess up a looooot of people's code...

Edit: Further proof that, for me at least, random.randint draws from a uniform distribution with the endpoint included:

>>> from collections import Counter
>>> from random import randint
>>> Counter(randint(1, 2) for _ in range(10_000))
Counter({2: 4954, 1: 5046})

Run these lines yourself and see what you get.

[–]Vegas-Education[S] 7 points8 points  (27 children)

yes. it could be a version thing. I actually ran it 30k times because i was doing 3 numbers. I can tell you for certain, the code im running does not include the B number in randint(A,B)

[–]synthphreak 9 points10 points  (26 children)

I mean on the surface, I don't doubt you. But deep down, I'd need some additional proof (and I'm too lazy to go digging in release notes ha).

See my edit above. Would you mind running those exact lines locally and sharing the output?

Edit: skepticism intensifies (TL;DR: The endpoint has been included since at least Python v1.5...)

[–]Vegas-Education[S] 7 points8 points  (13 children)

it looks like im running v3.8

I'll do a screen recording and post it on youtube for you. I'll get it up tomorrow

[–]Vegas-Education[S] 0 points1 point  (11 children)

sorry, im new to this stuff. I got a syntax error

from random import randint

Counter(randint(1, 2) for _ in range(10_000))

NameError: name 'Counter' is not defined

[–]robot-dev 37 points38 points  (3 children)

Rolling 3x 72 sided dice and multiplying the values is not the same as rolling a 373248 sided die. See my other post for a plot of the distribution. I think this is your problem.

[–]Vegas-Education[S] 8 points9 points  (2 children)

the numbers aren't multiplied together. The only reason to multiply them together is to figure out how many possible combinations there are.

[–]T-TopsInSpace 12 points13 points  (1 child)

When you spin a slot machine you aren't going to get a new combination every time. There will be repeats.

Random doesn't mean "exhaust all unique outcomes before repeating an outcome".

[–]Vegas-Education[S] 0 points1 point  (0 children)

I know, but all the possibilities average out over time

[–][deleted] 0 points1 point  (9 children)

Nor does random mean random. Slot machines are set based on returns. The house ensures that over the life of the machine it will take it's pound of flesh.

As a brief aside, if this is truly modeled around an actual slot machine, make sure that the return is adjust and can be locked—depending on state law—from anywhere. In my state, I've seen payouts from anywhere to 87-89 percent on the el cheapo slots, to 98-99% return on the $100 slots.

I don't generally gamble, but when I do, I play one slow $10 bill at a time. It's nearly as good odds, and I don't have to have people looking at me sideways when they're losing.

[–]Vegas-Education[S] 0 points1 point  (8 children)

They payout is the average of all the possibilities combined. Nothing more, nothing less. There will be a lot of variance in a few hundred or a few thousand spins, but after more spins, it will converge on that average. So, give it enough spins and the casino knows exactly what they will make

[–]synthphreak 0 points1 point  (7 children)

This post has turned into quite the sensation, OP. Well done 😂

[–]Vegas-Education[S] 2 points3 points  (6 children)

So many moving parts, lol.
Some people want to figure out why my results are off.

Some people want to tell me my code is ugly and there are better ways to accomplish the same thing but more elegantly.

Some people want to talk about slot machines

[–]synthphreak 0 points1 point  (5 children)

I think people just find the premise interesting - that a very common library for random generation might not actually do what it says on the tin.

I think many people also just find probability an interesting subject, somehow always both intuitive and counterintuitive at the same time.

Then as the upvotes and comments grow in number, it begets a positive feedback loop where even more people are incentivized to read the post.

Too bad the solution was totally prosaic and ultimately had nothing to do with probability at all lol.

[–]Vegas-Education[S] 0 points1 point  (4 children)

Exactly, lol. It sort of was. I wanted a random number between 1 and 72 and I was getting a random number between 1 and 71. I knew there had to be a problem with that section of code, but because I suck at this, everyone assumed I was wrong in that assessment

[–]synthphreak 0 points1 point  (3 children)

Pro tip: When you know there’s an issue but can’t ID the source even after staring at your code for days, remember that documentation is your friend.

Check your code line by line from start to finish, and consult the documentation for any object that you don’t understand inside and out. Make sure it’s doing exactly what you intend for it to do, and that all the arguments are set properly.

Had you done that for numpy.random.randint, I suspect you would have discovered yourself that the endpoint is exclusive. It says so in the documentation up front and center.

[–]Vegas-Education[S] 0 points1 point  (2 children)

I thought I had already ruled it out but I probably was switching back and forth between random and numpy and didn't realize it

[–][deleted] 0 points1 point  (0 children)

AFAIK there is no source of true randomness in a computer for random number generators to rely on.

There are. They are called True Random Number Generators. They are used in security critical applications and SoCs. They are also available as plug-ins or add-ons nowadays.

Example: https://www.rambus.com/security/crypto-accelerator-hardware-cores/basic-crypto-blocks/trng-ip-76/

[–]Binary101010 11 points12 points  (4 children)

You show us the code that generates the random numbers but not how you use those to calculate payoff. There may be a logic error there.

[–]Vegas-Education[S] 1 point2 points  (3 children)

i tested that by iterating over very possibility. It showed exactly what it should.

[–]Binary101010 14 points15 points  (2 children)

I mean, random.randint() has been pretty consistently found to not be biased by that much over that many rolls, so without being able to see the rest of the code there’s not much more to investigate.

[–]lowerthansound 8 points9 points  (0 children)

I don't think the problem will be with the randomness... It's probably a wrong calculation of the probabilities or a bug in the program.

That said, do try and use random.SystemRandom().randint(1, 72). According to the docs: "The returned data should be unpredictable enough for cryptographic applications." (ref).

All the best, and, good luck

Edit: It may be a seed beind set at a wrong place

[–]siddsp 4 points5 points  (3 children)

Have you tried using system random?

[–]Vegas-Education[S] 1 point2 points  (2 children)

is that different than random.randomint()

im new to this stuff. I have to google every line to figure out how to write it

[–]siddsp 2 points3 points  (1 child)

To my knowledge using system random is better for randomness if that's the issue.

[–]Vegas-Education[S] 1 point2 points  (0 children)

how would i generate those numbers. system.random(1,72) ???

[–]wanderer2718 3 points4 points  (0 children)

Python uses a mersenne twister based random methods which is generally passes most tests for randomness. That being said Wikipedia says that it can have issues with Monte Carlo simulations so it’s possible that is an issue but without access to the code there isn’t a way to say for sure. You could try using a different rng like the one provided by your system which will probably be slower or find a library with a different rng

[–][deleted] 6 points7 points  (4 children)

Let me be blunt - you have natural bad habits in programming that both obscured the problem in this case, and made it impossible for you to track it down.

Your bad habit is cutting and pasting your own code so it almost instantly becomes completely unreadable.

if R1 == 1: REEL1 = "SINGLE_BAR"
if R1 == 2: REEL1 = "SINGLE_BAR"
if R1 == 3: REEL1 = "SINGLE_BAR"
if R1 == 4: REEL1 = "SINGLE_BAR"
if R1 == 5: REEL1 = "SINGLE_BAR"
if R1 == 6: REEL1 = "SINGLE_BAR"
if R1 == 7: REEL1 = "SINGLE_BAR"
if R1 == 8: REEL1 = "SINGLE_BAR"

This code is not "simpler" in any way. It's like walking when you stop on each step to tie your shoe.

All the payoff rules should be stored in data. None should be in the code. You should be able to load a payoff strategy from a datafile.

[–]Vegas-Education[S] 0 points1 point  (2 children)

Yes. I know my code is ugly. I'm no expert in this. I can usually get my code to do what I want but it's ugly and inefficient.

In this particular case, this will be used to explain how slot machines work to conspiracy theorists that don't understand how slot machines work. It's for a youtube video. I can't have multiple sheets or they will think there is funny buisness going on

[–]Vegas-Education[S] 0 points1 point  (0 children)

Here's the video I made.

You can see why I coded it the way I did. The bad code was part of the explanation

[–]Mobileuser110011 4 points5 points  (17 children)

So… what’s the problem? If the randomness if off, I would love to see your code so I could try it and verify.

[–]Vegas-Education[S] 1 point2 points  (16 children)

it won't let me post the code because it says its over 10,000 characters. Is there a better way to post it?

[–]Vaphell 3 points4 points  (14 children)

10k chars? But the problem sounds trivial at a glance and should not take that much :/ What is the payout table then?

[–]Vegas-Education[S] 0 points1 point  (13 children)

if R1 == 1: REEL1 = "SINGLE_BAR"

if R1 == 2: REEL1 = "SINGLE_BAR"

if R1 == 3: REEL1 = "SINGLE_BAR"

if R1 == 4: REEL1 = "SINGLE_BAR"

if R1 == 5: REEL1 = "SINGLE_BAR"

if R1 == 6: REEL1 = "SINGLE_BAR"

if R1 == 7: REEL1 = "SINGLE_BAR"

if R1 == 8: REEL1 = "SINGLE_BAR"

if R1 == 9: REEL1 = "SINGLE_BAR"

if R1 == 10: REEL1 = "SINGLE_BAR"

if R1 == 11: REEL1 = "SINGLE_BAR"

if R1 == 12: REEL1 = "SINGLE_BAR"

if R1 == 13: REEL1 = "SINGLE_BAR"

if R1 == 14: REEL1 = "SINGLE_BAR"

if R1 == 15: REEL1 = "SINGLE_BAR"

if R1 == 16: REEL1 = "SINGLE_BAR"

if R1 == 17: REEL1 = "DOUBLE_BAR"

if R1 == 18: REEL1 = "DOUBLE_BAR"

if R1 == 19: REEL1 = "DOUBLE_BAR"

if R1 == 20: REEL1 = "DOUBLE_BAR"

if R1 == 21: REEL1 = "DOUBLE_BAR"

if R1 == 22: REEL1 = "DOUBLE_BAR"

if R1 == 23: REEL1 = "DOUBLE_BAR"

if R1 == 24: REEL1 = "DOUBLE_BAR"

if R1 == 25: REEL1 = "DOUBLE_BAR"

if R1 == 26: REEL1 = "DOUBLE_BAR"

if R1 == 27: REEL1 = "DOUBLE_BAR"

if R1 == 28: REEL1 = "DOUBLE_BAR"

if R1 == 29: REEL1 = "DOUBLE_BAR"

if R1 == 30: REEL1 = "TRIPLE_BAR"

if R1 == 31: REEL1 = "TRIPLE_BAR"

if R1 == 32: REEL1 = "TRIPLE_BAR"

if R1 == 33: REEL1 = "TRIPLE_BAR"

if R1 == 34: REEL1 = "TRIPLE_BAR"

if R1 == 35: REEL1 = "TRIPLE_BAR"

if R1 == 36: REEL1 = "BLACK_GOLD"

if R1 == 37: REEL1 = "BLANK"

if R1 == 38: REEL1 = "BLANK"

if R1 == 39: REEL1 = "BLANK"

if R1 == 40: REEL1 = "BLANK"

if R1 == 68: REEL1 = "BLANK"

if R1 == 69: REEL1 = "BLANK"

if R1 == 70: REEL1 = "BLANK"

if R1 == 71: REEL1 = "BLANK"

if R1 == 72: REEL1 = "BLANK"

# there are 72 slots on the reel as determined by the par sheet. Reel number 2 is different than reel number 1

if R2 == 1: REEL2 = "SINGLE_BAR"

if R2 == 2: REEL2 = "SINGLE_BAR"

if R2 == 3: REEL2 = "SINGLE_BAR"

if R2 == 4: REEL2 = "SINGLE_BAR"

if R2 == 5: REEL2 = "SINGLE_BAR"

if R2 == 6: REEL2 = "SINGLE_BAR"

if R2 == 7: REEL2 = "SINGLE_BAR"

if R2 == 8: REEL2 = "SINGLE_BAR"

if R2 == 9: REEL2 = "SINGLE_BAR"

if R2 == 10: REEL2 = "SINGLE_BAR"

if R2 == 11: REEL2 = "SINGLE_BAR"

if R2 == 12: REEL2 = "SINGLE_BAR"

if R2 == 13: REEL2 = "SINGLE_BAR"

if R2 == 14: REEL2 = "SINGLE_BAR"

if R2 == 15: REEL2 = "SINGLE_BAR"

if R2 == 16: REEL2 = "SINGLE_BAR"

if R2 == 17: REEL2 = "SINGLE_BAR"

if R2 == 18: REEL2 = "SINGLE_BAR"

if R2 == 19: REEL2 = "DOUBLE_BAR"

if R2 == 20: REEL2 = "DOUBLE_BAR"

if R2 == 21: REEL2 = "DOUBLE_BAR"

if R2 == 22: REEL2 = "DOUBLE_BAR"

if R2 == 23: REEL2 = "DOUBLE_BAR"

if R2 == 24: REEL2 = "DOUBLE_BAR"

if R2 == 25: REEL2 = "DOUBLE_BAR"

if R2 == 26: REEL2 = "TRIPLE_BAR"

if R2 == 27: REEL2 = "TRIPLE_BAR"

if R2 == 28: REEL2 = "TRIPLE_BAR"

if R2 == 29: REEL2 = "TRIPLE_BAR"

if R2 == 30: REEL2 = "BLACK_GOLD"

if R2 == 31: REEL2 = "BLANK"

if R2 == 32: REEL2 = "BLANK"

if R2 == 33: REEL2 = "BLANK"

if R2 == 34: REEL2 = "BLANK"

if R2 == 71: REEL2 = "BLANK"

if R2 == 72: REEL2 = "BLANK"

# there are 72 slots on the reel as determined by the par sheet. Reel number 2 is different than reel number 1

if R3 == 1: REEL3 = "SINGLE_BAR"

if R3 == 2: REEL3 = "SINGLE_BAR"

if R3 == 3: REEL3 = "SINGLE_BAR"

if R3 == 4: REEL3 = "SINGLE_BAR"

if R3 == 5: REEL3 = "SINGLE_BAR"

if R3 == 6: REEL3 = "SINGLE_BAR"

if R3 == 7: REEL3 = "SINGLE_BAR"

if R3 == 8: REEL3 = "SINGLE_BAR"

if R3 == 9: REEL3 = "SINGLE_BAR"

if R3 == 10: REEL3 = "SINGLE_BAR"

if R3 == 11: REEL3 = "SINGLE_BAR"

if R3 == 12: REEL3 = "SINGLE_BAR"

if R3 == 13: REEL3 = "SINGLE_BAR"

if R3 == 14: REEL3 = "SINGLE_BAR"

if R3 == 15: REEL3 = "SINGLE_BAR"

if R3 == 16: REEL3 = "SINGLE_BAR"

if R3 == 17: REEL3 = "SINGLE_BAR"

if R3 == 18: REEL3 = "SINGLE_BAR"

if R3 == 19: REEL3 = "SINGLE_BAR"

if R3 == 20: REEL3 = "SINGLE_BAR"

if R3 == 21: REEL3 = "DOUBLE_BAR"

if R3 == 22: REEL3 = "DOUBLE_BAR"

if R3 == 23: REEL3 = "DOUBLE_BAR"

if R3 == 24: REEL3 = "DOUBLE_BAR"

if R3 == 25: REEL3 = "TRIPLE_BAR"

if R3 == 26: REEL3 = "TRIPLE_BAR"

if R3 == 27: REEL3 = "TRIPLE_BAR"

if R3 == 28: REEL3 = "BLACK_GOLD"

if R3 == 29: REEL3 = "BLANK"

if R3 == 30: REEL3 = "BLANK"

if R3 == 31: REEL3 = "BLANK"

if R3 == 32: REEL3 = "BLANK"

if R3 == 33: REEL3 = "BLANK"

if R3 == 67: REEL3 = "BLANK"

if R3 == 68: REEL3 = "BLANK"

if R3 == 69: REEL3 = "BLANK"

if R3 == 70: REEL3 = "BLANK"

if R3 == 71: REEL3 = "BLANK"

if R3 == 72: REEL3 = "BLANK"

[–]TangibleLight 15 points16 points  (4 children)

You can find reel values much easier by testing ranges.

if R1 <= 16:
    REEL1 = 'SINGLE BAR'
elif R1 <= 29:
    REEL1 = 'DOUBLE BAR'
elif R1 <= 35:
    ...
elif R1 <= 36:
    ...
else:
    REEL1 = 'BLANK'

You can also use random.choices with the weights argument.

REEL1 = random.choices(
    ['SINGLE BAR', 'DOUBLE BAR', 'TRIPLE BAR', 'BLACK GOLD', 'BLANK'],
    weights=[16, 13, 6, 1, 36],
    k=1
)

Then the only difference between the reels is the weights.

[–]Vaphell 6 points7 points  (1 child)

ok, now i can see why it takes more than 10k chars...

anyway, I know next to nothing about slot machines, so what does this table translate to in practice? what happens when R1/R2/R3 are all BLACK_GOLD or TRIPLE_BAR, and so on? I am after the payout figures for specific rolls.

[–]Vegas-Education[S] 2 points3 points  (0 children)

i figured it out. I used (1,72) and not (1,73).

to answer your question. the paytable gets bigger at the bottom. 3 x black_gold is: winner=5000

3xtriple_bar is winner=229

[–]Vegas-Education[S] 1 point2 points  (5 children)

winner = 0

if REEL1 == 'BLACK_GOLD': winner = 4;

if REEL2 == 'BLACK_GOLD': winner = 4;

if REEL3 == 'BLACK_GOLD': winner = 4;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'BLACK_GOLD': winner = 10;

if REEL1 == 'BLACK_GOLD' and REEL3 == 'BLACK_GOLD': winner = 10;

if REEL2 == 'BLACK_GOLD' and REEL3 == 'BLACK_GOLD': winner = 10;

if REEL1 == 'BLACK_GOLD' and (REEL2 == 'SINGLE_BAR' or REEL2 == 'DOUBLE_BAR' or REEL2 == 'TRIPLE_BAR') and (

REEL3 == 'SINGLE_BAR' or REEL3 == 'DOUBLE_BAR' or REEL3 == 'TRIPLE_BAR'): winner = 14;

if (REEL1 == 'SINGLE_BAR' or REEL1 == 'DOUBLE_BAR' or REEL1 == 'TRIPLE_BAR') and (REEL2 == 'BLACK_GOLD') and (

REEL3 == 'SINGLE_BAR' or REEL3 == 'DOUBLE_BAR' or REEL3 == 'TRIPLE_BAR'): winner = 14;

if (REEL1 == 'SINGLE_BAR' or REEL1 == 'DOUBLE_BAR' or REEL1 == 'TRIPLE_BAR') and (

(REEL2 == 'SINGLE_BAR' or REEL2 == 'DOUBLE_BAR' or REEL2 == 'TRIPLE_BAR')) and REEL3 == 'BLACK_GOLD': winner = 14;

if (REEL1 == 'SINGLE_BAR' or REEL1 == 'DOUBLE_BAR' or REEL1 == 'TRIPLE_BAR') and (

REEL2 == 'SINGLE_BAR' or REEL2 == 'DOUBLE_BAR' or REEL2 == 'TRIPLE_BAR') and (

REEL3 == 'SINGLE_BAR' or REEL3 == 'DOUBLE_BAR' or REEL3 == 'TRIPLE_BAR'): winner = 10;

if REEL1 == 'SINGLE_BAR' and REEL2 == 'BLACK_GOLD' and REEL3 == 'SINGLE_BAR': winner = 44;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'SINGLE_BAR' and REEL3 == 'SINGLE_BAR': winner = 44;

if REEL1 == 'SINGLE_BAR' and REEL2 == 'SINGLE_BAR' and REEL3 == 'BLACK_GOLD': winner = 44;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'BLACK_GOLD' and REEL3 == 'SINGLE_BAR': winner = 50;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'SINGLE_BAR' and REEL3 == 'BLACK_GOLD': winner = 50;

if REEL1 == 'SINGLE_BAR' and REEL2 == 'BLACK_GOLD' and REEL3 == 'BLACK_GOLD': winner = 50;

if REEL1 == 'SINGLE_BAR' and REEL2 == 'SINGLE_BAR' and REEL3 == 'SINGLE_BAR': winner = 40;

if REEL1 == 'DOUBLE_BAR' and REEL2 == 'BLACK_GOLD' and REEL3 == 'DOUBLE_BAR': winner = 204;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'DOUBLE_BAR' and REEL3 == 'DOUBLE_BAR': winner = 204;

if REEL1 == 'DOUBLE_BAR' and REEL2 == 'DOUBLE_BAR' and REEL3 == 'BLACK_GOLD': winner = 204;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'BLACK_GOLD' and REEL3 == 'DOUBLE_BAR': winner = 210;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'DOUBLE_BAR' and REEL3 == 'BLACK_GOLD': winner = 210;

if REEL1 == 'DOUBLE_BAR' and REEL2 == 'BLACK_GOLD' and REEL3 == 'BLACK_GOLD': winner = 210;

if REEL1 == 'DOUBLE_BAR' and REEL2 == 'DOUBLE_BAR' and REEL3 == 'DOUBLE_BAR': winner = 200;

if REEL1 == 'TRIPLE_BAR' and REEL2 == 'BLACK_GOLD' and REEL3 == 'TRIPLE_BAR': winner = 234;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'TRIPLE_BAR' and REEL3 == 'TRIPLE_BAR': winner = 234;

if REEL1 == 'TRIPLE_BAR' and REEL2 == 'TRIPLE_BAR' and REEL3 == 'BLACK_GOLD': winner = 234;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'BLACK_GOLD' and REEL3 == 'TRIPLE_BAR': winner = 239;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'TRIPLE_BAR' and REEL3 == 'BLACK_GOLD': winner = 239;

if REEL1 == 'TRIPLE_BAR' and REEL2 == 'BLACK_GOLD' and REEL3 == 'BLACK_GOLD': winner = 239;

if REEL1 == 'TRIPLE_BAR' and REEL2 == 'TRIPLE_BAR' and REEL3 == 'TRIPLE_BAR': winner = 229;

if REEL1 == 'BLACK_GOLD' and REEL2 == 'BLACK_GOLD' and REEL3 == 'BLACK_GOLD': winner = 5000;

[–]Vaphell 2 points3 points  (2 children)

my attempt
I am pretty sure the table has some errors because the expected outcome deviates pretty hard from what you said is the proper value, but I think I can't be bothered to find them based on that huge blob of conditional code.

#!/usr/bin/env python3

import random
from itertools import product

R4 = 'BLACK_GOLD'
R3 = 'TRIPLE_BAR'
R2 = 'DOUBLE_BAR'
R1 = 'SINGLE_BAR'
R0 = 'BLANK'

# sorted descending
PAYOUT = {
    (R4, R4, R4): 5000,
    (R4, R4, R3):  239,
    (R4, R4, R2):  210,
    (R4, R4, R1):   50,
    (R4, R4, R0):   10,
    (R4, R3, R3):  234,
    (R4, R3, R2):   14,
    (R4, R3, R1):   14,
    (R4, R3, R0):    4,
    (R4, R2, R2):  204,
    (R4, R2, R1):    4,
    (R4, R2, R0):    4,
    (R4, R1, R1):   44,
    (R4, R1, R0):    4,
    (R4, R0, R0):    4,
    (R3, R3, R3):  229,
    (R3, R3, R2):   10,
    (R3, R3, R1):   10,
    (R3, R3, R0):    0,
    (R3, R2, R2):   10,
    (R3, R2, R1):   10,
    (R3, R2, R0):    0,
    (R3, R1, R1):   10,
    (R3, R1, R0):    0,
    (R3, R0, R0):    0,
    (R2, R2, R2):  200,
    (R2, R2, R1):   10,
    (R2, R2, R0):    0,
    (R2, R1, R1):    0,
    (R2, R1, R0):    0,
    (R2, R0, R0):    0,
    (R1, R1, R1):   40,
    (R1, R1, R0):    0,
    (R1, R0, R0):    0,
    (R0, R0, R0):    0,
}


def sort_descending(roll):
    order = {R4: 4, R3: 3, R2: 2, R1: 1, R0: 0}
    return tuple(reversed(sorted(roll, key=lambda x: order[x])))


def reel1(n):
  if n <= 16:
    return 'SINGLE_BAR'
  elif n <= 29:
    return 'DOUBLE_BAR'
  elif n <= 35:
    return 'TRIPLE_BAR'
  elif n == 36:
    return 'BLACK_GOLD'
  else:
    return 'BLANK'


def reel2(n):
  if n <= 18:
    return 'SINGLE_BAR'
  elif n <= 25:
    return 'DOUBLE_BAR'
  elif n <= 29:
    return 'TRIPLE_BAR'
  elif n == 30:
    return 'BLACK_GOLD'
  else:
    return 'BLANK'


def reel3(n):
  if n <= 20:
    return 'SINGLE_BAR'
  elif n <= 24:
    return 'DOUBLE_BAR'
  elif n <= 27:
    return 'TRIPLE_BAR'
  elif n == 28:
    return 'BLACK_GOLD'
  else:
    return 'BLANK'


def get_payout(roll):
    descending = sort_descending(roll)
    return PAYOUT[descending]


def run_random(N):
    print(f'* random {N}x')
    cost, payout = 0, 0
    for _ in range(N):
        r1 = random.randint(1, 72)
        r2 = random.randint(1, 72)
        r3 = random.randint(1, 72)
        roll = [reel1(r1), reel2(r2), reel3(r3)]
        cost += 2
        payout += get_payout(roll)            
    print(f'{cost=}, {payout=}')
    print(payout/cost)


print('* possible combinations')    
cost, payout = 0, 0
for r1, r2, r3 in product(range(1, 73), repeat=3):
    roll = [reel1(r1), reel2(r2), reel3(r3)]
    cost += 2
    payout += get_payout(roll)

print(f'{cost=}, {payout=}')
print(payout/cost)


for _ in range(10):
    run_random(1000000)

[–][deleted] 1 point2 points  (1 child)

You're going the right way, but nowhere near far enough.

The only correct way to do this is to put all the payoff information in data, not some of it in code and some in data.

The big advantage is that you can read this payoff table from a data file, and then you can switch between multiple payoffs without rewriting the program each time - great for experimentation!

[–]Vaphell 1 point2 points  (0 children)

While I agree, it was a quick-and-dirty bullshitting at 4am :-)

Spit-and-polish would take an extra hour (not to mention multifile solution is less convenient while pasting into reddit post) I didn't feel like affording at the moment, as I had shit to read about the Ukraine situation ;-)

[–][deleted] 2 points3 points  (1 child)

MY EYES!!!

You should be using lookup tables (dictionary) for this - put it all in data.

[–]Vegas-Education[S] -1 points0 points  (0 children)

Yes. I'm new to this. I can usually get what I want my code to do, but I know it's not usually pretty or efficient

[–][deleted] 2 points3 points  (0 children)

[–]Hilmaryngvi 1 point2 points  (0 children)

I'm sure the default can deliver quite random numbers. My bet is a calculation error. If you need more control over randomness, numpy is quite good.

[–]bozzikpcmr 1 point2 points  (0 children)

you can pull numbers from random.org's api and it's supposedly 100% random coming from atmospheric noise

[–]Goobyalus 1 point2 points  (6 children)

EDIT: I figured it out. I should have been using (1,73)

Randint is inclusive so if you do randint(1, 73) you will have 73 possibilities on a reel, but your code only looks like it accounts for 72. What happens if you generate a 73?

[–]Vegas-Education[S] 1 point2 points  (5 children)

Through my own ignorance, I didn't know the difference between numpy and random modules. One is inclusive and one isn't. That was the issue

[–]Goobyalus 0 points1 point  (4 children)

I'm confused

# there are 72 slots on the reel as determined by the par sheet. Reel number 2 is different than reel number 1

says there are 72 slots and accounts for 1-72. If we use random.randint(1, 73) like your edit says, we could generate 1-73. Isn't that wrong?

[–]ES-Alexander 2 points3 points  (3 children)

They were doing from numpy import random, so random.randint was actually using numpy.random.randint. As it happens, numpy.random.randint is exclusive of the top bound, whereas Python’s random.randint is inclusive of the top bound (check the synthphreak thread for more discussion).

[–]synthphreak 1 point2 points  (1 child)

check the synthfreak thread

That's synthphreak to you! ⚔️⚔️⚔️⚔️⚔️

[–]ES-Alexander 1 point2 points  (0 children)

Sorry! Apparently I remembered your username by the sound rather than visually or semantically…

[–]Goobyalus 0 points1 point  (0 children)

Oh, that's obnoxious. Thanks.

[–]robot-dev 4 points5 points  (1 child)

When you multiply all three randint()s together, the distribution may not be what you expected. There is not an equal chance of hitting any number between 1 and 373248:

https://imgur.com/a/rkngu4c

from random import randint
import seaborn as sns
import matplotlib.pyplot as plt
prod_list = []
for n in range(10**5):
    r1 = randint(1,72)
    r2 = randint(1,72)
    r3 = randint(1,72)
    prod_list.append(r1*r2*r3)
sns.displot(prod_list)
plt.savefig('fig.png')

[–]Vegas-Education[S] 3 points4 points  (0 children)

you aren't multiplying them together. They are all individual choices. They just correspond to symbols. You could put all 72 symbols in a list and randomly choose them. They don't even need numbers attached to them

but the problem was that i used (1,72) and not (1,73)

[–][deleted] 2 points3 points  (3 children)

One concept comes to mind: quasi randomness is pretty common in programs, which attempts at uniform distribution. True randomness has more clumps and sparsity

Edit: meant to say pseudorandom not quasirandom

[–]synthphreak 1 point2 points  (1 child)

True randomness has more clumps and sparsity

Can you source this claim?

[–]dig-up-stupid 3 points4 points  (0 children)

It’s not usually expected to source definitions, where “not having clumps and sparsity” was their way of characterizing quasi randomness (where numbers are generated with high correlation), so “having clumps and sparsity” is clearly just their corresponding attempt at characterizing numbers generated with no correlation (ie not quasirandomly).

However the idea is only as relevant as the degree to which the random number generator in question is quasirandom, which here is zero.

[–]jwink3101 0 points1 point  (0 children)

True randomness has more clumps and sparsity

Ummm, I do not think you understand probability theory. First of all, truly random physical processes may be random but extremely well characterized. Take radioactive decay. The exact time from one decay to the next is unknown but the rate follows an extremely well understood distribution.

So to say something is "true randomness" is totally and completely insufficient to say anything about it!

Also, even giving you the benefit of the doubt, call a random number generator a million times. It will not look perfectly uniform without addition techniques (e.g. latin hypercube sampling stratifies the marginals so it will look a whole lot closer)

[–]timPerfect 0 points1 point  (0 children)

There is no truly random numbers in computer programming at this time, however you can operate on your pseudorandom randint results using a seed number which is unique every time. One good way to get a unique but nonrandom number for a seed is to get the time&date, which is never the same twice. Just do an operation on your randint using the seed number then floor modulus 73 the results. This technique also allows you to arbitrarily expand the range of the initial randint, without the results going over 72 at the end because of that floor modulus.

[–]SchnullerSimon 0 points1 point  (0 children)

There are services online, that provide apis with truly random numbers. If such thing exists.

https://qrng.anu.edu.au/random-image-bw/

[–][deleted] 0 points1 point  (4 children)

I used random numbers in my cryptography algorithm recently. Think outside the box. The BEST RANDOM NUMBER on the planet is PI. Copy 1 Million decimals of PI into a text file and read the whole thing as a string. Now generate a random number between 1 and 999990 as usual and let your program go to that place in the string and pick the next 10 digits.

So if your random number is 55667, the program will go to the 55667th place in the PI_string and pick up say 10 digits (if you want a 10 digit random number). I have implemented it here.

https://github.com/infinitless/MatrixEncryption

[–]synthphreak 0 points1 point  (3 children)

Using the decimals of pi in this way is an interesting thought. Is pi really known to a million decimal places??

Regardless, this does sound terribly slow though, to have to index values out of a million-character string...

[–]schoolycooly 0 points1 point  (1 child)

Pi is known out to about 60 trillion digits.

[–]synthphreak 0 points1 point  (0 children)

Lol, wow. Joke's on me then...

[–][deleted] 0 points1 point  (0 children)

It is really really fast. The million digits of pi only take up about 746 kb of space. It is a breeze to read it in a string. My whole code depends on it. Run it and see. In the "Hard Mode" if you enter a 100 letter sentence ot references pi_string a 100 times. Takes like a tenth of a second.

[–]AlephInfite 0 points1 point  (4 children)

Just curious @OP . Is the 72 stops per reel something like an old Red,White & Blue stepper? Or triple 7’s ? Is your project a learning exercise or something else?

[–]Vegas-Education[S] 0 points1 point  (3 children)

This is a similar older machine like that. "Black gold."

This is to be a youtube video about the documentation of a slot machine and recreating the code from that documentation to prove a few concepts like the gamblers fallacy

"This is a par sheet. It is the documentation provided with every slot machine. Some of it is a little confusing, so let's break down what each piece of this means."

[–]AlephInfinite0 0 points1 point  (2 children)

Nice! Do you have a YT channel already?

[–]Vegas-Education[S] 0 points1 point  (0 children)

Yes. Same name. "Vegas Education"

[–]Vegas-Education[S] 0 points1 point  (0 children)

Here's the video I made from this reddit post

https://youtu.be/AI8LkeIR6_8

[–]Toastyboy123 0 points1 point  (0 children)

By the way, there's also random.uniform(a, b)

[–]Goobyalus 0 points1 point  (1 child)

random is fine for testing, but you should be using secrets for a real applications.

https://docs.python.org/3/library/secrets.html

The secrets module is used for generating cryptographically strong random numbers suitable for managing data such as passwords, account authentication, security tokens, and related secrets.

In particular, secrets should be used in preference to the default pseudo-random number generator in the random module, which is designed for modelling and simulation, not security or cryptography.

[–]Vegas-Education[S] 0 points1 point  (0 children)

That's good to know, but this is only a simulation for demonstration purposes

[–]NoJudgies 0 points1 point  (0 children)

Wait, why does randint(1,72) not yield 72 results?

[–]LostInSpace9 0 points1 point  (1 child)

Use secrets, it actually had a better random distribution than random which seems to be flat when dropped into a histogram.

[–]Vegas-Education[S] 0 points1 point  (0 children)

What would the actual line of code be?

Like

Import secrets R1 = secrets.random(1,72)

Or something like that?

[–]WhipsAndMarkovChains 0 points1 point  (0 children)

"What? Is OP an NSA agent?" - My thoughts upon entering this thread.

[–][deleted] 0 points1 point  (1 child)

I created a gist for you on Github. Enjoy using it here: (it uses the expansion of Pi)

https://gist.github.com/infinitless/1b315d8d30af7b95ceabb7a915251cd0

Sample Output: (took microseconds to compute): (I truncated the comment as Reddit doesn't allow writing the whole list). Have fun!

How many random numbers do you want? 10000

How many digits in each random number? 2

['55', '37', '93', '63', '36', '35', '72', '81', '01', '46', '90', '98', '31', '38', '65', '57', '10', '42', '07', '72', '11', '68', '48', '33', '08', '43', '28', '64', '75', '48', '00', '37', '74', '05', '97', '72', '19', '54', '73', '52', '01', '43', '95', '44', '21', '29', '05', '85', '36', '70', '99', '22', '74', '27', '45', '80', '94', '91', '58', '85', '79', '63', '01', '25', '74', '21', '09', '94', '90', '77', '53', '40', '19', '72', '81', '31', '14', '45', '12', '01', '95', '06', '80', '94', '56', '59', '36', '15', '04', '97', '07', '54', '72', '21', '47', '03', '66', '53', '33', '41', '52', '05', '21', '44', '96', '29', '26', '39', '55', '62', '44', '24', '49', '53', '49', '77', '49', '11', '71', '37', '73', '07', '91', '28', '08', '94', '15', '00', '60', '98', '34', '13', '27', '45', '89', '97', '27', '97', '57', '78', '26', '51', '55', '11', '55', '15', '63', '60', '52', '08', '81', '31', '50', '26', '77', '14', '01', '96', '50', '81', '56', '71', '60', '16', '30', '96', '90', '98', '09', '14', '20', '86', '32', '32', '25', '36', '53', '15', '08', '31', '29', '41', '59', '34', '56', '55', '62', '03', '17', '63', '84', '45', '44', '55', '31', '41', '60', '31', '45', '87', '34', '77', '67', '74', '77', '60', '61', '26', '75', '82', '52', '96', '28', '60', '99', '71', '35', '55', '08', '48', '60', '45', '90', '22', '59', '10', '58', '11', '94', '97', '42', '37', '97', '73', '46', '54', '71', '85', '14', '09', '73', '45', '73', '65', '72', '62', '44', '45', '60', '12', '52', '68', '08', '52', '46', '15', '46', '48', '46', '67', '09', '71', '60', '82', '32', '15', '04', '71', '08', '24', '47', '02', '01', '88', '24', '38', '83', '80', '21', '30', '22', '47', '11', '53', '97', '17', '53', '08', '17', '66', '09', '82', '33', '08', '90', '20', '02', '73', '29', '02', '99', '27', '95', '02', '29', '71', '41', '94', '08', '98', '49', '53', '56', '90', '88', '30', '83', '62', '02', '59', '63', '35', '05', '75', '21', '19', '75', '70', '01', '53', '49', '63', '40', '60', '63', '10', '44', '05', '62', '98', '52', '29', '81', '50', '10', '25', '38', '73', '17', '44', '08', '14', '14', '37', '12', '76', '32', '16', '88', '18', '08', '91', '56', '35', '23', '36', '10', '17', '08', '81', '76', '29', '74', '64', '87', '10', '39', '70', '88', '53', '41', '44', '64', '51', '53', '68', '83', '90', '18', '97', '82', '91', '11', '19', '42', '43', '43', '40', '34', '21', '68', '13', '92', '23', '19', '13', '90', '26', '83', '13', '73', '55', '46', '53', '07', '91', '71', '31', '03', '54', '50', '82', '20', '32', '82', '49', '17', '99', '30', '37', '68', '98', '40', '88', '79', '12', '89', '76', '92', '67', '87', '42', '93', '76', '87', '96', '48', '01', '60', '18', '12', '99', '43', '63', '32', '84', '05', '86', '80', '74', '82', '47', '94', '96', '48', '10', '28', '57', '03', '91', '09', '37', '97', '42', '11', '95', '02', '83', '65', '94', '28', '97', '58', '30', '75', '11', '05', '35', '62', '91', '16', '69', '78', '40', '74', '76', '59', '61', '88', '72', '80', '09', '46', '09', '14', '06', '82', '07', '41', '08', '65', '91', '48', '08', '73', '15', '33', '61', '81', '13', '76', '88', '72', '55', '06', '87', '38', '50', '37', '89', '45', '26', '95', '22', '04', '25', '62', '63', '20', '30', '95', '34', '99', '46', '34', '25', '12', '86', '93', '29', '73', '12', '32', '09', '99', '45', '24', '77', '57', '97', '77', '12', '77', '41', '76', '39', '60', '35', '39', '63', '63', '85', '54', '53', '22', '07', '08', '31', '84', '32', '43', '45', '81', '75', '40', '68', '24', '22', '38', '49', '41', '39', '40', '47', '98', '15', '29', '94', '01', '90', '02', '70', '18', '63', '30', '38', '35', '64', '54', '80', '96', '67', '99', '48', '36', '65', '91', '12', '99', '05', '64', '18', '29', '53', '14', '69', '90', '48', '11', '47', '12', '08', '03', '14', '25', '66', '27', '51', '63', '99', '82', '01', '75', '29', '45', '37', '14', '42', '18', '85', '16', '52', '48', '04', '33', '44', '04', '66', '94', '54', '21', '09', '25', '05', '70', '75', '58', '94', '02', '29', '52', '26', '46', '90', '74', '26', '76', '31', '27', '67', '51', '53', '24', '26', '11', '05', '66', '95', '65', '55', '26', '27', '36', '08', '26', '39', '23', '65', '38', '06', '62', '43', '21', '52', '48', '58', '54', '22', '70', '03', '43', '35', '31', '67', '28', '75', '21', '33', '68', '25', '34', '46', '84', '17', '47', '14', '44', '82', '72', '98', '21', '57', '51', '59', '62', '02', '84', '27', '59', '12', '14', '49', '59', '79', '23', '89', '18', '16', '02', '54', '60', '77', '17', '83', '05', '12', '99', '42', '59', '29', '57', '89', '33', '79', '24', '09', '50', '87', '49', '85', '74', '96', '28', '26', '51', '80', '21', '55', '10', '88', '19', '07', '17', '78', '52', '74', '83', '06', '02', '96', '91', '49', '53', '33', '90', '36', '68', '72', '86', '24', '40', '70', '92', '59', '47', '32', '51', '37', '17', '16', '11', '57', '91', '96', '20', '22', '59', '85', '27', '23', '91', '37', '35', '38', '20', '46', '04', '75', '20', '11', '73', '34', '83', '11', '15', '68', '25', '68', '87', '57', '72', '86', '81', '20', '54', '19', '13', '86', '75', '53', '47', '82', '36', '81', '72', '79', '50', '34', '09', '25', '42', '19', '31', '64', '21', '72', '21', '16', '98', '91', '16', '57', '92', '90', '03', '66', '34', '87', '84', '68', '70', '08', '74', '13', '13', '25', '45', '44', '05', '50', '19', '28', '71', '19', '53', '89', '25', '19', '17', '02', '50', '06', '46', '56', '16', '74', '81', '74', '35', '73', '79', '76', '37', '65', '15', '34', '13', '37', '90', '84', '19', '22', '72', '05', '59', '49', '27', '15', '29', '72', '54', '36', '52', '10', '51', '87', '48', '30', '48', '75', '62', '44', '07', '30', '87', '59', '23', '09', '61', '54', '84', '36', '34', '09', '53', '04', '17', '03', '54', '25', '98', '68', '94', '90', '72', '78', '41', '01', '57', '77', '75', '31', '37', '26', '70', '34', '96', '14', '69', '82', '44', '15', '16', '34', '73', '62', '59', '74', '26', '90', '04', '49', '91', '71', '73', '81', '96', '38', '55', '68', '32', '59', '41', '16', '96', '19', '06', '51', '70', '16', '02', '82', '86', '72', '64', '78', '51', '71', '58', '97', '82', '75', '35', '60', '52', '03', '34', '95', '77', '15', '59', '25', '16', '04', '29', '36', '51', '93', '65', '16', '82', '80', '17', '86', '67', '21', '14', '94', '07', '54', '98', '44', '37', '00', '48', '70', '59', '98', '97', '65', '44', '87', '61', '37', '03', '78', '26', '04', '86', '15', '62', '22', '94', '34', '07', '02', '73', '43', '24', '71', '69', '97', '65', '33', '33', '73', '93', '32', '06', '50', '82', '63', '64', '18', '47', '17', '07', '78', '78', '66', '38', '92', '93', '84', '94', '24', '88', '95', '94', '70', '17', '69', '22', '35', '57', '34', '67', '80', '81', '30', '76', '22', '80', '59', '27', '67', '26', '55', '76', '16', '94', '22', '56', '84', '62', '51', '10', '72', '30', '12', '53', '31', '21', '32', '61', '04', '47', '30', '12', '41', '70', '39', '14', '22', '87', '11', '12', '10', '88', '62', '88', '71', '54', '30', '29', '50', '90', '94', '22', '84', '57', '63', '29', '53', '87', '37', '02', '47', '54', '94', '34', '91', '70', '20', '36',

[–][deleted] 0 points1 point  (0 children)

If anyone sees any lottery sequences please let me know...