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

timetabling-solver

v0.5.0

Published

A library which exposes a genetic algorithm to solve (one of) the [timetabling problem](http://web.csulb.edu/~obenli/DSS/node2.html).

Readme

timetabling-solver

A library which exposes a genetic algorithm to solve (one of) the timetabling problem.

TL;DR: I want to assign things to time slots and minimize by the number of collisions between things

Problem

Given:

  • a list of time slots
  • a list of bookables that can be assigned to a specific time slot
  • a list of constraints in the form of:
    • a list of bookables which shouldn't overlap

Find the optimal assignment for bookables in time slots in order to minimize the number of collisions between bookables

Description

The genetic algorithm presented:

  • Generates a population of random timetables
  • Evaluates the fitness of the population
  • Applies mutation or crossover to the population to obtain another population

Crossover generates two timetables starting from two timetables. The parents are selected using genetic-js fittest and tournament2 algorithms. The crossover is a classic 2 points crossover:

  • A randomly sorted list of bookables get divided in 2 points
  • The bookables in the first and last segments will use the time slot selected in the first parent
  • The bookables in the middle semgent will use the time slot selected in the second parent

Mutation is specifically tuned to mutate a random collisions with a random element. The idea behind targeting a random collision and not a random element is that we want to target the problematic areas. Following this approaches increased performances greatly and reduced the number of cases in which the algorithm was getting stuck repeatedly on populations with very low number of collisions.

Basic Example

const solve = require('timetabling-solver').minCollisions

const slots = ['8:00', '10:00', '12:00']
const bookables = ['Tennis', 'Climbing', 'Gymnastic', 'Trapeze', 'Handstands']
const constraints = [ //list of non overlapping constraints
  ['Tennis', 'Climbing'],
  ['Handstands', 'Climbing'],
  ['Trapeze', 'Tennis'],
  ['Tennis', 'Handstands', 'Trapeze']
]

solve({ slots, bookables, constraints }, {}, (table, fitness) => console.log(table, fitness))

{ '10:00': [ 'Trapeze', 'Climbing' ],
  '12:00': [ 'Handstands', 'Gymnastic' ],
  '8:00': [ 'Tennis' ] } 0

Custom Constraints

From version 0.3.2 you can add custom constraints to your problems.

Instead of adding a list of colliding entries, you need to provide a function.

A custom constraint is a function which accept a timetable and returns a list of collisions:

  • Returning an empty array means there are no collisions according to your constraint.
  • Returning an array with values from your bookables means they're colliding
const solve = require('timetabling-solver').minCollisions

const slots = ['8:00', '10:00']
const bookables = ['Tennis', 'Climbing']
const constraints = [ //list of non overlapping constraints
  ['Tennis', 'Climbing'],
  (timetable) => timetable['8:00'].indexOf('Climbing') !== -1 ? ['Climbing'] : []
  //Climbing can't be at 8:00
]

solve({ slots, bookables, constraints }, {}, (table, fitness) => console.log(table, fitness))

API

require('timetabling-solver').minCollisions(data, config, callback, partialCallback)
  data = {              // Object which represents the specification of the problem
    slots               // List of time slots 
    bookables           // List of bookables that can be assigned to a specific time slot
    constraints: [      // List of constraints
      constraint        // List of bookables which shouldn't overlap
    ]  
    softFitness? = (timetable) // Optional, prioritizes between two equally fit solutions (smaller number -> more preferred)
  }
  config = {            // Object which contains extra configuration    
    iterations: 1000,   // Maximum number of generations before stopping the algorithm
    size: 250,          // Size of the population
    crossover: 0.3,     // Probability of crossover happening
    mutation: 0.8,      // Probability of a mutation happening
    skip: 20,           // Numnber of generations to skip before calling partialCallback
    debug?: false,      // provide additional data to callbacks, for easier debugging
    ...more: check out genetic-js documentation
  }
  callback = (timetable, meta) //Function called at the end of the computation
  partialCallback = (timetable, meta) //Function called after the number of generations set in skip
    timetable =         //Fittest timetable 
    meta = { 
      fitness,          //Fitness number which represent the number of collisions
      generation,       //Number of generation of elapsed
      stats,            //Stats as per genetic-js
      pop,              //Current population
      popTables,        //Same as pop, but each entity is converted into proper timetable. Available ONLY if config.debug is set
      collisions        //Collisions in the fittest timetable
    }

Examples

Check out the examples (the advanced one in particular).

Contributions

I love small and big contributions. Whether you want to solve another entire timetabling problem or whether you found a default configuration which yields better results in a demonstrable way - I'm always happy to review PRs and discuss about the project.

Unfortunately I benefited from this code a long time ago - aka I'm not using it actively and don't have too much time to work on it.

Contributors

Credits