@slugbugblue/trax 0.13.0 → 0.15.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/threats.js CHANGED
@@ -5,20 +5,26 @@
5
5
  */
6
6
 
7
7
  const REGEX = {
8
- a: '[am]', // White that pairs with the next white
9
- c: '[cp]', // White that forms a connectable pair with next white
10
- l: '[lmp]', // White in a hollow
11
- w: '[aclmpw]', // Any white
8
+ // Some of these are one for one:
9
+ // a: White that pairs with the next white
10
+ A: '[am]', // White maybe in a hollow that pairs with next white
11
+ // c: White that forms a connectable pair with next white
12
+ C: '[cp]', // White maybe in a hollow connectable pair with next white
13
+ l: '[lmp]', // Any White in a hollow
14
+ // m: "a" in a hollow
15
+ // p: "c" in a hollow)
16
+ w: '[acw]', // Any white except in hollows
17
+ W: '[aclmpw]', // Any white at all
12
18
  '!': '(?:b[acw])*?', // Ripple black/white
13
19
  '¡': '(?:[acw]b)*?', // Ripple white/black
14
20
  '*': 'b*?', // Zero or more black pieces
15
21
  '?': 'b?', // Zero or one black piece
16
22
  }
17
23
 
18
- class CustomInspectableThreat {
24
+ export const Threat = class {
19
25
  #depth = 0
20
26
  #pattern = ''
21
- #rx = null
27
+ #rx
22
28
  #reforms = false
23
29
 
24
30
  constructor(depth, pattern, reforms = false) {
@@ -31,7 +37,8 @@ class CustomInspectableThreat {
31
37
  rx += REGEX[char] || char
32
38
  }
33
39
 
34
- this.#rx = new RegExp(rx, 'g')
40
+ // Create the regex with lookahead assertions to allow overlapping matches
41
+ this.#rx = new RegExp('(?=(' + rx + '))', 'g')
35
42
  }
36
43
 
37
44
  get depth() {
@@ -65,7 +72,7 @@ class CustomInspectableThreat {
65
72
  }
66
73
  }
67
74
 
68
- class CustomInspectableThreatsDB {
75
+ export const ThreatsDB = class {
69
76
  #db = {}
70
77
  #depth = 0
71
78
 
@@ -75,9 +82,15 @@ class CustomInspectableThreatsDB {
75
82
  }
76
83
  }
77
84
 
85
+ compose(depth, pattern) {
86
+ pattern = pattern.replaceAll(/(?<=.)A/g, 'a').replaceAll(/W(?=.)/g, 'w')
87
+ return this.add(depth, pattern)
88
+ }
89
+
78
90
  add(depth, pattern, reforms = false) {
79
91
  this.#depth = Math.max(depth, this.#depth)
80
- this.#db[pattern] = new CustomInspectableThreat(depth, pattern, reforms)
92
+ if (pattern in this.#db) throw new Error('Duplicate pattern:' + pattern)
93
+ this.#db[pattern] = new Threat(depth, pattern, reforms)
81
94
  return this.#db[pattern]
82
95
  }
83
96
 
@@ -96,68 +109,93 @@ class CustomInspectableThreatsDB {
96
109
  // To make life at the REPL a little easier
97
110
  [Symbol.for('nodejs.util.inspect.custom')](_, out) {
98
111
  const style = out.stylize
99
- const inspection = '[' + style('Threats', 'special') + '] { '
112
+ const inspection = '[' + style('Threats', 'special') + ':'
100
113
  let total = 0
101
- const keys = []
114
+ const keys = ['{']
102
115
 
103
116
  for (let x = 0; x <= this.#depth; x++) {
104
117
  const count = this.level(x).length
105
- keys.push(style(x, 'string') + ': ' + style(count, 'number'))
118
+ keys.push(style(x, 'string') + ':' + style(count, 'number'))
106
119
  total += count
107
120
  }
108
121
 
109
- return inspection + keys.join(', ') + ' } total: ' + total
122
+ return inspection + style(total, 'number') + '] ' + keys.join(' ') + ' }'
110
123
  }
111
124
  }
112
125
 
113
- const pre = true
114
- const post = true
126
+ const pre = true // This pattern can appear first in an L threat
127
+ const post = true // This pattern can come second in an L threat
115
128
  const corners = {
116
- arw: { pre, post }, // Simple corner
117
- abrw: { post }, // Wide-first corner
118
- arbw: { pre }, // Wide-second corner
119
- abrbw: {}, // Wide corner
120
- 'ab?bw': { pre, post }, // Flat corner and wide flat corner
121
- cw: { pre, post }, // Connectable pair
122
- cbw: {}, // Wide connectable pair
129
+ ArW: { pre, post }, // Simple corner
130
+ AbrW: { post }, // Wide-first corner
131
+ ArbW: { pre }, // Wide-second corner
132
+ AbrbW: {}, // Wide corner
133
+ 'ab?bW': { pre, post }, // Flat corner and wide flat corner
134
+ CW: { pre, post }, // Connectable pair
135
+ CbW: {}, // Wide connectable pair
123
136
  }
124
137
 
125
- export const threats = new CustomInspectableThreatsDB(Object.keys(corners))
138
+ export const threats = new ThreatsDB(Object.keys(corners))
139
+
140
+ // Attacks
141
+ const attacks = [
142
+ 'a?W', // Simple attack and wide attack
143
+ 'm?W', // Simple and wide attack in a corner
144
+ ]
145
+ attacks.map((a) => threats.add(1, a))
126
146
 
127
147
  // Re-forming attacks
128
148
  const reformAttacks = [
129
149
  'rm?lr', // Cave/wide cave attack
130
150
  'rbm?lbr', // Deep cave/deep wide cave attack (impossibly rare?)
131
- 'ralmrw', // Attack at top of cave
151
+ 'ralmrW', // Attack at top of cave
132
152
  'arlmwr', // Reversed
133
153
  ]
134
154
  reformAttacks.map((ra) => threats.add(1, ra, true))
135
155
 
136
156
  const reformThreats = [
137
157
  'rp?lr', // Cave/wide cave threat
138
- 'rbp?lbr', // Deep cave/deept wide cave attach (impossibly rare?)
158
+ 'rbp?lbr', // Deep cave/deep wide cave threat (impossibly rare?)
159
+ 'rmbb???w', // Black corner with a long white loop potential
160
+ 'abb???lr', // Reverse
139
161
  ]
140
162
  reformThreats.map((rt) => threats.add(2, rt))
141
163
 
142
- // Attacks
143
- const attacks = [
144
- 'a?w', // Simple attack and wide attack
145
- ]
146
- attacks.map((a) => threats.add(1, a))
147
-
148
164
  // Create multi-stage threats
149
165
  for (const one of Object.keys(corners)) {
166
+ const start = one.replace('W', 'l')
167
+ let end = one.replace(/[aA]/, 'm').replace('C', 'p')
168
+ const pre = corners[one].post ? '?' : ''
169
+ let post = corners[one].pre ? '?' : ''
170
+ threats.compose(3, start + pre + '¡rm?W') // Attacking 3-stage threat
171
+ threats.compose(3, 'a?lr!' + post + end) // Reverse
172
+ threats.compose(3, one + '¡ab*bwr') // Corner by reforming hollow potential
173
+ threats.compose(3, 'rab*bw!' + one) // Reverse
174
+ threats.compose(3, one + '!brm*bW') // Corner by hollow attack, defends to edge
175
+ threats.compose(3, one + '!bbrmW') // Same, but slightly different layout
176
+ threats.compose(3, 'ab*lrb¡' + one) // Reverse
177
+ threats.compose(3, 'alrbb¡' + one) // Reverse of different layout
178
+
150
179
  for (const two of Object.keys(corners)) {
151
- // L threats
152
- const [before, after] = [corners[one].post, corners[two].pre]
153
- if (before || after) {
154
- const start = one.replace('w', 'l')
155
- threats.add(2, start + (before ? '?¡' : '¡') + two)
156
- const end = two.replace('a', 'm').replace('c', 'p')
157
- threats.add(2, one + (after ? '?!' : '!') + end)
180
+ end = two.replace(/[aA]/, 'm').replace('C', 'p')
181
+ post = corners[two].pre ? '?' : ''
182
+ if (pre) {
183
+ threats.compose(2, start + pre + '¡' + two) // Make an L
184
+ threats.compose(3, start + pre + 'rb¡' + two) // Make a three-stage threat
185
+ threats.compose(3, start + '?!rb¡' + two) // Attack to form an L threat
158
186
  }
159
187
 
160
- // Edge threats
161
- threats.add(3, one + '!b*b¡' + two)
188
+ if (post) {
189
+ threats.compose(2, one + post + '!' + end) // L
190
+ threats.compose(3, one + post + '!br' + end) // Three-stage
191
+ threats.compose(3, one + '!br¡?' + end) // Attack to form an L threat
192
+ }
193
+
194
+ threats.compose(3, one + '!b*b¡' + two) // Edge threat
162
195
  }
163
196
  }
197
+
198
+ threats.add(3, 'ralbmrW') // Attack into a wide cave
199
+ threats.add(3, 'Arlbmwr') // Reversed
200
+
201
+ threats.add(4, 'ArlbmrW') // Wide cave ready for an attack
package/src/tty.js CHANGED
@@ -22,7 +22,7 @@ import { Trax } from '@slugbugblue/trax'
22
22
  const esc = '\u001B[' // Start of console color escape code
23
23
  const off = esc + 'm' // Color reset code
24
24
  const ENV = process.env
25
- const NERD = ENV.NERDFONT
25
+ const NERD = ENV.NERDFONT || ENV.NERDFONTS
26
26
  const POWER = ENV.POWERLINE || ENV.P9K_TTY || ENV.P9K_SSH
27
27
  const leftBubble = NERD ? '' : POWER ? '' : '▐'
28
28
  const rightBubble = NERD ? '' : POWER ? '' : '▌'
package/src/types.d.ts CHANGED
@@ -25,12 +25,56 @@ type Colorer = {
25
25
  white: Colorize
26
26
  }
27
27
 
28
+ /** An object representing a space on the perimeter of a Trax position.
29
+ * x,y: the coordinates of the space
30
+ * c: the color present beside the space (w, b, l: both w and b, r: none)
31
+ * t: the type of possible moves: n-normal, x-none, c-cave, C-restricted cave
32
+ * b,w: if present, the number of the line to match with its other end
33
+ * pb, pw: if present, can pair with the next line in one turn
34
+ * xb, xw: the label of this space for each color
35
+ */
36
+ type EdgeLocation = {
37
+ x: number
38
+ y: number
39
+ c: string
40
+ t: string
41
+ b?: number
42
+ w?: number
43
+ pb?: boolean
44
+ pw?: boolean
45
+ zb?: string
46
+ zw?: string
47
+ }
48
+
49
+ type EdgeObject = {
50
+ b: string
51
+ w: string
52
+ edge: RawEdge
53
+ }
54
+
55
+ /** Object representing a concrete threat found in a position. */
56
+ type FoundThreat = { threat: string; match: string; at: number; value: number }
57
+
58
+ /** An object with keys representing each level, with arrays for each threat of
59
+ * that level. */
60
+ type FoundThreats = Record<string, FoundThreat[]>
61
+
62
+ /** All the threats found for both the white and black player. */
63
+ type FoundThreatsCollection = { b: ColorThreats; w: ColorThreats }
64
+
28
65
  /** A point-like object has numerical x and y properties. */
29
66
  type PointLike = {
30
67
  x: number
31
68
  y: number
32
69
  }
33
70
 
71
+ /** An object used to track an analysis on a position. */
72
+ type PositionScore = {
73
+ move: string
74
+ score: number
75
+ analysis?: Analysis
76
+ }
77
+
34
78
  /** A puzzle. */
35
79
  type Puzzle = {
36
80
  id: string
@@ -53,6 +97,10 @@ type PuzzleSource = {
53
97
  url?: string
54
98
  }
55
99
 
100
+ /** Array of objects representing the spaces surrounding the perimeter of a
101
+ * Trax position. */
102
+ type RawEdge = EdgeLocation[]
103
+
56
104
  /** Treat the save state as an opaque object,
57
105
  * produced by save() and fed into restore().
58
106
  */
@@ -76,6 +124,14 @@ type SaveState = {
76
124
  */
77
125
  type Slash = '/' | '\\' | '+'
78
126
 
127
+ /** Threat definition. */
128
+ type Threat = {
129
+ depth: number
130
+ pattern: string
131
+ rx: RegExp
132
+ value: number
133
+ }
134
+
79
135
  /** A single tile on the board. */
80
136
  type Tile = {
81
137
  id: TileId
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.13.0'
2
+ export const version = '0.15.0'