Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
What is _wrong_ with Python? a.k.a. PoR probabilities
#8
Warning: thinking "out loud". Do not expect coherency.

So let's think this through. What would it take to rewrite this thing (additive) "recursively"? The idea is that when we add a die D to a pool N, we look at each pool roll of N and see how each roll of D affects it. It isn't necessary to cycle through N, just the results of N, i.e. the number of successes and which rolls weren't used for those successes. As with my greedy algorithm, I don't have a formal proof, only anecdotal evidence in that I tried to create a situation whereby a poolroll p of size n takes too much such that looking at the remainder of p with d causes a success to me missed. I was able to build one where a success was missed after several dice were added for a specific sequence. This loss of a single success out of 4^6 isn't large but it can propagate. So the question is, are there enough of these chains to skew the probabilities down enough to exceed the rounding error?

That out of the way, we start with a Pool, which contains 2 dice to start. A Pool informs us of all the possible rolls, the number of which is the product of the sizes of the dice; [4,4] gives us {[0,0],[0,2],[0,3],[0,4],[2,0],[2,2],[2,3],[2,4],[3,0],[3,2],[3,3],[3,4],[4,0],[4,2],[4,3],[4,4]}. (A set of lists? Lists for pools makes sense, lists for rolls makes sense, but is it necessary to keep record of the die rolls? Especially since the strategy is to track the remainder.) This is meaningless to us without a target number, the smallest of which, for our example, is 5: {[0,0]:0,[0,2]:0,[0,3]:0,[0,4]:0,[2,0]:0,[2,2]:0,[2,3]:1,[2,4]:1,[3,0]:0,[3,2]:1,[3,3]:1,[3,4]:1,[4,0]:0,[4,2]:1,[4,3]:1,[4,4]:1}. Successes: [8,8]. (Do we need Successes or can we calculate it on demand? The database classes I took over a decade ago thinks the latter.) Oh, yeah. Remainders. If I reuse my cycle() code that I've already written, and why wouldn't I, we'd have {[0,0]:[0,0,0,0,0],[0,2]:[0,0,1,0,0],[0,3]:[0,0,0,1,0],[0,4]:[0,0,0,0,1],[2,0]:[0,0,1,0,0],[2,2]:[0,0,2,0,0],[2,3]:[0,0,0,0,0],[2,4]:[0,0,0,0,0],[3,0]:[0,0,0,1,0],[3,2]:[0,0,0,0,0],[3,3]:[0,0,0,0,0],[3,4]:[0,0,0,0,0],[4,0]:[0,0,0,0,1],[4,2]:[0,0,0,0,0],[4,3]:[0,0,0,0,0],[4,4]:[0,0,0,0,0]} after condensing. (That's two dictionaries using die rolls as the key. May need to keep that set of rolls.)

