HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Scoring system of a pool of dice

Submitted by: @import:stackexchange-codereview··
0
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:

  • [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 :

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) == - res


Also, 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 -res

Code 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) == - res
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 -res

Context

StackExchange Code Review Q#71411, answer score: 2

Revisions (0)

No revisions yet.