Wednesday, September 21, 2016

Non-transitive dice in Python



All dies are fair and six-sided but with differing numbers on each face.
One die is said to be stronger, > than another if that die has a higher probability of rolling a higher number than the other and so winning.
In [29]:
from itertools import product
from collections import namedtuple
In [35]:
Die = namedtuple('Die', 'name, faces')
dice = [Die('A', [3,3,3,3,3,3]),
        Die('B', [4,4,4,4,0,0]),
        Die('C', [5,5,5,1,1,1]),
        Die('D', [2,2,2,2,6,6])]
In [44]:
def cmp_die(die1, die2):
    'compares two die returning <, > or ='
    # Numbers of times one die wins against the other for all combinations
    win1 = sum(d1 > d2 for d1, d2 in product(die1, die2))
    win2 = sum(d2 > d1 for d1, d2 in product(die1, die2))
    return '>' if win1 > win2 else ('<' if win1 < win2 else '=')
In [45]:
dice
Out[45]:
[Die(name='A', faces=[3, 3, 3, 3, 3, 3]),
 Die(name='B', faces=[4, 4, 4, 4, 0, 0]),
 Die(name='C', faces=[5, 5, 5, 1, 1, 1]),
 Die(name='D', faces=[2, 2, 2, 2, 6, 6])]
In [46]:
cmp_die(dice[0].faces, dice[1].faces)
Out[46]:
'<'
In [47]:
shortform = []
for (n1, f1), (n2, f2) in zip(dice, dice[1:] + dice[:1]):
    comparison = cmp_die(f1, f2)
    print('%s: %r %s %s: %r' % (n1, f1, comparison, n2, f2))
    shortform.append('%s %s %s' % (n1, comparison, n2))
print('\nIn short:', ', '.join(shortform))
    
A: [3, 3, 3, 3, 3, 3] < B: [4, 4, 4, 4, 0, 0]
B: [4, 4, 4, 4, 0, 0] < C: [5, 5, 5, 1, 1, 1]
C: [5, 5, 5, 1, 1, 1] < D: [2, 2, 2, 2, 6, 6]
D: [2, 2, 2, 2, 6, 6] < A: [3, 3, 3, 3, 3, 3]

In short: A < B, B < C, C < D, D < A
Notice that it forms a circle of winning die!