@slugbugblue/trax 0.5.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/engine.md CHANGED
@@ -10,7 +10,7 @@ the game state and what actions can be taken on the game.
10
10
  ## Example usage
11
11
 
12
12
  ```javascript
13
- import Trax from '@slugbugblue/trax'
13
+ import { Trax } from '@slugbugblue/trax'
14
14
 
15
15
  let trax = new Trax()
16
16
  console.log(trax.possible_moves()) // "[ '@0/', '@0+' ]" -- initial moves
@@ -28,17 +28,37 @@ console.log(trax.winner) // "1" -- player 1 wins
28
28
 
29
29
  ## API
30
30
 
31
- ### Trax object
31
+ ### Trax class
32
32
 
33
33
  The Trax object can be called as a constructor, but it also has the following
34
34
  class properties and methods:
35
35
 
36
- - `Trax.variants`: an array of the different rule sets the engine supports.
36
+ - `Trax.variants`: a Set of the different rule sets the engine supports. Try
37
+ `Trax.variants.has(rule)` to see if your favorite rule set is supported. Or
38
+ `Trax.variants.forEach((rule) => { console.log(rule) })` to list them all.
37
39
 
38
- - `Trax.column(col)`: a function to turn a numeric column number into the column
39
- letter used in Trax notation.
40
+ - `Trax.names`: an object of variant rule set names to English names.
40
41
 
41
- ### Trax class
42
+ - `Trax.point(x, y)`: quick access to the [Point class][point-docs], for
43
+ two-dimensional locations used internally by the Trax engine and which are
44
+ required for some of the lower-level engine functions. Operates as a
45
+ constructor (without `new`) and returns a Point instance.
46
+
47
+ - `Trax.colorOf(playerNumber)`: Player 1 is white and Player 2 is black. So this
48
+ is a quick function to turn `1` into `'w'` and `2` into `'b'`. Quick and
49
+ dirty.
50
+
51
+ - `Trax.playerNumber(color)`: Quick and dirty way to turn `'w'` into `1` and
52
+ `'b'` into `2`.
53
+
54
+ - `Trax.other(color)`: Quick and dirty way to turn `'w'` into `'b'` and
55
+ vice-versa.
56
+
57
+ - `Trax.encodeCol(columnNumber)`: a function to turn a numeric column number
58
+ into the column letter used in Trax notation. The first column is `A`. The
59
+ empty column just to the left is `@`.
60
+
61
+ - `Trax.decodeCol(columnLetter)`: turns a column letter back into a number.
42
62
 
43
63
  #### constructor
44
64
 
@@ -46,7 +66,335 @@ class properties and methods:
46
66
  trax = new Trax(rules, moves, id)
47
67
  ```
48
68
 
