Agents.md LogoAgents.md

Lua Programming Development Guide - Agents.md Example

A comprehensive development guide for Lua programming with focus on game development, embedded systems, and scripting applications with best practices and performance optimization

Last Updated:Jun 16, 2025
Tags:
luascriptinggame-developmentembedded-systemsprogrammingperformancecoroutines
View on GitHub

About This Agents.md Example

Key Features of This Agents.md Example

This agents.md example showcases industry best practices for AI agent instruction. The agents.md example provides a comprehensive template that you can adapt for your specific project requirements.

Every element in this agents.md example has been carefully designed to optimize OpenAI Codex performance and ensure consistent AI agent behavior across your development workflow.

How to Implement This Agents.md Example

To use this agents.md example in your project, download the template and customize it according to your specific needs. This agents.md example serves as a solid foundation for your AI agent configuration.

Study the structure and conventions used in this agents.md example to understand how successful projects implement AI agent instruction for optimal results.

Lua Programming Development Guide

Project Overview

This comprehensive guide outlines best practices for Lua programming, covering its unique features and common use cases in game development, embedded systems, and scripting applications. The guide emphasizes clean, efficient code that leverages Lua's dynamic typing, powerful table features, and coroutines while maintaining excellent performance and readability.

Tech Stack

  • Language: Lua 5.4+ (with backward compatibility considerations)
  • Game Development: LÖVE 2D, Corona SDK, World of Warcraft API
  • Embedded Systems: OpenWrt, NodeMCU, ESP32
  • Web Development: OpenResty, Lapis Framework
  • Testing: Busted, LuaUnit
  • Documentation: LDoc
  • Performance: LuaJIT (when applicable)
  • Package Management: LuaRocks

Development Environment Setup

Installation Requirements

  • Lua 5.4+ interpreter
  • LuaRocks package manager
  • Text editor with Lua syntax support
  • Debugging tools (ZeroBrane Studio, VS Code with Lua extension)

Installation Steps

# Install Lua (Ubuntu/Debian)
sudo apt-get install lua5.4 lua5.4-dev

# Install LuaRocks
sudo apt-get install luarocks

# Install essential packages
luarocks install busted      # Testing framework
luarocks install ldoc        # Documentation generator
luarocks install luacheck    # Static analysis tool

# For game development
luarocks install love        # LÖVE 2D framework

Project Structure

lua-project/
├── src/                     # Source code
│   ├── main.lua            # Entry point
│   ├── modules/            # Custom modules
│   │   ├── utils.lua
│   │   ├── config.lua
│   │   └── game_logic.lua
│   └── lib/                # Third-party libraries
├── tests/                  # Test files
│   ├── test_utils.lua
│   └── test_game_logic.lua
├── docs/                   # Documentation
├── assets/                 # Game assets (if applicable)
│   ├── images/
│   ├── sounds/
│   └── fonts/
├── conf.lua               # Configuration (LÖVE 2D)
├── rockspec              # Package specification
└── README.md

Key Principles and Guidelines

Core Development Philosophy

  • Write clear, concise Lua code that follows idiomatic patterns
  • Leverage Lua's dynamic typing while maintaining code clarity
  • Use proper error handling and coroutines effectively
  • Follow consistent naming conventions and code organization
  • Optimize for performance while maintaining readability
  • Focus on end-user experience in all implementations

Naming Conventions

-- Variables and functions: snake_case
local player_health = 100
local function calculate_damage(base_damage, armor)
    return base_damage - armor
end

-- Classes/modules: PascalCase
local GameEngine = {}
local PlayerManager = require("modules.PlayerManager")

-- Constants: UPPERCASE
local MAX_PLAYERS = 4
local DEFAULT_SPEED = 200

-- Private functions/variables: prefix with underscore
local function _internal_helper()
    -- Private implementation
end

local _private_cache = {}

Code Organization and Module Pattern

-- modules/player.lua - Proper module structure
local Player = {}
Player.__index = Player

-- Module constants
local DEFAULT_HEALTH = 100
local MAX_LEVEL = 50

-- Private functions
local function _validate_level(level)
    return level >= 1 and level <= MAX_LEVEL
end

