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 🙏

© 2025 – Pkg Stats / Ryan Hefner

preferans-rating-js

v2.4.5

Published

nodejs rating for preferans

Readme

preferans-rating-js

build status codacy coverage dependencies npm Greenkeeper badge

A TypeScript/JavaScript library for calculating player rating changes in Preferans card games. This library implements a sophisticated rating algorithm that considers player performance, existing ratings, and game-specific parameters (bula) to determine fair rating adjustments after each game.

Documentation

TypeDoc documentation

Bula vs Final Score

It's important to understand the distinction between "bula" and "final score" in Preferans:

Bula:

  • Starting Bula: All players begin with an identical bula value, determining the length of the game.
  • Game End: The game finishes when the combined bulas of all three players equal zero.
  • Bula Tracking: Each player tracks their bula progression in the middle column of their scorecard.

Final Score Calculation: The final score used in rating calculations is much more complex than just the final bula. It involves:

  • Middle Column: Your bula progression (can be positive-bad or negative-good)
  • Side Columns: Points from tricks taken when other players were leading (multiplied by -10)
  • Opponent Deductions: Subtracting what opponents documented taking from you in games where you were leading

The score parameter in this library represents the final calculated score after all these complex scoring rules are applied, not the raw bula value.

Usage

const PrefRating = require("preferans-rating-js");

// Example: Three players with different ratings and final calculated scores
const player1 = {
    username: 'Pedja',
    rating: 1200,    // Current rating before the game
    score: -500      // Final calculated score (negative = good performance)
};

const player2 = {
    username: 'Marko', 
    rating: 1150,
    score: 40        // Final calculated score (positive = poor performance)
};

const player3 = {
    username: 'Johnny',
    rating: 1100,
    score: 460       // Final calculated score (positive = poor performance)
};

// Note: In Preferans scoring, final scores typically sum to zero: -500 + 40 + 460 = 0
const startingBula = 30; // The initial bula value for all players

// Calculate rating changes
const rating = new PrefRating(player1, player2, player3, startingBula);

// Get the results
const result = rating.rating;
console.log(result);
/*
Output:
{
  bula: 30,
  p1: { username: 'Pedja', score: -500, oldRating: 1200, rating: 1173, change: -27 },
  p2: { username: 'Marko', score: 40, oldRating: 1150, rating: 1152, change: 2 },
  p3: { username: 'Johnny', score: 460, oldRating: 1100, rating: 1125, change: 25 }
}
*/

API

Constructor

new PrefRating(player1, player2, player3, bula)

Parameters:

  • player1, player2, player3: Objects with username (string), rating (number), and score (number - final calculated score)
  • bula: Number representing the starting bula value for all players

Returns: PrefRating instance

Properties

  • rating: Returns an object containing the bula and updated player information including rating changes

Detailed Example

To grasp the complex scoring system, consider this detailed example with a starting bula of 30:

Players: Pedja, Marko, Johnny

  • Order: Pedja, Marko, Johnny

Final Papers:

  • Pedja:
    • Left (Johnny) = 320
    • Middle (Bula) = -20
    • Right (Marko) = 160
  • Marko:
    • Left (Pedja) = 120
    • Middle (Bula) = 4
    • Right (Johnny) = 60
  • Johnny:
    • Left (Marko) = 20
    • Middle (Bula) = 16
    • Right (Pedja) = 60

Calculations:

  • Pedja: -20 x 10 - 320 - 160 + 120 + 60 = -500
  • Marko: 4 x 10 - 120 - 60 + 160 + 20 = 40
  • Johnny: 16 x 10 - 20 - 60 + 320 + 60 = 460

Result:

  • Pedja: Good 500
  • Marko: Bad 40
  • Johnny: Bad 460

Explanation:

  • The formula: Middle (Bula) x 10 - Left - Right + Points Opponents Took
  • Negative scores are considered "good".

Rating Algorithm

The rating system is inspired by chess ELO but adapted for 3-player games. The algorithm considers three main factors:

Step 1: Calculate D Values (Score Performance)

D₁₂ = (Player1.score - Player2.score) / bula
D₁₃ = (Player1.score - Player3.score) / bula  
D₂₃ = (Player2.score - Player3.score) / bula

This measures actual performance differences, normalized by game length (bula).

Step 2: Calculate T Values (Rating Expectations)

T₁₂ = (Player2.rating - Player1.rating) × 2.8 / 100
T₁₃ = (Player3.rating - Player1.rating) × 2.8 / 100
T₂₃ = (Player3.rating - Player2.rating) × 2.8 / 100

This represents expected performance based on rating differences. The constant 2.8 is the "magic number" that scales rating differences.

Step 3: Calculate N Values (Performance Bonus)

N₁₂ = (Player1.score < Player2.score) ? -bula/100 : +bula/100
N₁₃ = (Player1.score < Player3.score) ? -bula/100 : +bula/100
N₂₃ = (Player2.score < Player3.score) ? -bula/100 : +bula/100

This gives a small bonus/penalty based on who performed better (remember: lower score = better in Preferans).

Step 4: Combine and Average

C₁₂ = D₁₂ + T₁₂ + N₁₂
C₁₃ = D₁₃ + T₁₃ + N₁₃  
C₂₃ = D₂₃ + T₂₃ + N₂₃

Player1.change = round((C₁₂ + C₁₃) / 2)
Player2.change = round((-C₁₂ + C₂₃) / 2)
Player3.change = round((-C₁₃ + -C₂₃) / 2)

Example Calculation (Pedja/Marko/Johnny):

  • D Values: -18, -32, -14 (Pedja dominated with -500 score)
  • T Values: -1.4, -2.8, -1.4 (Pedja had highest rating, so negative expectations)
  • N Values: -0.3, -0.3, -0.3 (All performed as expected relative to each other)
  • Final Changes: Pedja: -27, Marko: +2, Johnny: +25

Key Insight: Pedja had the best performance (-500 score) but lost rating points because his high initial rating (1200) created high expectations that even his excellent performance couldn't fully meet.

Differences from Chess ELO:

  1. 3 Players: Each player's change considers performance against both opponents
  2. No Draws: Scores are always different (theoretically ties possible but extremely rare)
  3. Base Rating: Uses 1000 instead of chess's 1200
  4. Score Normalization: Performance scaled by bula (game length)

Installation

npm install preferans-rating-js

Requirements

  • Node.js ≥ 16.0.0
  • TypeScript support included