Heads up: This post will contain math.

So I'm trying to build an accuracy calculator for my rhythm project, but the equation to calculate the accuracy requires division by 0. In words, the equasion goes like this:

Find the average of the time between the keypress and the time they key was supposed to be pressed for every keypresses since the song began

Subtract the average by the true value

Divide that by the true value

Multiply by 100

This is all well and good, but in my case, the true value is 0, because a second difference of 0 seconds means the key was hit at the perfect time. But of course, you can't divide by 0, and doing so seems to result in math.huge.

How would I go about resolving this?

If you need my code, here you go:

local sounds = game.SoundService
local CAS = game.ContextActionService
local rep = game.ReplicatedStorage
local getLevel = require(rep.GetLevel)
local beat = 0
local multiplier = 1000
local active = {}
local secDifs = {}

local testLvl = getLevel.GetLevel('TestingLevel')

local selectedLvl = testLvl
local beatDur = 60 / selectedLvl['BPM']
local TPU = 1 / selectedLvl['Speed'] -- Short for Time per unit

local function ProcessKeyPress(color)
	print(color..' has been pressed!')
	
	local correctNote
	
	for i, v in ipairs(active) do
		if v[1] == color then
			correctNote = v
		end
	end
	
	if correctNote ~= nil then
 -- This is  the part to  focus on!  -- This is  the part to  focus on!  -- This is  the part to  focus on!

		print('Nice!')

		local secDif = math.ceil((math.abs(beat - correctNote[2]) * beatDur) * multiplier)
		
		table.insert(secDifs, secDif)
		
		local all = 0
		
		for i, v in ipairs(secDifs) do
			all += v
		end
		
		local average = all / #secDifs
		local accuracy = ((average - 0) / 0) * 100
		
		print(accuracy)
		
		rep.AddPoints:Fire(secDif)
		print(beat, correctNote[2], beat - correctNote[2],  math.abs(beat - correctNote[2]),  (math.abs(beat - correctNote[2]) * beatDur), ((math.abs(beat - correctNote[2]) * beatDur) * multiplier))
		print('beat: '..tostring(beat), 'correctNote[2]: '..tostring(correctNote[2]), 'secDif  before  math.ceil: '..tostring((math.abs(beat - correctNote[2]) * beatDur) * multiplier), 'secDif: ',  tostring(secDif))
	else
		print('Miss!')
	end
	
	local closest = workspace.Notes:GetChildren()[1]

	if closest ~= nil then
		for i, v in ipairs(workspace.Notes:GetChildren()) do
			if math.floor(math.abs(v.Position.X)) < math.floor(closest.Position.X) and v.Name == color then
				--closest.Color = Color3.new(1, 0, 0)
				
				closest = v
			end
		end

		if math.ceil(math.abs(closest.Position.X) - workspace.Strumline[color].Position.X) <= 3 then
			coroutine.resume(coroutine.create(function()
				closest.Color = Color3.new(1, 1, 0)
				
				--wait(0.3)
				
				closest:Destroy()
			end))
		end
	end
end

rep.TempStart.Event:Wait()
	
print('eyey')
	
local keybinds = {
	Red = nil,
	Blue = nil,
	Green = nil,
	White = nil
}

for i, keybind in ipairs(rep.Keybinds:GetChildren())  do
	for i, enum in ipairs(Enum.KeyCode:GetEnumItems()) do
		if string.sub(tostring(enum), 14, string.len(tostring(enum))) == keybind.Value then
			keybinds[keybind.Name] = enum
		end
	end
end

selectedLvl['Song']:Play()

CAS:BindAction('Press red', function(nagareteku, instate, toki, no, naka, de, demo)
	if instate ~= Enum.UserInputState.Begin then return end
	
	ProcessKeyPress('Red')
end, false, keybinds['Red'])
CAS:BindAction('Press blue', function(nagareteku, instate, toki, no, naka, de, demo)
	if instate ~= Enum.UserInputState.Begin then return end

	ProcessKeyPress('Blue')
end, false, keybinds['Blue'])
CAS:BindAction('Press Green', function(nagareteku, instate, toki, no, naka, de, demo)
	if instate ~= Enum.UserInputState.Begin then return end

	ProcessKeyPress('Green')
end, false, keybinds['Green'])
CAS:BindAction('Press white', function(nagareteku, instate, toki, no, naka, de, demo)
	if instate ~= Enum.UserInputState.Begin then return end

	ProcessKeyPress('White')
end, false, keybinds['White'])