-- Constructor
function Player.new(name, level)
    assert(type(name) == "string", "Player name must be a string")
    assert(_validate_level(level), "Invalid player level")

    local self = setmetatable({}, Player)
    self.name = name
    self.level = level
    self.health = DEFAULT_HEALTH
    self.experience = 0
    return self
end

-- Public methods
function Player:take_damage(damage)
    assert(type(damage) == "number" and damage >= 0, "Invalid damage value")
    self.health = math.max(0, self.health - damage)
    return self.health <= 0
end

function Player:heal(amount)
    assert(type(amount) == "number" and amount >= 0, "Invalid heal amount")
    self.health = math.min(DEFAULT_HEALTH, self.health + amount)
end

function Player:gain_experience(exp)
    assert(type(exp) == "number" and exp >= 0, "Invalid experience value")
    self.experience = self.experience + exp

    -- Check for level up
    local required_exp = self.level * 100
    if self.experience >= required_exp then
        self:level_up()
    end
end

function Player:level_up()
    if self.level < MAX_LEVEL then
        self.level = self.level + 1
        self.experience = 0
        self.health = DEFAULT_HEALTH -- Full heal on level up
        print(string.format("%s leveled up to %d!", self.name, self.level))
    end
end

-- Metamethods
function Player:__tostring()
    return string.format("Player(%s, Level %d, HP: %d)",
                        self.name, self.level, self.health)
end

return Player

Core Feature Implementation

Error Handling and Validation

-- Proper error handling with pcall/xpcall
local function safe_divide(a, b)
    local success, result = pcall(function()
        if b == 0 then
            error("Division by zero", 2)
        end
        return a / b
    end)

    if success then
        return result
    else
        print("Error:", result)
        return nil
    end
end

-- Enhanced error handling with xpcall
local function enhanced_error_handler(err)
    local trace = debug.traceback(err, 2)
    print("Error occurred:", trace)
    -- Log to file or send to error reporting service
    return err
end

local function risky_operation(data)
    local success, result = xpcall(function()
        -- Complex operation that might fail
        assert(type(data) == "table", "Data must be a table")
        assert(data.value, "Data must have a value field")

        return data.value * 2
    end, enhanced_error_handler)

    return success and result or nil
end

-- Input validation utility
local Validator = {}

function Validator.is_positive_number(value)
    return type(value) == "number" and value > 0
end

function Validator.is_valid_string(value, min_length, max_length)
    if type(value) ~= "string" then return false end
    local len = #value
    return len >= (min_length or 1) and len <= (max_length or math.huge)
end

function Validator.is_in_range(value, min_val, max_val)
    return type(value) == "number" and value >= min_val and value <= max_val
end

Table Operations and Data Structures

-- Efficient table operations
local TableUtils = {}

-- Deep copy implementation
function TableUtils.deep_copy(original)
    local copy = {}
    for key, value in pairs(original) do
        if type(value) == "table" then
            copy[key] = TableUtils.deep_copy(value)
        else
            copy[key] = value
        end
    end
    return copy
end

-- Merge tables
function TableUtils.merge(target, source)
    for key, value in pairs(source) do
        if type(value) == "table" and type(target[key]) == "table" then
            TableUtils.merge(target[key], value)
        else
            target[key] = value
        end
    end
    return target
end

-- Filter table elements
function TableUtils.filter(tbl, predicate)
    local result = {}
    for i, value in ipairs(tbl) do
        if predicate(value) then
            table.insert(result, value)
        end
    end
    return result
end

-- Map function over table
function TableUtils.map(tbl, transform)
    local result = {}
    for i, value in ipairs(tbl) do
        result[i] = transform(value)
    end
    return result
end

-- Example usage
local numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
local even_numbers = TableUtils.filter(numbers, function(n) return n % 2 == 0 end)
local squared_numbers = TableUtils.map(numbers, function(n) return n * n end)

Performance Optimization Techniques

-- Local variable optimization
local function optimized_loop(data)
    -- Cache frequently used values as locals
    local math_sin = math.sin
    local math_cos = math.cos
    local table_insert = table.insert

    local results = {}
    local data_length = #data

    -- Pre-allocate table when size is known
    for i = 1, data_length do
        results[i] = 0  -- Pre-allocation
    end

    for i = 1, data_length do
        local value = data[i]
        results[i] = math_sin(value) + math_cos(value)
    end

    return results
end

