15
\$\begingroup\$

Roll for Initiative!

Introduction

In tabletop games like Dungeons and Dragons, when you begin a battle, all involved parties roll for initiative. In DnD 5e, this is 1d20 + DEX + Other bonuses, where DEX is the bonus given by your Dexterity stat. The characters that roll higher numbers go first. We'll use a similar, deterministic system in this challenge.

The Challenge

Write a program or function that, when given a list of characters, will output a list of characters in order of initiative.

A character is defined as this:

character = {
    name: "name" // a string
    statblock: [SPD, DEX, WHT] // a list of numbers
                               // DEX = dexterity, SPD = speed, WHT = weight
}

The formula for initiative is the following: $$\text{Initiative} = \left\lfloor{ \frac{\text{SPD}^2}{\sqrt{\lvert\text{DEX}\rvert}} }\right\rfloor - \text{WHT}$$

Input

A list of characters, unsorted. This can be a JSON object, a list of lists, a list of dictionaries, a series of strings etc.

It is guaranteed that all names will be unique.

Output

A list of characters, or character names, sorted by initiative order from highest to lowest, based on the above formula.

Rules

Sample IO

Input --> Output
[[Name, SPD, DEX, WHT], ...]
    --> [[Name, SPD, DEX, WHT], ...] (or [Name, Name, ...])
---------
[[Alice,1,2,3],[Bob,10,5,0],[Charlie,3,2,1]]
    --> [Bob, Charlie, Alice]
// Alice = -3, Bob = 44, Charlie = 5

[[Z,1,1,1],[B,1,1,1],[XY,5,1,1]]
    --> [XY, Z, B]
// Retain the order of characters from the input if they have the same initiative.
// Z = 0, B = 0, XY = 24

[[Neg,-3,-3,-1],[SomeNeg,5,-2,-4],[NoNeg,4,6,8]]
    --> [SomeNeg, Neg, NoNeg]
// Negative values are valid.
// Neg = 6, SomeNeg = 21, NoNeg = -2

[[Flo,1.5,2.5,3.5],[MoreFlo,2,2.5,3.5]]
    --> [[MoreFlo,2,2.5,3.5], [Flo,1.5,2.5,3.5]]
// Floats are also valid.
// Flo = -2.5, MoreFlo = -1.5

[[Lonely,1,2,3]]
    --> [[Lonely,1,2,3]]
// Input with 1 item.

[]
    --> []
// Empty input leads to empty output.

[[Foo,2,1,4], [Baz,5,5,4], [Bar,5,1,4]]
    --> [Bar, Baz, Foo]
// Foo = 0, Bar = 21, Baz = 7

[['Adam', 2, 4, 1], ['Eve', 2, 3, 1]]
--> [Adam, Eve]
// Adam = 2, Eve = 2
// If you do not floor, you'll end up with [Eve, Adam] (Adam = 2, Eve ~= 2.3)

Sandbox link

\$\endgroup\$
  • \$\begingroup\$ Curious - where did this initiative formula come from? \$\endgroup\$ – Quintec Dec 8 '19 at 16:52
  • 1
    \$\begingroup\$ Something I came up with on the fly while writing this, it's not really based on anything. \$\endgroup\$ – bigyihsuan Dec 8 '19 at 17:12
  • \$\begingroup\$ So it is. Thanks for catching it! \$\endgroup\$ – bigyihsuan Dec 8 '19 at 19:48
  • \$\begingroup\$ Can we sort the input in place (i.e whatever is passed into the function will be sorted when the function finishes)? \$\endgroup\$ – Noodle9 Dec 8 '19 at 21:54
  • \$\begingroup\$ So small dexterity is good here? \$\endgroup\$ – Paŭlo Ebermann Dec 9 '19 at 0:08

16 Answers 16

7
\$\begingroup\$

JavaScript (ES7),  73  60 bytes

-13 bytes by just sorting the input without isolating the names, as suggested by @asgallant

Expects a list of [SPD, DEX, WHT, Name].

a=>a.sort((a,b)=>(g=([s,d,w])=>~(s*s/(d*d)**.25)+w)(a)-g(b))

Try it online!

How?

To save a few bytes:

  • we compute \$-\text{Initiative}-1\$ instead and sort the list the other way around
  • we compute \$\sqrt{|\text{DEX}|}\$ as \$(\text{DEX}^2)^{\frac{1}{4}}\$
\$\endgroup\$
  • 1
    \$\begingroup\$ Outputting a list of characters (not just their names) is valid according to the challenge, so you can save a few bytes by dropping the .map call. \$\endgroup\$ – asgallant Dec 9 '19 at 18:20
4
\$\begingroup\$

05AB1E, 10 8 bytes

