Age of Steam: production probabilities

The following python script calculates the probabilities of a given cube on a row being produced by the end of a given round in a game of Age of Steam:

#! /usr/bin/env python
# Author: J C Lawrence <claw+blog@kanga.nu>
# Date: 25 June 2009
# Purpose: Calculate AoS cube production probabilities by row and round
def fact (x):
    if x < 1:
        return 1
    return reduce (lambda x, y: x * y, xrange (1, x + 1))
def xCy (x, y):
    return fact (x) / (fact (x - y) * fact (y))
def point_p (d, n):
    c = float (xCy (d, n))
    p = (((1.0 / 6) ** n) * ((5.0 / 6) ** (d - n)))
    return c * p
def sum_p (d, n):
    rc = 0.0
    for i in range (n, d + 1):
      rc = rc + point_p (d, i)
    return rc
def main ():
    for p in range (3, 7):
        print "Players: %d" % p     
        for r in range (1, [10, 8, 7, 6][p - 3]):
            print "\tRound %d:  Row 1 - %f\tRow 2 - %f\tRow 3 - %f" \
                        % (r, sum_p (r * p, 1), sum_p (r * p, 2), sum_p (r * p, 3))
if __name__ == "__main__":
    main ()

The results:

    Players: 3
        Round 1:  Row 1 - 0.421296  Row 2 - 0.074074    Row 3 - 0.004630
        Round 2:  Row 1 - 0.665102  Row 2 - 0.263224    Row 3 - 0.062286
        Round 3:  Row 1 - 0.806193  Row 2 - 0.457341    Row 3 - 0.178260
        Round 4:  Row 1 - 0.887843  Row 2 - 0.618667    Row 3 - 0.322574
        Round 5:  Row 1 - 0.935095  Row 2 - 0.740378    Row 3 - 0.467775
        Round 6:  Row 1 - 0.962439  Row 2 - 0.827219    Row 3 - 0.597346
        Round 7:  Row 1 - 0.978263  Row 2 - 0.886969    Row 3 - 0.704381
        Round 8:  Row 1 - 0.987421  Row 2 - 0.927041    Row 3 - 0.788168
        Round 9:  Row 1 - 0.992720  Row 2 - 0.953411    Row 3 - 0.851205
    Players: 4
        Round 1:  Row 1 - 0.517747  Row 2 - 0.131944    Row 3 - 0.016204
        Round 2:  Row 1 - 0.767432  Row 2 - 0.395323    Row 3 - 0.134847
        Round 3:  Row 1 - 0.887843  Row 2 - 0.618667    Row 3 - 0.322574
        Round 4:  Row 1 - 0.945912  Row 2 - 0.772831    Row 3 - 0.513209
        Round 5:  Row 1 - 0.973916  Row 2 - 0.869580    Row 3 - 0.671341
        Round 6:  Row 1 - 0.987421  Row 2 - 0.927041    Row 3 - 0.788168
        Round 7:  Row 1 - 0.993934  Row 2 - 0.959962    Row 3 - 0.868240
    Players: 5
        Round 1:  Row 1 - 0.598122  Row 2 - 0.196245    Row 3 - 0.035494
        Round 2:  Row 1 - 0.838494  Row 2 - 0.515483    Row 3 - 0.224773
        Round 3:  Row 1 - 0.935095  Row 2 - 0.740378    Row 3 - 0.467775
        Round 4:  Row 1 - 0.973916  Row 2 - 0.869580    Row 3 - 0.671341
        Round 5:  Row 1 - 0.989517  Row 2 - 0.937104    Row 3 - 0.811313
        Round 6:  Row 1 - 0.995787  Row 2 - 0.970511    Row 3 - 0.897210
    Players: 6
        Round 1:  Row 1 - 0.665102  Row 2 - 0.263224    Row 3 - 0.062286
        Round 2:  Row 1 - 0.887843  Row 2 - 0.618667    Row 3 - 0.322574
        Round 3:  Row 1 - 0.962439  Row 2 - 0.827219    Row 3 - 0.597346
        Round 4:  Row 1 - 0.987421  Row 2 - 0.927041    Row 3 - 0.788168
        Round 5:  Row 1 - 0.995787  Row 2 - 0.970511    Row 3 - 0.897210
 

No accommodations have been made for maps with different numbers of rounds per player count, or the insta-production rules used on most of my maps. Enjoy.