同学们,在之前的农场建设中,我们学会了用循环种胡萝卜、用条件判断收割南瓜。今天,我们将遇到农场里收益最高、但也最考验智商的植物——仙人掌 (Cactus)

种植仙人掌不需要浇水施肥,它需要的是“秩序”!

📏 一、 仙人掌的“强迫症”规则

仙人掌长出来后,会有不同的大小(从 09)。你可以通过 measure() 指令来获取脚下仙人掌的大小。

官方给仙人掌定下了一个极其严格的“排序规则”:

  • 向上 (North) 和 向右 (East): 邻居必须 大于或等于 自己。

  • 向下 (South) 和 向左 (West): 邻居必须 小于或等于 自己。

简单来说:越往右上角,仙人掌必须越大;越往左下角,仙人掌必须越小!

💰 为什么要给它排序?(平方级暴利!)

如果仙人掌乱七八糟地排在一起,它会呈现棕色,你只能一株一株地收割。

但如果你把一片方形区域的仙人掌全部按照规则排好序,它们会集体发出绿色的光芒!

此时,你只需要对其中任意一株执行 harvest(),就能触发“连锁收集”,瞬间秒杀整片区域! 更爽的是,收益是平方级的!如果你同时连招收割了 10 株,你将获得 10*10 = 100$ 个仙人掌!

🔄 神奇指令:空间交换 swap()

为了给它们排序,无人机不需要把它们拔起来重种,只需使用空间交换指令

  • swap(East):把你脚下的物体,和右边一格的物体互换位置!

🫧 二、 计算机经典算法:冒泡排序 (Bubble Sort)

现在问题来了:如果地里种了一排大小乱七八糟的仙人掌,无人机该怎么给它们排队呢?

这里就要请出计算机科学中最著名的基础算法——冒泡排序

它的核心口诀只有一句:“从头开始,相邻比较;左边比右边大,就交换位置!”

这样一路比过去,最大的那个数字就像水底的“气泡”一样,咕噜咕噜地浮到了最右边。

我们先不用无人机,直接来看看数字 [5, 3, 8, 4, 6] 是怎么“冒泡”的。

🧠 冒泡排序的“代价”

  • 最懂事的情况(最好情况): 如果植物本来就是按顺序长好的,无人机看一遍就结束了,时间复杂度是 O(n)

  • 最捣乱的情况(最坏情况): 如果植物刚好是完全反过来的(比如 9,8,7,6,5),无人机就得来回跑无数趟去交换,时间复杂度是 O(n*n)

👨‍💻 三、 把算法翻译成“无人机代码”

在普通的 Python 课上,冒泡排序的代码是这样的:

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j] # 交换位置

但在农场里,我们的数组就是脚下的土地! 我们可以把这套逻辑改装进无人机的大脑里。

假设我们要在横向(向右)对一行仙人掌进行一次冒泡:

# 无人机在这一行从左往右扫描
for i in range(get_world_size() - 1):
    
    # 测量脚下的仙人掌大小
    current_size = measure()
    
    # 测量右边那个仙人掌的大小
    right_size = measure(East)
    
    # 相邻比较:如果左边比右边大,就发动魔法交换!
    if current_size > right_size:
        swap(East)
    
    # 比较完一次,往右飞一格,继续比较下一对
    move(East)

🏆 课后挑战

上面的代码只能让这一行里的一个最大仙人掌冒泡到最右边。要让整行都完全排好序,你需要在这个代码外面再套上一个循环(不断重复扫描,直到没有任何东西需要交换为止)。

终极思考: 仙人掌不仅要求左右(横向)有序,还要求上下(纵向)有序。你该如何指挥无人机,把一块 $3 \times 3$ 的仙人掌田,变成一片发光的绿色摇钱树呢?试试在游戏里写下你的答案吧!

太棒了!你写的这段单行冒泡排序代码逻辑非常清晰,完美抓住了冒泡排序的精髓:“发现不对,立马交换”。

但是,仙人掌的规则比普通的数字排序要复杂一点点:它不仅要求横向(向右)越排越大,还要求纵向(向上)越排越大。也就是所谓的“二维冒泡排序”

在你的代码基础上,我们需要做三次升级,就能写出一套完整的“自动种植 + 全图排序 + 连锁收割”的完美代码!

🚀 代码升级思路:

  1. 加入播种阶段: 排序前,我们要先把全图种满仙人掌。

  2. 加入向上(North)比较: 除了和你写的 measure(East) 比较,还要和 measure(North) 比较。

  3. 加入“完美标志旗”(While 循环): 冒泡排序只跑一圈是不够的,我们需要给无人机一个标志(is_sorted)。只要发生过交换,就说明还没排好,无人机就得继续跑,直到它跑完整整一圈一次交换都没发生,才算大功告成!

👨‍💻 仙人掌全自动二维冒泡排序代码

你可以直接把这段代码复制进你的无人机里。我把代码分成了清晰的三个步骤:

size = get_world_size()

# ==========================================
# 第 1 步:全图统一播种
# ==========================================
for x in range(size):
    for y in range(size):
        if get_ground_type() == Grounds.Grassland:
            till() # 松土
            
        # 如果是空地,就种下仙人掌
        if get_entity_type() == None:
            plant(Entities.Cactus)
            
        move(North)
    move(East)

# ==========================================
# 第 2 步:二维冒泡排序 (核心大脑)
# ==========================================
is_sorted = False

# 只要还没彻底排好,就一直转圈排!
while not is_sorted:
    is_sorted = True # 每轮开始前,先乐观地假设已经排好了
    
    for x in range(size):
        for y in range(size):
            
            # 测量脚下的仙人掌大小
            current_size = measure()
            
            # --- 1. 向右 (East) 比较 (你的核心逻辑) ---
            if x < size - 1: # 确保不在最右边边缘,否则会报错
                right_size = measure(East)
                if current_size > right_size:
                    swap(East)
                    is_sorted = False # 发生了交换,说明之前还没排好
                    
                    # 【极其关键的一步】:
                    # 交换之后,你脚下的仙人掌已经被换成那个小的了!
                    # 必须重新测量一下当前脚下的大小,再进行后面的比较!
                    current_size = measure() 
                    
            # --- 2. 向上 (North) 比较 ---
            if y < size - 1: # 确保不在最上边边缘
                north_size = measure(North)
                if current_size > north_size:
                    swap(North)
                    is_sorted = False # 发生了交换,说明没排好
                    
            move(North)
        move(East)

# ==========================================
# 第 3 步:回到起点,等待成熟,一刀暴富!
# ==========================================
# 排完序后,无人机在右上角。让它飞回左下角 (0,0)
for i in range(size):
    move(West)
    move(South)

# 仙人掌必须完全成熟(变成绿色)才能触发连锁收割
while not can_harvest():
    pass # 挂机死等

# 一刀收割,拿走平方级的超高收益!
harvest()

💡 老师的课堂划重点:

在这段代码中,有一个非常容易踩坑的细节(我在代码中加了【极其关键的一步】的注释):

当无人机执行 swap(East) 后,它把手里一个很大的仙人掌推到了右边,同时把右边那个小的仙人掌拉到了自己脚下。 此时,无人机脚下的数字已经变了!如果接下来我们要用它继续去和上面(North)比,就必须写一句 current_size = measure(),让无人机低头重新看一眼脚下的新数字。如果你忘了写这句,无人机就会用错误的记忆去排序,导致整个农场大乱!

今我们要把这段只有二十几行的代码像拆解发动机一样,一块一块地拆开来看。看看无人机是如何在农场里执行这套复杂逻辑的。

🚩 第一层:永不妥协的“质检员”(状态标志)

