第三单元:if-else 双分支选择结构

Unit 3: The if-else Two-Way Selection
边际森林 Border Forest · 背靠背 → 勇敢的食人魔

3.1 从单分支到双分支

From Single-Branch to Two-Way Branch

3.1.1 单分支的局限(Limits of Single-Branch)

3.1.1.1 上单元我们只解决了一半 Last unit solved only half the problem

在第二单元里,你学会了单分支的 if 语句——"如果有敌人,就攻击它"。它很强大,但有一个明显的"留白":当条件不成立时,程序什么都不做

这在很多场景下是不够的。想象英雄在村口巡逻:有敌人时当然要攻击,但没有敌人时英雄该干什么? 用单分支,英雄只会原地发呆。可我们真正想要的是——没有敌人时,英雄应该回到防守位置保护村民。这就需要"否则该做什么"的能力。

3.1.2 什么是双分支(What is Two-Way Branch)

3.1.2.1 给"否则"也安排上动作 Giving the "otherwise" case an action too

CodeCombat 双分支:两条路二选一 - codecombat.cn
图1:双分支结构——条件成立走 A 路,不成立走 B 路,必走其一

双分支选择结构在单分支的基础上,增加了一条 else(否则)分支。它的逻辑是:"如果条件成立,做 A;否则,做 B。" 两条分支必定会走其中一条,绝不会两条都走,也绝不会一条都不走。

3.1.3 生活中的二选一(Either-Or in Daily Life)

3.1.3.1 无处不在的"否则" "Otherwise" is everywhere

双分支在生活中比单分支更常见,因为我们的决定往往是"二选一"的:

注意这些句子都有完整的"如果……就……否则……"三段式。翻译成 Python,就是本单元的主角:if-else 语句。

3.2 if-else 语句

The if-else Statement

3.2.1 基本语法(Basic Syntax)

3.2.1.1 if 与 else 配对出现 if and else come as a pair

if-else 语句在第二单元 if 的下方,多了一个 else 代码块。它的基本语法是:

if 条件:
    # 条件成立时执行这里
    动作 A
else:
    # 条件不成立时执行这里
    动作 B

注意 else 后面没有条件,直接跟一个冒号 :。因为它代表的就是"除了 if 条件成立之外的所有情况",不需要再写条件。

3.2.2 else 的含义(The Meaning of else)

3.2.2.1 "if 以外的所有情况" "Everything outside the if"

正如"Else 之战"关卡所说:"else 在 if 语句条件不成立时执行动作。可以这样认为,else 的情况也就是 if 语句以外的所有情况。" 这是理解 else 最精准的一句话。

💡 用"背靠背"关卡来理解

"背靠背"是你第一次接触 else 的关卡。它的逻辑非常清晰:"如果(if) 有敌人,那么(then) 攻击它,否则(else) 移动到 X 标记处。" 攻击和撤防是两个互斥的动作——英雄要么在前线杀敌,要么回阵地防守,二者必居其一。这正是双分支的精髓。

3.2.3 缩进与对齐(Indentation and Alignment)

3.2.3.1 else 要和 if 顶格对齐 else must align with its if

CodeCombat if 与 else 对齐示意 - codecombat.cn
图2:else 必须与对应的 if 在同一缩进层级上对齐

书写 if-else 时有两个缩进规则必须牢记:第一,else 关键字要和它配对的 if 处在同一缩进层级(左边对齐);第二,ifelse 各自的代码块都要向右缩进

if enemy:
    hero.attack(enemy)     # 属于 if 的代码块(缩进)
else:                     # else 与 if 对齐(顶格)
    hero.moveXY(40, 34)  # 属于 else 的代码块(缩进)

3.2.4 执行流程图(Flowchart)

3.2.4.1 一图看懂双分支 Two-way branch in one diagram

开始 条件成立? if 条件 动作 A(if) 动作 B(else) 继续往下
图3:双分支流程图——两条路必走其一,然后汇合继续

3.3 else 的核心规则

Core Rules of else

3.3.1 必须依附 if(else Must Follow an if)

3.3.1.1 else 不能单独存在 else cannot stand alone

"Else 之战"关卡明确指出:"else 只能与相应的 if 语句配对使用。" 这意味着你不能写一个孤零零的 else——它必须紧跟在某个 if(及其代码块)之后。else 就像 if 的"影子",没有 if 就没有 else

3.3.2 互斥与完备(Mutually Exclusive & Complete)

