Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/sabakihq/immutable-gametree
An immutable game tree data type.
https://github.com/sabakihq/immutable-gametree
baduk board-game data-type go immutable sgf tree weiqi
Last synced: about 1 month ago
JSON representation
An immutable game tree data type.
- Host: GitHub
- URL: https://github.com/sabakihq/immutable-gametree
- Owner: SabakiHQ
- License: mit
- Created: 2018-07-14T19:10:47.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2021-07-09T01:01:37.000Z (over 3 years ago)
- Last Synced: 2024-11-14T10:34:12.390Z (about 1 month ago)
- Topics: baduk, board-game, data-type, go, immutable, sgf, tree, weiqi
- Language: JavaScript
- Homepage:
- Size: 174 KB
- Stars: 20
- Watchers: 3
- Forks: 10
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# @sabaki/immutable-gametree [![Build Status](https://github.com/SabakiHQ/immutable-gametree/workflows/CI/badge.svg?branch=master)](https://github.com/SabakiHQ/immutable-gametree/actions)
An immutable game tree data type.
## Installation
Use npm to install:
```
$ npm install @sabaki/immutable-gametree
```## Usage
```js
const GameTree = require('@sabaki/immutable-gametree')let tree = new GameTree()
let newTree = tree.mutate(draft => {
let id1 = draft.appendNode(draft.root.id, {B: ['dd']})
let id2 = draft.appendNode(id1, {W: ['dq']})draft.addToProperty(id2, 'W', 'qd')
})console.log(newTree !== tree)
// => true
console.log(tree.root.children.length)
// => 0
console.log(newTree.root.children.length)
// => 1
console.log(newTree.root.children[0].children[0].data.W)
// => ['dq', 'qd']
```## API
### Node Object
A node is represented by an object of the following form:
```js
{
id: ,
data: {
[property: ]: >
},
parentId: | null,
children: >
}
```### Currents Object
A node can have a distinguished child. You can specify the distinguished
children of nodes with an object of the following form:```js
{
[id: ]:
}
```Every value is the id of the distinguished child of the node with its key as id.
If the currents object doesn't specify a distinguished child for a node, the
default will be the first child index-wise.---
### `class GameTree`
#### `new GameTree([options])`
- `options` `` _(optional)_
- `getId` `` _(optional)_
- `merger` `` _(optional)_
- `root` [``](#node-object) _(optional)_#### `tree.getId`
`` - The `getId` function will be called to get an id for each
appended node. It should return a primitive value which is unique for each call.
Defaults to a simple counter.This property will be inherited across mutations.
#### `tree.merger`
`` - When appending a new node during mutations, you can instruct your
`GameTree` to automatically merge your new data into an existing node when
desired. The merger function has the following signature:```js
(node: , data: ) -> | null
```where `node` is a merge candidate and `data` the data to be appended. Return
`null` if you do not want `data` to be merged into the existing `node`. Return
an object (representing the merged data) if you want `node` to get that data
instead (and no new nodes are going to be appended).This property will be inherited across mutations.
#### `tree.root`
[``](#node-object) - The root node.
#### `tree.get(id)`
- `id` ``
Searches the whole tree for a node with the specified id and returns a
[node object](#node-object). If a node with the specified id doesn't exist, it
will return `null`. Each instance of `GameTree` will maintain a cache.Please refrain from mutating the returned object to ensure immutability.
#### `*tree.getSequence(id)`
A generator function that yields [node objects](#node-objects), starting with
the node of the given `id` and continuing with its children until we reach a
descendant which has multiple or no children.#### `tree.mutate(mutator)`
- `mutator` ``
The `mutator` will be called with a [`Draft`](#class-draft) class. In the
`mutator` function you will apply all your changes to the draft. Returns a new
`GameTree` instance with the changes you applied to the draft, without changing
the original `GameTree` instance.We use structural sharing to make mutations fairly efficient.
#### `tree.navigate(id, step, currents)`
- `id` ``
- `step` ``
- `currents` [``](#currents-object)Starts at the node with the given `id`, takes the specified `step` forward or
backward with respect to `currents`, and returns the node at the new position.#### `*tree.listNodes()`
A generator function that yields all the nodes as [node objects](#node-object)
of the game tree.#### `*tree.listNodesHorizontally(startId, step)`
- `startId` ``
- `step` `` - `1` or `-1`A generator function that yields the nodes as [node objects](#node-object) of
the game tree by walking horizontally along the game tree (left if `step` is
`-1`, otherwise right) starting at the node with id `startId`.#### `*tree.listNodesVertically(startId, step, currents)`
- `startId` ``
- `step` `` - `1` or `-1`
- `currents` [``](#currents-object)A generator function that yields the nodes as [node objects](#node-object) of
the game tree by walking vertically along given `currents` (up if `step` is
`-1`, otherwise down) starting at the node with id `startId`.#### `*tree.listCurrentNodes(currents)`
- `currents` [``](#currents-object)
Equivalent to `tree.listNodesVertically(tree.root.id, 1, currents)`.
#### `*tree.listMainNodes()`
Equivalent to `tree.listCurrentNodes({})`.
#### `tree.getLevel(id)`
- `id` ``
Returns an integer denoting the level of the node with the given `id`. If node
doesn't exist, it will return `null`.#### `*tree.getSection(level)`
- `level` ``
A generator function that yields all nodes of the given `level`.
#### `tree.getCurrentHeight(currents)`
- `currents` [``](#currents-object)
Equivalent to `[...tree.listCurrentNodes(currents)].length`.
#### `tree.getHeight()`
Calculates and returns the height of the tree as an integer. This value will be
cached across mutations when possible.#### `tree.getHash()`
Calculates and returns a hash of the whole tree as a string. This value will
be cached.#### `tree.getStructureHash()`
Calculates and returns a hash of the tree structure as a string. This value will
be cached across mutations when possible.#### `tree.onCurrentLine(id, currents)`
- `id` ``
- `currents` [``](#currents-object)Returns whether the node with the given `id` is the root node or a distinguished
descendant of the root node with respect to `currents`.#### `tree.onMainLine(id)`
- `id` ``
Equivalent to `tree.onCurrentLine(id, {})`.
#### `tree.toJSON()`
Returns `tree.root`.
---
### `class Draft`
#### `draft.root`
See [tree.root](#treeroot).
#### `draft.get(id)`
- `id` ``
See [tree.get(id)](#treegetid).
#### `draft.appendNode(parentId, data[, options])`
- `parentId` ``
- `data` ``
- `options` `` _(optional)_
- `disableMerging` `` - Default: `false`Appends a new node with the given `data` to the node with id `parentId`. Returns
`null` if operation has failed, otherwise the id of the new node. If
`disableMerging` is set to `true`, automatic merging via
[`tree.merger`](#treemerger) will be disabled.#### `draft.UNSAFE_appendNodeWithId(parentId, id, data[, options])`
- `parentId` ``
- `id` ``
- `data` ``
- `options` `` _(optional)_ - See
[`draft.appendNode`](#draftappendnodeparentid-data-options)Appends a new node with the given `id` and `data` to the node with id
`parentId`. Returns `false` if operation has failed, otherwise `true`.Make sure the `id` provided does not already exist in the tree and that the
`getId` function will never return `id`. We won't do any checks for you.#### `draft.removeNode(id)`
- `id` ``
Removes the node with given `id`. Throws an error if specified `id` represents
the root node. Returns `false` if operation has failed, otherwise `true`.#### `draft.shiftNode(id, direction)`
- `id` ``
- `direction` `` - One of `'left'`, `'right'`, `'main'`Changes the position of the node with the given `id` in the children array of
its parent node. If `direction` is `'main'`, the node will be shifted to the
first position. Returns `null` if operation has failed, otherwise the new index.#### `draft.makeRoot(id)`
- `id` ``
Makes the node with the given `id` the root node of the mutated tree. Returns
`false` if operation has failed, otherwise `true`.#### `draft.addToProperty(id, property, value)`
- `id` ``
- `property` ``
- `value` ``Adds the given `value` to the specified `property` of the node with the given
`id`. Ignores duplicate values. If data doesn't include the given `property`, it
will add it. Returns `false` if operation has failed, otherwise `true`.#### `draft.removeFromProperty(id, property, value)`
- `id` ``
- `property` ``
- `value` ``Removes the given `value` from the specified `property` of the node with the
given `id`. If property list gets empty, the property key will be removed from
data. Returns `false` if operation has failed, otherwise `true`.#### `draft.updateProperty(id, property, values)`
- `id` ``
- `property` ``
- `values` `>`Sets the specified `property` of the node with the given `id` as `values`.
Refrain from mutating `values` to ensure immutability. Returns `false` if
operation has failed, otherwise `true`.#### `draft.removeProperty(id, property)`
- `id` ``
- `property` ``Removes the specified `property` from the node. Returns `false` if operation has
failed, otherwise `true`.## Related
- [crdt-gametree](https://github.com/SabakiHQ/crdt-gametree) - An immutable,
conflict-free replicated game tree data type.