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

@tungstenstudio/darts-handicap-calculator

v1.0.0

Published

Handicap calculator for x01 and cricket darts matches

Downloads

213

Readme

@tungstenstudio/darts-handicap-calculator

Handicap calculator for x01 and cricket darts matches. Uses pure ratio methods — no lookup tables, flat offsets, or tiered brackets.

Live Demo

Install

npm i @tungstenstudio/darts-handicap-calculator
# or
pnpm add @tungstenstudio/darts-handicap-calculator
# or
yarn add @tungstenstudio/darts-handicap-calculator
# or
bun add @tungstenstudio/darts-handicap-calculator

X01 Handicap

Scores are adjusted proportionally by the ratio of player averages (PPD), so both players need roughly the same number of darts to finish.

Formula: adjustedScore = baseScore × (weakerPPD ÷ strongerPPD), rounded to the nearest integer.

Quick Start

import { calculateX01Handicap } from '@tungstenstudio/darts-handicap-calculator';

const result = calculateX01Handicap({
  baseScore: 501,
  direction: 'negative',
  averageType: 'ppd',
  playerA: { value: 22, name: 'Alice' },
  playerB: { value: 16, name: 'Bob' },
});

result.playerA.startScore;      // 501  (stronger player keeps base)
result.playerB.startScore;      // 364  (weaker player starts lower)
result.handicapPoints;           // 137
result.playerA.estimatedDarts;   // 23
result.playerB.estimatedDarts;   // 23
result.playerA.name;             // 'Alice'
result.capped;                   // false

With a Handicap Cap

const result = calculateX01Handicap({
  baseScore: 501,
  direction: 'negative',
  averageType: 'ppd',
  playerA: { value: 20 },
  playerB: { value: 10 },
  maxHandicap: 150,
});

result.playerA.startScore;  // 501
result.playerB.startScore;  // 351  (capped — would be 251 without cap)
result.handicapPoints;       // 150
result.capped;               // true

With Score Rounding

const result = calculateX01Handicap({
  baseScore: 501,
  direction: 'negative',
  averageType: 'ppd',
  playerA: { value: 17 },
  playerB: { value: 12 },
  roundTo: 25,
});

result.playerB.startScore;  // 350  (354 rounded to nearest 25)
result.rounded;              // true

With Handicap Percentage

const result = calculateX01Handicap({
  baseScore: 501,
  direction: 'negative',
  averageType: 'ppd',
  playerA: { value: 20 },
  playerB: { value: 10 },
  handicapPercentage: 75,
});

result.playerB.startScore;  // 313  (75% of 250-point gap applied)
result.handicapPoints;       // 188
result.compressed;           // true

API

calculateX01Handicap(input: X01HandicapInput): X01HandicapResult

Calculates handicapped start scores for two players.

interface X01HandicapInput {
  baseScore: number;        // The base game score (e.g. 301, 501, 701)
  direction: Direction;     // 'negative' | 'positive'
  averageType: AverageType; // 'ppd' | '3da'
  playerA: PlayerAverage;
  playerB: PlayerAverage;
  maxHandicap?: number;     // Optional cap on the handicap spread
  roundTo?: number;         // Round adjusted score to nearest N (e.g. 5, 10, 25)
  handicapPercentage?: number; // Apply only N% of the handicap spread (1–100, default: 100)
}

interface PlayerAverage {
  value: number;            // The player's average
  name?: string;            // Optional player name (passed through to result)
}

Returns:

interface X01HandicapResult {
  baseScore: number;
  direction: Direction;
  handicapPoints: number;   // Absolute difference between start scores
  capped: boolean;          // Whether maxHandicap was applied
  rounded: boolean;         // Whether roundTo changed the score
  compressed: boolean;      // Whether handicapPercentage reduced the spread
  playerA: PlayerResult;
  playerB: PlayerResult;
}

interface PlayerResult {
  startScore: number;       // The handicapped start score
  ppd: number;              // Points per dart
  threeDartAverage: number; // 3-dart average
  estimatedDarts: number;   // Estimated darts to finish (ceil(startScore / ppd))
  name?: string;            // Player name (if provided in input)
}

Direction

The direction parameter controls which player's score is adjusted:

| Direction | Stronger player | Weaker player | |-----------|----------------|---------------| | 'negative' | Keeps the base score | Starts lower (reduced proportionally) | | 'positive' | Starts higher (raised proportionally) | Keeps the base score |

Both directions produce the same relative handicap. Choose based on your league's convention:

  • Negative is the most common — it keeps the strong player at the standard game score.
  • Positive is useful when you want all players to start at at least the base score.

Cricket Handicap

The weaker player starts with "spot marks" — free marks toward closing numbers — so both players need roughly the same number of rounds to close all seven numbers.

