Thinking Through Group Rolls for Stars and Worlds without Number

Avoiding the Pitfalls of Everyone Rolls

Note: This post has content disclaimers.

Stomping through the wilderness, the characters come upon a traveller. The traveller issues the following warning then scampers off; “Beware the Ferocious Beast of the Dread Forest!”

Immediately one of the players asks, “I want to make a Knowledge check regarding the Ferocious Beast of the Dread Forest.” And suddenly, everyone is grabbing there dice to fire off individual Knowledge tests.

Or someone says, “I want to track that traveller.” And the players all grab dice to start rolling for it.

Or someone says, “Yikes, a Ferocious Beast, I’m going to hide.” And all of the players grab dice to roll a hide check.

The above narrative describes a few common scenarios with group efforts in Role Playing Game (RPG 📖). For some, this may be more of a Straw Man Dog example, but I’m going to work from those examples.

At those moments when the players begin reaching for the dice, as a Referee, I try to ask some quick questions to pause the roll of the dice. This strategy comes from Burning Wheel Gold (BWG 📖)’s approach to skill tests: establish task and intent. That is to say, what are they doing and why are they doing it?

Yes, I want to go to the dice and see what their oracular powers have to say.

A Concrete Example

In ’s Worlds without Number (WWN 📖), the characters wanted to track down some creatures that had been harrassing the farming village.

Instead of letting everyone make individual tracking rolls, we established who was the best tracker. The best tracker would make the primary roll. I asked who else would be helping and how would they help.

Below is Worlds without Number’s Aiding a Skill Check section. Note: The rules for help in WWN are the same as those in Stars without Number: Revised Edition (SWN 📖).

Aiding a Skill Check

Sometimes one PC will want to lend a hand to another as they attempt a difficult feat. To do this, the player first describes what sort of action they are taking to help their comrade. If the GM agrees that their effort makes sense and would be helpful, the player then rolls a relevant skill check against the same difficulty as the original check. If it’s a success, the other PC gets a +1 bonus on their roll. Multiple PCs can try to help, but the acting PC can’t earn more than a +1 total bonus.

The helping skill doesn’t necessarily have to be the same skill the PC is checking. If a player can think of a way to help a Sneak check with Talk, then so be it.

One character, a Necromancer, said they’d use the Magic skill to listen for the ghostly echoes of last night’s slain victim. Sounds good, I called for a Magic/Charisma test. In WWN, a Necromancer has a “casting stat” of either Intelligence or Charisma. This character chose Intelligence for their casting stat. But I felt listening to and seeking the voices of the dead is much more of a Charisma type check.

Another character, used their Know skill to recall some topographical maps and the local geography.

And the third helping character, tested Sneak/Intelligence to think through how the creature might have snuck into the village and prowled around.

Everyone succeeded on their tests, and so I gave a higher quality of success. I was feeling a bit off my game last night, and should’ve done a bit more regarding the Necromancer’s check.

Later , as I was mulling over the help test, I really liked that everyone described how they contributed to the test. It provided some fictional meaning.

I didn’t frame it as a Dungeons and Dragons: Fourth Edition (4E 📖) Skill Challenge, though it certainly could’ve been that.

In the moment, I wanted a relatively quick roll of the dice to move the fiction forward. I also wanted to avoid everyone throwing individual tracking checks yet wanted to include their characters in the search.

My brain also began wondering how I would adjudicate in WWN the inevitable moment when the group attempts to collectively sneak past some danger. I’m not satisfied with the idea of everyone roll Sneak, and if anyone fails they fail to sneak past the danger.

Quick to the Probability Charts

With all of this rattling around in my brain, I decided to dig into the probabilities. For the purposes of simplicity, my scenarios involve 4 characters.

For each test of a specific skill, they’re collective modifiers are -1, 0, +1, and +2. If I allow them to pick their skill, they all have a modifier of +2. The above concrete example is one in which they picked their skill.

I’m also ignoring any re-rolls or foci that modify the dice pool. Table 217 details the probability.

update: From a conversation over on Reddit, one person had an interesting approach. You take the base DC, multiple that by the number of participants to get a success threshold. Then everyone rolls. If the sum of everyone’s rolls equals or exceeds the success threshold, the group succeeds. I wanted to see that probability, so I added another scenario (eg. Group rolls to beat sum).

I also expanded the probability tests from the original Difficulty Class (DC 📖) 8 and 12 to DC 6, 8, 10, and 12.

