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 🙏

© 2024 – Pkg Stats / Ryan Hefner

batsat

v0.0.2

Published

A CatSAT implementation for the browser 🦇

Downloads

6

Readme

BatSAT 🦇

Coverage Status NPM Module

BatSAT is a declarative language embedded in JavaScript intended for procedural content generation. It aims to reimplement Ian Douglas Horswill's CatSAT library, which implements similar functionality in C#.

BatSAT is currently GPL licensed, but if that licensing ends up being an obstacle for you, let me know!

Documentation

A solver is created by creating a new Problem. Constraints and rules must be written in terms of previously-defined predicates.

Here's an example script that will assign species and homes for four characters:

import { Problem } from 'batsat';

const p = new Problem();
const cast = ['celeste', 'nimbus', 'luna', 'terra'];
const species = ['cat', 'dog'];
const home = ['uplands', 'lowlands', 'catlands', 'doghouse'];

p.predicate('species', [cast, species]);
p.predicate('home', [cast, home]);

for (const c of cast) {
  p.unique(species.map((s) => `species ${c} ${s}`));
  p.unique(home.map((h) => `home ${c} ${h}`));

  // Only dogs live in the doghouse (but dogs can live elsewhere)
  p.implies([`home ${c} doghouse`], `species ${c} dog`);
}

// Luna and terra must live in different places
for (const h of home) {
  p.inconsistent(`home luna ${h}`, `home terra ${h}`);
}

// If Celeste is a cat, she must live in the catlands
p.equal([`species celeste cat`], [`home celeste catlands`]);

// There need to be 1 or 2 uplanders
p.quantify(
  1,
  2,
  cast.map((c) => `home ${c} uplands`),
);

// There's only room for one in the doghouse
p.atMost(
  1,
  cast.map((c) => `home ${c} doghouse`),
);

const s = p.solve();
s.trueAttributes; // Something like ["species celeste cat", ...]
s.lookup['species celeste cat']; // Maybe true, maybe false
s.lookup['species celeste cat'] === s.lookup['home celeste catlands']; // Definitely true

:factory: Problem

Problems are the primary engine of BatSAT. You use a problem to declare attributes, attach constraints, and generate solutions.

Methods

:gear: quantify

Require that some number of arguments be satisfied

| Method | Type | | ---------- | ------------------------------------------------------------ | | quantify | (min: number, max: number, propositions: string[]) => void |

Parameters:

  • min: Minimum number of arguments that must be satisfied (inclusive)
  • max: Maximum number of arguments that must be satisfied (inclusive)

:gear: exactly

Require that exactly a given number of the arguments be satisfied

p.exactly(n, [a, b, c...]) is equivalent to p.quantify(n, n, [a, b, c...])

| Method | Type | | --------- | --------------------------------------------- | | exactly | (n: number, propositions: string[]) => void |

Parameters:

  • n: The number of arguments from the list that must be satisfied

:gear: all

Require that all arguments be satisfied

p.all([a, b, c]) is equivalent to p.quantify(3, 3, [a, b, c]) or p.exactly(3, [a, b, c])

| Method | Type | | ------ | ---------------------------------- | | all | (propositions: string[]) => void |

:gear: atLeast

Require that some non-zero number of arguments be satisfied

p.atLeast(n, [a, b, c, d]) is equivalent to p.quantify(n, 4, [a, b, c, d])

| Method | Type | | --------- | ----------------------------------------------- | | atLeast | (min: number, propositions: string[]) => void |

Parameters:

  • min: The minimum number of arguments that must be satisfied (inclusive)

:gear: atMost

Require that at most some number of arguments be satisfied

p.atMost(n, [a, b, c]) is equivalent to p.quantify(0, n, [a, b, c])

| Method | Type | | -------- | ----------------------------------------------- | | atMost | (max: number, propositions: string[]) => void |

Parameters:

  • max: The maximum number of arguments that must be satisfied (inclusive)

:gear: unique

Require that exactly one of the arguments be satisfied

