← كل الأدلة

احتساب نقاط دوري 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));

يطابق هذا المثال الاختيارات بالاسم للتبسيط. في الإنتاج، احفظ rider_id لكل اختيار من GET /riders/seasons/{year} عندما يكوّن اللاعب فريقه، وفهرس النتائج بحسب rider_id.

مجاميع الموسم

احتسب كل جولة منتهية واجمع النتائج. تعيد الجولة التي لم تُقم بعد قائمة فارغة، وتمنح الجميع صفر نقطة:

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);
}

احفظ نقاط كل جولة في قاعدة بياناتك بعد صدور النتائج. لا تتغير النقاط بعد أن تصبح الجولة نهائية، فلا تستدعي الواجهة إلا لأحدث جولة.

قوائم الاختيار والتسعير

  • من يمكن اختياره: يسرد GET /riders/seasons/{year}?category=MOTOGP شبكة الموسم.
  • أسعار السائقين: استنتجها من البطولة الحالية، أي GET /standings/seasons/{year}/riders، بحيث يكلّف المتصدرون أكثر من المتأخرين.