Now add a die. Continuing the precedent of least typing, let's look at adding a d4: [4,4,4]
Rolls:
{[0,0,0],[0,2,0],[0,3,0],[0,4,0],[2,0,0],[2,2,0],[2,3,0],[2,4,0],[3,0,0],[3,2,0],[3,3,0],[3,4,0],[4,0,0],[4,2,0],[4,3,0],[4,4,0],[0,0,2],[0,2,2],[0,3,2],[0,4,2],[2,0,2],[2,2,2],[2,3,2],[2,4,2],[3,0,2],[3,2,2],[3,3,2],[3,4,2],[4,0,2],[4,2,2],[4,3,2],[4,4,2],[0,0,3],[0,2,3],[0,3,3],[0,4,3],[2,0,3],[2,2,3],[2,3,3],[2,4,3],[3,0,3],[3,2,3],[3,3,3],[3,4,3],[4,0,3],[4,2,3],[4,3,3],[4,4,3],[0,0,4],[0,2,4],[0,3,4],[0,4,4],[2,0,4],[2,2,4],[2,3,4],[2,4,4],[3,0,4],[3,2,4],[3,3,4],[3,4,4],[4,0,4],[4,2,4],[4,3,4],[4,4,4]}.
Success:
{[0,0,0]:0,[0,2,0]:0,[0,3,0]:0,[0,4,0]:0,[2,0,0]:0,[2,2,0]:0,[2,3,0]:1,[2,4,0]:1,[3,0,0]:0,[3,2,0]:1,[3,3,0]:1,[3,4,0]:1,[4,0,0]:0,[4,2,0]:1,[4,3,0]:1,[4,4,0]:1,[0,0,2]:0,[0,2,2]:0,[0,3,2]:1,[0,4,2]:1,[2,0,2]:0,[2,2,2]:1,[2,3,2]:1,[2,4,2]:1,[3,0,2]:1,[3,2,2]:1,[3,3,2]:1,[3,4,2]:1,[4,0,2]:1,[4,2,2]:1,[4,3,2]:1,[4,4,2]:1,[0,0,3]:0,[0,2,3]:1,[0,3,3]:1,[0,4,3]:1,[2,0,3]:1,[2,2,3]:1,[2,3,3]:1,[2,4,3]:1,[3,0,3]:1,[3,2,3]:1,[3,3,3]:1,[3,4,3]:1,[4,0,3]:1,[4,2,3]:1,[4,3,3]:1,[4,4,3]:1,[0,0,4]:0,[0,2,4]:1,[0,3,4]:1,[0,4,4]:1,[2,0,4]:1,[2,2,4]:1,[2,3,4]:1,[2,4,4]:1,[3,0,4]:1,[3,2,4]:1,[3,3,4]:1,[3,4,4]:1,[4,0,4]:1,[4,2,4]:1,[4,3,4]:1,[4,4,4]:1}.
(At this point I'm seriously questioning the wisdom of tracking successes per roll. Seems completely unnecessary and definitely a waste of memory if unneeded. Just need a running total.)
Remainders:
{[0,0,0]:[0,0,0,0,0],[0,2,0]:[0,0,1,0,0],[0,3,0]:[0,0,0,0,1],[0,4,0]:[0,0,0,0,1],[2,0,0]:[0,0,1,0,0],[2,2,0]:[0,0,2,0,0],[2,3,0]:[0,0,0,0,0],[2,4,0]:[0,0,0,0,0],[3,0,0]:[0,0,0,1,0],[3,2,0]:[0,0,0,0,0],[3,3,0]:[0,0,0,0,0],[3,4,0]:[0,0,0,0,0],[4,0,0]:[0,0,0,0,1],[4,2,0]:[0,0,0,0,0],[4,3,0]:[0,0,0,0,0],[4,4,0]:[0,0,0,0,0],[0,0,2]:[0,0,1,0,0],[0,2,2]:[0,0,2,0,0],[0,3,2]:[0,0,0,0,0],[0,4,2]:[0,0,0,0,0],[2,0,2]:[0,0,2,0,0],[2,2,2]:[0,0,0,0,0],[2,3,2]:[0,0,1,0,0],[2,4,2]:[0,0,1,0,0],[3,0,2]:[0,0,0,0,0],[3,2,2]:[0,0,1,0,0],[3,3,2]:[0,0,1,0,0],[3,4,2]:[0,0,1,0,0],[4,0,2]:[0,0,0,0,0],[4,2,2]:[0,0,1,0,0],[4,3,2]:[0,0,1,0,0],[4,4,2]:[0,0,1,0,0],[0,0,3]:[0,0,0,1,0],[0,2,3]:[0,0,0,0,0],[0,3,3]:[0,0,0,0,0],[0,4,3]:[0,0,0,0,0],[2,0,3]:[0,0,0,0,0],[2,2,3]:[0,0,1,0,0],[2,3,3]:[0,0,0,1,0],[2,4,3]:[0,0,0,1,0],[3,0,3]:[0,0,0,0,0],[3,2,3]:[0,0,0,1,0],[3,3,3]:[0,0,0,1,0],[3,4,3]:[0,0,0,1,0],[4,0,3]:[0,0,0,0,0],[4,2,3]:[0,0,0,1,0],[4,3,3]:[0,0,0,1,0],[4,4,3]:[0,0,0,1,0],[0,0,4]:[0,0,0,0,1],[0,2,4]:[0,0,0,0,0],[0,3,4]:[0,0,0,0,0],[0,4,4]:[0,0,0,0,0],[2,0,4]:[0,0,0,0,0],[2,2,4]:[0,0,1,0,0],[2,3,4]:[0,0,0,0,1],[2,4,4]:[0,0,0,0,1],[3,0,4]:[0,0,0,0,0],[3,2,4]:[0,0,0,0,1],[3,3,4]:[0,0,0,0,1],[3,4,4]:[0,0,0,0,1],[4,0,4]:[0,0,0,0,0],[4,2,4]:[0,0,0,0,1],[4,3,4]:[0,0,0,0,1],[4,4,4]:[0,0,0,0,1]}.
It becomes clear that my above questioning of tracking successes by roll was a mistake. How else to determine if a roll has two successes for larger pools? So we need a pool roll, the number of successes in that roll, and the remainder in a single storage unit. Makes me yearn for Pascal's struct. Or is that C? I'm thinking C. I believe Pascal had records.
Code:
class Poolroll:
  roll = []
  rolls = []  # I think I need to do this for the following functions
  def rollsum(self):
    sum = 0
    for i in range (2,13):
      sum += self.rolls[i] * i
    return sum

  def newlarge(self):
    x = 12
    while x > 0:
      if self.rolls[x]:
        return x
      else:
        x += -1
    return 0
  
  def newsmall(self):
    x=2        #[0] and [1] will always be 0
    while x < 13:
      if self.rolls[x]:
        return x
      else:
        x += 1
    return 0

  def __init__( self, oldpoolroll, newdieroll ):
    self.TN = oldpoolroll.TN
    self.roll = oldpoolroll.roll[:]  #if I understand this stuff correctly, this is the fastest copy method for complex data types
                             #and with making a copy any changes made to roll are made to oldroll also.
                             #Something to do with Python being a strict pointer-based language.
    self.roll.append(newdieroll)
    self.rolls = oldpoolroll.rolls[:]  #roll is the list of the numbers rolled, rolls is the number of each value rolled or remainder, as the case may be.
                             #roll would be [4,4,4] and rolls [0,0,0,0,3,0,0,0,0,0,0,0]
    self.rolls[newdieroll] += 1
    self.successes = oldpoolroll.successes
    #It would be nice to go ahead and evaluate the new successes here, but the function needs to be defined before it can be called
    #and I can't define it first because the variables haven't been defined yet. But thinking about it, because we're only looking at a possible
    #increase of only a single success, the process wouldn't be recursive.
  
    if self.rollsum() < self.TN:
      pass    #no new successes
    else:
      large = newlarge()
      if self.rolls[ self.TN - large ]:
        self.rolls[ self.TN - large ] += -1
        self.rolls[self.large] += -1
        self.successes += 1
      else:
        total = large
        self.rolls[large] += -1
        while total < self.TN:
          small = self.newsmall()
          total += small
          self.rolls[small] += -1
        self.successes +=1

Anything missing? Roll result: check,poolroll.roll; successes: check, poolroll.successes; remainder: check, poolroll.rolls. And it's set to take a smaller sized poolroll instance. But how do we get the initial seed? Crap, I'm going to have to look up overloading, aren't I. Mmmm... Maybe not. Python doesn't make one declare the type of data of function inputs, so we might be able to make a seed class that satisfies our needs.

Code:
class Seed:
  rolls = [0]*13
  successes = 0
  def __init__(self, roll, TN)
    self.roll = [roll]
    if roll:
      self.rolls[roll] = 1  #0 is never counted so why muck things up by including 0 rolls
    self.TN = TN
This has all the requirements for initializing a Poolroll instance and to get our seed:
Code:
for i in (4, 6, 8, 10, 12):
  for j in (0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) if j <= i:
    seed[i,j] = Seed(j,TN) #Yes, I know Python doesn't have multidimensional arrays. Used only for demonstrational purposes.

Meh. Tired. Time for sleep.
Getting me free admission into gaming conventions for a decade
Reply


Messages In This Thread
RE: What is _wrong_ with Python? - by Kersus - 10-31-2016, 01:00 PM
RE: What is _wrong_ with Python? - by Kersus - 11-03-2016, 03:56 PM
RE: What is _wrong_ with Python? - by Oedipussy Rex - 11-08-2016, 10:13 PM

Forum Jump:


Users browsing this thread: 1 Guest(s)