3.3.2.1 二者恰好执行其一 Exactly one branch runs

if-else 有两个重要特性,理解它们能帮你避免很多 bug:

特性含义
互斥(Mutually Exclusive)if 块和 else 块不可能同时执行,每次只走一条
完备(Complete)两条分支覆盖了所有情况,必定会执行其中一条
一句话记忆:if-else 是一道"二选一"的门——要么进左门,要么进右门,不多走、不少走、不漏走

3.3.3 单分支 vs 双分支(Single vs. Two-Way)

3.3.3.1 关键区别一目了然 The key differences at a glance

对比项单分支 if双分支 if-else
结构只有 ifif + else
条件为假时什么都不做,跳过执行 else 块
形象比喻"有就做,没有就算了""有就做这个,没有就做那个"
典型场景发现敌人才攻击有敌人攻击,否则撤防

3.4 条件的进阶

Advanced Conditions

本单元的条件不再只是"有没有敌人",而是引入了更丰富的判断依据:距离技能冷却。这让英雄的决策更加精细。

3.4.1 distanceTo 测距(Measuring Distance)

3.4.1.1 测量到目标的距离 Measure the distance to a target

CodeCombat distanceTo 测距示意 - codecombat.cn
图4:distanceTo 返回英雄与目标之间的距离(米)

新的眼镜赋予了英雄 distanceTo() 技能:它能测量英雄与某个目标(或坐标)之间的距离,单位是米。通常我们会把结果存进一个变量,方便后面判断:

enemy = hero.findNearestEnemy()
distance = hero.distanceTo(enemy)  # 测出离这个敌人多远

在"测距仪"关卡里,英雄甚至不用亲自战斗——只需用 distanceTo() 测出每个敌人的距离,再用 say() 喊给大炮听,让大炮去轰击。这是 distanceTo 的纯粹应用。

3.4.2 比较运算符 <(The < Operator)

3.4.2.1 把距离变成"真"或"假" Turning distance into true or false

光测出距离还不够,我们需要拿它和一个数字比较,得到一个布尔值,才能放进 if 里。这就要用到小于运算符 <

if distance < 10:   # 如果距离小于 10 米……
    hero.attack(enemy)

这行代码可以读作:"如果距离小于 10 米,那么就攻击敌人。" 当 distance 确实小于 10 时,distance < 10 的结果是 True,攻击执行;否则结果是 False,走 else 分支。

表达式distance 的值结果
distance < 107True(执行 if)
distance < 1015False(执行 else)
distance < 53True(执行 if)

3.4.3 isReady 技能判断(Checking Skill Readiness)

3.4.3.1 特殊技能有冷却时间 Special skills have cooldowns

CodeCombat isReady 检查技能冷却 - codecombat.cn
图5:cleave 等特殊技能有冷却时间,使用前要先用 isReady 检查

英雄的长剑有一个强力技能 cleave()(劈斩)——它能一次攻击周围十米内的所有敌人!但如此强大的技能不能连续使用,它有冷却时间(每 10 秒一次)。所以使用前必须先用日晷腕表的 isReady() 方法检查它是否准备好了:

if hero.isReady("cleave"):  # 如果劈斩准备好了……
    hero.cleave(enemy)          # 就劈斩!
else:                          # 否则(还在冷却中)……
    hero.attack(enemy)          # 用普通攻击撑过冷却
妙用 else:else 在冷却期间发动普通攻击,能让英雄"不浪费每一秒"——劈斩好了就劈斩,没好就普攻,火力永不停歇。

3.5 if-else 实战模式

Common if-else Patterns

本单元的关卡虽多,但 if-else 的用法可归纳为四种经典"决策模式"。认清模式,就能以不变应万变。

3.5.1 模式一:攻击或撤防(Attack or Defend)

3.5.1.1 有敌人就打,没敌人就回防 Fight if there's an enemy, otherwise defend

这是最基础的 if-else 模式:条件是"是否有敌人",两条分支分别是"攻击"和"回到防守位置"。代表关卡有"背靠背""林间圣地"。逻辑骨架是:

if enemy:
    hero.attack(enemy)
else:
    hero.moveXY(防守点 x, 防守点 y)

3.5.2 模式二:攻击或开箱(Attack or Loot)

3.5.2.1 有敌人就打,没敌人就干别的事 Fight if there's an enemy, otherwise do other work

这个模式把 else 分支换成了"做正事"——比如攻击宝箱开锁。代表关卡是"食人魔营地":有食人魔来就反击,没有了就持续攻击 "Chest" 把宝箱打开。它体现了一种"战斗优先,间隙做事"的智能。

3.5.3 模式三:劈斩或普攻(Cleave or Attack)

3.5.3.1 技能好了就放大招,否则普攻 Use the big skill if ready, otherwise basic attack

这个模式的条件不是"有没有敌人",而是"技能是否就绪"。代表关卡有"森林劈斩者""Else 之战""边地僵局"系列。它的价值在于用 else 填满技能的冷却空窗,让输出最大化。

3.5.4 模式四:按距离决策(Decide by Distance)

3.5.4.1 敌人近了才出手 Act only when the enemy is close

这个模式用 distanceTo + < 作为条件,只有当敌人足够近时才采取行动。代表关卡有"贴身护卫""矮人蜂拥而至""勇敢的食人魔"。它让英雄学会"沉住气、不贪攻"——不被远处的敌人引诱离开关键位置。

3.6 实战关卡精讲

Level Strategy & Analysis

这一节我们聚焦每一关的难点、思路和策略,只用关键片段辅助讲解,不展示完整解法。读懂"为什么这样设计",你就能自己补全代码。

3.6.1 背靠背(Back to Back)

3.6.1.1 思路:用 else 守住阵地 Strategy: hold your ground with else

CodeCombat 背靠背关卡 - codecombat.cn
图6:背靠背——有敌人就出击,没敌人就回到中间守护两侧农民

这是你第一次使用 else 的关卡,也是理解"否则"的最佳起点。英雄要同时保护背靠背的两个农民,难点在于:不能为了追一个敌人而跑太远,否则另一边的农民就危险了。

策略是把"中间防守点"设为 else 分支的归宿:发现敌人就上前清剿,一旦清完(没有敌人了),立刻 moveXY 回到正中央的防守坐标。这样英雄就像一个钟摆,始终守在能照顾两侧的位置。

关卡警示:"确保别把 X 标记的坐标弄错,否则你将来不及同时保护两个农民。" 防守点的坐标选错,整个策略就崩了——它必须是能兼顾两侧的"中点"。

3.6.2 食人魔营地(Ogre Encampment)

3.6.2.1 思路:把 else 用在"干正事"上 Strategy: use else to get work done

这一关的目标是夺回被锁在宝箱里的宝物。挑战在于:营地里的食人魔会不断骚扰你,你不能光顾着开箱被打死,也不能光打怪不开箱。

if-else 完美解决了这个两难:把"有敌人"作为条件,if 分支反击敌人,else 分支攻击宝箱。这样英雄会自动"战斗优先"——只要还有食人魔就先打它们,一旦清场就抓紧时间凿宝箱。注意这里 else 攻击的目标是用字符串 "Chest" 指定的:

if enemy:
    hero.attack(enemy)
else:
    hero.attack("Chest")  # 用名字攻击宝箱

3.6.3 边地僵局(Borderlands Stymie)系列

3.6.3.1 思路:让 else 填满冷却空窗 Strategy: fill the cooldown gap with else

CodeCombat 边地僵局劈斩关卡 - codecombat.cn
图7:边地僵局——劈斩就绪时清场,冷却期间用普攻续航

"森林劈斩者""Else 之战""边地僵局 A/B"这一系列,核心都是围绕技能冷却来做决策。矮人成群结队地涌来,cleave() 能一次清掉一片,但它有 10 秒冷却。如果只会等劈斩,那冷却的这 10 秒英雄就只能挨打。

聪明的做法是用 if-else 把这 10 秒"榨干":if 判断 isReady("cleave")——好了就劈斩;else——没好就普通攻击。这样英雄永远在输出,要么群秒,要么单点,绝不空闲。

关卡点拨:"用 else 能让你的英雄避免对 cleave 的目标敌人进行普通攻击。" 也就是说,else 还顺带避免了"劈斩好了却跑去普攻单个敌人"的低效行为。

3.6.4 贴身护卫(Close Protection)

3.6.4.1 思路:距离是新的判断标准 Strategy: distance becomes the new criterion

CodeCombat 贴身护卫关卡 - codecombat.cn
图8:贴身护卫——敌人近了就反击,远了就回到农民身边

这一关首次把距离引入 if 条件。要护卫脆弱的农民 Victor,英雄不能见敌人就追——追远了 Victor 就暴露了。所以判断标准变成了"敌人离得够不够近"。

