@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/CHANGELOG.md +20 -0
- package/README.md +46 -7
- package/docs/analyst.md +44 -2
- package/docs/point.md +2 -1
- package/package.json +6 -7
- package/src/analyst.js +163 -23
- package/src/cli.js +60 -30
- package/src/cmds/analyze.js +3 -3
- package/src/cmds/import-export.js +1 -1
- package/src/cmds/list.js +2 -2
- package/src/cmds/new.js +5 -3
- package/src/cmds/suggest.js +52 -0
- package/src/engine.js +167 -29
- package/src/point.js +63 -18
- package/src/puzzles.js +798 -0
- package/src/threats.js +31 -32
- package/src/tty.js +30 -15
- package/src/types.d.ts +105 -0
- package/src/version.js +1 -1
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 '
|
|
25
|
-
import { version } from '
|
|
26
|
-
import * as tty from '
|
|
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
|
-
|
|
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
|
-
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
case '#': {
|
|
135
142
|
id = true
|
|
136
143
|
colors.push(CLI.COLORS.id)
|
|
137
144
|
out += c('#')
|
|
138
145
|
break
|
|
139
|
-
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
case '|': {
|
|
140
149
|
out += short('|')
|
|
141
150
|
break
|
|
142
|
-
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
case '[': {
|
|
143
154
|
colors.push(CLI.COLORS.optional)
|
|
144
155
|
out += short('[')
|
|
145
156
|
break
|
|
146
|
-
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
case ']': {
|
|
147
160
|
colors.pop()
|
|
148
161
|
out += short(']')
|
|
149
162
|
break
|
|
150
|
-
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
case '<': {
|
|
151
166
|
colors.push(CLI.COLORS.variable)
|
|
152
167
|
out += c('')
|
|
153
168
|
break
|
|
154
|
-
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
case '>': {
|
|
155
172
|
colors.pop()
|
|
156
173
|
out += c('')
|
|
157
174
|
break
|
|
158
|
-
|
|
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:
|
|
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
|
|
591
|
+
const cmdLower = CMD?.toLowerCase() || ''
|
|
556
592
|
CLI.CMD = cmdLower
|
|
557
|
-
|
|
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 =
|
|
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()
|
package/src/cmds/analyze.js
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
// CLI analyze command
|
|
17
17
|
|
|
18
|
-
import { analyze } from '
|
|
19
|
-
import { Trax } from '
|
|
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] ||
|
|
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 '
|
|
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 '
|
|
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
|
|
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
|
-
['
|
|
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
|
-
['
|
|
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
|
-
|
|
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
|
+
}
|