---
name: set-rolling-a-group
description: How to roll a party, an army, a wave of enemies or any roster with the Set World API. Use when one seed must expand into many characters or items with deriveSeed, when requests must be sequenced one at a time, when a roster must stay reproducible against one contentVersion, when budgeting orbs across a roster to equip it better, or when designing a roster record for a game.
license: Free to use when obtained from set.world, commercially too (the API, what it returns, the skills and downloaded models). The source code of Set is © Jimmy Lee under CC BY-NC 4.0 and its commercial use needs a paid waiver.
metadata:
  version: "0.8.9"
  title: Rolling a group
---

# Rolling a group

There is no party endpoint and no `count=` parameter. A group is your code calling the single-character or single-item endpoint once per member, each with its own seed derived from one base seed. Four heroes, two forty-character armies and a thousand roguelike deaths all work the same way. This skill owns `deriveSeed`.

## The rule

```text
deriveSeed(seed, i) = (seed + Math.imul(i, 0x9E3779B9)) >>> 0
```

Member `i` of a roster is exactly a single roll at `deriveSeed(seed, i)`. `deriveSeed(seed, 0)` is `seed`.

```js
const deriveSeed = (seed, i) => ((seed >>> 0) + Math.imul(i, 0x9E3779B9)) >>> 0;
```

```python
def derive_seed(seed: int, i: int) -> int:
    return (seed + i * 0x9E3779B9) % 2**32
```

With base seed 7 the first four member seeds are 7, 2654435776, 1013904249 and 3668340018. Over MCP, the `derive_seed` tool returns the same values ([API connection](https://set.world/skills/api-connection)).

## Roll a party of four

```js
async function rollParty(seed, size = 4) {
  const members = [];
  let contentVersion = null;
  for (let index = 0; index < size; index++) {
    const memberSeed = deriveSeed(seed, index);
    const response = await fetch(`https://set.world/api/roll/character?seed=${memberSeed}`);
    if (!response.ok) throw new Error((await response.json()).error.message);
    const character = await response.json();
    contentVersion ??= character.contentVersion;
    if (character.contentVersion !== contentVersion) throw new Error('The content snapshot changed; roll the party again.');
    members.push({ index, seed: memberSeed, character });
  }
  return { seed, size, contentVersion, endpoint: '/api/roll/character', members };
}
```

## The roster schema

Set returns one character per call; the roster is your own record. A schema that holds everything needed to replay it:

```ts
type Roster = {
  seed: number;                    // the base seed
  size: number;
  contentVersion: string;          // one snapshot for every member
  endpoint: '/api/roll/character' | '/api/roll/item';
  parameters?: Record<string, string>;   // for example { equipmentBonus: "1" }
  members: Array<{
    index: number;                 // i
    seed: number;                  // deriveSeed(seed, i)
    character: Character;          // the full response, from Rolling a character
  }>;
};
```

```jsonc
{
  "seed": 7,
  "size": 4,
  "contentVersion": "4.swpi78HUCtcrZ1Eoi9dipw",
  "endpoint": "/api/roll/character",
  "members": [
    { "index": 0, "seed": 7, "character": { /* Character */ } },
    { "index": 1, "seed": 2654435776, "character": { /* Character */ } },
    { "index": 2, "seed": 1013904249, "character": { /* Character */ } },
    { "index": 3, "seed": 3668340018, "character": { /* Character */ } }
  ]
}
```

## Rules that keep a roster honest

- **One request at a time.** Creation is single-flight on the server: each roll waits for the one before it. Sequential requests cost nothing extra and keep completed members when a later one fails.
- **One snapshot per roster.** Every member must share one `contentVersion`. If it changes mid-run, stop and roll again.
- **Keep the full responses.** Old snapshots are not hosted, so the seed alone cannot rebuild a roster after the tables change.
- **Give each member your own ID.** A seed reproduces a roll; it is not a unique person or a login.
- **Separate streams.** Use different base seeds, or disjoint index ranges, for things that must not collide: heroes at `deriveSeed(seed, 0…3)`, enemies at `deriveSeed(seed, 100…)`.

## Common groups

| Group | Recipe |
| --- | --- |
| Party | 4 calls at `deriveSeed(seed, 0…3)` |
| Two armies | 32 calls at `deriveSeed(seed, 0…31)`; even indexes are one side, odd the other (how Set's own army demo splits them) |
| Encounter | One call per enemy; the same endpoint makes monsters |
| Loot pile | `/api/roll/item?seed=deriveSeed(seed, i)` per drop |
| Daily run | Base seed from the date; every player gets the same roster |

## Spend orbs across a group

A roster costs nothing to roll. Orbs enter when your game equips or strengthens its members, one craft or one stat purchase at a time ([Rolling a character](https://set.world/skills/rolling-a-character) shows what orbs buy for one member). The budget and how it is split are yours:

| Plan | Requests | Orbs |
| --- | ---: | ---: |
| Re-craft every item of a party of 4 at 125 orbs each | 4 × 15 | 7,500 |
| The same for 32 soldiers | 32 × 15 | 60,000 |
| One Grade S guarantee for the leader | 1 | 15,625 |

- **Spread or concentrate.** A spend of 125 orbs reaches Grade B or better 0.95% of the time; one craft at 15,625 reaches it 98.2%. Many small crafts lift everyone a little; a few large ones make heroes. The full odds are in [Crafting an item](https://set.world/skills/crafting-an-item).
- **Seed crafts apart from the roster.** Give each craft its own seed in a range the members do not use, for example `deriveSeed(seed, 1000 + member × 100 + craft)`.
- **One request at a time, one snapshot.** The same rules as the roster itself. Charge the `orbs` each response reports.

**Orbs are your game's currency, not Set's.** Set has no party purse and no rule for filling one. Your game tracks the orbs: earned per kill, shared by a guild, or converted from the wood, stone and ore the party gathered, at rates you set. The bridge is in [Crafting an item](https://set.world/skills/crafting-an-item).

```js
async function outfitRoster(roster, wallet, orbsPerCraft, craftsEach) {
  for (const member of roster.members) {                                  // sequential, never parallel
    await outfit(member.hero, wallet, orbsPerCraft, craftsEach, deriveSeed(roster.seed, 1000 + member.index * 100));
  }
}
```

`outfit` is the function in [Rolling a character](https://set.world/skills/rolling-a-character).

## Matching a group against another

`powerRating` and `equipmentOrbValue` can cap a roster (a salary cap is a sum). Equal totals do not prove a fair fight. To price kills, see [Crafting an item](https://set.world/skills/crafting-an-item); for turn order without your own rules, see [Combat](https://set.world/skills/basic-lightweight-combat) (experimental and optional). How Set's own party, army and dungeon use rosters is in [Scenes](https://set.world/skills/anima-scenes).
