Last active
November 27, 2025 15:50
-
-
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- | |
| -- 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