4
\$\begingroup\$

In anydice, I'd like to create a die, so that a roll would be the sum of 4 random values from the following sequence:

{1, 1, 1, 1, 1, 3, 3, 3, 5, 5, 7}

So that each number in the sequence is used at maximum once. (That is 3+5+5+7 result is possible where as 5+5+7+7 is not because there are not enough 7s for that) Is this possible?

This is a cards mechanics, not dice mechanics. I'm trying to get a roll by getting a player to draw several card out of pool of cards that I put together. While Anydice is mostly for dice mechanics not for cards drawing mechanics Anydice works well for probability analysis in general, and for sharing the visual spread of probabilities with less technically apt people.

\$\endgroup\$
  • \$\begingroup\$ @Mourdos I think there should be 11!/(7! * 4!) = 330 different results... The sequence that you listed is smaller. \$\endgroup\$ – Andrew Savinykh Jun 7 '15 at 9:56
  • 1
    \$\begingroup\$ AnyDice is for simulating dice mechanics; the reason you're having trouble is this doesn't appear to be a dice mechanic. There are no dice here, just random choosing from a set, like picking cards from a fan of them. Why are you trying to model it using AnyDice? \$\endgroup\$ – doppelgreener Jun 7 '15 at 12:18
  • 1
    \$\begingroup\$ @doppelgreener, but surely we can "hack" it, can't we? =) With help of nice folks here I was able to figure it out, and even documented it for posterity. \$\endgroup\$ – Andrew Savinykh Jun 7 '15 at 12:53
  • 1
    \$\begingroup\$ @zespri - Are you modeling dice rolls for the Advanced Fighting Fantasy RPG? AFF uses non-uniform distributions for weapon damage and armor resistance die rolls. \$\endgroup\$ – RobertF Jun 7 '15 at 14:37
  • 2
    \$\begingroup\$ @doppelgreener because anydice does work well for probability analysis in general, in most cases. Furthermore, the modular nature of the code means you get a lot of code reuse out of things. Additionally, the graphic output, sorting options, and shareability with the technologically incompetent make the program extremely useful for explaining and understanding probability things even outside the realm of literal dice rolls. Basically: yes, this is a little awkward but its worth it to keep the other features of the software, and to be able to be consistent with existing libraries. \$\endgroup\$ – Please stop being evil Jun 7 '15 at 22:36
8
\$\begingroup\$

I came up with two solutions, which why inherently different, give the same result. This instills confidence in me that they are correct.

Solution 1

This solution is the one I started working on before I read Sandwich's answer. You can try it here. The idea is to use Buckles algorithm for mapping an lexicographical index (from a die roll) to a selection from my sequence. Let's look at the code:

set "maximum function depth" to 100
function: factorial A:n {
  RESULT: 1
  loop N over {1..A} { RESULT: RESULT*N }
  result: RESULT
}

function: N:n choose K:n {
  result: [factorial N]/[factorial K]/[factorial N-K]
}

function: buckles inner CURRENT:n N:n K:n LIMIT:n INDEX:n {
  NEWLIMIT: [N-CURRENT choose K] + LIMIT
  if NEWLIMIT >= INDEX { result: {CURRENT,LIMIT}}
  else { result: [buckles inner CURRENT+1 N K NEWLIMIT INDEX]}
}

function: N:n buckles K:n index INDEX:n {
  RESULT: {}
  CURRENT: 0
  LIMIT: 0
  loop I over {1..K-1}{
    TUPLE: [buckles inner CURRENT+1 N K-I LIMIT INDEX]
    CURRENT: 1@TUPLE
    LIMIT: 2@TUPLE
    RESULT:{RESULT,CURRENT}
  }
  result: {RESULT,CURRENT+INDEX-LIMIT} 
}

function: select SELECTOR:s from SEQUENCE:s {
  RESULT: {}
  loop I over SELECTOR { RESULT: {RESULT,I@SEQUENCE} }  
  result: RESULT  
}

