How to efficiently divide players into 4 team equally?

I was building a team assignment script for 4 team, each team has a equal number of player to the other team, atleast either got 1 more or 1 less. However, im stuck at the concept and math to divide it equally. Its been two days since im stuck on it that i probably got into the point that i want to ask. Can anyone help me out?

I know that this is some sort of unfavouring question, but i need help.

Create a list of players and fill each team using a for loop.

Pretty sure Roblox does this automatically when you’re using teams. But if you want to account for players leaving/new players joining, this developer docs post shows you how to balance them:

https://developer.roblox.com/en-us/api-reference/class/Teams
(scroll to the bottom)

The best way to do this is to just have 4 teams, and assign a player to the smallest team whenever they join the game. Here’s a solution I just made:

 -- server script (preferably in ServerScriptService)
local teamService = game:GetService("Teams")
local playerService = game:GetService("Players")

local possibleTeams = teamService:GetChildren()

function chooseTeam(player)
    local chosenTeam
    for _, team in pairs(possibleTeams) do
        if not chosenTeam or #team:GetPlayers() > #chosenTeam:GetPlayers() then
            chosenTeam = team
        end
    end
    return chosenTeam
end

playerService.PlayerAdded:Connect(function(player)
    player.Team = chooseTeam(player)
end)

Sorry for being innacurate. But i mean that to assign a team equally only at the start of the round. And when there’s a new player. They are neutral, not assigned to any team until next round. And i don’t have desire to balance the team at the mid round. Which is why auto assignable is off in my team.

By the way if anyone could tell me how to divide a table equally. That might solve my problem easily

So, the goal is to divide an array of players roughly equally into 4 parts. Below I’ve drawn a diagram of what this might look like.

The natural thing to do is to divide the array into quarters as shown with the green arrows. Unfortunately 4 here doesn’t evenly divide the 9 players. The next best thing to do would be to move each green arrow left a bit. The teams formed by using these shifted arrows are the zig-zag underlines. These will not differ by more than one because without the shift they are all equal. And with the shift between zero to one is subtracted from each.

Of course, we could have chosen any fractions (not just quarters) so this process would work dividing teams into however many parts.

Below I’ve attached some code which does the exact process I’ve described. I’ve generalised it to work for any number of teams; you’ll want to call makeTeams(4).

local function makeTeams(k)
	local players = game.Players:GetPlayers()
	local n = #players
	shuffle(players)

	local teams = {}
	for i = 1, k do
		local team = {}
		local lo = math.floor((i-1) * n / k) + 1
		local hi = math.floor(i * n / k) + 1
		for j = lo, hi-1 do
			table.insert(team, players[j])
		end
		table.insert(teams, team)
	end

	return teams
end

Here the variables lo and hi compute the positions of shifted arrows by multiplying n (the number of players) by the fraction. Rounding down is the shift operation.

You’ll note there’s a shuffle function which I’ve added. I’ve assumed you don’t want the same teams repeated over and over so to prevent this, I’ve randomly shuffled the players array before dividing. The algorithm I’ve lifted to implement this shuffle is the Fisher-Yates shuffle below.

local function shuffle(array)
	local n = #array
	for i = 1, n-1 do
		local j = math.random(i, n)
		array[i], array[j] = array[j], array[i]
	end
end

Thanks for reading and I hope this has helped!

8 Likes

So i tried your solution, with a little modification to fit in my script. And they work perfectly. Every player are inserted to a random team once the round started.

I already made my own shuffle script. However, your shuffle script seems shorter and more efficient. However, im going to use my own shuffle script since im comfortable with because it produce no error so far.

So the script went out like this

local function ShufflePlayer() -- This is my shuffle script!
	local OldPlayerList = Players:GetPlayers()
	local PickTable = {table.unpack(OldPlayerList)}
	local NewPlayerList = {}
	for iteration,player in pairs(PickTable) do
		local PickNumber = math.random(1,#OldPlayerList)
		NewPlayerList[#NewPlayerList+1] = OldPlayerList[PickNumber]
		table.remove(OldPlayerList,PickNumber)
	end
	return NewPlayerList
end
local function AssignTeam(TeamAmount,Length,PlayerList)
	local Teams = {}
	for i = 1, 4 do
			local Team = {}
			local lo = math.floor((i-1) * Length / TeamAmount) + 1
			local hi = math.floor(i * Length / TeamAmount) + 1
			for j = lo, hi-1 do
				table.insert(Team, PlayerList[j])
			end
			table.insert(Teams, Team)
	end
	return Teams
end

 local PlayerList = ShufflePlayer()
	local PlayerList2 = AssignTeam(4,#PlayerList,PlayerList)
	
	for i,Team in pairs(PlayerList2)do
		local TeamName = TeamList[i]
		for i,Player in pairs(PlayerList2[i]) do
			Players[Player.Name].Team = TeamName
		end
	end	

It worked really well. Thank you so much!