-- String concatenation optimization
local function build_large_string(parts)
    -- Use table.concat instead of string concatenation
    local buffer = {}
    for i, part in ipairs(parts) do
        buffer[i] = tostring(part)
    end
    return table.concat(buffer)
end

-- Memory-efficient object pooling
local ObjectPool = {}
ObjectPool.__index = ObjectPool

function ObjectPool.new(create_func, reset_func)
    local self = setmetatable({}, ObjectPool)
    self.create_func = create_func
    self.reset_func = reset_func
    self.available = {}
    self.in_use = {}
    return self
end

function ObjectPool:acquire()
    local obj = table.remove(self.available)
    if not obj then
        obj = self.create_func()
    end
    self.in_use[obj] = true
    return obj
end

function ObjectPool:release(obj)
    if self.in_use[obj] then
        self.in_use[obj] = nil
        if self.reset_func then
            self.reset_func(obj)
        end
        table.insert(self.available, obj)
    end
end

Coroutines and Concurrency

Basic Coroutine Usage

-- Simple coroutine example
local function countdown_coroutine(start)
    for i = start, 1, -1 do
        print("Countdown:", i)
        coroutine.yield(i)
    end
    print("Blast off!")
    return "completed"
end

-- Create and run coroutine
local co = coroutine.create(countdown_coroutine)
local success, value = coroutine.resume(co, 5)

while coroutine.status(co) ~= "dead" do
    print("Received:", value)
    success, value = coroutine.resume(co)
end

-- Advanced coroutine scheduler
local Scheduler = {}
Scheduler.__index = Scheduler

function Scheduler.new()
    local self = setmetatable({}, Scheduler)
    self.tasks = {}
    self.current_time = 0
    return self
end

function Scheduler:add_task(func, delay)
    local co = coroutine.create(func)
    table.insert(self.tasks, {
        coroutine = co,
        wake_time = self.current_time + (delay or 0)
    })
end

function Scheduler:update(dt)
    self.current_time = self.current_time + dt

    for i = #self.tasks, 1, -1 do
        local task = self.tasks[i]

        if self.current_time >= task.wake_time then
            local success, delay = coroutine.resume(task.coroutine)

            if coroutine.status(task.coroutine) == "dead" then
                table.remove(self.tasks, i)
            elseif delay then
                task.wake_time = self.current_time + delay
            end
        end
    end
end

-- Usage example
local scheduler = Scheduler.new()

scheduler:add_task(function()
    while true do
        print("Task 1 running")
        coroutine.yield(1.0)  -- Wait 1 second
    end
end)

scheduler:add_task(function()
    for i = 1, 5 do
        print("Task 2, iteration", i)
        coroutine.yield(0.5)  -- Wait 0.5 seconds
    end
end)

Game Development Patterns

-- Game state management
local GameState = {}
GameState.__index = GameState

function GameState.new(name)
    local self = setmetatable({}, GameState)
    self.name = name
    return self
end

function GameState:enter() end
function GameState:exit() end
function GameState:update(dt) end
function GameState:draw() end
function GameState:keypressed(key) end

-- Game state manager
local StateManager = {}
StateManager.__index = StateManager

function StateManager.new()
    local self = setmetatable({}, StateManager)
    self.states = {}
    self.current_state = nil
    return self
end

function StateManager:add_state(name, state)
    self.states[name] = state
end

function StateManager:change_state(name)
    if self.current_state then
        self.current_state:exit()
    end

    self.current_state = self.states[name]
    if self.current_state then
        self.current_state:enter()
    end
end

function StateManager:update(dt)
    if self.current_state then
        self.current_state:update(dt)
    end
end

function StateManager:draw()
    if self.current_state then
        self.current_state:draw()
    end
end

-- Entity Component System (ECS) pattern
local Entity = {}
Entity.__index = Entity

function Entity.new(id)
    local self = setmetatable({}, Entity)
    self.id = id
    self.components = {}
    return self
end

function Entity:add_component(component_type, component)
    self.components[component_type] = component
    return self
end

function Entity:get_component(component_type)
    return self.components[component_type]
end

function Entity:has_component(component_type)
    return self.components[component_type] ~= nil
end

function Entity:remove_component(component_type)
    self.components[component_type] = nil
end

-- Component examples
local PositionComponent = {}
function PositionComponent.new(x, y)
    return {x = x or 0, y = y or 0}
