Examples

Simulator SDK Demo

Build domain-specific simulators rapidly with the Simulator SDK.

Minimal Configuration

Implement a simulator with just 2 callback functions:

// my_simulator.c
#include "lcvm_sim.h"

typedef struct {
    double robot_x, robot_y;
    double target_x, target_y;
} RobotState;

// FFI: Set robot target position
static void ffi_set_target(LcvmState *vm) {
    double y = lcvm_pop_double(vm);
    double x = lcvm_pop_double(vm);
    RobotState *state = (RobotState *)vm->user_data;
    state->target_x = x;
    state->target_y = y;
}

// FFI: Get current X position
static void ffi_get_position_x(LcvmState *vm) {
    RobotState *state = (RobotState *)vm->user_data;
    lcvm_push_double(vm, state->robot_x);
}

// FFI: Get current Y position
static void ffi_get_position_y(LcvmState *vm) {
    RobotState *state = (RobotState *)vm->user_data;
    lcvm_push_double(vm, state->robot_y);
}

// Initialization callback
void on_init(LcvmState *vm, void *user) {
    lcvm_register_function_ex("set_target",     ffi_set_target,     2, "dd");
    lcvm_register_function_ex("get_position_x", ffi_get_position_x, 0, "");
    lcvm_register_function_ex("get_position_y", ffi_get_position_y, 0, "");
}

// Frame update callback
void on_frame(LcvmState *vm, float dt, void *user) {
    RobotState *state = (RobotState *)user;

    // Simple movement simulation
    double dx = state->target_x - state->robot_x;
    double dy = state->target_y - state->robot_y;
    state->robot_x += dx * dt * 0.5;
    state->robot_y += dy * dt * 0.5;

    // Rendering (RayLib, etc.)
    // draw_robot(state->robot_x, state->robot_y);
}

int main() {
    RobotState state = {0, 0, 0, 0};

    lcvm_sim_config_t config = {
        .script_path       = "robot.c",
        .enable_hot_reload = 1,
        .on_init           = on_init,
        .on_frame          = on_frame,
        .user_data         = &state,
    };

    lcvm_sim_ctx_t *sim = lcvm_sim_create(&config);
    if (!sim) return 1;

    while (!should_quit()) {
        lcvm_sim_update(sim, get_frame_dt());
    }
    lcvm_sim_destroy(sim);
    return 0;
}

Control Script

Lambda C is a C subset and does not support multi-value returns, so position retrieval is split per axis.

// robot.c
void   set_target(double x, double y);
double get_position_x();
double get_position_y();

int main() {
    // Set target position
    set_target(10.0, 5.0);

    // Wait until arrival
    while (1) {
        double x = get_position_x();
        double y = get_position_y();

        double dx = 10.0 - x;
        double dy = 5.0 - y;
        if (dx * dx + dy * dy < 0.01) break;  // Arrival check
    }
    return 0;
}

Drone Delivery Demo

The Lambda C package includes a complete drone delivery simulation (demos/raylib_drone/) controlled by Lambda C scripts: pick up at point A, deliver to B, return to A. Implemented as an 11-state mission machine with proportional altitude/position control. Press R to hot-swap between two route scripts — round-trip delivery.c (A→B→A) and patrol patrol.c (A→B→C→A) — while the drone is in flight.

The design philosophy, the script-vs-host responsibility split, the relationship to the Simulator SDK, and full build/run instructions live on the Drone Demo page.


Function Pointers

Callback patterns using function pointers:

// callback.c

// Function pointer type
typedef int (*operation_t)(int, int);

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }

// Function taking function pointer as argument
int apply(operation_t op, int x, int y) {
    return op(x, y);
}

int main() {
    int result;

    result = apply(add, 10, 5);  // 15
    printf("add: %d\n", result);

    result = apply(sub, 10, 5);  // 5
    printf("sub: %d\n", result);

    result = apply(mul, 10, 5);  // 50
    printf("mul: %d\n", result);

    return 0;
}

State Transition Table

// state_table.c
typedef void (*state_handler_t)();

void state_idle()   { printf("IDLE\n"); }
void state_run()    { printf("RUN\n"); }
void state_stop()   { printf("STOP\n"); }

state_handler_t handlers[3] = { state_idle, state_run, state_stop };

int main() {
    int state = 0;
    int i;

    for (i = 0; i < 3; i++) {
        handlers[state]();
        state = state + 1;
    }

    return 0;
}

