Arbitrary Dice with Probability
I wrote a script that gives a "generalized" function of an arbitrary dice. Is this any good? How can it improve so to make it cleaner?
import random
def roll(die):
number = random.randint(0,len(die)-1)
b = die[number]
return b
Die1 = [1,2,3,4]
Die2 = [1,2,3,4,5,6] #num lists
def inptchacc():
ending_conditions = ['stop','Stop','quit','Quit']
end = False
inpu = input('what number would you like to add to the new face of the Die? (to end the die please type "stop or Stop or quit or Quit to finish the DIE" )')
while end == False:
if not(inpu in ending_conditions):
try:
retr = int(inpu)
return retr
except:
string = 'invalid input please try again'
inpu = input('invalid input please try again ')
else:
stop = 'stop'
return stop
def deeper(IDie):
list =
Adding = True
while Adding:
print('The Die ' + IDie + ' is currently ' + str(list) )
toadd = inptchacc()
if toadd != 'stop':
list.append(toadd)
else:
Adding = False
return list
def chance_overlap(Die1,Die2):
highnumber = 1000
counter = 0
for n in range(highnumber):
x = roll(Die1)
y = roll(Die2)
if x == y:
counter += 1
chance = counter/highnumber
return chance
chance = chance_overlap(Die1,Die2)
print(chance)
Doing = True
while Doing:
try:
IDie1 = deeper('IDie1')
IDie2 = deeper('IDie1')
chance2 = chance_overlap(IDie1,IDie2)
Doing = False
except:
print ('incompatible options selected returning....... ')
print(chance2)
python python-3.x
locked by Vogel612♦ 12 hours ago
This post has been locked while disputes about its content are being resolved. For more info visit meta.
comments disabled on deleted / locked posts / reviews |
I wrote a script that gives a "generalized" function of an arbitrary dice. Is this any good? How can it improve so to make it cleaner?
import random
def roll(die):
number = random.randint(0,len(die)-1)
b = die[number]
return b
Die1 = [1,2,3,4]
Die2 = [1,2,3,4,5,6] #num lists
def inptchacc():
ending_conditions = ['stop','Stop','quit','Quit']
end = False
inpu = input('what number would you like to add to the new face of the Die? (to end the die please type "stop or Stop or quit or Quit to finish the DIE" )')
while end == False:
if not(inpu in ending_conditions):
try:
retr = int(inpu)
return retr
except:
string = 'invalid input please try again'
inpu = input('invalid input please try again ')
else:
stop = 'stop'
return stop
def deeper(IDie):
list =
Adding = True
while Adding:
print('The Die ' + IDie + ' is currently ' + str(list) )
toadd = inptchacc()
if toadd != 'stop':
list.append(toadd)
else:
Adding = False
return list
def chance_overlap(Die1,Die2):
highnumber = 1000
counter = 0
for n in range(highnumber):
x = roll(Die1)
y = roll(Die2)
if x == y:
counter += 1
chance = counter/highnumber
return chance
chance = chance_overlap(Die1,Die2)
print(chance)
Doing = True
while Doing:
try:
IDie1 = deeper('IDie1')
IDie2 = deeper('IDie1')
chance2 = chance_overlap(IDie1,IDie2)
Doing = False
except:
print ('incompatible options selected returning....... ')
print(chance2)
python python-3.x
locked by Vogel612♦ 12 hours ago
This post has been locked while disputes about its content are being resolved. For more info visit meta.
What is the overall function of this, just rolling some arbitrary dice? It looks awfully complicated for such a simple task if that's it.
– Mast
yesterday
" The assignment deals with a game in which two players both have a set of dice. In the game, each player casts his/her dice and sums the outcomes of the dice. The player with the highest sum wins. If the sums are equal, it's a draw. Usually the dice within one set are equal, but different from the kind of dice in the other set." This is the assignment
– Beginner-Coder123
yesterday
1
As we all want to make our code more efficient or improve it in one way or another, try to write a title that summarizes what your code does, not what you want to get out of a review. Please see How to get the best value out of Code Review - Asking Questions for guidance on writing good question titles.
– BCdotWEB
yesterday
3
@Beginner-Coder123: You should add that description to the question body.
– Graipher
yesterday
1
Please do not vandalize your posts. By posting on the Stack Exchange network, you've granted a non-revocable right for SE to distribute that content (under the CC BY-SA 3.0 license). By SE policy, any vandalism will be reverted.
– Glorfindel
12 hours ago
comments disabled on deleted / locked posts / reviews |
I wrote a script that gives a "generalized" function of an arbitrary dice. Is this any good? How can it improve so to make it cleaner?
import random
def roll(die):
number = random.randint(0,len(die)-1)
b = die[number]
return b
Die1 = [1,2,3,4]
Die2 = [1,2,3,4,5,6] #num lists
def inptchacc():
ending_conditions = ['stop','Stop','quit','Quit']
end = False
inpu = input('what number would you like to add to the new face of the Die? (to end the die please type "stop or Stop or quit or Quit to finish the DIE" )')
while end == False:
if not(inpu in ending_conditions):
try:
retr = int(inpu)
return retr
except:
string = 'invalid input please try again'
inpu = input('invalid input please try again ')
else:
stop = 'stop'
return stop
def deeper(IDie):
list =
Adding = True
while Adding:
print('The Die ' + IDie + ' is currently ' + str(list) )
toadd = inptchacc()
if toadd != 'stop':
list.append(toadd)
else:
Adding = False
return list
def chance_overlap(Die1,Die2):
highnumber = 1000
counter = 0
for n in range(highnumber):
x = roll(Die1)
y = roll(Die2)
if x == y:
counter += 1
chance = counter/highnumber
return chance
chance = chance_overlap(Die1,Die2)
print(chance)
Doing = True
while Doing:
try:
IDie1 = deeper('IDie1')
IDie2 = deeper('IDie1')
chance2 = chance_overlap(IDie1,IDie2)
Doing = False
except:
print ('incompatible options selected returning....... ')
print(chance2)
python python-3.x
I wrote a script that gives a "generalized" function of an arbitrary dice. Is this any good? How can it improve so to make it cleaner?
import random
def roll(die):
number = random.randint(0,len(die)-1)
b = die[number]
return b
Die1 = [1,2,3,4]
Die2 = [1,2,3,4,5,6] #num lists
def inptchacc():
ending_conditions = ['stop','Stop','quit','Quit']
end = False
inpu = input('what number would you like to add to the new face of the Die? (to end the die please type "stop or Stop or quit or Quit to finish the DIE" )')
while end == False:
if not(inpu in ending_conditions):
try:
retr = int(inpu)
return retr
except:
string = 'invalid input please try again'
inpu = input('invalid input please try again ')
else:
stop = 'stop'
return stop
def deeper(IDie):
list =
Adding = True
while Adding:
print('The Die ' + IDie + ' is currently ' + str(list) )
toadd = inptchacc()
if toadd != 'stop':
list.append(toadd)
else:
Adding = False
return list
def chance_overlap(Die1,Die2):
highnumber = 1000
counter = 0
for n in range(highnumber):
x = roll(Die1)
y = roll(Die2)
if x == y:
counter += 1
chance = counter/highnumber
return chance
chance = chance_overlap(Die1,Die2)
print(chance)
Doing = True
while Doing:
try:
IDie1 = deeper('IDie1')
IDie2 = deeper('IDie1')
chance2 = chance_overlap(IDie1,IDie2)
Doing = False
except:
print ('incompatible options selected returning....... ')
print(chance2)
python python-3.x
python python-3.x
edited 12 hours ago
Glorfindel
2841615
2841615
asked yesterday
Beginner-Coder123Beginner-Coder123
293
293
locked by Vogel612♦ 12 hours ago
This post has been locked while disputes about its content are being resolved. For more info visit meta.
locked by Vogel612♦ 12 hours ago
This post has been locked while disputes about its content are being resolved. For more info visit meta.
What is the overall function of this, just rolling some arbitrary dice? It looks awfully complicated for such a simple task if that's it.
– Mast
yesterday
" The assignment deals with a game in which two players both have a set of dice. In the game, each player casts his/her dice and sums the outcomes of the dice. The player with the highest sum wins. If the sums are equal, it's a draw. Usually the dice within one set are equal, but different from the kind of dice in the other set." This is the assignment
– Beginner-Coder123
yesterday
1
As we all want to make our code more efficient or improve it in one way or another, try to write a title that summarizes what your code does, not what you want to get out of a review. Please see How to get the best value out of Code Review - Asking Questions for guidance on writing good question titles.
– BCdotWEB
yesterday
3
@Beginner-Coder123: You should add that description to the question body.
– Graipher
yesterday
1
Please do not vandalize your posts. By posting on the Stack Exchange network, you've granted a non-revocable right for SE to distribute that content (under the CC BY-SA 3.0 license). By SE policy, any vandalism will be reverted.
– Glorfindel
12 hours ago
comments disabled on deleted / locked posts / reviews |
What is the overall function of this, just rolling some arbitrary dice? It looks awfully complicated for such a simple task if that's it.
– Mast
yesterday
" The assignment deals with a game in which two players both have a set of dice. In the game, each player casts his/her dice and sums the outcomes of the dice. The player with the highest sum wins. If the sums are equal, it's a draw. Usually the dice within one set are equal, but different from the kind of dice in the other set." This is the assignment
– Beginner-Coder123
yesterday
1
As we all want to make our code more efficient or improve it in one way or another, try to write a title that summarizes what your code does, not what you want to get out of a review. Please see How to get the best value out of Code Review - Asking Questions for guidance on writing good question titles.
– BCdotWEB
yesterday
3
@Beginner-Coder123: You should add that description to the question body.
– Graipher
yesterday
1
Please do not vandalize your posts. By posting on the Stack Exchange network, you've granted a non-revocable right for SE to distribute that content (under the CC BY-SA 3.0 license). By SE policy, any vandalism will be reverted.
– Glorfindel
12 hours ago
What is the overall function of this, just rolling some arbitrary dice? It looks awfully complicated for such a simple task if that's it.
– Mast
yesterday
What is the overall function of this, just rolling some arbitrary dice? It looks awfully complicated for such a simple task if that's it.
– Mast
yesterday
" The assignment deals with a game in which two players both have a set of dice. In the game, each player casts his/her dice and sums the outcomes of the dice. The player with the highest sum wins. If the sums are equal, it's a draw. Usually the dice within one set are equal, but different from the kind of dice in the other set." This is the assignment
– Beginner-Coder123
yesterday
" The assignment deals with a game in which two players both have a set of dice. In the game, each player casts his/her dice and sums the outcomes of the dice. The player with the highest sum wins. If the sums are equal, it's a draw. Usually the dice within one set are equal, but different from the kind of dice in the other set." This is the assignment
– Beginner-Coder123
yesterday
1
1
As we all want to make our code more efficient or improve it in one way or another, try to write a title that summarizes what your code does, not what you want to get out of a review. Please see How to get the best value out of Code Review - Asking Questions for guidance on writing good question titles.
– BCdotWEB
yesterday
As we all want to make our code more efficient or improve it in one way or another, try to write a title that summarizes what your code does, not what you want to get out of a review. Please see How to get the best value out of Code Review - Asking Questions for guidance on writing good question titles.
– BCdotWEB
yesterday
3
3
@Beginner-Coder123: You should add that description to the question body.
– Graipher
yesterday
@Beginner-Coder123: You should add that description to the question body.
– Graipher
yesterday
1
1
Please do not vandalize your posts. By posting on the Stack Exchange network, you've granted a non-revocable right for SE to distribute that content (under the CC BY-SA 3.0 license). By SE policy, any vandalism will be reverted.
– Glorfindel
12 hours ago
Please do not vandalize your posts. By posting on the Stack Exchange network, you've granted a non-revocable right for SE to distribute that content (under the CC BY-SA 3.0 license). By SE policy, any vandalism will be reverted.
– Glorfindel
12 hours ago
comments disabled on deleted / locked posts / reviews |
3 Answers
3
active
oldest
votes
Since you need to roll many times in the chance_overlap
function, you might want to optimize making n
rolls, using random.choices
(Python 3.6+):
from itertools import groupby
def roll(die, n=1):
if n == 1:
return random.choice(die)
return random.choices(die, k=n)
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
def overlap_chance(*dice, n=1000):
rolls = [roll(die, n) for die in dice]
equal_rolls = sum(all_equal(roll) for roll in zip(*rolls))
return equal_rolls / n
Here I chose to include it in your roll
function, which is nice because you only have on function, but you do have different return types depending on the value of k
, which is not so nice. If you want to you can make it into two separate functions instead.
I made chance_overlap
take a variable number of dice so it even works for more than two (and also for one, which is a bit boring).
In addition, I followed Python's official style-guide, PEP8 for variable names (lower_case
).
The all_equal
function is directly taken from the itertools
recipes.
Using a Monte-Carlo method to determine the chance for the dice to get the same values is fine, but you could just use plain old math.
Each distinct value $j$ on each die $i$ has probability $p^i_j = n^i_j / k_i$, where $k_i$ is the number of faces of die $i$ and $n^i_j$ the number of times the value $j$ appears on that die. Then the chance to have an overlap is simply given by
$P(overlap) = sumlimits_j prodlimits_i p^i_j = sumlimits_j prodlimits_i n^i_j / k_i$,
where $i$ goes over all dice and $j$ over all values present on any dice (with $n^i_j = 0$ if value $j$ does not appear on die $i$).
In other words dice rolls are independent events and e.g. the chance to get two heads or two tails with a fair coin (dice = [["H", "T"], ["H", "T"]]
) are $P(HH vee TT) = 0.5cdot0.5 + 0.5cdot0.5 = 0.5$.
from collections import Counter
from functools import reduce
from itertools import chain
from operator import mul
def overlap_chance_real(*dice):
all_values = set(chain.from_iterable(dice))
counters = [Counter(die) for die in dice]
lengths = [len(die) for die in dice]
return sum(reduce(mul, [counter[val] / length
for counter, length in zip(counters, lengths)])
for val in all_values)
The nice thing about this is that we don't need to worry if not all dice have the same values, since Counter
objects return a count of zero for non-existing keys.
For dice = [[1,2,3], [1,1,1]]
it returns the correct (and precise) value of 0.3333333333333333
. and 0.5
for dice = [["H", "T"], ["H", "T"]]
.
The execution time of the first function (overlap_chance
) increases linearly with the number of dice (all six sided) and it is in general slower than the second function (overlap_chance_real
):
With two dice with increasing number of faces the first function is slower but basically constant in time while the second function is faster but the time increases with the number of faces. This plot also contains your function (since it can only deal with two dice it was not included in the previous plot), which is slower than both:
1
What did you use to make those graphs?
– esote
yesterday
1
@esotetimeit
andmatplotlib
, as detailed in this question (and it's answer).
– Graipher
yesterday
I tried to implement this, but I broke the code.
– Beginner-Coder123
14 hours ago
I just don't seem to know what functions you're specifying with the "all_equal", and "overlap_chance_real" If I intend to replace them with some of my written functions, it doesn't work :/. What changes have to be made exactly when reviewing the answer from you? I'm a newbie to be honest
– Beginner-Coder123
14 hours ago
1
Haha, yes I made the mistake with specifying who it was. but alright, I'll give it a try. Much appreciated!
– Beginner-Coder123
14 hours ago
|
show 5 more comments
The right functions
def roll(die):
number = random.randint(0,len(die)-1)
b = die[number]
return b
Can be:
def roll(die):
return random.choice(die)
inptchacc()
You could instead check only the first character of the input (case insensitive), rather than expecting the whole keyword.
Your return variable stop
isn't needed. Just return directly.
stop = 'stop'
return stop
Should be:
return 'stop'
Likewise elsewhere in your code.
Use __main__
Rather than putting code between functions and also at the end of the file, instead use:
if __name__ == '__main__':
See here for more details.
add a comment |
Make the computer count
These:
Die1 = [1,2,3,4]
Die2 = [1,2,3,4,5,6]
should be
die1 = list(range(1, 5))
die2 = list(range(1, 7))
Only compare once
This list:
ending_conditions = ['stop','Stop','quit','Quit']
has two problems. First, don't store multiple cases for one word - just store lower (or upper) case - I prefer lower, because no shouting.
Also, it shouldn't be a list. It's getting tested for membership, so make it a set. In other words,
ending_conditions = {'stop', 'quiet'}
then for use, write "not in", instead of "not x in"; i.e.
if inpu.lower() not in ending_conditions:
PEP8
Run your code through a linter. It will catch several things, including
- Don't capitalize your variables (
Die1
,Die2
,Adding
,Doing
) if they're local. If they're global constants, they need to be all-uppercase. In the case ofDie*
, they probably shouldn't be global at all. - Add a couple of newlines after your
import
s
Grammar
Write:
print('Incompatible options selected. Returning...')
add a comment |
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Since you need to roll many times in the chance_overlap
function, you might want to optimize making n
rolls, using random.choices
(Python 3.6+):
from itertools import groupby
def roll(die, n=1):
if n == 1:
return random.choice(die)
return random.choices(die, k=n)
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
def overlap_chance(*dice, n=1000):
rolls = [roll(die, n) for die in dice]
equal_rolls = sum(all_equal(roll) for roll in zip(*rolls))
return equal_rolls / n
Here I chose to include it in your roll
function, which is nice because you only have on function, but you do have different return types depending on the value of k
, which is not so nice. If you want to you can make it into two separate functions instead.
I made chance_overlap
take a variable number of dice so it even works for more than two (and also for one, which is a bit boring).
In addition, I followed Python's official style-guide, PEP8 for variable names (lower_case
).
The all_equal
function is directly taken from the itertools
recipes.
Using a Monte-Carlo method to determine the chance for the dice to get the same values is fine, but you could just use plain old math.
Each distinct value $j$ on each die $i$ has probability $p^i_j = n^i_j / k_i$, where $k_i$ is the number of faces of die $i$ and $n^i_j$ the number of times the value $j$ appears on that die. Then the chance to have an overlap is simply given by
$P(overlap) = sumlimits_j prodlimits_i p^i_j = sumlimits_j prodlimits_i n^i_j / k_i$,
where $i$ goes over all dice and $j$ over all values present on any dice (with $n^i_j = 0$ if value $j$ does not appear on die $i$).
In other words dice rolls are independent events and e.g. the chance to get two heads or two tails with a fair coin (dice = [["H", "T"], ["H", "T"]]
) are $P(HH vee TT) = 0.5cdot0.5 + 0.5cdot0.5 = 0.5$.
from collections import Counter
from functools import reduce
from itertools import chain
from operator import mul
def overlap_chance_real(*dice):
all_values = set(chain.from_iterable(dice))
counters = [Counter(die) for die in dice]
lengths = [len(die) for die in dice]
return sum(reduce(mul, [counter[val] / length
for counter, length in zip(counters, lengths)])
for val in all_values)
The nice thing about this is that we don't need to worry if not all dice have the same values, since Counter
objects return a count of zero for non-existing keys.
For dice = [[1,2,3], [1,1,1]]
it returns the correct (and precise) value of 0.3333333333333333
. and 0.5
for dice = [["H", "T"], ["H", "T"]]
.
The execution time of the first function (overlap_chance
) increases linearly with the number of dice (all six sided) and it is in general slower than the second function (overlap_chance_real
):
With two dice with increasing number of faces the first function is slower but basically constant in time while the second function is faster but the time increases with the number of faces. This plot also contains your function (since it can only deal with two dice it was not included in the previous plot), which is slower than both:
1
What did you use to make those graphs?
– esote
yesterday
1
@esotetimeit
andmatplotlib
, as detailed in this question (and it's answer).
– Graipher
yesterday
I tried to implement this, but I broke the code.
– Beginner-Coder123
14 hours ago
I just don't seem to know what functions you're specifying with the "all_equal", and "overlap_chance_real" If I intend to replace them with some of my written functions, it doesn't work :/. What changes have to be made exactly when reviewing the answer from you? I'm a newbie to be honest
– Beginner-Coder123
14 hours ago
1
Haha, yes I made the mistake with specifying who it was. but alright, I'll give it a try. Much appreciated!
– Beginner-Coder123
14 hours ago
|
show 5 more comments
Since you need to roll many times in the chance_overlap
function, you might want to optimize making n
rolls, using random.choices
(Python 3.6+):
from itertools import groupby
def roll(die, n=1):
if n == 1:
return random.choice(die)
return random.choices(die, k=n)
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
def overlap_chance(*dice, n=1000):
rolls = [roll(die, n) for die in dice]
equal_rolls = sum(all_equal(roll) for roll in zip(*rolls))
return equal_rolls / n
Here I chose to include it in your roll
function, which is nice because you only have on function, but you do have different return types depending on the value of k
, which is not so nice. If you want to you can make it into two separate functions instead.
I made chance_overlap
take a variable number of dice so it even works for more than two (and also for one, which is a bit boring).
In addition, I followed Python's official style-guide, PEP8 for variable names (lower_case
).
The all_equal
function is directly taken from the itertools
recipes.
Using a Monte-Carlo method to determine the chance for the dice to get the same values is fine, but you could just use plain old math.
Each distinct value $j$ on each die $i$ has probability $p^i_j = n^i_j / k_i$, where $k_i$ is the number of faces of die $i$ and $n^i_j$ the number of times the value $j$ appears on that die. Then the chance to have an overlap is simply given by
$P(overlap) = sumlimits_j prodlimits_i p^i_j = sumlimits_j prodlimits_i n^i_j / k_i$,
where $i$ goes over all dice and $j$ over all values present on any dice (with $n^i_j = 0$ if value $j$ does not appear on die $i$).
In other words dice rolls are independent events and e.g. the chance to get two heads or two tails with a fair coin (dice = [["H", "T"], ["H", "T"]]
) are $P(HH vee TT) = 0.5cdot0.5 + 0.5cdot0.5 = 0.5$.
from collections import Counter
from functools import reduce
from itertools import chain
from operator import mul
def overlap_chance_real(*dice):
all_values = set(chain.from_iterable(dice))
counters = [Counter(die) for die in dice]
lengths = [len(die) for die in dice]
return sum(reduce(mul, [counter[val] / length
for counter, length in zip(counters, lengths)])
for val in all_values)
The nice thing about this is that we don't need to worry if not all dice have the same values, since Counter
objects return a count of zero for non-existing keys.
For dice = [[1,2,3], [1,1,1]]
it returns the correct (and precise) value of 0.3333333333333333
. and 0.5
for dice = [["H", "T"], ["H", "T"]]
.
The execution time of the first function (overlap_chance
) increases linearly with the number of dice (all six sided) and it is in general slower than the second function (overlap_chance_real
):
With two dice with increasing number of faces the first function is slower but basically constant in time while the second function is faster but the time increases with the number of faces. This plot also contains your function (since it can only deal with two dice it was not included in the previous plot), which is slower than both:
1
What did you use to make those graphs?
– esote
yesterday
1
@esotetimeit
andmatplotlib
, as detailed in this question (and it's answer).
– Graipher
yesterday
I tried to implement this, but I broke the code.
– Beginner-Coder123
14 hours ago
I just don't seem to know what functions you're specifying with the "all_equal", and "overlap_chance_real" If I intend to replace them with some of my written functions, it doesn't work :/. What changes have to be made exactly when reviewing the answer from you? I'm a newbie to be honest
– Beginner-Coder123
14 hours ago
1
Haha, yes I made the mistake with specifying who it was. but alright, I'll give it a try. Much appreciated!
– Beginner-Coder123
14 hours ago
|
show 5 more comments
Since you need to roll many times in the chance_overlap
function, you might want to optimize making n
rolls, using random.choices
(Python 3.6+):
from itertools import groupby
def roll(die, n=1):
if n == 1:
return random.choice(die)
return random.choices(die, k=n)
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
def overlap_chance(*dice, n=1000):
rolls = [roll(die, n) for die in dice]
equal_rolls = sum(all_equal(roll) for roll in zip(*rolls))
return equal_rolls / n
Here I chose to include it in your roll
function, which is nice because you only have on function, but you do have different return types depending on the value of k
, which is not so nice. If you want to you can make it into two separate functions instead.
I made chance_overlap
take a variable number of dice so it even works for more than two (and also for one, which is a bit boring).
In addition, I followed Python's official style-guide, PEP8 for variable names (lower_case
).
The all_equal
function is directly taken from the itertools
recipes.
Using a Monte-Carlo method to determine the chance for the dice to get the same values is fine, but you could just use plain old math.
Each distinct value $j$ on each die $i$ has probability $p^i_j = n^i_j / k_i$, where $k_i$ is the number of faces of die $i$ and $n^i_j$ the number of times the value $j$ appears on that die. Then the chance to have an overlap is simply given by
$P(overlap) = sumlimits_j prodlimits_i p^i_j = sumlimits_j prodlimits_i n^i_j / k_i$,
where $i$ goes over all dice and $j$ over all values present on any dice (with $n^i_j = 0$ if value $j$ does not appear on die $i$).
In other words dice rolls are independent events and e.g. the chance to get two heads or two tails with a fair coin (dice = [["H", "T"], ["H", "T"]]
) are $P(HH vee TT) = 0.5cdot0.5 + 0.5cdot0.5 = 0.5$.
from collections import Counter
from functools import reduce
from itertools import chain
from operator import mul
def overlap_chance_real(*dice):
all_values = set(chain.from_iterable(dice))
counters = [Counter(die) for die in dice]
lengths = [len(die) for die in dice]
return sum(reduce(mul, [counter[val] / length
for counter, length in zip(counters, lengths)])
for val in all_values)
The nice thing about this is that we don't need to worry if not all dice have the same values, since Counter
objects return a count of zero for non-existing keys.
For dice = [[1,2,3], [1,1,1]]
it returns the correct (and precise) value of 0.3333333333333333
. and 0.5
for dice = [["H", "T"], ["H", "T"]]
.
The execution time of the first function (overlap_chance
) increases linearly with the number of dice (all six sided) and it is in general slower than the second function (overlap_chance_real
):
With two dice with increasing number of faces the first function is slower but basically constant in time while the second function is faster but the time increases with the number of faces. This plot also contains your function (since it can only deal with two dice it was not included in the previous plot), which is slower than both:
Since you need to roll many times in the chance_overlap
function, you might want to optimize making n
rolls, using random.choices
(Python 3.6+):
from itertools import groupby
def roll(die, n=1):
if n == 1:
return random.choice(die)
return random.choices(die, k=n)
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
def overlap_chance(*dice, n=1000):
rolls = [roll(die, n) for die in dice]
equal_rolls = sum(all_equal(roll) for roll in zip(*rolls))
return equal_rolls / n
Here I chose to include it in your roll
function, which is nice because you only have on function, but you do have different return types depending on the value of k
, which is not so nice. If you want to you can make it into two separate functions instead.
I made chance_overlap
take a variable number of dice so it even works for more than two (and also for one, which is a bit boring).
In addition, I followed Python's official style-guide, PEP8 for variable names (lower_case
).
The all_equal
function is directly taken from the itertools
recipes.
Using a Monte-Carlo method to determine the chance for the dice to get the same values is fine, but you could just use plain old math.
Each distinct value $j$ on each die $i$ has probability $p^i_j = n^i_j / k_i$, where $k_i$ is the number of faces of die $i$ and $n^i_j$ the number of times the value $j$ appears on that die. Then the chance to have an overlap is simply given by
$P(overlap) = sumlimits_j prodlimits_i p^i_j = sumlimits_j prodlimits_i n^i_j / k_i$,
where $i$ goes over all dice and $j$ over all values present on any dice (with $n^i_j = 0$ if value $j$ does not appear on die $i$).
In other words dice rolls are independent events and e.g. the chance to get two heads or two tails with a fair coin (dice = [["H", "T"], ["H", "T"]]
) are $P(HH vee TT) = 0.5cdot0.5 + 0.5cdot0.5 = 0.5$.
from collections import Counter
from functools import reduce
from itertools import chain
from operator import mul
def overlap_chance_real(*dice):
all_values = set(chain.from_iterable(dice))
counters = [Counter(die) for die in dice]
lengths = [len(die) for die in dice]
return sum(reduce(mul, [counter[val] / length
for counter, length in zip(counters, lengths)])
for val in all_values)
The nice thing about this is that we don't need to worry if not all dice have the same values, since Counter
objects return a count of zero for non-existing keys.
For dice = [[1,2,3], [1,1,1]]
it returns the correct (and precise) value of 0.3333333333333333
. and 0.5
for dice = [["H", "T"], ["H", "T"]]
.
The execution time of the first function (overlap_chance
) increases linearly with the number of dice (all six sided) and it is in general slower than the second function (overlap_chance_real
):
With two dice with increasing number of faces the first function is slower but basically constant in time while the second function is faster but the time increases with the number of faces. This plot also contains your function (since it can only deal with two dice it was not included in the previous plot), which is slower than both:
edited 14 hours ago
answered yesterday
GraipherGraipher
23.7k53585
23.7k53585
1
What did you use to make those graphs?
– esote
yesterday
1
@esotetimeit
andmatplotlib
, as detailed in this question (and it's answer).
– Graipher
yesterday
I tried to implement this, but I broke the code.
– Beginner-Coder123
14 hours ago
I just don't seem to know what functions you're specifying with the "all_equal", and "overlap_chance_real" If I intend to replace them with some of my written functions, it doesn't work :/. What changes have to be made exactly when reviewing the answer from you? I'm a newbie to be honest
– Beginner-Coder123
14 hours ago
1
Haha, yes I made the mistake with specifying who it was. but alright, I'll give it a try. Much appreciated!
– Beginner-Coder123
14 hours ago
|
show 5 more comments
1
What did you use to make those graphs?
– esote
yesterday
1
@esotetimeit
andmatplotlib
, as detailed in this question (and it's answer).
– Graipher
yesterday
I tried to implement this, but I broke the code.
– Beginner-Coder123
14 hours ago
I just don't seem to know what functions you're specifying with the "all_equal", and "overlap_chance_real" If I intend to replace them with some of my written functions, it doesn't work :/. What changes have to be made exactly when reviewing the answer from you? I'm a newbie to be honest
– Beginner-Coder123
14 hours ago
1
Haha, yes I made the mistake with specifying who it was. but alright, I'll give it a try. Much appreciated!
– Beginner-Coder123
14 hours ago
1
1
What did you use to make those graphs?
– esote
yesterday
What did you use to make those graphs?
– esote
yesterday
1
1
@esote
timeit
and matplotlib
, as detailed in this question (and it's answer).– Graipher
yesterday
@esote
timeit
and matplotlib
, as detailed in this question (and it's answer).– Graipher
yesterday
I tried to implement this, but I broke the code.
– Beginner-Coder123
14 hours ago
I tried to implement this, but I broke the code.
– Beginner-Coder123
14 hours ago
I just don't seem to know what functions you're specifying with the "all_equal", and "overlap_chance_real" If I intend to replace them with some of my written functions, it doesn't work :/. What changes have to be made exactly when reviewing the answer from you? I'm a newbie to be honest
– Beginner-Coder123
14 hours ago
I just don't seem to know what functions you're specifying with the "all_equal", and "overlap_chance_real" If I intend to replace them with some of my written functions, it doesn't work :/. What changes have to be made exactly when reviewing the answer from you? I'm a newbie to be honest
– Beginner-Coder123
14 hours ago
1
1
Haha, yes I made the mistake with specifying who it was. but alright, I'll give it a try. Much appreciated!
– Beginner-Coder123
14 hours ago
Haha, yes I made the mistake with specifying who it was. but alright, I'll give it a try. Much appreciated!
– Beginner-Coder123
14 hours ago
|
show 5 more comments
The right functions
def roll(die):
number = random.randint(0,len(die)-1)
b = die[number]
return b
Can be:
def roll(die):
return random.choice(die)
inptchacc()
You could instead check only the first character of the input (case insensitive), rather than expecting the whole keyword.
Your return variable stop
isn't needed. Just return directly.
stop = 'stop'
return stop
Should be:
return 'stop'
Likewise elsewhere in your code.
Use __main__
Rather than putting code between functions and also at the end of the file, instead use:
if __name__ == '__main__':
See here for more details.
add a comment |
The right functions
def roll(die):
number = random.randint(0,len(die)-1)
b = die[number]
return b
Can be:
def roll(die):
return random.choice(die)
inptchacc()
You could instead check only the first character of the input (case insensitive), rather than expecting the whole keyword.
Your return variable stop
isn't needed. Just return directly.
stop = 'stop'
return stop
Should be:
return 'stop'
Likewise elsewhere in your code.
Use __main__
Rather than putting code between functions and also at the end of the file, instead use:
if __name__ == '__main__':
See here for more details.
add a comment |
The right functions
def roll(die):
number = random.randint(0,len(die)-1)
b = die[number]
return b
Can be:
def roll(die):
return random.choice(die)
inptchacc()
You could instead check only the first character of the input (case insensitive), rather than expecting the whole keyword.
Your return variable stop
isn't needed. Just return directly.
stop = 'stop'
return stop
Should be:
return 'stop'
Likewise elsewhere in your code.
Use __main__
Rather than putting code between functions and also at the end of the file, instead use:
if __name__ == '__main__':
See here for more details.
The right functions
def roll(die):
number = random.randint(0,len(die)-1)
b = die[number]
return b
Can be:
def roll(die):
return random.choice(die)
inptchacc()
You could instead check only the first character of the input (case insensitive), rather than expecting the whole keyword.
Your return variable stop
isn't needed. Just return directly.
stop = 'stop'
return stop
Should be:
return 'stop'
Likewise elsewhere in your code.
Use __main__
Rather than putting code between functions and also at the end of the file, instead use:
if __name__ == '__main__':
See here for more details.
answered yesterday
esoteesote
2,0991933
2,0991933
add a comment |
add a comment |
Make the computer count
These:
Die1 = [1,2,3,4]
Die2 = [1,2,3,4,5,6]
should be
die1 = list(range(1, 5))
die2 = list(range(1, 7))
Only compare once
This list:
ending_conditions = ['stop','Stop','quit','Quit']
has two problems. First, don't store multiple cases for one word - just store lower (or upper) case - I prefer lower, because no shouting.
Also, it shouldn't be a list. It's getting tested for membership, so make it a set. In other words,
ending_conditions = {'stop', 'quiet'}
then for use, write "not in", instead of "not x in"; i.e.
if inpu.lower() not in ending_conditions:
PEP8
Run your code through a linter. It will catch several things, including
- Don't capitalize your variables (
Die1
,Die2
,Adding
,Doing
) if they're local. If they're global constants, they need to be all-uppercase. In the case ofDie*
, they probably shouldn't be global at all. - Add a couple of newlines after your
import
s
Grammar
Write:
print('Incompatible options selected. Returning...')
add a comment |
Make the computer count
These:
Die1 = [1,2,3,4]
Die2 = [1,2,3,4,5,6]
should be
die1 = list(range(1, 5))
die2 = list(range(1, 7))
Only compare once
This list:
ending_conditions = ['stop','Stop','quit','Quit']
has two problems. First, don't store multiple cases for one word - just store lower (or upper) case - I prefer lower, because no shouting.
Also, it shouldn't be a list. It's getting tested for membership, so make it a set. In other words,
ending_conditions = {'stop', 'quiet'}
then for use, write "not in", instead of "not x in"; i.e.
if inpu.lower() not in ending_conditions:
PEP8
Run your code through a linter. It will catch several things, including
- Don't capitalize your variables (
Die1
,Die2
,Adding
,Doing
) if they're local. If they're global constants, they need to be all-uppercase. In the case ofDie*
, they probably shouldn't be global at all. - Add a couple of newlines after your
import
s
Grammar
Write:
print('Incompatible options selected. Returning...')
add a comment |
Make the computer count
These:
Die1 = [1,2,3,4]
Die2 = [1,2,3,4,5,6]
should be
die1 = list(range(1, 5))
die2 = list(range(1, 7))
Only compare once
This list:
ending_conditions = ['stop','Stop','quit','Quit']
has two problems. First, don't store multiple cases for one word - just store lower (or upper) case - I prefer lower, because no shouting.
Also, it shouldn't be a list. It's getting tested for membership, so make it a set. In other words,
ending_conditions = {'stop', 'quiet'}
then for use, write "not in", instead of "not x in"; i.e.
if inpu.lower() not in ending_conditions:
PEP8
Run your code through a linter. It will catch several things, including
- Don't capitalize your variables (
Die1
,Die2
,Adding
,Doing
) if they're local. If they're global constants, they need to be all-uppercase. In the case ofDie*
, they probably shouldn't be global at all. - Add a couple of newlines after your
import
s
Grammar
Write:
print('Incompatible options selected. Returning...')
Make the computer count
These:
Die1 = [1,2,3,4]
Die2 = [1,2,3,4,5,6]
should be
die1 = list(range(1, 5))
die2 = list(range(1, 7))
Only compare once
This list:
ending_conditions = ['stop','Stop','quit','Quit']
has two problems. First, don't store multiple cases for one word - just store lower (or upper) case - I prefer lower, because no shouting.
Also, it shouldn't be a list. It's getting tested for membership, so make it a set. In other words,
ending_conditions = {'stop', 'quiet'}
then for use, write "not in", instead of "not x in"; i.e.
if inpu.lower() not in ending_conditions:
PEP8
Run your code through a linter. It will catch several things, including
- Don't capitalize your variables (
Die1
,Die2
,Adding
,Doing
) if they're local. If they're global constants, they need to be all-uppercase. In the case ofDie*
, they probably shouldn't be global at all. - Add a couple of newlines after your
import
s
Grammar
Write:
print('Incompatible options selected. Returning...')
answered yesterday
ReinderienReinderien
4,077821
4,077821
add a comment |
add a comment |
What is the overall function of this, just rolling some arbitrary dice? It looks awfully complicated for such a simple task if that's it.
– Mast
yesterday
" The assignment deals with a game in which two players both have a set of dice. In the game, each player casts his/her dice and sums the outcomes of the dice. The player with the highest sum wins. If the sums are equal, it's a draw. Usually the dice within one set are equal, but different from the kind of dice in the other set." This is the assignment
– Beginner-Coder123
yesterday
1
As we all want to make our code more efficient or improve it in one way or another, try to write a title that summarizes what your code does, not what you want to get out of a review. Please see How to get the best value out of Code Review - Asking Questions for guidance on writing good question titles.
– BCdotWEB
yesterday
3
@Beginner-Coder123: You should add that description to the question body.
– Graipher
yesterday
1
Please do not vandalize your posts. By posting on the Stack Exchange network, you've granted a non-revocable right for SE to distribute that content (under the CC BY-SA 3.0 license). By SE policy, any vandalism will be reverted.
– Glorfindel
12 hours ago