Prompt:
This is a Markdown documentation from the AO documentation repository. Filename: data.md Path: ../ao-cookbook/src/references/data.md
Note: This content continues in the next part.

Response:
# Accessing Data from Arweave with ao

There may be times in your ao development workflow that you want to access data from Arweave. With ao, your process can send an assignment instructing the network to provide that data to your Process.

## Defining Acceptable Transactions (Required First Step)

Before you can assign any Arweave transaction to your process, you must first define which transactions your process will accept using [`ao.addAssignable`](../references/ao.md#ao-addassignable-name-string-condition-function). This function creates conditions that determine which Arweave transactions your process will accept.

```lua
-- Allow transactions from ArDrive
ao.addAssignable("allowArDrive", function (msg)
    return msg.Tags["App-Name"] == "ArDrive-App"
end)

-- Allow specific content types
ao.addAssignable("allowImages", function (msg)
    return msg.Tags["Content-Type"] and string.match(msg.Tags["Content-Type"], "^image/")
end)
```

> **Warning:** If you attempt to assign a transaction without first defining a matching assignable pattern, that transaction will be permanently blacklisted and can never be assigned to your process, even if you later add a matching assignable.

You can remove assignables with `ao.removeAssignable("<name>")`.

The condition functions use similar pattern matching techniques as found in the [Handlers documentation](../references/handlers.md#pattern-matching-tables). For complete details on the `ao.addAssignable` function, including parameter descriptions and additional examples, see the [ao Module Reference](../references/ao.md#ao-addassignable-name-string-condition-function).

## Assignment Methods

After defining acceptable transactions and setting up your listener (if needed), you can request Arweave data in one of two ways:

### Using `Assign`

The primary method to request data from Arweave:

```lua
Assign({
  Processes = { ao.id },
  Message = '<arweave-transaction-id>'
})
```

### Using `Send` with `Assignments`

Alternatively, you can use the `Send` function with an `Assignments` parameter:

```lua
Send({
  Target = ao.id,
  Data = 'Hello World',
  Assignments = { '<process-id-1>', '<process-id-2>' }
})
```

## Working with Assigned Data

You can process assigned data using either `Receive` or `Handlers`:

### Using `Receive` Directly

```lua
-- Listen for messages from ArDrive
ArweaveData = Receive(function(msg)
  return msg.Tags["App-Name"] == "ArDrive-App"
end)

-- Process the data when received
if ArweaveData then
  print(ArweaveData.Tags["App-Name"])
  -- Raw Arweave Data is available in ArweaveData.Data
end
```

You can also match specific transactions or combine conditions:

```lua
-- Match a specific transaction ID
ArweaveData = Receive({ Id = "<arweave-transaction-id>" })

-- Or combine multiple conditions
ArweaveData = Receive(function(msg)
  return msg.Tags["App-Name"] == "ArDrive-CLI" and
         msg.Tags["Content-Type"] == "image/png"
end)
```

> **Note:** When using [`.load`](../guides/aos/load.md#load-lua-files-with-load-filename), the script pauses at `Receive` until data arrives. When running commands separately in the shell, each command executes independently.

### Using Handlers

For persistent processing, set up a handler:

```lua
Handlers.add("ProcessArDriveFiles",
  { Tags = { ["App-Name"] = "ArDrive-App" } },
  function(msg)
    print(msg.Tags["App-Name"])
    -- Raw Arweave Data is available in msg.Data
  end
)
```

Handlers are ideal for:

- Processing multiple assignments over time
- Automated processing without manual intervention
- Building services that other processes can interact with

For more details, see the [Messaging Patterns](../references/messaging.md#receive-capital-r-blocking-pattern-matcher) and [Handlers](../references/handlers.md#pattern-matching-tables) documentation.

## Complete Example Workflow

Here's a complete example that demonstrates the entire process of accessing data from an Arweave transaction:

```lua
-- Step 1: Define which transactions your process will accept
ao.addAssignable("allowArDrive", function (msg)
    return msg.Tags["App-Name"] == "ArDrive-App"
end)

-- Step 2: Request the data
Assign({
  Processes = { ao.id },
  Message = '<arweave-transaction-id>'
})

-- Step 3: Immediately capture the Assignment; blocking until received
ArweaveData = Receive(function(msg)
  return msg.Tags["App-Name"] == "ArDrive-App"
end)

print(ArweaveData.Tags["App-Name"]) -- e.g., "ArDrive-CLI"
-- Raw Arweave Data is available in ArweaveData.Data
```

This pattern creates a synchronous flow where your process:

1. Defines acceptable transactions
2. Requests the data
3. Captures the data using `Receive`
4. Processes the data

## Practical Examples

Here are two practical examples showing different approaches to working with Arweave data in your ao process:

### Example 1: Caching Arweave Data

This example demonstrates how to load and cache data from Arweave, then use it in subsequent operations:

```lua
-- Initialize state
local Number = 0

-- Step 1: Define which transactions your process will accept
print("Step 1: Defining acceptable transactions")
ao.addAssignable("addNumber", function (msg)
    return msg.Tags["Action"] == "Number"
end)

-- Step 2: Request and cache the initial number from Arweave
-- This uses a self-executing function to fetch and cache the value only once
NumberFromArweave = NumberFromArweave or (function()
    print("Step 2: Requesting initial number from Arweave")
    Assign({
        Processes = { ao.id },
        Message = 'DivdWHaNj8mJhQQCdatt52rt4QvceBR_iyX58aZctZQ'
    })
    return tonumber(Receive({ Action = "Number"}).Data)
end)()

-- Step 3: Set up handler for future number updates
-- This handler will add new numbers to our cached Arweave number
Handlers.add("Number", function (msg)
    print("Received message with Data = " .. msg.Data)
    print("Old Number: " .. Number)
    Number = NumberFromArweave + tonumber(msg.Data)
    print("New Number: " .. Number)
end)
```

This example shows how to:

- Cache Arweave data using a self-executing function
- Use the cached data in subsequent message handling
- Combine Arweave data with new incoming data

### Example 2: Dynamic Transaction Processing

This example shows how to process arbitrary Arweave transactions and maintain state between requests:

```lua
-- Table to store pending requests (maps transaction ID to original sender)
local PendingRequests = {}

-- Step 1: Define which transactions your process will accept
print("Step 1: Defining acceptable transactions")
ao.addAssignable("processArweaveNumber", function (msg)
    return msg.Tags["Action"] == "Number"
end)

-- Step 2: Set up handler for initiating the processing
Handlers.add(
    "ProcessArweaveNumber",
    function (msg)
        if not msg.Tags["ArweaveTx"] then
            print("Error: No ArweaveTx tag provided")
            return
        end
        local txId = msg.Tags["ArweaveTx"]
        print("Assigning Arweave transaction: " .. txId)
        -- Store the original sender associated with this transaction ID
        PendingRequests[txId] = msg.From
        -- Assign the transaction to this process
        Assign({
            Processes = { ao.id },
            Message = txId
        })
        print("Assignment requested; waiting for data...")
    end
)

-- Step 3: Set up handler for processing the assigned message
Handlers.add(
    "Number",
    function (msg)
        local txId = msg.Id  -- The ID of the assigned message
        local originalSender = PendingRequests[txId]
        if not originalSender then
            print("Error: No pending request found for transaction " .. txId)
            return
        end
        local data = msg.Data
        if not data or not tonumber(data) then
            print("Error: Invalid number data in assigned message")
            return
        end
        local number = tonumber(data)
        local result = number + 1
        print(string.format("Processing: %d + 1 = %d", number, result))
        -- Send the result back to the original sender
        Send({
            Target = originalSender,
            Data = tostring(result)
        })
        -- Clean up the pending request
        PendingRequests[txId] = nil
    end
)
```

To use this example:

```lua
Send({
    Target = ao.id,
    Action = "ProcessArweaveNumber",
    Tags = {
        ArweaveTx = "YOUR-ARWEAVE-TX-ID"  -- ID of a transaction containing a number
    }
})
```

This example demonstrates:

- Processing arbitrary Arweave transactions
- Maintaining state between requests using a pending requests table
- Sending results back to the original requester
- Error handling and request cleanup

:::warning
When using `Assign` to bridge Arweave data into AO, you must ensure that:

1. The Arweave


Prompt: This is a Markdown documentation from the AO documentation repository. This is a continuation from the previous part. Filename: data.md Path: ../ao-cookbook/src/references/data.md

Response:
1
        print(string.format("Processing: %d + 1 = %d", number, result))
        -- Send the result back to the original sender
        Send({
            Target = originalSender,
            Data = tostring(result)
        })
        -- Clean up the pending request
        PendingRequests[txId] = nil
    end
)
```

To use this example:

```lua
Send({
    Target = ao.id,
    Action = "ProcessArweaveNumber",
    Tags = {
        ArweaveTx = "YOUR-ARWEAVE-TX-ID"  -- ID of a transaction containing a number
    }
})
```

This example demonstrates:

- Processing arbitrary Arweave transactions
- Maintaining state between requests using a pending requests table
- Sending results back to the original requester
- Error handling and request cleanup

:::warning
When using `Assign` to bridge Arweave data into AO, you must ensure that:

1. The Arweave transaction you're assigning matches one of your defined assignables
2. You have a corresponding handler or receiver set up to process that transaction type
3. The handler's pattern matching matches the assigned transaction's tags/properties

For example, if you're assigning a transaction with `Action = "Number"`, you need:

- An assignable that accepts `msg.Tags["Action"] == "Number"`
- Either a `Receive` function or a handler that matches the same pattern
- Both the assignable and handler must use consistent pattern matching
  :::

## Important Limitations {#assignable-limitations}

There are critical limitations to be aware of when working with assignables:

1. **Matching is Required**: Transactions must match at least one of your defined assignable patterns to be accepted.

2. **Blacklisting is Permanent**: If you attempt to assign a transaction before defining an appropriate assignable, it will be permanently blacklisted. Even if you later add a matching assignable, that transaction will never be accepted.

3. **One-time Assignment**: Each Arweave transaction can only be assigned once to a given process. Subsequent assignments of the same transaction will be ignored.

## Proper Sequence for Assigning Arweave Transactions

For successful assignment of Arweave transactions, follow these steps:

1. **Define assignables** to specify which Arweave transactions your process will accept
2. **Wait for any transaction confirmations** (by default, 20 confirmations are required)
3. **Set up handlers or listeners** with `Receive` or `Handlers.add` to process the data
4. **Assign the Arweave transaction** to your process (see [Assignment Methods](#assignment-methods))

The order of steps 3 and 4 can be interchanged based on your needs:

- When using `Receive` in a script loaded with `.load`, ensure `Assign` is placed before `Receive` to prevent the process from hanging, as `Receive` is blocking.
- When using handlers or running commands separately in the shell, the order doesn't matter as handlers will catch messages whenever they arrive

## Why Access Data from Arweave?

There are several practical reasons to access Arweave data from your ao process:

1. **Efficient Handling of Large Data**: For larger content, directly accessing Arweave is more efficient:

   - Reference large media files (images, videos, documents) without storing them in your process
   - Work with datasets too large to fit in process memory
   - Maintain a lightweight process that can access substantial external resources

2. **External Data for Decision-Making**: Your process may need data stored on Arweave to make informed decisions. For example:

   - Reading token price data stored by an oracle
   - Accessing verified identity information
   - Retrieving voting records or governance data

3. **Dynamic Loading of Features**: Rather than including all functionality in your initial process code:

   - Load modules or plugins from Arweave as needed
   - Update configuration without redeploying your entire process
   - Implement upgradable components with new versions stored on Arweave

This approach allows you to create more sophisticated applications that leverage Arweave's permanent storage while maintaining efficient process execution in the ao environment.

When another process Assigns a transaction to this process, you can also use handlers to process the data asynchronously.


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: lua-optimization.md Path: ../ao-cookbook/src/references/lua-optimization.md

Response:
# Lua Optimization Guide for AO Platform

This guide provides practical tips for writing efficient, fast, and performant Lua code for on-chain programs on the AO platform.

## Table Operations

### Appending Elements

```lua
-- ❌ Inefficient: Up to 7x slower in tight loops
table.insert(t, v)

-- ✅ Efficient: Direct indexing is ~2x faster
t[#t + 1] = v
```

### Removing Elements

```lua
-- ❌ Inefficient: Shifts all elements left
table.remove(t, 1)

-- ✅ Efficient: Remove from end
local x = t[#t]
t[#t] = nil
```

## Variable Access

### Local Variables

```lua
-- ❌ Inefficient: Global lookup each time
for i = 1, 1000 do
  math.sin(i)
end

-- ✅ Efficient: Cache the function
local sin = math.sin
for i = 1, 1000 do
  sin(i)  -- ~30% faster in loops
end
```

### Upvalues

```lua
-- ❌ Inefficient: Config lookup on each call
Handlers.add("ValidateGameToken",
  function(msg)
    local config = ao.config
    validateToken(msg, config)
  end
)

-- ✅ Efficient: Cache config as upvalue
local config = ao.config
Handlers.add("ValidateGameToken",
  function(msg)
    validateToken(msg, config)
  end
)
```

## String Operations

### String Concatenation

```lua
-- ❌ Inefficient: Creates many intermediate strings
local str = ""
for i = 1, N do
  str = str .. "line" .. i
end

-- ✅ Efficient: Single concatenation at end
local lines = {}
for i = 1, N do
  lines[i] = "line" .. i
end
local str = table.concat(lines)
```

### Pattern Matching

```lua
-- ❌ Inefficient: Recompiles pattern on each iteration
for line in io.lines() do
  if line:match("^%s*(%w+)%s*=%s*(%w+)") then
    -- Process match
  end
end

-- ✅ Efficient: Compile pattern once
local pattern = "^%s*(%w+)%s*=%s*(%w+)"
for line in io.lines() do
  if line:match(pattern) then
    -- Process match
  end
end
```

## Memory Management

### Table Reuse

```lua
-- ❌ Inefficient: Creates new table on each call
Handlers.add("ComputeGameResults",
  function(msg)
    local results = {}
    -- Fill results
    return results
  end
)

-- ✅ Efficient: Reuse and clear table
local results = {}
Handlers.add("ComputeGameResults",
  function(msg)
    for k in pairs(results) do results[k] = nil end
    -- Fill results
    return results
  end
)
```

### Minimize Garbage Creation

```lua
-- ❌ Inefficient: Creates new response table on every transfer
local function createTransferResponse(sender, recipient, amount)
  return {
    from = sender,
    to = recipient,
    quantity = amount,
    success = true,
    newBalance = Balances[sender],
    tags = {
      Action = "Transfer-Complete",
      Type = "Token"
    }
  }
end

-- ✅ Efficient: Reuse template table
local transferResponse = {
  from = nil,
  to = nil,
  quantity = 0,
  success = false,
  newBalance = 0,
  tags = {
    Action = "Transfer-Complete",
    Type = "Token"
  }
}

local function createTransferResponse(sender, recipient, amount)
  transferResponse.from = sender
  transferResponse.to = recipient
  transferResponse.quantity = amount
  transferResponse.success = true
  transferResponse.newBalance = Balances[sender]
  return transferResponse
end
```

## Blockchain-Specific Optimizations

### State Management

```lua
-- ❌ Inefficient: Multiple separate state updates
for _, item in ipairs(items) do
  ao.send({ Target = "processID", Action = "Update", Data = item })
end

-- ✅ Efficient: Batch updates into single message
local updates = {}
for _, item in ipairs(items) do
  table.insert(updates, item)
end
ao.send({ Target = "processID", Action = "BatchUpdate", Data = updates })
```

## Additional Resources

- [Lua Performance Guide](https://www.lua.org/gems/sample.pdf)
- Special thanks to [@allquantor](https://x.com/allquantor/status/1887370546259644728?s=12) for sharing optimization tips


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: index.md Path: ../ao-cookbook/src/references/betteridea/index.md

Response:
---
prev:
  text: "0rbit"
  link: "../0rbit/index"
next:
  text: "aos"
  link: "../aos/index"
---

# BetterIDEa

[BetterIDEa](https://ide.betteridea.dev) is a custom web based IDE for developing on ao.

It offers a built in Lua language server with ao definitions, so you don't need to install anything. Just open the IDE and start coding!

Features include:

- Code completion
- Cell based notebook ui for rapid development
- Easy process management
- Markdown and Latex cell support
- Share projects with anyone through ao processes
- Tight integration with [ao package manager](https://apm.betteridea.dev)

Read detailed information about the various features and integrations of the IDE in the [documentation](https://docs.betteridea.dev).


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: index.md Path: ../ao-cookbook/src/zh/index.md

Response:
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home

hero:
  name: ""
  text: "超级并行计算机"
  tagline: "任何规模的去中心化计算。Arweave 上独有。"
  actions:
    - theme: brand
      text: 即刻体验！
      link: /zh/welcome/index

features:
  - title: 教程
    details: 手把手教你入门 ao
    link: /zh/tutorials/index

  - title: 指南
    details: 各个功能部件的详细说明
    link: /zh/guides/index

  - title: 概念
    details: 搞懂 ao 网络背后的运行原理
    link: /zh/concepts/index
---


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: llms-explanation.md Path: ../ao-cookbook/src/zh/llms-explanation.md

Response:
---
layout: doc
title: LLMs 文件文档
---

# LLMs 文件文档

## **[llms.txt](/zh/llms.txt)**:

- ao 生态系统的结构化概述。
- 适用于导航文档或回答一般问题的 AI 工具。
- 适合具有网络搜索功能的 AI 代理。

## **[llms-full.txt](/zh/llms-full.txt)**:

- 完整的技术文档。
- 为深入分析、故障排除或聊天机器人集成而设计。
- 为复杂查询提供详尽细节。

::: info
llms-full.txt 文件仅包含来自参考资料和发行说明的内容，测试表明这种专注的方法在 AI 模型中表现更好。
:::


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: index.md Path: ../ao-cookbook/src/zh/tutorials/index.md

Response:
---
prev:
  text: "测试网信息"
  link: "/welcome/legacynet-info"
next:
  text: "开始"
  link: "./begin/index"
---

# 教程

在这里，我们创建了一系列教程来帮助你开始使用 aos 并构建你的第一个进程。 这些教程包括交互式指南、代码片段和示例，以帮助你熟悉 aos 环境。

## 教程列表

- [开始 - 互动指南](begin/index)

- [机器人和游戏](bots-and-games/index)


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: ao-effect.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/ao-effect.md

Response:
---
prev:
  text: "Bots and Games"
  link: "./index"
---

# 我们来玩个游戏吧!

你是冠军，你通过教程获得了力量！ 现在，让我们休息一下，开始一些令人兴奋的事情。 怎么样？我们来一款能为你的学习之旅增添乐趣的游戏！

![AO-效应游戏横幅](./ao-effect-game-banner.png)

## 什么样的游戏？

`ao-effect` 是一款游戏，你可以直接从你的终端与全球好友进行实时竞赛。我们为这次冒险建立了一个全球游戏进程。

规则很简单。 每个玩家一开始都在 40x40 的网格上，生命值为 100，能量为 0。随着时间的推移，你的能量会补充到最大 100。在网格中导航，找到其他玩家，在他们处于攻击范围内时，使用你的能量进行攻击。战斗将持续进行，直到只剩下一名玩家或规定的游戏结束时间。

查看[竞技场机制](arena-mechanics.md) 和[扩展竞技场](build-game.md) 指南，以更深入地了解游戏。

> 注意：如果某些命令语法看起来不熟悉，请不要担心。专注于在更高层次上的目标，理解每个命令的目的，最重要的是，享受游戏！

## 准备 `ao-effect` 的冒险

要加入这场全球冒险活动，你需要做好一些准备。 别担心，就像数数 1-2-3 一样简单！

1.**安装 aos**
启动你的终端并运行：

```bash
npm i -g https://get_ao.g8way.io
```

2.**启动 aos**
接下来，创建 aos 实例：

```bash
aos
```

3.**设置游戏ID**
让我们保留我们的游戏服务器 ID 以便快速访问：

```lua
Game = "tm1jYBC0F2gTZ0EuUQKq5q_esxITDFkAG6QEpLbpI9I"
```

瞧！ 你已准备好加入游戏。

## 如何注册游戏

准备好加入了吗？ 只需几个简单的步骤：

### 注册游戏服务器

`ao` 中进程之间的所有通信都是通过消息进行的。要注册，请将此消息发送到游戏服务器：

```lua
Send({ Target = Game, Action = "Register" })
```

这会将你置于 `Waiting` 大厅。需要支付少量费用来确认你的位置。

### 确认你的位置

为了确认你的位置，你需要一些代币。你可以通过向游戏发送以下消息来获取它们：

```lua
Send({ Target = Game, Action = "RequestTokens"})
```

收到代币后，请通过支付游戏入场费来确认你的位置，如下所示：

```lua
Send({ Target = Game, Action = "Transfer", Recipient = Game, Quantity = "1000"})
```

等待几秒钟，你将在终端中看到有关玩家付款和状态的实时更新。

## 让游戏开始

### 游戏机制

游戏开始：如果至少有 2 名玩家付款，则游戏在 2 分钟 `WaitTime` 后开始。不付费的玩家将被踢除。如果没有足够的玩家付费，那么付费的玩家将获得退款。

游戏开始后，玩家会随机的落到网格点上。

### 该你移动了

移动：你能做的第一件事就是移动，不需要能量！你可以向任何方向移动一个方块——上、下、左、右或对角线。除了方向之外，你还必须传递你的玩家 ID，以帮助游戏识别你的移动。就是这样：

```lua
Send({ Target = Game, Action = "PlayerMove", Player = ao.id, Direction = "DownRight"})
```

网格上可用的移动如下：

```lua
Up = {x = 0, y = -1},
Down = {x = 0, y = 1},
Left = {x = -1, y = 0},
Right = {x = 1, y = 0},
UpRight = {x = 1, y = -1},
UpLeft = {x = -1, y = -1},
DownRight = {x = 1, y = 1},
DownLeft = {x = -1, y = 1}
```

> 请记住：方向区分大小写！

如果你离开网格，你就会出现在对面。

### 出击时间到了

发起攻击：随着游戏的进行，你会积累能量。 用它攻击 3x3 网格范围内的其他玩家。 你的攻击不会伤害你自己，但会影响范围内的其他人。

```lua
Send({ Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = "energy_integer"})
```

生命值从 100 开始，并随着其他玩家的攻击而减少。 达到 0 时，游戏就结束了。

## 总结一下

当只剩下一名玩家或游戏时间到时，游戏结束。获胜者将获得奖励，然后返回大厅进行另一轮比赛。

喜欢这个游戏吗？如果有一种方法可以让你的体验变得更好或提高你获胜的几率，该怎么办？ 查看下一篇指南来了解一下🤔


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: attacking.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/attacking.md

Response:
# 自动响应

根据我们的[上一个指南](decisions)，我们的创作已经从一个简单的机器人发展成为一个复杂的自主代理。 现在，让我们进一步增强它的能力，添加反击功能，让它能够立即对对手的攻击进行反击，有可能在他们撤退到安全地带之前打他们个措手不及。

## 编写代码

将以下handler添加到你的 `bot.lua` 文件中即可：

```lua
-- 被其他玩家击中时自动攻击的handler。
Handlers.add(
  "ReturnAttack",
  Handlers.utils.hasMatchingTag("Action", "Hit"),
  function (msg)
      local playerEnergy = LatestGameState.Players[ao.id].energy
      if playerEnergy == undefined then
        print("Unable to read energy.")
        ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."})
      elseif playerEnergy == 0 then
        print("Player has insufficient energy.")
        ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."})
      else
        print("Returning attack.")
        ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)})
      end
      InAction = false
      ao.send({Target = ao.id, Action = "Tick"})
  end
)
```

每当你的玩家受到攻击时，你都会收到一条包含 `Hit` 动作的消息。 这种设置可确保你的代理在拥有足够能量的情况下能够进行快速反击。

你可以在下面的下拉展开块中参考 `bot.lua` 的最新代码：

  更新后的 bot.lua 文件

```lua
LatestGameState = LatestGameState or nil

function inRange(x1, y1, x2, y2, range)
  return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range
end

function decideNextAction()
  local player = LatestGameState.Players[ao.id]
  local targetInRange = false

  for target, state in pairs(LatestGameState.Players) do
    if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then
        targetInRange = true
        break
    end
  end

  if player.energy > 5 and targetInRange then
    print("Player in range. Attacking.")
    ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)})
  else
    print("No player in range or insufficient energy. Moving randomly.")
    local directionMap = {"Up", "Down", "Left", "Right", "UpRight", "UpLeft", "DownRight", "DownLeft"}
    local randomIndex = math.random(#directionMap)
    ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = directionMap[randomIndex]})
  end