local stTick = tick()
local permT = tick()

game:GetService('RunService').Heartbeat:Connect(function()
	beat += math.floor(((tick() - stTick) / beatDur) * 1000) / 1000 -- Reduce time to 3 decimals
	stTick = tick()
end)

for i, v in ipairs(selectedLvl['Chart']) do
	local activateKeyPress = coroutine.create(function()
		print(v[1]..' wait duration: '..tostring((v[2] * beatDur) - TPU * 3), 'unactivate wait duration: '..tostring(((v[2] * beatDur) - TPU * 3) + (TPU * 6)))

		wait((v[2] * beatDur) - TPU * 3)
				
		table.insert(active, v)
		
		print(v[1]..' active')
		
		wait(TPU * 6)
		
		print(v[1]..' not active')
		
		for I, V in ipairs(active) do
			if v == V then
				rep.AddPoints:Fire(-25)
				
				table.remove(active, I)
			end
		end
	end)
	
	local createKeys = coroutine.create(function()
		wait((v[2] * beatDur) - (TPU * 20))
		
		local keyClone = workspace.Strumline[v[1]]:Clone()
		keyClone.Parent = workspace.Notes
		keyClone.Transparency = 0
		
		print(workspace.Strumline[v[1]])
		
		keyClone.Position = workspace.Strumline[v[1]].Position - Vector3.new(20, 0, 0)
		
		local tween = game.TweenService:Create(keyClone, TweenInfo.new(
			TPU * 40,
			Enum.EasingStyle.Linear,
			Enum.EasingDirection.InOut
		), {Position = workspace.Strumline[v[1]].Position + Vector3.new(20, 0, 0)})
		
		tween:Play()
	end)
	
	coroutine.resume(activateKeyPress)
	coroutine.resume(createKeys)
end

If you need the module script code and the bindable event code, here you also go:

local levels = {
	TestingLevel = {
		Song = game.SoundService.Tem,
		Speed = 32, --  32 units per second, or 32u/s
		BPM = 120,
		Chart = { -- Every table in the Chart represents a note. Properties: string button, num beat, num beatToReleaseAt
			{'Red',  10},
			{'Green',  11},
			{'Blue',  12},
			{'White',  13},
			{'Blue',  14},
			{'Green',  15},
			{'Red',  16, 21},
			{'Green',  17},
			{'Blue',  18},
			{'White',  19},
			{'Blue',  20},
			{'Green',  21},
			{'Red',  22, 27},
			{'Green',  24},
			{'Blue',  25},
			{'White',  26},
			{'Blue',  27},
			{'Green',  28},
			{'Red',  29, 34},
			{'Green',  30},
			{'Blue',  31},
			{'White',  32},
			{'Blue',  33},
			{'Green',  34},
			{'Red',  35, 45}
		}
	}
}
local module = {}

module.GetLevel = function(name)
	return levels[name]
end

return module

e

local rep = game.ReplicatedStorage
local frame = script.Parent
local points = 0

rep.AddPoints.Event:Connect(function(pointsToAdd)
	points += pointsToAdd
	
	frame.Score.Text = 'Points: '..tostring(points)
	
	print(points)
end)

rep.ChangeAccuracy.Event:Connect(function(accuracy)   -- Currently unused
	frame.Accuracy = tostring(accuracy)..'%'
end)

rep.TempStart.Event:Connect(function()
	script.Parent.Visible = true
end)

Anything is appreciated!

4 comments

0

Just answering blindly here; cant you just use an id statement? If its 0 then make it 1 or something so then when it multiplies, it gives the highest score.

0

The issue here is not whether the players pressed the key with a second difference of 0. No matter the difference, the equation requires division by 0 unless I change something about the equasion.

0

Then change the equation. If your not smart enough to do it the first time, you will figure it out eventually.

0

How can I change the equation to still provide an accurate accuracy if the true value is 0?