Σ`nsÄt÷-

Input in the format and order ["name", WHT, DEX, SPD], and output are the same inner lists sorted from highest to lowest initiative.

-2 bytes thanks to @Grimmy.

Try it online or verify all test cases.

Explanation:

The sort builtin Σ sorts from lowest to highest by default, so I'm using the following modified formula to not only save bytes, but sort from highest to lowest at the same time with this builtin (thanks @Grimmy):
$$Initiative = WHT - \left\lfloor\frac{SPD^2}{\sqrt{\lvert DEX\rvert}}\right\rfloor$$

Σ         # Sort the (implicit) input-list by:
 `        #  Pop and push all values of the inner list separated to the stack
  n       #  Square the SPD at the top of the stack
   s      #  Swap so the DEX is now at the top of the stack
    Ä     #  Take its absolute value
     t    #  Then its square-root
      ÷   #  And integer-divide the squared SPD by this
       -  #  Subtract this from the WHT (to sort in descending instead of ascending order)
          #  (after which the result is output implicitly)
\$\endgroup\$
  • \$\begingroup\$ s-( can be just -. \$\endgroup\$ – Grimmy Dec 9 '19 at 13:10
3
\$\begingroup\$

Japt, 20 18 bytes

ñÈÌ-(Xg1 ²/Xg2a ¬f

Try it

Simply calculates the formula for each character and sorts. Negates the formula to sort in the correct order.

Takes input as a list of lists [Name, SPD, DEX, WHT]. (I could save a byte taking input as [SPD, Name, DEX, WHT] but that feels cheaty.)

\$\endgroup\$
3
\$\begingroup\$

K (oK), 38 bytes

{(!x)@>{(-z)+(x*x)%%:(y;-1*y)0>y}.'.x}

Try it online!

takes dict as input, where keys are character names and values are stats. can definitely be golfed more

\$\endgroup\$
3
\$\begingroup\$

Python 3, 63 59 bytes

lambda c:sorted(c,key=lambda x:x[3]-x[1]**2//abs(x[2]**.5))

Try it online!

Pretty straigtforward. Sorts by - initiative to get the right output order.

Python 2, 60 56 bytes

lambda c:sorted(c,key=lambda(a,b,c,d):d-b*b//abs(c)**.5)

Try it online!

thanks to @Chas Brown

\$\endgroup\$
  • 1
    \$\begingroup\$ In python2, you can shave off 2 bytes via unpacking. (This is one of the few things I don't like in Python 3). \$\endgroup\$ – Chas Brown Dec 8 '19 at 21:40
  • \$\begingroup\$ I think you are supposed to return a list of just the names,not the entire records. \$\endgroup\$ – Galen Ivanov Dec 9 '19 at 10:14
  • \$\begingroup\$ @GalenIvanov The Output section of the challenge specifies you can return the whole Character, or the characters name. This answer has chosen the first option. \$\endgroup\$ – Potato44 Dec 9 '19 at 10:41
  • \$\begingroup\$ @Potato44 Yes, the Sample IO shows different possibilities. \$\endgroup\$ – Galen Ivanov Dec 9 '19 at 11:13
  • \$\begingroup\$ @ChasBrown Thank you, I tried this because I thought I had seen it somewhere, but it resulted in an error and I didn't think that it could be 2-only. Got it to 60 with b**2 -> b*b. \$\endgroup\$ – Black Owl Kai Dec 9 '19 at 15:02
2
\$\begingroup\$

Ruby, 44 bytes

->d{d.sort_by{|_,x,y,z|-x*x/y.abs()**1/2+z}}

Try it online!

-3 bytes thanks to @79037662

\$\endgroup\$
  • \$\begingroup\$ You can get rid of the reverse by sorting "backwards": tio.run/##VczNC4IwHMbx@/6K4UnXb@qmRheD6t6l4xix@VIDS/… \$\endgroup\$ – 79037662 Dec 8 '19 at 20:17
  • \$\begingroup\$ @79037662 thanks! Good point! I also got rid of some extra bytes by replacing indexing with variables! \$\endgroup\$ – game0ver Dec 8 '19 at 20:37
  • \$\begingroup\$ See the last test case, which fails if you ignore DEX. \$\endgroup\$ – bigyihsuan Dec 9 '19 at 13:59
  • \$\begingroup\$ No it doesn't, it gives Bar, Baz, Foo which is the test case... \$\endgroup\$ – game0ver Dec 9 '19 at 14:10
1
\$\begingroup\$

Add++, 24 bytes

D,g,@#~,p2^$|.5^/i_
L,§g

Try it online!

Inputs and outputs a list of lists representing the characters, in the same order given in the challenge ([Name SPD DEX WHT])

How it works

On the second line, we define an anonymous function that takes in an argument and sorts it on the result of g, defined on the first list.

g starts by reversing its argument and splatting the elements to its stack: #~. It then removes the name and squares the first stat, Speed, (p2^) before square rooting the absolute value of the second stat, Dexterity, ($|.5^). Finally, we integer divide the two (/i) and subtract from Weight (_). This is the opposite to the formula (where Weight is subtracted from the division), as it will cause the sort function in the second line to sort in the order we want. As Add++ uses a stable sort, order is retained if two characters have the same initiative.

\$\endgroup\$
1
\$\begingroup\$

APL+WIN, 41 bytes

Prompts for a n x 4 matrix where n is the number of rows 1 per character and the columns are Name, SPD, DEX, WHT. The output is a 1 column matrix of character names

m←⎕⋄⊃m[⍒⌊((m[;2]*2)÷(|m[;3])*.5)-m[;4];1]

Try it online!Courtesy of Dyalog Classic

\$\endgroup\$
1
\$\begingroup\$

Red, 108 107 bytes

-1 byte thanks to raznagul

func[b][extract next sort/skip collect[foreach e
b[keep 0 -(e/2 ** 2 /(e/3 ** 2 ** .25)- e/4)keep e/1]]2 2]

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ I don't know red, but shouldn't you be able to replace e/2 * e/2 with e/2 ** 2? \$\endgroup\$ – raznagul Dec 9 '19 at 13:16
  • \$\begingroup\$ @raznagul Yes,of course. You can see that I did it for e/3 but apparently I forgot to apply it to e/2. Thanks! \$\endgroup\$ – Galen Ivanov Dec 9 '19 at 13:21
1
\$\begingroup\$

Google Sheets, 49 37 bytes

-12 bytes thanks to Black Owl Kai

=Sort(A:A,D:D-B:B^2/Sqrt(Abs(C:C)),1)

Input is in the range A1:D with the four columns being player, speed, dexterity, and weight.

Explanation:
D:D-B:B^2/Sqrt(Abs(C:C)) do the math bit. The values are actually Initiaive * -1 so we can sort them ascending and get the order we want. Otherwise, all the blank rows would be sorted before the player names.
=Sort(A:A,~,1) sorts the player list based on the math bit output.

Test cases:
Test Cases

\$\endgroup\$
  • 1
    \$\begingroup\$ =Sort(A:A;D:D-B:B^2/Sqrt(Abs(C:C));1) gets rid of the if \$\endgroup\$ – Black Owl Kai Dec 10 '19 at 8:40
0
\$\begingroup\$

Jelly, 20 bytes

ḊA2¦U*ؽj.¤UŒH:/€IµÞ

Try it online!

Well, this is horribly hacked together but it works. The Footer in the TIO link simply runs the code over each list then returns just the names. The code itself returns the names and all stats in the same format as the input, namely [Name, SPD, DEX, WHT]

Will post an explanation after any more golfing

\$\endgroup\$
0
\$\begingroup\$

Charcoal, 36 bytes

FA⊞υ⟦⁻⌊∕X⊟ι²₂↔⊟ι⊟ι⊟ι⟧W⌈υ«≔Φυ¬⁼ικυ⟦⊟ι

Try it online! Takes quadruples in the order name, weight, speed, dexterity. Explanation:

FA

Loop over the quadruples.

⊞υ⟦⁻⌊∕X⊟ι²₂↔⊟ι⊟ι⊟ι⟧

Square the dexterity, floor divide by the square rooted absolute speed and subtract the weight, then create a tuple with the result and the name and push that to the predefined list.

W⌈υ«

While there are still tuples in the list, take the maximum.

≔Φυ¬⁼ικυ

Remove the tuple from the list.

⟦⊟ι

Output the name on its own line.

\$\endgroup\$
0
\$\begingroup\$

Jelly, 15 14 bytes

ṛ²:A½$}ɗ_@3ƭ/Þ

Try it online!

A monadic link that takes a list of characters as its argument. Each list is structured as [Name, SPD, DEX, WHT]. Returns a list of characters in the desired order.

\$\endgroup\$
0
\$\begingroup\$

Python 3.8, 81 bytes

This solution uses the formula as described in the question. As input a dictionary is expected in the following form: {"name":[SPD, DEX, WHT],...}

lambda d:sorted(n:={_:(i:=-a*a/abs(b)**.5+c)for _,(a,b,c)in d.items()},key=n.get)

Try it online!

\$\endgroup\$
  • \$\begingroup\$ See the last test case, which fails if you ignore DEX. \$\endgroup\$ – bigyihsuan Dec 9 '19 at 13:58
  • \$\begingroup\$ No it doesn't, it gives Bar, Baz, Foo which is the test case... \$\endgroup\$ – game0ver Dec 9 '19 at 14:11
0
\$\begingroup\$

C++ (clang), 324 ... 236 232 bytes

#import<queue>
#import<regex>
#import<cmath>
#import<map>
#define i(s)int(*s**s/sqrt(abs(s[1])))-s[2]
struct C{std::string n;float s[3];};void f(std::vector<C>&v){std::stable_sort(&v[0],&*end(v),[](C a,C b){return i(a.s)>i(b.s);});}

Try it online!

Saved 18 bytes thanks to @cielingcat!!!

Ungolfed:

#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;

struct C {
    string n;
    float s[3];
};
float i(C c) {
    return floor(c.s[0]*c.s[0] / sqrt(abs(c.s[1]))) - c.s[2];
}
void f(vector<C>&v) {
    stable_sort(begin(v), end(v),
        [](C a,C b) {
            return i(a) > i(b);
        });
}
\$\endgroup\$
0
\$\begingroup\$

APL(NARS), chars 40, bytes 80

{⍵≡⍬:⍬⋄↑¨⍵[⍒{(4⊃⍵)-⍨⌊(2*⍨2⊃⍵)÷√∣3⊃⍵}¨⍵]}

test:

  f←{⍵≡⍬:⍬⋄↑¨⍵[⍒{(4⊃⍵)-⍨⌊(2*⍨2⊃⍵)÷√∣3⊃⍵}¨⍵]}
  ⎕fmt ,⊂(('Lonely') 1 2 3)
┌1────────────────┐
│┌4──────────────┐│
││┌6──────┐      ││
│││ Lonely│ 1 2 3││
││└───────┘ ~ ~ ~2│
│└∊──────────────┘3
└∊────────────────┘
  ⎕fmt f,⊂(('Lonely') 1 2 3)
┌1────────┐
│┌6──────┐│
││ Lonely││
│└───────┘2
└∊────────┘
  ⎕fmt (('Alice') 1 2 3)(('Bob') 10 5 0)(('Charlie') 3 2 1)
┌3──────────────────────────────────────────────────┐
│┌4─────────────┐ ┌4────────────┐ ┌4───────────────┐│
││┌5─────┐      │ │┌3───┐       │ │┌7───────┐      ││
│││ Alice│ 1 2 3│ ││ Bob│ 10 5 0│ ││ Charlie│ 3 2 1││
││└──────┘ ~ ~ ~2 │└────┘ ~~ ~ ~2 │└────────┘ ~ ~ ~2│
│└∊─────────────┘ └∊────────────┘ └∊───────────────┘3
└∊──────────────────────────────────────────────────┘
  ⎕fmt f (('Alice') 1 2 3)(('Bob') 10 5 0)(('Charlie') 3 2 1)
┌3─────────────────────────┐
│┌3───┐ ┌7───────┐ ┌5─────┐│
││ Bob│ │ Charlie│ │ Alice││
│└────┘ └────────┘ └──────┘2
└∊─────────────────────────┘
  ⎕fmt f ('Z' 1 1 1)('B' 1 1 1)(('XY') 5 1 1)
┌3────────┐
│┌2──┐    │
││ XY│ Z B│
│└───┘ ¯ ¯2
└∊────────┘
  ⎕fmt f (('Neg') ¯3 ¯3 ¯1)(('SomeNeg') 5 ¯2 ¯4)(('NoNeg') 4 6 8)
┌3─────────────────────────┐
│┌7───────┐ ┌3───┐ ┌5─────┐│
││ SomeNeg│ │ Neg│ │ NoNeg││
│└────────┘ └────┘ └──────┘2
└∊─────────────────────────┘
  ⎕fmt f ⍬
┌0─┐
│ 0│
└~─┘
  ⎕fmt f (('Flo') 1.5 2.5 3.5)(('MoreFlo') 2 2.5 3.5)
┌2────────────────┐
│┌7───────┐ ┌3───┐│
││ MoreFlo│ │ Flo││
│└────────┘ └────┘2
└∊────────────────┘
  ⎕fmt f (('Foo') 2 1 4)(('Baz') 5 5 4)(('Bar') 5 1 4)
┌3───────────────────┐
│┌3───┐ ┌3───┐ ┌3───┐│
││ Bar│ │ Baz│ │ Foo││
│└────┘ └────┘ └────┘2
└∊───────────────────┘
  ⎕fmt f (('Adam') 2 4 1)(('Eve') 2 3 1)
┌2─────────────┐
│┌4────┐ ┌3───┐│
││ Adam│ │ Eve││
│└─────┘ └────┘2
└∊─────────────┘
\$\endgroup\$

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.