@slugbugblue/trax 0.11.0 → 0.13.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/src/cli.js CHANGED
@@ -21,9 +21,9 @@ import repl from 'node:repl'
21
21
 
22
22
  import YAML from 'yaml'
23
23
 
24
- import { Trax } from './engine.js'
25
- import { version } from './version.js'
26
- import * as tty from './tty.js'
24
+ import { Trax } from '@slugbugblue/trax'
25
+ import { version } from '@slugbugblue/trax/version'
26
+ import * as tty from '@slugbugblue/trax/tty'
27
27
 
28
28
  // Load all the commands as individual plugins
29
29
  import { analyzeCmd } from './cmds/analyze.js'
@@ -40,6 +40,7 @@ import { listCmd } from './cmds/list.js'
40
40
  import { newCmd } from './cmds/new.js'
41
41
  import { playCmd, tryCmd } from './cmds/play-try.js'
42
42
  import { selectCmd } from './cmds/select.js'
43
+ import { suggestCmd } from './cmds/suggest.js'
43
44
  import { undoCmd } from './cmds/undo.js'
44
45
  import { viewCmd } from './cmds/view.js'
45
46
 
@@ -88,6 +89,7 @@ const plugins = [
88
89
  playCmd,
89
90
  quitCmd,
90
91
  selectCmd,
92
+ suggestCmd,
91
93
  tryCmd,
92
94
  undoCmd,
93
95
  versionCmd,
@@ -111,7 +113,10 @@ const newId = (DATA) => {
111
113
  return String(min - 1)
112
114
  }
113
115
 
114
- // Colorize text according to simplistic and not very complete patterns
116
+ /** Colorize text according to simplistic and not very complete patterns.
117
+ * @type Colorer
118
+ */
119
+ // @ts-ignore
115
120
  const color = (text, def) => {
116
121
  let out = ''
117
122
  let quote = false
@@ -121,7 +126,7 @@ const color = (text, def) => {
121
126
  const short = (t) => tty.color(t, CLI.COLORS.short) + c()
122
127
  for (const char of text) {
123
128
  switch (char) {
124
- case '"':
129
+ case '"': {
125
130
  if (quote) {
126
131
  colors.pop()
127
132
  } else {
@@ -131,31 +136,45 @@ const color = (text, def) => {
131
136
  out += c('')
132
137
  quote = !quote
133
138
  break
134
- case '#':
139
+ }
140
+
141
+ case '#': {
135
142
  id = true
136
143
  colors.push(CLI.COLORS.id)
137
144
  out += c('#')
138
145
  break
139
- case '|':
146
+ }
147
+
148
+ case '|': {
140
149
  out += short('|')
141
150
  break
142
- case '[':
151
+ }
152
+
153
+ case '[': {
143
154
  colors.push(CLI.COLORS.optional)
144
155
  out += short('[')
145
156
  break
146
- case ']':
157
+ }
158
+
159
+ case ']': {
147
160
  colors.pop()
148
161
  out += short(']')
149
162
  break
150
- case '<':
163
+ }
164
+
165
+ case '<': {
151
166
  colors.push(CLI.COLORS.variable)
152
167
  out += c('')
153
168
  break
154
- case '>':
169
+ }
170
+
171
+ case '>': {
155
172
  colors.pop()
156
173
  out += c('')
157
174
  break
158
- default:
175
+ }
176
+
177
+ default: {
159
178
  if (id && !/[\da-z]/.test(char)) {
160
179
  id = false
161
180
  colors.pop()
@@ -163,6 +182,7 @@ const color = (text, def) => {
163
182
  } else {
164
183
  out += char
165
184
  }
185
+ }
166
186
  }
167
187
  }
168
188
 
@@ -170,7 +190,7 @@ const color = (text, def) => {
170
190
  }
171
191
 
172
192
  // Internal representations of files
173
- const CONFIG = { id: 0 }
193
+ const CONFIG = { id: '' }
174
194
  const DATA = { games: {} }
175
195
 
176
196
  // CLI context object for plugins
@@ -550,11 +570,34 @@ const completer = (line) => {
550
570
  return [completions, match]
551
571
  }
552
572
 
573
+ const ambiguousSuggestions = (cmd) => {
574
+ const maybe = new Set()
575
+ for (const alias of cmd) {
576
+ if (alias.startsWith(CLI.CMD)) maybe.add(alias)
577
+
578
+ let alts = commands[alias].alt
579
+ alts = is(alts, 'arr') ? alts : [alts || '']
580
+
581
+ for (const alt of alts) {
582
+ if (alt.startsWith(CLI.CMD)) maybe.add(alt)
583
+ }
584
+ }
585
+
586
+ return maybe
587
+ }
588
+
553
589
  const evaluate = async (command) => {
554
590
  const [CMD, ...words] = command.split(/\s+/).filter(Boolean)
555
- const cmdLower = CMD.toLowerCase()
591
+ const cmdLower = CMD?.toLowerCase() || ''
556
592
  CLI.CMD = cmdLower
557
- const cmd = abbreviations[cmdLower]
593
+ let cmd = abbreviations[cmdLower]
594
+
595
+ // If we are not in a REPL, let's remove hidden commands
596
+ if (!CLI.repl && is(cmd, 'arr')) {
597
+ cmd = cmd.filter((c) => !commands[c].hide)
598
+ if (cmd.length === 1) cmd = cmd[0]
599
+ }
600
+
558
601
  if (is(cmd, 'str')) {
559
602
  if (words.length > 0 && abbreviations[words[0].toLowerCase()] === 'help') {
560
603
  commands.help.fn(CLI, cmd)
@@ -563,18 +606,7 @@ const evaluate = async (command) => {
563
606
  }
564
607
  } else if (is(cmd, 'arr')) {
565
608
  CLI.error('Ambiguous command. Perhaps you meant one of the following:')
566
- const maybe = new Set()
567
-
568
- for (const alias of cmd) {
569
- if (alias.startsWith(cmdLower)) maybe.add(alias)
570
-
571
- let alts = commands[alias].alt
572
- alts = is(alts, 'arr') ? alts : [alts || '']
573
-
574
- for (const alt of alts) {
575
- if (alt.startsWith(cmdLower)) maybe.add(alt)
576
- }
577
- }
609
+ const maybe = ambiguousSuggestions(cmd)
578
610
 
579
611
  if (maybe.size > 0) {
580
612
  for (const alias of [...maybe].sort()) {
@@ -593,8 +625,6 @@ const evaluate = async (command) => {
593
625
  CLI.error('Unknown command. Type "help" for a list of valid commands.')
594
626
  }
595
627
  }
596
-
597
- return undefined
598
628
  }
599
629
 
600
630
  const main = async () => {
@@ -630,4 +660,4 @@ const main = async () => {
630
660
  }
631
661
  }
632
662
 
633
- main()
663
+ await main()
@@ -15,8 +15,8 @@
15
15
 
16
16
  // CLI analyze command
17
17
 
18
- import { analyze } from '../analyst.js'
19
- import { Trax } from '../engine.js'
18
+ import { analyze } from '@slugbugblue/trax/analyst'
19
+ import { Trax } from '@slugbugblue/trax'
20
20
 
21
21
  const notationRx = /^[@a-z]+\d+[bps/\\+]$/i
22
22
 
@@ -40,7 +40,7 @@ const threatNames = {
40
40
  const listThreats = (threats, detailed) => {
41
41
  const summary = []
42
42
  for (const level of Object.keys(threats || {})) {
43
- const name = (threatNames[level] || `stage-${level} threats`) + ': '
43
+ const name = (threatNames[level] || `${level}-stage threats`) + ': '
44
44
  const items = []
45
45
  if (detailed) {
46
46
  for (const item of threats[level]) {
@@ -21,7 +21,7 @@ import process from 'node:process'
21
21
  import envPaths from 'env-paths'
22
22
  import makeDir from 'make-dir'
23
23
 
24
- import { Trax } from '../engine.js'
24
+ import { Trax } from '@slugbugblue/trax'
25
25
 
26
26
  const paths = envPaths('trax', { suffix: '' })
27
27
  const PATHS = {}
package/src/cmds/list.js CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  // CLI list command
17
17
 
18
- import { Trax } from '../engine.js'
18
+ import { Trax } from '@slugbugblue/trax'
19
19
 
20
20
  export const listCmd = {
21
21
  name: 'list',
@@ -41,7 +41,7 @@ export const listCmd = {
41
41
  }
42
42
 
43
43
  const plural = (n, noun, nouns) =>
44
- n + ' ' + (Math.abs(n) === 1 ? noun : nouns ? nouns : noun + 's')
44
+ n + ' ' + (Math.abs(n) === 1 ? noun : nouns || noun + 's')
45
45
 
46
46
  const alphaSort = (a, b) => {
47
47
  a = String(a).toLowerCase()
package/src/cmds/new.js CHANGED
@@ -58,13 +58,13 @@ const pairs = [
58
58
  ['Mario', 'Luigi'],
59
59
  ['Marlin', 'Dory'],
60
60
  ['Mary-Kate', 'Ashley'],
61
- ['Micky', 'Minnie'],
61
+ ['Mickey', 'Minnie'],
62
62
  ['Miss Piggy', 'Kermit'],
63
63
  ['Mork', 'Mindy'],
64
64
  ['Obi-Wan', 'Anakin'],
65
65
  ['Pan', 'Hook'],
66
66
  ['Phineas', 'Ferb'],
67
- ['Player 1', 'Player 2'],
67
+ ['player 1', 'player 2'],
68
68
  ['p1', 'p2'],
69
69
  ['R2-D2', 'C-3PO'],
70
70
  ['Ren', 'Stimpy'],
@@ -89,7 +89,9 @@ const pairs = [
89
89
  ]
90
90
 
91
91
  const pairUp = () => {
92
- return pairs[Math.floor(Math.random() * pairs.length)]
92
+ const duo = pairs[Math.floor(Math.random() * pairs.length)]
93
+ if (duo[0].toLowerCase() !== duo[0] && Math.random() < 0.5) duo.reverse()
94
+ return duo
93
95
  }
94
96
 
95
97
  newCmd.fn = (CLI, variant, ...names) => {
@@ -0,0 +1,52 @@
1
+ /* Copyright 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.
14
+ */
15
+
16
+ // CLI suggest command
17
+
18
+ import { suggest } from '@slugbugblue/trax/analyst'
19
+
20
+ export const suggestCmd = {
21
+ name: 'suggest',
22
+ alt: ['bot'],
23
+ desc: 'suggest a move',
24
+ help: ['Suggest a move in the current game.'],
25
+ }
26
+
27
+ suggestCmd.fn = (CLI, debug) => {
28
+ if (!String(CLI.GAME?.id)) {
29
+ return CLI.error('No active game. Type "new" to start a game.')
30
+ }
31
+
32
+ const game = CLI.TRAX
33
+ const suggestion = suggest(game)
34
+
35
+ if (!suggestion.best) return
36
+
37
+ CLI.out(
38
+ CLI.bubble(
39
+ game.turn === '1' ? 'wh' : 'bh',
40
+ String(game.move + 1) + '. ' + suggestion.pick.move,
41
+ ),
42
+ )
43
+
44
+ if ('debug'.startsWith(debug)) {
45
+ const m = []
46
+ for (const move of suggestion.options) {
47
+ m.push(move.move + ' ' + move.score)
48
+ }
49
+
50
+ CLI.out('[' + m.join(', ') + '] ' + suggestion.ms + 'ms')
51
+ }
52
+ }