On Monday, 9 February 2015 21:57:51 UTC, Paul  Moore  wrote:
> On Friday, 6 February 2015 23:49:51 UTC, Steven D'Aprano  wrote:
> > Very nice! Care to share the code?
> 
> Will do.

Here's the code I used for the Monopoly calculations.

import numpy as np

def monopoly(samples):
    # 2d6 x 3
    num = 2
    sides = 6
    rolls = 3
    dice = np.random.randint(1, sides + 1, size=(samples, rolls, num))

    # Doubles are if the two dice are the same
    doubles = dice[...,0] == dice[...,1]
    # Reroll if this (and all previous rolls) are doubles
    reroll = np.logical_and.accumulate(doubles, axis=1)
    # Keep the first entry, and any valid rerolls
    keep = np.insert(reroll, 0, True, axis=1)
    # The last column (all 3 are doubles) is "go to gaol"
    keep, gaol = np.split(keep, [-1], axis=-1)
    gaol = gaol[...,0]

    # Add up all the rolls and zero out any we don't want to keep
    totals = dice.sum(axis=-1) * keep
    # Remove any "go to gaol" cases
    totals = totals[~gaol,...]
    # Add up the distance moved
    totals = totals.sum(axis=-1)

    gaol_count = np.sum(gaol)

    return totals, gaol_count

samples = 1000000
totals, gaol_count = monopoly(samples)

print("Average distance moved =", totals.mean())
print("Percentage of times you go to gaol: {:%}".format(gaol_count/samples))
for i, count in enumerate(np.bincount(totals)):
    print("{:>2} {}".format(i, count))

The actual calculations are fairly simple, once you get your head round the 
tricks you need to do everything array-based. The part I find the hardest by 
far, is keeping track of the dimensions of the arrays involved, and what I need 
to do to match up axes, etc. For example, it took me a long time to work out 
what was going wrong with stripping out the "go to gaol" cases. The problem 
turned out to be that I needed the line "gaol = gaol[...,0]" to strip off a 
dimension - but it was really hard working out what was going on, particularly 
as numpy broadcasts the values you have, so you don't get errors, just weirdly 
wrong answers. (This isn't unique to numpy, I remember having the same issues 
when I used to work with J).

One thing it'd be nice to do would be to abstract out the array dimension for 
the number of samples, so that the "application" code could be written as if 
working on just one sample, and the driver code extended that to multiple 
samples. But I don't have the first idea how I'd do that without ending up 
looping in Python, which is what I need to avoid if I want performance to 
remain good.

Lots of scope for more things to learn here, then :-)

Thanks again to everyone that helped.
Paul
-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to