Stress Systems - Creating Stress Tracking Model

Hello guys! Today, I will be showing you on how to create a stress-tracking model to determine the players’ stress level.

Currently, you need these to produce the model:

  • Any KalmanFilter model

  • A player data that is stored in matrix

Setting Up

Before we train our model, we will first need to construct a regression model as shown below.


local DataPredict = require(DataPredict)

local StressTrackingModel = DataPredict.Models.KalmanFilter.new({})

The Feature Matrix


-- We're just adding 1 here to add "bias".

local playerDataVector = {
    {
        1,
		timeSinceLastInput,
		numberOfConsecutiveInputs,
        actionsPerMinute,
        effectiveActionsPerMinute,
    }
}

Full Setup


local maximumStressScore = 100 -- This must be adjusted based on your data and your environment.
local adaptiveRate = 0.01 -- How fast thresholds adapt (lower = more stable).
local rollingCostRate = 0.9
local rollingCostRateComplement = 0.1
local numberOfSecondsToResetStressWarning = 60

local function onPlayerConnect(Player: Player)
	
	local isStressDetected = false
	local stressScore = 0
	local rollingCost = 0
	local adaptiveMean = 0
	local adaptiveVariance = 0
	local timeSinceLastWarned = 0
	
	local previousStateVector
	local currentStateVector
	local isIdle
	local costArray
	local cost
	local valueDifference
	local standardDeviationValue
	local lowerBoundRollingCostThreshold
	local upperBoundRollingCostThreshold
	local deviationValue

	InputRemoteEvent.OnClientEvent:Connect(function(Player)
		
		currentStateVector = getStateVector(Player, previousStateVector)
		isIdle = checkIfIsIdle(previousStateVector, currentStateVector)

		-- Fixed variable name
		costArray = StressTrackingModel:train(previousStateVector, currentStateVector)
		
		previousStateVector = currentStateVector

		cost = costArray[1]
		
		-- Exponential smoothing
		rollingCost = (rollingCostRate * rollingCost) + (rollingCostRateComplement * cost)
		
		valueDifference = rollingCost - adaptiveMean
		
		adaptiveMean = adaptiveMean + (adaptiveRate * valueDifference)
		adaptiveVariance = (1 - adaptiveRate) * (adaptiveVariance + (adaptiveRate * math.pow(valueDifference, 2)))

		standardDeviationValue = math.sqrt(adaptiveVariance)
		
		lowerBoundRollingCostThreshold = adaptiveMean - (3 * standardDeviationValue)
		upperBoundRollingCostThreshold = adaptiveMean + (3 * standardDeviationValue)
		
		deviationValue = 0
		
		if (isIdle) then
			stressScore = math.max(0, stressScore - 1)
		elseif (rollingCost < lowerBoundRollingCostThreshold) then
			deviationValue = (lowerBoundRollingCostThreshold - rollingCost)
		elseif (rollingCost > upperBoundRollingCostThreshold) then
			deviationValue = (rollingCost - upperBoundRollingCostThreshold)
		else
			stressScore = math.max(0, stressScore - 1)
		end
		
		stressScore = stressScore + (deviationValue * 0.1)
		
		-- Fixed decay logic: Constant decay
		stressScore = math.max(0, stressScore - 0.05)
		
		isStressDetected = (stressScore >= maximumStressScore)
		
		if (isStressDetected) and (timeSinceLastWarned <= 0) then
			timeSinceLastWarned = numberOfSecondsToResetStressWarning
			warn(warningString:format(Player.Name, stressScore, rollingCost, cost))
		else
			timeSinceLastWarned = math.max(timeSinceLastWarned - delta, 0)
		end
	
	end)
	
end


This site uses Just the Docs, a documentation theme for Jekyll.