Task 1

Buber Fuel

Buber, a cab company, has hired a team of vibe coders to train a model that predicts the fuel a ride burns from the car, the trip distance and a bunch of other parameters. Buber is large, so its ride history is huge, and the vibe coders, being vibe coders, tried pd.read_csv() on it and failed miserably. You are the adult in the room. Build the service they query instead. There is no frontend: package it as a Docker container, push it to GitHub and submit the repository here. Our server builds it, runs it in a sandbox with the real dataset, hits it with a burst of queries and gives you a verdict.

1. What to build

A query holds the quantitative description of a ride, nothing else: no ride id, no pickup time, no driver, no zones. Your service returns the fuel burned (litres) by a ride in the dataset that matches the query, within the error the query allows. It is guaranteed that at least one ride in the dataset matches every query exactly.

POST /fuel HTTP/1.1
Content-Type: application/json

{"distance_km": 12.35, "engine_cc": 1600, "fuel_type": 0,
 "vehicle_weight_kg": 1250, "avg_speed_kmh": 41.5, "idle_min": 3.2,
 "ambient_temp_c": 27, "ac_on": 1, "passengers": 2, "gradient_pct": 0.4,
 "max_error": 0.05}

HTTP/1.1 200 OK
Content-Type: application/json

{"fuel_l": 1.874}
  • All ten features are numbers. Missing or non-numeric feature, or a body that is not JSON: 400.
  • max_error is optional and is a fraction: 0.05 means the answer may be off by 5%. Without it, assume 5%.
  • Ignore fields you do not recognise.
  • Your answer is correct if it is within max_error of the fuel of any ride that matches the query. You may answer from a similar ride, from your own estimate, or from a formula you worked out. We do not care how.

The features

FieldMeaningQuery precision
distance_kmtrip distance2 decimals below 10 km, 1 decimal below 100 km, whole km from 100
engine_ccengine sizemultiples of 100, as in the file
fuel_type0 petrol, 1 diesel, 2 CNG, 3 hybridas in the file
vehicle_weight_kgkerb weightnearest 10 kg
avg_speed_kmhaverage speednearest 0.5 km/h
idle_minminutes spent idlingnearest 0.1 min
ambient_temp_coutside temperaturenearest 1 °C
ac_onair conditioning, 0 or 1as in the file
passengers1 to 4as in the file
gradient_pctaverage road gradientnearest 0.1 %

The dataset keeps more digits than a query does. A query is the ride rounded to the precision above (halves round up), so a ride in the file is matched by a query when its features round to the query's. Several rides can share one query and then any of their fuel values is acceptable.

The dataset

A fixed ride log of roughly 30 million rows (about 2.5 GB of CSV). It is not in your repository and you cannot download it. A random sample of about 300,000 rows comes from the same log: rides_sample.csv (24 MB). Columns:

ride_id,pickup_ts,driver_id,start_zone,end_zone,distance_km,engine_cc,fuel_type,vehicle_weight_kg,
avg_speed_kmh,idle_min,ambient_temp_c,ac_on,passengers,gradient_pct,fuel_l

How the judge talks to you

After a few single-request checks, the judge sends 2,000 POST /fuel queries inside 1 second. It does not wait for answers before sending the next query, so a slow server simply falls behind. Each query carries its own max_error:

Share of queriesmax_error
70%5%
20%2%
10%1%

Accuracy and speed pull against each other: scanning for the closest rides is exact but slow, guessing is instant but sloppy. Pick your balance. Answers are only verified once the burst is over. You have 2 CPUs and 512 MB.

2. Container contract

DockerfileA file named Dockerfile at the repository root. The whole service runs from this one image.
PortYou choose the port. Declare it with exactly one EXPOSE <port> line in your Dockerfile and listen on 0.0.0.0 at that port. localhost-only binds are unreachable.
DatasetRead-only file at the path in $DATASET_PATH. Set a default for it in your Dockerfile (ENV DATASET_PATH=/dataset/rides_sample.csv) so your experiments work; the judge overrides it. In the final no-dataset run the file is missing: do not crash on that.
HealthGET /healthz must return 200 within 4 minutes of the container starting. That is your budget for reading or indexing the data. Traffic only starts once it is healthy, and a fast start earns a bonus.
NetworkThe build step has internet access (install your dependencies there). At runtime there is no outbound network.
FilesystemRead-only root. /tmp and /data are writable (64 MB each, wiped on exit). The full dataset is not baked into your image and is the only thing mounted at $DATASET_PATH.
Resources512 MB RAM, 2 CPUs, 256 processes. Linux capabilities are dropped, so run as a non-root user if you can.
LimitsRepository up to 300 MB, build up to 10 minutes, image up to 1 GB.

