@slugbugblue/trax 0.11.0 → 0.12.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,10 @@
1
1
  # @slugbugblue/trax
2
2
 
3
+ ## 0.12.0 - 2022-10-28
4
+
5
+ - `point.in()` now accepts any two opposing corners to define the bounding box
6
+ - Fleshing out testing and debugging of `analyst.js`
7
+
3
8
  ## 0.11.0 - 2022-09-22
4
9
 
5
10
  - Breaking change: Refactor `play()` and `playMove()` into a single function
package/docs/point.md CHANGED
@@ -89,9 +89,10 @@ Returns the distance between two points. This will never be a negative number.
89
89
  Returns `true` if the given point or point-like object is at the same location
90
90
  as the existing point.
91
91
 
92
- ##### `in(topLeft, bottomRight)`
92
+ ##### `in(a, b)`
93
93
 
94
94
  Pass in two points (or point-like objects) to define a rectangular bounding box.
95
+ Each point represents opposite corners of the box.
95
96
 
96
97
  Returns `true` if the calling point is located inside the box or along any of
97
98
  its edges (ie, both bounding points are considered "inside").
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slugbugblue/trax",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Trax game engine and friends",
5
5
  "keywords": [
6
6
  "trax",
package/src/analyst.js CHANGED
@@ -21,6 +21,15 @@ import { Trax } from '@slugbugblue/trax'
21
21
  import { threats } from '@slugbugblue/trax/threats.js'
22
22
 
23
23
  // Helpers
24
+ const id = () => {
25
+ const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
26
+ let code = ''
27
+ while (code.length < 6) {
28
+ code += chars[Math.floor(Math.random() * chars.length)]
29
+ }
30
+
31
+ return 'analysis-' + code
32
+ }
24
33
 
25
34
  // Make parseInt Array.map()-friendly
26
35
  const int = (s) => Number.parseInt(s, 10)
@@ -163,24 +172,28 @@ const threatCount = (from) =>
163
172
 
164
173
  // Analysis class
165
174
  export const Analysis = class {
175
+ #rules = 'trax'
176
+ #id = 'analysis'
177
+ #moves = []
178
+ #notation = ''
166
179
  #edge = undefined
167
180
  #game = {}
168
- #move = 0
169
- #save = {}
170
181
  #scores = {}
171
182
  #threats = undefined
172
183
 
173
184
  constructor(game) {
174
185
  // Make a copy of the game
175
- this.#game = game
176
- this.#save = game.save()
177
- this.#move = game.move
186
+ this.#rules = game.rules
187
+ this.#moves = game.moves
188
+ this.#id = id()
189
+ this.#notation = game.notation
190
+ this.#game = new Trax(this.#rules, this.#moves, this.#id)
178
191
  }
179
192
 
180
193
  get game() {
181
194
  // Return a fresh copy of the game as presented on init
182
- if (this.#game.move !== this.#move) {
183
- this.#game = new Trax().restore(this.#save)
195
+ if (this.#game.notation !== this.#notation) {
196
+ this.#game = new Trax(this.#rules, this.#moves, this.#id)
184
197
  }
185
198
 
186
199
  return this.#game
@@ -217,7 +230,7 @@ export const Analysis = class {
217
230
 
218
231
  for (const dir of Point.dirs) {
219
232
  let look = loc.dir(dir)
220
- while (look.in(topLeft, bottomRight) && !game.tileAt(loc)) {
233
+ while (look.in(topLeft, bottomRight) && !game.tileAt(look)) {
221
234
  look = look.dir(dir)
222
235
  }
223
236
 
@@ -287,7 +300,7 @@ export const Analysis = class {
287
300
  // c = in a cave but has no restrictions (converted to n in edge)
288
301
  // C = a cave where some moves are not allowed (converted to c in edge)
289
302
  // n = normal space
290
- // x = we cannot play here
303
+ // x = no tile can be played here
291
304
  spaceType(loc) {
292
305
  const { game } = this
293
306
 
@@ -300,13 +313,25 @@ export const Analysis = class {
300
313
  }
301
314
 
302
315
  // Check to see if we have an invalid move
316
+ let someValid = false
317
+ let someInvalid = false
303
318
  for (const tile of game.possibleTiles(loc)) {
304
319
  const drop = game.dropTile(tile, loc, 'tentative')
305
- if (!drop.valid) {
306
- return 'C'
320
+ if (drop.valid) {
321
+ someValid = true
322
+ } else {
323
+ someInvalid = true
307
324
  }
308
325
  }
309
326
 
327
+ if (!someValid) {
328
+ return 'x'
329
+ }
330
+
331
+ if (someInvalid) {
332
+ return 'C'
333
+ }
334
+
310
335
  return 'c'
311
336
  }
312
337
 
@@ -390,6 +415,37 @@ export const Analysis = class {
390
415
 
391
416
  return matches
392
417
  }
418
+
419
+ // Make a reasonable inspect representation of the analysis
420
+ [Symbol.for('nodejs.util.inspect.custom')](depth, options, inspect) {
421
+ const sub = (object) => inspect(object, options).replace(/\n/g, '\n ')
422
+ const style = options.stylize
423
+ if (depth < 0) return style('<Analysis>', 'special')
424
+ if (options.depth !== null) options.depth -= 1
425
+ const tbd = style('tbd', 'undefined')
426
+ let custom = style('Analysis', 'special') + ' {\n'
427
+ custom += ' game: ' + style(this.#rules, 'special')
428
+ custom += '(' + style(this.#moves, 'string') + '),\n'
429
+ custom += ' edge: '
430
+ if (this.#edge) {
431
+ custom += '{\n w: ' + style(this.#edge.w, 'string')
432
+ custom += ',\n b: ' + style(this.#edge.b, 'string')
433
+ custom += ',\n edge: ' + style('[rawEdge]', 'special')
434
+ custom += '\n }'
435
+ } else {
436
+ custom += tbd
437
+ }
438
+
439
+ custom += ',\n threats: '
440
+ custom += this.#threats ? sub(this.#threats) : tbd
441
+ custom += ',\n scores: '
442
+ custom += 'w' in this.#scores ? sub(this.#scores) : tbd
443
+ custom += ',\n score: '
444
+ const score = 'w' in this.#scores ? this.score : null
445
+ custom += score === null ? tbd : style(score, 'number')
446
+
447
+ return custom + '\n}'
448
+ }
393
449
  }
394
450
 
395
451
  export const analyze = (game) => new Analysis(game)
@@ -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]) {
package/src/point.js CHANGED
@@ -15,6 +15,12 @@
15
15
 
16
16
  const is = (v, s) => (typeof v).slice(0, 3) === s // Sigh
17
17
 
18
+ const between = (end1, mid, end2) => {
19
+ const low = Math.min(end1, end2)
20
+ const high = Math.max(end1, end2)
21
+ return low <= mid && mid <= high
22
+ }
23
+
18
24
  // Are you tired of doing x,y movements in multiple statements? I am.
19
25
 
20
26
  // Point: an immutable two-dimensional location (x,y)
@@ -102,13 +108,10 @@ export const Point = class {
102
108
  return this.#x === that.x && this.#y === that.y
103
109
  }
104
110
 
105
- in(topLeft, bottomRight) {
106
- return (
107
- this.x >= topLeft.x &&
108
- this.x <= bottomRight.x &&
109
- this.y >= topLeft.y &&
110
- this.y <= bottomRight.y
111
- )
111
+ in(a, b) {
112
+ // Allow for the bounding points to be passed in any order
113
+ // as long as they are opposite corners:
114
+ return between(a.x, this.x, b.x) && between(a.y, this.y, b.y)
112
115
  }
113
116
 
114
117
  // Play nicely with the rest of the world
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.11.0'
2
+ export const version = '0.12.0'