MVPeak AI
Personal Project · Team of 2Role
Co-Developer
Team
Najaf Arash Dastnaei
Joshua Mills Lamptey
Timeline
4 Months
Overview
MVPeak was an AI-powered League of Legends coaching platform designed to help players understand and improve their gameplay. We combined match data, high-level player statistics and AI analysis to identify mistakes, explain decision-making and provide personalised guidance. The aim was to move beyond generic advice and give players insights based on what actually happened in their games.
Tech Stack
Python, FastAPI, Riot Games API, OpenAI & Anthropic APIs, Qdrant, Supabase, PostgreSQL, Vercel, Google Cloud Run
Situation
League of Legends gives players extensive match statistics, but raw numbers rarely explain the decisions that actually led to a loss. Reviewing games manually to spot positioning mistakes, missed opportunities, and better alternatives takes significant time and game knowledge most players don't have.
Task
As co-developer working with one teammate over 4 months, my task was to help design and build an AI coaching platform that could turn raw Riot match data into personalised, explainable feedback benchmarked against high-elo play, at a cost the two of us could realistically sustain.
Action
I helped build an ETL pipeline that ingested match data from the Riot Games API, parsed game events into structured situations, stored everything in PostgreSQL, and generated statistical baselines from high-ranked players. We combined those datasets with vector embeddings in Qdrant and an LLM (OpenAI and Anthropic) to generate coaching reports, split the expensive processing into an offline pipeline so live requests returned quickly, and deployed the API to Google Cloud Run with the frontend on Vercel.
Result
The application was fully functional end to end, generating real coaching reports from live match data. To validate coaching quality, we had practising League of Legends coaches review the output and rate its usefulness and accuracy, which averaged 8.8 out of 10, with incorrect guidance appearing only rarely. What made the project unsustainable was cost: running the pipeline outside local development incurred high storage costs at scale, which was beyond what we could support as a self-funded two-person team, so we made the call to stop development rather than continue spending on something we couldn't realistically sustain long term.
Turning matches into structured situations
Every ingested match was parsed into structured situations rather than raw stats. A custom extractor walked Riot's match timeline and produced 23 distinct situation types across kills, objectives, structures, economy, vision, and periodic game state snapshots, each enriched with a full ten player state matrix, lane and jungle threat context, wave management analysis, and live tower and objective respawn timers. An AFK filter removed every situation for a champion still sitting in base at the one minute mark, so leaver games never polluted the reference data used for coaching.
Embedding gameplay in a way that transfers across ranks
Each situation was embedded as a templated natural language description, not raw numbers. Farm and gold were expressed as percentile brackets relative to Challenger baselines for that role and minute, so a Gold player's pace could semantically match a Challenger's even though the raw numbers were worlds apart. At query time, the player's own situation was embedded and matched against a Qdrant index of professional and Challenger situations, filtered by role and patch and ranked by cosine similarity, before the surrounding event sequence for each match was pulled from Postgres and split into situations that led to survival versus death.
Turning retrieval into a lesson, not a lecture
A Fisher's exact test compared those two groups to find which decision, such as recalling or placing a ward, actually separated the outcomes, with a guardrail that discarded any signal running against its expected direction so a handful of unlucky samples could never produce bad advice. The retrieved comparisons themselves never reached the language model. Only the resulting instruction did, generated by an o3 based coaching layer with Anthropic's Claude available as a fallback provider, so the coaching stayed in plain, natural language instead of reading like a statistics report.