Roll for Initiative!

17

3

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

bigyihsuan

Posted 2019-12-08T15:45:23.283

Reputation: 1 483

Curious - where did this initiative formula come from? – Quintec – 2019-12-08T16:52:59.223

1Something I came up with on the fly while writing this, it's not really based on anything. – bigyihsuan – 2019-12-08T17:12:30.840

So it is. Thanks for catching it! – bigyihsuan – 2019-12-08T19:48:29.577

Can we sort the input in place (i.e whatever is passed into the function will be sorted when the function finishes)? – Noodle9 – 2019-12-08T21:54:10.073

So small dexterity is good here? – Paŭlo Ebermann – 2019-12-09T00:08:41.320

For some reason ignoring DEX parameter does not affect the final result. – game0ver – 2019-12-09T13:51:15.033

I just added a new test case. You should not ignore DEX. If you look at Baz in the last test case, you'll get 7 if you use DEX, and 21 if you don't. – bigyihsuan – 2019-12-09T13:57:14.977

@bigyihsuan But you get Bar, Baz, Foo even if ignore it: check here, or do I miss something here?

– game0ver – 2019-12-09T14:17:22.757

@game0ver Your formula fails for [['A', 1, 1, 5], ['B', 2, 50, 5]]. With the correct formula, you get [A, B], your formula returns [B, A] – Black Owl Kai – 2019-12-09T15:09:28.427

@BlackOwlKai thanks, your are right, didn't check that... Also I ended up doing some math and it turned out that the formula I used was completely arbitrary. There is an alternative way to write the formula but it doesn't save bytes unfortunately... – game0ver – 2019-12-09T15:23:48.773

Suggested testcase: [['Adam', 2, 4, 1], ['Eve', 2, 3, 1]]. Both have an initiative of 2, so input order should be retained and the output should be ['Adam', 'Eve']. But if a solution does not round down the first part, Adam stays at 2, but Eve gets to ~2.3, therefore the solution outputs ['Eve', 'Adam'] – Black Owl Kai – 2019-12-10T08:56:09.857

Answers

7

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}}\$

Arnauld

Posted 2019-12-08T15:45:23.283

Reputation: 111 334

1Outputting 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. – asgallant – 2019-12-09T18:20:51.190

4

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)

Kevin Cruijssen

Posted 2019-12-08T15:45:23.283

Reputation: 67 575

s-( can be just -. – Grimmy – 2019-12-09T13:10:40.580

3

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.)

Quintec

Posted 2019-12-08T15:45:23.283

Reputation: 2 801

3

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

Black Owl Kai

Posted 2019-12-08T15:45:23.283

Reputation: 980

1

In python2, you can shave off 2 bytes via unpacking. (This is one of the few things I don't like in Python 3).

– Chas Brown – 2019-12-08T21:40:07.293

I think you are supposed to return a list of just the names,not the entire records. – Galen Ivanov – 2019-12-09T10:14:41.947

@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. – Potato44 – 2019-12-09T10:41:23.253

@Potato44 Yes, the Sample IO shows different possibilities. – Galen Ivanov – 2019-12-09T11:13:18.323

@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. – Black Owl Kai – 2019-12-09T15:02:53.593

@BlackOwlKai You don't need int(...), you can save 5 bytes... The same goes also for python2

– game0ver – 2019-12-09T15:33:01.673

3

K (oK), 28 bytes

f:{(!x)@>{-z-(x*x)%%y|-y}.'.x}

Try it online!

-10 thanks to ngn :-)

scrawl

Posted 2019-12-08T15:45:23.283

Reputation: 1 079

(y;-1*y)0>y -> y|-y, %: -> %, (-z)+ -> -z- – ngn – 2020-02-16T05:34:56.717

@ngn aghhh, the y sign switch was obviously doing too much work in my implementation. your solution seems to obvious in hindsight. i won't forget that! – scrawl – 2020-02-16T20:57:52.563

2

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

game0ver

Posted 2019-12-08T15:45:23.283

Reputation: 621

@79037662 thanks! Good point! I also got rid of some extra bytes by replacing indexing with variables! – game0ver – 2019-12-08T20:37:43.570

See the last test case, which fails if you ignore DEX. – bigyihsuan – 2019-12-09T13:59:08.043

No it doesn't, it gives Bar, Baz, Foo which is the test case... – game0ver – 2019-12-09T14:10:37.210

1

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.

caird coinheringaahing

Posted 2019-12-08T15:45:23.283

Reputation: 13 702

1

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

Graham

Posted 2019-12-08T15:45:23.283

Reputation: 3 184

1

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!

Galen Ivanov

Posted 2019-12-08T15:45:23.283

Reputation: 13 815

1I don't know red, but shouldn't you be able to replace e/2 * e/2 with e/2 ** 2? – raznagul – 2019-12-09T13:16:25.823

@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! – Galen Ivanov – 2019-12-09T13:21:46.060

1

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

Engineer Toast

Posted 2019-12-08T15:45:23.283

Reputation: 5 769

1=Sort(A:A;D:D-B:B^2/Sqrt(Abs(C:C));1) gets rid of the if – Black Owl Kai – 2019-12-10T08:40:14.610

0

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

caird coinheringaahing

Posted 2019-12-08T15:45:23.283

Reputation: 13 702

0

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!

game0ver

Posted 2019-12-08T15:45:23.283

Reputation: 621

See the last test case, which fails if you ignore DEX. – bigyihsuan – 2019-12-09T13:58:55.990

No it doesn't, it gives Bar, Baz, Foo which is the test case... – game0ver – 2019-12-09T14:11:44.537

0

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.

Neil

Posted 2019-12-08T15:45:23.283

Reputation: 95 035

0

C++ (clang), 324 \$\cdots\$ 232 204 bytes

Saved 18 46 bytes thanks to cielingcat!!!

#import<regex>
#import<cmath>
#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!

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);
        });
}

Noodle9

Posted 2019-12-08T15:45:23.283

Reputation: 2 776

@ceilingcat Very nice - thanks! :-) – Noodle9 – 2020-02-27T01:13:02.393

0

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.

Nick Kennedy

Posted 2019-12-08T15:45:23.283

Reputation: 11 829

0

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
└∊─────────────┘

RosLuP

Posted 2019-12-08T15:45:23.283

Reputation: 3 036

0

Perl 5 -n, 72 bytes

sub i{int($_[1]**2/sqrt abs$_[2])-pop}say$$_[0]for sort{i(@$b)-i@$a}eval

Try it online!

Xcali

Posted 2019-12-08T15:45:23.283

Reputation: 7 671