Lua Language Samples

Essential Lua programming examples for game development and embedded scripting

Key Facts

Category
Programming Languages
Items
3
Format Families
sample

Sample Overview

Essential Lua programming examples for game development and embedded scripting This sample set belongs to Programming Languages and can be used to test related workflows inside Elysia Tools.

💻 Lua Hello World lua

🟢 simple

Basic Lua Hello World program and fundamental syntax examples

⏱️ 15 min 🏷️ lua, programming, beginner, game development, scripting
Prerequisites: Basic programming concepts
-- Lua Hello World Examples

-- 1. Basic Hello World
print("Hello, World!")

-- 2. Hello World with variable
local message = "Hello, World!"
print(message)

-- 3. Hello World with function
function sayHello()
    return "Hello, World!"
end

print(sayHello())

-- 4. Hello World with function parameters
function greet(name)
    return "Hello, " .. name .. "!"
end

print(greet("World"))
print(greet("Lua"))

-- 5. Hello World with table
local greeter = {
    message = "Hello, World!"
}

function greeter:sayHello()
    return self.message
end

print(greeter:sayHello())

-- 6. Hello World with class-like structure
local Greeter = {}
Greeter.__index = Greeter

function Greeter.new(message)
    local self = setmetatable({}, Greeter)
    self.message = message or "Hello, World!"
    return self
end

function Greeter:greet()
    return self.message
end

local greeter1 = Greeter.new()
local greeter2 = Greeter.new("Hello from custom class!")

print(greeter1:greet())
print(greeter2:greet())

-- 7. Hello World multiple times
for i = 1, 5 do
    print("Hello, World! " .. i)
end

-- 8. Hello World with table
local greetingList = {"Hello", "Bonjour", "Hola", "Ciao", "こんにちは"}

for _, greeting in ipairs(greetingList) do
    print(greeting .. ", World!")
end

-- 9. Hello World with map-like table
local greetings = {
    en = "Hello",
    es = "Hola",
    fr = "Bonjour",
    de = "Hallo",
    ja = "こんにちは"
}

for lang, greeting in pairs(greetings) do
    print(greeting .. ", World! (" .. lang .. ")")
end

-- 10. Hello World with user input simulation
-- Note: In a real environment, you might use io.read()
function simulateUserInput()
    local userName = "Lua User"  -- Simulated input
    return "Hello, " .. userName .. "!"
end

print(simulateUserInput())

-- Basic data types examples
-- Numbers (Lua has only one number type: double)
local integer = 42
local float = 3.14
local scientific = 1.23e-4

-- Strings
local singleQuote = 'Hello'
local doubleQuote = "World"
local multiline = [[
This is a multiline string
that can span multiple lines
without escaping.
]]

-- Booleans
local isTrue = true
local isFalse = false

-- Nil (represents absence of value)
local nilValue = nil

-- Tables (main data structure)
local emptyTable = {}
local list = {1, 2, 3, 4, 5}
local map = {name = "Lua", version = "5.4", awesome = true}

print("Data types:")
print("Integer:", integer, "Type:", type(integer))
print("Float:", float, "Type:", type(float))
print("String:", singleQuote, "Type:", type(singleQuote))
print("Boolean:", isTrue, "Type:", type(isTrue))
print("Nil:", nilValue, "Type:", type(nilValue))
print("Table:", list, "Type:", type(list))

-- Control flow examples
local age = 18

-- If-else statement
if age >= 18 then
    print("You are an adult")
else
    print("You are a minor")
end

-- If-elseif-else
local score = 85
local grade

if score >= 90 then
    grade = "A"
elseif score >= 80 then
    grade = "B"
elseif score >= 70 then
    grade = "C"
else
    grade = "F"
end

print("Score:", score, "Grade:", grade)

-- Loop examples
-- Numeric for loop
print("For loop:")
for i = 1, 3 do
    print("Iteration:", i)
end

-- While loop
print("While loop:")
local count = 0
while count < 3 do
    print("Count:", count)
    count = count + 1
end

-- Repeat-until loop
print("Repeat-until loop:")
local repeatCount = 0
repeat
    print("Repeat count:", repeatCount)
    repeatCount = repeatCount + 1
until repeatCount >= 3

-- Functions as first-class citizens
-- Anonymous function
local multiply = function(a, b)
    return a * b
end

print("5 * 3 =", multiply(5, 3))

