← Todas las guías

Puntúa una liga fantasy de MotoGP

Convierte los resultados de carrera en puntos fantasy con tus propias reglas y clasifica a los equipos de tu liga tras cada ronda.

10 min de lectura

Los juegos fantasy son uno de los mejores usos de los datos de carreras: los jugadores eligen pilotos y cada resultado mueve la tabla de la liga. Esta guía puntúa una liga en JavaScript con reglas que controlas tú.

Las reglas

Cada equipo fantasy elige tres pilotos. Tras cada ronda, cada piloto gana:

EventoPuntos fantasy
Puntos de campeonato logrados en la carrera1 por punto
Victoria+10 de bonus
Podio (P1–P3)+5 de bonus
No terminar−5

Guarda las reglas como datos, no como código, para poder ajustarlas entre temporadas.

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

Puntúa una ronda

GET /races/seasons/{year}/round/{round}/results devuelve una fila por piloto con position, points, status, rider_name y un rider_id estable. Indexa las filas por piloto y suma las elecciones de cada equipo:

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

Este ejemplo relaciona las elecciones por nombre para que sea legible. En producción, guarda el rider_id de cada elección desde GET /riders/seasons/{year} cuando el jugador arma su equipo, e indexa los resultados por rider_id.

Totales de la temporada

Puntúa cada ronda terminada y suma los resultados. Una ronda que aún no se ha disputado devuelve una lista vacía y puntúa 0 para todos:

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

Guarda en tu base de datos las puntuaciones de cada ronda en cuanto haya resultados. Las puntuaciones nunca cambian cuando una ronda es definitiva, así que solo llamas a la API para la ronda más reciente.

Listas de elección y precios

  • Quién se puede elegir: GET /riders/seasons/{year}?category=MOTOGP lista la parrilla de la temporada.
  • Precios de los pilotos: derívalos del campeonato actual — GET /standings/seasons/{year}/riders — para que los líderes cuesten más que los últimos.