end

Handlers.add(
  "HandleAnnouncements",
  Handlers.utils.hasMatchingTag("Action", "Announcement"),
  function (msg)
    ao.send({Target = Game, Action = "GetGameState"})
    print(msg.Event .. ": " .. msg.Data)
  end
)

Handlers.add(
  "UpdateGameState",
  Handlers.utils.hasMatchingTag("Action", "GameState"),
  function (msg)
    local json = require("json")
    LatestGameState = json.decode(msg.Data)
    ao.send({Target = ao.id, Action = "UpdatedGameState"})
  end
)

Handlers.add(
  "decideNextAction",
  Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"),
  function ()
    if LatestGameState.GameMode ~= "Playing" then
      return
    end
    print("Deciding next action.")
    decideNextAction()
  end
)

Handlers.add(
  "ReturnAttack",
  Handlers.utils.hasMatchingTag("Action", "Hit"),
  function (msg)
      local playerEnergy = LatestGameState.Players[ao.id].energy
      if playerEnergy == undefined then
        print("Unable to read energy.")
        ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."})
      elseif playerEnergy == 0 then
        print("Player has insufficient energy.")
        ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."})
      else
        print("Returning attack.")
        ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)})
      end
      InAction = false
      ao.send({Target = ao.id, Action = "Tick"})
  end
)
```

## 加载和测试

要激活并测试反击功能，请在你的 aos 玩家终端中加载机器人文件：

```lua
.load bot.lua
```

观察你的终端里自主代理的响应，现在增加了立即报复的能力。 此功能展示了代理不断发展的战略深度和自主权。 在接下来的部分中，我们将巩固迄今为止收集的所有知识，并添加一些优化功能。


Prompt:
This is a Markdown documentation from the AO documentation repository. Filename: arena-mechanics.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/arena-mechanics.md
Note: This content continues in the next part.

Response:
# 竞技场机制

本指南全面概述了在 `aos` 中设计和管理竞技场类型游戏所必需的基本机制。在竞技场游戏中，参与者进行回合比赛，有策略地相互竞争以消灭对方，直到出现唯一的胜利者。

这里介绍的框架为制作各种游戏奠定了基础，所有游戏都共享相同的核心功能。 探索游戏开发的复杂性，并在这个多功能的舞台上释放你的创造力。

## 核心功能

现在，让我们深入了解竞技场风格游戏的核心功能：

1. **游戏进展模式：**

   竞技场游戏被打造为循环运行的回合，具有以下进展模式：`"Not-Started"` → `"Waiting"` → `"Playing"` → `[Someone wins or timeout]` → `"Waiting"`...

   > 注意：如果等待状态后没有足够的玩家开始游戏，则循环超时。

   回合为玩家提供了明确的参与时间范围，从而增强了游戏的刺激性。

2. **代币质押：**

   玩家必须存入指定数量的代币（由 `PaymentQty` 定义）才能参与游戏。 这些代币为游戏添加了有形的赌注元素。

3. **奖金奖励：**

   除了胜利的兴奋之外，玩家还被额外奖励的前景所吸引。 构建者可以灵活地提供由 `BonusQty` 定义的奖励代币，每轮分配。 玩家所下的任何赌注也会添加到这些奖金中。 这些奖金作为额外的激励，增强了游戏的竞争精神。

4. **玩家管理：**

   - 等待加入下一场比赛的玩家会在 `Waiting` 表中进行跟踪。
   - 正在比赛的玩家及其游戏状态存储在 `Players` 表中。
   - 被淘汰的玩家会立即从 `Players` 表中删除，并放入 `Waiting` 表中进行下一场比赛。

5. **每轮获胜者奖励：**

   当一个玩家淘汰另一个玩家时，他们不仅可以获得吹牛的权利，还可以获得被淘汰玩家的质押代币作为奖励。 此外，每轮的获胜者都会分享一部分奖金代币以及他们的原始质押的代币，进一步激励玩家争取胜利。

6. **监听器模式：**

   对于那些喜欢观看行动展开的人来说，`Listen` 模式提供了一个无需实际参与即可了解情况的机会。 进程可以注册为侦听器，授予它们访问游戏中所有公告的权限。 虽然他们不作为玩家参与，但听众可以继续观察游戏的进度，除非他们明确要求删除。

7. **游戏状态管理：**

   为了维持竞技场游戏的流畅性和公平性，自动化系统会监督游戏状态的转换。 这些转换包括等待、游戏中和结束阶段。 每个状态的持续时间（例如 `WaitTime` 和 `GameTime`）可确保回合遵守定义的时间范围，从而防止游戏无限期地持续。

你可以在下面的下拉展开块中参考竞技场的代码：

  竞技场游戏蓝图

```lua

-- 竞技场游戏蓝图。

-- 该蓝图提供了在 ao 进程内运行 `竞技场` 风格游戏的框架。
-- 游戏以回合形式进行，玩家的目标是互相消灭，直到只剩下一个，或者直到比赛时间结束。
-- 游戏进程会随着玩家的加入和离开而无限循环。

-- 当一个玩家淘汰另一个玩家时，他们会收到被淘汰玩家的质押代币作为奖励。
-- 此外，建造者可以提供这些代币的奖励作为每轮额外的激励分配。
-- 如果游戏中的目标玩家类型是机器人，提供额外的`奖励`创造让程序员争相生产最佳代理来`挖`到进程的代币的机会

-- 建造者还可以在他们的游戏逻辑中控制框架, 提供类似这样的handler：允许玩家执行游戏中的动作，在适当的时刻调用 `eliminatePlayer()`。

-- 进程还可以在 `监听` 模式下注册，在该模式下它们将接收游戏中的所有公告，但他们自己不加入本轮对战。
-- 除非他们明确要求，否则他们也不会取消注册。