function: draw K:n items from SEQUENCE:s at lexicographical index INDEX:n {
  result: [select [#SEQUENCE buckles K index INDEX] from SEQUENCE]
}

function: draw from SEQUENCE:s K:n times {
  INDEX: d[#SEQUENCE choose K]
  result: [draw K items from SEQUENCE at lexicographical index INDEX]
}

SEQUENCE: {1, 1, 1, 1, 1, 3, 3, 3, 5, 5, 7}
output [draw from SEQUENCE 4 times] named "mydie"

The factorial and choose functions are self explanatory. The first calculates 1*2*3....*n, and the second one gives the total number of different ways to get k item out of n, in my case 4 out of 11. buckles and buckles inner implement the Buckles algorithm itself. If we pass N=11, P=4 and X=1 to the buckles function we will receive {1,2,3,4}. If we pass N=11, P=4 and X=2, we will receive {1,2,3,5} and so on an so forth until we go all the way to N=11, P=4 and X=330 to get {8,9,10,11}. Note that 330 here is [11 choose 4].

select is a helper function, that takes a selector, a sequence and returns the resulting sequence. For example for selector {1,6,7,11} and sequence {1, 1, 1, 1, 1, 3, 3, 3, 5, 5, 7} the result will be {1,3,3,7}.

draw items function selects K items from the given sequence at a given lexicographical index. For example with K = 4 at index 1 and sequence {1, 3, 5 , 9, 11} the result will be {1, 3 , 5 , 9}.

The second draw function allow you specifying a sequence and the number of items to draw from it. That will be the die.

Solution 2

This one is completely inspired by sandwich's answer. You try it here. And here is the code:

function: remove N:n from SEQUENCE:s {
  RESULT:{}
  REMOVED: 0
  loop C over SEQUENCE {
    if (REMOVED = 0) & (C=N) { REMOVED: 1 } else { RESULT: {RESULT,C} }
  }
  result: RESULT
}

function: draw from SEQUENCE:s with X:n and RESULT:s C:n more times{
  if C = 0 {
   result: RESULT
  } else {
    SEQUENCE: [remove X from SEQUENCE]
    result: [draw from SEQUENCE with dSEQUENCE and {RESULT,X} C-1 more times]
  }
}

function: draw from SEQUENCE:s N:n times {
  result: [draw from SEQUENCE with dSEQUENCE and {} N more times]
}

SEQUENCE:{1, 1, 1, 1, 1, 3, 3, 3, 5, 5, 7}
output [draw from SEQUENCE 4 times] named "mydie"

I was not sure how to remove a value from an existing sequence, so I had to write my own function for that. It's remove. The two draw functions implement sandwich's idea of rolling a die and then removing the result from the die sequence. There are two of them, because one of them is recursive and the other bootstraps the first one.

I can tell you that the first solution took me about 3 hours, and the second one only a few minutes. Well done sandwich!

|improve this answer|||||
\$\endgroup\$
  • 1
    \$\begingroup\$ You may want to replace the rest of these one-letter variables in both code samples while you still remember what they mean (and the abbreviations: what's ACC?). Code is hard to read, and I'm having a lot of difficulty here - words would help. And I don't know about you, but it tends to take me only about a month before I've totally forgotten how a script I wrote works, at which time if I hadn't spelled things out well originally, I begin to wish I had. \$\endgroup\$ – doppelgreener Jun 8 '15 at 23:34
  • \$\begingroup\$ @doppelgreener ACC is accumulator, it's very clear from it's use, it's initialized with a empty sequence in the beginning, receives elements in the loop and is returned in the end. N and K are standard combinatorial notation for the size of the set and the size of selection. The rest of them are just short lived temporary variables. \$\endgroup\$ – Andrew Savinykh Jun 9 '15 at 3:31
  • \$\begingroup\$ Note, I'm not opposing your suggestions, these are good suggestions, I just don't know what else can be done in this particular case. \$\endgroup\$ – Andrew Savinykh Jun 9 '15 at 3:32
  • \$\begingroup\$ @zespri If it was that very clear, I would hardly be asking for clarification on it. Lots of words start with 'acc', and that wasn't one that came to mind. It is not a convention I've been exposed to. What you can do further is consider expanding each of those temporary variables: What are S, N, A, X, dS, and R? I trust you chose those letters because they stand for something or mean something: what do they stand for and mean? Replace those letters with the full word, and you'll cut down the time it'll take to understand and work with this script by at least one order of magnitude. \$\endgroup\$ – doppelgreener Jun 9 '15 at 3:45
  • 1
    \$\begingroup\$ An alternate way to think of them is: they are totally temporary and short lived... as far as script execution is concerned, at least. As far as readers go, however, they are absolutely long-lived and will be here to stay. Many readers will have to go through decoding what they mean, and some will get them wrong. Meaningful names will help them understand and get it right, and understand faster. (Because you've just outright told them.) \$\endgroup\$ – doppelgreener Jun 9 '15 at 3:49
5
\$\begingroup\$

To create the die you're looking for, you can use the following syntax:

output d{1, 1, 1, 1, 1, 3, 3, 3, 5, 5, 7} named "1d11"

That would roll a single die named d11 that has an equal probability of rolling each of the individual results in the sequence.

The way you'd likely have to remove each individual step from the dice is you'd have to use the die result to define a variable, and if that variable equals seven, to roll another die with the seven in the sequence removed. Each step of the "if/else" statement would define a new variable (w/x/y/z).

If that seems like too much work you can just roll the one die to get the results you're trying to achieve, however, rerolling when you roll a 7 once, a 5 twice, or a 3 thrice.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ If you go to the "View" section of Anydice and click where it says "Roller" and type a four into that box you can get a sequence that includes more than 1 seven, 2 fives, or 3 threes, you can click the reroll button until you get a result that meets your criteria as well. \$\endgroup\$ – Sandwich Jun 7 '15 at 9:49
  • \$\begingroup\$ Well, I wanted to see the distribution for that die, so re-rolling here unfortunately won't help. But I'm looking into the way you described in the answer \$\endgroup\$ – Andrew Savinykh Jun 7 '15 at 9:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.