← home

PORTFOLIO

01

LOL DRAFT TOOL

Champion pick recommendations from challenger match data
In progress
The draft board: your team and enemy team pick slots on either side, ban rows below, and a ranked list of recommended champions in the middle

THE PROBLEM

In League of Legends, each team picks its champions in turn before the game starts, and the hardest part is choosing yours. The best pick depends on who your teammates and opponents have chosen so far, but tier lists rate champions in isolation. Nothing tells you which champion is strongest for the specific draft in front of you, so you end up picking on instinct.

THE SOLUTION

A recommendation tool that suggests the best champion to pick as the draft unfolds. As you enter bans and picks, it ranks your options by how each one interacts with the champions your teammates and opponents have chosen. It's built on every current-patch game the top-ranked players have played, so the advice reflects how the game is played right now.

UNDER THE HOOD

  • Rate limiter. A thread-safe sliding window keeps the sync inside Riot's two limits (20 requests/sec, 100 per 2 min), with retry and backoff.
  • Incremental sync. Each player resumes from a checkpoint, games shared between players are de-duplicated, and writes are batched per player.
  • Patch-aware. A daily job detects a new game patch and resets the statistics, so recommendations never mix old and new balance changes.
  • Model-ready storage. Every match is stored as a role-by-role row of champion picks and the result, ready to train on.
  • Push-to-deploy. The API redeploys to Cloud Run from GitHub Actions through Workload Identity Federation, with no stored cloud credentials.
  • Lean API. Two endpoints, one for champion metadata and one for recommendations, typed end to end with Pydantic.
  • Python
  • PostgreSQL
  • FastAPI
  • GitHub Actions
  • Cloud Run
02

UNDECIDABILITY IN FRACTAL GEOMETRY

A proof that no algorithm can (always) answer a simple question
Undergraduate research course with Dr. Alp Uzman · Dec 2024
Complete
from the paper
A rotated, scaled Sierpinski gasket in the unit square, crossed by the line segment from (0,0) to (1,1)
The paper's opening example: a Sierpiński gasket, rotated 30°, halved and shifted, against the diagonal line segment from (0,0) to (1,1). Does the line intersect the fractal, or pass through its gaps? Checking ever-finer approximations can prove the line misses, but never that it intersects. For fractals in general, no algorithm can always decide whether the two intersect.

THE QUESTION

A fractal is a shape built from smaller copies of itself at every scale. One way to make one is an iterated function system: a set of shrinking maps that, applied over and over, settle on a single shape called the attractor. Take such a fractal in the unit square, along with the diagonal from (0,0) to (1,1). Do they intersect? The obvious method, refining the fractal until the line misses it, never stops when they do intersect. Is there any algorithm that always answers this question?

THE APPROACH

Simant Dube proved the answer is no in 1993, as one result in a paper built to prove many. I isolated that single proof and rebuilt it for classmates with no background in computability: a reduction from the Infinite Post Correspondence Problem, known to be undecidable. Dube's interpretation function reads strings as base-(k+1) numbers, so each PCP instance becomes a set of maps that "append" word pairs, and a matching sequence exists exactly when the fractal intersects the diagonal.

THE RESULT

An 8-page self-contained write-up with full proofs, and a 10-minute talk at the end of the semester that took the class from "what is a decision problem?" to the undecidability result.

  • Computability
  • Fractal geometry
  • Formal languages
  • LaTeX
open in new tab ↗