@slugbugblue/trax 0.22.0 → 1.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @slugbugblue/trax
2
2
 
3
+ ## 1.0.0 - 2025-11-04
4
+
5
+ v1 release:
6
+
7
+ - Update dev dependencies to latest versions
8
+ - Fix xo complaints
9
+ - No other code changes
10
+
11
+ ## 0.23.0 - 2023-03-28
12
+
13
+ Release candidate
14
+
15
+ Finalize work with jsdoc/typescript
16
+
3
17
  ## 0.22.0 - 2023-03-25
4
18
 
5
19
  - Pull point.js into its own package: `@slugbugblue/point`
@@ -12,7 +26,6 @@
12
26
  ## 0.21.0 - 2023-03-12
13
27
 
14
28
  - Update the analysis engine to be more lazy
15
-
16
29
  - The LRU cache is more likely to have a hit because it no longer separates
17
30
  validated from unvalidated analyses
18
31
  - Suggestions are much faster because it now skips validations for low-scoring
@@ -28,7 +41,7 @@
28
41
  - Drop support for node 14; node v16.15 is our minimum supported version
29
42
  - More puzzles from Martin M. S. Pedersen -- bringing the total to 100+
30
43
  - Enhance the docker configuration and document it in the README
31
- - Refine faulty threat detection and add test for once case it was failing
44
+ - Refine faulty threat detection and add test for one case it was failing
32
45
 
33
46
  ## 0.19.0 - 2023-02-06
34
47
 
@@ -37,23 +50,19 @@
37
50
  ## 0.18.0 - 2023-02-05
38
51
 
39
52
  - Analysis improvements:
40
-
41
53
  - Add `count.b(level)` and `count.w(level)` methods to an analysis to quickly
42
54
  count the number of threats at a given level
43
55
  - Remove faulty threats from an analysis
44
56
  - Add additional threats for more puzzle solving mojo
45
57
 
46
58
  - Development environment enhancements:
47
-
48
59
  - Added an initial benchmarking framework with puzzle benchmarks so far
49
60
  - Added husky pre-commit hook to ensure npm test is run before a commit
50
61
 
51
62
  - CLI improvements:
52
-
53
63
  - Puzzle information is now stored as notes, to make it searchable
54
64
 
55
65
  - More puzzles:
56
-
57
66
  - Martin M. S. Pedersen contributed eleven new puzzles
58
67
 
59
68
  ## 0.17.0 - 2022-12-29
@@ -63,7 +72,6 @@
63
72
  ## 0.16.0 - 2022-12-27
64
73
 
65
74
  - Added a `notes` command to the CLI to add comments to a game
66
-
67
75
  - all comments are included in the output generated by the `export` command
68
76
  - the latest comment can be used as a text filter by the `list` command
69
77
  - the latest comment is displayed by the `view` and `list` commands
@@ -73,17 +81,14 @@
73
81
  ## 0.15.0 - 2022-12-14
74
82
 
75
83
  - Added shared `puzzles` commands to the CLI:
76
-
77
84
  - `trax ls puzzles`: see all the puzzles
78
85
  - `trax new puzzle`: start a new puzzle
79
86
 
80
87
  - Puzzle improvements:
81
-
82
88
  - Adjust puzzle category levels for harder puzzles
83
89
  - Add multiple hints when the puzzle can be solved multiple ways
84
90
 
85
91
  - Analysis improvements:
86
-
87
92
  - Analysis suggestions now "solve" all tough puzzles
88
93
  - Reworked threats to be more precise
89
94
  - Reworked scoring to add more nuance
@@ -91,7 +96,6 @@
91
96
  ## 0.14.0 - 2022-12-05
92
97
 
93
98
  - Added `puzzles` to the CLI:
94
-
95
99
  - `trax puzzles ls`: see all the puzzles
96
100
  - `trax puzzle new`: start a new puzzle
97
101
 
@@ -102,13 +106,11 @@
102
106
  ## 0.13.0 - 2022-11-25
103
107
 
104
108
  - Add move suggestions to `analyst.js` for CLI and bot use
105
-
106
109
  - use `suggest(trax)` to get a suggestion for the current position