p.unique(a, b, c...) is equivlanet to p.exactly(1, a, b, c...)

| Method | Type | | -------- | ---------------------------------- | | unique | (propositions: string[]) => void |

:gear: inconsistent

Require that two propositions not be simultaneously satisfied

| Method | Type | | -------------- | -------------------------------- | | inconsistent | (a: string, b: string) => void |

:gear: implies

Indicates that the premise or premises imply the conclusion: if all premises are satisfied, the conclusion must be satisfied.

An array of premises is treated as conjunction: p.implies([a, b, c], d) logically means (a /\ b /\ c) -> d.

Leaves open the possibility that the conclusion may be satisfied even if some premises are unsatisfied. If that's not what you want --- if you only want d to be satisfied if there's some rule that gives a reason for it to be satisfied, you want to use rule() instead of implies().

| Method | Type | | --------- | -------------------------------------------------- | | implies | (premises: string[], conclusion: string) => void |

Parameters:

  • premises: A conjuctive list of premises
  • conclusion: A proposition that must be satisfied if premises are

:gear: equal

Requires two conjuctive formulas be equal: either both are satisfied or neither are satisfied. (This is also called an if-and-only-if relationship.)

An array is treated as conjunction: p.equal([a, b], [c, d, e]) logically means (a /\ b) <-> (c /\ d /\ e).

| Method | Type | | ------- | ------------------------------------ | | equal | (a: string[], b: string[]) => void |

Parameters:

  • a: A conjuctive list of propositions
  • b: A conjuctive list of propositions

:gear: assert

Assert that a single proposition must be satisfied.

| Method | Type | | -------- | --------------------- | | assert | (a: string) => void |

:gear: rule

Indicates that the attribute in the conclusion (the "head" of the rule) is defined by this rule (and every other rule that has the attribute at the head). If all premises are satisfied, the conclusion must be assigned true, and if the conclusion is assigned true, then that must be justified derivable via some rule that defines the conclusion, for which the premise holds.

It's the second part, the fact that the conclusion must be derivable from some premise that holds, which makes rule() different from implies(). They also "point" in opposite directions.

An array of premises is treated as conjunction: p.rule(a, [b, c, d]) logically means that (b /\ c /\ d) -> a and that, if a is assigned true, either (b /\ c /\ d) is satisfied OR the premises of some other rule that has a as its conclusion is satisfied.

| Method | Type | | ------ | -------------------------------------------------- | | rule | (conclusion: string, premises: string[]) => void |

Parameters:

  • conclusion: The head of the rule (must not be negated)
  • premises: A conjuctive list of premises

:gear: showConstraints

Print current constraints to the console

| Method | Type | | ----------------- | ------------ | | showConstraints | () => void |

:gear: solve

Attempt to find an assignment that will satisfy all the currently-declared constraints.

Throws an exception if enough iterations go by without finding a satisfying assignment.

| Method | Type | | ------- | ---------------- | | solve | () => Solution |

:gear: attribute

Declares a new attribute.

To create a new attribute that takes no arguments, you can write something like p.attribute('q') or p.attribute('q', []). Both mean the same thing: they declare a predicate q that takes no arguments.

To specify a predicate that does take arguments, you must describe the domain of those arguments. For instance, if you wanted to describe some cats and their colors, so that you could have attributes like colored celeste gray and colored terra orange, then you'd declare a predicate colored like this:

let cast = ['celeste', 'nimbus', 'terra'];
let color = ['gray', 'black', 'white', 'orange'];
p.predicate('colored', [cast, color]);

| Method | Type | | ----------- | ------------------------------------------- | | attribute | (name: string, args?: string[][]) => void |

Parameters:

  • name: The name of the predicate
  • args: The domains of the argument

:factory: Solution

Solutions are returned the solve() method of a problem.

Fields

:gear: trueAttributes

Holds all the attributes that have been assigned true in the solution (in sorted order).

Type: string[]

:gear: lookup

A read-only map from attributes to their values.

Type: { [attribute: string]: boolean }