Formula: spotMarks = 21 × (1 − weakerAvg ÷ strongerAvg), rounded to the nearest integer. The total of 21 represents 3 marks × 7 cricket numbers.

Quick Start

import { calculateCricketHandicap } from '@tungstenstudio/darts-handicap-calculator';

const result = calculateCricketHandicap({
  averageType: 'mpr',
  playerA: { value: 3.0, name: 'Alice' },
  playerB: { value: 2.0, name: 'Bob' },
});

result.spotMarks;                  // 7
result.playerA.startingMarks;      // 0   (stronger player starts at 0)
result.playerB.startingMarks;      // 7   (weaker player gets spot marks)
result.playerA.estimatedRounds;    // 7
result.playerB.estimatedRounds;    // 7

With a Handicap Cap

const result = calculateCricketHandicap({
  averageType: 'mpr',
  playerA: { value: 4.0 },
  playerB: { value: 1.0 },
  maxHandicap: 10,
});

result.spotMarks;  // 10  (capped — would be 16 without cap)
result.capped;     // true

With Rounding Mode

const result = calculateCricketHandicap({
  averageType: 'mpr',
  playerA: { value: 2.5 },
  playerB: { value: 2.0 },
  rounding: 'ceil',   // 'round' (default) | 'ceil' | 'floor'
});

result.spotMarks;  // 5  (4.2 ceiled — would be 4 with default rounding)

With Handicap Percentage

const result = calculateCricketHandicap({
  averageType: 'mpr',
  playerA: { value: 3.0 },
  playerB: { value: 2.0 },
  handicapPercentage: 50,
});

result.spotMarks;   // 4  (50% of 7 spot marks)
result.compressed;  // true

API

calculateCricketHandicap(input: CricketHandicapInput): CricketHandicapResult

Calculates spot marks for two cricket players.

interface CricketHandicapInput {
  averageType: CricketAverageType; // 'mpr' | 'ppd'
  playerA: PlayerAverage;
  playerB: PlayerAverage;
  maxHandicap?: number;            // Optional cap on spot marks
  rounding?: CricketRounding;      // 'round' (default) | 'ceil' | 'floor'
  handicapPercentage?: number;     // Apply only N% of the spot marks (1–100, default: 100)
}

Returns:

interface CricketHandicapResult {
  spotMarks: number;               // Marks spotted to the weaker player
  capped: boolean;                 // Whether maxHandicap was applied
  rounding: CricketRounding;       // The rounding mode used
  compressed: boolean;             // Whether handicapPercentage reduced the spread
  playerA: CricketPlayerResult;
  playerB: CricketPlayerResult;
}

interface CricketPlayerResult {
  startingMarks: number;           // 0 for stronger player, spotMarks for weaker
  marksNeeded: number;             // 21 − startingMarks
  average: number;                 // The player's average
  averageType: CricketAverageType; // 'mpr' | 'ppd'
  estimatedRounds: number;         // ceil(marksNeeded / average)
  name?: string;                   // Player name (if provided in input)
}

Utility Functions

ppdTo3DA(ppd: number): number

Converts points-per-dart to 3-dart average.

ppdTo3DA(20); // 60

threeDAToPPD(tda: number): number

Converts 3-dart average to points-per-dart.

threeDAToPPD(60); // 20

Error Handling

The calculator throws HandicapError (extends Error) with a code property for invalid inputs:

import { calculateX01Handicap, HandicapError } from '@tungstenstudio/darts-handicap-calculator';

try {
  calculateX01Handicap(input);
} catch (err) {
  if (err instanceof HandicapError) {
    console.error(err.code);    // e.g. 'INVALID_PLAYER_A_AVERAGE'
    console.error(err.message); // Human-readable description
  }
}

| Error Code | Cause | |------------|-------| | INVALID_BASE_SCORE | baseScore is not a finite positive number | | INVALID_PLAYER_A_AVERAGE | Player A's average is not a finite positive number | | INVALID_PLAYER_B_AVERAGE | Player B's average is not a finite positive number | | INVALID_MAX_HANDICAP | maxHandicap is provided but not a finite positive number | | INVALID_ROUND_TO | roundTo is provided but not a finite positive integer | | INVALID_HANDICAP_PERCENTAGE | handicapPercentage is provided but not a finite number between 1 and 100 | | INVALID_ROUNDING | rounding (cricket) is provided but not 'round', 'ceil', or 'floor' |

Types

All TypeScript types are exported for convenience:

import type {
  Direction,
  AverageType,
  PlayerAverage,
  PlayerResult,
  X01HandicapInput,
  X01HandicapResult,
  CricketAverageType,
  CricketRounding,
  CricketHandicapInput,
  CricketPlayerResult,
  CricketHandicapResult,
  HandicapErrorCode,
} from '@tungstenstudio/darts-handicap-calculator';

License

ISC