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.
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.
I just pick a random one out of the two that have equal votes
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.
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).
Doesn’t that just favor the faster clickers? What’s the actual benefit to this? Either way 50% are bummed out.
Galaxy brain: select Map 3 instead
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.
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
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.
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!
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.
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.
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)]
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)]
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.
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.