6
\$\begingroup\$

Background

In the game stick ranger, there is an item which grants the character the ability to get critical hits. Each time a projectile hits an enemy, it has some probability to be a critical hit (crit). For most weapons this is calculated independently each time, but for one class (the angel) things work differently. The angel throws multiple rings per volley, and each has a chance to crit. But, (and apparently this is not a bug) once a ring crits, that crit multiplier stays. And in fact if one is lucky enough for a second ring to crit in the same volley, the crits will multiply. So if the first crit is 10X the original damage, the second will be 100X. All crits persist until the next volley is reached. This is great, but I want to know what my damage per second is given a particular set up. I know the base damage and hit rate, but this crit multiplier business is tricky, and I could use a short program to help me out.

The challenge

You will be given three inputs: the crit probability, the crit multiplier, and the number of rings. We'll assume each ring hits some enemy exactly once. You must then output the average damage per ring (or per volley) gained by having the crit applied.

Standard I/O rules apply, the inputs and output may be in any logical format including fractions, decimals, and percentages. You may assume \$0<\text{odds}<1\$, \$1<\text{multiplier}\$, and \$0<\text{hits}\$. The output from your program must be within \$0.1\%\$ of the true value at least \$90\%\$ of the time.

This is , so the shortest answer in bytes wins.

Worked example

Let's work through the answer with a crit chance of 0.2, multiplier of 6, and 2 rings. For this example all I/O uses decimals, and I'll find the average damage per ring. There are four distinct outcomes

outcome             damage          probability         weighted

no crits            1 + 1 = 2       0.8 * 0.8 = 0.64    2  * 0.64 = 1.28
no crit then crit   1 + 6 = 7       0.8 * 0.2 = 0.16    7  * 0.16 = 1.12
crit then no crit   6 + 6 = 12      0.2 * 0.8 = 0.16    12 * 0.16 = 1.92
two crits           6 + 36 = 42     0.2 * 0.2 = 0.04    42 * 0.04 = 1.68
                                                                    ----
total                                                               6.00

Thus our answer is 6/2 = 3 average damage per ring. This is much higher than what a simple crit system would yield 1*0.8 + 6*0.2 = 2

Test cases

odds, multiplier, hits -> output

0.2, 6, 2 -> 3
0.3, 5, 3 -> 5.896
0.5, 2, 4 -> 3.046875
0.2, 7, 5 -> 18.529984
0.15, 10, 5 -> 24.6037391875
```
\$\endgroup\$
2
  • 1
    \$\begingroup\$ May we assume the crit multiplier is more than 1? My answer would break if it was 1. Also, may we assume it's an integer? \$\endgroup\$
    – xnor
    Jul 11, 2023 at 23:04
  • \$\begingroup\$ You may assume the multiplier is more than 1, but not that it is an integer. \$\endgroup\$ Jul 12, 2023 at 13:59

6 Answers 6

8
\$\begingroup\$

Charcoal, 16 bytes

NθI∕ΣX⊕×N⊖N…·¹θθ

Try it online! Link is to verbose version of code. Takes the number of rings as the first parameter, then the odds and multiplier. Explanation: Each ring has a p chance of dealing m times as much damage as the previous ring and a 1-p chance of dealing the same damage as the previous ring. The expected damage of the ring is therefore 1-p+pm times the expected damage of the previous ring and each ring's expected damage is therefore the nth power of that. It then remains to take the average of these values.

Nθ                  Number of rings as an integer
        N           Odds as a number
       ×            Multiplied by
          N         Multiplier as a number
         ⊖          Decremented
      ⊕             Incremented
     X              Vectorised raise to power
           …·       Inclusive range from
             ¹      Literal integer `1` to
              θ     Number of rings
    Σ               Take the sum
   ∕                Divide by
               θ    Number of rings
  I                 Cast to string
                    Implicitly print
\$\endgroup\$
7
\$\begingroup\$

Python 3, 40 bytes

lambda p,m,n:((c:=1-p+p*m)**n-1)/(n-n/c)

Try it online!

A formula I derived. With crit probability \$p\$, multiplier \$m\$, and \$n\$ hits, the average damage per hit is

$$ \frac{c}{n} \frac{c^n-1}{c-1}, \text{ where } c=1-p+pm$$

Thanks to dingledooper for -2 bytes.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Found a 40 doing some rearranging: lambda p,m,n:((c:=1-p+p*m)**n-1)/(n-n/c). \$\endgroup\$ Jul 11, 2023 at 23:21
4
\$\begingroup\$

05AB1E, 8 bytes

<*>ILmÅA

Inputs in the order critDamageMultiplier (\$m\$), critProbability (\$p\$), amountOfRings (\$n\$).

Port of @Neil's Charcoal answer, so make sure to upvote him as well!

Try it online or verify all test cases.

Explanation:

Uses the formula:

$$a(m,p,n)=\frac{\sum_{i=1}^n{(p(m-1)+1)^i}}{n}$$

<         # Decrease the first (implicit) input-integer (the multiplier) by 1
 *        # Multiply it to the second (implicit) input-decimal (the probability)
  >       # Increase it by 1
   IL     # Push a list in the range [1, third input-integer (the amount of rings)]
     m    # Take the earlier value to the power of each of these integers
      ÅA  # Pop and get the average of this list
          # (after which it is output implicitly as result)
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES7), 96 bytes

Naive implementation of the algorithm.

(p,m,n)=>eval("for(t=0,i=2**n;i--;t+=d*q)for(q=x=1,d=0,j=n;j--;d+=x)q*=i>>j&1?(x*=m,p):1-p;t/n")

Try it online!

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

Arturo, 34 bytes

$[p m n][c:+1-p p*m(-c^n 1)/n-n/c]

Try it!

Port of xnor's Python answer.

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

JavaScript (Node.js), 56 bytes

p=>m=>g=(h,w=1/h)=>h--&&(1-p)*(g(h,w)+w)+p*(g(h,w*=m)+w)

Try it online!

Recursive fork possibility

JavaScript (Node.js), 35 bytes

(p,m,n)=>((c=1-p+p*m)**n-1)/(n-n/c)

Try it online!

Port of formula

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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