patternpythonMinor
Scoring system of a pool of dice
Viewed 0 times
poolscoringsystemdice
Problem
I want to do some Monte Carlo simulations on a system based on a pool of dice. For each trial, a number of dies are rolled, and the winner is the highest value, getting as many points as dice are over the second, including ties.
Examples:
Here is my algorithm. Can you think of a better way?
```
from collections import Counter
import numpy as np
def check_rolls(one, other):
one_c = Counter(one)
other_c = Counter(other)
ties = Counter()
# Remove ties:
for tie_val in set(one_c.keys()).intersection(other_c.keys()):
toremove = min(one_c[tie_val], other_c[tie_val])
one_c[tie_val] -= toremove
other_c[tie_val] -= toremove
ties[tie_val] = toremove
# Clear if 0
if one_c[tie_val] == 0:
del one_c[tie_val]
if other_c[tie_val] == 0:
del other_c[tie_val]
max_one = max(one_c.keys()) if one_c else 0
max_other = max(other_c.keys()) if other_c else 0
winner = max_one > max_other
threshold = min(max_one, max_other)
if winner:
# One wins
result = sum(val for key, val in one_c.iteritems()
if key > threshold) + \
sum(val for key, val in ties.iteritems()
if key >= threshold)
else:
result = -(sum(val for key, val in other_c.iteritems()
if key > threshold) +
sum(val for key, val in ties.iteritems()
if key >= threshold))
return result
def roll(d_player, d_gm):
return check_rolls(np.random.randint(1, 21, size=d_player),
np.random.randint(1, 21, size=d_gm))
def test():
assert check_rolls([1, 1, 2], [1, 1]) == 3
assert che
Examples:
- [1, 2], [5, 1] One point for the second (5 is the only value over 2).
- [20, 12, 20, 14], [17, 15, 19, 18] Two points for the first one (two 20 over 19).
- [20, 12, 20, 14], [17, 15, 20, 18] Two points for the first one (one tie at 20, one die over 18).
Here is my algorithm. Can you think of a better way?
```
from collections import Counter
import numpy as np
def check_rolls(one, other):
one_c = Counter(one)
other_c = Counter(other)
ties = Counter()
# Remove ties:
for tie_val in set(one_c.keys()).intersection(other_c.keys()):
toremove = min(one_c[tie_val], other_c[tie_val])
one_c[tie_val] -= toremove
other_c[tie_val] -= toremove
ties[tie_val] = toremove
# Clear if 0
if one_c[tie_val] == 0:
del one_c[tie_val]
if other_c[tie_val] == 0:
del other_c[tie_val]
max_one = max(one_c.keys()) if one_c else 0
max_other = max(other_c.keys()) if other_c else 0
winner = max_one > max_other
threshold = min(max_one, max_other)
if winner:
# One wins
result = sum(val for key, val in one_c.iteritems()
if key > threshold) + \
sum(val for key, val in ties.iteritems()
if key >= threshold)
else:
result = -(sum(val for key, val in other_c.iteritems()
if key > threshold) +
sum(val for key, val in ties.iteritems()
if key >= threshold))
return result
def roll(d_player, d_gm):
return check_rolls(np.random.randint(1, 21, size=d_player),
np.random.randint(1, 21, size=d_gm))
def test():
assert check_rolls([1, 1, 2], [1, 1]) == 3
assert che
Solution
Disclaimer : this is not quite an answer but more than a comment.
You've written tests and I'd like to start by thanking your for this. This definitly makes reviewing easier.
Good think to know if that you can make easily double the amount of test you have without thinking too much.
Indeed, it seems like by inversing the parameters of your function, the return value should be the opposite value.
You can write this :
Also, I thought I'd take this chance to test
Edit : still not quite a review but you got me interested and I wondered if this could be written in a more simple way. Here's what I got translating my understanding of the problem into Python code :
You've written tests and I'd like to start by thanking your for this. This definitly makes reviewing easier.
Good think to know if that you can make easily double the amount of test you have without thinking too much.
Indeed, it seems like by inversing the parameters of your function, the return value should be the opposite value.
You can write this :
for d1, d2, res in [
([1, 1, 2], [1, 1], 3),
([1, 2, 4, 5], [3, 4], 2),
([1, 2, 4, 5], [3, 5], 2),
([1, 2, 4, 5], [3, 6], -1),
([20, 12, 20, 14], [17, 15, 20, 5, 18, 2], 2),
([20, 10, 2, 4], [ 5, 20, 6, 4, 19, 3], -2)
]:
assert check_rolls(d1, d2) == res
assert check_rolls(d2, d1) == - resAlso, I thought I'd take this chance to test
check_rolls(d1, d1) and it seems like it always returns - len(d1). Shouldn't you handle perfect tie situation in a different way ?Edit : still not quite a review but you got me interested and I wondered if this could be written in a more simple way. Here's what I got translating my understanding of the problem into Python code :
def check_rolls(d1, d2):
c1, c2 = Counter(d1), Counter(d2)
res = 0 # nb points
winner = 0 # player winning so far : 1 if 1, 2 if 2, 0 otherwise
for key in reversed(sorted(set(c1) | set(c2))):
v1, v2 = c1[key], c2[key]
if v1 == v2:
res+= v1 # ties count for whoever is winning
else:
local_winner = 1 if v1 > v2 else 2
if winner and local_winner != winner:
break
else:
winner = local_winner
res+= max(v1, v2)
return res if winner == 1 else -resCode Snippets
for d1, d2, res in [
([1, 1, 2], [1, 1], 3),
([1, 2, 4, 5], [3, 4], 2),
([1, 2, 4, 5], [3, 5], 2),
([1, 2, 4, 5], [3, 6], -1),
([20, 12, 20, 14], [17, 15, 20, 5, 18, 2], 2),
([20, 10, 2, 4], [ 5, 20, 6, 4, 19, 3], -2)
]:
assert check_rolls(d1, d2) == res
assert check_rolls(d2, d1) == - resdef check_rolls(d1, d2):
c1, c2 = Counter(d1), Counter(d2)
res = 0 # nb points
winner = 0 # player winning so far : 1 if 1, 2 if 2, 0 otherwise
for key in reversed(sorted(set(c1) | set(c2))):
v1, v2 = c1[key], c2[key]
if v1 == v2:
res+= v1 # ties count for whoever is winning
else:
local_winner = 1 if v1 > v2 else 2
if winner and local_winner != winner:
break
else:
winner = local_winner
res+= max(v1, v2)
return res if winner == 1 else -resContext
StackExchange Code Review Q#71411, answer score: 2
Revisions (0)
No revisions yet.