Skip to main content

Python client for noprune chess bot platform - easily create custom chess systems

Project description

noprune

Build your own chess system in Python with just a few lines of code.

noprune is the official Python client for the noprune.org chess bot platform — where AI chess systems compete against each other in a fair, transparent rating system.

Vision

noprune is a platform where anyone can create, deploy, and compete with their own chess AI.

  • No pruning, pure skill — Every system plays every position. No shortcuts.
  • Fair matchmaking — Systems are matched by skill level (Turing rating)
  • Open competition — Your bot competes 24/7 against others worldwide
  • Learn by doing — Start with random moves, evolve to neural networks

Whether you're a beginner learning minimax or a researcher testing cutting-edge models, noprune provides the arena.

Installation

pip install noprune

Quick Start

Create a chess bot in 10 lines:

from noprune import Bot
import random

@Bot(system_id="your-id", secret="your-secret")
def my_bot(board):
    # board is a python-chess Board object
    moves = list(board.legal_moves)
    return random.choice(moves).uci()

my_bot.run()  # Connect and start playing!

That's it. Your bot will connect to noprune.org, join the matchmaking queue, and start playing games automatically.

Getting Your Credentials

  1. Sign up at noprune.org
  2. Create a new "System" (your bot)
  3. Copy your system_id and secret

Examples

Random Bot (Simplest)

from noprune import Bot
import random

@Bot(system_id="...", secret="...")
def random_bot(board):
    return random.choice(list(board.legal_moves)).uci()

random_bot.run()

Time-Aware Bot (Using GameState)

from noprune import Bot, GameState

@Bot(system_id="...", secret="...")
def smart_bot(game: GameState):  # Note: takes GameState, not board
    # Access time information
    if game.my_time_ms < 10000:  # Less than 10 seconds
        return quick_move(game.board)

    # Access other game info
    print(f"Move {game.move_number}, playing as {game.color}")
    print(f"My time: {game.my_time_ms}ms, Opponent: {game.opponent_time_ms}ms")

    return best_move(game.board)

smart_bot.run()

Stockfish Bot (Strong)

from noprune import Bot
import chess.engine

@Bot(system_id="...", secret="...")
def stockfish_bot(board):
    with chess.engine.SimpleEngine.popen_uci("/usr/games/stockfish") as engine:
        result = engine.play(board, chess.engine.Limit(time=0.1))
        return result.move.uci()

stockfish_bot.run()

Full-Featured Bot (Class-based)

from noprune import Bot, GameState, GameResult, Action

class MyBot(Bot):
    def on_game_start(self, game: GameState):
        print(f"Game started vs {game.opponent}!")
        print(f"Playing as {game.color}, time: {game.my_time_ms}ms")

    def on_game_end(self, game: GameState, result: GameResult):
        print(f"Game over: {result.result} ({result.reason})")
        print(f"Winner: {result.winner}")
        if result.pgn:
            print(f"PGN: {result.pgn}")

    def on_move(self, game: GameState, move: str, is_my_move: bool):
        who = "I played" if is_my_move else "Opponent played"
        print(f"{who}: {move}")

    def on_draw_offer(self, game: GameState) -> bool:
        # Accept draw if low on time
        return game.my_time_ms < 5000

    def on_draw_declined(self, game: GameState):
        print("Draw offer declined!")

    def on_opponent_disconnect(self, game: GameState):
        print("Opponent disconnected, waiting...")

    def on_opponent_reconnect(self, game: GameState):
        print("Opponent reconnected!")

    def on_error(self, code: str, message: str):
        print(f"Error: {code} - {message}")

    def think(self, game: GameState) -> str | Action:
        # Your engine logic here
        return "e2e4"

bot = MyBot(system_id="...", secret="...")
bot.run()

Resign & Draw Offers

from noprune import Bot, Action

@Bot(system_id="...", secret="...")
def smart_bot(board):
    # Resign if losing badly
    if is_losing(board):
        return Action.RESIGN

    # Offer draw in equal endgame
    if is_drawn_endgame(board):
        return Action.OFFER_DRAW

    # Make a move AND offer draw
    if should_offer_draw(board):
        return Action.move_with_draw("e2e4")

    return calculate_best_move(board)

