Skip to content

Traffic Lights

A simple example demonstrating basic state machine concepts with a traffic light controller.

Basic Traffic Light

flow
machine: @traffic_light

scenario: normal operation

  given:
    traffic_light is in #red

  on :timer_expired from @system
    ? traffic_light is in #red
      traffic_light moves to #green
      start timer for 30 seconds

    ? traffic_light is in #green
      traffic_light moves to #yellow
      start timer for 5 seconds

    ? traffic_light is in #yellow
      traffic_light moves to #red
      start timer for 30 seconds

  expect:
    = traffic light cycles correctly

With Pedestrian Crossing

flow
machine: @traffic_light

scenario: pedestrian crossing

  given:
    traffic_light is in #red
    $pedestrian_waiting: boolean is false

  on :pedestrian_button_pressed from @pedestrian (api)
    $pedestrian_waiting becomes true

    ? traffic_light is in #green
      start shortened green timer

  on :timer_expired from @system
    ? traffic_light is in #red
      ? $pedestrian_waiting
        traffic_light moves to #pedestrian_crossing
        start timer for 20 seconds
        $pedestrian_waiting becomes false
      otherwise
        traffic_light moves to #green
        start timer for 30 seconds

    ? traffic_light is in #pedestrian_crossing
      traffic_light moves to #green
      start timer for 30 seconds

    ? traffic_light is in #green
      traffic_light moves to #yellow
      start timer for 5 seconds

    ? traffic_light is in #yellow
      traffic_light moves to #red
      start timer for 30 seconds

  expect:
    = pedestrians can cross safely

Intersection Controller

flow
system: intersection

machine: @north_south_light

  on :switch_to_green from @controller
    light moves to #green
    start timer for 30 seconds

  on :timer_expired from @system
    ? light is in #green
      light moves to #yellow
      start timer for 5 seconds

    ? light is in #yellow
      light moves to #red
      emit :ns_is_red to @controller

machine: @east_west_light

  on :switch_to_green from @controller
    light moves to #green
    start timer for 30 seconds

  on :timer_expired from @system
    ? light is in #green
      light moves to #yellow
      start timer for 5 seconds

    ? light is in #yellow
      light moves to #red
      emit :ew_is_red to @controller

machine: @controller

  scenario: coordinate lights

    given:
      @north_south_light is in #green
      @east_west_light is in #red

    on :ns_is_red from @north_south_light
      // Wait 2 seconds for safety
      wait 2 seconds
      emit :switch_to_green to @east_west_light

    on :ew_is_red from @east_west_light
      // Wait 2 seconds for safety
      wait 2 seconds
      emit :switch_to_green to @north_south_light

    expect:
      = only one direction is green at a time

State Diagram

@traffic_light#red:timer#green:timer#yellow:timerCycle: #red → #green → #yellow → #red

With Pedestrian State

@traffic_light#red:timer#green[pedestrian waiting]#pedestrian_crossing:timer#yellow:timer:timer[back to #red]

Lane Diagram (Intersection)

@controller@north_south_light@east_west_light#green#red:timer#yellow:timer#red:ns_is_red[wait 2s]:switch_to_green#green

Testing Scenarios

Normal Cycle

flow
scenario: complete cycle

  given:
    traffic_light is in #red

  on :timer_expired from @system
    traffic_light moves to #green

  on :timer_expired from @system
    traffic_light moves to #yellow

  on :timer_expired from @system
    traffic_light moves to #red

  expect:
    = traffic_light is in #red
    = cycle completed successfully

Pedestrian Interruption

flow
scenario: pedestrian requests crossing

  given:
    traffic_light is in #green
    $pedestrian_waiting is false

  on :pedestrian_button_pressed from @pedestrian (api)
    $pedestrian_waiting becomes true

  on :timer_expired from @system
    traffic_light moves to #yellow

  on :timer_expired from @system
    traffic_light moves to #red

  on :timer_expired from @system
    ? $pedestrian_waiting
      traffic_light moves to #pedestrian_crossing

  expect:
    = traffic_light is in #pedestrian_crossing
    = $pedestrian_waiting is false

Intersection Safety

flow
scenario: lights never both green

  given:
    @north_south_light is in #green
    @east_west_light is in #red

  on :timer_expired from @system
    @north_south_light moves to #yellow

  on :timer_expired from @system
    @north_south_light moves to #red

  on :ns_is_red from @north_south_light
    // Controller waits, then switches

  on :switch_to_green from @controller
    @east_west_light moves to #green

  expect:
    = @north_south_light is in #red
    = @east_west_light is in #green
    = never both green simultaneously

Emergency Mode with Broadcasting

When an emergency occurs, all traffic lights need to stop immediately. This uses the broadcast feature to notify all instances:

flow
machine: @emergency_controller

scenario: emergency override

  on :emergency_detected from @sensor (api)
    emit :emergency_stop to all @traffic_light
    controller moves to #emergency_mode

  on :emergency_cleared from @operator (api)
    emit :resume_normal to all @traffic_light in #emergency_stop
    controller moves to #normal

machine: @traffic_light

  on :emergency_stop from @emergency_controller
    traffic_light moves to #emergency_stop
    flash red light

  on :resume_normal from @emergency_controller
    traffic_light moves to #red
    start timer for 30 seconds

The emit :event to all @machine syntax broadcasts to every instance. Adding in #state filters recipients to only those in the specified state.

Key Concepts Demonstrated

  1. Simple state transitions - Traffic light cycles through states
  2. Timer-based triggers - State changes on timer expiration
  3. Guard conditions - Check current state before transitioning
  4. Multi-machine coordination - Controller synchronizes lights
  5. Safety invariants - Ensure conflicting states never occur simultaneously
  6. Broadcasting events - emit :event to all @machine for emergency overrides

Released under the MIT License.