@slugbugblue/trax 0.16.0 → 0.17.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,12 +1,16 @@
1
1
  # @slugbugblue/trax
2
2
 
3
+ ## 0.17.0 - 2022-12-29
4
+
5
+ - The `import` CLI command now loads comments from a .trx file
6
+
3
7
  ## 0.16.0 - 2022-12-27
4
8
 
5
9
  - Added a `notes` command to the CLI to add comments to a game
6
10
 
7
- - can be used as a text filter by the `list` command
8
- - will be displayed by the `view` command
9
- - will be included in the output generated by the `export` command
11
+ - all comments are included in the output generated by the `export` command
12
+ - the latest comment can be used as a text filter by the `list` command
13
+ - the latest comment is displayed by the `view` and `list` commands
10
14
 
11
15
  - Minor analysis improvements
12
16
 
package/README.md CHANGED
@@ -13,7 +13,7 @@ Trax is a two-player boardless board game invented by David Smith. For more
13
13
  information about Trax, including its [rules][traxrules] and
14
14
  [history][traxhistory], see the official website at [traxgame.com][traxgame].
15
15
 
16
- This goal of this javascript project is to allow Trax to be played
16
+ The goal of this javascript project is to allow Trax to be played
17
17
  programmatically. It can be used as an engine in a web browser, for example at
18
18
  [slugbugblue.com][sbb]; or in nodejs, for example by using the `trax` CLI
19
19
  provided as part of this project.
@@ -27,7 +27,7 @@ changes to the engine should meet the same standard.
27
27
  # Install into a javascript project
28
28
  npm install @slugbugblue/trax
29
29
 
30
- # Or install globally to use the trax CLI
30
+ # Or install globally to use the trax CLI more easily
31
31
  npm install -g @slugbugblue/trax
32
32
  ```
33
33
 
@@ -43,11 +43,12 @@ avoid worries about the need to escape certain characters from the shell, and it
43
43
  can be easier to run multiple commands back to back.
44
44
 
45
45
  Help is available for the CLI through the use of the `help` command. Type
46
- `trax help` to see a list of the available commands, and `trax help play` to see
47
- information about the `play` command. Aliases and abbreviations are provided to
48
- minimize typing and to try to avoid the requirement of memorizing the commands.
49
- For example, the `list` command has an alias of `ls`, and the `select` command
50
- can be activated using `#`.
46
+ `trax help` (or `npx trax help` with a local installation, or simply `help` if
47
+ run interactively) to see a list of the available commands, and `trax help play`
48
+ to see information about the `play` command. Aliases and abbreviations are
49
+ provided to minimize typing and to try to avoid the requirement of memorizing
50
+ the commands. For example, the `list` command has an alias of `ls`, and the
51
+ `select` command can be activated using `#`.
51
52
 
52
53
  When you first launch the CLI, you can create a new game using the `new`
53
54
  command, and then enter moves for that game using `play`. Since `play` is likely
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slugbugblue/trax",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Trax game engine and friends",
5
5
  "keywords": [
6
6
  "trax",
@@ -137,6 +137,79 @@ export const exportCmd = {
137
137
  ],
138
138
  }
139
139
 