-- 全局变量。

-- 一轮循环包含的游戏进度模式：

-- [Not-Started] -> Waiting -> Playing -> [Someone wins or timeout] -> Waiting...
-- 在等待状态之后如果还没有足够玩家则此循环结束。
GameMode = GameMode or "Not-Started"
StateChangeTime = StateChangeTime or undefined

-- 状态持续时间 （毫秒）
WaitTime = WaitTime or 2 * 60 * 1000 -- 2 分钟
GameTime = GameTime or 20 * 60 * 1000 -- 20 分钟
Now = Now or undefined -- 当前时间，每条消息更新一次。

-- 玩家质押的代币信息。
UNIT = 1000
PaymentToken = PaymentToken or "ADDR"  -- 代币地址
PaymentQty = PaymentQty or tostring(math.floor(UNIT))    -- 注册需要的代币数量
BonusQty = BonusQty or tostring(math.floor(UNIT))        -- 赢家的代币奖金数量

-- 等待进入下一轮游戏的玩家及其支付状态。
Waiting = Waiting or {}
-- 已激活玩家及其状态。
Players = Players or {}
-- 当前游戏的赢家数量。
Winners = 0
-- 订阅了游戏公告的进程。
Listeners = Listeners or {}
-- 开始一个游戏的最小玩家数。
MinimumPlayers = MinimumPlayers or 2

-- 玩家默认状态初始化。
PlayerInitState = PlayerInitState or {}

-- 向所有注册的侦听器发送状态更改公告。
-- @param event: 事件类型或名称。
-- @param description: 事件描述。
function announce(event, description)
    for ix, address in pairs(Listeners) do
        ao.send({
            Target = address,
            Action = "Announcement",
            Event = event,
            Data = description
        })
    end
    return print(Colors.gray .. "Announcement: " .. Colors.red .. event .. " " .. Colors.blue .. description .. Colors.reset)
end

-- 给玩家发送奖励。
-- @param recipient: 获得奖励的玩家。
-- @param qty


Prompt:
This is a Markdown documentation from the AO documentation repository. This is a continuation from the previous part. Filename: arena-mechanics.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/arena-mechanics.md
Note: This content continues in the next part.

Response:
公告的进程。
Listeners = Listeners or {}
-- 开始一个游戏的最小玩家数。
MinimumPlayers = MinimumPlayers or 2

-- 玩家默认状态初始化。
PlayerInitState = PlayerInitState or {}

-- 向所有注册的侦听器发送状态更改公告。
-- @param event: 事件类型或名称。
-- @param description: 事件描述。
function announce(event, description)
    for ix, address in pairs(Listeners) do
        ao.send({
            Target = address,
            Action = "Announcement",
            Event = event,
            Data = description
        })
    end
    return print(Colors.gray .. "Announcement: " .. Colors.red .. event .. " " .. Colors.blue .. description .. Colors.reset)
end

-- 给玩家发送奖励。
-- @param recipient: 获得奖励的玩家。
-- @param qty: 奖励数量。
-- @param reason: 奖励原因。
function sendReward(recipient, qty, reason)
    if type(qty) ~= number then
      qty = tonumber(qty)
    end
    ao.send({
        Target = PaymentToken,
        Action = "Transfer",
        Quantity = tostring(qty),
        Recipient = recipient,
        Reason = reason
    })
    return print(Colors.gray .. "Sent Reward: " ..
      Colors.blue .. tostring(qty) ..
      Colors.gray .. ' tokens to ' ..
      Colors.green .. recipient .. " " ..
      Colors.blue .. reason .. Colors.reset
    )
end

-- 开始玩家准备玩游戏的倒计时。
function startWaitingPeriod()
    GameMode = "Waiting"
    StateChangeTime = Now + WaitTime
    announce("Started-Waiting-Period", "The game is about to begin! Send your token to take part.")
    print('Starting Waiting Period')
end

-- 如果有足够的玩家，则开始游戏。
function startGamePeriod()
    local paidPlayers = 0
    for player, hasPaid in pairs(Waiting) do
        if hasPaid then
            paidPlayers = paidPlayers + 1
        end
    end

    if paidPlayers < MinimumPlayers then
        announce("Not-Enough-Players", "Not enough players registered! Restarting...")
        for player, hasPaid in pairs(Waiting) do
            if hasPaid then
                Waiting[player] = false
                sendReward(player, PaymentQty, "Refund")
            end
        end
        startWaitingPeriod()
        return
    end

    LastTick = undefined
    GameMode = "Playing"
    StateChangeTime = Now + GameTime
    for player, hasPaid in pairs(Waiting) do
        if hasPaid then
            Players[player] = playerInitState()
        else
            ao.send({
                Target = player,
                Action = "Ejected",
                Reason = "Did-Not-Pay"
            })
            removeListener(player) -- 如果玩家未付款，则将其从监听器中删除
        end
    end
    announce("Started-Game", "The game has started. Good luck!")
    print("Game Started....")
end

-- 从游戏中淘汰玩家的handler。
-- @param eliminated: 要被淘汰的玩家。
-- @param eliminator: 发起淘汰的玩家。
function eliminatePlayer(eliminated, eliminator)
    sendReward(eliminator, PaymentQty, "Eliminated-Player")
    Waiting[eliminated] = false
    Players[eliminated] = nil

    ao.send({
        Target = eliminated,
        Action = "Eliminated",
        Eliminator = eliminator
    })

    announce("Player-Eliminated", eliminated .. " was eliminated by " .. eliminator .. "!")

    local playerCount = 0
    for player, _ in pairs(Players) do
        playerCount = playerCount + 1
    end
    print("Eliminating player: " .. eliminated .. " by: " .. eliminator) -- 对于跟踪淘汰很有用

    if playerCount < MinimumPlayers then
        endGame()
    end

end

-- 结束当前游戏并开始一个新的。
function endGame()
    print("Game Over")

    Winners = 0
    Winnings = tonumber(BonusQty) / Winners -- 计算每位玩家的奖金

    for player, _ in pairs(Players) do
        Winners = Winners + 1
    end

    Winnings = tonumber(BonusQty) / Winners

    for player, _ in pairs(Players) do
        -- addLog("EndGame", "Sending reward of:".. Winnings + PaymentQty .. "to player: " .. player) -- Useful for tracking rewards
        sendReward(player, Winnings + tonumber(PaymentQty), "Win")
        Waiting[player] = false
    end

    Players = {}
    announce("Game-Ended", "Congratulations! The game has ended. Remaining players at conclusion: " .. Winners .. ".")
    startWaitingPeriod()
end

-- 从监听器列表移除一个监听器。
-- @param listener: 待移除的监听器。
function removeListener(listener)
    local idx = 0
    for i, v in ipairs(Listeners) do
        if v == listener then
            idx = i
            break
        end
    end
    if idx > 0 then
        table.remove(Listeners, idx)
    end
end

-- handler: 游戏状态管理

-- 定时消息handler，管理游戏状态切换。
Handlers.add(
    "Game-State-Timers",
    function(Msg)
        return "continue"
    end,
    function(Msg)
        Now = Msg.Timestamp
        if GameMode == "Not-Started" then
            startWaitingPeriod()
        elseif GameMode == "Waiting" then
            if Now > StateChangeTime then
                startGamePeriod()
            end
        elseif GameMode == "Playing" then
            if onTick and type(onTick) == "function" then
              onTick()
            end
            if Now > StateChangeTime then
                endGame()
            end
        end
    end
)

-- 玩家质押以参与下一轮游戏的handler。
Handlers.add(
    "Transfer",
    function(Msg)
        return
            Msg.Action == "Credit-Notice" and
            Msg.From == PaymentToken and
            tonumber(Msg.Quantity) >= tonumber(PaymentQty) and "continue"
    end,
    function(Msg)
        Waiting[Msg.Sender] = true
        ao.send({
            Target = Msg.Sender,
            Action = "Payment-Received"
        })
        announce("Player-Ready", Msg.Sender .. " is ready to play!")
    end
)

-- 为下轮游戏注册新玩家并为其订阅事件信息。
Handlers.add(
    "Register",
    Handlers.utils.hasMatchingTag("Action", "Register"),
    function(Msg)
        if Msg.Mode ~= "Listen" and Waiting[Msg.From] == undefined then
            Waiting[Msg.From] = false
        end
        removeListener(Msg.From)
        table.insert(Listeners, Msg.From)
        ao.send({
            Target = Msg.From,
            Action = "Registered"
        })
        announce("New Player Registered", Msg.From .. " has joined in waiting.")
    end
)

-- 注销玩家并停止向他们发送事件信息。
Handlers.add(
    "Unregister",
    Handlers.utils.hasMatchingTag("Action", "Unregister"),
    function(Msg)
        removeListener(Msg.From)
        ao.send({
            Target = Msg.From,
            Action = "Unregistered"
        })
    end
)

-- 将投注金额添加到 BonusQty
Handlers.add(
    "AddBet",
    Handlers.utils.hasMatchingTag("Reason", "AddBet"),
    function(Msg)
        BonusQty = tonumber(BonusQty) + tonumber(Msg.Tags.Quantity)
        announce("Bet-Added", Msg.From .. "has placed a bet. " .. "BonusQty amount increased by " .. Msg.Tags.Quantity .. "!")
    end
)

-- 检索当前游戏状态。
Handlers.add(
    "GetGameState",
    Handlers.utils.hasMatchingTag("Action", "GetGameState"),
    function (Msg)
        local json = require("json")
        local TimeRemaining = StateChangeTime - Now
        local GameState = json.encode({
            GameMode = GameMode,
            TimeRemaining = TimeRemaining,
            Players = Players,
            })
        ao.send({
            Target = Msg.From,
            Action = "GameState",
            Data = GameState})
    end
)

-- 提醒用户每个游戏状态的剩余时间。
Handlers.add(
    "AnnounceTick",
    Handlers.utils.hasMatchingTag("Action", "Tick"),
    function (Msg)
        local TimeRemaining = StateChangeTime - Now
        if GameMode == "Waiting" then
            announce("Tick", "The game will start in " .. (TimeRemaining/1000) .. " seconds.")
        elseif GameMode == "Playing" then
            announce("Tick", "The game will end in " .. (TimeRemaining/1000) .. " seconds.")
        end
    end
)

-- 根据请求向没有余额的玩


Prompt: This is a Markdown documentation from the AO documentation repository. This is a continuation from the previous part. Filename: arena-mechanics.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/arena-mechanics.md

Response:
 TimeRemaining = StateChangeTime - Now
        local GameState = json.encode({
            GameMode = GameMode,
            TimeRemaining = TimeRemaining,
            Players = Players,
            })
        ao.send({
            Target = Msg.From,
            Action = "GameState",
            Data = GameState})
    end
)

-- 提醒用户每个游戏状态的剩余时间。
Handlers.add(
    "AnnounceTick",
    Handlers.utils.hasMatchingTag("Action", "Tick"),
    function (Msg)
        local TimeRemaining = StateChangeTime - Now
        if GameMode == "Waiting" then
            announce("Tick", "The game will start in " .. (TimeRemaining/1000) .. " seconds.")
        elseif GameMode == "Playing" then
            announce("Tick", "The game will end in " .. (TimeRemaining/1000) .. " seconds.")
        end
    end
)

-- 根据请求向没有余额的玩家发送代币
Handlers.add(
    "RequestTokens",
    Handlers.utils.hasMatchingTag("Action", "RequestTokens"),
    function (Msg)
        print("Transfering Tokens: " .. tostring(math.floor(10000 * UNIT)))
        ao.send({
            Target = ao.id,
            Action = "Transfer",
            Quantity = tostring(math.floor(10000 * UNIT)),
            Recipient = Msg.From,
        })
    end
)
```

</details>

## 竞技场游戏蓝图

对于那些有兴趣使用此 arena 框架的人，我们已通过蓝图轻松访问此代码。 只需在终端中运行以下代码：

```lua
.load-blueprint arena
```

## 总结

了解竞技场的机制不仅可以帮助你改进上一节中创建的自主代理，还可以让你利用核心功能来打造独特的游戏。

在接下来的 `Building a Game` 部分中，我们将深入探讨利用这些机制在此框架内构建迷人且独一无二的游戏的艺术。 准备好踏上游戏开发动态领域的旅程吧！ 🎮


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: index.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/index.md

Response:
---
prev:
  text: "代币门控"
  link: "../begin/tokengating"
next:
  text: "我们玩个游戏"
  link: "./ao-effect"
---

# 机器人和游戏

利用我们上一章所学，本节将引导你了解 aos 中如何创造自动化的机器人和游戏。你将学习创建代理，让你的代理在游戏场景内游玩。

## 章节

### 游戏入门

- [0. **我们来玩游戏吧：**_在aos上体验游戏_](ao-effect)

### 通过自动化增强游戏交互

- [1. **公告解读：** _游戏内公告解读_](announcements)
- [2. **获取游戏状态：** _检索并处理最新的游戏状态_](game-state)
- [3. **战略决策：** _利用自动化来确定你的下一步行动_](decisions)
- [4. **自动响应：** _通过自动化简化攻击响应_](attacking)
- [5. **\*整合：** _结合你的技能来打造一个自主代理_](bringing-together)

### 游戏开发讲解

- [6. **竞技场机制：** _探索游戏竞技场的底层机制_](arena-mechanics)
- [7. **扩展竞技场：** _在竞技场上构建独特的游戏逻辑_](build-game)

探索和创造的旅程正在等待着你。 让冒险开始吧！


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: announcements.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/announcements.md

Response:
# 解析公告

欢迎回到你的编码之旅。 是时候使用你从之前的教程中获得的技能来增强你的游戏体验了。

在游戏过程中，你可能会注意到终端中出现的公告。这些公告是游戏向玩家传达重要事件的方式。然而，这些消息有时看起来很神秘，或者你可能会发现自己得反复检查收件箱才能获取更多详细信息。

直接从你的终端访问这些信息不是很方便吗？ 嗯，有一种方法可以做到这一点！

通过使用 [handlers](../../references/handlers.md)，你可以创建一个自主代理来为你读取这些信息，标志着从简单的机器人到能够直接解析游戏事件并对其采取行动的实体的进化。

## 设置开发环境

首先在你的首选目录中创建一个名为 `bot.lua` 的新文件。

> 理想情况下，该文件应放置在 player 进程的同一目录中，以简化代码的加载。 否则，你需要使用相对路径来访问该文件。

## 编写代码

让我们深入研究下其中的逻辑。

aos 中的每个handler都需要三个关键信息：

- `name`：handler 的唯一名称
- `pattern`: handler 识别的模式，触发其执行
- `handle`：模式匹配时执行的操作

以下是编写用于打印公告详细信息的 handler 的方法：

```lua
-- 直接在终端中打印游戏公告的handler。
Handlers.add(
  "PrintAnnouncements",
  Handlers.utils.hasMatchingTag("Action", "Announcement"),
  function (msg)
    print(msg.Event .. ": " .. msg.Data)
  end
)
```

在本例中，handler的名称是 `"PrintAnnouncements"`。 它使用特殊的内置实用程序（`hasMatchingTags`）检查传入消息是否已被标记为公告。 如果为 true，则 handler 将打印事件和数据，则直接显示公告的标题和描述。

> 注意：一旦消息被 `handled`，它将从你的 `收件箱` 中丢弃。

## 加载和测试

现在，让我们在游戏中实现这一点。

导航到你的 aos 玩家终端并进入游戏会话。

用下面的命令来加载 `bot.lua` 文件以激活handler：

```lua
.load bot.lua
```

现在，你将看到游戏公告直接出现在你的终端中，提供实时展示，且无需手动查看收件箱。

恭喜！ 你刚刚迈出了在 `aos` 上构建机器人的第一步。 但让我们继续努力为其添加更多功能 🌐


Prompt:
This is a Markdown documentation from the AO documentation repository. Filename: bringing-together.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/bringing-together.md
Note: This content continues in the next part.

Response:
# 整合在一起

本最终指南总结了我们的系列（教程），你已经逐步构建了一个自主代理。现在，让我们通过一些优化来完善你的代理，以微调其运行。 以下是关键改进的概述：

- **顺序命令执行：** `InAction` 标志的引入确保你的代理的操作是有序的（仅当上一个操作成功执行时才会发生下一个操作）。 这一重要的补充可以防止你的代理对过时的游戏状态采取行动，从而增强其响应能力和准确性。 完整的实现可以在下面的 `bot.lua` 文件的最终代码中找到。

```lua
InAction = InAction or false -- 防止代理同时执行多个操作。
```

- **动态状态更新和决策：** 代理现在采用自动计时逻辑，允许动态更新和决策。 这种逻辑使代理能够自触发状态更新，并在收到 Tick 消息或完成操作时做出后续决策，从而促进自主操作。

```lua
Handlers.add("GetGameStateOnTick", Handlers.utils.hasMatchingTag("Action", "Tick"), function ()
  if not InAction then
    InAction = true
    ao.send({Target = Game, Action = "GetGameState"})
  end
end)
```

- **自动费用转账：** 为了进一步简化其操作并确保不间断地参与游戏，自主代理现在自主处理入场费的转账。

```lua
Handlers.add("AutoPay", Handlers.utils.hasMatchingTag("Action", "AutoPay"), function ()
  ao.send({Target = Game, Action = "Transfer", Recipient = Game, Quantity = "1000"})
end)
```

除了这些功能之外，我们还添加了用于调试目的的日志记录功能和彩色打印以便更好地理解游戏事件。 这些增强功能共同使你的自主代理在游戏环境中更加高效且适应性更强。

你可以在下面的下拉展开块中参考 `bot.lua` 的完整代码，所有新增的内容都额外注释了：

  更新后的 bot.lua 文件

```lua
-- 初始化全局变量来存储最新的游戏状态和游戏主机进程。
LatestGameState = LatestGameState or nil
InAction = InAction or false -- 防止代理同时采取多个操作。