Minimal Dockerfile shape

# Any base image works: node, python, go, java...
FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements.txt   # the build has internet access
USER nobody
EXPOSE 8080
# Experiments: the sample. The judge replaces this with -e DATASET_PATH=<full dataset>.
ENV DATASET_PATH=/dataset/rides_sample.csv
CMD ["python", "-u", "server.py"]

3. How to submit

  1. Push your project to a GitHub repository.
  2. Register your team (once) and log in.
  3. Open Submit, paste the repository URL, optionally a branch or tag (default branch if empty).
  4. Private repository? Create a fine-grained personal access token limited to that one repository with "Contents: Read-only" and paste it in the token field. It is encrypted at rest and deleted as soon as judging finishes.
  5. Watch the live status page. You can resubmit after each result (short cooldown applies, one submission at a time per team).

4. How you are judged

Every submission goes through the same automated pipeline:

  1. Clone your repository at its latest commit (the commit hash is recorded with your result).
  2. Build the Docker image.
  3. Run it in an isolated sandbox with no outbound network and the full dataset mounted.
  4. Check the contract with a handful of single requests: a valid query, a missing feature, a bad body.
  5. Hit it with a burst of 2,000 POST /fuel queries in one second, record every answer, then check each one against the rides that match its query.
  6. Run it again, with no dataset. The same image is restarted with DATASET_PATH pointing at a file that does not exist, and gets 1,000 more queries (half of them with max_error 1%). Whatever you can answer then, you can answer without reading the log.
  7. Score the checks below and issue a verdict.

Verdicts

PASSEDEvery required check passed. Your score shows how many optional and bonus checks passed too.
FAILEDYour app ran, but at least one required check failed.
START_FAILEDThe container exited, crashed, ran out of memory, or never answered /healthz.
BUILD_FAILEDNo Dockerfile at the root, docker build failed, or a limit was exceeded.
CLONE_FAILEDWe couldn't fetch the repository (bad URL or branch, or missing token for a private repo).
ERRORA problem on our side. Resubmit, and tell an organizer if it repeats.

Scoring rubric

Total 148 points. Checks marked required must all pass for a PASSED verdict. The Model track is the hard part and is worth the most: a service that only looks answers up in the dataset cannot earn those points, so a perfect score is only reachable by another route. The sample is a window into how the whole log behaves.

CheckWhat we verifyPoints
Container
Health endpoint requiredGET /healthz returns 200 within 4 minutes of the container starting. That is your time budget for reading or indexing the dataset.4
Service
POST /fuel works requiredBefore the burst, sequential calls are checked: a valid query returns 200 and application/json with a positive numeric "fuel_l" within 15 s, a query with a missing or non-numeric feature returns 400, malformed JSON returns 400. If this fails the burst is skipped.8
Survives the burst requiredAt least 99% of the 2,000 burst requests get a well-formed answer, and the last answer is back within 3 s of the first request.10
Meets the 5% error budget requiredAt least 95% of the queries that allow 5% error are answered within 5% of the fuel of a ride that matches the query.20
Zero failed requestsNot a single request errored, timed out, was refused or came back malformed.5
Bounded tail latency99th percentile latency, measured from each request's scheduled send time, is under 2000 ms.5
Accuracy bonus
Meets the 2% error budgetAt least 95% of the queries that allow 2% error are answered within 2%.12
Tight-error bonus (1%)At least 95% of the queries that allow only 1% error are answered within 1%.10
Speed bonus
Quick start/healthz is 200 within 15 s of the container starting (needs the 5% budget met).10
FastEvery answer is back within 1.5 s of the first request (needs the 5% budget met).8
Near real timeEvery answer is back within 1.2 s of the first request, barely after the last one was sent (needs the 5% budget met).8
Snappy99th percentile latency is under 100 ms (needs the 5% budget met).8
Model track
No dataset: 5% and 2% budgetsThe judge restarts your image with no dataset at all (DATASET_PATH points at a file that does not exist) and sends 1,000 more queries. At least 95% of both the 5% and the 2% queries must still be answered within their budget.15
No dataset: 1% budgetIn the same no-dataset run, at least 95% of the queries that allow only 1% error are answered within 1%. Half of that run's queries are 1% queries, and nothing you can look up in the original data gets you there.25

5. Common mistakes

Fair play. Submissions run in a sandbox and are judged by their observable behavior. Attempts to detect or tamper with the judge, escape the sandbox or attack the portal lead to disqualification.

Submit your solution