解析 freeCodeCamp 每日编程挑战第 244 题:车与象的攻击判定(Rook and Bishop Attack)
2026/9/10 21:38:31 网站建设 项目流程

解析 freeCodeCamp 每日编程挑战第 244 题:车与象的攻击判定(Rook and Bishop Attack)

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

本篇技术指南以 freeCodeCamp 开源仓库中 Challenge 244: Rook and Bishop Attack 为讲解核心,逐行拆解这道棋盘攻击判定题目的数学模型、官方测试用例与标准解法,并结合仓库内区块配置、每日挑战 API 与前端日历组件源码,说明该挑战在真实项目中的组织与运行方式。读完本文,你将掌握"用代数坐标描述棋盘棋子、以行列差判定直线攻击、以绝对值差判定斜线攻击"这一类算法题的通用解题范式,并理解 freeCodeCamp 每日挑战从 Markdown 题目到在线测评的完整链路。

题目背景:freeCodeCamp 每日编程挑战体系中的第 244 题

这道题属于 freeCodeCamp 仓库中daily-coding-challenges-javascript区块。在该区块的元数据文件 curriculum/structure/blocks/daily-coding-challenges-javascript.json 中,可以看到本挑战的登记信息:

{ "id": "69b5b2be76ec8135a7fbe973", "title": "Challenge 244: Rook and Bishop Attack" }

该区块被编排在dev-playground超级块(superblock)之下(见 curriculum/structure/superblocks/dev-playground.json),整块采用legacy-challenge-list布局,按challengeOrder数组中的顺序逐题推进。挑战文件本体位于 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/69b5b2be76ec8135a7fbe973.md,它是第 243 题"Rook Attack"(车吃车)的升级版本:上一题只要求判断两个车是否互相攻击,而本题让车与象同台竞技,需要同时处理两种完全不同的走法规则。

棋盘坐标模型:代数记谱法(Algebraic Notation)

题目给定棋盘为标准的 8×8 国际象棋盘,坐标体系采用代数记谱法:

  • 列(file):从左到右标记为AH
  • 行(rank):从下到上标记为18

每个格子由"列字母 + 行数字"唯一标识,例如左下角为A1,右上角为H8。题目原文给出了完整的坐标表:

A8B8C8D8E8F8G8H8
A7B7C7D7E7F7G7H7
A6B6C6D6E6F6G6H6
A5B5C5D5E5F5G5H5
A4B4C4D4E4F4G4H4
A3B3C3D3E3F3G3H3
A2B2C2D2E2F2G2H2
A1B1C1D1E1F1G1H1

将棋盘建模为代数坐标系后,一个格子的坐标天然被拆成两部分:下标0是列字符('A''H'),下标1是行字符('1''8')。字符串索引的直接可读性正是本题选择字符串输入的原因。

攻击规则与返回值约定

题目明确了两枚棋子的移动能力:

  • 车(Rook):可在水平或垂直方向移动任意格数;
  • 象(Bishop):可在任意对角线方向移动任意格数;
  • 攻击判定:一枚棋子能够移动到另一枚棋子所在的格子,即视为可以攻击对方。

函数签名与返回值约定为:rookBishopAttack(rook, bishop),其中第一个参数是车的位置字符串,第二个参数是象的位置字符串,返回值必须是以下三种之一:

返回值含义
"rook"车可以攻击到象
"bishop"象可以攻击到车
"neither"双方均无法攻击对方

需要特别留意:车和象的走法永远不会冲突——车只走直线、象只走斜线,因此同一对坐标不可能同时满足两种攻击条件(除非两枚棋子位于同一格,这不在合法输入范围内)。这正是本解法可以先判车、再判象、最后兜底返回"neither"的原因。

官方测试用例(hints)逐条解析

挑战文件的--hints--段定义了 6 组必须通过的断言,全部基于 freeCodeCamp 在线测评使用的assert.equal

1. 同列攻击(车胜出)

assert.equal(rookBishopAttack("A1", "A5"), "rook");

车在A1,象在A5。两者列相同(均为A),车可沿垂直方向直上 4 格到达象的位置,因此车获胜。

2. 对角线攻击(象胜出)

assert.equal(rookBishopAttack("C3", "F6"), "bishop");

C3F6:列差为|C − F| = 3,行差为|3 − 6| = 3,列差等于行差,说明两点处于同一条 45° 对角线上,象可沿斜线攻击车。

3. 同行攻击(车胜出)

assert.equal(rookBishopAttack("D4", "D7"), "rook");

又是同列(D列),车垂直上移 3 格即可攻击象。

4. 长对角线攻击(象胜出)

assert.equal(rookBishopAttack("B7", "H1"), "bishop");

B7H1:列差|B − H| = 6,行差|7 − 1| = 6,列差等于行差,处于同一对角线,象获胜。

5. 完全无法互攻

assert.equal(rookBishopAttack("B3", "C5"), "neither");

B3C5:列不同(B ≠ C),行不同(3 ≠ 5),列差 1 ≠ 行差 2,既不同行不同列,也不在同一条对角线上,因此返回"neither"

6. 又一个互不攻击的用例

assert.equal(rookBishopAttack("G3", "E8"), "neither");

G3E8:列差|G − E| = 2,行差|3 − 8| = 5,2 ≠ 5,双方均无法攻击。

从测试分布可以看出,出题者刻意覆盖了四种情形:同列、同行(车攻击)各 1 例,短/长对角线(象攻击)各 1 例,以及 2 例"neither",确保所有分支都被验证。

种子代码:从"占位实现"出发

挑战文件中的--seed-contents--段提供了起始代码,学习者在编辑器中以此为基础补全函数体:

function rookBishopAttack(rook, bishop) { return rook; }

种子函数默认直接返回rook(即车的位置字符串),显然不满足任何一条断言。这种"返回第一个参数占位"的写法是 freeCodeCamp 挑战模板的常见做法,其目的仅仅是保证函数在修改前可被调用、测评系统能正常加载,真正逻辑需要学习者自行编写。

官方标准解法逐行拆解

挑战文件的--solutions--段给出了参考实现:

function rookBishopAttack(rook, bishop) { const colDiff = Math.abs(rook.charCodeAt(0) - bishop.charCodeAt(0)); const rowDiff = Math.abs(rook[1] - bishop[1]); if (rook[0] === bishop[0] || rook[1] === bishop[1]) return "rook"; if (colDiff === rowDiff) return "bishop"; return "neither"; }

下面逐行分析其原理:

第 1 行:计算列差

const colDiff = Math.abs(rook.charCodeAt(0) - bishop.charCodeAt(0));

charCodeAt(0)取出坐标首字符(列字母)的 Unicode 码点:'A'为 65,'B'为 66,依此类推,'H'为 72。由于字母表中相邻字母码点连续,'A'.charCodeAt(0) - 'B'.charCodeAt(0)恰好等于 1,即 1 列的棋盘距离。Math.abs保证列差总是非负值,从而无需关心两枚棋子谁在左谁在右。这是把"字母列"转成"数值距离"的最简洁手段。

第 2 行:计算行差

const rowDiff = Math.abs(rook[1] - bishop[1]);

rook[1]bishop[1]是行数字符。这里利用了 JavaScript 的隐式类型转换:当减号作用于字符串时,JS 会先把两个操作数转为数值再相减,因此'5' - '1'得到数值 4。严格来说更显式的写法是Math.abs(Number(rook[1]) - Number(bishop[1])),但官方解法直接依赖隐式转换,代码更紧凑。Math.abs同样保证行差为非负。

第 3 行:车攻击判定

if (rook[0] === bishop[0] || rook[1] === bishop[1]) return "rook";

车只能在水平(同行,rook[1] === bishop[1])或垂直(同列,rook[0] === bishop[0])方向移动。只要两者行相同或列相同,车就能直线走到象的位置,立即返回"rook"。注意:由于题目保证输入为合法坐标且两枚棋子位置不同,同行与同列不会同时成立,因此不存在歧义。

第 4 行:象攻击判定

if (colDiff === rowDiff) return "bishop";

象沿 45° 对角线移动,其几何特征可表述为"横向走的格数等于纵向走的格数"。因此只要列差等于行差,两点必在同一条对角线上(无论是\方向还是/方向),象即可攻击车,返回"bishop"

第 5 行:兜底分支

return "neither";

若车无法直线到达(行列都不相同),象也无法斜线到达(列差 ≠ 行差),则双方互不能攻击,返回"neither"

整体来看,该解法的时间复杂度与空间复杂度均为 O(1),没有任何循环或额外数据结构,是典型的"数学判定型"题目标准解。

从第 243 题看递进关系:单棋子 → 双棋子

将本题与仓库中前一道 Challenge 243: Rook Attack 对比,可以更清晰地看出题目设计者的递进意图:

第 243 题要求判断两个车是否互攻,标准解是:

function rookAttack(rook1, rook2) { return rook1[0] === rook2[0] || rook1[1] === rook2[1]; }

这里只需判断"同行或同列",返回布尔值true/false。而第 244 题引入走法截然不同的象之后,必须同时考虑直线的行列相等与斜线的"列差等于行差"两种判定,且返回值从布尔值升级为三态字符串"rook"/"bishop"/"neither"。两题共享"坐标字符串拆分为列字符 + 行字符"这一核心建模思想,第 244 题正是第 243 题的自然延伸。

仓库源码佐证:挑战如何从 Markdown 走向在线测评

在 freeCodeCamp 仓库中,一道每日挑战的生命周期横跨课程数据、后端 API 与前端展示三个层面:

课程数据层

挑战以 Markdown 形式存放在 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/ 目录下,通过 YAML frontmatter 声明idtitlechallengeTypedashedName等元信息。其中challengeType: 28即"每日挑战"这一题型标识。区块的题目顺序、帮助分类(helpCategory: "JavaScript")、是否启用多文件编辑器(usesMultifileEditor)等均在 curriculum/structure/blocks/daily-coding-challenges-javascript.json 中配置。仓库测试 curriculum/src/test/daily-challenges.test.js 会遍历daily-coding-challenges-javascriptdaily-coding-challenges-python两个区块,校验每日挑战数据的完整性与关联关系。

