@slugbugblue/trax 0.19.0 → 0.21.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/.dockerignore +11 -0
- package/CHANGELOG.md +19 -0
- package/CONTRIBUTING.md +113 -0
- package/Dockerfile +10 -5
- package/LICENSE +1 -1
- package/README.md +54 -15
- package/docs/analyst.md +26 -6
- package/package.json +13 -3
- package/src/analyst.js +158 -84
- package/src/cmds/import-export.js +9 -2
- package/src/cmds/notes.js +3 -0
- package/src/cmds/play-try.js +8 -2
- package/src/cmds/puzzles.js +42 -12
- package/src/cmds/suggest.js +5 -2
- package/src/cmds/view.js +5 -1
- package/src/puzzles.js +314 -16
- package/src/threats.js +1 -1
- package/src/types.d.ts +10 -2
- package/src/utils.js +22 -0
- package/src/version.js +1 -1
- package/.husky/pre-commit +0 -4
- package/benchmark/benchmarks.json +0 -60
- package/benchmark/puzzles.bench.js +0 -190
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/** Let's try to solve the puzzles
|
|
4
|
-
* @copyright 2023
|
|
5
|
-
* @author Chad Transtrum <chad@transtrum.net>
|
|
6
|
-
* @license Apache-2.0
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from 'node:fs'
|
|
10
|
-
import process from 'node:process'
|
|
11
|
-
import { fileURLToPath } from 'node:url'
|
|
12
|
-
|
|
13
|
-
import { Trax } from '@slugbugblue/trax'
|
|
14
|
-
import { suggest } from '@slugbugblue/trax/analyst'
|
|
15
|
-
import { puzzles } from '@slugbugblue/trax/puzzles'
|
|
16
|
-
import { version } from '@slugbugblue/trax/version'
|
|
17
|
-
|
|
18
|
-
import * as tty from '@slugbugblue/trax/tty'
|
|
19
|
-
|
|
20
|
-
let STUMPED = false
|
|
21
|
-
const FINAL = {}
|
|
22
|
-
|
|
23
|
-
const record = (name, data) => {
|
|
24
|
-
FINAL['puzzles.firstmove.' + name] = data
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const timeString = (ms) => {
|
|
28
|
-
if (ms < 1000) {
|
|
29
|
-
return (ms === Math.floor(ms) ? String(ms) : ms.toFixed(2)) + ' ms'
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
ms /= 1000
|
|
33
|
-
if (ms < 60) return ms.toFixed(3) + ' sec'
|
|
34
|
-
|
|
35
|
-
const min = Math.floor(ms / 60)
|
|
36
|
-
ms -= min * 60
|
|
37
|
-
return String(min) + ' min ' + ms.toFixed(3) + ' sec'
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const time = (ms) => timeString(Date.now() - ms)
|
|
41
|
-
|
|
42
|
-
const out = (messages) => {
|
|
43
|
-
tty.outty(messages.join(' '))
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const section = (title) => {
|
|
47
|
-
out([tty.color('===', 250), tty.color(title, 39), tty.color('===', 250)])
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const success = (puzzle, ms) => {
|
|
51
|
-
out([tty.color('✓', 35), tty.color(puzzle, 250), tty.color(ms, 240)])
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const puzzled = (puzzle, move) => {
|
|
55
|
-
tty.display(new Trax(puzzle.game, puzzle.notation))
|
|
56
|
-
tty.outty('Puzzled by ' + puzzle.id, 205)
|
|
57
|
-
out([
|
|
58
|
-
tty.color(' played', 250),
|
|
59
|
-
tty.color(move, 127),
|
|
60
|
-
tty.color('instead of', 250),
|
|
61
|
-
tty.color(puzzle.hint || puzzle.hints.join(' '), 39),
|
|
62
|
-
])
|
|
63
|
-
STUMPED = true
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const finalize = (name, ms, count) => {
|
|
67
|
-
out([
|
|
68
|
-
tty.color('Finished', 195),
|
|
69
|
-
tty.color(String(count) + ' ' + name + ' puzzles', 123),
|
|
70
|
-
tty.color('in ' + time(ms), 195),
|
|
71
|
-
])
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const solvePuzzleLevel = (name, level) => {
|
|
75
|
-
if (STUMPED) return 0
|
|
76
|
-
section(name + ' puzzles')
|
|
77
|
-
name = name.toLowerCase()
|
|
78
|
-
let count = 0
|
|
79
|
-
const begin = Date.now()
|
|
80
|
-
for (const puzzle of puzzles) {
|
|
81
|
-
if (puzzle.level !== level) continue
|
|
82
|
-
const start = Date.now()
|
|
83
|
-
const game = new Trax(puzzle.game, puzzle.notation)
|
|
84
|
-
const { move } = suggest(game).pick
|
|
85
|
-
const solved = puzzle.hint
|
|
86
|
-
? move === puzzle.hint
|
|
87
|
-
: puzzle.hints
|
|
88
|
-
? puzzle.hints.includes(move)
|
|
89
|
-
: false
|
|
90
|
-
if (solved) {
|
|
91
|
-
success(puzzle.id, time(start))
|
|
92
|
-
count++
|
|
93
|
-
record(name, { count, ms: Date.now() - begin })
|
|
94
|
-
} else {
|
|
95
|
-
puzzled(puzzle, move)
|
|
96
|
-
process.exitCode = level + 1
|
|
97
|
-
return count
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
finalize(name, begin, count)
|
|
102
|
-
return count
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Gather the benchmark data
|
|
106
|
-
let total = 0
|
|
107
|
-
const timer = Date.now()
|
|
108
|
-
total += solvePuzzleLevel('Tutorial', 0)
|
|
109
|
-
total += solvePuzzleLevel('Beginner', 1)
|
|
110
|
-
total += solvePuzzleLevel('Tough', 2)
|
|
111
|
-
total += solvePuzzleLevel('Advanced', 3)
|
|
112
|
-
total += solvePuzzleLevel('Genius', 4)
|
|
113
|
-
|
|
114
|
-
record('total', { count: total, ms: Date.now() - timer })
|
|
115
|
-
tty.outty(
|
|
116
|
-
'Benchmark: ' + total + ' puzzles in ' + time(timer),
|
|
117
|
-
STUMPED ? 205 : 35,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
tty.outty()
|
|
121
|
-
|
|
122
|
-
// Load the previous benchmarks
|
|
123
|
-
const FILE = new URL('benchmarks.json', import.meta.url)
|
|
124
|
-
let CONTENTS
|
|
125
|
-
try {
|
|
126
|
-
CONTENTS = fs.readFileSync(FILE, 'utf8')
|
|
127
|
-
} catch {
|
|
128
|
-
CONTENTS = '{}'
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const BENCHMARK = JSON.parse(CONTENTS)
|
|
132
|
-
|
|
133
|
-
const PREV = Object.values(BENCHMARK).pop() || {}
|
|
134
|
-
|
|
135
|
-
// Report the benchmark data
|
|
136
|
-
const size = [0, 0, 0, 0, 0]
|
|
137
|
-
const REPORT = []
|
|
138
|
-
for (const name of Object.keys(FINAL)) {
|
|
139
|
-
const report = [
|
|
140
|
-
name,
|
|
141
|
-
String(FINAL[name].count) + ' puzzles',
|
|
142
|
-
'in ' + timeString(FINAL[name].ms),
|
|
143
|
-
'avg ' + timeString(FINAL[name].ms / FINAL[name].count),
|
|
144
|
-
]
|
|
145
|
-
if (PREV[name]) {
|
|
146
|
-
const diff =
|
|
147
|
-
FINAL[name].ms / FINAL[name].count - PREV[name].ms / PREV[name].count
|
|
148
|
-
if (diff <= 0) {
|
|
149
|
-
report.push('-' + timeString(Math.abs(diff)), '35')
|
|
150
|
-
} else {
|
|
151
|
-
report.push('+' + timeString(diff), '208')
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
report.push('new', '250')
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
size[0] = Math.max(size[0], report[0].length)
|
|
158
|
-
size[1] = Math.max(size[1], report[1].length)
|
|
159
|
-
size[2] = Math.max(size[2], report[2].length)
|
|
160
|
-
size[3] = Math.max(size[3], report[3].length)
|
|
161
|
-
size[4] = Math.max(size[4], report[4].length)
|
|
162
|
-
REPORT.push(report)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
for (const line of REPORT) {
|
|
166
|
-
out([
|
|
167
|
-
tty.color(line[0].padEnd(size[0]), 39),
|
|
168
|
-
tty.color(line[1].padStart(size[1]), 123),
|
|
169
|
-
tty.color(line[2].padStart(size[2]), 195),
|
|
170
|
-
tty.color(line[3].padEnd(size[3]), 245),
|
|
171
|
-
tty.color(line[4].padStart(size[4]), Number(line[5])),
|
|
172
|
-
])
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
tty.outty()
|
|
176
|
-
|
|
177
|
-
// Save the benchmark data to a file
|
|
178
|
-
if (BENCHMARK[version]) {
|
|
179
|
-
tty.outty('Not overwriting existing benchmark data')
|
|
180
|
-
} else {
|
|
181
|
-
BENCHMARK[version] = FINAL
|
|
182
|
-
try {
|
|
183
|
-
fs.writeFileSync(FILE, JSON.stringify(BENCHMARK, null, 2), 'utf8')
|
|
184
|
-
const filename = fileURLToPath(FILE)
|
|
185
|
-
tty.outty('Saved benchmark data to ' + tty.color(filename, 74))
|
|
186
|
-
} catch (error) {
|
|
187
|
-
tty.outty('Error saving benchmark data', 205)
|
|
188
|
-
tty.outty(String(error.code) + ' ' + error.message, 127)
|
|
189
|
-
}
|
|
190
|
-
}
|