《编程农场》教程 (10):向日葵核电站!解锁模块化编程 (import) 与最值算法
欢迎回来,农场首席架构师!
在征服了傲娇的树木和极度需要照顾的巨型南瓜后,你的农场逻辑已经非常精密了。但是,你有没有发现无人机飞来飞去的速度有点慢? 今天,我们将解锁农场的“终极能源”——向日葵(Sunflower)!只要拥有了能量,你的无人机和代码执行速度会直接翻倍(2倍速)!
同时,为了应对越来越复杂的农场系统,我们将引入 Python 编程中真正的大师级技巧——多文件管理与模块导入(import)。
1. 向日葵的“抢花瓣”游戏规则
向日葵的种植方法和胡萝卜完全一样,但它的收割规则非常特殊,就像一场选美比赛:
能量加成:收割向日葵不给果实,只给“能量”。
参赛门槛:农场上必须至少种有 10 株向日葵。
寻找花魁(8倍奖励):每株向日葵有 7 到 15 片花瓣不等。如果你在这 10+ 株向日葵中,精准收割了花瓣数量最多的那一株,你将获得 8 倍 的巨额能量!
雷达探测:使用新函数
measure()可以测出脚下向日葵的花瓣数。最棒的是,向日葵还没完全长大时,就能提前测出花瓣数了!
如果瞎割一气,你只能拿保底能量;如果精准制导,你的农场将化身核电站!
2. 告别单一文件:模块导入(import)的魔法
在找“花魁”之前,我们必须先解决一个严峻的问题:随着代码越来越多(比如我们之前写的 try_harvest、smart_plant、加上移动找坐标的 goto(x,y)),全塞在一个文件里会让人崩溃。
在真正的软件开发中,我们会把代码分门别类地拆到不同的文件里。这就需要用到 import。
方案 A:标准导入(官方推荐 👍)
假设你建了一个名叫 farm_utils.py 的文件,里面装满了你写好的移动和浇水函数。 在你的主文件里,你只需要一句话就能借用它们:
import farm_utils
# 使用时,必须带上前缀,指明是借谁的工具:
farm_utils.goto(3, 4) 方案 B:解包导入(新手慎用 ⚠️)
官方说明书特意警告了这种写法:from farm_utils import (把里面所有的东西直接倒进现在的房间里)。 这种写法虽然调用时不用加前缀,但极其容易引发*“命名冲突”和“导入循环锁死”**。所以,听官方的劝:老老实实使用 import file 语法!
3. 终极护盾:if __name__ == "__main__":
关于 import,有一个非常可怕的“副作用”:当你导入一个文件时,电脑会把你导入的那个文件从头到尾执行一遍! 假设你的 farm_utils.py 底部有一句测试用的 harvest(),当你在主文件里 import farm_utils 时,无人机会莫名其妙地原地挥一刀!
为了防止这种“副作用”,我们需要给文件加上一把安全锁:
# 你的工具和函数定义在上面
def goto(x, y):
pass
# 【安全锁】:只有当你直接点击运行这个文件时,锁里的代码才会执行。
# 如果别人只是 import 借用你的工具,锁里的代码绝对不会被触发!
if __name__ == "__main__":
# 测试代码放这里
goto(0,0)
harvest()4. 实战:搭建你的“向日葵核电站”
5.命名为 utils (你的工具箱)
在这个文件里,我们只放通用的移动功能:
# 文件名:utils
def goto(x, y):
while get_pos_x() < x:
move(East)
while get_pos_x() > x:
move(West)
while get_pos_y() < y:
move(North)
while get_pos_y() > y:
move(South)
# 工具箱不需要执行任何具体动作,所以不用写 if __name__ 锁。
同时我们把“判断是不是耕地”这个极其常用的动作,封装到工具箱里。
# ==========================================
# 文件名:utils
# ==========================================
def goto(x, y):
while get_pos_x() < x:
move(East)
while get_pos_x() > x:
move(West)
while get_pos_y() < y:
move(North)
while get_pos_y() > y:
move(South)
# 新增:智能耕地功能
def prepare_soil():
# 如果脚下是草地,就翻成耕地
if get_ground_type() == Grounds.Grassland:
till()
6.编写主程序(降序扫描法)
现在,我们要写主程序了。你的思路极其完美,但有一个游戏机制的小细节我们需要兼顾: “农场上必须至少有 10 株向日葵,才能触发 8 倍能量。” 为了保证场上向日葵数量永远达标,我们在割掉一株最高花瓣的向日葵后,最好立刻在原地补种一株新的,这样全图的向日葵永远是满的!
来看这段极其干脆利落的代码:
# ==========================================
# 文件名:main
# ==========================================
import utils
def run_sunflower_reactor():
size = get_world_size()
# --- 第 1 步:全图遍历,种满向日葵 ---
for x in range(size):
for y in range(size):
utils.goto(x, y)
if get_entity_type() == None:
utils.prepare_soil() # 调用工具箱里的耕地代码
plant(Entities.Sunflower)
# --- 第 2 步:降序收割(从 15 一路扫到 7) ---
# range(15, 6, -1) 的意思是:从 15 开始,每次减 1,一直数到 7(不包含 6)
for target_petals in range(15, 6, -1):
# 拿着当前的目标花瓣数(比如 15),去全图巡视一遍
for x in range(size):
for y in range(size):
utils.goto(x, y)
# 确保脚下确实是向日葵,再去测花瓣
if get_entity_type() == Entities.Sunflower:
# 如果花瓣数刚好等于我们当前的目标!
if measure() == target_petals:
# 等它彻底成熟
while not can_harvest():
pass
# 一刀收割!拿下当前的最高 8 倍加成!
harvest()
# 【细节优化】:割完立刻补种,保证全图向日葵永远 > 10 株
utils.prepare_soil()
plant(Entities.Sunflower)
# 主程序入口锁
if __name__ == "__main__":
while True:
run_sunflower_reactor()
🧠 为什么这个逻辑“神乎其技”?
我发明的这个逻辑,完美利用了游戏底层的**“相对最大值”**规则! 游戏规定:如果你收获了花瓣数量最多的那一株,就能获得 8 倍能量。
第 1 遍扫描(找 15 瓣):全场最大的肯定是 15,你把所有的 15 都割了,拿满了 8 倍能量。
第 2 遍扫描(找 14 瓣):此时场上已经没有 15 了!所以现在的全场最大值变成了 14!你去割 14,依然能触发 8 倍能量加成!
以此类推...:你一路扫到 7,你的每一刀,割的都是当时的“全场最大值”,你的每一刀都是 8 倍收益!
最后,找出代码中的错误!