-- Function that returns multiple values
function getMinMax(numbers)
    local min = numbers[1]
    local max = numbers[1]

    for _, num in ipairs(numbers) do
        if num < min then min = num end
        if num > max then max = num end
    end

    return min, max
end

local numbers = {5, 2, 8, 1, 9, 3}
local minVal, maxVal = getMinMax(numbers)
print("Min:", minVal, "Max:", maxVal)

-- String manipulation
local text = "Hello, Lua World!"

print("Original:", text)
print("Upper:", string.upper(text))
print("Lower:", string.lower(text))
print("Length:", #text)
print("Substring:", string.sub(text, 1, 5))
print("Find Lua:", string.find(text, "Lua"))

-- String concatenation and formatting
local name = "Lua"
local version = "5.4"
local formatted = string.format("Language: %s, Version: %s", name, version)
print(formatted)

-- Table operations
-- Lists (arrays)
local fruits = {"apple", "banana", "cherry", "date"}

print("Fruits list:")
for i, fruit in ipairs(fruits) do
    print(i, fruit)
end

-- Add to end
table.insert(fruits, "elderberry")
print("After insert:", table.concat(fruits, ", "))

-- Remove from end
local removed = table.remove(fruits)
print("Removed:", removed)
print("Remaining:", table.concat(fruits, ", "))

-- Map-like tables
local person = {
    name = "John Doe",
    age = 30,
    city = "New York",
    skills = {"programming", "gaming", "music"}
}

print("Person information:")
print("Name:", person.name)
print("Age:", person.age)
print("City:", person.city)
print("Skills:", table.concat(person.skills, ", "))

-- Dynamic table access
local key = "age"
print("Dynamic access:", person[key])

-- Table iteration
print("Person details:")
for key, value in pairs(person) do
    if type(value) ~= "table" then
        print(key .. ":", value)
    end
end

-- Error handling with pcall
function riskyOperation(shouldFail)
    if shouldFail then
        error("Something went wrong!")
    end
    return "Success!"
end

-- Safe execution
local success, result = pcall(riskyOperation, false)
if success then
    print("Operation result:", result)
else
    print("Operation failed:", result)
end

local success, result = pcall(riskyOperation, true)
if success then
    print("Operation result:", result)
else
    print("Operation failed:", result)
end

-- Variable scope examples
local globalVar = "I am global"  -- Available everywhere

function testScope()
    local localVar = "I am local"  -- Available only in this function
    print("Inside function - Global:", globalVar)
    print("Inside function - Local:", localVar)
end

testScope()
print("Outside function - Global:", globalVar)
-- print(localVar)  -- This would cause an error: localVar is not defined

-- Metatables demonstration
local vector = {x = 3, y = 4}

-- Metatable for vector operations
local vectorMeta = {
    __add = function(a, b)
        return {x = a.x + b.x, y = a.y + b.y}
    end,
    __tostring = function(v)
        return "(" .. v.x .. ", " .. v.y .. ")"
    end
}

setmetatable(vector, vectorMeta)

local vector2 = {x = 1, y = 2}
setmetatable(vector2, vectorMeta)

local result = vector + vector2
print("Vector addition:", tostring(result))

💻 Lua Tables and Metatables lua

🟡 intermediate ⭐⭐⭐⭐

Advanced table operations, metatables, and object-oriented programming in Lua

⏱️ 30 min 🏷️ lua, tables, metatables, oop, data structures
Prerequisites: Lua basics, Understanding of metatables, Object-oriented concepts
-- Lua Tables and Metatables Examples

-- 1. Advanced Table Operations

-- Table as array/list
local numbers = {10, 20, 30, 40, 50}

-- Array operations
function arrayCopy(arr)
    local copy = {}
    for i, v in ipairs(arr) do
        copy[i] = v
    end
    return copy
end

function arrayFilter(arr, predicate)
    local result = {}
    for i, v in ipairs(arr) do
        if predicate(v) then
            table.insert(result, v)
        end
    end
    return result
end

function arrayMap(arr, transform)
    local result = {}
    for i, v in ipairs(arr) do
        result[i] = transform(v)
    end
    return result
end

function arrayReduce(arr, accumulator, initial)
    local result = initial
    for _, v in ipairs(arr) do
        result = accumulator(result, v)
    end
    return result
end

-- Test array operations
local doubled = arrayMap(numbers, function(x) return x * 2 end)
local evens = arrayFilter(numbers, function(x) return x % 2 == 0 end)
local sum = arrayReduce(numbers, function(acc, x) return acc + x end, 0)

print("Original:", table.concat(numbers, ", "))
print("Doubled:", table.concat(doubled, ", "))
print("Even numbers:", table.concat(evens, ", "))
print("Sum:", sum)

-- 2. Table as dictionary/map
local person = {
    name = "Alice",
    age = 30,
    city = "New York",
    email = "[email protected]",
    hobbies = {"reading", "hiking", "coding"}
}

-- Dictionary operations
function tableCopy(t)
    local copy = {}
    for k, v in pairs(t) do
        if type(v) == "table" then
            copy[k] = tableCopy(v)
        else
            copy[k] = v
        end
    end
    return copy
end

function tableKeys(t)
    local keys = {}
    for k, _ in pairs(t) do
        table.insert(keys, k)
    end
    return keys
end

function tableValues(t)
    local values = {}
    for _, v in pairs(t) do
        table.insert(values, v)
    end
    return values
end

function tableMerge(t1, t2)
    local result = tableCopy(t1)
    for k, v in pairs(t2) do
        result[k] = v
    end
    return result
end

-- Test dictionary operations
local keys = tableKeys(person)
local values = tableValues(person)
local extended = tableMerge(person, {country = "USA", phone = "555-1234"})

print("Person keys:", table.concat(keys, ", "))
print("Extended person:")
for k, v in pairs(extended) do
    print("  " .. k .. ":", type(v) == "table" and table.concat(v, ", ") or v)
end

-- 3. Stack and Queue implementations

-- Stack implementation using table
local Stack = {}
Stack.__index = Stack

function Stack.new()
    return setmetatable({}, Stack)
end

function Stack:push(item)
    table.insert(self, item)
end

function Stack:pop()
    if #self == 0 then
        return nil
    end
    return table.remove(self)
end

function Stack:peek()
    if #self == 0 then
        return nil
    end
    return self[#self]
end

function Stack:isEmpty()
    return #self == 0
end

function Stack:size()
    return #self
end

-- Test Stack
local stack = Stack.new()
stack:push("first")
stack:push("second")
stack:push("third")

print("Stack size:", stack:size())
print("Peek:", stack:peek())
print("Pop:", stack:pop())
print("Pop:", stack:pop())
print("Is empty:", stack:isEmpty())

-- Queue implementation using table
local Queue = {}
Queue.__index = Queue

function Queue.new()
    return setmetatable({head = 0, tail = -1}, Queue)
end

function Queue:enqueue(item)
    self.tail = self.tail + 1
    self[self.tail] = item
end

function Queue:dequeue()
    if self.head > self.tail then
        return nil
    end
    local value = self[self.head]
    self[self.head] = nil  -- Free memory
    self.head = self.head + 1
    return value
end

function Queue:isEmpty()
    return self.head > self.tail
end

function Queue:size()
    if self:isEmpty() then
        return 0
    end
    return self.tail - self.head + 1
end

-- Test Queue
local queue = Queue.new()
queue:enqueue("Alice")
queue:enqueue("Bob")
queue:enqueue("Charlie")

print("Queue size:", queue:size())
print("Dequeue:", queue:dequeue())
print("Dequeue:", queue:dequeue())
print("Queue size after dequeues:", queue:size())

-- 4. Metatables - The Magic of Lua

-- Basic metatable example
local myTable = {a = 10, b = 20}

-- Create metatable with __index method
local myMeta = {
    __index = function(table, key)
        print("Accessing missing key:", key)
        return "default value"
    end,

    __newindex = function(table, key, value)
        print("Setting new key:", key, "to:", value)
        rawset(table, key, value)
    end
}

setmetatable(myTable, myMeta)

print(myTable.c)  -- Triggers __index
myTable.c = 30    -- Triggers __newindex
print(myTable.c)  -- Regular access

-- 5. Operator overloading with metatables

-- Vector class
local Vector = {}
Vector.__index = Vector

function Vector.new(x, y)
    local self = setmetatable({x = x or 0, y = y or 0}, Vector)
    return self
end

function Vector.__add(a, b)
    return Vector.new(a.x + b.x, a.y + b.y)
end

function Vector.__sub(a, b)
    return Vector.new(a.x - b.x, a.y - b.y)
end

function Vector.__mul(a, scalar)
    if type(a) == "table" and type(scalar) == "number" then
        return Vector.new(a.x * scalar, a.y * scalar)
    elseif type(a) == "number" and type(scalar) == "table" then
        return Vector.new(a * scalar.x, a * scalar.y)
    end
    error("Invalid multiplication")
end

function Vector.__eq(a, b)
    return math.abs(a.x - b.x) < 0.0001 and math.abs(a.y - b.y) < 0.0001
end

function Vector.__tostring(v)
    return string.format("(%g, %g)", v.x, v.y)
end

function Vector:magnitude()
    return math.sqrt(self.x * self.x + self.y * self.y)
end

function Vector:normalize()
    local mag = self:magnitude()
    if mag > 0 then
        return Vector.new(self.x / mag, self.y / mag)
    end
    return Vector.new(0, 0)
end

function Vector:dot(other)
    return self.x * other.x + self.y * other.y
end

-- Test Vector class
local v1 = Vector.new(3, 4)
local v2 = Vector.new(1, 2)

print("Vector1:", v1)
print("Vector2:", v2)
print("Addition:", v1 + v2)
print("Subtraction:", v1 - v2)
print("Multiplication:", v1 * 2)
print("Magnitude:", v1:magnitude())
print("Normalized:", v1:normalize())
print("Dot product:", v1:dot(v2))

-- 6. Read-only table proxy
function makeReadOnly(t)
    local proxy = {}
    local meta = {
        __index = t,
        __newindex = function(t, k, v)
            error("Attempt to modify read-only table")
        end,
        __pairs = function()
            return pairs(t)
        end,
        __ipairs = function()
            return ipairs(t)
        end
    }
    setmetatable(proxy, meta)
    return proxy
end

local config = {
    max_connections = 100,
    timeout = 30,
    debug = false
}

local readonlyConfig = makeReadOnly(config)

print("Max connections:", readonlyConfig.max_connections)
-- readonlyConfig.max_connections = 200  -- This would throw an error

-- 7. Table with default values
function withDefaults(defaults)
    local meta = {
        __index = defaults
    }
    return setmetatable({}, meta)
end

local gameSettings = withDefaults({
    volume = 0.5,
    difficulty = "normal",
    fullscreen = false,
    resolution = {width = 1920, height = 1080}
})

print("Volume:", gameSettings.volume)
gameSettings.volume = 0.8  -- This overwrites the default
print("New volume:", gameSettings.volume)
print("Resolution width:", gameSettings.resolution.width)

-- 8. Chainable interface (fluent interface)
local StringBuilder = {}
StringBuilder.__index = StringBuilder

function StringBuilder.new()
    return setmetatable({buffer = {}}, StringBuilder)
end

function StringBuilder:add(text)
    table.insert(self.buffer, text)
    return self
end

function StringBuilder:addLine(text)
    table.insert(self.buffer, text or "")
    return self
end

function StringBuilder:clear()
    self.buffer = {}
    return self
end

function StringBuilder:toString()
    return table.concat(self.buffer)
end

function StringBuilder:length()
    return #self.buffer
end

-- Test StringBuilder
local sb = StringBuilder.new()
local result = sb
    :add("Hello")
    :add(" ")
    :add("World")
    :addLine()
    :add("This is Lua")
    :addLine("StringBuilder example")
    :toString()

print("StringBuilder result:")
print(result)

-- 9. Weak tables for caching
local cache = setmetatable({}, {__mode = "v"})  -- Weak values

function expensiveComputation(x)
    if cache[x] then
        print("Cache hit for", x)
        return cache[x]
    end

    print("Computing for", x)
    local result = x * x * x  -- Simulate expensive operation
    cache[x] = result
    return result
end

print("Result 1:", expensiveComputation(10))
print("Result 2:", expensiveComputation(10))
print("Result 3:", expensiveComputation(20))
print("Result 4:", expensiveComputation(10))

-- 10. Observable table pattern
function makeObservable(t, onChange)
    local proxy = {}
    local meta = {
        __index = function(_, k)
            return t[k]
        end,

        __newindex = function(_, k, v)
            local oldValue = t[k]
            t[k] = v
            if onChange and oldValue ~= v then
                onChange(k, oldValue, v)
            end
        end,

        __pairs = function()
            return pairs(t)
        end,

        __ipairs = function()
            return ipairs(t)
        end
    }

    return setmetatable(proxy, meta)
end

local playerData = makeObservable({
    score = 0,
    lives = 3,
    level = 1
}, function(key, oldValue, newValue)
    print(string.format("Player %s changed: %s -> %s", key, oldValue, newValue))
end)

playerData.score = 100
playerData.lives = 2
playerData.level = 2

💻 Lua Coroutines and Modules lua

🟡 intermediate ⭐⭐⭐⭐

Coroutine programming, module system, and practical game development patterns

⏱️ 35 min 🏷️ lua, coroutines, modules, game dev, entity system, async
Prerequisites: Lua basics, Table operations, Understanding of game programming concepts
-- Lua Coroutines and Modules Examples

-- 1. Basic Coroutine Examples

-- Simple coroutine
function simpleCoroutine()
    print("Coroutine started")
    for i = 1, 3 do
        print("Coroutine step", i)
        coroutine.yield("Yielded value " .. i)
    end
    print("Coroutine finished")
    return "Final result"
end

-- Create and run coroutine
local co = coroutine.create(simpleCoroutine)

print("Coroutine status:", coroutine.status(co))

-- Resume coroutine multiple times
while true do
    local success, result = coroutine.resume(co)
    print("Resume result:", success, result)
    if not success or coroutine.status(co) == "dead" then
        break
    end
end

print("Final coroutine status:", coroutine.status(co))

-- 2. Coroutine for task scheduling
function taskScheduler()
    local tasks = {}

    -- Add task function
    local function addTask(name, func, delay)
        table.insert(tasks, {
            name = name,
            func = func,
            delay = delay,
            timeRemaining = delay,
            coroutine = coroutine.create(func)
        })
    end

    -- Update function (call this every frame)
    local function update(deltaTime)
        for i, task in ipairs(tasks) do
            task.timeRemaining = task.timeRemaining - deltaTime

            if task.timeRemaining <= 0 and coroutine.status(task.coroutine) ~= "dead" then
                local success, result = coroutine.resume(task.coroutine)
                if not success then
                    print("Task error:", task.name, result)
                    table.remove(tasks, i)
                elseif coroutine.status(task.coroutine) == "dead" then
                    print("Task completed:", task.name)
                    table.remove(tasks, i)
                end
            end
        end
    end

    return {
        addTask = addTask,
        update = update,
        getTaskCount = function() return #tasks end
    }
end

-- Test task scheduler
local scheduler = taskScheduler()

-- Define some tasks
local function countdownTask()
    for i = 5, 1, -1 do
        print("Countdown:", i)
        coroutine.yield()
    end
    print("Blast off!")
end

local function printTask()
    for i = 1, 3 do
        print("Printing line", i)
        coroutine.yield()
    end
end

-- Add tasks with delays
scheduler:addTask("Countdown", countdownTask, 1.0)
scheduler:addTask("Print", printTask, 2.0)

-- Simulate game loop
for frame = 1, 10 do
    print("=== Frame", frame, "===")
    scheduler:update(1.0)  -- 1 second per frame
    if scheduler.getTaskCount() == 0 then
        break
    end
end

-- 3. Coroutine for animation
function animateValue(startValue, endValue, duration, updateFunc)
    local startTime = os.clock()

    while true do
        local currentTime = os.clock()
        local elapsed = currentTime - startTime
        local progress = math.min(elapsed / duration, 1.0)

        -- Easing function (ease in-out)
        local easedProgress = progress < 0.5
            and 2 * progress * progress
            or 1 - math.pow(-2 * progress + 2, 2) / 2

        local currentValue = startValue + (endValue - startValue) * easedProgress
        updateFunc(currentValue)

        if progress >= 1.0 then
            break
        end

        coroutine.yield()
    end
end

-- Create animation coroutine
local animationCo = coroutine.create(function()
    animateValue(0, 100, 3.0, function(value)
        print(string.format("Animation progress: %.1f%%", value))
    end)
    print("Animation complete!")
end)

-- Run animation
while coroutine.status(animationCo) ~= "dead" do
    coroutine.resume(animationCo)
    os.execute("sleep 0.1")  -- Wait 100ms (in real code, use proper timing)
end

-- 4. Generator pattern with coroutines
function rangeGenerator(start, stop, step)
    step = step or 1
    return coroutine.create(function()
        for i = start, stop, step do
            coroutine.yield(i)
        end
    end)
end

function fibonacciGenerator(n)
    return coroutine.create(function()
        local a, b = 0, 1
        for i = 1, n do
            coroutine.yield(a)
            a, b = b, a + b
        end
    end)
end

-- Test generators
print("Range generator:")
local rangeGen = rangeGenerator(1, 5)
while true do
    local success, value = coroutine.resume(rangeGen)
    if not success or coroutine.status(rangeGen) == "dead" then
        break
    end
    print("  Value:", value)
end

print("\nFibonacci generator:")
local fibGen = fibonacciGenerator(10)
while true do
    local success, value = coroutine.resume(fibGen)
    if not success or coroutine.status(fibGen) == "dead" then
        break
    end
    print("  Fibonacci:", value)
end

-- 5. Coroutine-based state machine
function createStateMachine()
    local state = "idle"
    local co = coroutine.create(function()
        while true do
            if state == "idle" then
                print("State: Idle")
                state = coroutine.yield("idle")

            elseif state == "loading" then
                print("State: Loading...")
                for i = 1, 3 do
                    print("  Loading", i, "/ 3")
                    coroutine.yield("loading")
                end
                state = "playing"

            elseif state == "playing" then
                print("State: Playing")
                state = coroutine.yield("playing")

            elseif state == "paused" then
                print("State: Paused")
                state = coroutine.yield("paused")

            else
                print("Unknown state:", state)
                state = "idle"
            end
        end
    end)

    return {
        setState = function(newState) state = newState end,
        update = function()
            if coroutine.status(co) ~= "dead" then
                local success, currentState = coroutine.resume(co)
                return success and currentState or "error"
            end
            return "dead"
        end
    }
end

-- Test state machine
local stateMachine = createStateMachine()

for i = 1, 10 do
    print("--- Update", i, "---")
    local currentState = stateMachine.update()

    -- Simulate state transitions
    if i == 2 then
        stateMachine.setState("loading")
    elseif i == 5 then
        stateMachine.setState("playing")
    elseif i == 7 then
        stateMachine.setState("paused")
    elseif i == 8 then
        stateMachine.setState("playing")
    end
end

-- 6. Module system examples

-- In Lua, modules are typically created as tables
-- Here's how to structure a module:

-- mathUtils.lua (simulated here)
local mathUtils = {}

function mathUtils.clamp(value, min, max)
    if value < min then return min end
    if value > max then return max end
    return value
end

function mathUtils.lerp(a, b, t)
    return a + (b - a) * t
end

function mathUtils.randomFloat(min, max)
    return min + math.random() * (max - min)
end

function mathUtils.distance(x1, y1, x2, y2)
    return math.sqrt((x2 - x1)^2 + (y2 - y1)^2)
end

-- Add metatable for module
setmetatable(mathUtils, {
    __tostring = function()
        return "MathUtils module"
    end
})

-- Test the math utilities module
print("\n=== Math Utils Module ===")
print("Clamp 15 to [0, 10]:", mathUtils.clamp(15, 0, 10))
print("Lerp 0 to 10 by 0.3:", mathUtils.lerp(0, 10, 0.3))
print("Random float [1, 5]:", string.format("%.2f", mathUtils.randomFloat(1, 5)))
print("Distance between (0,0) and (3,4):", mathUtils.distance(0, 0, 3, 4))

-- 7. Game entity system with modules

-- Entity component system
local Entity = {}
Entity.__index = Entity

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

function Entity:addComponent(name, component)
    self.components[name] = component
    return self
end

function Entity:getComponent(name)
    return self.components[name]
end

function Entity:hasComponent(name)
    return self.components[name] ~= nil
end

function Entity:addTag(tag)
    self.tags[tag] = true
    return self
end

function Entity:hasTag(tag)
    return self.tags[tag] == true
end

function Entity:update(dt)
    -- Update all components that have update method
    for name, component in pairs(self.components) do
        if component.update then
            component:update(self, dt)
        end
    end
end

-- Component modules
local Transform = {}
Transform.__index = Transform

function Transform.new(x, y)
    return setmetatable({
        x = x or 0,
        y = y or 0,
        rotation = 0,
        scaleX = 1,
        scaleY = 1
    }, Transform)
end

function Transform:update(entity, dt)
    -- Transform component logic
end

local Health = {}
Health.__index = Health

function Health.new(maxHealth)
    return setmetatable({
        current = maxHealth,
        maximum = maxHealth
    }, Health)
end

function Health:update(entity, dt)
    -- Regeneration logic could go here
end

function Health:takeDamage(amount)
    self.current = math.max(0, self.current - amount)
    return self.current <= 0
end

function Health:heal(amount)
    self.current = math.min(self.maximum, self.current + amount)
end

local Movement = {}
Movement.__index = Movement

function Movement.new(speed)
    return setmetatable({
        speed = speed or 100,
        direction = {x = 1, y = 0}
    }, Movement)
end

function Movement:update(entity, dt)
    local transform = entity:getComponent("transform")
    if transform then
        transform.x = transform.x + self.direction.x * self.speed * dt
        transform.y = transform.y + self.direction.y * self.speed * dt
    end
end

function Movement:setDirection(x, y)
    local length = math.sqrt(x * x + y * y)
    if length > 0 then
        self.direction.x = x / length
        self.direction.y = y / length
    end
end

-- Test entity system
print("\n=== Entity System ===")

local player = Entity.new("player")
    :addComponent("transform", Transform.new(100, 100))
    :addComponent("health", Health.new(100))
    :addComponent("movement", Movement.new(150))
    :addTag("player")
    :addTag("alive")

print("Player created with ID:", player.id)
print("Player has transform:", player:hasComponent("transform"))
print("Player has health:", player:hasComponent("health"))
print("Player is alive:", player:hasTag("alive"))

-- Update player
for i = 1, 3 do
    print("\n--- Update", i, "---")
    local movement = player:getComponent("movement")
    local transform = player:getComponent("transform")

    movement:setDirection(1, 0)  -- Move right
    player:update(0.1)  -- 100ms update

    print("Player position:", string.format("(%.1f, %.1f)", transform.x, transform.y))
end

-- Take damage
local health = player:getComponent("health")
health:takeDamage(30)
print("Player health after damage:", health.current .. "/" .. health.maximum)

-- 8. Coroutine-based AI behavior
local AIBehavior = {}
AIBehavior.__index = AIBehavior

function AIBehavior.new(entity)
    local self = setmetatable({
        entity = entity,
        state = "patrol",
        target = nil,
        coroutine = nil
    }, AIBehavior)

    -- Start AI coroutine
    self.coroutine = coroutine.create(function()
        self:runAI()
    end)

    return self
end

function AIBehavior:runAI()
    while true do
        if self.state == "patrol" then
            print("AI: Patrolling...")
            for i = 1, 3 do
                self:moveTo(math.random(-100, 100), math.random(-100, 100))
                coroutine.yield()
            end
            self.state = "search"

        elseif self.state == "search" then
            print("AI: Searching for targets...")
            self.target = {x = 50, y = 50}  -- Simulate finding a target
            self.state = "chase"
            coroutine.yield()

        elseif self.state == "chase" then
            if self.target then
                print("AI: Chasing target at", self.target.x, self.target.y)
                self:moveTo(self.target.x, self.target.y)

                local transform = self.entity:getComponent("transform")
                local dist = mathUtils.distance(transform.x, transform.y,
                                              self.target.x, self.target.y)
                if dist < 5 then
                    print("AI: Reached target!")
                    self.target = nil
                    self.state = "patrol"
                end
            else
                self.state = "patrol"
            end
            coroutine.yield()
        end
    end
end

function AIBehavior:moveTo(x, y)
    local transform = self.entity:getComponent("transform")
    if transform then
        print("AI moving to:", x, y)
        transform.x = x
        transform.y = y
    end
end

function AIBehavior:update(dt)
    if self.coroutine and coroutine.status(self.coroutine) ~= "dead" then
        local success, result = coroutine.resume(self.coroutine)
        if not success then
            print("AI Error:", result)
        end
    end
end

-- Test AI behavior
print("\n=== AI Behavior ===")
local enemy = Entity.new("enemy")
    :addComponent("transform", Transform.new(0, 0))
    :addComponent("movement", Movement.new(100))

local ai = AIBehavior.new(enemy)

-- Run AI for several updates
for i = 1, 10 do
    print("\nAI Update", i)
    ai:update(0.1)
end

print("\nAll examples completed!")