107
110
  - added the `suggest` CLI command for quick access to a random suggestion
108
111
 
109
112
  - Use font glyphs for text bubbles in `tty.js` only if one of the following
110
113
  environment variables are defined and non-empty:
111
-
112
114
  - `NERDFONT`, `POWERLINE`, `P9K_TTY`, `P9K_SSH`
113
115
 
114
116
  - Begin work on documenting the javascript files using jsdoc, with the initial
@@ -125,7 +127,6 @@
125
127
  `play()`. `playMove()` is no longer available.
126
128
  - Add a new function `playMoves()` to submit multiple moves at once
127
129
  - Initial work on `analyst.js`, with documentation and a start on tests
128
-
129
130
  - use `analyze(trax)` to perform an analysis
130
131
  - added the `analyze` CLI command to see an analysis
131
132
 
@@ -185,7 +186,6 @@ variables from snake_case to camelCase.
185
186
  - The name and signature for provisional move handling, which was broken, has
186
187
  been changed. It is now called `provisional_moves()` and takes three
187
188
  arguments:
188
-
189
189
  - `from`: the normalized position code of the board before the move
190
190
  - `to`: the normalized position code of the board after the move
191
191
  - `via`: the move notation to go from `from` to `to`
@@ -221,7 +221,6 @@ More convenience:
221
221
  Initial release:
222
222
 
223
223
  - engine.js
224
-
225
224
  - create a new Trax game with `trax = new Trax()`
226
225
  - play one move at a time with `trax.play('@0/')`
227
226
  - ... etc
package/README.md CHANGED
@@ -71,7 +71,7 @@ Trax][gnutrax] for over two decades, and has a lot of expertise in this area.
71
71
 
72
72
  ## License
73
73
 
74
- Copyright 2019-2023 Chad Transtrum
74
+ Copyright 2019-2025 Chad Transtrum
75
75
 
76
76
  Licensed under the Apache License, Version 2.0 (the "License"); you may not use
77
77
  the files in this project except in compliance with the License. You may obtain
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slugbugblue/trax",
3
- "version": "0.22.0",
3
+ "version": "1.0.0",
4
4
  "description": "Trax game engine",
5
5
  "keywords": [
6
6
  "trax",
@@ -19,10 +19,10 @@
19
19
  },
20
20
  "repository": "gitlab:slugbugblue/trax",
21
21
  "scripts": {
22
- "genversion": "genversion --es6 src/version.js",
22
+ "genversion": "genversion --esm src/version.js",
23
23
  "git-add": "git add src/version.js",
24
24
  "test": "xo && c8 ava",
25
- "prepare": "husky install",
25
+ "prepare": "husky",
26
26
  "preversion": "npm test",
27
27
  "version": "npm run genversion ; npm run git-add",
28
28
  "postversion": "git push && git push --tags && npm publish"
@@ -32,12 +32,12 @@
32
32
  "@slugbugblue/point": "^1.0.0"
33
33
  },
34
34
  "devDependencies": {
35
- "ava": "^5.1.0",
36
- "c8": "^7.11.0",
35
+ "ava": "^6.4.1",
36
+ "c8": "^10.1.3",
37
37
  "genversion": "^3.0.2",
38
- "husky": "^8.0.3",
39
- "prettier": "^2.6.1",
40
- "xo": "0.*"
38
+ "husky": "^9.1.7",
39
+ "prettier": "^3.6.2",
40
+ "xo": "^1.2.3"
41
41
  },
42
42
  "type": "module",
43
43
  "engines": {
@@ -60,6 +60,7 @@
60
60
  },
61
61
  "xo": {
62
62
  "prettier": true,
63
+ "space": true,
63
64
  "rules": {
64
65
  "curly": [
65
66
  "error",
package/src/engine.js CHANGED
@@ -1,20 +1,9 @@
1
- /* Copyright 2019-2022 Chad Transtrum
2
- *
3
- * Licensed under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License.
5
- * You may obtain a copy of the License at
6
- *
7
- * http://www.apache.org/licenses/LICENSE-2.0
8
- *
9
- * Unless required by applicable law or agreed to in writing, software
10
- * distributed under the License is distributed on an "AS IS" BASIS,
11
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- * See the License for the specific language governing permissions and
13
- * limitations under the License.
1
+ /** Trax engine.
2
+ * @copyright 2019-2022
3
+ * @author Chad Transtrum <chad@transtrum.net>
4
+ * @license Apache-2.0
14
5
  */
15
6
 
16
- // Trax internals. Woot.
17
-
18
7
  import { Point } from '@slugbugblue/point'
19
8
 
20
9
  // Fun trax helper constants
@@ -36,7 +25,9 @@ const notationRegex = /^([@a-z]+)(\d+)([/\\+])$/i
36
25
  * */
37
26
  const tileTypes = ['a', 'b', 'c', 'd', 'e', 'f']
38
27
 
39
- /** Slashes are represented by / \ or +.
28
+ /** Slashes indicate the direction the tile is placed. A forward slash or a
29
+ * backslash indicate the tile is a "curve" and the plus sign indicates the
30
+ * tile is a "straight".
40
31
  * @readonly
41
32
  * @type {Record<ValidTiles, Slash>}
42
33
  */
@@ -119,50 +110,42 @@ const addBlank = (code) => {
119
110
  * @returns {number} - the actual length of the row
120
111
  */
121
112
  const codeRowLength = (row) =>
122
- row.replace(/\d/g, (n) => '.'.repeat(Number(n) + 1)).length
113
+ row.replaceAll(/\d/g, (n) => '.'.repeat(Number(n) + 1)).length
123
114
 
124
115
  // This is where the magic happens
125
116
  /** A digital representation of a Trax game. */
126
117
  export class Trax {
127
118
  // Static class properties
128
-
129
119
  /** @readonly @type {Record<TraxVariant, string>} */
130
120
  static names = {
131
121
  trax: 'Trax',
132
122
  traxloop: 'Loop Trax',
133
123
  trax8: '8x8 Trax',
134
124
  }
135
-
136
125
  /** @readonly */
137
126
  static variants = new Set(Object.keys(this.names)) // Known variants
138
-
139
127
  // Static class methods
140
-
141
128
  /** Create an x,y Point object with special functions.
142
129
  * @arg {number|PointLike} x - either the x value, or an object with x,y keys
143
130
  * @arg {number} [y] - if x is a number, y must be provided as well
144
131
  * @returns {Point} a new Point object
145
132
  */
146
133
  static point = (x, y) => new Point(x, y)
147
-
148
134
  /** Given a player number, get the color.
149
135
  * @arg {number} playerNumber - the player number, 1 or 2
150
136
  * @returns {Color} the color of that player, w or b
151
137
  */
152
- static colorOf = (playerNumber) => ({ 1: 'w', 2: 'b' }[playerNumber])
153
-
138
+ static colorOf = (playerNumber) => ({ 1: 'w', 2: 'b' })[playerNumber]
154
139
  /** Given a color, get the player number.
155
140
  * @arg {string} color - the color, w or b
156
141
  * @returns {number} the player number, 1 or 2
157
142
  */
158
- static playerNumber = (color) => ({ b: 2, w: 1 }[color])
159
-
143
+ static playerNumber = (color) => ({ b: 2, w: 1 })[color]
160
144
  /** Given a color, get the other color.
161
145
  * @arg {string} color - the color, w or b
162
146
  * @returns {Color} the other color, b or w
163
147
  */
164
- static other = (color) => ({ b: 'w', w: 'b' }[color])
165
-
148
+ static other = (color) => ({ b: 'w', w: 'b' })[color]
166
149
  /** Encode a numeric column number into the Trax notation column letter.
167
150
  * @arg {number} col - the colum number, with 0 just to the left of the tiles
168
151
  * @returns {string} the encoded column letter
@@ -177,7 +160,6 @@ export class Trax {
177
160
 
178
161
  return n || '@'
179
162
  }
180
-
181
163
  /** Decode a Trax notation column letter back to a number.
182
164
  * @arg {string} col - the Trax column letter
183
165
  * @returns {number} the column number
@@ -197,10 +179,10 @@ export class Trax {
197
179
  // Trax class constructor
198
180
  /** Create a new Trax game
199
181
  * @arg {TraxVariant} [rules='trax'] - the variant to play
200
- * @arg {string|string[]} [moves=[]] - the initial moves to pre-play
182
+ * @arg {string|string[]} [moves=''] - the initial moves to pre-play
201
183
  * @arg {string} [id='trax'] - an id used to differentiate tiles from multiple games
202
184
  */
203
- constructor(rules = 'trax', moves = [], id = 'trax') {
185
+ constructor(rules = 'trax', moves = '', id = 'trax') {
204
186
  if (!Trax.variants.has(rules)) rules = 'trax'
205
187
  this.id = String(id)
206
188
  this.rules = rules
@@ -311,6 +293,7 @@ export class Trax {
311
293
  */
312
294
  addTile(type, loc) {
313
295
  const id = this.tileId(loc)
296
+ /** @type {Tile} */
314
297
  const tile = { type, loc, id, move: this.move, seq: this.count }
315
298
  if (type === 'x') {
316
299
  this.invalid = true // Invalid tile is being played
@@ -334,7 +317,7 @@ export class Trax {
334
317
  }
335
318
 
336
319
  /** Is this tile valid?
337
- * @type {(type: string, loc: Point) => type is TileType}
320
+ * @type {(type: string, loc: Point) => type is ValidTiles}
338
321
  */
339
322
  validTile(type, loc) {
340
323
  if (this.count === 0 && loc.x === 0 && loc.y === 0) {
@@ -377,6 +360,10 @@ export class Trax {
377
360
  return this.right - this.left + 1
378
361
  }
379
362
 
363
+ /** Determine if a location is valid to play in.
364
+ * @arg {Point} loc - the location to check
365
+ * @returns {boolean}
366
+ */
380
367
  validLocation(loc) {
381
368
  if (this.over) return false
382
369
  if (this.tileAt(loc)) return false
@@ -398,26 +385,31 @@ export class Trax {
398
385
  return false
399
386
  }
400
387
 
388
+ /** Find all possible locations to play in. Note that these are not
389
+ * necessarily valid locations, just empty ones that border existing tiles.
390
+ */
401
391
  possibleLocations() {
402
392
  if (this.count === 0) return [zero]
403
393
  /** @type Record<TileId, Point> */
404
394
  const possibles = {}
405
395
 
406
396
  for (const tile of Object.values(this.tiles)) {
407
- tile.loc.around.map((loc) => {
397
+ for (const loc of tile.loc.around) {
408
398
  const id = this.tileId(loc)
409
399
  if (!(id in this.tiles)) possibles[id] = loc
410
- return loc
411
- })
400
+ }
412
401
  }
413
402
 
414
- // Get rid of all the real tiles and return all the maybes
415
403
  return Object.values(possibles)
416
404
  }
417
405
 
406
+ /** Find all possible moves as a list of notations,
407
+ * ie: ['@1+', '@1/', '@1\\', ...]
408
+ *
409
+ * Note that moves are not guaranteed to be valid.
410
+ */
418
411
  possibleMoves() {
419
- // Returns a list of every currently possible move
420
- // as notations, ie ['@1+', '@1/', '@1\\', ...]
412
+ /** @type {string[]} */
421
413
  const possibles = []
422
414
  if (this.over) return possibles // Shortcut if the game is over
423
415
  for (const loc of this.possibleLocations()) {
@@ -430,9 +422,14 @@ export class Trax {
430
422
  return possibles
431
423
  }
432
424
 
425
+ /** Called after a move was just played, to determine the forced moves.
426
+ * @arg {Point} loc - the location just played at
427
+ * @returns {Tile[]} a list of tiles that should be added as part of the move
428
+ */
433
429
  forcedMoves(loc) {
434
- // We just played at loc, so return all forced moves
430
+ /** @type {Tile[]} */
435
431
  const forced = []
432
+ /** @type {Record<string, boolean>} */
436
433
  const invalids = {}
437
434
  let check = loc.around
438
435
  while (check.length > 0) {
@@ -455,16 +452,26 @@ export class Trax {
455
452
  return forced
456
453
  }
457
454
 
455
+ /** Determine the notation for a move. Note that this must be determined
456
+ * BEFORE the move is placed on the board.
457
+ * @arg {ValidTiles} type - the tile placed on the board
458
+ * @arg {Point} loc - the location the tile is placed
459
+ * @returns {string} the notation of the move
460
+ */
458
461
  notate(type, loc) {
459
- // Notation depends on the position of the board BEFORE the move
460
462
  let notation = Trax.encodeCol(loc.x - this.left + 1)
461
463
  notation += String(loc.y - this.top + 1)
462
464
  notation += slash[type]
463
465
  return notation
464
466
  }
465
467
 
468
+ /** Turn a move notation into a tile type and location.
469
+ * @arg {string} notation - a notation for a single move to be played at
470
+ * the current board position
471
+ * @returns {RawMove} the tile type and location of the move
472
+ */
466
473
  decodeNotation(notation) {
467
- // Return {type, loc} from a notation
474
+ /** @type {RawMove} */
468
475
  const bad = { type: 'x', loc: zero }
469
476
  const match = notation.match(notationRegex)
470
477
  if (match === null) return bad
@@ -477,6 +484,14 @@ export class Trax {
477
484
  return bad
478
485
  }
479
486
 
487
+ /** Follow a color from one location through one or more tiles to the other
488
+ * end of the color line.
489
+ * @arg {Color} color - the color to follow
490
+ * @arg {Point} loc - the location to start
491
+ * @arg {string} from - the edge of the tile to start from
492
+ * @returns {LineEnd} the ending location and a list of the tile ids the path
493
+ * takes to get there.
494
+ */
480
495
  follow(color, loc, from) {
481
496
  const path = []
482
497
  let id = this.tileId(loc)
@@ -504,7 +519,13 @@ export class Trax {
504
519
  return { loc, path }
505
520
  }
506
521
 
522
+ /** Given a tile and a color, follow the line for that color to each end.
523
+ * @arg {Color} color - the color of ends of interest
524
+ * @arg { Point} loc - the location of the tile of interest
525
+ * @returns {LineEnd[]} a list of two items, each one end of the line
526
+ */
507
527
  findEnds(color, loc) {
528
+ /** @type {LineEnd[]} */
508
529
  const ends = []
509
530
  const tile = this.tileAt(loc)
510
531
  if (upColor(tile) === color) ends.push(this.follow(color, loc, 'top'))
@@ -514,6 +535,11 @@ export class Trax {
514
535
  return ends
515
536
  }
516
537
 
538
+ /** Determine if a given line ends the game.
539
+ * @arg {Point} locA - one end of the line
540
+ * @arg {Point} locB - the other end of the line
541
+ * @returns {boolean} - true if this line wins the game
542
+ */
517
543
  lineWin(locA, locB) {
518
544
  if (this.rules === 'traxloop') return false
519
545
  if (this.width > 7 && locA.distX(locB) > this.width) return true
@@ -521,6 +547,9 @@ export class Trax {
521
547
  return false
522
548
  }
523
549
 
550
+ /** Determine if the game has ended.
551
+ * @arg {Tile[]} tiles - a list of the tiles placed during the last move.
552
+ */
524
553
  checkWin(tiles) {
525
554
  /** @type {Color|false} */
526
555
  let winner = false
@@ -529,13 +558,13 @@ export class Trax {
529
558
  if (winner) continue // Skip the second player if the first has won
530
559
  for (const tile of tiles) {
531
560
  if (winner) continue // Skip the remaining tiles once we find a win
532
- const ends = this.findEnds(color, tile.loc)
533
- if (ends[0].loc.eq(ends[1].loc)) {
561
+ const [end1, end2] = this.findEnds(color, tile.loc)
562
+ if (end1.loc.eq(end2.loc)) {
534
563
  winner = color // Loop win
535
- this.path = ends[0].path // Both paths are essentially the same
536
- } else if (this.lineWin(ends[0].loc, ends[1].loc)) {
564
+ this.path = end1.path // Both paths are essentially the same
565
+ } else if (this.lineWin(end1.loc, end2.loc)) {
537
566
  winner = color // Line win, paths need to be combined
538
- this.path = [...ends[0].path.reverse(), ...ends[1].path.slice(1)]
567
+ this.path = [...end1.path.reverse(), ...end2.path.slice(1)]
539
568
  }
540
569
  }
541
570
  }
@@ -576,6 +605,11 @@ export class Trax {
576
605
  return this.dropTile(notation)
577
606
  }
578
607
 
608
+ /** Play one or more moves.
609
+ * @arg {string|string[]} moves - the list of moves, provided either as a
610
+ * space-separated string of notations, or as a list of notations. Move
611
+ * numbers are optional, but if provided will be checked for accuracy.
612
+ */
579
613
  playMoves(moves) {
580
614
  // Play one or more moves, from a string or a list of moves
581
615
  // If move numbers are included, ensure they are accurate
@@ -583,12 +617,12 @@ export class Trax {
583
617
  moves = moves.join(' ')
584
618
  }
585
619
 
586
- moves = moves.replace(/\n/g, ' ').split(/\s+/)
620
+ moves = moves.replaceAll('\n', ' ').split(/\s+/)
587
621
 
588
622
  let moveNumber = 0
589
623
  for (const move of moves) {
590
624
  if (moveNumberRegex.test(move)) {
591
- moveNumber = Number(move.replace(/\D/g, ''))
625
+ moveNumber = Number(move.replaceAll(/\D/g, ''))
592
626
  }
593
627
 
594
628
  if (notationRegex.test(move)) {
@@ -603,6 +637,9 @@ export class Trax {
603
637
  }
604
638
  }
605
639
 
640
+ /** Add the current move notation to the notation string
641
+ * @arg {string} notation - the notation of the current move
642
+ */
606
643
  updateNotation(notation) {
607
644
  this.move++
608
645
  let note = this.move + '. ' + notation
@@ -617,13 +654,13 @@ export class Trax {
617
654
  /** An array of the moves made in the game. */
618
655
  get moves() {
619
656
  return this.notation
620
- .replace(/\n/g, ' ')
657
+ .replaceAll('\n', ' ')
621
658
  .split(' ')
622
659
  .filter((n) => Boolean(n) && !n.endsWith('.'))
623
660
  }
624
661
 
625
662
  /** Drop a tile onto the board. This is a lower level call. Use play() if possible instead.
626
- * @arg {string|TileType} type - a special move, a tile type, or a notation
663
+ * @arg {string|ValidTiles} type - a special move, a tile type, or a notation
627
664
  * @arg {Point} [loc] - a location if type is a tile type
628
665
  * @arg {string|boolean} [tentative] - if truthy, the move will not be saved
629
666
  * @returns {TileDrop} an object representing the results of the drop
@@ -673,6 +710,11 @@ export class Trax {
673
710
  return { dropped, notation, valid }
674
711
  }
675
712
 
713
+ /** Symmetry helper. Rotates a move around the board in case we are trying to
714
+ * play a symmetrical rather than an exact move.
715
+ * @arg {string} move - the notation of the move to be rotated
716
+ * @returns {string[]} the four rotations of this move
717
+ */
676
718
  moveRotations(move) {
677
719
  // Symmetry helper, rotate a move around the board
678
720
  const match = move.match(notationRegex)
@@ -695,15 +737,24 @@ export class Trax {
695
737
  return [...moves]
696
738
  }
697
739
 
740
+ /** Play a provisional move if it is valid.
741
+ * @arg {string} from - the normalized encoding of the starting position
742
+ * @arg {string} to - the normalized encoding of the ending position
743
+ * @arg {string} via - the move to be used to transition
744
+ * @returns {false|string} if the provisional move is invalid: false; if the
745
+ * provisional move will never be valid for any future moves:
746
+ * 'delete-provisional'; if the provisional move is valid, the correct
747
+ * notation, which may be symmetrically adjusted as needed
748
+ */
698
749
  provisionalMove(from, to, via) {
699
750
  // Test a provisional move against the current state
700
- let rows = from.slice(1).split(':')
751
+ const lines = from.slice(1).split(':')
701
752
  let cols = 0
702
- for (const row of rows) {
703
- cols = Math.max(cols, codeRowLength(row))
753
+ for (const line of lines) {
754
+ cols = Math.max(cols, codeRowLength(line))
704
755
  }
705
756
 
706
- rows = rows.length
757
+ const rows = lines.length
707
758
  if (
708
759
  (this.width > rows && this.height > cols) ||
709
760
  (this.width > cols && this.height > rows)
@@ -726,15 +777,31 @@ export class Trax {
726
777
  return false
727
778
  }
728
779
 
780
+ /** Get an encoded representation of the current position, useful for drawing
781
+ * the board without having to do much analysis.
782
+ * @returns {string} the current position code
783
+ */
729
784
  get icon() {
730
785
  return this.positionCode()
731
786
  }
732
787
 
788
+ /** Get an encoded representation of the current position, with a set of
789
+ * tiles highlighted differently, useful for showing the effects of a move.
790
+ * @arg {TileDrop} drops - the drops of the most recent play
791
+ * @returns {string} the current position code, with drops highlighted
792
+ */
733
793
  dropsIcon(drops) {
734
794
  return this.positionCode(undefined, undefined, undefined, drops)
735
795
  }
736
796
 
737
- // Symmetry helper, draw the board from different angles
797
+ /** Symmetry helper, draw the board from different angles.
798
+ * @arg {boolean} [rightToLeft] - reverse order horizontally
799
+ * @arg {boolean} [bottomToTop] - reverse order vertically
800
+ * @arg {boolean} [rotate] - rotate the tiles by 90 degrees
801
+ * @arg {TileDrop} [drops] - the drops of the most recent play, if you want
802
+ * them highlighted
803
+ * @returns {string} an encoding of the position
804
+ */
738
805
  positionCode(rightToLeft, bottomToTop, rotate, drops) {
739
806
  const startX = rightToLeft ? this.right : this.left
740
807
  const startY = bottomToTop ? this.bottom : this.top
@@ -799,9 +866,11 @@ export class Trax {
799
866
  return code.join(':')
800
867
  }
801
868
 
869
+ /** Trax has the potential for symmetry, so this gives us the ability to
870
+ * examine horizontal, vertical, and rotational symmetry for a color.
871
+ * @returns {string} a position code that matches all symmetrical positions
872
+ */
802
873
  normalize() {
803
- // Trax has the potential for symmetry, so this gives us the ability to
804
- // examine horizontal, vertical, and rotational symmetry for a color
805
874
  let norm = 'z'
806
875
  const bools = [true, false]
807
876
  for (const rightToLeft of bools) {
@@ -816,6 +885,9 @@ export class Trax {
816
885
  return (this.color || 't').toUpperCase() + norm
817
886
  }
818
887
 
888
+ /** Get the normalized code for this position. All symmetrical positions will
889
+ * result in the same normalized code.
890
+ */
819
891
  get normalized() {
820
892
  return this.normalize()
821
893
  }
package/src/types.d.ts CHANGED
@@ -7,6 +7,21 @@
7
7
  /** Color is a single character to represent white or black. */
8
8
  type Color = 'w' | 'b'
9
9
 
10
+ /** One end of a line and the tiles taken to get there. */
11
+ type LineEnd = {
12
+ loc: Point
13
+ path: TileId[]
14
+ }
15
+
16
+ /** A notation of a move. */
17
+ type Notation = string
18
+
19
+ /** A tile type and a location determine a raw move. */
20
+ type RawMove = {
21
+ type: TileType
22
+ loc: Point
23
+ }
24
+
10
25
  /** Treat the save state as an opaque object,
11
26
  * produced by save() and fed into restore().
12
27
  */
@@ -19,7 +34,7 @@ type SaveState = {
19
34
  right: number
20
35
  top: number
21
36
  bottom: number
22
- notation: string
37
+ notation: Notation
23
38
  tiles: string
24
39
  path: string
25
40
  invalid: boolean
@@ -42,7 +57,7 @@ type Tile = {
42
57
  /** When a tile is dropped, this object represents the results. */
43
58
  type TileDrop = {
44
59
  dropped: Tile[]
45
- notation: string
60
+ notation: Notation
46
61
  valid: boolean
47
62
  }
48
63
 
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.22.0'
2
+ export const version = '1.0.0'