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;
}