PROTRAILBLAZER

Jev generate and judge · Flow
Tallyjev-latest · 1 call per room
Judged
0
Kept
0
Calls/s
0.0
$/hour
$0.00

How can a game generate hundreds of levels a minute and keep only the good ones?

Procedural generation is cheap; judging what it makes is the hard part. Here a random room generator produces candidates continuously, and each candidate is sent as state to a System One model that scores it on four separate rubrics (fairness, readability, novelty against recent rooms, fun shape) and checks a theme sentence with a yes/no question. Code combines the scores with weights you can change while it runs, keeps rooms above the bar, and you play the survivors.

Live feedgenerator idle
Generated
0
Pre-filtered
0
Judged
0
Accepted
0
Accept rate
--
Calls/s
0.0
Tokens/s
0
$/hour
$0.00
pre-filtered = unsolvable, rejected by code before any call · tap a room to inspect it
Tasteweights and bar live in code
Weights
0.35
0.15
0.20
0.30

weights sum to 1.00, so a perfect room totals 1.00

0.62

and theme_fit at least 0.60. Moving any slider re-ranks the queue at once, with no new calls.

4/s
Play the survivorsqueue 0

Accepted rooms collect in the queue below. Play them in order, best total first.

arrows or A D to move, Space or W to jumpnot playing
Accepted queue, best total first

Nothing accepted yet.

Jev inspectorsimulated · POST /v1/systemone
Simulated Jev responses A local mock. No request leaves this page.

Every solvable room becomes one request: the room as ascii, its metrics, the theme sentence, and the last 5 rooms that were kept. Five questions go with it: four scores and one yes or no check. The inspector shows the last judged room, refreshed about once a second.

Input tokens, this room--
Input price$0.042 / M tokens, output free
Spent this session$0.000000
Cost per 1,000 rooms judged--
Request not sent yet
{}
Response mock, documented shape
{}
How it worksgenerate cheaply, judge narrowly, decide in code

Why four questions beat "is this room good"

"Is this room good" bundles four different judgments into one number, and you cannot tell afterward which one sank a room. Asking four narrow questions gives each judgment its own rubric, written as situations the model can check against the room: "winnable only with pixel perfect timing" is something you can look for in the ascii, "pretty hard" is not. You also get four separate distributions back, so the inspector can show that a room lost on novelty and not on fairness, and the designer can fix the right rubric. All five questions ride in one request, so the extra questions cost a few hundred tokens of rubric text, not extra calls.

for each candidate:
    room = generate(seed)                         // cheap, local, hundreds per minute
    if not solvable(room): prefiltered += 1; continue   // code check, no call
    state = { room_ascii, metrics, theme, recent_accepted: last_5_kept }
    a = jev(state, { fairness, readability, novelty, fun_shape, theme_fit })

    norm(q)       = a[q].score / (levels(q) - 1)  // every score onto 0 to 1
    fairness_term = 1 - abs(norm(fairness) - 0.67) * 2
    total = w_fair * fairness_term + w_read * norm(readability)
          + w_novel * norm(novelty) + w_fun * norm(fun_shape)
    keep  = total >= bar and a.theme_fit.noul >= 0.6

Why normalize before weighting

The rubrics have different numbers of levels. Fairness and fun shape have four (scores from 0 to 3), readability and novelty have three (0 to 2). Add the raw scores and a four level question carries half again as much pull as a three level one before any weight touches it, so the weights stop meaning what they say. Dividing each score by its top level puts every question on 0 to 1 first. Then a weight of 0.3 on fun shape means 30 percent of the total, and with the default weights summing to 1 a perfect room totals exactly 1. If you push the weights so they sum past 1, the bar means something different, and the line under the sliders says so.

Why fairness has a sweet spot, not a maximum

The fairness rubric runs from "unwinnable" through "pixel perfect" and "fair" to "trivial". More is not better at the top: a trivial room is boring, the same way an unwinnable one is broken. So code does not reward the raw score. It measures distance from the "fair" level (2 of 3, which normalizes to 0.67) and turns that into a term that peaks at 1 for fair and falls toward 0.33 for both "pixel perfect" and "trivial". This is a design decision that lives in code, not in the model: the model only says what kind of room it sees, and the game decides what it wants.

Why code rejects unsolvable rooms first

The cheapest call is the one you never make. Whether a player can get from start to exit is a graph question, and a flood fill (jumps of up to 2 tiles up and gaps of up to 3 tiles) answers it exactly in microseconds. Sending those rooms to a model would pay for a judgment that code already has, and risk a wrong one. So the pre-filter drops them and counts them separately, and the model only sees rooms that can be finished. The fairness rubric still keeps its "unwinnable" level, because the flood fill is coarse about headroom and timing, and the model is the second check.

Throughput, cost and concurrency

At 8 candidates a second, about 5 survive the pre-filter, and each judgment takes 70 to 500 ms. One call at a time would fall behind, so the dispatcher keeps up to 6 requests in flight, the same way any network client would. Each request is about 780 input tokens once five kept rooms ride along; those five rooms are close to half of it, because novelty cannot be judged without them. At $0.042 per million input tokens with output free, a thousand judged rooms cost about three cents, and an hour at top speed stays well under a dollar. One side effect of concurrency is visible in the novelty scores: rooms in flight at the same moment were compared against the same five recent rooms, so two similar rooms sent together can both look new.

What this unlocks

Infinite content stops meaning infinite noise. The designer's taste lives in two places a non-programmer can edit: the rubric text (what "a memorable set piece" looks like) and the weights and bar (how much each judgment matters). Both can change at runtime with no retraining and no new code, and because the judgments are cached on each room, moving a weight re-ranks everything already judged without a single new call. The theme sentence goes one step further and hands part of the taste to the player: type "enemy gauntlet" and the same generator starts feeding you a different game.

About this demo: every answer comes from a local mock, and nothing on this page calls Jev, which is in early access. The mock reads the same state the request carries. Fairness comes from the flood fill's hardest moves (jumps at the reach limit, jumps over pits) and from spikes and enemies next to the route. Readability comes from how full the room is and how scattered its platforms are. Novelty is a hardcoded distance over the metrics and the tile layout against the five recent rooms; about one candidate in seven is a remix of a recent kept room, so you can watch novelty catch near copies. Fun shape counts how often risk tiles and reward tiles alternate along the solved route. Theme fit understands only the word list shown under the theme sentence, which is a limit of the mock, not of the idea: a real model reads the whole sentence. Each score becomes a distribution over levels by a softmax with jitter; the score is the weighted mean of level indices; confidence is 1 minus normalized entropy, an approximation of TypeSafe's unpublished formula. Latency is drawn uniformly from 70 to 500 ms, and token counts are the JSON length divided by 4.