策略是:测出与最近敌人的距离,如果 distance < 10 就攻击(说明敌人已逼近,威胁到农民);否则就 moveXY 回到 Victor 身边的防守点。这是"模式一"的升级版——把条件从"有没有敌人"换成了"敌人近不近"。

坐标提示:关卡明确给出防御位置 {x: 40, y: 37}。else 分支一定要回到这个正确的位置,否则走远了食人魔就会伤到 Victor。

3.6.5 矮人蜂拥而至(Dwarvenholdt)

3.6.5.1 思路:多个知识点的总集成 Strategy: integrating everything learned

这是一道"综合大考",需要同时用上 if/elsedistanceTo()<cleave()while True 循环和 attack("Chest")。任务是:在被矮人群包围的情况下,打开一个大宝箱并存活。

关键策略是用距离来决定何时放劈斩:矮人因为怕你,会扎堆靠近后才前进。所以当最近的矮人距离 < 10 时用 cleave 群秒,把它们一锅端;else 则去攻击 "Chest",趁敌人不近的间隙开箱。

判断成功的标志:关卡给了一个绝妙的自检方法——"只要英雄不追击远离宝箱的敌人,你就知道自己的距离判别成功了。" 如果英雄乱跑追敌,说明你的 distance < 10 逻辑出了问题。

3.6.6 勇敢的食人魔(Brave Ogre)

3.6.6.1 思路:嵌套——if 里面还有 if-else Strategy: nesting if-else inside if

这道概念挑战引入了一个更高级的结构:嵌套(Nesting)。大多数食人魔害怕英雄不敢上前,但有的会勇敢进攻。规则是:离得近(< 5 米)的就打它,否则去攻击 "Door"

这里巧妙的地方是双层判断:外层先用 if enemy: 确认敌人存在(避免对 None 测距出错),内层再用 if distance < 5 / else 按距离决策。来看这个嵌套骨架:

if enemy:                       # 外层:先确认有敌人
    distance = hero.distanceTo(enemy)
    if distance < 5:           # 内层 if:近
        hero.attack(enemy)
    else:                       # 内层 else:远
        hero.attack("Door")
为什么要先 if enemy?因为如果场上没有敌人,findNearestEnemy() 返回 None,对 None 调用 distanceTo 会出错。外层的 if enemy 就是一道"安全锁"——这也呼应了第二单元学过的存在性检查。

3.7 常见错误与技巧

Common Mistakes & Tips

3.7.1 易错点清单(Pitfalls)

3.7.1.1 写 if-else 最容易踩的坑 Common pitfalls with if-else

易错点清单:
  • else 后面误写条件——else 不带条件,直接跟冒号。
  • else 没和 if 对齐——缩进层级不一致会报错。
  • else 也写了条件却忘了——孤立的 else 没有配对的 if
  • 测距前没先 if enemy——对 None 调用 distanceTo 会崩溃。
  • < 写反成 >——逻辑就完全相反了。
  • 用 cleave 前忘了 isReady 检查——技能在冷却时调用会无效。

3.7.2 选择小结(Selection Recap)

3.7.2.1 三种选择结构的全景 The full picture of selection

到目前为止,你已经接触了选择结构的几种形态。把它们放在一起对比,脉络就清晰了:

形态结构含义
单分支if满足才做,否则跳过
双分支if … else二选一,必走其一
嵌套if 里再放 if-else先大类判断,再细分决策

本单元小结 ✨

恭喜你完成边际森林第三单元!你的英雄从"会判断"进化到了"会权衡"——不仅知道该不该做,还知道该做这个还是做那个。来回顾一下核心概念:

核心概念关键点对应关卡
双分支结构条件成立做 A,否则做 B全部
if-else 语法else: 不带条件背靠背
else 规则必须配对 if、互斥完备Else 之战
缩进对齐else 与 if 同层级全部
distanceTo测量到目标的距离测距仪
比较运算符 <距离与数字比较得真假贴身护卫
isReady检查技能是否冷却完毕森林劈斩者
cleave 劈斩群体攻击,需配合 isReady边地僵局
else 干正事有敌人打,没敌人开箱食人魔营地
嵌套判断if 里再套 if-else勇敢的食人魔

会权衡、懂取舍的英雄,才能在险境中游刃有余。
继续征战边际森林吧,勇敢的程序员!🌲⚔️

CodeCombat 编程知识 · 边际森林:第三单元

CodeCombat教研团队