Grading system for PVE wave survival game

I am planning on making a grading system for my upcoming PVE game, but it’s not yet executed due to an uncertain idea I made up for it. So I’m going to describe it before asking some questions.

Grading (or Ranking) is a game mechanic to judge players’ performances, and give them rewards based on rank scale (from F to A).

In my game, this mechanic is simple. All it has to do is to calculate player’s total kills, total assists, and percentage of damage taken at the end of the current wave. Each wave has a different amount of mobs so ranking requirements would be varied (scaled).
In the boss wave case, it’s different, kills and assists are replaced with the percentage of damage done. Instead of being removed, they are bonuses for this type of wave.

For overall rank in a wave, I can sum all players’ performance scores and divide them by amount of players, then round it

That’s all I can tell. But again, I’m still unsure about it. So what’s your thought about this? Any suggestions to improve/optimize this mechanic to make it become fair for both casual and skillful players?

2 Likes

You should factor in some things that help other players. For example while a boss is targeting a player, their efficiency in avoiding damage should be considered, because it means other players are getting the chance to deal damage for free.

What you have gotten there is a pretty solid ranking system. How I would go about this is to create a dictionary table in a script that would run at the end of the game, and the table would look somewhat like this:

local rankings = {
RankA = 5 -- idk how hard your game is so 50 kills average is going to be our A rank, the numbers are small for a reason
RankB = 4
RankC = 3
RankD = 2
RankE = 1
RankF = 0

It’s totally possible that a player will get somewhere in between those numbers, so we can do this:

local rankings = {
["A"] = 5
["B"] = 4
["C"] = 3
["D"] = 2
["E"] = 1
["F"] = 0 -- I may have screwed up on the square brackets, can't remember if you're supposed to use them when the name of a value is a string
-- I didn't define player, make sure you do that before this step as well as some sort of tracking system to track kills and assists
for rank, kills in pairs(rankings) do -- loops through the ranking list
    if player.Humanoid.Kills / 10 == kills then -- implying that you have already created a NumberValue in the Humanoid of a player's character that records the kills
        local rank = Instance.new("StringValue") -- you could also use an Attribute for this, but in my experience Attributes rarely work
        rank.Value = rank
        rank.Name = "Rank"
        rank.Parent = player.Humanoid
    elseif  math.floor((player.Humanoid.Kills / 10) + 0.5)-- this is why the rankings are small; I'll explain after
        local rank = Instance.new("StringValue")
        rank.Value = rank
        rank.Name = "Rank"
        rank.Parent = player.Humanoid

math.floor() is a rounding function used to round integers to the nearest whole number. If we use 23 kills as an example, 23 is already a whole number; it needs to be converted to a number with a decimal place, hence dividing by 10. Adding 0.5 is necessary to make rounding easier for math.floor(), which rounds down no matter what. Somebody with 29 kills would be given a rank of D instead of C, because 2.9 rounds down to 2 when using math.floor(). Using math.floor() after adding 0.5 would be appropriate because 3.4 would be rounded to 3. I originally tried this with Rank A being 100 kills, but increments of 20 did not work very well.

Hope this helps! The code is kind of shabby, I wrote it just now and I did not test to see if it worked.

Hey, thank you for your advice. However, I don’t think that would solve the problem regarding the balance between casual and pro players. So I’ve recently implemented grading system, this time it will calculate the percentage instead of points. So here we are

We have two arrays to summarize, one is requirements, and one is player information

--Table of rank requirements
local ranks = {
	["S"] = {min = 1, max = math.huge},
	["A"] = {min = 0.7, max = 1},
	["B"] = {min = 0.4, max = 0.7},
	["C"] = {min = 0.2, max = 0.4},
	["D"] = {min = 0.1, max = 0.2},
	["E"] = {min = 0, max = 0.1},
}

--Table of player summaries (IN WAVE)
local players = {
	{name = "JOY", currentKills = 6, currentAssists = 0, lessThan20Damage = true},
	{name = "GLENN", currentKills = 6, currentAssists = 0, lessThan20Damage = true},
	{name = "JOHN", currentKills = 9, currentAssists = 0, lessThan20Damage = true},
	{name = "DOE", currentKills = 9, currentAssists = 0, lessThan20Damage = true},
}

Next, we calculate the percentage in each player and declare their personal rank. We also store player’s score for future reference

local function Round(n, decimals)
	decimals = decimals or 0
	return math.floor(n * 10^decimals) / 10^decimals
end

print("===============")

local waveMobs = 30 --How many mobs in current wave?
local trueSum = 0

for i, v in pairs(players) do
	local noMissBonus = (v.lessThan20Damage and 0.15 or 0) --Increase percentage by 0.15 if u dont take more than 20% damage
	local sum = v.currentKills + (v.currentAssists * 0.5) --Halve assist count
	local percentage = Round((sum / waveMobs) + noMissBonus, 3)
	local rank = "F"
	for ii, vv in pairs(ranks) do
		if percentage >= vv.min and percentage < vv.max then
			rank = ii
		end
	end
	--Print personal rank info
	print("name: "..v.name.." - score: "..percentage.." - no miss: "..(v.lessThan20Damage and "YES" or "NO").." - personal rank: "..rank)
	trueSum += percentage --For future reference
end

Lastly, we accumulate percentages from all players and then average to get overall rank

local truePercentage = Round(trueSum / #players, 3) --Divide by numbers of players
local trueRank = "F"
for i, v in pairs(ranks) do
	if truePercentage >= v.min and truePercentage < v.max then
		truePercentage = i
	end
end

print("===============")

--Accumulate percentages from all players and then average to get overall rank
print("wave score: "..truePercentage.." - overall rank: "..trueRank)

And we finally have results…

There’s still one problem though, all players get having fair amounts of kills still get lower rank. This can be solved by reducing requirements. But for me, I’m uncertain.

If you have any ideas, don’t hesitate to ask or answer me

Edit: I marked this reply as a solution so people can use it as a reference.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.