npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

weiqi-level-bots

v3.1.2

Published

Go (Baduk/Weiqi) bots graded by playing strength — 15 measured levels from absolute beginner to low amateur dan, driving Pachi, GNU Go, AmiGo and Brown over GTP

Readme

weiqi-level-bots

Go (Baduk / Weiqi) bots graded by playing strength — 15 levels, from complete beginner to low amateur dan.

Finding an opponent that plays like a person and is genuinely weak is surprisingly hard. This package cuts playing strength into 15 rungs, measured rather than guessed: pick a level and start playing.

Install

npm install weiqi-level-bots

Requires Node.js 18 or later, plus a Go engine installed separately (see Installing an engine).

Quick start

import { runBotWithSlot } from 'weiqi-level-bots';

const res = await runBotWithSlot('lv6', {
  moves: [['B', 'D4'], ['W', 'Q16']],
  boardSize: 19
});

console.log(res.move); // 'Q4'

moves is the game record so far, an array of [colour, coordinate] pairs; coordinates use standard GTP notation (which skips the letter I). The returned move is the bot's move, and PASS means it passes.

Strength levels

15 levels, weakest first:

| Levels | Suited to | |---|---| | lv1 … lv6 | Absolute beginners who have just learned the rules | | lv7 lv8 lv9 | Novices who can finish a game | | lv10 lv11 lv12 | Improvers who know basic life and death | | lv13 lv14 lv15 | Intermediate and above |

The ordering comes from measurement, not estimation: 3,611 games over 489 handicap matchups on a 19x19 board, with the upper half measured twice to check reproducibility.

Two deliberate quirks worth knowing:

  • lv5 and lv6 overlap lv3 and lv4 in raw strength. They play their first six moves with GNU Go before handing over to a weak engine. Six moves out of ~300 barely changes the result, but it keeps the opening from looking random. For beginners that matters more than a rating gap — winning games is itself the lesson, so several weak-but-different opponents serve better than one strictly ordered ladder.
  • lv12 and lv13 sit close together. GNU Go's level slider compresses almost its whole 0~12 range into one rung; they are kept apart because they feel different to play against, not because the measurement separates them.

To build a level picker, use SLOTS — it carries the rung number and intended audience, but never the recipe behind a level:

import { SLOTS } from 'weiqi-level-bots';

SLOTS[0];                                    // { id: 'lv1', order: 1, tier: 'beginner' }
SLOTS.filter((s) => s.tier === 'beginner');  // every beginner level

Installing an engine

No engine binaries ship with this package. The 15 levels are drawn from four engines, because no single engine covers the whole range:

| Engine | Levels | Install | |---|---|---| | Pachi | 1, 2, 4, 6, 8, 10, 11, 14, 15 | build from pasky/pachi with make DCNN=0, keeping its data files beside the binary | | GNU Go | 12, 13 (and the opening of 5, 6) | brew install gnu-go / apt install gnugo | | AmiGo | 7, 9 | build from karino2/AmigoGtpFork with CFLAGS="-std=gnu89" | | Brown | 3, 5 | build Brown 1.0, adding #include <string.h> to interface.c |

Levels whose engine is missing throw an error naming it; the rest keep working.

Docker: docker/engines.Dockerfile ships with the package — after install it sits at node_modules/weiqi-level-bots/docker/engines.Dockerfile. It builds all three source-built engines into /engines, with the patches and build flags already worked out. Use it as a build stage and install GNU Go from apt in your runtime image:

FROM node:20-bookworm-slim
# Debian installs gnugo into /usr/games, which is not on PATH in slim images —
# link it, or levels 12, 13 and the openings of 5 and 6 fail with ENOENT.
RUN apt-get update && apt-get install -y --no-install-recommends gnugo \
 && ln -s /usr/games/gnugo /usr/local/bin/gnugo \
 && rm -rf /var/lib/apt/lists/*
COPY --from=engines /engines /opt/go-engines
ENV WEIQI_ENGINE_DIR=/opt/go-engines
# Fail the build if the link above is missing. Without this the image builds and passes
# a naive health check, then serves with levels 12, 13 and the openings of 5, 6 dead.
RUN gnugo --version

If the executable is not on PATH, there are two ways to point at it.

① Environment variable (easiest when several engines live in one directory):

export WEIQI_ENGINE_DIR=/opt/go-engines

② Per call:

import { runBot } from 'weiqi-level-bots';

await runBot({
  moves: [],
  boardSize: 9,
  enginePath: '/usr/local/bin/gnugo'
});

Lookup order is enginePath → WEIQI_ENGINE_DIR → PATH. When an engine is missing, the thrown error carries installation instructions.

API

| Export | Description | |---|---| | runBotWithSlot(slotId, options) | Play one move at a given slot (recommended) | | runBot(options) | Play one move with parameters of your choosing | | SLOTS | Every level as { id, order, tier }, for building a level picker | | SLOT_IDS | Every level id (lv1 … lv15) |

Commonly used options fields:

| Field | Default | Description | |---|---|---| | moves | required | The game record so far | | boardSize | 9 | Board size | | komi | 6.5 | Komi | | color | alternates | Colour to play | | timeoutMs | 30000 | Per-call timeout | | includeCandidates | false | Also return the candidate moves (topMoves) |

Ending a game

This package only produces moves. Detecting that a game has ended, and scoring it, are the caller's job.

Do not rely on the bot to pass. Weak engines pass at the wrong moments and keep playing when nothing is left — lv4 reached the move cap in 77 of 81 measured games. Treat only two passes in a row, or RESIGN, as an ending, and run your own end-of-game check past a move threshold.

Notes

  • One engine process is spawned per move. If you expose this over a network, add your own authentication, concurrency limits and rate limiting.
  • The game record you pass in is validated; an invalid coordinate or colour throws.
  • A PASS early in the game is usually an engine artefact rather than a real pass. The package already suppresses some of these, but not all.

Licence

MIT. The engines themselves carry their own licences (GNU Go is GPL); this package neither bundles nor links them, and only communicates with them as separate processes.