26
loading...
This website collects cookies to deliver better user experience
analyze_position
that takes in a chess position and outputs a detailed analysis.$ mkdir chess-api
$ python3 -m venv venv
$ source venv/bin/activate
(venv) $ pip install python-chess
(venv) $ pip install Flask
# app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
curl
# flask run automatically looks for app.py
(venv) $ flask run
# different terminal, flasks default port is 5000
$ curl localhost:5000
<p>Hello, World!</p>
# app.py
@app.route("/json")
def hello_world_json():
return {"hello": "world"}
$ curl -v localhost:5000/json
...
< HTTP/1.0 200 OK
< Content-Type: application/json
< Content-Length: 18
...
{"hello":"world"}
...
# A FEN is a standard way of representing a chess board
# Make sure that both the FEN string is valid,
# and the resulting board is valid
def is_valid_fen(field, value, error):
try:
board = chess.Board(fen=value)
if not board.is_valid():
error(field, "Invalid FEN")
except ValueError:
error(field, "Invalid FEN")
SCHEMA = {
"fen": {"type": "string", "required": True, "check_with": is_valid_fen},
"num_moves_to_return": {"type": "integer", "min": 1, "max": 10, "default": 1},
"time_limit": {"type": "number", "min": 0.1, "max": 3600, "default": 60},
"depth_limt": {"type": "integer", "min": 5, "max": 50}
}
fen
field, we want to make sure it's both a string and a valid FEN, so we need to define a custom validation function.v = Validator(SCHEMA)
is_valid = v.validate(some_json_request)
v.errors
contains exactly what's wrong with it. v.normalized(some_json_request)
modifies it's input which we will use to fill in defaults for the optional fields.parser.py
from cerberus import Validator
from flask import abort, jsonify, make_response
# ... schema from above
def parse_request(json_body):
v = Validator(SCHEMA)
# If invalid, exit out with a 400
if not v.validate(json_body):
# abort exits early with an HTTP response
abort(make_response(jsonify(v.errors), 400))
# Fill in defaults
return v.normalized(json_body)
app.py
. For now, let's return the parsed request.from parser import parse_request
# ...
@app.route("/analyze", methods=["POST"])
def analyze():
return parse_request(request.get_json())
$ curl -X POST
-H "Content-Type: application/json"
-d '{"fen": "8/8/6P1/4R3/8/6k1/2r5/6K1 b - - 0 1"}' localhost:5000/analyze
{"fen":"8/8/6P1/4R3/8/6k1/2r5/6K1 b - - 0 1","num_moves_to_return":1,"time_limit":60}
$ curl -v -X POST
-H "Content-Type: application/json"
-d '{"fen": "nonsense"}' localhost:5000/analyze
...
< HTTP/1.0 400 BAD REQUEST
...
{"fen": ["Invalid FEN"]}
analyze_position
from the last post... what if we just use that directly in our route?@app.route("/analyze", methods=["POST"])
def analyze():
parsed_request = parse_request(request.get_json())
analysis = analyze_position(fen=parsed_request.get("fen"),
num_moves_to_return=parsed_request.get("num_moves_to_return"),
depth_limit=parsed_request.get("depth_limit"),
time_limit=parsed_request.get("time_limit"))
return {"analysis": analysis}
$ curl -X POST
-H "Content-Type: application/json"
-d '{"fen": "8/8/6P1/4R3/8/6k1/2r5/6K1 b - - 0 1"}' localhost:5000/analyze
{"analysis":[{"centipawn_score":null,"mate_score":-2,"pv":["c2c1","e5e1","c1e1"]}]}