Skip to content

Instantly share code, notes, and snippets.

@dreasgrech
Last active November 27, 2025 15:50
Show Gist options
  • Select an option

  • Save dreasgrech/54953481e5ed4d9b7e92698ea1e116be to your computer and use it in GitHub Desktop.

Select an option

Save dreasgrech/54953481e5ed4d9b7e92698ea1e116be to your computer and use it in GitHub Desktop.
A simple Finite State Machine (FSM) implementation of a trivial Red->Yellow->Green traffic lights system in lua for Assetto Corsa
--
-- This code demonstrates a simple Finite State Machine (FSM) implementation of a trivial Red->Yellow->Green traffic lights system.
-- https://github.com/dreasgrech
--
---@enum COUNTDOWN_TIMER_STATE
local COUNTDOWN_TIMER_STATE = {
Inactive = 0,
Red = 1,
Yellow = 2,
Green = 3
}
local COUNTDOWN_TIMER_GAP_BETWEEN_STATES_SECONDS = 2.0
local countdownTimerState = COUNTDOWN_TIMER_STATE.Inactive
local countdownTimerCurrentStateEnterTimeSeconds = 0.0
---The statemachine that's updated every frame which determines if it's time to move to a new state
---@type table<COUNTDOWN_TIMER_STATE, fun(currentTimeSeconds: number)>
local countdownTimerStateMachine = {
[COUNTDOWN_TIMER_STATE.Inactive] = function(currentTimeSeconds)
end,
[COUNTDOWN_TIMER_STATE.Red] = function(currentTimeSeconds)
if currentTimeSeconds - countdownTimerCurrentStateEnterTimeSeconds >= COUNTDOWN_TIMER_GAP_BETWEEN_STATES_SECONDS then
ac.log(string.format('Countdown Timer State changing to Yellow. currentTimeSeconds: %f, stateEnterTime: %f', currentTimeSeconds, countdownTimerCurrentStateEnterTimeSeconds))
countdownTimerState = COUNTDOWN_TIMER_STATE.Yellow
countdownTimerCurrentStateEnterTimeSeconds = currentTimeSeconds
end
end,
[COUNTDOWN_TIMER_STATE.Yellow] = function(currentTimeSeconds)
if currentTimeSeconds - countdownTimerCurrentStateEnterTimeSeconds >= COUNTDOWN_TIMER_GAP_BETWEEN_STATES_SECONDS then
ac.log(string.format('Countdown Timer State changing to Green. currentTimeSeconds: %f, stateEnterTime: %f', currentTimeSeconds, countdownTimerCurrentStateEnterTimeSeconds))
countdownTimerState = COUNTDOWN_TIMER_STATE.Green
countdownTimerCurrentStateEnterTimeSeconds = currentTimeSeconds
end
end,
[COUNTDOWN_TIMER_STATE.Green] = function(currentTimeSeconds)
if currentTimeSeconds - countdownTimerCurrentStateEnterTimeSeconds >= COUNTDOWN_TIMER_GAP_BETWEEN_STATES_SECONDS then
ac.log(string.format('Countdown Timer State changing to Inactive. currentTimeSeconds: %f, stateEnterTime: %f', currentTimeSeconds, countdownTimerCurrentStateEnterTimeSeconds))
countdownTimerState = COUNTDOWN_TIMER_STATE.Inactive
countdownTimerCurrentStateEnterTimeSeconds = currentTimeSeconds
end
end
}
---The statemachine that's called during the render pass which draws the timer circle depending on the current state
---@type table<COUNTDOWN_TIMER_STATE, fun()>
local countdownTimerStateMachine_UI = {
[COUNTDOWN_TIMER_STATE.Inactive] = function()
end,
[COUNTDOWN_TIMER_STATE.Red] = function()
local windowSize = ui.windowSize()
ui.drawCircleFilled(vec2(windowSize.x*0.5, windowSize.y*0.25), 100, rgbm(1, 0, 0, 1), 24)
end,
[COUNTDOWN_TIMER_STATE.Yellow] = function()
local windowSize = ui.windowSize()
ui.drawCircleFilled(vec2(windowSize.x*0.5, windowSize.y*0.5), 100, rgbm(1, 1, 0, 1), 24)
end,
[COUNTDOWN_TIMER_STATE.Green] = function()
local windowSize = ui.windowSize()
ui.drawCircleFilled(vec2(windowSize.x*0.5, windowSize.y*0.75), 100, rgbm(0, 1, 0, 1), 24)
end
}
script.update = function(dt)
local currentTimeSeconds = ac.getSim().time * 0.001
local stateMachineFunction = countdownTimerStateMachine[countdownTimerState]
stateMachineFunction(currentTimeSeconds)
end
script.drawUI = function()
local stateMachineFunction = countdownTimerStateMachine_UI[countdownTimerState]
stateMachineFunction(currentTimeSeconds)
end
--- The function which starts the countdown timer, by setting the current state to Red
local startCountdownTimer = function()
countdownTimerState = COUNTDOWN_TIMER_STATE.Red
local currentTimeSeconds = ac.getSim().time * 0.001
ac.log(string.format('Starting Countdown Timer at time: %f seconds', currentTimeSeconds))
countdownTimerCurrentStateEnterTimeSeconds = currentTimeSeconds
countdownTimerStartTime = currentTimeSeconds
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment