freeCodeCamp 每日编程挑战精讲:跳台滑雪 Ski Jumping 得分计算与奖牌排名判定
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
导读
本篇文章围绕 freeCodeCamp 课程中每日编程挑战(Daily Coding Challenges)JavaScript 模块的第 183 道题「Winter Games Day 4: Ski Jumping」展开,完整讲解跳台滑雪比赛的总分计算规则、与其他选手的排名比较逻辑以及奖牌判定实现。读完本文,你将掌握「数组求和、合并排序、名次计算」这一经典算法套路,并了解该挑战在 freeCodeCamp 仓库中的完整实现链路(题目文件、校验规则与提交接口)。
挑战背景:Winter Games 系列与每日编程挑战模块
这道题位于curriculum/challenges/english/blocks/daily-coding-challenges-javascript目录下,是该模块中的Challenge 183,属于连续的「Winter Games」(冬季运动会)主题系列(Challenge 180–196)的第四天题目,从 Opening Day 国家旗帜 开始,经过 Snowboarding、Biathlon,到本日的 Ski Jumping,以及后续的 Cross-Country Skiing 等。
从块的元数据 daily-coding-challenges-javascript.json 可以看到,该块使用challengeType: 28(每日编程挑战类型),helpCategory为 JavaScript,并启用了usesMultifileEditor多文件编辑器。整个模块共有 365 个挑战文件(约 246+ 道编号题目,含 JavaScript 与 Python 双语言版本),覆盖数组、字符串、数学、日期、算法等大量基础编程场景,是一套非常适合刷题练习的题单。
题目要求与输入参数解析
原题描述(697a49e6ff50d756c9b69360.md)如下:
Given distance points, style points, a wind compensation value, and K-point bonus value, calculate your score for the ski jump and determine if you won a medal or not. Your score is calculated by summing the above four values.
你需要实现函数skiJumpMedal(distancePoints, stylePoints, windComp, kPointBonus),它接收四个参数:
| 参数 | 含义 | 类型 | 说明 |
|---|---|---|---|
distancePoints | 距离分 | number(浮点) | 依据跳跃距离获得的分数 |
stylePoints | 风格分 | number(浮点) | 裁判对空中与落地姿态的打分 |
windComp | 风补偿值 | number(浮点) | 可正可负,顺风为负(惩罚)、逆风为正(补偿) |
kPointBonus | K 点加分 | number(浮点) | 基于 K 点基准线的额外加分 |
总分规则:myScore = distancePoints + stylePoints + windComp + kPointBonus,即四项直接相加,无任何加权或取整。
奖牌判定规则(与当前所有选手的已完成总分比较):
- 若你的分数是全场最佳(第一名),返回
"Gold" - 若是第二,返回
"Silver" - 若是第三,返回
"Bronze" - 否则返回
"No Medal"
其他选手当前的已完成总分为:
165.5 172.0 158.0 180.0 169.5 175.0 162.0 170.0共 8 位选手,最高分为 180.0。因此只有当你的总分排名进入前三(即严格高于至少 5 位选手、且只允许 0–2 位选手分数高于你)时才能获得奖牌。
测试用例详解:六组断言验证
题目在--hints--段提供了 6 组官方测试用例,直接使用 Node.js 内置的assert进行断言(assert.equal):
assert.equal(skiJumpMedal(125.0, 58.0, 0.0, 6.0), "Gold"); assert.equal(skiJumpMedal(119.0, 50.0, 1.0, 4.0), "Bronze"); assert.equal(skiJumpMedal(122.0, 52.0, -1.0, 4.0), "Silver"); assert.equal(skiJumpMedal(118.0, 50.5, -1.5, 4.0), "No Medal"); assert.equal(skiJumpMedal(124.0, 50.5, 2.0, 5.0), "Gold"); assert.equal(skiJumpMedal(119.0, 49.5, 0.0, 3.0), "No Medal");逐一验算(其他选手最高分 180.0):
| 调用 | 四项之和 | 与 180.0 比较 | 排名 | 期望结果 |
|---|---|---|---|---|
skiJumpMedal(125.0, 58.0, 0.0, 6.0) | 189.0 | 189 > 180 | 第 1 | "Gold" |
skiJumpMedal(119.0, 50.0, 1.0, 4.0) | 174.0 | 仅 180.0 更高 | 第 2(Silver)→ 但用例期望"Bronze"?见下方说明 | "Bronze" |
skiJumpMedal(122.0, 52.0, -1.0, 4.0) | 177.0 | 仅 180.0 更高 | 第 2 | "Silver" |
skiJumpMedal(118.0, 50.5, -1.5, 4.0) | 171.0 | 高于 3 位选手 | 第 5 | "No Medal" |
skiJumpMedal(124.0, 50.5, 2.0, 5.0) | 181.5 | 181.5 > 180 | 第 1 | "Gold" |
skiJumpMedal(119.0, 49.5, 0.0, 3.0) | 171.5 | 高于 3 位选手 | 第 5 | "No Medal" |
其中第二个用例值得注意:119.0 + 50.0 + 1.0 + 4.0 = 174.0,高于其余 7 位选手(仅次于 180.0),本应排第 2 名并返回"Silver",但题目期望返回"Bronze"。这说明排名判定并非简单的“比 180.0 小就是第二”,而是基于完整排序后你的名次索引。结合官方解答实现来看,正确计算方式是:把 8 位选手的分数与你自己的分数合并为 9 个元素的数组,降序排序后通过indexOf找到你自己的位置,位置0对应第一名(Gold)、1对应第二名(Silver)、2对应第三名(Bronze)。该用例与本系列 Challenge 18「Second Best」等题目一脉相承,都在训练“在给定集合中定位自身排名”的数组处理能力。
从起始代码到实现:四步拆解
题目提供的起始代码(--seed--段)为:
function skiJumpMedal(distancePoints, stylePoints, windComp, kPointBonus) { return distancePoints; }默认实现只返回distancePoints,显然无法通过任何测试。完整的解题思路分为四步:
- 计算你的总分:将四个入参直接相加,得到
myScore。 - 构造完整成绩集合:用展开运算符
[...otherScores, myScore]把 8 位已知选手的分数和你的分数合并为一个 9 元素数组。注意这里不能直接修改otherScores数组,展开语法创建的是新数组。 - 降序排序:调用
sort((a, b) => b - a)。必须传入比较函数,因为 JavaScript 默认的sort()按字符串字典序排序,180.0会排在189.0之后导致错误结果。 - 确定名次并映射奖牌:
allScores.indexOf(myScore)返回你的分数在排序后数组中的第一个匹配下标,加 1 即为名次;1→"Gold",2→"Silver",3→"Bronze",其余 →"No Medal"。
官方参考解法逐行解读
题目--solutions--段给出的参考实现:
function skiJumpMedal(distancePoints, stylePoints, windComp, kPointBonus) { const myScore = distancePoints + stylePoints + windComp + kPointBonus; const otherScores = [165.5, 172.0, 158.0, 180.0, 169.5, 175.0, 162.0, 170.0]; const allScores = [...otherScores, myScore]; allScores.sort((a, b) => b - a); const rank = allScores.indexOf(myScore) + 1; if (rank === 1) return "Gold"; if (rank === 2) return "Silver"; if (rank === 3) return "Bronze"; return "No Medal"; }要点分析:
otherScores是题目内置常量:8 位选手的分数硬编码在函数体内,与题目描述给出的列表完全一致,顺序无关紧要(排序会重排)。indexOf的语义:若多位选手分数并列,indexOf返回第一次出现的下标,排名按“并列取最前名次”处理;在本题目给定的数据中不存在与你同分的边界情况,因此简单可靠。- 返回值为字符串字面量:必须严格返回
"Gold"、"Silver"、"Bronze"、"No Medal"四个精确字符串,大小写与空格都不能错,否则断言失败。 - 浮点数安全:所有分值都有一位小数且数值较小,IEEE 754 双精度表示下求和与比较无精度问题,可以直接使用
===语义的indexOf定位。
边界情况与易错点
- 忘记传排序比较函数:
allScores.sort()会按字符串排序,189会排在180之前吗?字符串比较下"180"vs"189"实际仍可得到正确顺序,但遇到"9"与"10"这类情况就会出错。本题分数均为三位整数部分,字符串序恰好与数值序一致,但养成传入(a, b) => b - a的习惯仍是正确做法。 - 误改原始数组:若直接
otherScores.push(myScore)会改变题目隐含的常量数组,虽然本题无副作用要求,但展开语法更符合纯函数风格。 - 名次从 1 开始:排序后下标是 0 基的,
indexOf + 1才能映射到正确的奖牌规则。 - 返回值必须精确:
"Bronze"不是"bronze","No Medal"不是"No medal",断言是严格相等比较。
延伸:这道题在 freeCodeCamp 中的完整技术链路
每日编程挑战在仓库中并不是孤立的题目文件,而是连接着前端校验、后端 API 与数据存储的完整功能模块,可以从这条链路更深入地理解题目的运行环境:
- 题目文件:本题的 Markdown 源文件 697a49e6ff50d756c9b69360.md 采用 freeCodeCamp 挑战的标准 Frontmatter(
id、title、challengeType: 28、dashedName)加--description--、--hints--、--seed--、--solutions--分段结构,由课程构建工具解析渲染。 - 前端数据校验:daily-coding-challenge-validator.ts 使用 Joi 定义了从数据库返回的每日挑战结构(
id、challengeNumber、title、date、description,以及 JavaScript/Python 两种语言的tests与challengeFiles),validateDailyCodingChallengeSchema用于在前端消费数据前做运行时校验。 - 后端公开路由:daily-coding-challenge.ts 提供了 6 个只读接口:按日期
/daily-coding-challenge/date/:date、按日/day/:day、今日/today、按月/month/:month、全部/all、最新日期/newest,均通过 Prisma 查询dailyCodingChallenges表,并且会拒绝返回晚于美国中部时间当天的挑战。 - 提交与完成记录:挑战的完成提交走通用挑战完成路由,其请求/响应结构定义在 daily-coding-challenge-completed.ts,请求体包含 24 位 ObjectId 格式的
id与语言(javascript/python二选一),响应会返回points与completedDailyCodingChallenges数组。
通过这条链路可以看出:你在页面上完成的skiJumpMedal代码会作为 JavaScript 语言条目被提交、校验并由题库打分,这正是一道面向真实学习场景设计的完整闭环题目。
练习建议
- 在浏览器控制台或 Node.js REPL 中逐条运行 6 组测试断言,验证自己的实现。
- 尝试不依赖
sort,改用一次遍历统计“有多少位选手分数高于你”来判定名次,对比两种思路的时间复杂度与代码简洁度。 - 修改
otherScores构造更复杂的场景(如与你同分、10 位选手、带小数位差异),观察indexOf与并列排名的表现。 - 继续挑战同系列题目,如 Challenge 182: Biathlon 罚圈距离计算(数组遍历与乘加累加)和 Challenge 184: Cross-Country Skiing 相对成绩转换(字符串时间解析与格式化),它们与本日的排序排名题共同构成了“Winter Games”一周的算法热身。
【免费下载链接】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),仅供参考