后端 API 层

每日挑战的内容由 API 的 daily-coding-challenge 模块提供。该模块的 README(api/src/daily-coding-challenge/README.md)明确指出:这些接口只负责获取挑战信息,挑战的提交仍走主挑战完成路由。在 api/src/daily-coding-challenge/routes/daily-coding-challenge.ts 中注册了六个公开 GET 路由:

路由用途
/daily-coding-challenge/date/:dateYYYY-MM-DD日期取某一天挑战
/daily-coding-challenge/day/:dayMM-DD取某月某日的挑战
/daily-coding-challenge/today取美国中部时间当天的挑战
/daily-coding-challenge/month/:monthYYYY-MM取整月挑战列表
/daily-coding-challenge/all取全部已发布挑战列表
/daily-coding-challenge/newest取最新挑战的日期

其中实现细节值得注意:接口以"美国中部时间当天"为发布边界,不会返回未来日期尚未发布的挑战;参数校验失败返回 400,找不到挑战返回 404,异常统一捕获后返回 500,并通过 Sentry 记录dcc.challenge_not_founddcc.challenge_viewed等指标。虽然 Challenge 244 本身由测评系统运行 JavaScript 代码判定,但它的题目信息正是通过这套路由提供给前端展示的。

前端展示层

客户端在 client/src/components/daily-coding-challenge/calendar.tsx 中消费上述 API:从 Redux 选择器读取用户已完成挑战列表,拉取整月挑战数据后生成日历视图,逐日渲染完成状态与语言标记;client/src/components/daily-coding-challenge/widget.tsx 则在学习地图与首页提供"前往今日挑战"与"挑战归档"两个入口,其中今日入口链接形如/learn/daily-coding-challenge/MM-DD。由此可见,一道像 Challenge 244 这样的题目,从 curriculum 目录下的 Markdown 文件 出发,最终通过 API 与日历组件呈现在学习者面前。

常见错误与边界情况讨论

在实际编写本题时,有几个容易踩坑的点值得单独说明:

  1. 忘记Math.abs导致负数比较:若不取绝对值,'C'.charCodeAt(0) - 'F'.charCodeAt(0)为 -3,与行差 3 永远不相等,对角线判定将完全失效。
  2. 使用rook[1] - bishop[1]隐式转换时误写为字符串拼接:若误用+运算符,'3' + '6'会得到字符串'36',导致判定结果完全错误;减法没有该问题。
  3. 返回值大小写与引号不匹配:题目要求返回小写字符串"rook""bishop""neither",返回布尔值、大写字符串或null都会导致assert.equal失败。
  4. 假设"列字母到数字"需要手动映射:虽然可以用{A:1, B:2, ...}对象映射,但charCodeAt与字母表连续码点这一数学性质让解法可以压缩为一行计算,这也是官方解法的精妙所在。
  5. 认为"相同对角线"只指\方向colDiff === rowDiff同时覆盖\(如C3F6)与/(如B7H1)两个方向,因为两者在坐标差上的表现一致——横向步数与纵向步数相等。

解法扩展:不同语言与不同实现风格

虽然测评环境限定 JavaScript,但该判定模型可以无缝迁移到任意语言。例如在 Python 中,利用ord()abs()可以写出等价实现:

def rook_bishop_attack(rook, bishop): col_diff = abs(ord(rook[0]) - ord(bishop[0])) row_diff = abs(int(rook[1]) - int(bishop[1])) if rook[0] == bishop[0] or rook[1] == bishop[1]: return "rook" if col_diff == row_diff: return "bishop" return "neither"

即便不依赖charCodeAt/ord,也可以把 8×8 棋盘预计算为"列字母 → 列索引"的查找表,或直接用 ASCII 差作为列距离。核心思路始终是:把棋盘的几何攻击关系转化为坐标数值的相等/差值比较。这种"将问题域建模为坐标系后做数学判定"的方法,同样适用于题库中其他棋盘类题目(如第 130 题 Checkerboard、第 160 题 Knight Moves、第 173 题 Valid Pawn Moves、第 217 题 Captured Chess Pieces,均可从 curriculum/structure/blocks/daily-coding-challenges-javascript.json 的challengeOrder中检索到)。

小结

Challenge 244: Rook and Bishop Attack 是一道小而精的算法题:它用 8×8 棋盘的代数记谱法做输入建模,要求同时处理车的直线攻击与象的斜线攻击,并以三态字符串输出判定结果。官方解法通过charCodeAt把列字母转为数值、借助 JS 隐式转换处理行字符,再用colDiff === rowDiff统一表达两个方向的对角线关系,将整个判定压缩为常数时间的数学比较。在 freeCodeCamp 仓库中,本题不仅是 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/69b5b2be76ec8135a7fbe973.md 的一份 Markdown,更通过 区块结构配置、API 路由实现 与 前端日历组件 构成了"题目编写 → 服务端供给 → 前端呈现 → 在线测评"的完整闭环,是理解 freeCodeCamp 课程体系与工程架构的绝佳样本。

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询