140
+ /** Interpret the rules string of a .trx file.
141
+ * @arg {string} r - rules string in lower case
142
+ * @returns {TraxVariant}
143
+ */
144
+ const getRules = (r) =>
145
+ r.includes('loop') ? 'traxloop' : r.includes('8x8') ? 'trax8' : 'trax'
146
+
147
+ /** Decode a .trx formatted string into a game, preserving players and comments.
148
+ * @arg {any} CLI - the CLI object
149
+ * @arg {string} content - the contents of the .trx file
150
+ * @returns {boolean} - true if a game was created
151
+ */
152
+ const interpretFile = (CLI, content) => {
153
+ let rules
154
+ let trax
155
+ let players = []
156
+ const moves = []
157
+ const comments = []
158
+ for (const line of content.split('\n')) {
159
+ if (!line) continue
160
+ const [ln, ...comment] = line.split(/[#;]/)
161
+ if (ln) {
162
+ const lower = ln.toLowerCase()
163
+ if (!rules && lower.includes('trax')) {
164
+ rules = getRules(lower)
165
+ } else if (players.length === 0 && lower.includes(' vs ')) {
166
+ players = ln.split(' ').filter(Boolean)
167
+ } else {
168
+ moves.push(...ln.split(' '))
169
+ }
170
+ }
171
+
172
+ if (!trax && rules && (players.length > 0 || moves.length > 0)) {
173
+ trax = new Trax(rules)
174
+ }
175
+
176
+ if (moves.length > 0) {
177
+ if (!trax) return false
178
+ if (trax.gameOver) {
179
+ comments.push({
180
+ note: moves.map((c) => c.trim()).join(' '),
181
+ move: trax.move,
182
+ })
183
+ } else {
184
+ trax.playMoves(moves.filter(Boolean))
185
+ moves.splice(0, moves.length)
186
+ }
187
+ }
188
+
189
+ if (comment.length > 0) {
190
+ const note = comment.map((c) => c.trim()).join(' ')
191
+ if (!/^@slugbugblue\/trax cli\.js v/.test(note)) {
192
+ comments.push({
193
+ note,
194
+ move: trax?.move || 0,
195
+ })
196
+ }
197
+ }
198
+ }
199
+
200
+ if (!trax) return false
201
+
202
+ CLI.do('new', rules, ...players)
203
+ CLI.GAMES[String(CLI.GAME.id)].notes = comments
204
+ if (trax.moves.length > 0) {
205
+ CLI.do('play', ...trax.moves)
206
+ } else {
207
+ CLI.save()
208
+ }
209
+
210
+ return true
211
+ }
212
+
140
213
  importCmd.fn = async (CLI, filename) => {
141
214
  cli = CLI
142
215
  if (!filename) {
@@ -156,35 +229,11 @@ importCmd.fn = async (CLI, filename) => {
156
229
  }
157
230
  }
158
231
 
159
- if (!content) CLI.error(fsError.toString())
232
+ if (!content) return CLI.error(fsError.toString())
160
233
  }
161
234
 
162
- if (content) {
163
- let game
164
- let players = []
165
- const moves = []
166
- for (const line of content.split('\n')) {
167
- if (!line) continue
168
- const ln = line.split(/[#;]/)[0]
169
- if (!ln) continue
170
- const lower = ln.toLowerCase()
171
- if (!game && lower.includes('trax')) {
172
- game = 'trax'
173
- if (lower.includes('loop')) game = 'traxloop'
174
- if (lower.includes('8x8')) game = 'trax8'
175
- } else if (players.length === 0 && lower.includes(' vs ')) {
176
- players = ln.split(' ').filter(Boolean)
177
- } else {
178
- moves.push(...ln.split(' '))
179
- }
180
- }
181
-
182
- if (game) {
183
- CLI.do('new', game, ...players)
184
- CLI.do('play', ...moves.filter(Boolean))
185
- } else {
186
- CLI.error(filename + ' does not appear to be formatted correctly.')
187
- }
235
+ if (!interpretFile(CLI, content)) {
236
+ CLI.error(filename + ' does not appear to be formatted correctly.')
188
237
  }
189
238
  }
190
239
 
@@ -204,6 +253,15 @@ const gameNotes = (move, notes) => {
204
253
  )
205
254
  }
206
255
 
256
+ /** Join two strings together with a character with the appropriate joining
257
+ * character depending on the length of the strings.
258
+ * @arg {string} a - the first string
259
+ * @arg {string} b - the second string
260
+ * @returns {string} - both strings correctly joined
261
+ */
262
+ const join = (a, b) =>
263
+ a.length === 0 ? b : a.length + b.length > 78 ? a + '\n' + b : a + ' ' + b
264
+
207
265
  /** Interleave the game moves with any notes, formatted for the export file.
208
266
  * @arg {string[]} moves - an array of move notations
209
267
  * @arg {GameNotes} notes? - The notes for a game
@@ -216,9 +274,7 @@ const interleaveNotation = (moves, notes) => {
216
274
  for (const move of moves) {
217
275
  const comments = gameNotes(moveNumber, notes)
218
276
  if (comments) {
219
- const [lengthA, lengthC] = [annotation.length, comments.length]
220
- const space = lengthA > 0 ? (lengthA + lengthC < 78 ? ' ' : '\n') : ''
221
- content += space + annotation + comments
277
+ content += join(annotation, comments)
222
278
  annotation = ''
223
279
  }
224
280
 
@@ -234,9 +290,7 @@ const interleaveNotation = (moves, notes) => {
234
290
 
235
291
  const comments = gameNotes(moveNumber, notes)
236
292
  if (comments) {
237
- const [lengthA, lengthC] = [annotation.length, comments.length]
238
- const space = lengthA > 0 ? (lengthA + lengthC < 78 ? ' ' : '\n') : ''
239
- content += space + annotation + comments
293
+ content += join(annotation, comments)
240
294
  annotation = ''
241
295
  }
242
296
 
@@ -142,7 +142,7 @@ playCmd.fn = (CLI, ...moves) => {
142
142
  }
143
143
 
144
144
  tryCmd.fn = (CLI, move) => {
145
- if (move.length === 0) {
145
+ if (!move || move.length === 0) {
146
146
  CLI.error('You must provide a move.')
147
147
  return CLI.do('help', 'try')
148
148
  }
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.16.0'
2
+ export const version = '0.17.0'