Enum and Union

Enum (Enumeration)

// enum_example.c

// Auto-increment
enum Color {
    RED,      // 0
    GREEN,    // 1
    BLUE      // 2
};

// Explicit values
enum Status {
    OK = 0,
    ERROR = -1,
    TIMEOUT = -2
};

int main() {
    enum Color c = GREEN;
    enum Status s = OK;

    printf("Color: %d\n", c);   // 1
    printf("Status: %d\n", s);  // 0

    return 0;
}

Union

// union_example.c

union Value {
    int i;
    double d;
    char *s;
};

struct Variant {
    int type;  // 0=int, 1=double, 2=string
    union Value val;
};

void print_variant(struct Variant *v) {
    switch (v->type) {
        case 0: printf("int: %d\n", v->val.i); break;
        case 1: printf("double: %f\n", v->val.d); break;
        case 2: printf("string: %s\n", v->val.s); break;
    }
}

int main() {
    struct Variant v;

    v.type = 0;
    v.val.i = 42;
    print_variant(&v);

    v.type = 1;
    v.val.d = 3.14;
    print_variant(&v);

    return 0;
}

Heap Watermark Usage

Efficient temporary memory management:

// heap_watermark.c

int process_data() {
    int mark = heap_mark();  // Record current position

    // Allocate large temporary memory
    char *buffer = malloc(1024);
    int *array = malloc(100 * sizeof(int));

    // ... processing ...

    heap_release(mark);  // Batch release (O(1))
    return 0;
}

int main() {
    int i;
    for (i = 0; i < 1000; i++) {
        process_data();  // No memory leak
    }
    return 0;
}

GPIO Control

// gpio_blink.c
void gpio_write(int pin, int value);
void delay_ms(int ms);

int main() {
    int i;
    for (i = 0; i < 10; i++) {
        gpio_write(13, 1);  // LED on
        delay_ms(500);
        gpio_write(13, 0);  // LED off
        delay_ms(500);
    }
    return 0;
}

Host FFI Implementation

void ffi_gpio_write(LcvmState *vm) {
    int value = lcvm_pop_int(vm);
    int pin = lcvm_pop_int(vm);

    // Validate pin number
    if (pin < 0 || pin >= NUM_GPIO_PINS) {
        lcvm_record_error(vm, "Invalid GPIO pin", 0);
        return;
    }

    // Hardware access
    HAL_GPIO_WritePin(GPIO_PORT, pin, value);
}

// Register with type signature
lcvm_register_function_ex("gpio_write", ffi_gpio_write, 2, "ii");

Sensor Reading

// temperature_monitor.c
double read_temperature();
void send_alert(double temp);

int main() {
    double temp;
    int i;

    for (i = 0; i < 100; i++) {
        temp = read_temperature();
        printf("Temperature: %.2f C\n", temp);

        if (temp > 85.0) {
            send_alert(temp);
            break;
        }

        delay_ms(1000);
    }

    return 0;
}

PID Controller

// pid_control.c
double setpoint = 100.0;
double kp = 1.0, ki = 0.1, kd = 0.05;
double integral = 0.0, prev_error = 0.0;

double get_sensor_value();
void set_actuator(double value);
void delay_ms(int ms);

double pid_update(double measured) {
    double error = setpoint - measured;
    double proportional = kp * error;

    integral = integral + error;
    double integral_term = ki * integral;

    double derivative = error - prev_error;
    double derivative_term = kd * derivative;

    prev_error = error;

    return proportional + integral_term + derivative_term;
}

int main() {
    int i;
    for (i = 0; i < 1000; i++) {
        double measured = get_sensor_value();
        double output = pid_update(measured);
        set_actuator(output);
        delay_ms(10);
    }
    return 0;
}

State Machine

// state_machine.c
int state = 0;
int counter = 0;

void led_on();
void led_off();
int button_pressed();
void delay_ms(int ms);

int main() {
    while (1) {
        switch (state) {
            case 0:  // IDLE
                led_off();
                if (button_pressed()) {
                    state = 1;
                    counter = 0;
                }
                break;

            case 1:  // RUNNING
                led_on();
                counter = counter + 1;
                if (counter >= 100) {
                    state = 2;
                }
                break;

            case 2:  // DONE
                led_off();
                if (button_pressed()) {
                    state = 0;
                }
                break;
        }
        delay_ms(10);
    }
    return 0;
}