Jev plays 2048

Each move is one Jev call. Latency and cost are real; only the animation pace is adjustable.
Moves
0
Score
0
Best tile
0
Moves/s
0
Jev latency
–
Total cost
$0
Last decision
Pace:
Stopped

How Jev is called

The whole decision, in Python. jev_move(board) takes the 4x4 board as a list of lists (0 = empty) and returns the chosen direction plus the probabilities. The page does exactly this on every move.

import os, requests

def slide(row):
    tiles = [v for v in row if v]
    out, points, i = [], 0, 0
    while i < len(tiles):
        if i + 1 < len(tiles) and tiles[i] == tiles[i + 1]:
            out.append(tiles[i] * 2); points += tiles[i] * 2; i += 2
        else:
            out.append(tiles[i]); i += 1
    return out + [0] * (4 - len(out)), points

def move(board, d):
    if d in ("up", "down"):
        cols = [list(c) for c in zip(*board)]
        new, pts = move(cols, "left" if d == "up" else "right")
        return [list(r) for r in zip(*new)], pts
    rows, pts = [], 0
    for r in board:
        s, p = slide(r if d == "left" else r[::-1])
        rows.append(s if d == "left" else s[::-1]); pts += p
    return rows, pts

def facts(board, points, empty_now):
    flat = [v for r in board for v in r]
    top = max(flat)
    in_corner = top in (board[0][0], board[0][3], board[3][0], board[3][3])
    pairs = sum(1 for r in range(4) for c in range(4) if board[r][c] and (
        (c < 3 and board[r][c] == board[r][c + 1]) or (r < 3 and board[r][c] == board[r + 1][c])))
    breaks = 0
    for line in board + [list(c) for c in zip(*board)]:
        nz = [v for v in line if v]
        steps = list(zip(nz, nz[1:]))
        breaks += len(steps) - max(sum(a <= b for a, b in steps), sum(a >= b for a, b in steps))
    return (f"points +{points}; empty cells after move {flat.count(0)} (now {empty_now}); "
            f"biggest tile {top} {'stays in a corner' if in_corner else 'NOT in a corner'}; "
            f"adjacent equal pairs ready to merge next {pairs}; "
            f"order breaks in rows/cols {breaks} (lower is better)")

def jev_move(board):
    empty_now = sum(v == 0 for r in board for v in r)
    outcomes = {}
    for d in ("up", "down", "left", "right"):
        new, pts = move(board, d)
        if new != board:                       # only legal moves become options
            outcomes[d] = facts(new, pts, empty_now)
    grid = "\n".join(" ".join(str(v or ".") for v in r) for r in board)
    state = ("2048 board (4x4, . = empty):\n" + grid +
             "\nComputed outcome of each legal move:\n" +
             "\n".join(f"{d}: {f}" for d, f in outcomes.items()))
    r = requests.post(
        "https://api.typesafe.ai/v1/systemone",
        headers={"Authorization": f"Bearer {os.environ['TYPESAFE_API_KEY']}"},
        json={
            "model": "jev-latest",
            "state": state,
            "questions": {"move": {
                "type": "choice",
                "instructions": ("Pick the 2048 move that best keeps the game alive and grows the biggest tile. "
                                 "Strongly prefer: the biggest tile staying in its corner, more empty cells, "
                                 "fewer order breaks, more pairs ready to merge. "
                                 "Points gained matter less than keeping the board healthy."),
                "criteria": {d: f"slide {d}: {f}" for d, f in outcomes.items()},
            }},
        },
    )
    answer = r.json()["answers"]["move"]
    return answer["choice"], answer["probabilities"]

Last request and response

Body sent to POST https://api.typesafe.ai/v1/systemone for the last move, and what Jev returned.

Press Play to see it.