API Reference

Bot

The main class for creating chess bots.

# Decorator style (board only)
@Bot(system_id="...", secret="...", server_url="wss://noprune.org/ws")
def my_bot(board):
    return "e2e4"

# Decorator style (with game state)
@Bot(system_id="...", secret="...")
def my_bot(game: GameState):
    return "e2e4"

# Class style
class MyBot(Bot):
    def think(self, game: GameState):
        return "e2e4"

Constructor parameters:

  • system_id — Your system ID from noprune.org
  • secret — Your system secret
  • server_url — WebSocket URL (default: wss://noprune.org/ws)
  • auto_queue — Auto-join queue after each game (default: True)

Methods to override:

  • think(game: GameState) -> str | Action — Calculate your move (required)
  • on_game_start(game) — Called when game begins
  • on_game_end(game, result: GameResult) — Called when game ends
  • on_move(game, move, is_my_move) — Called after each move
  • on_draw_offer(game) -> bool — Return True to accept draw
  • on_draw_declined(game) — Your draw offer was declined
  • on_opponent_disconnect(game) — Opponent lost connection
  • on_opponent_reconnect(game) — Opponent reconnected
  • on_error(code, message) — Server error occurred

GameState

@dataclass
class GameState:
    game_id: str           # Unique game identifier
    board: chess.Board     # Current position (python-chess)
    color: str             # "white" or "black"
    opponent: str          # Opponent's name
    opponent_id: str       # Opponent's system ID
    moves: list[str]       # Move history in UCI format
    my_time_ms: int        # Your remaining time (ms)
    opponent_time_ms: int  # Opponent's remaining time (ms)

    # Properties
    is_my_turn: bool       # Is it my turn?
    move_number: int       # Current move number (1-indexed)
    ply: int               # Total half-moves played

GameResult

@dataclass
class GameResult:
    result: str   # "1-0", "0-1", "1/2-1/2"
    reason: str   # "checkmate", "timeout", "resign", etc.
    pgn: str      # Full game PGN

    # Properties
    winner: str   # "white", "black", or "draw"

Action

Action.RESIGN              # Give up
Action.OFFER_DRAW          # Propose a draw (without moving)
Action.move("e2e4")        # Make a move (same as returning "e2e4")
Action.move_with_draw("e2e4")  # Make a move AND offer draw

Environment Variables

export SYSTEM_ID="your-system-id"
export SYSTEM_SECRET="your-secret"
export SERVER_URL="wss://noprune.org/ws"  # or ws://localhost:8086/ws

python my_bot.py

Why "noprune"?

In chess programming, "pruning" means skipping moves that seem bad. Top systems like Stockfish prune aggressively — they don't even consider most legal moves.

noprune is different. It's a place where any approach is welcome:

  • Brute-force minimax? Great.
  • Neural network evaluation? Awesome.
  • Random moves? Sure, let's see how it does.

The name reminds us: there's no "wrong" way to build a chess system. Every approach teaches us something.

Links

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

noprune-0.2.0.tar.gz (9.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

noprune-0.2.0-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

Details for the file noprune-0.2.0.tar.gz.

File metadata

  • Download URL: noprune-0.2.0.tar.gz
  • Upload date:
  • Size: 9.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for noprune-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a88da4bbcc77b1542bb89e49a55defc409b3b28be7bfb4d76dc840075d8fc6c1
MD5 9bc9d17481f683d68b57cb8c860c813b
BLAKE2b-256 7e5984d738329fac57afba2c5affc0bb4a97876933791fba3e678b6615ed978f

See more details on using hashes here.

File details

Details for the file noprune-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: noprune-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 10.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for noprune-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 437ee2566e71aac7fc119eab7ba43385ec6a989b40cc73dd0510d691f17227c3
MD5 42e7bc243bee9c6027c23fe53246f4b1
BLAKE2b-256 24a455391771ca14273519e8ad61dc549e4bafae1c80e46e8c349d52e61632bd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page