end

local VelocityComponent = {}
function VelocityComponent.new(dx, dy)
    return {dx = dx or 0, dy = dy or 0}
end

local HealthComponent = {}
function HealthComponent.new(max_health)
    return {
        current = max_health or 100,
        maximum = max_health or 100
    }
end

Testing and Quality Assurance

Unit Testing with Busted

-- tests/test_player.lua
local Player = require("src.modules.player")

describe("Player", function()
    local player

    before_each(function()
        player = Player.new("TestPlayer", 1)
    end)

    describe("initialization", function()
        it("should create player with correct attributes", function()
            assert.are.equal("TestPlayer", player.name)
            assert.are.equal(1, player.level)
            assert.are.equal(100, player.health)
            assert.are.equal(0, player.experience)
        end)

        it("should validate input parameters", function()
            assert.has_error(function()
                Player.new(123, 1)  -- Invalid name type
            end)

            assert.has_error(function()
                Player.new("Test", 0)  -- Invalid level
            end)
        end)
    end)

    describe("combat", function()
        it("should take damage correctly", function()
            local is_dead = player:take_damage(30)
            assert.are.equal(70, player.health)
            assert.is_false(is_dead)
        end)

        it("should die when health reaches zero", function()
            local is_dead = player:take_damage(150)
            assert.are.equal(0, player.health)
            assert.is_true(is_dead)
        end)

        it("should heal correctly", function()
            player:take_damage(50)
            player:heal(20)
            assert.are.equal(70, player.health)
        end)

        it("should not heal above maximum", function()
            player:heal(50)
            assert.are.equal(100, player.health)
        end)
    end)

    describe("experience and leveling", function()
        it("should gain experience", function()
            player:gain_experience(50)
            assert.are.equal(50, player.experience)
        end)

        it("should level up when experience threshold is reached", function()
            player:gain_experience(100)  -- Level 1 requires 100 exp
            assert.are.equal(2, player.level)
            assert.are.equal(0, player.experience)
            assert.are.equal(100, player.health)  -- Full heal on level up
        end)
    end)
end)

Performance Testing and Profiling

-- Performance testing utilities
local Profiler = {}

function Profiler.time_function(func, iterations)
    iterations = iterations or 1000
    local start_time = os.clock()

    for i = 1, iterations do
        func()
    end

    local end_time = os.clock()
    local total_time = end_time - start_time
    local avg_time = total_time / iterations

    return {
        total_time = total_time,
        average_time = avg_time,
        iterations = iterations
    }
end

function Profiler.memory_usage()
    collectgarbage("collect")
    return collectgarbage("count")
end

-- Example performance test
local function test_table_operations()
    local data = {}
    for i = 1, 1000 do
        data[i] = math.random(1, 100)
    end

    -- Test different sorting methods
    local function bubble_sort(arr)
        local n = #arr
        for i = 1, n do
            for j = 1, n - i do
                if arr[j] > arr[j + 1] then
                    arr[j], arr[j + 1] = arr[j + 1], arr[j]
                end
            end
        end
    end

    local function lua_sort(arr)
        table.sort(arr)
    end

    -- Compare performance
    local bubble_result = Profiler.time_function(function()
        local copy = TableUtils.deep_copy(data)
        bubble_sort(copy)
    end, 10)

    local lua_result = Profiler.time_function(function()
        local copy = TableUtils.deep_copy(data)
        lua_sort(copy)
    end, 10)

    print("Bubble sort average:", bubble_result.average_time)
    print("Lua sort average:", lua_result.average_time)
end

Security and Input Validation

-- Security utilities
local Security = {}

function Security.sanitize_string(input, max_length)
    if type(input) ~= "string" then
        return ""
    end

    -- Remove potentially dangerous characters
    input = input:gsub("[<>\"'&]", "")

    -- Limit length
    if max_length and #input > max_length then
        input = input:sub(1, max_length)
    end

    return input
end

function Security.validate_number_input(input, min_val, max_val)
    local num = tonumber(input)
    if not num then
        return nil, "Invalid number format"
    end

    if min_val and num < min_val then
        return nil, "Number too small"
    end

    if max_val and num > max_val then
        return nil, "Number too large"
    end

    return num
end

