In a map voting system, how would you handle tiebreakers?

For example, if Map1 and Map2 tie in votes, how would you handle it?

I’m just curious because I’m planning on implementing a map voting system in an upcoming game.

2 Likes

I just pick a random one out of the two that have equal votes

1 Like

Pick the one that reached the tie first. For instance, if map A reaches 4 votes and then map B reaches 4 votes, map A should be picked. Randomly selecting a map isn’t really a great solution.

6 Likes

Picking one of the tied ones at random is the traditional way to handle it. Depending on your game, it might fit to have a new tiebreaker vote with just the tied maps (if there was other maps in the first vote).

2 Likes

Doesn’t that just favor the faster clickers? What’s the actual benefit to this? Either way 50% are bummed out.

1 Like

Galaxy brain: select Map 3 instead

16 Likes

Don’t put too much thought into it, chances are your players aren’t going to care much about losing a tiebreaker.

I’d recommend just using RNG to select one of the tied maps. Its the most unbiased and simple way to do such.

2 Likes

There’s never a reason to have tie breakers in the first place. Players often will force themselves to play maps they know instead of maps that are new and fun; instead of picking the map with the most votes, pick a random vote and have that be the map; so that the maps with the more votes will probably win; but the other maps with less votes can win

4 Likes

Or you could throw them in for a spin and pick Map 4 too. That’d be the most unbiased since it doesn’t account for any of the votes whatsoever, and is entirely up to the system instead.

To put it in your realm, its like seeing a player go to a level 1 area to fight level 1 monsters, then spawning in a level 50 monsters to “throw them in for a spin” and then they freak out and die.

Personally, I would say go with map 5 instead, map 4 is way too old.

1 Like

It would only spawn the level 50 monsters if there happened to be a tiebreaker in the level select of the area which shouldn’t happen at all. Knowing me, though, this is definitely something that would occur.

In which case I would actually go as far to say that selecting map 6 is the best option at this point. Thanks for the input!

1 Like

I dont even worry about this, script should still work but would probably always pick the first/last map in the table if theres a tie.

1 Like

I believe this is the best solution. Maps with more votes have a higher probability of selection, but never a definite one like most games program it. This also solves any issues with tie breakers, as tied results aren’t seen any differently than other results.

Consider Map1, Map2, and Map3.

  • Map1 receives 3 votes,
  • Map2 receives 4 votes, and
  • Map3 receives 1 votes

for a total of 8 votes. Map1 has 3 / 8 votes, so it has a 37.5% probability of being selected. Map2 has a 50% selection probability, and Map3 12.5%.

So how do we express this in Lua? The original voting table is as such:

local Votes = {
	Map1 = 3, 
	Map2 = 4, 
	Map3 = 1
} 

For the random probability selection, I’m sure someone’s made a fancy formula for it someplace. I couldn’t think of one, so I made my own solution using arrays. We create an array VotingIncidenceArray = {} with 8 indices, using a distribution of values equal to the percentages mentioned before. These values will be ordered randomly among the indices afterward. To get there though, we must first make a regular old array VotingIncidenceArray = {}

To insert the maps, a temporary table is first shallow-copied from the original voting table. We then make a generic for loop to fill array at the end of the last paragraph (actual code at the bottom of this post):

for i = 1, 8 do

end

On each iteration, we select a map and then subtract the selected map’s respective voting value by 1, removing the map from the temporary table when the voting value reaches 0.

For my example, I inserted the maps in the original map order, like so (indices visible for readability):

{
	[1] = "Map1",
	[2] = "Map1",
	[3] = "Map1",
	[4] = "Map2",
	[5] = "Map2",
	[6] = "Map2", 
	[7] = "Map2",
	[8] = "Map3"
}

I then randomized the array using a very old script by stickmasterluke:
(Ignore this code, as it’s unnecessary)

{
	[1] = "Map2",
	[2] = "Map1",
	[3] = "Map1",
	[4] = "Map1",
	[5] = "Map2",
	[6] = "Map3", 
	[7] = "Map2",
	[8] = "Map2"
}

And there you have it. Because of the randomization, any index can be used to select a map for the final voting value. Personally I would use [1]. Now that the incidences of each map are distributed properly, you can select a random map. See the very bottom of the below code for the random map selection.

The code:

local function ShallowCopy(Table)
	local Copy = {}
	for k,v in pairs(Table) do
		Copy[k] = typeof(v) == "table" and ShallowCopy(v) or v
	end
	return Copy
end

local function GetFirstIndex(Table)
	local FirstIndex 
	for Index in pairs(Table) do 
		FirstIndex=Index 
		break 
	end 
	return FirstIndex
end

-- End functions
-- Begin code

local Votes = {
	Map1 = 3, 
	Map2 = 4, 
	Map3 = 1
} 

local TempVotes = ShallowCopy(Votes)
local Total = 0
for _,Num in pairs(TempVotes) do
	Total = Total + Num
end

local VotingIncidenceArray = {}

for i = 1, Total do
	local Index = GetFirstIndex(TempVotes)
	-- Index = string representing a map
	if TempVotes[Index] > 0 then
		table.insert(VotingIncidenceArray, Index)
		TempVotes[Index] = TempVotes[Index] - 1
		if TempVotes[Index] == 0 then
			TempVotes[Index] = nil
		end
	end
end

local RandomMap = VotingIncidenceArray[math.random(1, #VotingIncidenceArray)]
4 Likes

I agree that it’s the best voting solution, but I think some of your code could be improved.

Your map list is randomized via math.random, and then you take the first index.
This results in an index off of a math.random, with O(N) of N being the number of votes.

You’d get the same results if, instead of randomizing the array, you just used a random index instead.
Still gives a random map, but that’s O(1) because the number of votes doesn’t change the work.

Besides having better big O, it’s actually faster on a practical level, since you call math.random only once instead of N times.

To improve your code, you only need to change the last 2 lines.

-- Instead of randomizing array order, we randomize array index
local RandomMap = VotingIncidenceArray[math.random(#VotingIncidenceArray)]
2 Likes

Good catch. For some reason that I’ve since forgotten, I had consciously chosen in my head to not do this when I wrote my post. This is the more straight-forward solution. I will edit my post to point out the change.

By the way, you made a typo in your code. You typed math.random(#VotingIncidenceArray) when the parameters should be a range, starting with 1.

1 Like

Nope, not a typo. It defaults to (1,X) if you leave only give (X)
You can prove this to yourself by running this code:

for i=1,50 do
	print(math.random(10))
end

You’ll see it prints numbers from 1-10.

3 Likes