Table 217: Test Scenarios for Stars and Worlds without Number and Probabilities for Success
ScenarioSkill DistributionDC 6DC 8DC 10DC 12
everyone must succeedChoose0.7060.2720.0300.001
everyone must succeedSame0.3220.0490.0020.000
at least one must succeedChoose1.0000.9940.8840.518
at least one must succeedSame0.9980.9510.6780.257
at least half must succeedChoose0.9980.9320.5530.132
at least half must succeedSame0.9660.7030.2310.020
least adept count one helpChoose0.9720.8310.5500.213
least adept count one helpSame0.7220.4070.1370.007
least adept count all helpChoose0.9990.9190.6110.225
least adept count all helpSame0.8700.5340.1620.008
most adept count one helpChoose0.9720.8310.5500.213
most adept count one helpSame0.9710.8140.4910.179
most adept count all helpSame0.9940.8500.5020.179
most adept count all helpChoose0.9990.9190.6110.225
group adds rolls to beat sumChoose0.9960.8210.2380.007
group adds rolls to beat sumSame0.9090.3800.0240.000
Note: the "count all help" option deviates from the rules as written.
Same: Assumes the skill test modifier's are +2, +1, 0, and -1 for checks.
Choose: Assumes all skills test with a +2 modifier.

Interpretting those Results

The two scenarios that I want to work from are:

  • Everyone must succeed with the same skill
  • At least one must succeed with the same skill

The former is the frustrating “Okay group, try to sneak past the guards”. The latter is “Okay group, do you know a piece of trivia? Or can you track the creatures?” Both of those, without guidance, often result in near automatic failures or automatic successes.

And that’s boring!

I’ve long appreciated Burning Wheel Gold’s rules for group tests, below is that advice.

When using help to make a test as a group—such as running away, sneaking or climbing—the character with the lowest exponent must make the test for the group. If two characters have the same exponent, use the one with the higher obstacle penalty.

Slowest and Loudest applies to tests in which the group must succeed together: They are all trying to convince someone, they are all sneaking together, they are all fleeing an enraged monster, etc. It is not a universal rule to be applied to all help. Sometimes the master can accept help from the students, but at other times, when the students have to escape the rampaging monster, the protective master can only go as fast as the slowest of the pupils.

The Burning Wheel Codex p39

I find the advice easy to remember. The section is named “Loudest and Slowest”. I want to work towards adopting that advice for SWN and WWN. The probability helps me understand the impact.

Using the Aiding a Skill Check rule and the Loudest and Slowest guidance, I think I’m settling on the following.

Group Skill Checks

When making a group skill check and:

The group must succeed together
the least adept character must test. All others aid that test using the same tested skill. The group's fate is bound to the least adept character's test result.
One member must succeed
the most adept character tests the skill. All others may aid that test with skills of their choosing—so long as the rational is acceptable. The group's fate is bound to that test.

Who is the least or most adept character is up for intepretation; An Expert with a re-roll available is often rather adept.

In the case of needing only one success, poke a little at each character’s approach. Flavor the success with their approach. And certainly flavor the failure with their approach as well.

From a probability stand-point, this moves away from the near auto-failure of “Everyone must succeed” or near auto-success of “Someone must succeed.”

Instead, these two approaches create uncertainty, and a reasonable And by all means, if there’s nothing interesting or complicating in their failure, say Yes to their task and just move on.

Code for the Curious

The Ruby rake task to tabulate probabilities
task :probability do
  class Accumulator
    def initialize(number_of_characters:, dcs: [8,10], scenario:)
      @dcs = dcs
      @scenario = scenario
      @number_of_characters = number_of_characters
      @universe_size = (36**number_of_characters).to_f
      @tests = {}
      methods.grep(/^test_/).each do |method_name|
        @tests[method_name] = {}
        @dcs.each do |dc|
          @tests[method_name][dc] = 0
        end
      end
    end
    attr_reader :universe_size

    def test!(rolls:)
      @tests.each_key do |method_name|
        @dcs.each do |dc|
          if send(method_name, rolls: rolls, dc: dc)
            @tests[method_name][dc] += 1
          end
        end
      end
    end

    def render_results
      puts "Scenario: #{@scenario}\n"
      header_template = "| %-30s |" + @dcs.map {|_| " DC %2d |"}.join("")
      row_template = "| %-30s |" + @dcs.map {|_| " %.3f |"}.join("")
      table_head = sprintf(header_template, @scenario, *@dcs)
      puts table_head
      puts "-" * table_head.length
      @tests.each_pair do |test, results|
        label = test.to_s.sub("test_", "")
        cells = [label]
        @dcs.each do |dc|
          result = results.fetch(dc)
          cells << (result / universe_size)
        end
        puts sprintf(row_template, *cells)
      end
      puts "\n\n"
    end

    def test_everyone_must_succeed(rolls:, dc:)
      rolls.all? { |r| r >= dc }
    end

    def test_at_least_one_must_succeed(rolls:, dc:)
      rolls.max >= dc
    end

    def test_at_least_half_must_succeed(rolls:, dc:)
      threshold = (rolls.count / 2.to_f).round
      rolls.select { |r| r >= dc }.count >= threshold
    end

    def test_least_adept_count_one_help(rolls:, dc:)
      return true if rolls[-1] >= dc
      return false if rolls[-1] < dc - 1

      rolls[0..-2].max >= dc
    end

    def test_least_adept_count_all_help(rolls:, dc:)
      return true if rolls[-1] >= dc

      # There aren't enough possible successes to bump the primary
      # character's failure
      return false if rolls[-1] + @number_of_characters - 1 < dc

      returning_value = false
      # Check if the others can help dig failure out
      (1..(@number_of_characters-1)).each do |i|
        if rolls[-1] + rolls[0..-2].select { |r| r >= dc }.size >= dc
          returning_value = true
          break
        end
      end
      returning_value
    end

    def test_most_adept_count_one_help(rolls:, dc:)
      return true if rolls[0] >= dc
      return false if rolls[0] < dc - 1

      rolls[1..-1].max >= dc
    end

    def test_most_adept_count_all_help(rolls:, dc:)
      return true if rolls[0] >= dc

      # There aren't enough possible successes to bump the primary
      # character's failure
      return false if rolls[0] + @number_of_characters - 1 < dc

      returning_value = false
      # Check if the others can help dig failure out
      (1..(@number_of_characters-1)).each do |i|
        if rolls[0] + rolls[1..-1].select { |r| r >= dc }.size >= dc
          returning_value = true
          break
        end
      end
      return returning_value
    end

    def test_group_adds_rolls_to_beat_sum(rolls:, dc:)
      rolls.sum >= dc * rolls.size
    end

    def each_result(modifier: 0)
      (1..6).each do |a|
        (1..6).each do |b|
          yield(a+b+modifier)
        end
      end
    end
  end

  Accumulator.new(
    number_of_characters: 4,
    scenario: "Choose Skill",
    dcs: [6,8,10,12]
  ).tap do |ac|
    ac.each_result(modifier: 2) do |p1|
      ac.each_result(modifier: 2) do |p2|
        ac.each_result(modifier: 2) do |p3|
          ac.each_result(modifier: 2) do |p4|
            rolls = [p1, p2, p3, p4]
            ac.test!(rolls: rolls)
          end
        end
      end
    end
    ac.render_results
  end

  Accumulator.new(
    number_of_characters: 4,
    scenario: "Same Skill",
    dcs: [6,8,10,12]
  ).tap do |ac|
    # By convention, the p1 should be the most adept character
    ac.each_result(modifier: 2) do |p1|
      ac.each_result(modifier: 1) do |p2|
        ac.each_result(modifier: 0) do |p3|
          ac.each_result(modifier: -1) do |p4|
            rolls = [p1, p2, p3, p4]
            ac.test!(rolls: rolls)
          end
        end
      end
    end
    ac.render_results
  end
end
Results of the above rake probability task
Scenario: Choose Skill
| Choose Skill                   | DC  6 | DC  8 | DC 10 | DC 12 |
------------------------------------------------------------------
| everyone_must_succeed          | 0.706 | 0.272 | 0.030 | 0.001 |
| at_least_one_must_succeed      | 1.000 | 0.994 | 0.884 | 0.518 |
| at_least_half_must_succeed     | 0.998 | 0.932 | 0.553 | 0.132 |
| least_adept_count_one_help     | 0.972 | 0.831 | 0.550 | 0.213 |
| least_adept_count_all_help     | 0.999 | 0.919 | 0.611 | 0.225 |
| most_adept_count_one_help      | 0.972 | 0.831 | 0.550 | 0.213 |
| most_adept_count_all_help      | 0.999 | 0.919 | 0.611 | 0.225 |
| group_adds_rolls_to_beat_sum   | 0.996 | 0.821 | 0.238 | 0.007 |


Scenario: Same Skill
| Same Skill                     | DC  6 | DC  8 | DC 10 | DC 12 |
------------------------------------------------------------------
| everyone_must_succeed          | 0.322 | 0.049 | 0.002 | 0.000 |
| at_least_one_must_succeed      | 0.998 | 0.951 | 0.678 | 0.257 |
| at_least_half_must_succeed     | 0.966 | 0.703 | 0.231 | 0.020 |
| least_adept_count_one_help     | 0.722 | 0.407 | 0.137 | 0.007 |
| least_adept_count_all_help     | 0.870 | 0.534 | 0.162 | 0.008 |
| most_adept_count_one_help      | 0.971 | 0.814 | 0.491 | 0.179 |
| most_adept_count_all_help      | 0.994 | 0.850 | 0.502 | 0.179 |
| group_adds_rolls_to_beat_sum   | 0.909 | 0.380 | 0.024 | 0.000 |