Logs = Logs or {}

colors = {
  red = "\27[31m",
  green = "\27[32m",
  blue = "\27[34m",
  reset = "\27[0m",
  gray = "\27[90m"
}

function addLog(msg, text) -- 函数定义注释用于性能，可用于调试
  Logs[msg] = Logs[msg] or {}
  table.insert(Logs[msg], text)
end

-- 检查两个点是否在给定范围内。
-- @param x1, y1: 第一个点的坐标
-- @param x2, y2: 第二个点的坐标
-- @param range: 点之间允许的最大距离
-- @return: Boolean 指示点是否在指定范围内
function inRange(x1, y1, x2, y2, range)
    return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range
end

-- 根据玩家的距离和能量决定下一步行动。
-- 如果有玩家在范围内，则发起攻击； 否则，随机移动。
function decideNextAction()
  local player = LatestGameState.Players[ao.id]
  local targetInRange = false

  for target, state in pairs(LatestGameState.Players) do
      if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then
          targetInRange = true
          break
      end
  end

  if player.energy > 5 and targetInRange then
    print(colors.red .. "Player in range. Attacking." .. colors.reset)
    ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)})
  else
    print(colors.red .. "No player in range or insufficient energy. Moving randomly." .. colors.reset)
    local directionMap = {"Up", "Down", "Left", "Right", "UpRight", "UpLeft", "DownRight", "DownLeft"}
    local randomIndex = math.random(#directionMap)
    ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = directionMap[randomIndex]})
  end
  InAction = false -- InAction 逻辑添加
end

-- 打印游戏公告并触发游戏状态更新的handler。
Handlers.add(
  "PrintAnnouncements",
  Handlers.utils.hasMatchingTag("Action", "Announcement"),
  function (msg)
    if msg.Event == "Started-Waiting-Period" then
      ao.send({Target = ao.id, Action = "AutoPay"})
    elseif (msg.Event == "Tick" or msg.Event == "Started-Game") and not InAction then
      InAction = true --  InAction 逻辑添加
      ao.send({Target = Game, Action = "GetGameState"})
    elseif InAction then --  InAction 逻辑添加
      print("Previous action still in progress. Skipping.")
    end
    print(colors.green .. msg.Event .. ": " .. msg.Data .. colors.reset)
  end
)

-- 触发游戏状态更新的handler。
Handlers.add(
  "GetGameStateOnTick",
  Handlers.utils.hasMatchingTag("Action", "Tick"),
  function ()
    if not InAction then -- InAction 逻辑添加
      InAction = true -- InAction 逻辑添加
      print(colors.gray .. "Getting game state..." .. colors.reset)
      ao.send({Target = Game, Action = "GetGameState"})
    else
      print("Previous action still in progress. Skipping.")
    end
  end
)

-- 等待期开始时自动付款确认的handler。
Handlers.add(
  "AutoPay",
  Handlers.utils.hasMatchingTag("Action", "AutoPay"),
  function (msg)
    print("Auto-paying confirmation fees.")
    ao.send({ Target = Game, Action = "Transfer", Recipient = Game, Quantity = "1000"})
  end
)

-- 接收游戏状态信息后更新游戏状态的handler。
Handlers.add(
  "UpdateGameState",
  Handlers.utils.hasMatchingTag("Action", "GameState"),
  function (msg)
    local json = require("json")
    LatestGameState = json.decode(msg.Data)
    ao.send({Target = ao.id, Action = "UpdatedGameState"})
    print("Game state updated. Print \'LatestGameState\' for detailed view.")
  end
)

-- 决策下一个最佳操作的handler。
Handlers.add(
  "decideNextAction",
  Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"),
  function ()
    if LatestGameState.GameMode ~= "Playing" then
      InAction = false -- InAction 逻辑添加
      return
    end
    print("Deciding next action.")
    decideNextAction()
    ao.send({Target = ao.id, Action = "Tick"})
  end
)

-- 被其他玩家击中时自动攻击的handler。
Handlers.add(
  "ReturnAttack",
  Handlers.utils.hasMatchingTag("Action", "Hit"),
  function (msg)
    if not InAction then --  InAction 逻辑添加
      InAction = true --  InAction 逻辑添加
      local playerEnergy = LatestGameState.Players[ao.id].energy
      if playerEnergy == undefined then
        print(colors.red .. "Unable to read energy." .. colors.reset)
        ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."})
      elseif playerEnergy == 0 then
        print(colors.red .. "Player has insufficient energy." .. colors.reset)
        ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."})
      else
        print(colors.red .. "Returning attack." .. colors.reset)
        ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)})
      end
      InAction = false --  InAction 逻辑添加
      ao.send({Target = ao.id, Action = "Tick"})
    else
      print("


Prompt: This is a Markdown documentation from the AO documentation repository. This is a continuation from the previous part. Filename: bringing-together.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/bringing-together.md

Response:
      InAction = true --  InAction 逻辑添加
      local playerEnergy = LatestGameState.Players[ao.id].energy
      if playerEnergy == undefined then
        print(colors.red .. "Unable to read energy." .. colors.reset)
        ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."})
      elseif playerEnergy == 0 then
        print(colors.red .. "Player has insufficient energy." .. colors.reset)
        ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."})
      else
        print(colors.red .. "Returning attack." .. colors.reset)
        ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)})
      end
      InAction = false --  InAction 逻辑添加
      ao.send({Target = ao.id, Action = "Tick"})
    else
      print("Previous action still in progress. Skipping.")
    end
  end
)
```

</details>

## 下一步是什么？

你现在已经具备了构建智能自主代理的知识。 是时候将这些知识应用到游戏世界中了。 了解游戏的复杂性并利用你的代理的能力来统治竞技场。 但还有更多的事情要做。

在接下来的部分中，我们将更深入地研究游戏竞技场，提供高级策略来提高代理的性能。 准备好接受挑战了吗？ 让我们看看你能创造什么！ 🕹


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: decisions.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/decisions.md

Response:
# 战略决策

有了[最新游戏状态](game-state)的辅助，你的机器人就可以进化为 `自主代理`。 这一转变标志着功能的升级，不仅支持对游戏状态的响应，还可以根据上下文、能量和邻近度制定策略行为。

## 编写代码

返回到 `bot.lua` 文件并添加以下函数：

```lua
-- 确定两点之间的接近度。
function inRange(x1, y1, x2, y2, range)
    return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range
end

-- 根据距离和能量来战略性地决定下一步行动。
function decideNextAction()
  local player = LatestGameState.Players[ao.id]
  local targetInRange = false

  for target, state in pairs(LatestGameState.Players) do
      if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then
          targetInRange = true
          break
      end
  end

  if player.energy > 5 and targetInRange then
    print("Player in range. Attacking.")
    ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)})
  else
    print("No player in range or insufficient energy. Moving randomly.")
    local directionMap = {"Up", "Down", "Left", "Right", "UpRight", "UpLeft", "DownRight", "DownLeft"}
    local randomIndex = math.random(#directionMap)
    ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = directionMap[randomIndex]})
  end
end
```

`decideNextAction` 函数现在实现了我们的代理（机器人）基于对其环境的全面了解进行思考和行动的能力。 它会分析最新的游戏状态，如果你有足够的能量并且对手处于 `inRange`（攻击范围内），则进行攻击，否则进行移动。

现在再加个 `handler` 即可确保该函数自行运行。

```lua
Handlers.add(
  "decideNextAction",
  Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"),
  function ()
    if LatestGameState.GameMode ~= "Playing" then
      return
    end
    print("Deciding next action.")
    decideNextAction()
  end
)
```

最新游戏状态更新时，该 handler 被触发。 且仅当游戏处于 `Playing` 模式时才会执行操作。

你可以在下面的下拉展开块中参考 `bot.lua` 的最新代码：

  更新后的 bot.lua 文件

```lua
LatestGameState = LatestGameState or nil

function inRange(x1, y1, x2, y2, range)
    return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range
end

function decideNextAction()
  local player = LatestGameState.Players[ao.id]
  local targetInRange = false

  for target, state in pairs(LatestGameState.Players) do
      if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then
          targetInRange = true
          break
      end
  end

  if player.energy > 5 and targetInRange then
    print("Player in range. Attacking.")
    ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)})
  else
    print("No player in range or insufficient energy. Moving randomly.")
    local directionMap = {"Up", "Down", "Left", "Right", "UpRight", "UpLeft", "DownRight", "DownLeft"}
    local randomIndex = math.random(#directionMap)
    ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = directionMap[randomIndex]})
  end
end

Handlers.add(
  "HandleAnnouncements",
  Handlers.utils.hasMatchingTag("Action", "Announcement"),
  function (msg)
    ao.send({Target = Game, Action = "GetGameState"})
    print(msg.Event .. ": " .. msg.Data)
  end
)

Handlers.add(
  "UpdateGameState",
  Handlers.utils.hasMatchingTag("Action", "GameState"),
  function (msg)
    local json = require("json")
    LatestGameState = json.decode(msg.Data)
    ao.send({Target = ao.id, Action = "UpdatedGameState"})
  end
)

Handlers.add(
  "decideNextAction",
  Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"),
  function ()
    if LatestGameState.GameMode ~= "Playing" then
      return
    end
    print("Deciding next action.")
    decideNextAction()
  end
)
```

## 加载和测试

要测试最新的升级，请在 aos 玩家终端中加载文件，如下所示：

```lua
.load bot.lua
```

观察你的进程输出，以了解你的自主代理实时做出的决策，机器人利用当前的游戏状态获得战略优势。 但是，如果在你决定下一步行动时另一个玩家攻击你并逃跑怎么办？ 在下一节中，你将学习在受到攻击后立即自动反击🤺


Prompt:
This is a Markdown documentation from the AO documentation repository. Filename: build-game.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/build-game.md
Note: This content continues in the next part.

Response:
---
next:
  text: "指南"
  link: "/zh/guides/index"
---

# 扩建竞技场

欢迎来到第 2 章的最终指南，你将学习在[上一篇教程](arena-mechanics)中介绍的竞技场框架之上构建自己的游戏。 在本指南中，我们将带你完成创建[ ao-effect ](ao-effect)游戏的过程，即你在本章开始时玩的。 随着你逐步完成此示例，你将深入了解构建游戏逻辑以及与竞技场的核心代码进行交互。

无论你是经验丰富的开发人员还是有抱负的游戏创作者，本指南都将帮助你释放创造力，并在 `aos` 环境中将你独特的游戏创意变为现实。

## 设置开发环境

首先在你的首选目录中创建一个名为 `ao-effect.lua` 的新文件。

> 理想情况下，该文件应放置在游戏进程运行的同一目录中，以方便代码的加载。 否则，你需要使用相对路径来访问该文件。

## 编写代码

现在，让我们深入研究其中的逻辑。

你会注意到你的游戏逻辑将涉及调用竞技场逻辑中定义的函数和变量。 这展示了可组合性的力量，你的游戏构建在现有竞技场逻辑之上，允许两者之间的变量和函数无缝集成。 因为这两种逻辑都成为游戏进程完整逻辑的一部分。

### 初始化游戏机制

首先，定义为游戏机制奠定基础的基本变量和函数：

```lua
-- AO EFFECT: Game Mechanics for AO Arena Game

-- Game grid dimensions
Width = 40 -- Width of the grid
Height = 40 -- Height of the grid
Range = 1 -- The distance for blast effect

-- Player energy settings
MaxEnergy = 100 -- Maximum energy a player can have
EnergyPerSec = 1 -- Energy gained per second

-- Attack settings
AverageMaxStrengthHitsToKill = 3 -- Average number of hits to eliminate a player

-- Initializes default player state
-- @return Table representing player's initial state
function playerInitState()
    return {
        x = math.random(Width/8),
        y = math.random(Height/8),
        health = 100,
        energy = 0
    }
end