49
- - `rules
69
+ - `rules`: a string used to determine which trax variant will be played. If this
70
+ string is missing or is not found in `Trax.variants`, the default rule set of
71
+ `trax` will be used.
72
+
73
+ - `moves`: an array of moves or a space-separated string of moves to use as the
74
+ starting position of the game board. If missing, the board will be empty. Note
75
+ that if an array is used, each item in the array should be a notation string.
76
+ If a string is used instead, it is permissible to include the move numbers of
77
+ each move as well. As an example, the following are equivalent:
78
+
79
+ - `['@0/', '@1+', 'B0\\']`
80
+ - `'1. @0/ 2. @1+ 3. B0\\'`
81
+ - `@0/ @1+ b0\\`
82
+
83
+ - `id`: an optional string id that will be prepended to the key of each tile.
84
+ This is useful for setting the `id` value of HTML entities, but will probably
85
+ not be very useful for any other situations. You can likely safely ignore
86
+ this. Defaults to `'trax'`.
87
+
88
+ Returns an instance of the `Trax` class, which contains the following properties
89
+ and methods:
90
+
91
+ ### Trax instance
92
+
93
+ #### properties
94
+
95
+ - `id`: the string provided to the constructor
96
+ - `rules`: which of the `Trax.variants` is being played
97
+ - `move`: number, how many moves have been played
98
+ - `turn`: number, the player to make the next move
99
+ - `over`: boolean, `true` if the game is over
100
+ - `left`: number, the left-most column in play
101
+ - `right`: number, the right-most column in play
102
+ - `top`: number, the top-most row in play
103
+ - `bottom`: number, the bottom-most row in play
104
+ - note that each of these four numbers are using the internal coordinates of
105
+ the board rather than the Trax notation coordinates, since the latter
106
+ potentially change after every move
107
+ - `notation`: string, the notation of the current state, wrapped at 80 columns
108
+ - `tiles`: object, key/value pairs of data for each tile on the board
109
+ - key: a string composed of the game id and location of the tile
110
+ - value: an object containing the following key/value pairs:
111
+ - `type`: a string (`a` - `f`) representing the tile
112
+ - `loc`: an (x, y) Point object with the tile location
113
+ - `id`: the key of this object, provided here for easy cross-reference
114
+ - `move`: the move number this tile was placed on the board
115
+ - `seq`: the order in which this tile was played
116
+ - `path`: array, each item is a key of `tiles` involved in the winning path
117
+ - `invalid`: boolean, `true` only when a tile played into a cave results in an
118
+ illegal move
119
+
120
+ Note that while there is no protection on these properties, you should treat
121
+ them as read-only, since changing them directly will likely result in
122
+ unpredictable and invalid operations by the engine.
123
+
124
+ #### calculated properties
125
+
126
+ - `name`: string, a shortcut to get the actual name of the game from its variant
127
+ rule set code
128
+ - `count`: number, the total number of tiles currently in play
129
+ - `color`: string, the color (`'w'` or `'b'`) of the player whose turn it is
130
+ - `gameOver`: boolean, `true` if the game is over
131
+ - `winner`: `false` if the game is still in progress, number of the winning
132
+ player otherwise (0 = tie)
133
+ - `height`: the number of rows in play
134
+ - `width`: the number of columns in play
135
+ - `moves`: an array of move notations for the current game
136
+ - `icon`: a string containing an encoded representation of the current board
137
+ position, useful for quickly drawing the game board without having to iterate
138
+ through the list of tiles (see [Encoding the icon](#encoding-the-icon) for
139
+ details on the structure of this code)
140
+ - `normalized`: the normalized code for this position
141
+
142
+ #### useful methods
143
+
144
+ ##### `save()`
145
+
146
+ This method encodes and returns the current state of the game into an object
147
+ that can later be restored. You should treat this object as opaque, to be used
148
+ only with the `restore()` method. Save a position before attempting tentative or
149
+ exploratory moves, so that you can restore the original position without having
150
+ to re-play all of the moves to arrive at that position.
151
+
152
+ `save()` and `restore()` are preferable to saving the current notation and
153
+ calling `new Trax()` with that notation, since it avoids all of the calculations
154
+ necessary to arrive at the same state.
155
+
156
+ ##### `restore(savedObj)`
157
+
158
+ This method accepts the returned value of the `save()` method and restores the
159
+ previously saved position.
160
+
161
+ ##### `possibleMoves()`
162
+
163
+ Returns a list of all moves (using game notation) that can be made from the
164
+ current position. Since it does not examine the result of forced tiles for each
165
+ move, it does not detect illegal cave positions, so there is no guarantee that
166
+ each move that can be played will be legal.
167
+
168
+ ##### `play(notation)`
169
+
170
+ Attempts to play the given `notation`. Updates the game instance if the notation
171
+ is valid for the current position.
172
+
173
+ Returns an object with at minimum the following key/value pair:
174
+
175
+ - `valid`: `true` if the move is valid and results in a legal position
176
+
177
+ If the move is valid, the following will also be part of the returned object:
178
+
179
+ - `dropped`: a list of the tiles that were played by this move
180
+ - `notation`: the resulting notation of the move
181
+
182
+ ##### `playMove(moveNumber, notation)`
183
+
184
+ Used to play a move, but with an extra safeguard around the status of the game
185
+ so that if this is called multiple times with the same values, it will ignore
186
+ subsequent calls, which can be useful in the context of asynchronous
187
+ communication and retries, such as when this engine is being used as the back
188
+ end of a web interface. For example, if no moves have been made yet in a game,
189
+ this can be called either as `playMove(1, '@0/')` or `playMove(1, '@0+')`.
190
+
191
+ Returns the same object structure as the `play()` method.
192
+
193
+ ##### `provisionalMove(from, to, via)`
194
+
195
+ Test to see if a provisionally entered move is a match for the current position,
196
+ as determined by the normalized `from` position, playing the move notation
197
+ `via`, which must result in the normalized `to` position. Note that normalized
198
+ positions match symmetrically identical positions, so it's possible that `via`
199
+ isn't the actual move notation required. Symmetrical rotations of the notation
200
+ will be examined as well.
201
+
202
+ Returns `'delete-provisional'` if the provisional move will never be valid in
203
+ any future position. Returns `false` if the move doesn't match but may
204
+ potentially match a future position. Returns the potentially rotated move
205
+ notation if the provisional move was a match.
206
+
207
+ ##### `dropsIcon(drops)`
208
+
209
+ Returns an `icon` but with the included `drops` encoded distinctly so that they
210
+ can be distinguished from other tiles.
211
+
212
+ #### internal methods you probably don't need to use directly
213
+
214
+ Since these are not likely to be used directly, the documentation is a little
215
+ less complete.
216
+
217
+ ##### `tileId(location)`
218
+
219
+ Takes a `location` and returns a unique string id for the tile at that location.
220
+ The `tiles` property object uses the `tileId()` as a key for the tiles.
221
+
222
+ ##### `addTile(type, location)`
223
+
224
+ This function is used internally to place a tile of `type` on the board at
225
+ `location`. It has no validity checks, since that is handled in other parts of
226
+ the interface. It is not recommended to use this directly. It returns the tile
227
+ object that it adds to the board.
228
+
229
+ ##### `tileAt(location)`
230
+
231
+ Checks to see if a tile is at the specified `location`, and returns the tile
232
+ type or `undefined`.
233
+
234
+ ##### `validTile(type, location)`
235
+
236
+ Checks to see if a tile of `type` can be played at the given `location`. Returns
237
+ `true` if so. Note that this result is not a guarantee, since it does not check
238
+ forced tiles for illegal cave positions.
239
+
240
+ ##### `possibleTiles(location, slash)`
241
+
242
+ Returns a list of the possibly valid tiles at a given `location`. If the
243
+ optional `slash` parameter is included, it limits its search to tiles that also
244
+ match that `slash`.
245
+
246
+ ##### `validLocation(location)`
247
+
248
+ Returns true if a piece can be played at the given `location`. However, this
249
+ does not check for illegal cave positions.
250
+
251
+ ##### `possibleLocations()`
252
+
253
+ Returns a list of all locations where a tile may be played. Does not check for
254
+ illegal cave positions.
255
+
256
+ ##### `forcedMoves(location)`
257
+
258
+ After a tile is placed at `location`, play all the forced moves that result,
259
+ adding them to the board as well. Returns the list of all tile objects (as
260
+ returned from `addTile()`). It is during this function when illegal cave moves
261
+ are detected. When that happens, the tile at that location will be given a type
262
+ of `x`, and the `invalid` property of the Trax instance will be set to `true`,
263
+ but the tiles will not be removed. It is up to the calling function to check for
264
+ this condition and `restore` a previous position.
265
+
266
+ ##### `notate(type, location)`
267
+
268
+ Return the game notation when playing a tile of `type` at the given `location`.
269
+ This has to be done before the move is played, because if the move expands the
270
+ board to the left or upward, the notation will be invalid.
271
+
272
+ ##### `decodeNotation(notation)`
273
+
274
+ Returns an object with two keys: `type` and `location`.
275
+
276
+ ##### `follow(color, location, from)`
277
+
278
+ Follows the line of one `color` starting at the tile at the given `location`, in
279
+ the direction as if coming `from` a given edge. Returns an object giving the (x,
280
+ y) `loc` of the final tile, as well as a `path` array holding the tile id's
281
+
282
+ ##### `findEnds(color, location)`
283
+
284
+ Uses `follow()` to trace both ends of the given `color` from the given
285
+ `location`. Returns an array of two items, each containing an object describing
286
+ one of the ends of the line, as returned by `follow()`.
287
+
288
+ ##### `lineWin(locA, locB)`
289
+
290
+ Given `locA` as the (x, y) location of one end of a line and `locB` as the other
291
+ end, does the line constitute a line win? Returns `true` or `false`.
292
+
293
+ ##### `checkWin(tiles)`
294
+
295
+ Used after a play is made to determine if any of the played `tiles` results in a
296
+ win. Updates the instance's `over` and `turn` properties as needed. Returns
297
+ nothing.
298
+
299
+ ##### `updateNotation(notation)`
300
+
301
+ Handles updating the `notation` game instance property using the `notation` of
302
+ the current move. Returns nothing.
303
+
304
+ ##### `dropTile(notation, _, tentative)`
305
+
306
+ ##### `dropTile(type, location, tentative)`
307
+
308
+ Attempts to play a tile given either a `notation` or a `type` and `location`. If
309
+ `tentative` is provided and is truthy, the move will not actually be made, but
310
+ the return value will be the same as if it were made.
311
+
312
+ Returns an object with the following key/value pairs:
313
+
314
+ - `dropped`: a list of the tiles that were played by this move
315
+ - `notation`: the resulting notation of the move
316
+ - `valid`: `true` if the move is valid and results in a legal position
317
+
318
+ ##### `moveRotations(move)`
319
+
320
+ Rotates a given `move` around the board to assist with symmetry calculations.
321
+ Returns an array of possible symmetrical meanings of the given move.
322
+
323
+ ##### `positionCode(rightToLeft, bottomToTop, rotate, drops)`
324
+
325
+ This is a helper function to provide a string representation to a position,
326
+ applying optional symmetrical operations on it. By default, the position code
327
+ returned will be provided using a top to bottom, right to left, unrotated
328
+ approach. By setting `rightToLeft`, `topToBottom`, or `rotate` to `true`,
329
+ transformations can be applied to the position code to return any of 8 different
330
+ codes representing the 8 possible symmetries of any given position. If `drops`
331
+ is provided, it should be the array of `dropped` tiles returned by `dropTile()`,
332
+ and those tiles will be encoded differently. See
333
+ [Encoding the icon](#encoding-the-icon) for details on the encoding.
334
+
335
+ ##### `normalize()`
336
+
337
+ Returns a normalized position code icon. Every symmetrical position will return
338
+ the same normalized code, so you can determine if two different positions are
339
+ the same position with only changes in symmetry by calling this on both game
340
+ instances and comparing the resulting icon string. While this code should be
341
+ treated as if opaque, you can read about the way it is created from an
342
+ [encoded position code](#encoding-the-icon) in the section on
343
+ [normalizing the position code](#normalizing-a-position).
344
+
345
+ ## Trax tiles
346
+
347
+ There isn't an inherent order to the way in which Trax tiles are presented.
348
+ However, to allow for some sort of ease of encoding, the Trax module names each
349
+ tile with a lower-case letter from `a` to `f` as shown in the following
350
+ ASCII-art representations of each tile. The `##` characters are used to
351
+ represent black.
352
+
353
+ ```
354
+ Trax tiles
355
+ a b c d e f
356
+ ________ ________ ________ ________ _______ ________
357
+ |__## | |__##__| | ##__| |_/ | | | | | | | | \_|
358
+ |_ \###| |______| |###/ _| |__/###| |#| |#| |###\__|
359
+ |_\_|__| |__##__| |__|_/_| |__##__| |_|_|_| |__##__|
360
+
361
+ (white wins / black wins)
362
+ g m h n i o j p k q l r
363
+ ```
364
+
365
+ ### Encoding the icon
366
+
367
+ The `icon` computed property encodes the position of the board by replacing each
368
+ tile on the board with its corresponding letter as described immediately above.
369
+ In cases where the tile in question is part of a winning line or loop, the
370
+ character for that tile is increased by 6 letters for a white win, or by 12
371
+ letters for a black win. In addition, when requested, the icon can also include
372
+ information about which tiles were placed in the last move, and those tiles are
373
+ represented by their upper-case counterparts.
374
+
375
+ In the event that a given move results in an illegal position, one of the tiles
376
+ that make up the illegal position will be replaced by an `x` instead of one of
377
+ the listed tiles. This signifies that no possible tile can meet the forced moves
378
+ rules of that location. Only one of the tiles will be so marked, even though it
379
+ requires several contributing tiles to create a situation where a move is
380
+ illegal.
381
+
382
+ Spaces are encoded into the icon using single-digit numbers. `0` means there is
383
+ one space. `9` would indicate there are 10 spaces. Spaces at the left or in
384
+ between tiles are encoded. Empty spaces to the right of all tiles are ignored.
385
+
386
+ A colon is used to separate rows.
387
+
388
+ ### Normalizing a position
389
+
390
+ A normalized position is one where a single position code is chosen such that
391
+ symmetrical positions will be represented by a single position code.
392
+
393
+ Different from a standard position code, a normalized position code includes the
394
+ addition of a leading character indicating the state of the game. `W` means
395
+ either that it is white's turn or that white has won the game. `B` indicates
396
+ that black is to play or has already won. `T` means that the game has ended in a
397
+ tie.
50
398
 
