3
\$\begingroup\$

I have a sequence storing a variety of stats for a unit which will be put through a probabilistic gauntlet. Because the gauntlet is multi-step and I want to know when the unit's hp reaches 0, I need to be able to modify an element of the sequence mid-run. Currently I'm trying:

/bunch of stuff/
function: combat between ATTACKER:s and DEFENDER:s
{
    loop N over {1..3}
    {
        if {1@ATTACKER>0&1@DEFENDER>0}
        {
             if {3@ATTACKER<3@DEFENDER}
             {
                  1@DEFENDER:1@DEFENDER-[1@ATTACKER 2@ATTACKER battleroll] 
/lots more stuff, ending the conditionals and loop and function/

but I get a "'}' expected" error.

Why do I get this error? Is element access not the right way to refer to an element for assignment? Do I have to refer to the whole sequence (e.g.
DEFENDER:{1@DEFENDER-[1@ATTACKER 2@ATTACKER battleroll],2@DEFENDER,3@DEFENDER}) every time I want to update the value? How ought I go about altering the contents of a sequence probabilistically?

\$\endgroup\$
3
  • \$\begingroup\$ I was about to say on an answer but some one already has. You forgot to cap the end of the code like so "}}}}" since you have 4 "{" without their cap "}" \$\endgroup\$
    – Pallas
    Commented Aug 19, 2015 at 13:49
  • \$\begingroup\$ @Pallas hence my ending comment in the code. I don't actually forget to cap it, as you can see if you follow the link to the program itself. The question really is about sequence alteration, ending brackets weren't in fact missing (though I'll admit the indentation was bad, as answers pointed out). \$\endgroup\$ Commented Aug 19, 2015 at 21:07
  • \$\begingroup\$ Yeah you're right looked over it a couple of times and no matter how many "}" you put in it will just say it still expects another "}" \$\endgroup\$
    – Pallas
    Commented Aug 19, 2015 at 21:44

2 Answers 2

3
+100
\$\begingroup\$

As Dale M notes, you can't assign directly to sequence elements in AnyDice. What you can do, in general, is rebuild the whole sequence like this:

function: set element I:n in SEQ:s to N:n {
  NEW: {}
  loop J over {1 .. #SEQ} {
    if I = J { NEW: {NEW, N} }
    else { NEW: {NEW, J@SEQ} }
  }
  result: NEW
}

In your case, though, that would be overkill. It's much easier to just unpack your mutable sequence elements into normal numeric variables (and, if it were necessary, later re-pack them into sequences):

function: combat between ATTACKER:s and DEFENDER:s {
  S_ATT: 1@ATTACKER
  E_ATT: 2@ATTACKER
  I_ATT: 3@ATTACKER
  S_DEF: 1@DEFENDER
  E_DEF: 2@DEFENDER
  I_DEF: 3@DEFENDER
  loop N over {1..3} {
    if S_ATT > 0 & S_DEF > 0 {
      if I_ATT < I_DEF {
        S_DEF: S_DEF - [S_ATT E_ATT battleroll]
        if S_DEF > 0 { S_ATT: S_ATT - [S_DEF E_DEF battleroll] }
      } else {
        S_ATT: S_ATT - [S_DEF E_DEF battleroll]
        if S_ATT > 0 { S_DEF: S_DEF - [S_ATT E_ATT battleroll] }
      }
    }
  }
  result: (S_ATT > 0) - (S_DEF > 0)
}

(Actually, in this case, it would be enough to only unpack the S_ATT and S_DEF variables, since those are the only ones that will change. But, IMO, it looks more readable this way.)

Of course, you could also just write your function to take multiple numeric arguments instead of fixed-length sequences, and let the caller handle the unpacking if they need to.


Ps. A few other remarks regarding your code:

First of all, try to format and indent your code cleanly. For "brace languages" like the AnyDice syntax, consistently indenting the content of each brace-delimited block (except possibly for one-liners) makes the code much easier to read later.

Also, your battle roll function may be simplified down to just:

function: STRENGTH:n EFFECTIVENESS:n battleroll {
  VALUE: 0
  loop N over {1 .. STRENGTH} {
    VALUE: VALUE + (1d6 <= EFFECTIVENESS)
  }
  result: VALUE
}

In fact, it could be simplified further to just:

function: STRENGTH:n EFFECTIVENESS:n battleroll {
  result: [count {1 .. EFFECTIVENESS} in STRENGTH d 6]
}

but this becomes inefficient for high STRENGTH values, because AnyDice isn't smart enough to realize that it doesn't need to enumerate every possible outcome of rolling the dice. Probably the most concise and efficient way to implement it, however, is to use custom dice:

function: STRENGTH:n EFFECTIVENESS:n battleroll {
  result: STRENGTH d {1:EFFECTIVENESS, 0:(6-EFFECTIVENESS)}
}

Finally, if you actually try to run the fixed code I suggested above, you'll find that it compiles fine, but then fails at runtime with a message saying:

calculation error
Boolean values can only be numbers, but you provided "d{?}".
Depending on what you want, you might need to create a function.

This is because, after the first battle roll, your S_ATT / S_DEF variables are no longer plain numbers, but rather random values that depend on the die rolls inside the battleroll function. AnyDice will not allow such random values to be used in if statements.

There is, however, a workaround, and the is to use a recursive function call, e.g. like this:

function: combat between S_ATT:n E_ATT:n and S_DEF:n E_DEF:n for LIMIT:n steps {
  if S_ATT <= 0 { result: -1 }
  if S_DEF <= 0 { result: +1 }
  if LIMIT <= 0 { result: 0 }
  S_NEW: S_DEF - ( S_ATT d {1:E_ATT, 0:(6-E_ATT)} )
  \ reverse the roles for the next half-turn, negate result: \
  result: -[combat between S_NEW E_DEF and S_ATT E_ATT for LIMIT-1 steps]
}

This works, because if S_NEW is a random value, AnyDice will automatically call the function for each possible value, and average the results according to their probability. (Warning: Resist the temptation to reuse the function argument S_DEF instead of S_NEW as the variable storing new strength value. It seems to work, but apparently can trigger some weird / buggy behavior in AnyDice.)

Note that, if you use recursion like this, you will probably want to increase AnyDice's maximum recursion depth, like this:

set "maximum function depth" to 99

As long as you're controlling your recursion depth yourself (like I did with the LIMIT variable above), just setting the built-in limit to some high enough value is enough.

\$\endgroup\$
2
\$\begingroup\$

Short answer: you can't change the value of an element of a sequence; suggest you rebuild the sequence.

\$\endgroup\$
1
  • \$\begingroup\$ Is there a long answer, or is that just the answer? \$\endgroup\$ Commented Aug 15, 2015 at 7:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .