Math Help - Probability per Tick

I’m trying to create a small RPG simulator that will simulate a fight between a team of players and a boss of some sort.

  1. Each boss and player has a skill level.
  2. If a team is composed entirely of members who are of equal level of skill as the boss they are fighting, they should win half of the time. If they are 3 levels above the boss, they should win 3/4ths of the time. If they are 3 levels below the boss, they should win 1/4th of the time.
  3. To win a boss fight, at least half of the team has to survive 10 “ticks”. During each tick, each player is given a probability of death that is based half upon his own person skill level, and half upon the average of the rest of the players of the team.

The goal is to simulate the domino effect of team deaths. If one person dies, the rest of the team is less likely to be able to survive. If every player in the team is the exact same level as the boss, they’ll win 50%, but if it’s just the group average that is equal to the boss and some of the players are several levels below, this might make it more likely that the team will fail since those members will have a higher chance of death and their deaths will lesten the group average, which is counted half against the remaining players.

But, for the life of me I can’t figure out how to calculate the odds I should give each person for each tick.

I was thinking that if, for instance, I wanted half to live (in the case where all players are the same level and even with the boss) that it was simply a matter of figuring out what percentage of deaths would compound into 50% over ten runs.

0.5 = x^10
x = 0.5^0.1
x = ~0.933

But, since the players individual levels are shrunk as people die, when it goes back in to calculate for the levels of players on the second tick (for instance), they’re no longer even with the boss. Instead they’re (I think) half they’re own average, which is even with the boss, and half the average of their group which is 93.3% dead. So where as before the fight, each individual player had a == rating with the boss which translates to a 50% chance of death @ the end of 10 ticks, their lowered level gives them something more like 48.3% chance the second tick, which makes my code calculate:

0.483 = x^10
x = 0.483^0.1
x = ~0.930

So during the second tick, slightly more people will die than are intended, and during the third even more, and so on. In total, the team can only half survive for seven or eight ticks instead of the projected ten.

I’m not sure how to work back to figure out what odds to give for each tick when the result is based on the result of the previous.

I hope this makes sense…

How many players per team?

I don’t understand this part. How do you treat dead players? If the weak ones die, would the average of the live ones go up? Do dead players become level zero or something, or do you ignore them, or …?

25 is what I have been using.

At the moment, I treat dead players as being -6 of the boss’s level (i.e. having a 100% chance to die by the end of the fight.) This is rather cheesy but it’s somewhat of a different problem.

Here’s my source code at the moment (in the D language):



import std.stdio;
import std.random;
import std.math;
import std.c.time;

const int NUM_RAIDERS		= 50;
const int NUM_OUTING		= 25;
const double MIN_ATTENDANCE	= 0.5;

const int SLOT_COUNT			= 10;
const int INSTANCE_COUNT		= 5;
const int BOSSES_PER_INSTANCE	= 10;
const int LOOT_PER_BOSS			= 3;
const int ITEM_TYPE_COUNT		= 10;
const int ITEM_LEVEL_ADD		= 10;

class Raider {
public:
	int dkp;
	double attendance;
	int item_levels[SLOT_COUNT];
	double average_level;
	int class_type;
	bool is_guest;	
	
	void setLevel(int level) {
		for (int L = 0; L < SLOT_COUNT; L++) {
			item_levels[L] = level;
		}
		average_level = level;
	}
	
	void updateAverage() {
		int total = 0;
		for (int L = 0; L < SLOT_COUNT; L++) {
			total += item_levels[L];
		}
		average_level = cast(double)total / cast(double)SLOT_COUNT;
	}
}

class LootSystem {
protected:
	Raider[] all_raiders;
	Raider[NUM_OUTING] outing_raiders;
	Raider[NUM_RAIDERS] resting_raiders;
	int resting_count;
	
	Raider[NUM_OUTING] guests;
	Random gen;
	
public:
	this(Raider[] raiders) {
		double attendance_increase = (1.0 - MIN_ATTENDANCE) / cast(double)NUM_RAIDERS;
		double curr_attendance = MIN_ATTENDANCE;
		
		gen = Random(time(null));
		
		for (int L = 0; L < NUM_RAIDERS; L++) {			
			raiders[L].dkp = 100;
			raiders[L].attendance = curr_attendance;
			raiders[L].attendance = curr_attendance;
			raiders[L].class_type = uniform(gen, 0, ITEM_TYPE_COUNT - 1);
			raiders[L].setLevel(0);
			
			curr_attendance += attendance_increase;
		}
		for (int L = 0; L < NUM_OUTING; L++) {
			guests[L] = new Raider;	
			guests[L].is_guest = true;
		}
		all_raiders = raiders;
	}
	
	void createGroup(int level) {
		bool[NUM_RAIDERS] can_go;
		int available_count = 0;
		
		for (int L = 0; L < NUM_RAIDERS; L++) {	// use the attendance rates to choose which members are available to go
			can_go[L] = uniform(gen, 0.0, 1.0) <= all_raiders[L].attendance;
			if (can_go[L]) {
				++available_count;
			}
		}
		
		if (available_count < NUM_OUTING) {	// If there aren't enough people, add guests
			int i = 0;
			for (int L = 0; L < NUM_RAIDERS; L++) {	// copy members over
				if (can_go[L]) {
					outing_raiders* = all_raiders[L];
					++i;
				}
			}
			for (int L = 0; L < (NUM_OUTING - available_count); L++) {	// copy guests over
				guests[L].setLevel(level);
				outing_raiders* = guests[L];
				++i;
			}
		}
		else {
			while (available_count != NUM_OUTING) {	// If there are too many people, randomly choose some to remove until we have the right number
				int i = uniform(gen, 0, NUM_RAIDERS - 1);
				if (can_go*) {
					can_go* = false;
					--available_count;
				}
			}
			
			int i = 0;
			for (int L = 0; L < NUM_RAIDERS; L++) {	// Copy everyone over
				if (can_go[L]) {
					outing_raiders* = all_raiders[L];
					++i;
				}
			}
		}
		
		resting_count = 0;
		for (int L = 0; L < NUM_RAIDERS; L++) {
			if (can_go[L] == false) {
				resting_raiders[resting_count] = all_raiders[L];
				++resting_count;
			}
		}
	}
	
	bool fight_boss(double boss_level) {
		const int ALIVE	= 1;
		const int DYING	= 2;
		const int DEAD	= 0;
		
		double group_average;
		double player_level;
		int alive_count;
		int[NUM_OUTING] raider_state = ALIVE;
		double death_chance, death_test;

		for (int L = 0; L < 8; L++) {	// Boss fights are divided into 10 ticks during which there is a chance that people can die and hence cease adding to the group output, causing potentially more deaths
			for (int L2 = 0; L2 < NUM_OUTING; L2++) {	// For each player, determine if they will live or die this turn
				if (raider_state[L2] == DEAD) continue;	// No need to figure out if he's dead
				
				group_average = 0;
				for (int L3 = 0; L3 < NUM_OUTING; L3++) {	// Find the average level of all players who aren't the current player
					if (L2 == L3) {
						continue;
					}
					else if (raider_state[L3] == DEAD) {
						group_average += -6.0L;	// adds nothing
					}
					else {
						group_average += outing_raiders[L3].average_level - boss_level;
					}
				}
				group_average /= cast(double)(NUM_OUTING - 1);
				
				player_level =	// Figure out the average of this player and the group and use that as his strength level for the tick
					(
						outing_raiders[L2].average_level - boss_level
						+ group_average
					)
					/ 2.0L
				;

				if (	// If the boss is more than six levels above us, we die.
					boss_level > player_level
					&& boss_level - player_level >= 6.0L
				) {
					raider_state[L2] = DYING;
					continue;
				}
				else if (	// If the player is more than six levels above the boss, we live.
					player_level > boss_level
					&& player_level - boss_level >= 6.0L
				) {
					continue;
				}
				
				death_chance = player_level - boss_level + 6.0L;	// 0.0 < death_chance < 12.0
				death_chance /= 12.0L;	// 0.0 < death_chance < 1.0
				death_chance = pow(death_chance, 0.1L);	// death_chance^(1/10)	-- when the team is the same level as the boss, they should have a 50% chance of besting it. To get a 50% chance through 10 ticks, the odds of any one person dying needs to be x^10 = .5 or x = .5^(1/10)
				
				death_test = uniform(gen, 0.0L, 1.0L);
				if (death_test > death_chance) {
					raider_state[L2] = DYING;
				}
			}
			
			alive_count = 0;
			for (int L2 = 0; L2 < NUM_OUTING; L2++) {	// Kill all players who died during the round
				if (raider_state[L2] == ALIVE) {
					++alive_count;
				}
				else if (raider_state[L2] == DYING) {
					raider_state[L2] = DEAD;
				}
				// else is dead
			}

			if (alive_count < (NUM_OUTING / 2)) {
				return false;
			}
		}
		return true;
	}
	
	void before_run() {}
	void do_loot(int item_level, int item_slot, int item_type) {}
	void after_run() {}

	int go() {
		int current_instance = 0;
		int max_boss = 0;
		int start_boss, curr_boss;
		int run_count = 0;
		
		while (current_instance < INSTANCE_COUNT) {	
			double group_average;
			start_boss = curr_boss = current_instance * BOSSES_PER_INSTANCE;

			createGroup(max_boss);
			
			before_run();
			
			while (
				curr_boss < (start_boss + BOSSES_PER_INSTANCE)	// Don't start a new instance after completing the current one
				&& fight_boss(curr_boss)
			) {
				if (curr_boss > max_boss) {
					max_boss = curr_boss;
				}
				
				for (int L; L < LOOT_PER_BOSS; L++) {
					int level = curr_boss + uniform(gen, 1, ITEM_LEVEL_ADD);
					int slot = uniform(gen, 0, SLOT_COUNT - 1);
					int type = uniform(gen, 0, ITEM_TYPE_COUNT - 1);
					do_loot(level, slot, type);
				}
				++curr_boss;
			}
			
			after_run();
			
			group_average = 0;
			for (int L = 0; L < NUM_RAIDERS; L++) {
				group_average += all_raiders[L].average_level;
			}
			group_average /= cast(double)NUM_RAIDERS;
			
			if (
				curr_boss == (start_boss + BOSSES_PER_INSTANCE)
				&& group_average >= (start_boss + BOSSES_PER_INSTANCE)
			) {
				current_instance += 1;
			}
			
			++run_count;
		}
		
		return run_count;
	}
}

class RandomLoot : LootSystem {
private:
	class RandomRaider : Raider {
		
	}
	
public:
	this() {
		all_raiders = new RandomRaider[NUM_RAIDERS];
		for (int L = 0; L < NUM_RAIDERS; L++) {
			all_raiders[L] = new RandomRaider;
		}
		
		super(all_raiders);
	}
	
	void do_loot(int item_level, int item_slot, int item_type) {
		int eligibles[NUM_OUTING];
		int eligible_count = 0;
		
		for (int L = 0; L < NUM_OUTING; L++) {
			if (
				outing_raiders[L].class_type == item_type
				&& outing_raiders[L].item_levels[item_slot] < item_level
			) {
				eligibles[eligible_count] = L;
				++eligible_count;
			}
		}
		if (eligible_count > 1) {
			int i = uniform(gen, 0, eligible_count - 1);
			i = eligibles*;
			outing_raiders*.item_levels[item_slot] = item_level;
			outing_raiders*.updateAverage();
		}
		else if (eligible_count == 1) {
			int i = eligibles[0];
			outing_raiders*.item_levels[item_slot] = item_level;
			outing_raiders*.updateAverage();
		}
	}
}

int main(char args[][]) {
	RandomLoot rand_loot = new RandomLoot();
	rand_loot.go();

	return 0;
}


Of course, if anyone has a better solution to the ultimate goal than the 10-tick, half survives simulator, then that’s fully welcome as well.

If you’re looking for any kind of reasonable RPG fight simulator, the number of players has to enter into the equation. Any boss that doesn’t require a special strategy to beast is toast if you throw enough level 1 fighters at it.

It’s slightly more complicated, but you should keep track of hit points for both players and boss. Then you can tweak the parameters to get the desired survival distributions.

Well I was hoping for something that required less grunt balancing. I’d feel like I would need to create tank, healer, and DPS classes and simulate them, which means coming up with realistic damage mitigation formulas, healer AI, DPS output, etc. and then a lot of fudging about to make it balance.

I’m trying to figure out an optimal DKP/loot scheme given the potential domino-death effect of favoring a minority of players, not to create an uber-realistic RPG sim.

It doesn’t have to instantly become complicated with hitpoints and such. As your simulation stands everyone is basically a level x commoner with a chandelier that falls on the boss if they live long enough. All you have to do is make them do x amount of damage per turn, and have z amount of hitpoints depending on level and do the same with the boss (maybe between x-y damage if you’re edgy enough to use randoms for it… crits if you’re getting into it). This simulates the domino effect nicely actually, as a person dies the boss is taking less damage per turn giving him more oppurtunity to kill, hoping he attacks the higher level is better (unless he can attack everyone in the group with a multi-hit move).

Of course you always could throw in some basic healing, taunting, and damaging moves, but overall I think an easy hitpoint system per round would be better than calculating the probability that someone dies a given round.

Well, just a report of the results, in case anyone is interested (copied from my raid groups website):

Overall Results:

  • Anything random isn’t very good. Suicide kings, charging a lump sum, loot lists, etc. are largely interchangeable with random selection. Of course, some of this can theoretically be improved via passing, but that is unreliable.

  • Priority schemes (farthest behind gets first pick, EPGP, etc.) can vary between decent to suck depending on group size and attendance distribution, or whatever else. They’re your best bet to mess yourself up.

  • Bidding schemes are consistently good and largely interchangeable.

  • A bidding scheme mixed with EPGP is consistent better than plain bidding. Though, plain bidding cuts down enough (from random) that improvements over them are not impressive.
    This was the second version of my application, so I don’t have my Suicide Kings test results. But from those results I’m decently certain that loot lists, etc. would be no better than random.

The Schemes:
Random - Dice roll that goes to anyone for whom it would be an upgrade
33/66/100 - Bidders must bid 33%, 66%, or 100% of their total DKP.
Plus One Bid - People bid (in secret) a minimum of 50 DKP and pay +1 of whatever the second highest bid was (or 50 DKP if there were no others.)
Greatest Increase - The item goes to whoever would gain the largest increase from it.
EPGP (AKA Relational DKP) - The person is given DKP, but they’re given a priority based upon DKP/DKP Spent, with neither ever decreasing. DKP Spent is increased by a set value.
EPGP Bid - 33/66/100 mixed with EPGP. Instead of gaining via prioritisation, you use your DKP/DKP Spent value as your amount you have to bid with.

Results are an average of 100 runs through 5 instances. The percentage value at the front is the amount of improvement/loss versus Random.

A simulation of our team make-up:

Total Members: 18
Raid Size: 10 Man Instances
Minimum Attendance: 45% (50% attendance + irregular hickups)
Items per Boss: 2
Class Split: 3 (this means that on average there will be 10/3 people who might be interested in the item)

Number of runs to completion of 5 instances:
100% Random: ~656
76.9% 33/66/100: ~505
78.5% Plus One: ~515
76.2% Greatest Increase: ~500
94.5% EPGP: ~620 @ 100% of the quotient (found to be optimum of schemes tested)
74.3% EPGP Bid: ~488

A simulation of a 25-man instance group:

Total Members: 40
Raid Size: 25 Man Instances
Minimum Attendance: 45%
Items per Boss: 3
Class Split: 6 (25/6 people who might be interested in the item)

100% Random: ~1030
79.6% 33/66/100: ~820
78.6% Plus One: ~810
77.7% Greatest Increase: ~800
98.1% EPGP: ~1010 @ 100% of quotient
76.7% EPGP Bid: ~790

A simulation of a 25-man instance group with low attendance:

Total Members: 55
Raid Size: 25 Man Instances
Minimum Attendance: 20%
Items per Boss: 3
Class Split: 6 (25/6 people who might be interested in the item)

100% Random: ~1240
78.2% 33/66/100: ~970
77.4% Plus One: ~960
79.8% Greatest Increase: ~990
97.6% EPGP: ~1210 @ 100% of quotient
74.2% EPGP Bid: ~920

The total source code is just too long to fit inside a post. If anyone wants it, just PM me and we can figure something out.

Does the boss always attack the main tank or does he spread his damage around? If the boss attacks strategic targets like the main healer that can have a huge impact on the outcome.

Here’s how I’d go about balancing something like this. I’d boil everything down to damage per tick. How much damage per tick on average is every member of the party delivering to the boss? All sorts of things get factored into this – buffs, cooldown times, equipment, strategic choices. On the other side, how much damage per tick can the party members absorb? And how does the boss allocate damage to different party members? Factor in the effects of the healers or defensive buffs or other strategic choices into these numbers.

Then it’s relatively easy to do a quick and dirty simulation of the battle. You can see that after X ticks the main tank will die and after Y ticks the secondary will go and then the battle is probably over. Jiggle the numbers, simulate, jiggle the numbers, simulate until you’re happy with the outcome.

Pochacco, if you distill everything down, every player is equally worthwhile. A healer is extending your life span, but if you can speed up the bosses death, that’s equal.

I’ll just post the bossFight() function:


	bool fightBoss(double boss_level) {
		const double BOSS_MAGIC = 1000.0L;	// Play with this number to calibrate
		
		int alive_count;
		bool[NUM_OUTING] raider_alive = true;
		double[NUM_OUTING] raider_hp;
		double[NUM_OUTING] raider_dps;
		double boss_hp =
			NUM_OUTING	// Boss HP is related to the number of attackers
			* (5 * 60 / 3)	// we are aiming for a five minute fight with 3 second cooldown between spells for all parties
			* (BOSS_MAGIC + (boss_level * cast(double)(100 / BOSSES_PER_INSTANCE)))	// bosses should be ready for players who do +100 DP/3S per readiness for a new instance
		;
		double boss_dps =
			(10000.0L + (boss_level * cast(double)(1000 / BOSSES_PER_INSTANCE)))	// player HP
			/ (5 * 60 / 3)	// projected tick count
		;
		
		for (int L = 0; L < NUM_OUTING; L++) {
			raider_hp[L] = 10000.0L + (outing_raiders[L].average_level * cast(double)(1000 / BOSSES_PER_INSTANCE));	// add 1000 HP per readiness for a new inertance
			raider_dps[L] = 1000.0L + (outing_raiders[L].average_level * cast(double)(100 / BOSSES_PER_INSTANCE));	// add 100 DP/3S per readiness for a new instance
		}
		
		while (true) {
			for (int L = 0; L < NUM_OUTING; L++) {
				if (raider_alive[L]) {
					boss_hp -= raider_dps[L];
					raider_hp[L] -= boss_dps;
					
					if (raider_hp[L] <= 0.0L) {
						raider_alive[L] = false;
					}
				}
			}
			
			if (boss_hp <= 0.0L) {
				return true;
			}
			
			alive_count = 0;
			for (int L = 0; L < NUM_OUTING; L++) {
				if (raider_alive[L]) {
					++alive_count;
				}
			}
			if (alive_count == 0) {
				return false;
			}
		}
	}

I removed the element of chance from it, but since everything scales with its level at a perfectly even math that matches up when all raiders == boss level, I’m decently certain that it’s a viable simulator. And having no randomness also the effect of speeding up run time so that’s good.

I think I’m misunderstanding what you’re trying to do. Let me go back to something you said in your original post:

“During each tick, each player is given a probability of death that is based half upon his own person skill level, and half upon the average of the rest of the players of the team.”

This seems like an odd way to model combat in an RPG. If you’re simply assigning a probability to each player that determines whether or not they will die in a round then there’s a non-zero probability that everyone on the team will die in round one! But in most RPGs that’s impossible. In most RPGs a boss has a max amount of damage that it can pump out in one round. It may be focused on one character or it may be spread out over the whole team, but unless the team is really weak and the boss is really strong that max damage amount will not be large enough to wipe out everyone at once. So already you’ve built an impossible outcome into your model. You’re not accurately simulating the possible outcomes of the actual play experience.

As I said, if I was trying to model this system I wouldn’t start with death probabilities. I would start with damage flow. How much damage per tick can all the entities absorb and deliver? It’s then very easy to model different boss strategies and see how the team collapses. Do the healers go first? The DPSers? The tanks?

You might consider taking your guild’s DPS, HPS, and some sort of HPxArmor numbers, and treating them as orthogonal but linked variables. Figure out the formula for DPS as a function of Armor across all classes. Then roll a random Monte Carlo raid that is statistically representative of your group. Bin the raid by HPxArmor and assume that it tracks closely with classes (top 3% are tanks, next 10% are wearing mail, etc.).

Once you have a Monte Carlo raid, you can have each character roll their DPS plus a small fudge factor each tick. The boss rolls main attacks against the tank and random AoEs that hit 33% or 66% of the raid randomly for about the same damage as a main attack. Healers take a turn at the end of each round, retroactively focusing their healing for the round on the tank or another player. You can simulate morale problems, too - every time a player dies, another player in that group does only half their DPS for the round (simulating that player panicking and/or using mana-inefficient tactics to try to rescue their groupmate).

With some simple algorithms like this, I think you’ll start to see semi-realistic raids – short successful runs where everyone does lots of DPS from start to end, long slogs where a few folks die, and quick wipes where the boss’s first lucky AoE hit devolves into chaos.

Provided a level 1 fighter has a non-zero chance of inflicting non-zero damage on the boss per phase / round / second / tick / whatever. (And leaving aside regeneration, etc).

I’m confused. :slight_smile:

How does the form of simulator you’ve discussed help with the 2nd objective… since there appears to be no “favoured player” effect? Or are these objectives two even related.

Very confused. Interested. But confused. :slight_smile:

(Wouldn’t the optimal DKP scheme be one that leaves the largest number of players feeling that they were fairly treated and likely to want to play again?)

Well I dropped my initial method and adopted Jragon’s, so the strategy formulated in the OP is no longer relevant. I agree with him that healing, tanking, etc. all make a person in a raid equally useful to the team as a whole (perhaps varying a bit by boss within the game, but overall), so you might as well abstract everything down to DPS and HP and ignore healing/damage mitigation/etc. The end result should be the same and ultimately is easier to program and not have to worry that you might have misbalanced things.

There will almost always be favored players based upon attendance, simply because the more times you are present to receive loot, the faster you’re going to gear up. But, you can favor players in other ways. For instance, bidding systems give favor to people who will receive a greater upgrade. The “Greatest Increase” loot scheme I included in my results is a bidding sheme where everyone has the same amount of bidding points and so the person with the lowest level in attendance is favored.

Using “Greatest Increase” with a group that has a lot of members with low attendance proved to be ultimately a negative though since (presumably) so many of them are so rarely in attendance that trying to keep them all geared up ultimately proves to be a drag.

But the ultimate hypothesis that I wanted to test was that a system which favored people who had fallen behind more than a pure bidding system, but less than a socialistic one, would end up being advantageous because of the domino effect in battles. Ultimately it is a group effort and so you do need to keep up the other players who aren’t able to attend 100% of the raids.

I suspect that the results would have been greater if I had implemented a pareto distribution to attendance, but I don’t understand the formula for the curves well enough to create one that seems feasible.

Yes, I didn’t phrase my comment well. When I wrote that there appeared to be no favoured player effect, I was meaning that in the combat simulator scheme you had originally detailed (and in subsequent discussions of HP and Dam instead) there seemed to be no dimension / factor that modeled the effect of better gear, either concentrated (on a few favoured players) or dispersed through the raid. No part of the model appeared to handle the the upgrading; the effect of which seemed to be your ultimate aim. There isn’t (or doesn’t seem to be) a variable for gear-ed-ness that makes any raid member harder to kill and thus offsets the domino. (Perhaps you were leaving out this factor initially for simplicity – until the basic model was working – but that was the cause of my confusion between your stated aims and model as presented).

Sure is, and that seems a sensible approach, provided it can be seen to be balanced by the high attenders, else they may feel slighted and their high attendance unappreciated.

Ah, no there is if you read the code. Each player’s individual HP and DPS per tick is directly proportional to his personal average_level (the average of his gear items.) At level 0 (where everyone starts), everyone has exactly 10,000 HP and 1,000 DPS. For every ten levels (BOSSES_PER_INSTANCE), they gain 1000 HP and 100 DPS.


raider_hp[L] = 10000.0L + (outing_raiders[L].average_level * cast(double)(1000 / BOSSES_PER_INSTANCE));	// add 1000 HP per readiness for a new instance
raider_dps[L] = 1000.0L + (outing_raiders[L].average_level * cast(double)(100 / BOSSES_PER_INSTANCE));	// add 100 DP/3S per readiness for a new instance

Where L is the loop index.

For average_level = 0:

hp = 10000.0L + (outing_raiders[L].average_level * cast(double)(1000 / BOSSES_PER_INSTANCE))
hp = 10000 + (0 * (1000 / 10))
hp = 10000

dps = 1000.0L + (outing_raiders[L].average_level * cast(double)(100 / BOSSES_PER_INSTANCE))
dps = 1000 + (0 * (100 / 10))
dps = 1000

For average_level = 0.5:

hp = 10000 + (0.5 * (1000 / 10))
hp = 10000 + 50
hp = 10050

dps = 1000 + (0.5 * (100 / 10))
dps = 1000 + 5
dps = 1005

So if I have one player who’s average is 0 and another who has gotten enough loot to get to 0.5, the latter player will do more damage and take more.

What about a system that distributes loot as follows:

  1. First pick - the armor item that gives a player the best upgrade goes to that player

  2. Second pick - the five attendees (out of 25) with the best attendance over the last N days get to vote among themselves, strict majority rule, which remaining piece of gear they’ll take from the drop. A “top five” comprised of a tank and four clothies might choose a cloth drop, and roll on it, with the assertion that as long as the tank keeps up his attendance, they’ll vote to take the plate next time and he’ll get it by default.

  3. Third, fourth, and fifth pick - a bidding scheme of some sort for the remaining drops.

You could reverse (1) and (2) if you wanted to reward attendance more, but you will be hurting players who can only make (e.g.) the Tuesday raid and not the Friday raid. Perhaps the player with the best attendance to a given event? That way the Friday raid would only count attendance to previous Friday raids, with other events serving as tie-breakers.

Just a thought.

You mean first/second/etc. as in if there are one/two/etc. items that drop on a boss, you first find which gives the greatest increase and use the “first pick” rule on it, let the top five choose which is the second, etc.?

I can implement it into the simulator if you want to see the results.

That’s what I was thinking of. With 25 players in an array, you could just run the “biggest benefit” function twice: once on the whole group, and once on the top 5. Of course now that I’ve phrased it that way, a third solution becomes apparent…

For N items that drop from the final boss, divide the party into N groups by attendance percentage, with the top group being half the size of the bottom group, and the middle group being sized between them*. Allocate one item to each of the N groups, and then run the best_benefit algorithm against those groups. This means that an individual in the top group has statistically twice the chances of getting a drop compared to an individual in the bottom group, but that gear will still be spread over the raid somewhat fairly.

For three drops, you’d divide the raid into 6,7, and 12 players. You can either give the top group first pick, or allocate the items in such a way as to maximize the sum of the benefits.

  • You can divide the 25 players any way you like, really; I picked this one because it gives a strict “2x” chance to the top players. 3x or 4x starts getting you into territory where your top group is fewer than 3 players and you might not get any drops that appeal to that group.