Eval Explained

Eval Explained

Eval Explained

Eval Explained: Lichess Cloud Eval API with FEN and Python eval()

A senior developer's practical guide to the word "eval" in two worlds: chess engines and Python

Introduction

I have lost count of how many times a junior developer has asked me, "What does eval actually do?" The funny part is that the answer depends on who is asking. A chess player sees eval as the number next to the board. A Python developer sees python eval as a built-in that runs a string as code. Both are useful. Both can bite you if you treat them casually.

Eval Explained


In this guide I'll walk through both meanings the way I'd explain them to my own team: a working example of the lichess cloud eval api fen call, how to read the response, and when python eval is the wrong tool. Every snippet below is something you can paste and run today.

Quick answer: Send a FEN string to https://lichess.org/api/cloud-eval, read pvs[0].cp for the score in centipawns, and never pass untrusted text into Python's eval().

What Does "Eval" Mean?

1. Eval in chess engines

In chess software, eval is the engine's numeric judgement of a position. It is usually shown in centipawns (1 pawn = 100 centipawns). A score of +120 means White is roughly 1.2 pawns better. A negative number favours Black. When a forced checkmate exists, engines report "mate in N" instead of a pawn score.

2. Eval in Python

The built-in eval() takes a string, parses it as a Python expression, runs it, and returns the result. Powerful, tiny, and risky. We'll cover that after the chess part.

Visual: how to read an eval bar

Black
-1.0
0.0
+1.0 White

Left = Black advantage • Middle = equal • Right = White advantage. Values are centipawns divided by 100.

Lichess Cloud Eval API with a FEN

Lichess keeps a shared database of positions already analysed by strong engines. The endpoint returns a stored evaluation instantly, so you do not need to install or run Stockfish yourself. It needs no API token for basic use.

The request

You send a FEN (Forsyth-Edwards Notation, the text format that describes a chess position) as a query parameter:

GET https://lichess.org/api/cloud-eval
    ?fen=rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1
    &multiPv=1

Remember to URL-encode the FEN. Spaces must become %20 or +. The Python requests library does that for you when you pass params.

Python example

import requests

API = "https://lichess.org/api/cloud-eval"

def cloud_eval(fen, multi_pv=1):
    resp = requests.get(
        API,
        params={"fen": fen, "multiPv": multi_pv},
        timeout=10,
    )
    if resp.status_code == 404:
        return None          # position not in the cloud cache
    resp.raise_for_status()  # catches 429 rate limits, 5xx, etc.
    return resp.json()

fen = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
data = cloud_eval(fen)

if data:
    best = data["pvs"][0]
    print("Depth:", data["depth"])
    print("Best line:", best["moves"])
    print("Score (cp):", best.get("cp"))
else:
    print("No cloud eval for this position")

The response

A typical reply looks like this (numbers will differ by position):

{
  "fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
  "knodes": 106325,
  "depth": 35,
  "pvs": [
    { "moves": "e7e5 g1f3 b8c6 ...", "cp": -18 }
  ]
}
FieldMeaning
depthHow deep the engine searched
knodesThousands of positions searched
pvs[].movesBest line in UCI moves
pvs[].cpCentipawn score (White's point of view)
pvs[].mateMoves to mate, present instead of cp when forced

Visual: request flow

Your FEN → Python requests → Lichess cloud-eval → JSON cp / mate

Turn centipawns into a win percentage

Raw centipawns are hard for readers to feel. Lichess uses a logistic curve to convert them into a win chance, and you can reuse the same idea:

import math

def win_percent(cp):
    return 50 + 50 * (2 / (1 + math.exp(-0.00368208 * cp)) - 1)

print(round(win_percent(100), 1))   # about 59.1 for White
print(round(win_percent(-300), 1))  # about 24.9 for White

Things that will trip you up

  • 404 Not Found: the position simply is not in the cloud database. Rare or deep-in-game positions often miss. Fall back to a local engine if you need an answer every time.
  • 429 Too Many Requests: you are being rate limited. Slow down, send one request at a time, and cache results.
  • Point of view: read the sign carefully. Check your own results against a known position before you trust a pipeline.
  • Variants: pass variant if you are not analysing standard chess.

Python eval(): Useful, but Handle with Care

Now the other meaning. python eval evaluates a string as an expression:

print(eval("2 + 3 * 4"))   # 14
x = 10
print(eval("x * 2"))       # 20

It looks harmless until the string comes from a user. In code review, this is one of the first things I flag:

# DANGEROUS: never do this with untrusted input
user_text = "__import__('os').system('echo you have been hacked')"
eval(user_text)

That one line can run any command your process is allowed to run. A restricted globals dictionary does not make it safe either, because Python's internals offer many ways around it.

Safer alternatives

import ast, json

ast.literal_eval("[1, 2, {'a': 3}]")   # only literals, no function calls
json.loads('{"cp": -18}')              # best choice for API data

Notice the connection to our chess example: the Lichess reply is JSON, so use resp.json(). Reaching for eval() to parse an API response is a classic beginner mistake.

Visual: which tool to pick

Your dataUseRisk
API / JSON textjson.loads / resp.json()Low
Python literal stringast.literal_evalLow-Medium
Untrusted expressioneval()High, avoid

Best Practices Checklist

  • Always set a timeout on network calls.
  • Cache each FEN result locally so you never ask for the same position twice.
  • Handle cp and mate separately, since only one appears per line.
  • Treat eval() as a last resort, never as a parser.
  • Log the FEN with every error, because it makes debugging far quicker.

Frequently Asked Questions (FAQ)

What is the Lichess cloud eval API?

It is a free endpoint that returns stored engine evaluations for chess positions you describe with a FEN, so you can get analysis without running an engine locally.

Why does the cloud eval API return 404?

The position has not been analysed and stored yet. This is normal for unusual positions. Use a local engine such as Stockfish as a fallback.

Do I need an API key for the Lichess cloud eval API?

No token is needed for this public endpoint, but you should respect rate limits and keep requests sequential.

What does a positive eval mean?

A positive centipawn value means White is better. A negative value means Black is better. Divide by 100 to get an approximate pawn advantage.

Is Python eval() safe?

Only with fully trusted input. For anything coming from users, files, or the network, use json.loads or ast.literal_eval instead.

What is a FEN string?

A single line of text that describes a chess position: piece placement, side to move, castling rights, en passant square, and move counters.

Conclusion

Whether you are building a chess analysis tool or tidying up a Python codebase, eval deserves respect. The lichess cloud eval api fen workflow gives you fast, free engine insight in about fifteen lines of code. And python eval is best kept for trusted, throwaway situations, with json and ast.literal_eval doing the real work.

Try the snippets with your own FEN strings, add a small cache, and you will have the core of a position analyser by tonight. If this guide saved you time, share it with a fellow developer, and drop your questions in the comments.

Read more :


AngularThink
Written by AngularThink Team
Full-Stack & AI engineering insights, tutorials and best practices.

0 Comments

Post a Comment