为 MotoGP 梦幻联赛计分
用你自己的计分规则把比赛结果转换为梦幻积分,并在每一轮之后为联赛中的队伍排名。
阅读约 10 分钟
梦幻游戏是赛事数据的最佳用途之一:玩家挑选车手,每个结果都会改变联赛排行榜。本指南用 JavaScript 按你可控的规则为联赛计分。
规则
每支梦幻队伍挑选三名车手。每一轮之后,每名车手获得:
| 事件 | 梦幻积分 |
|---|---|
| 在比赛中获得的锦标赛积分 | 每分 1 分 |
| 夺冠 | +10 奖励 |
| 登上领奖台(P1–P3) | +5 奖励 |
| 未完赛 | −5 |
请把规则当作数据而不是代码,以便在赛季之间调整。
// scoring.js
export const RULES = Object.freeze({
perPoint: 1,
win: 10,
podium: 5,
dnf: -5,
});
/** Fantasy points for one rider's race result. Pure: same input, same output. */
export function scoreResult(result, rules = RULES) {
let score = result.points * rules.perPoint;
if (result.status === "FINISHED") {
if (result.position === 1) score += rules.win;
if (result.position <= 3) score += rules.podium;
} else if (result.status === "DNF") {
score += rules.dnf;
}
return score;
}
为一轮计分
GET /races/seasons/{year}/round/{round}/results 为每名车手返回一行,包含 position、points、status、rider_name 和稳定的 rider_id。按车手建立索引,并汇总每支队伍所选车手的得分:
// round.js
import { scoreResult } from "./scoring.js";
const HOST = "motogp-api-championship-data-statistics.p.rapidapi.com";
const headers = { "X-RapidAPI-Key": process.env.RAPIDAPI_KEY, "X-RapidAPI-Host": HOST };
async function get(path) {
const res = await fetch(`https://${HOST}${path}`, { headers });
if (!res.ok) throw new Error(`${path}: ${res.status}`);
return (await res.json()).data;
}
export async function scoreRound(year, round, teams) {
const results = await get(`/races/seasons/${year}/round/${round}/results?category=MOTOGP`);
const byRider = new Map(results.map((r) => [r.rider_name, r]));
return teams
.map((team) => ({
name: team.name,
points: team.riders.reduce((sum, rider) => {
const result = byRider.get(rider);
return sum + (result ? scoreResult(result) : 0); // didn't start: 0
}, 0),
}))
.sort((a, b) => b.points - a.points);
}
const league = [
{ name: "Late Brakers", riders: ["Marco Bezzecchi", "Raúl Fernández", "Fabio Di Giannantonio"] },
{ name: "Apex Hunters", riders: ["Marc Márquez", "Jorge Martín", "Pedro Acosta"] },
];
console.table(await scoreRound(2026, 1, league));
为便于阅读,本例按姓名匹配所选车手。在生产环境中,请在玩家组建队伍时,从 GET /riders/seasons/{year} 保存每位车手的 rider_id,并按 rider_id 为结果建立索引。
赛季总分
为每个已完赛的轮次计分并累加。尚未举行的轮次会返回空列表,所有人得 0 分:
export async function seasonTable(year, teams, lastRound) {
const totals = new Map(teams.map((t) => [t.name, 0]));
for (let round = 1; round <= lastRound; round++) {
for (const { name, points } of await scoreRound(year, round, teams)) {
totals.set(name, totals.get(name) + points);
}
}
return [...totals].map(([name, points]) => ({ name, points })).sort((a, b) => b.points - a.points);
}
结果确定后,把每一轮的得分存入数据库。一轮比赛定局后分数不会再变,因此你只需为最新一轮调用 API。
候选名单与定价
- 可选车手:
GET /riders/seasons/{year}?category=MOTOGP列出本赛季的发车阵容。 - 车手价格: 根据当前锦标赛(
GET /standings/seasons/{year}/riders)推算,让领跑者比落后者更贵。