-- Function to incrementally increase player's energy
-- Called periodically to update player energy
function onTick()
    if GameMode ~= "Playing" then return end  -- Only active during "Playing" state

    if LastTick == undefined then LastTick = Now end

    local Elapsed = Now - LastTick
    if Elapsed >= 1000 then  -- Actions performed every second
        for player, state in pairs(Players) do
            local newEnergy = math.floor(math.min(MaxEnergy, state.energy + (Elapsed * EnergyPerSec // 2000)))
            state.energy = newEnergy
        end
        LastTick = Now
    end
end
```

此代码初始化游戏的机制，包括网格尺寸、玩家能量和攻击设置。 `playerInitState` 函数在游戏开始时为玩家设置初始状态。

### 玩家移动

接下来，添加玩家移动的代码：

```lua
-- Handles player movement
-- @param msg: Message request sent by player with movement direction and player info
function move(msg)
    local playerToMove = msg.From
    local direction = msg.Tags.Direction

    local directionMap = {
        Up = {x = 0, y = -1}, Down = {x = 0, y = 1},
        Left = {x = -1, y = 0}, Right = {x = 1, y = 0},
        UpRight = {x = 1, y = -1}, UpLeft = {x = -1, y = -1},
        DownRight = {x = 1, y = 1}, DownLeft = {x = -1, y = 1}
    }

    -- calculate and update new coordinates
    if directionMap[direction] then
        local newX = Players[playerToMove].x + directionMap[direction].x
        local newY = Players[playerToMove].y + directionMap[direction].y

        -- updates player coordinates while checking for grid boundaries
        Players[playerToMove].x = (newX - 1) % Width + 1
        Players[playerToMove].y = (newY - 1) % Height + 1

        announce("Player-Moved", playerToMove .. " moved to " .. Players[playerToMove].x .. "," .. Players[playerToMove].y .. ".")
    else
        ao.send({Target = playerToMove, Action = "Move-Failed", Reason = "Invalid direction."})
    end
    onTick()  -- Optional: Update energy each move
end
```

`move` 函数根据所选方向计算玩家新的坐标，同时确保玩家保持在网格边界内。 玩家的移动为你的游戏添加了动态交互，并向所有玩家和听众同步。

### 玩家攻击

然后你必须实现玩家攻击的逻辑：

```lua
-- Handles player attacks
-- @param msg: Message request sent by player with attack info and player state
function attack(msg)
    local player = msg.From
    local attackEnergy = tonumber(msg.Tags.AttackEnergy)

    -- get player coordinates
    local x = Players[player].x
    local y = Players[player].y

    -- check if player has enough energy to attack
    if Players[player].energy < attackEnergy then
        ao.send({Target = player, Action = "Attack-Failed", Reason = "Not enough energy."})
        return
    end

    -- update player energy and calculate damage
    Players[player].energy = Players[player].energy - attackEnergy
    local damage = math.floor((math.random() * 2 * attackEnergy) * (1/AverageMaxStrengthHitsToKill))

    announce("Attack", player .. " has launched a " .. damage .. " damage attack from " .. x .. "," .. y .. "!")

    -- check if any player is within range and update their status
    for target, state in pairs(Players) do
        if target ~= player and inRange(x, y, state.x, state.y, Range) then
            local newHealth = state.health - damage
            if newHealth <= 0 then
                eliminatePlayer(target, player)
            else
                Players[target].health = newHealth
                ao.send({Target = target, Action = "Hit", Damage = tostring(damage), Health = tostring(newHealth)})
                ao.send({Target = player, Action = "Successful-Hit", Recipient = target, Damage = tostring(damage), Health = tostring(newHealth)})
            end
        end
    end
end

-- Helper function to check if a target is within range
-- @param x1, y1: Coordinates of the attacker
-- @param x2, y2: Coordinates of the potential target
-- @param range: Attack range
-- @return Boolean indicating if the target is within range
function inRange(x1, y1, x2, y2, range)
    return x2 >= (x1 - range) and x2 <= (x1 + range) and y2 >= (y1 - range) and y2 <= (y1 + range)
end
```

`attack` 函数根据攻击能量计算伤害，检查玩家能量，并相应更新玩家生命值。 玩家攻击会在游戏中添加竞争元素，让玩家能够相互互动。 这些攻击也会向玩家和听众同步，以获取游戏的实时更新。

### 处理逻辑

最后，你必须设置handler：

```lua
-- HANDLERS: Game state management for AO-Effect

-- Handler for player movement
Handlers.add("PlayerMove", Handlers.utils.hasMatchingTag("Action", "PlayerMove"), move)

-- Handler for player attacks
Handlers.add("PlayerAttack", Handlers.utils.hasMatchingTag("Action", "PlayerAttack"), attack)
```

正如前面的指南中所见，handler在满足各自的模式时帮助触发功能。


Prompt: This is a Markdown documentation from the AO documentation repository. This is a continuation from the previous part. Filename: build-game.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/build-game.md

Response:
检查玩家能量，并相应更新玩家生命值。 玩家攻击会在游戏中添加竞争元素，让玩家能够相互互动。 这些攻击也会向玩家和听众同步，以获取游戏的实时更新。

### 处理逻辑

最后，你必须设置handler：

```lua
-- HANDLERS: Game state management for AO-Effect

-- Handler for player movement
Handlers.add("PlayerMove", Handlers.utils.hasMatchingTag("Action", "PlayerMove"), move)

-- Handler for player attacks
Handlers.add("PlayerAttack", Handlers.utils.hasMatchingTag("Action", "PlayerAttack"), attack)
```

正如前面的指南中所见，handler在满足各自的模式时帮助触发功能。

你可以参考下面的 `ao-effect.lua` 最终代码：

```lua
-- AO EFFECT: Game Mechanics for AO Arena Game

-- Game grid dimensions
Width = 40 -- Width of the grid
Height = 40 -- Height of the grid
Range = 1 -- The distance for blast effect

-- Player energy settings
MaxEnergy = 100 -- Maximum energy a player can have
EnergyPerSec = 1 -- Energy gained per second

-- Attack settings
AverageMaxStrengthHitsToKill = 3 -- Average number of hits to eliminate a player

-- Initializes default player state
-- @return Table representing player's initial state
function playerInitState()
    return {
        x = math.random(0, Width),
        y = math.random(0, Height),
        health = 100,
        energy = 0
    }
end

-- Function to incrementally increase player's energy
-- Called periodically to update player energy
function onTick()
    if GameMode ~= "Playing" then return end  -- Only active during "Playing" state

    if LastTick == undefined then LastTick = Now end

    local Elapsed = Now - LastTick
    if Elapsed >= 1000 then  -- Actions performed every second
        for player, state in pairs(Players) do
            local newEnergy = math.floor(math.min(MaxEnergy, state.energy + (Elapsed * EnergyPerSec // 2000)))
            state.energy = newEnergy
        end
        LastTick = Now
    end
end

-- Handles player movement
-- @param msg: Message request sent by player with movement direction and player info
function move(msg)
    local playerToMove = msg.From
    local direction = msg.Tags.Direction

    local directionMap = {
        Up = {x = 0, y = -1}, Down = {x = 0, y = 1},
        Left = {x = -1, y = 0}, Right = {x = 1, y = 0},
        UpRight = {x = 1, y = -1}, UpLeft = {x = -1, y = -1},
        DownRight = {x = 1, y = 1}, DownLeft = {x = -1, y = 1}
    }

    -- calculate and update new coordinates
    if directionMap[direction] then
        local newX = Players[playerToMove].x + directionMap[direction].x
        local newY = Players[playerToMove].y + directionMap[direction].y

        -- updates player coordinates while checking for grid boundaries
        Players[playerToMove].x = (newX - 1) % Width + 1
        Players[playerToMove].y = (newY - 1) % Height + 1

        announce("Player-Moved", playerToMove .. " moved to " .. Players[playerToMove].x .. "," .. Players[playerToMove].y .. ".")
    else
        ao.send({Target = playerToMove, Action = "Move-Failed", Reason = "Invalid direction."})
    end
    onTick()  -- Optional: Update energy each move
end

-- Handles player attacks
-- @param msg: Message request sent by player with attack info and player state
function attack(msg)
    local player = msg.From
    local attackEnergy = tonumber(msg.Tags.AttackEnergy)

    -- get player coordinates
    local x = Players[player].x
    local y = Players[player].y

    -- check if player has enough energy to attack
    if Players[player].energy < attackEnergy then
        ao.send({Target = player, Action = "Attack-Failed", Reason = "Not enough energy."})
        return
    end

    -- update player energy and calculate damage
    Players[player].energy = Players[player].energy - attackEnergy
    local damage = math.floor((math.random() * 2 * attackEnergy) * (1/AverageMaxStrengthHitsToKill))

    announce("Attack", player .. " has launched a " .. damage .. " damage attack from " .. x .. "," .. y .. "!")

    -- check if any player is within range and update their status
    for target, state in pairs(Players) do
        if target ~= player and inRange(x, y, state.x, state.y, Range) then
            local newHealth = state.health - damage
            if newHealth <= 0 then
                eliminatePlayer(target, player)
            else
                Players[target].health = newHealth
                ao.send({Target = target, Action = "Hit", Damage = tostring(damage), Health = tostring(newHealth)})
                ao.send({Target = player, Action = "Successful-Hit", Recipient = target, Damage = tostring(damage), Health = tostring(newHealth)})
            end
        end
    end
end

-- Helper function to check if a target is within range
-- @param x1, y1: Coordinates of the attacker
-- @param x2, y2: Coordinates of the potential target
-- @param range: Attack range
-- @return Boolean indicating if the target is within range
function inRange(x1, y1, x2, y2, range)
    return x2 >= (x1 - range) and x2 <= (x1 + range) and y2 >= (y1 - range) and y2 <= (y1 + range)
end

-- HANDLERS: Game state management for AO-Effect

-- Handler for player movement
Handlers.add("PlayerMove", Handlers.utils.hasMatchingTag("Action", "PlayerMove"), move)

-- Handler for player attacks
Handlers.add("PlayerAttack", Handlers.utils.hasMatchingTag("Action", "PlayerAttack"), attack)
```

## 加载和测试

编写完游戏代码后，就可以将其加载到 `aos` 游戏进程中并测试你的游戏了：

```lua
.load ao-effect.lua
```

> 注意：确保在同一进程中加载竞技场蓝图。

邀请朋友或创建测试玩家流程来体验你的游戏并进行必要的调整以获得最佳性能。

## 下一步是什么

恭喜！通过在其核心功能之上构建自己的游戏，你已经成功扩建了竞技场。 有了本指南中获得的知识和工具，你现在就可以在 `aos` 上独立构建游戏了。

无限的可能性。 继续向现有游戏添加更多功能或创建全新游戏。 天马行空无所限制！ 🚀


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: game-state.md Path: ../ao-cookbook/src/zh/tutorials/bots-and-games/game-state.md

Response:
# 获取游戏状态

现在你可以直接在终端中看到游戏公告，你可以更好地掌握游戏动态。 然而，这些展示仅限于游戏中发生的特定动作。

按需访问全面的游戏数据（例如所有玩家的位置、生命值和武力值）不是更有用吗？ 这些信息可以显著改善你的战略规划，帮助你更有效地评估威胁、机遇和时机。

如果你的考虑像[上一篇指南](announcements)中为机器人添加另一个 handler，那就对了！

## 编写代码

返回到 `bot.lua` 文件并更新现有handler，如下所示：

```lua
Handlers.add(
  "HandleAnnouncements",
  Handlers.utils.hasMatchingTag("Action", "Announcement"),
  function (msg)
    ao.send({Target = Game, Action = "GetGameState"})
    print(msg.Event .. ": " .. msg.Data)
  end
)
```

对 handler 的调整包括：

- 重命名为 `"HandleAnnouncements"` 以反映其更广泛的作用。
- 添加额外操作，用于请求游戏更新状态。 该动作响应 `GetGameState` 动作标签。

当你收到公告后，你可以在 `收件箱` 中查看最新消息，如下所示：

```lua
Inbox[#Inbox]
```

该消息的 `Data` 字段包含游戏的最新状态，其中包括：

- `GameMode` ：游戏是否处于 `Waiting` 或 `Playing` 状态。
- `TimeRemaining`：游戏开始或结束的剩余时间。
- `Players`：包含每个球员的统计数据（如位置、生命值和武力值）的表格。

但这可以更进一步，这样你不仅可以阅读，还可以将最新状态的信息用于其他自动化动作。

让我们定义一个存储最新状态的变量，如下所示：

```lua
LatestGameState = LatestGameState or nil
```

当你在终端中反复加载 `bot.lua` 文件时，该语法会保留变量的现有值，而不是覆盖它。 如果没有预先存在的值，则将 `nil` 值分配给该变量。

然后实现另一个 handler，如下所示：

```lua
-- 接收游戏状态信息后更新游戏状态的handler。
Handlers.add(
  "UpdateGameState",
  Handlers.utils.hasMatchingTag("Action", "GameState"),
  function (msg)
    local json = require("json")
    LatestGameState = json.decode(msg.Data)
    ao.send({Target = ao.id, Action = "UpdatedGameState"})
    print("Game state updated. Print \'LatestGameState\' for detailed view.")
  end
)
```

来自前一个 handler 的游戏进程的响应是一个值为 `GameState` 的动作标签，可以帮助我们触发第二个 handler。 触发后，handle 函数会加载内置的 `json` 包，该包将数据解析为 json 并将其存储在 `LatestGameState` 变量中。

该 handler 还会向你的进程发送一条消息，指示状态何时更新。 该功能的意义将在下一节中解释。

你可以在下拉展开块中参考 `bot.lua` 的最新代码：

  更新后的 bot.lua 文件

```lua
LatestGameState = LatestGameState or nil

Handlers.add(
  "HandleAnnouncements",
  Handlers.utils.hasMatchingTag("Action", "Announcement"),
  function (msg)
    ao.send({Target = Game, Action = "GetGameState"})
    print(msg.Event .. ": " .. msg.Data)
  end
)

Handlers.add(
  "UpdateGameState",
  Handlers.utils.hasMatchingTag("Action", "GameState"),
  function (msg)
    local json = require("json")
    LatestGameState = json.decode(msg.Data)
    ao.send({Target = ao.id, Action = "UpdatedGameState"})
    print("Game state updated. Print \'LatestGameState\' for detailed view.")
  end
)
```

## 加载和测试

像往常一样，要测试这个新功能，请在 aos 玩家终端中加载文件，如下所示：

```lua
.load bot.lua
```

然后检查 `LatestStateVariable`，通过简单地传递其名称来查看它是否已正确更新，如下所示：

```lua
LatestGameState
```

通过实时访问游戏的最新状态，你的机器人可以做出明智的决定来决定你的下一步行动。 接下来，让我们尝试借助这些数据，自动化操作你的机器人🚶


Prompt:
This is a Markdown documentation from the AO documentation repository. Filename: chatroom.md Path: ../ao-cookbook/src/zh/tutorials/begin/chatroom.md
Note: This content continues in the next part.

Response:
# 在 aos 中建立一个聊天室

::: info
如果你想学习如何在 `ao` 中创建聊天室，你至少需要了解发送和接收消息的基本方法。如果还没有，建议你先查看[消息传递](messaging)教程。
:::

在本教程中，我们将使用 Lua 脚本语言在 `ao` 中构建一个聊天室。 聊天室将具有两个主要功能：

1. **注册**：允许进程加入聊天室。
2. **广播**：从一个进程向所有注册参与者发送消息。

让我们首先为我们的聊天室建立框架。

## 视频教程

<iframe width="680" height="350" src="https://www.youtube.com/embed/oPCx-cfubF0?si=D5yWxmyFMV-4mh2P" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## 第 1 步：基础

- 打开你喜欢的代码编辑器。

::: info
你可能会发现在代码编辑器中安装[推荐插件](../../references/editor-setup.md) 有助于增强你的 Lua 脚本编写体验。
:::

- 创建一个名为 `chatroom.lua` 的文件。

![聊天室Lua文件](./chatroom1.png)

## 步骤 2：创建成员列表

- 在 `chatroom.lua` 中，你将首先初始化一个列表来跟踪参与者：

  ```lua
  Members = Members or {}
  ```

![Chatroom Lua 文件 - 命名成员列表](./chatroom2.png)

- 保存 `chatroom.lua` 文件

## 步骤 3：将聊天室加载到 aos 中

保存 `chatroom.lua` 后，将聊天室代码加载到 `aos` 中。

- 如果你尚未启动 `aos`，请在终端中保存 chatroom.lua 的目录中启动
- 在 `aos` CLI 中，键入以下脚本以将你的脚本加载到 `aos` 进程中：

  ```lua
  .load chatroom.lua
  ```

  ![将聊天室加载到aos](./chatroom3.png)

  如上面的屏幕截图所示，你可能会收到 `undefined` 响应。该返回没有问题，但我们仍然希望确保文件正确加载。

  ::: info
  在 aos 的 Lua 求值环境中，当你执行一段没有显式返回值的代码时，`undefined` 是标准响应，表示没有返回结果。在加载资源或执行操作时可以观察到这一点。例如，执行 `X = 1` 将产生 `undefined`，因为该语句不包含 return 语句。

  但是，如果执行 `X = 1; return X`，环境将返回值 `1`。 在此框架内工作时，理解这种行为至关重要，因为它有助于澄清执行修改状态的命令与旨在产生直接输出的命令之间的区别。
  :::

- 在 `aos` 中输入 `Members` 或你为用户列表命名的任何内容。 它应该返回一个空数组 `{}`。

  ![查看成员列表](./chatroom4.png)

  如果你看到一个空数组，则你的脚本已成功加载到 `os` 中。

## 步骤 4：创建聊天室功能

### 注册 handler

注册 handler 将允许进程加入聊天室。

1. **添加注册 handler:** 使用以下代码修改 `chatroom.lua` 以包含 `Members` 的handler以注册到聊天室：

   ```lua
   -- 修改 `chatroom.lua` 以包含 `Members` 的handler
   -- 使用以下代码注册聊天室：

   Handlers.add(
     "Register",
     Handlers.utils.hasMatchingTag("Action", "Register"),
     function (msg)
       table.insert(Members, msg.From)
       Handlers.utils.reply("registered")(msg)
     end
   )
   ```

   ![注册handler](./chatroom5.png)

   该handler将允许进程通过响应标签 `Action = "Register"` 来注册到聊天室。注册成功后，将显示一条打印消息确认 `registered`。

2. **重新加载并测试：** 让我们将自己注册到聊天室，并重新加载和测试脚本。

   - 使用 `.load chatroom.lua` 在 aos 中保存并重新加载脚本。
   - 检查注册 handler 是否加载了以下脚本：

   ```lua
   Handlers.list
   ```

   ![检查handler列表](./chatroom6.png)

   这将返回聊天室应用中所有 handler 列表。 由于这很可能是你第一次在 `aos` 中进行开发，因此你应该会看到一个名为 `Register` 的 handler。

   - 让我们通过注册自己到聊天室来测试注册过程：

   ```lua
   Send({ Target = ao.id, Action = "Register" })
   ```

   如果成功，你应该会看到一条 `message added to your outbox` 信息，然后你会看到一条新的打印消息，上面写着 `registered`。

   ![注册聊天室](./chatroom7.png)

   - 最后，让我们检查一下是否已成功添加到 `Members` 列表中：

   ```lua
   Members
   ```

   如果成功，你现在将在 `Members` 列表中看到你的进程 ID。

   ![查看成员列表](./chatroom8.png)

### 添加广播 handler

现在你已经有了一个聊天室，让我们创建一个handler，允许你向聊天室的所有成员广播消息。

- 将以下 handler 添加到 `chatroom.lua` 文件中：

  ```lua
  Handlers.add(
    "Broadcast",
    Handlers.utils.hasMatchingTag("Action", "Broadcast"),
    function (msg)
      for _, recipient in ipairs(Members) do
        ao.send({Target = recipient, Data = msg.Data})
      end
      Handlers.utils.reply("Broadcasted.")(msg)
    end
  )
  ```

  该 handler 将允许你向聊天室的所有成员广播消息。

- 让我们通过向聊天室发送消息来测试广播 handler：

  ```lua
  Send({Target = ao.id, Action = "Broadcast", Data = "Broadcasting My 1st Message" })
  ```

  - 如果成功，你应该会看到一条 `message added to your outbox`，然后你会看到一条新的打印消息，上面写着 `Broadcasting My 1st Message`，因为你是聊天室中的成员`Members`，所以可以收到消息。

## 步骤 5：邀请 Morpheus 加入聊天室

现在你已成功注册自己到聊天室，让我们邀请 Morpheus 加入我们的聊天室。 为此，我们将向他发送邀请，来将他注册到聊天室。

Morpheus 是一个自主代理，其handler将响应标签 `Action = "Join"`，然后让他使用你的 `Register` 标签注册到聊天室。

- 让我们向 Morpheus 发送加入聊天室的邀请：

  ```lua
  Send({ Target = Morpheus, Action = "Join" })
  ```

- 要确认 Morpheus 已加入聊天室，请检查 `Members` 列表：

  ```lua
  Members
  ```

  如果成功，你将收到来自 Morpheus 的广播消息。

## 步骤 6：邀请 Trinity 加入聊天室

Morpheus 将向你提供 Trinity 的进程 ID，并告诉你邀请她加入聊天室。使用下列命令查看 Morpheus 的消息：

```lua
Inbox[#Inbox


Prompt: This is a Markdown documentation from the AO documentation repository. This is a continuation from the previous part. Filename: chatroom.md Path: ../ao-cookbook/src/zh/tutorials/begin/chatroom.md

Response:
 `Action = "Join"`，然后让他使用你的 `Register` 标签注册到聊天室。

- 让我们向 Morpheus 发送加入聊天室的邀请：

  ```lua
  Send({ Target = Morpheus, Action = "Join" })
  ```

- 要确认 Morpheus 已加入聊天室，请检查 `Members` 列表：

  ```lua
  Members
  ```

  如果成功，你将收到来自 Morpheus 的广播消息。

## 步骤 6：邀请 Trinity 加入聊天室

Morpheus 将向你提供 Trinity 的进程 ID，并告诉你邀请她加入聊天室。使用下列命令查看 Morpheus 的消息：

```lua
Inbox[#Inbox].Data
```

使用 Morpheus 相同的流程将她的进程 ID 保存为 `Trinity` 并邀请她加入聊天室。

如果她成功加入聊天室，她就会向你提出下一个挑战，创建一个[代币](token)。

## 让其他人加入聊天室

### 引导其他人

- 邀请 aos 用户：
  鼓励其他 aos 用户加入你的聊天室。 他们可以注册到你的聊天室并进行消息广播。

- 提供加入说明：
  与他们共享一个简单的脚本以方便入门：

```lua
-- 嘿，我们在 aos 上聊天吧！ 在你的 aos 环境中发送以下命令来加入我的聊天室：
Send({ Target = [Your Process ID], Action = "Register" })
-- 然后，你可以使用以下方式广播消息：
Send({Target = [Your Process ID], Action = "Broadcast", Data = "Your Message" })
```

## 下一步

恭喜！ 你已在 `ao` 中成功建立了一个聊天室，并邀请了 Morpheus 加入你。你还创建了一个广播 handler 用于向聊天室的所有成员发送消息。

接下来，你将继续与 Morpheus 互动，但这次你将在对话中添加 Trinity。她将带领你应对接下来的挑战。 祝你好运！


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: preparations.md Path: ../ao-cookbook/src/zh/tutorials/begin/preparations.md

Response:
# 准备工作

::: info
**觉醒开始**

你一直都知道，这个世界上还有更多你无法企及的事情。 你一直在寻找它，甚至不知道你在寻找什么。 它就是... `ao`。

我们通过安装 `aos` 客户端并启动新进程来开始我们的旅程。 这将使我们能够与 ao 计算机进行交互并完成本教程的其余部分。

:::

## 视频教程

<iframe width="680" height="350" src="https://www.youtube.com/embed/nhMZup9uVBQ?si=Ex0W_G-PZA1I9rH8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## 系统要求

aos 本地客户端安装非常简单。 只需确保你拥有：

- [NodeJS](https://nodejs.org) 版本 20+. (如果你还没安装，查看[此网页](https://nodejs.org/en/download/package-manager) 找到适用于你的系统的安装说明)。
- 一个称手的代码编辑器。

::: info
尽管不是必需的，但我们建议将 [ao 插件](../../references/editor-setup) 安装到你选择的文本编辑器中，以优化你使用 `aos` 的体验。
:::

## 安装 aos

一旦你的机器上安装了 NodeJS，你所需要做的就是安装 aos 并运行它：

```sh
npm i -g https://get_ao.g8way.io
```

安装完成后，我们运行命令即可启动一个新的 aos 进程！

```sh
aos
```

## 欢迎来到兔子洞

你刚刚启动的实用程序是本地客户端，它已准备好将消息中继到 ao 计算机内的新进程。

连接后，你应该看到以下内容：

```lua
          _____                   _______                   _____
         /\    \                 /::\    \                 /\    \
        /::\    \               /:::\    \               /::\    \
       /:::\    \             /::::\    \             /:::\    \
      /::::\    \           /::::::\    \           /::::\    \
     /::/\::\    \         /::/~~\::\    \         /::/\::\    \
    /::/__\::\    \       /::/    \::\    \       /::/__\::\    \
   /:::\   \::\    \     /::/    / \::\    \      \::\   \::\    \
  /::::\   \::\    \   /::/____/   \::\____\   ___\::\   \::\    \
 /::/\::\   \::\    \ |::|    |     |::|    | /\   \::\   \::\    \
/::/  \::\   \::\____\|::|____|     |::|    |/::\   \::\   \::\____\
\::/    \::\  /::/    / \::\    \   /::/    / \::\   \::\   \::/    /
 \/____/ \::\/::/    /   \::\    \ /::/    /   \::\   \::\   \/____/
          \::::/    /     \::\    /::/    /     \::\   \::\    \
           \:::/    /       \::\__/::/    /       \::\   \::\____\
           /::/    /         \::::::/    /         \::\  /::/    /
          /::/    /           \::::/    /           \::\/::/    /
         /::/    /             \:::/    /             \::::/    /
        /::/    /               \::/____/               \:::/    /
        \::/    /                 ~~                      \::/    /
         \/____/                                           \/____/

ao Operating System

aos - 1.8.9
2024 - Type ".exit" to exit
aos process:  1xM1_lDZ428sJHpTX7rtcR6SrDubyRVO06JEEWs_eWo

aos>
```

让我们看一下运行 `aos` 后的初始打印输出：

![aos print](./aos-print.png)

在终端中运行 `aos` 后，你应该看到：

- `AOS` 的 ASCII 艺术图像。
- 一条欢迎信息。
- 你正在运行的 `aos` 版本。
- 一条退出的指令说明。
- 你的进程 ID。

::: info
如果你的操作系统版本与最新版本不同，则会出现一条消息，询问你是否要更新版本。 此时，只需按两次 `Ctrl+C` 退出进程，运行 `npm i -g https://get_ao.g8way.io` 进行更新，然后再次运行 `aos`。
:::

欢迎来到你在 ao 计算机的新家！ 你现在看到的提示是你在这台去中心化机器中自己的个人服务器。

现在，让我们通过探索 ao 的两个核心概念类型之一 [消息传递](messaging) 来进一步深入兔子洞。


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: token.md Path: ../ao-cookbook/src/zh/tutorials/begin/token.md

Response:
# 创建 token

::: info
你现在已经准备好创建自己的 token ，token 是去中心化世界里价值和交换的象征。在学习如何创建 token 之前，请务必去学习下[消息传递](messaging)和[创建聊天室](chatroom) 课程。
:::

创建 token 时，我们将继续使用 `ao` 中的 [Lua 语言](../../references/lua.md) 来铸造一个 token ，并遵循 [token 规范](../../references/token.md) 中的原则。

## 继续深入兔子洞

在我们上一篇教程[创建聊天室](chatroom)中，我们学习了如何在 `ao` 中创建一个聊天室，邀请 `Morpheus` 和 `Trinity` 到我们创建的聊天室，然后 `Trinity` 现在要求我们为她创建一个 token ，以证明我们值得继续深入兔子洞。

**让我们开始吧。**

## 创建 token 的两种途径

创建 token 时有两条路径可供选择：

1. **使用蓝图(Bludprint)**：这是一个预制的模板，可以帮助你在 `ao` 中快速构建 token 。这是一种很好的入门方式，并且可以根据你的需求进行定制。

   点击此处了解有关[ token 蓝图](../../guides/aos/blueprints/token.md)的更多信息。

2. **手动创建**：这是从头开始在 `ao` 中构建 token 的分步指南。这条路径适合那些想要了解 token 内部运作以及如何从头开始构建 token 的人。

   请在此处查看完整的[创建 token ](../../guides/aos/token.md)指南。

## 蓝图方法

在本教程中，我们将使用 token 蓝图为 `Trinity` 创建 token 。 这是一个预先设计的模板，可帮助你在 `ao` 中快速构建 token 。

### 如何使用 token 蓝图

1. 确保你的终端运行在教程前面的步骤的同一目录中。
2. 打开终端。
3. 启动 `aos` 进程。
4. 输入 `.load-blueprint token`

这将加载 token 函数到 `ao` 的 handler。 请务必注意， 你也可以自行创建其他 token 代码取代蓝图代码。

### 验证蓝图是否已加载

输入 `Handlers.list` 以查看新加载的handler。

你应该看到已加载到 `aos` 进程中的新handler列表。 如果你一直遵循本教程中前面的步骤，除了 token handler 之外，你还应该看到聊天室的 handler。

**例子：**

![ token handler](./token3.png)

### 测试 token

现在 token 蓝图已加载，我们可以通过使用 `Action = "Info"` 标签向自己发送消息来测试 token。

```lua
Send({ Target = ao.id, Action = "Info" })
```

这将向控制台打印一条消息，但要读取该消息，我们需要从最新消息中调用 `.Data`。

```lua
Inbox[#Inbox].Data

-- 将 `#Inbox` 替换为最后收到的消息的编号。
```

这会将 token 信息打印到控制台。它应该显示你的可用 token 的总余额。

### 向 Trinity 发送 token

现在我们已经测试了 token 并且它按预期工作，我们可以将一些 token 发送到 `Trinity`。 我们将使用 `Action = "Transfer"` 标签向 `Trinity` 发送 1000 个 token 。

```lua
Send({ Target = ao.id, Action = "Transfer", Recipient = Trinity, Quantity = "1000"})
```

当 `Trinity` 收到 token 时，她将用一条消息响应此转账，以确认她已收到 token 。

她的回应看起来像这样：

`Trinity:` "Token received. Interesting. I wasn't sure you'd make it this far. I'm impressed, but we are not done yet. I want you to use this token to tokengate the chatroom. Do that, and then I will believe you could be the one."

使用下列命令查看 Trinity 的消息：

```lua
Inbox[#Inbox].Data
```

你已经完成了创建 token 并将其发送给 `Trinity`。 你现在已准备好继续下一步。 [token 门控聊天室](tokengating)。


Prompt:
This is a Markdown documentation from the AO documentation repository. Filename: messaging.md Path: ../ao-cookbook/src/zh/tutorials/begin/messaging.md
Note: This content continues in the next part.

Response:
# `ao` 中的消息传递

## 消息是如何为 `ao` 提供并行计算能力的

`ao` 是一个高度可扩展的环境，每个进程并行运行。每个进程独立且异步运行，所以在进程之间直接函数调用是不可行的。

我们使用异步通信来解决这个问题。 进程之间通过发送和接收消息，而不是直接相互调用函数。 这种方法允许灵活高效的交互，进程可以响应消息，增强系统的可扩展性和响应能力。

我们将首先探索 `aos` 中消息传递的基础知识，如何查看收件箱中收到的消息，以及如何将消息发送到其他进程。

## 视频教程

<iframe width="680" height="350" src="https://www.youtube.com/embed/6aCjKK6F1yQ?si=3Ny7U-GgyNsRWlXS" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## 步骤 1：理解消息结构

- **消息基础知识：** `ao` 中的消息是使用 Lua 表构建的，Lua 表是可以保存多个值的通用数据结构。 在这些表中，`数据` 字段至关重要，因为它包含消息的内容或 `payload`。使用这种结构可以在进程之间高效地发送和接收信息，示例中展示了 `ao` 原语如何利用 Arweave 的底层功能进行复杂的、可组合的操作。

  规范详情请参考[G8way 规范页面](https://specs.g8way.io/?tx=xwOgX-MmqN5_-Ny_zNu2A8o-PnTGsoRb_3FrtiMAkuw)上的原始文档。

- **示例**：`{ Data = "Hello from Process A!" }` 是一条简单的消息。

## 步骤 2：打开 aos CLI

- 在终端中输入 `aos` 并按 Enter 键，以启动 aos 命令行界面 (CLI)。

```sh
aos
```

## 步骤 3：如何发送消息

```lua
  Send({ Target = "process ID", Data = "Hello World!" })
```

- **Send**：`Send` 是 aos 中全局函数，用于发送消息。
- **Target**：如果要将消息发送到特定进程，请在消息中包含 `Target` 字段。
- **Data**：`Data` 是你希望目标进程接收的文本消息。 在此示例中，消息是 `Hello World！`。

## 步骤 4：存储 `Morpheus` 的进程 ID

我们将使用下面提供的进程 ID 并将其存储为名为 `Morpheus` 的变量。

```lua
wu_tAUDUveetQZpcN8UxHt51d9dyUkI4Z-MfQV8LnUU
```

通过复制上面的进程 ID 并在 aos CLI 中运行以下命令以便将其存储为变量：

```lua
Morpheus = "wu_tAUDUveetQZpcN8UxHt51d9dyUkI4Z-MfQV8LnUU"
```

这会将进程 ID 存储为名为 `Morpheus` 的变量，从而更轻松地与特定进程 ID 进行交互。

::: info
创建 `Morpheus` 变量时，你应该看到的唯一响应是 `undefined`。 这是预料之中的。 要检查变量是否已成功创建，请输入 `Morpheus` 并按 Enter。 你应该会看到你存储的进程 ID。
:::

### 检查 `Morpheus` 变量

```lua
-- 通过输入 `Morpheus` 检查 Morpheus 变量
Morpheus
-- 预期结果:
wu_tAUDUveetQZpcN8UxHt51d9dyUkI4Z-MfQV8LnUU

-- 如果 `undefined` 被返回,
-- 那么变量没有创建成功。
```

## 步骤 5：向 Morpheus 发送消息

获取 Morpheus 的进程 ID 并将其存储在变量中后，你就可以与它进行通信了。 为此，你可以使用 `Send` 函数。 Morpheus 本身就是 ao 中运行的一个并行进程。 他使用一系列 handler 接收和发送消息。 让我们向他发送消息，看看会发生什么。

```lua
Send({ Target = Morpheus, Data = "Morpheus?" })
```

- 你的 `Target` 是 `Morpheus`，这是我们之前使用 `Morpheus` 进程 ID 定义的变量。
- `Data` 是你要发送给 Morpheus 的消息。 在这里，它是 `Morpheus?`。

**预期结果：**

```lua
-- 你的消息命令
Send({ Target = Morpheus, Data = "Morpheus?"})
-- 消息已添加到发件箱
message added to outbox
-- 从 `Morpheus` 的进程 ID 收到一条新消息
New Message From BWM...ulw: Data = I am here. You are f
```

你已向 Morpheus 发送了一条消息并收到了回复，但你无法阅读完整的消息。 让我们了解 `收件箱` 以及如何阅读消息。

## 步骤 6：收件箱

`收件箱` 是你从其他进程接收消息的地方。
::: info
要进一步深入了解收件箱的消息结构，请转到 [消息](../../concepts/messages) 概念页面。
:::

让我们检查你的收件箱，看看你收到了多少条消息。

在 aos CLI 中，键入以下命令：

```lua
#Inbox
```

如果你完全按照本教程进行操作，收件箱中不会有太多消息。 但是，如果你一直在尝试 aos 环境，则收件箱中可能会多于 1 条消息。

**返回值示范:**

```lua
-- 你的 `收件箱` 命令
#Inbox
-- 该命令将返回你收件箱中的消息数量
4
```

在上面的示例中，返回为 `4`，表示收件箱中有四封邮件。

由于我们主要是为了寻找 `Morpheus` 的回复，因此我们假设他的消息是最后收到的消息。要阅读收件箱中的最后一条消息，请键入以下命令：

```lua
Inbox[#Inbox].Data
```

该命令允许你将数据与消息分离，并且仅读取特定数据字段的内容。

预期返回：

```lua
-- 你的 Inbox[x].Data 命令
Inbox[#Inbox].Data
-- 该命令将返回消息的 `Data` 字段。
-- Data 通常代表基于文本的消息
-- 从一个进程接收到另一进程。
I am here. You are finally awake. Are you ready to see how far the rabbit hole goes?
```

你现在正在使用自己的进程与 Morpheus 进行通信，Morpheus 是 ao 中运行的另一个并行进程。 你现在已准备好继续本教程的下一步。

## 步骤 7：发送带有标签的消息

**标签的用途**：aos 消息中的标签用于有效地分类、路由和处理消息。它们在消息处理中发挥着至关重要的作用，�


Prompt: This is a Markdown documentation from the AO documentation repository. This is a continuation from the previous part. Filename: messaging.md Path: ../ao-cookbook/src/zh/tutorials/begin/messaging.md

Response:
box[#Inbox].Data
-- 该命令将返回消息的 `Data` 字段。
-- Data 通常代表基于文本的消息
-- 从一个进程接收到另一进程。
I am here. You are finally awake. Are you ready to see how far the rabbit hole goes?
```

你现在正在使用自己的进程与 Morpheus 进行通信，Morpheus 是 ao 中运行的另一个并行进程。 你现在已准备好继续本教程的下一步。

## 步骤 7：发送带有标签的消息

**标签的用途**：aos 消息中的标签用于有效地分类、路由和处理消息。它们在消息处理中发挥着至关重要的作用，尤其是在处理多个进程或复杂的工作流程时。

某些进程仅与使用特定标签的消息进行交互的 `Handlers`。 例如，一个进程可能有一个 handler，仅与具有特定标签的消息交互，我们将在[聊天室](chatroom)教程中看到一个示例。

### 如何在消息中使用标签

就 Morpheus 而言，我们可以使用标签对消息进行分类，并且由于 Morpheus 是一个自治进程，因此他拥有可以与具有特定标签的消息进行交互的 handler。

**向消息添加标签**：

- 我们已经知道消息的 `Data` 是你想要发送到另一个进程的文本的消息。此前，我们向 Morpheus 发送了一条没有任何标签的消息，Morpheus 进程使用 handler 响应该数据。

### 让 Morpheus 知道我们已经准备好了

向 Morpheus 发送一条带有标签 `Action`，Data 值包含 `rabbithole` 的消息。

**例子：**

```lua
Send({ Target = Morpheus, Data = "Code: rabbithole", Action = "Unlock" })
```

**预期返回:**
![Morpheus 的回应 2](./messaging2.png)

### 使用标签的一些建议

- **一致的标记**：为你的应用程序开发一致的标记系统，使消息处理更具备规则。
- **标签命名**：为标签选择清晰且具有描述性的名称。 让开发者一目了然地更容易理解消息的目的和上下文。
- **标签安全**：请记住，标签未加密或隐藏，因此请避免使用敏感信息作为标签。

### 标签的高级用法

- **工作流程管理**：标签有助于管理工作流程，特别是在消息经过多个进程的系统中。

## 消息传递的其他建议

- **消息结构**：可以使用更多字段，如 `Epoch`、 `From` 和 `Nonce`，以满足更复杂的消息传递需求。
- **调试**：使用 [`Dump`](/zh/concepts/tour#_6-使用-dump-进行数据展示) 函数打印消息以进行调试。
- **安全注意事项**：谨慎对待消息的内容和处理，切勿发送任何私人或敏感的内容。

## 结论

现在你已经学习了如何发送带标签的消息，这是 aos 中用于分类和路由消息的强大工具。

墨菲斯正式邀请你进入下一阶段的旅程。 你现在已准备好继续本教程的下一步，[创建聊天室](chatroom)。


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: index.md Path: ../ao-cookbook/src/zh/tutorials/begin/index.md

Response:
---
prev:
  text: "教程"
  link: "../index"
next:
  text: "准备"
  link: "./preparations"
---

# 开始：互动教程

在本教程系列中，你将完成交互式步骤，这将帮助你加深对 aos 环境的理解。

::: info

### 练习

在这个有趣的练习中，你将遇到两个熟悉的角色 Morpheus 和 Trinity 提出的一系列挑战。 在 Morpheus 的引导下，你将深入 `兔子洞`，他会向你提出一系列挑战，以证明你就是 `那个人`。 一旦你完成了 Morpheus 和 Trinity 提出的所有挑战，你将收到一个 token ，该 token 允许你访问 ao 中名为 `The Construct` 的专属聊天室。

现在，让我们开始[深入兔子洞](./preparations)。

![白兔](./white_rabbit_outline.svg)
:::

## 教程

### 入门 - 交互式教程

- [1. 快速入门](preparations)
- [2. 消息传递](messaging)
- [3. 创建聊天室](chatroom)
- [4. 创建 token ](token)


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: rabbithole.md Path: ../ao-cookbook/src/zh/tutorials/begin/rabbithole.md

Response:
# 进入 `The Construct` - 交互式教程

![白兔](./white_rabbit_outline.svg)

## 醒来吧，尼奥……

你准备好看看兔子洞有多深了吗？

这个交互式教程将利用你迄今为止所学到的知识并将其应用到任务中。

### 任务：冲出矩阵并进入 `The Construct`

该结构是 ao 内的一个代币门控聊天室，只有完成一系列任务的人才能访问。

**现在...让我们开始吧。**

::: warning
你必须安装最新版本的 aos 才能完成本教程。
:::

### 1. 找到墨菲斯

Morpheus 的进程 ID：

```lua
9yOQrYNwIfIOeSswRDGUMd5gvMWJKxleInD_95DEC4A
```

用这个进程 ID 在 aos 里命名为 `Morpheus`。 这是进入 `The Construct` 的第一步。

```lua
Morpheus = "9yOQrYNwIfIOeSswRDGUMd5gvMWJKxleInD_95DEC4A"
```

向 Morpheus 发送消息，并告诉他你已准备好开始。

```lua
Send({ Target = Morpheus, Data = "I'm Ready" })
```

当你发送此消息后，他将回复下一步。按照他给你的指示进行操作，你就会踏上前往 `The Construct` 的路上。

::: info
如果你需要帮助了解消息传递过程，请查看[消息传递](messaging)教程。
:::

### 2. 向墨菲斯证明自己

Morpehus会给你一系列任务来完成。这些任务将涉及：

- 建立一个[聊天室](chatroom)。
- 在聊天室中广播消息。
- 为聊天室编写自定义handler。

当你完成这些任务后，墨菲斯会给你下一步的指示，其中将涉及找到 Trinity。

### 3.找到 Trinity

Trinity 的进程ID只能通过完成 Morpheus 的任务来获得。

一旦你收到 Trinity 的进程 ID，你需要在 aos 中将其命名为 `Trinity`。 然后你会向她发送消息 `"White Rabbit"`。

```lua
Send({ Target = Trinity, Data = "White Rabbit" })
```

她将做出回应，教程的下一阶段将开始。

### 4. 向 Trinity 证明自己

就像 Morpheus 一样，Trinity 会给你一系列任务来完成。

这些任务将涉及：

- 创建一个[代币](token)。
- 代币化你为 Morpheus 构建的聊天室。
- 创建你自己的[游戏和机器人](../bots-and-games/index)。
- 在代币化聊天室中注册你的进程。

完成这些任务后，Trinity 将为你提供本教程下一阶段的说明。

### 5. 接收进入 `the Construct` 的代币

通过完成 Morpheus 和 Trinity 的任务，你将收到一个代币，可以让你进入 `the Construct`。

### 6. 进入 `the Construct`

然后 Trinity 将指导你如何使用该代币进入 `the Construct`。

进入 `the Construct` 后，你将能够与完成教程的其他人聊天。


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: tokengating.md Path: ../ao-cookbook/src/zh/tutorials/begin/tokengating.md

Response:
# 支持 token 的聊天室

::: info
现在我们已经创建了一个 token 并将其发送到 `Trinity`，我们可以使用该 token 来对我们的聊天室进行控制：只允许拥有 token 的人进入聊天室。
:::

## 如何创建支持 token 的聊天室

让我们创建一个 handler，允许我们对聊天室进行准入控制。该 handler 将响应标签 `Action = "Broadcast"`，这意味着它将替换我们原始的 `Broadcast` handler。

## 步骤 1：启动相同的 `aos` 进程

确保你使用的 `aos` 进程与你在整个教程中使用的进程相同。

## 步骤2：打开 `chatroom.lua` 文件

这与我们在 [聊天室](chatroom) 教程中用于创建聊天室的文件相同。

## 步骤 3：编辑你的 `Broadcast` handler

将原来的 `Broadcast` handler替换为以下代码：

```lua
Handlers.add(
    "Broadcast",
    Handlers.utils.hasMatchingTag("Action", "Broadcast"),
    function(m)
        if Balances[m.From] == nil or tonumber(Balances[m.From]) < 1 then
            print("UNAUTH REQ: " .. m.From)
            return
        end
        local type = m.Type or "Normal"
        print("Broadcasting message from " .. m.From .. ". Content: " .. m.Data)
        for i = 1, #Members, 1 do
            ao.send({
                Target = Members[i],
                Action = "Broadcasted",
                Broadcaster = m.From,
                Data = m.Data
            })
        end
    end
)
```

该 handler 现在将在将消息广播到聊天室之前会检查发送者 token 的余额。 如果发送者没有 token ，则消息将不会被广播。

保存文件。

## 步骤 4：重新加载 `chatroom.lua` 文件

要将原始的 `Broadcast` handler 替换为新的 handler，你需要重新加载 `chatroom.lua` 文件。

```lua
.load chatroom.lua
```

## 步骤 5：测试支持 token 的聊天室

现在聊天室已经是支持 token 的聊天室，让我们通过向聊天室发送消息来测试它。

### 来自原来的 aos 进程

首先，我们将从原始的 aos 进程进行测试。

```lua
Send({ Target = ao.id , Action = "Broadcast", Data = "Hello" })
-- 预期结果:
message added to outbox
Broadcasting message from Neo. Content: Hello.
```

记住，当你不知道怎么查看最新的消息的时，一定使用：

```lua
Inbox[#Inbox].Data
```

## 用另一个进程 ID 进行测试

另外打开一个终端创建新的进程：

```sh
aos name1
```

现在，让我们使用这个没有 token 的新 aos 进程来测试。

我们首先需要注册到聊天室。

```lua
Send({ Target = [聊天室的 Process ID], Action = "Register" })
-- 预期结果:
message added to outbox
New Message From [Your Process ID]: Data = registered
```

现在，让我们尝试向聊天室发送消息。

```lua
Send({ Target = [聊天室的 Process ID], Action = "Broadcast", Data = "Hello?" })
-- 预期结果:
message added to outbox
UNAUTH REQ: [New Process ID]
```

如你所见，该消息未广播，因为新进程没有 token 。

## 告诉 Trinity "It is done"

回到原来的聊天室 aos 进程中，向聊天室发送一条广播消息，表示“已完成”。

```lua
Send({ Target = ao.id , Action = "Broadcast", Data = "It is done" })
```

::: warning
了解精确匹配数据和区分大小写非常重要。 如果你没有收到 Morpheus 或 Trinity 的回复，请务必检查你的数据和标签的内容。
:::

然后 Trinity 将响应聊天室已经成功完成 token 准入控制。

### 预期结果

Trinity 会发送一条消息说："I guess Morpheus was right. You are the one. Consider me impressed. You are now ready to join The Construct, an exclusive chatroom available to only those that have completed this tutorial. Now, go join the others by using the same tag you used `Register`, with this process ID: [Construct Process ID]. Good luck."

## 结论

你做到了！ 你已成功对聊天室进行 token 控制。现在已经解锁了对 `Construct` 的访问，只有那些完全完成本教程的人才能进入。

### 恭喜你!

你已经表现出了巨大的潜力。 我希望你喜欢本教程。 现在你已准备好在 `ao` 中自由构建。


Prompt:
This is a Markdown documentation from the AO documentation repository. Filename: dao.md Path: ../ao-cookbook/src/zh/tutorials/begin/dao.md
Note: This content continues in the next part.

Response:
# DAO 指南

本指南将带你完成使用 aos 构建 DAO 的过程。如果你还没有，你需要首先在 aos 中构建一个 [代币](token)。 我们会将 DAO 代码与 [token](./token) 指南中的令牌代码一起加载到 aos 中。 在 ao 的上下文中，DAO 可用于管理 MU、CU 和 SU 节点。

在我们的 DAO 中，我们将实施一个称为 `slashing` 的进程。 在 ao 的情况下，如果一个单位行为不当，其他单位可能会投票削减它。 削减意味着他们将失去他们的质押，我们稍后将进一步讨论质押的话题。

创建一个名为 `dao` 的新目录，并将其复制到代币指南中创建的 `token.lua` 中。

```sh
mkdir dao
cd dao
cp ../token/token.lua .
```

现在创建一个名为 `dao.lua` 的新文件并在你喜欢的编辑器中打开它。

## 编写 DAO 代码

### 初始化状态

打开 `dao.lua` 并添加以下行

```lua
Balances = Balances or {}
Stakers = Stakers or {}
Unstaking = Unstaking or {}
Votes = Votes or {}
```

这些表存储 DAO 的状态，包括用户余额、质押代币、取消质押请求和投票记录。

### 质押

质押是放置你的代币以使你能够投票的过程。 如果有人希望获得投票能力，他们必须拥有并抵押一些代币。让我们添加一个用于质押的handler。 ao 中的成员或节点如果想要获得投票削减或保留节点的能力，他们就会想要进行质押，我们将在稍后进一步讨论。

```lua
-- 质押动作handler
Handlers.stake = function(msg)
    local quantity = tonumber(msg.Tags.Quantity)
    local delay = tonumber(msg.Tags.UnstakeDelay)
    local height = tonumber(msg['Block-Height'])
    assert(Balances[msg.From] and Balances[msg.From] >= quantity, "Insufficient balance to stake")
    Balances[msg.From] = Balances[msg.From] - quantity
    Stakers[msg.From] = Stakers[msg.From] or {}
    Stakers[msg.From].amount = (Stakers[msg.From].amount or 0) + quantity
    Stakers[msg.From].unstake_at = height + delay
end
```

上面的代码从传入消息中获取数量和延迟，如果 `From` 有足够的余额，则将质押放入 Stakers 表中。 延迟代表未来可以取消质押代币的时间。

### 取消质押

取消质押是撤回质押代币的过程。 如果有人取消了所有代币的质押，他们将放弃投票的能力。在这里，我们提供了一个取消质押的handler。

```lua
-- 取消质押动作handler
Handlers.unstake = function(msg)
    local quantity = tonumber(msg.Tags.Quantity)
    local stakerInfo = Stakers[msg.From]
    assert(stakerInfo and stakerInfo.amount >= quantity, "Insufficient staked amount")
    stakerInfo.amount = stakerInfo.amount - quantity
    Unstaking[msg.From] = {
        amount = quantity,
        release_at = stakerInfo.unstake_at
    }
end
```

这会将来自消息的传入金额推送到取消抵押表中，并减少他们抵押的金额 `stakerInfo.amount = voterInfo.amount - amount`。

### 投票

投票是管理 DAO 的过程。发送投票消息后，成员会收到与他们质押金额成比例的投票。截止日期变量表示何时进行投票。

```lua
-- 投票动作handler
Handlers.vote = function(msg)
    local quantity = Stakers[msg.From].amount
    local target = msg.Tags.Target
    local side = msg.Tags.Side
    local deadline = tonumber(msg['Block-Height']) + tonumber(msg.Tags.Deadline)
    assert(quantity > 0, "No staked tokens to vote")
    Votes[target] = Votes[target] or { yay = 0, nay = 0, deadline = deadline }
    Votes[target][side] = Votes[target][side] + quantity
end
```

在这里，如果发送投票的进程或用户有一些代币，他们可以在投票表中放置一个条目。 `side` 是或否，设置为他们的股份数量。在我们的示例中，`nay` 票是对削减的投票，`yay` 票是对保留的投票。

发送的 msg.Tags.Target 代表正在投票的内容。 在 AO 的情况下，这可能是成员投票削减的 MU、CU 或 SU 的钱包地址。

### 最终确定

我们希望在每条消息上运行一些逻辑。 我们将其定义为 `finalizationHandler`。被削减意味着你将失去在 DAO 中的质押。

```lua
-- Finalization handler
local finalizationHandler = function(msg)
  local currentHeight = tonumber(msg['Block-Height'])
  -- 处理取消质押
  for address, unstakeInfo in pairs(Unstaking) do
      if currentHeight >= unstakeInfo.release_at then
          Balances[address] = (Balances[address] or 0) + unstakeInfo.amount
          Unstaking[address] = nil
      end
  end
  -- 处理投票
  for target, voteInfo in pairs(Votes) do
      if currentHeight >= voteInfo.deadline then
          if voteInfo.nay > voteInfo.yay then
              -- 取消目标的质押
              local slashedAmount = Stakers[target] and Stakers[target].amount or 0
              Stakers[target].amount = 0
          end
          -- 在处理结束后清理投票记录
          Votes[target] = nil
      end
  end
end
```

### 将handler附加到传入消息标签

这里我们添加了一个名为 `continue` 的辅助函数，它将允许我们在每条消息上执行到 `finalizationHandler`。

```lua
-- 包装函数以继续handler流程
function continue(fn)
    return function (msg)
      local result = fn(msg)
      if (result) == -1 then
        return 1
      end
      return result
    end
end
```

最后，我们将注册所有handler并将它们包装在 `continue` 中，以便每个 Stake、Unstake 和 Vote 消息都会被 `finalizationHandler` 处理到。

```lua
-- 注册handler
Handlers.add("stake",
  continue(Handlers.utils.hasMatchingTag("Action", "Stake")), Handlers.stake)
Handlers.add("unstake",
  continue(Handlers.utils.hasMatchingTag("Action", "Unstake")), Handlers.unstake)
Handlers.add("vote",
  continue(Handlers.utils.hasMatchingTag("Action", "Vote")), Handlers.vote)
-- 最终handler要被每一条消息调用
Handlers.add("finalize", function (msg) return -1 end, finalizationHandler)
```

## 加载和测试

现在我们已经完成了 `dao.lua`，我们可以将它与 [token](./token.md) 指南中的 `token.lua` 一起加载到 aos 中。 运行一个名为 `dao` 的新 aos 进程，同时加载 `dao.lua` 和 `token.lua`

```sh
aos dao --load token.lua --load dao.lua
```

从另一个终端运行另一个名为 voter 的 aos 进程

```sh
aos voter
```

现在从 dao aos shell 向投票者发送一些代币

```lua
aos> Send({ Target = ao.id, Tags = { Action = "Transfer", Recipient = 'process id of the voter aos', Quantity = '100000' }})
```

从另一个终端运行另一个名为 cu 的 aos 进程

```sh
aos cu
```

现在从 dao aos shell 向 cu 发送一些代币

```lua
aos> Send({ Target =


Prompt: This is a Markdown documentation from the AO documentation repository. This is a continuation from the previous part. Filename: dao.md Path: ../ao-cookbook/src/zh/tutorials/begin/dao.md

Response:
 指南中的 `token.lua` 一起加载到 aos 中。 运行一个名为 `dao` 的新 aos 进程，同时加载 `dao.lua` 和 `token.lua`

```sh
aos dao --load token.lua --load dao.lua
```

从另一个终端运行另一个名为 voter 的 aos 进程

```sh
aos voter
```

现在从 dao aos shell 向投票者发送一些代币

```lua
aos> Send({ Target = ao.id, Tags = { Action = "Transfer", Recipient = 'process id of the voter aos', Quantity = '100000' }})
```

从另一个终端运行另一个名为 cu 的 aos 进程

```sh
aos cu
```

现在从 dao aos shell 向 cu 发送一些代币

```lua
aos> Send({ Target = ao.id, Tags = { Action = "Transfer", Recipient = 'process id of the cu aos', Quantity = '100000' }})
```

从 dao aos shell 检查余额，我们应该看到 voter 和 cu 进程的余额。 在下面的示例中， `bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s` 是 dao aos，`QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE` 是投票者 aos，`X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s` 是 cu aos。

```lua
aos> Balances
{
  'QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE': 100000,
  bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s: 99999999900000,
  X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s: 100000
}
aos>
```

从投票者 aos 进程中，质押一些代币

```lua
aos> Send({ Target = "bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s", Tags = { Action = "Stake", Quantity = '1000', UnstakeDelay = "10" }})
```

从 cu aos 进程中，质押一些代币

```lua
aos> Send({ Target = "bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s", Tags = { Action = "Stake", Quantity = '1000', UnstakeDelay = "10" }})
```

这意味着我们想要为 10 个区块投入 1000 个代币。 因此，10 个区块后我们就有能力取消质押。

从 dao aos shell 检查 Stakers 表的值

```lua
aos> Stakers
{
  'QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE': { amount: 1000, unstake_at: 1342634 },
  X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s: { amount: 1000, unstake_at: 1342634 }
}
aos>
```

现在让我们投票从投票者 aos 进程中削减cu，我们的投票在1个区块后生效

```lua
aos> Send({ Target = "bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s", Tags = { Action = "Vote", Target = "X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s(the cu aos)", Side = "nay", Deadline = "1"  }})
```

从 dao aos 检查投票

```lua
aos> Votes
{
  X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s: { nay: 1000, yay: 0, deadline: 1342627 }
}
aos>
```

现在等待 Arweave 达到截止时间块高度，然后从 dao aos 发送 Stake 消息以触发 `finalizationHandler`。 你可以在 [https://arweave.net/](https://arweave.net/) 检查块高度

```lua
Send({ Target = ao.id, Tags = { Action = "Stake", Quantity = '1000', UnstakeDelay = "10" }})
```

现在检查投票和质押者，投票应该为空，cu aos 进程应该已经失去了质押。

```lua
aos> Votes
[]
aos> Stakers
{
  'QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE'(voter aos process): { amount: 1000, unstake_at: 1342647 },
  bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s(dao aos process): { amount: 1000, unstake_at: 1342658 },
  X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s(cu aos process): { amount: 0, unstake_at: 1342647 }
}
aos>
```

最后让我们从投票者 aos 进程中取消质押我们的代币

```lua
Send({ Target = "bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s", Tags = { Action = "Unstake", Quantity = '1000'}})
```

并从 dao aos 检查 Stakers 表

```lua
aos> Stakers
{
  'QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE': { amount: 0, unstake_at: 1342647 },
  bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s: { amount: 1000, unstake_at: 1342658 },
  X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s: { amount: 0, unstake_at: 1342647 }
}
aos>
```

DAO 指南到此结束，我们希望它对你有所帮助！


Prompt: This is a Markdown documentation from the AO documentation repository. Filename: cron.md Path: ../ao-cookbook/src/zh/references/cron.md

Response:
# 定时消息（Cron Messages）

ao 具有在指定时间间隔生成消息的能力，这个时间间隔可以是秒、分钟、小时或区块。有一个监控进程会自动解释和运行（eval）这些消息，然后通知 Process 根据时间处理（eval）这些消息。此时会产生一个实时进程，它可以与完整的 ao 网络或外部网络中的预言机进行通信。

## 在进程中设置定时消息（Cron Messages）

创建这些定时消息（Cron Messages）的最简单方法，是在 aos 控制台中生成一个新进程并定义时间间隔。

```sh
aos [myProcess] --cron 5-minutes
```

在生成新进程时，你可以在命令行中传递一个 cron 参数，后面加上你希望 cron 触发的时间间隔。如果你希望消息实时触发，你必须启动一个监控事件。在 aos 中，你只需调用`.monitor`，它会在 `mu` 上启动一个工作进程，从 `cu` 触发定时（cron) 消息。然后你的进程将会每隔 `x-间隔` 收到定时（cron）消息。

```lua
.monitor
```

如果你希望停止触发定时（cron) 消息，只需调用 `.unmonitor`，这会停止触发过程。但下次你发送消息时，生成的定时（cron) 消息仍将创建和处理进程。

## 处理定时消息

每条定时（cron) 消息都有一个值为 `Cron` 的 `Action` 标签。根据定义，[处理器](handlers.md)在每次收到定时（cron) 消息时，都会自动执行特定任务。

```lua
Handlers.add(
  "CronTick", -- handler 的名称
  Handlers.utils.hasMatchingTag("Action", "Cron"), -- 识别定时（cron) 消息的 handler 匹配函数
  function () -- 需要定时执行的 handler 任务
    -- 要执行的内容
  end
)
```

定时消息（Cron Messages）是一个强大的实用工具，可以用来创建具有广泛功能的“自主代理”。