function Security.hash_password(password, salt)
    -- Simple example - use proper crypto library in production
    local combined = password .. (salt or "default_salt")
    local hash = 0
    for i = 1, #combined do
        hash = (hash * 31 + string.byte(combined, i)) % 2^32
    end
    return tostring(hash)
end

-- Safe loading of user scripts
function Security.safe_load_script(script_string, env)
    env = env or {}

    -- Restricted environment
    local safe_env = {
        -- Allow basic functions
        print = print,
        tostring = tostring,
        tonumber = tonumber,
        type = type,
        pairs = pairs,
        ipairs = ipairs,
        next = next,

        -- Math functions
        math = {
            abs = math.abs,
            ceil = math.ceil,
            floor = math.floor,
            max = math.max,
            min = math.min,
            random = math.random,
            sqrt = math.sqrt
        },

        -- String functions
        string = {
            len = string.len,
            sub = string.sub,
            find = string.find,
            match = string.match,
            gsub = string.gsub
        }
    }

    -- Merge with provided environment
    for k, v in pairs(env) do
        safe_env[k] = v
    end

    local chunk, err = load(script_string, "user_script", "t", safe_env)
    if not chunk then
        return nil, err
    end

    return pcall(chunk)
end

Memory Management and Optimization

Garbage Collection Best Practices

-- Memory management utilities
local MemoryManager = {}

function MemoryManager.force_cleanup()
    collectgarbage("collect")
end

function MemoryManager.get_memory_usage()
    return collectgarbage("count")
end

function MemoryManager.set_gc_params(pause, stepmul)
    -- pause: controls how long to wait before starting a new cycle
    -- stepmul: controls the relative speed of the collector
    collectgarbage("setpause", pause or 100)
    collectgarbage("setstepmul", stepmul or 200)
end

-- Weak table implementation for caches
function MemoryManager.create_weak_cache(mode)
    local cache = {}
    setmetatable(cache, {__mode = mode or "v"})  -- "k", "v", or "kv"
    return cache
end

-- Example: Image cache with weak references
local image_cache = MemoryManager.create_weak_cache("v")

local function load_image(filename)
    if image_cache[filename] then
        return image_cache[filename]
    end

    local image = love.graphics.newImage(filename)  -- LÖVE 2D example
    image_cache[filename] = image
    return image
end

Best Practices Summary

Code Quality Guidelines

  • Use local variables whenever possible for better performance and scope management
  • Implement proper error handling using pcall/xpcall for robust applications
  • Follow consistent naming conventions (snake_case for functions/variables, PascalCase for modules)
  • Write modular, reusable code with clear separation of concerns
  • Optimize performance by caching frequently used values and pre-allocating tables
  • Implement comprehensive testing with unit tests and integration tests
  • Use proper documentation with clear comments and API documentation

Security Considerations

  • Validate all input data to prevent injection attacks and data corruption
  • Sanitize user-provided strings before processing or display
  • Use restricted environments when loading user scripts or untrusted code
  • Implement proper access controls for sensitive operations
  • Avoid using loadstring with untrusted input; prefer safer alternatives
  • Handle sensitive data appropriately with proper encryption and secure storage

Performance Optimization

  • Cache frequently accessed values as local variables
  • Pre-allocate tables when the size is known in advance
  • Use table.concat() for efficient string concatenation
  • Minimize table creation in performance-critical loops
  • Implement object pooling for frequently created/destroyed objects
  • Profile your code regularly to identify bottlenecks
  • Use appropriate data structures for your specific use cases

Game Development Specific

  • Implement proper game loop structure with fixed timestep for physics
  • Use efficient collision detection algorithms appropriate for your game type
  • Manage game state effectively with proper state machines
  • Optimize render operations by batching draws and minimizing state changes
  • Handle input processing efficiently with proper event systems
  • Implement proper resource management for assets and memory

Debugging and Maintenance

  • Use proper debugging tools like ZeroBrane Studio or VS Code extensions
  • Implement comprehensive logging for production environments
  • Use print statements strategically during development
  • Monitor performance metrics and memory usage
  • Implement error reporting systems for production applications
  • Maintain clear documentation and code comments
  • Follow consistent code formatting and style guidelines

This comprehensive guide provides a solid foundation for developing efficient, maintainable Lua applications across various domains, from game development to embedded systems and scripting applications.