51
399
  ## License
52
400
 
@@ -62,3 +410,5 @@ Unless required by applicable law or agreed to in writing, software distributed
62
410
  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
63
411
  CONDITIONS OF ANY KIND, either express or implied. See the License for the
64
412
  specific language governing permissions and limitations under the License.
413
+
414
+ [point-docs]: point.md
package/docs/point.md ADDED
@@ -0,0 +1,134 @@
1
+ # point.js API documentation
2
+
3
+ This module provides a class structure to work with `x`, `y` locations as a
4
+ single point.
5
+
6
+ Since I don't need it to be super fancy, there isn't a lot of extra
7
+ functionality here.
8
+
9
+ ## Example usage
10
+
11
+ ```javascript
12
+ import { Point } from '@slugbugblue/trax/point.js'
13
+
14
+ const zero = new Point(0, 0)
15
+
16
+ const up = zero.up
17
+ console.log('up:', up.x, up.y) // up: 0 -1
18
+
19
+ const rt = zero.right
20
+ console.log('rt:', rt) // rt: Point(1,0)
21
+
22
+ const up2 = zero.dir('up')
23
+ console.log('up === up2 ?', up.eq(up2)) // up === up2 ? true
24
+
25
+ // distances
26
+ console.log(zero.distX(up), zero.distX(rt)) // 0 1
27
+ console.log(up.distX(rt), up.distY(rt)) // 0 0
28
+ console.log(zero.distance(up), zero.distance(rt)) // 1 1
29
+ console.log(up.distance(rt)) // 1.4142135623730951
30
+ ```
31
+
32
+ ## API
33
+
34
+ ### Point class
35
+
36
+ A Point instance is a representation of an immutable point in two dimensional
37
+ space, represented by an `x` and a `y` component. Since this implementation was
38
+ created for game boards, we assume that all points are integers, though that is
39
+ not a requirement, and the math will work equally well on non-integers.
40
+
41
+ #### constructor
42
+
43
+ ##### `new Point(fromPoint)`
44
+
45
+ ##### `new Point(x, y)`
46
+
47
+ You can create a point from either a point-like object (ie, any object with both
48
+ an `x` and a `y` key with numeric values), or from individual `x` and `y`
49
+ numbers. Once a point is created, it cannot be changed; however, you can use its
50
+ functions to return other points relative to it.
51
+
52
+ ##### `x` and `y` immutable properties
53
+
54
+ You can return the individual `x` or `y` property of a point by accessing it
55
+ directly. Attempts to change these properties will fail.
56
+
57
+ ##### `up`, `down`, `left`, `right` calculated properties
58
+
59
+ Since points are immutable, to move a point, you need to create a new point.
60
+ These calculated properties provide quick access to cardinal integer neighboring
61
+ points.
62
+
63
+ ##### `around` calculated property
64
+
65
+ Or, get an array of all four cardinal points around the existing point.
66
+
67
+ ##### `dir(direction)`
68
+
69
+ Pass in a string named direction (ie, one of `up`, `down`, `left`, or `right`)
70
+ to get a point one integer away from the existing point in the direction
71
+ indicated. This does the exact same thing as the calculated properties with
72
+ these names, but it can be done using a variable instead.
73
+
74
+ ##### `distX(from)`, `distY(from)`
75
+
76
+ Returns the difference between the `x` or `y` properties of two points. This
77
+ will always be represented by zero or a positive number.
78
+
79
+ ##### `distance(from)`
80
+
81
+ Returns the distance between two points. This will never be a negative number.
82
+
83
+ ##### `eq(to)`
84
+
85
+ Returns `true` if the given point or point-like object is at the same location
86
+ as the existing point.
87
+
88
+ #### Plays well with others
89
+
90
+ The Point class also includes functionality to provide useful default results
91
+ when used in other contexts.
92
+
93
+ ##### `toString()`
94
+
95
+ Returns a string representation of the point in the format `Point(x,y)`. This
96
+ allows simple logging calls with `console.log(point)`.
97
+
98
+ ##### `toJSON()`
99
+
100
+ JSON cannot encode special classes, but this class can be stored as a bare
101
+ object with `x` and `y` keys when converted to JSON. Note, though, that when
102
+ parsing the resultant JSON string, all Point functionality is not restored by
103
+ default. For example:
104
+
105
+ ```javascript
106
+ const zero = new Point(0, 0)
107
+ const json = JSON.stringify(zero)
108
+ const jsonzero = JSON.parse(json)
109
+
110
+ console.log(zero.eq(jsonzero)) // true
111
+ jsonzero.eq(zero) // TypeError: jsonzero.eq is not a function
112
+ const reconstituted = new Point(jsonzero)
113
+ console.log(reconstituted.eq(zero)) // true
114
+ ```
115
+
116
+ ##### `util.inspect.custom()`
117
+
118
+ When running in the node command line interpreter, a point will be
119
+ pretty-printed using the magic of `util.inspect`.
120
+
121
+ ## License
122
+
123
+ Copyright 2019-2022 Chad Transtrum
124
+
125
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
126
+ the files in this project except in compliance with the License. You may obtain
127
+ a copy of the License at
128
+
129
+ http://www.apache.org/licenses/LICENSE-2.0
130
+
131
+ Unless required by applicable law or agreed to in writing, software distributed
132
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
133
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
134
+ specific language governing permissions and limitations under the License.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slugbugblue/trax",
3
- "version": "0.5.0",
3
+ "version": "0.9.0",
4
4
  "description": "Trax game engine and friends",
5
5
  "keywords": [
6
6
  "trax",
@@ -11,18 +11,27 @@
11
11
  "bugs": "https://gitlab.com/slugbugblue/trax/issues",
12
12
  "license": "Apache-2.0",
13
13
  "author": "Chad Transtrum <chad@transtrum.net>",
14
- "exports": "./engine.js",
14
+ "exports": "./src/engine.js",
15
15
  "repository": "gitlab:slugbugblue/trax",
16
+ "bin": {
17
+ "trax": "./src/cli.js"
18
+ },
16
19
  "scripts": {
17
20
  "test": "xo && c8 ava",
18
21
  "preversion": "npm test",
19
- "postversion": "git push && git push --tags"
22
+ "postversion": "hooks/postversion.sh src/version.js"
23
+ },
24
+ "dependencies": {
25
+ "env-paths": "^3.0.0",
26
+ "make-dir": "^3.1.0",
27
+ "yaml": "^2.0.0-11"
20
28
  },
21
29
  "devDependencies": {
22
- "ava": "^4.0.1",
30
+ "ava": "^4.1.0",
23
31
  "c8": "^7.11.0",
24
- "prettier": "^2.5.1",
25
- "xo": "^0.47.0"
32
+ "genversion": "^3.0.2",
33
+ "prettier": "^2.6.1",
34
+ "xo": "^0.48.0"
26
35
  },
27
36
  "type": "module",
28
37
  "engines": {
@@ -37,6 +46,12 @@
37
46
  "useTabs": false
38
47
  },
39
48
  "xo": {
40
- "prettier": true
49
+ "prettier": true,
50
+ "rules": {
51
+ "curly": [
52
+ "error",
53
+ "multi-line"
54
+ ]
55
+ }
41
56
  }
42
57
  }