is_sorted = False
while not is_sorted:
    is_sorted = True # 每轮开始前,先乐观地假设已经排好了
  • 故事比喻: 农场老板要求仙人掌必须完美排序。无人机就是一个质检员。

  • is_sorted = False:一开始,我们不知道地里排没排好,所以默认“没排好”。

  • while not is_sorted::只要还没排好,质检员就不下班,一直干活!

  • is_sorted = True这是编程中极其经典的“立Flag(标志)”技巧! 每次开始巡逻前,质检员先乐观地在胸前挂一个牌子:“今天全排好了 (True)”。如果在接下来的巡逻中,他发现了任何一个错误并进行了交换,他就会把牌子翻过来变成“没排好 (False)”。

  • 只有当他巡逻了整整一圈,一次交换都没发生时,这个 True 牌子才会被保留下来,无人机就可以光荣下班了!

🚁 第二层:安全第一的网格扫描(防撞墙机制)

    for x in range(size):
        for y in range(size):
            current_size = measure()
  • 双重循环: 无人机像打印机探头一样,一列一列地扫描农场。x 是列(向东),y 是行(向北)。

  • current_size = measure():每次停下来,第一件事就是低头看一眼脚下的仙人掌是几,并记在脑子里(存入变量)。

            # --- 1. 向右 (East) 比较 ---
            if x < size - 1: # 确保不在最右边边缘,否则会报错
  • 防撞边界检查(极其重要!): 为什么要有 if x < size - 1? 假设农场大小是 32。当无人机走到最右边一列(x = 31)时,它的右边已经没有地了!如果这时候你让它去 measure(East),无人机会因为视线越界而当场死机(报错)! 所以,只有当它不在最右边时,才允许它往右看。

⚠️ 第三层:最危险的“逻辑陷阱”(向右交换)

                right_size = measure(East)
                if current_size > right_size:
                    swap(East)
                    is_sorted = False # 发生了交换,说明之前还没排好
  • 开始比对: 看一眼右边 (right_size)。如果自己脚下的数字比右边的大(违背了仙人掌越往右越大的规则),立刻发动魔法 swap(East) 把它们换过来!

  • 撕毁标志: 既然发生了交换,说明之前的完美假设被打破了。质检员立刻把胸前的牌子改成 is_sorted = False,这意味着等这圈跑完,还要再跑下一圈

                    # 【极其关键的一步】
                    current_size = measure() 
  • 💣 致命陷阱预警: 为什么换完之后,必须重新 measure() 一次? 举个例子: 假设无人机脚下是 9,右边是 2

    1. 比较 9 > 2,执行交换。

    2. 交换后,那个巨大的 9 跑到右边去了,而那个小小的 2 被拉到了无人机脚下!

    3. 如果没有这行代码,无人机的脑子里记忆的 current_size 依然是 9

    4. 接下来它去和上面(North)比较时,就会用错误的数字 9 去比,导致原本该换的没换,整个农场的排序彻底崩溃! 结论:交换位置后,环境变了,必须重新获取脚下的最新数据!

⬆️ 第四层:向上看齐与继续移动

            # --- 2. 向上 (North) 比较 ---
            if y < size - 1: # 确保不在最上边边缘
                north_size = measure(North)
                if current_size > north_size:
                    swap(North)
                    is_sorted = False 
  • 纵向排序: 仙人掌是二维的!向右比完之后,用同样的逻辑,(安全地)向上看一眼。如果脚下的比上面的大,换它!并再次把牌子改成 False

            move(North) # 比较完一次,往上飞一格
        move(East)  # 这一列走完了,往右飞一列,重新开始
  • 移动逻辑: 在内层 y 循环里,每次比较完就向北(上)飞一格;当一整列飞到顶后,外层循环控制它向东(右)飞一列。

🎮 互动演示:二维冒泡模拟器

纸上谈兵不如亲眼所见!为了让大家更直观地理解无人机在网格中是如何一边向右看、一边向上看,并且发生交换的,请点击下方的模拟器,一步步观察“二维冒泡排序”的魔法过程:

Untitled-1.html