@slugbugblue/trax 0.12.0 → 0.14.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
@@ -1,23 +1,12 @@
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.
1
+ /** Threats database: hand-coded threats
2
+ * @copyright 2022
3
+ * @author Chad Transtrum <chad@transtrum.net>
4
+ * @license Apache-2.0
14
5
  */
15
6
 
16
- // Threats database: hand-coded threats
17
-
18
7
  const REGEX = {
19
- a: '[am]', // White that pairs with the next white
20
- c: '[cp]', // White that forms a connectable pair with next white
8
+ a: '[am]', // White that pairs with the next white (m=hollow)
9
+ c: '[cp]', // White that forms a connectable pair with next white (p=hollow)
21
10
  l: '[lmp]', // White in a hollow
22
11
  w: '[aclmpw]', // Any white
23
12
  '!': '(?:b[acw])*?', // Ripple black/white
@@ -26,14 +15,16 @@ const REGEX = {
26
15
  '?': 'b?', // Zero or one black piece
27
16
  }
28
17
 
29
- class CustomInspectableThreat {
18
+ class Threat {
30
19
  #depth = 0
31
20
  #pattern = ''
32
21
  #rx = null
22
+ #reforms = false
33
23
 
34
- constructor(depth, pattern) {
24
+ constructor(depth, pattern, reforms = false) {
35
25
  this.#depth = depth
36
26
  this.#pattern = pattern
27
+ this.#reforms = reforms
37
28
 
38
29
  let rx = ''
39
30
  for (const char of pattern) {
@@ -55,20 +46,26 @@ class CustomInspectableThreat {
55
46
  return this.#rx
56
47
  }
57
48
 
49
+ get value() {
50
+ return this.#reforms ? 2 : 1
51
+ }
52
+
58
53
  // To make life at the REPL a little easier
59
54
  [Symbol.for('nodejs.util.inspect.custom')](_, out) {
60
55
  const style = out.stylize
61
- return [
56
+ const display = [
62
57
  '[' + style('Threat', 'special') + ']',
63
58
  '{',
64
59
  style(this.#pattern, 'string') + ':',
65
60
  style(this.#depth, 'number'),
61
+ this.#reforms ? '!' : '',
66
62
  '}',
67
- ].join(' ')
63
+ ]
64
+ return display.filter((d) => d !== '').join(' ')
68
65
  }
69
66
  }
70
67
 
71
- class CustomInspectableThreatsDB {
68
+ class ThreatsDB {
72
69
  #db = {}
73
70
  #depth = 0
74
71
 
@@ -78,9 +75,10 @@ class CustomInspectableThreatsDB {
78
75
  }
79
76
  }
80
77
 
81
- add(depth, pattern) {
78
+ add(depth, pattern, reforms = false) {
82
79
  this.#depth = Math.max(depth, this.#depth)
83
- this.#db[pattern] = new CustomInspectableThreat(depth, pattern)
80
+ if (pattern in this.#db) throw new Error('Duplicate pattern:' + pattern)
81
+ this.#db[pattern] = new Threat(depth, pattern, reforms)
84
82
  return this.#db[pattern]
85
83
  }
86
84
 
@@ -113,8 +111,8 @@ class CustomInspectableThreatsDB {
113
111
  }
114
112
  }
115
113
 
116
- const pre = true
117
- const post = true
114
+ const pre = true // This pattern can appear first in an L threat
115
+ const post = true // This pattern can come second in an L threat
118
116
  const corners = {
119
117
  arw: { pre, post }, // Simple corner
120
118
  abrw: { post }, // Wide-first corner
@@ -125,7 +123,7 @@ const corners = {
125
123
  cbw: {}, // Wide connectable pair
126
124
  }
127
125
 
128
- export const threats = new CustomInspectableThreatsDB(Object.keys(corners))
126
+ export const threats = new ThreatsDB(Object.keys(corners))
129
127
 
130
128
  // Attacks
131
129
  const attacks = [
@@ -134,15 +132,19 @@ const attacks = [
134
132
  attacks.map((a) => threats.add(1, a))
135
133
 
136
134
  // Re-forming attacks
137
- const reform = [
135
+ const reformAttacks = [
138
136
  'rm?lr', // Cave/wide cave attack
139
- 'rp?lr',
140
137
  'rbm?lbr', // Deep cave/deep wide cave attack (impossibly rare?)
141
- 'rbp?lbr',
142
138
  'ralmrw', // Attack at top of cave
143
139
  'arlmwr', // Reversed
144
140
  ]
145
- reform.map((r) => threats.add(2, r))
141
+ reformAttacks.map((ra) => threats.add(1, ra, true))
142
+
143
+ const reformThreats = [
144
+ 'rp?lr', // Cave/wide cave threat
145
+ 'rbp?lbr', // Deep cave/deept wide cave attach (impossibly rare?)
146
+ ]
147
+ reformThreats.map((rt) => threats.add(2, rt))
146
148
 
147
149
  // Create multi-stage threats
148
150
  for (const one of Object.keys(corners)) {
@@ -151,14 +153,16 @@ for (const one of Object.keys(corners)) {
151
153
  const [before, after] = [corners[one].post, corners[two].pre]
152
154
  if (before || after) {
153
155
  const start = one.replace('w', 'l')
154
- threats.add(2, start + (before ? '?¡' : '¡') + two)
156
+ const pre = before ? '?' : ''
157
+ const post = after ? '?' : ''
158
+ threats.add(2, start + pre + '¡' + two) // Make an L
159
+ threats.add(3, start + pre + 'rb¡' + two) // Make a three-stage threat
155
160
  const end = two.replace('a', 'm').replace('c', 'p')
156
- threats.add(2, one + (after ? '?!' : '!') + end)
161
+ threats.add(2, one + post + '!' + end) // L
162
+ threats.add(3, one + post + '!br' + end) // Three-stage
157
163
  }
158
164
 
159
165
  // Edge threats
160
166
  threats.add(3, one + '!b*b¡' + two)
161
167
  }
162
168
  }
163
-
164
- // Create edge threats
package/src/tty.js CHANGED
@@ -17,12 +17,21 @@
17
17
 
18
18
  import process from 'node:process'
19
19
 
20
- import { Trax } from './engine.js'
20
+ import { Trax } from '@slugbugblue/trax'
21
21
 
22
22
  const esc = '\u001B[' // Start of console color escape code
23
23
  const off = esc + 'm' // Color reset code
24
-
25
- // Colorize a string
24
+ const ENV = process.env
25
+ const NERD = ENV.NERDFONT
26
+ const POWER = ENV.POWERLINE || ENV.P9K_TTY || ENV.P9K_SSH
27
+ const leftBubble = NERD ? '' : POWER ? '' : '▐'
28
+ const rightBubble = NERD ? '' : POWER ? '' : '▌'
29
+
30
+ /** Colorize a string.
31
+ * @arg {string} contents
32
+ * @arg {string|number} [fgcolor]
33
+ * @arg {string|number} [bgcolor]
34
+ */
26
35
  export const color = (contents = '', fgcolor = '', bgcolor = '') => {
27
36
  const c = []
28
37
  if (fgcolor) c.push('38;5;' + fgcolor)
@@ -35,9 +44,7 @@ export const outty = (message, fgcolor, bgcolor) => {
35
44
  console.log(color(message, fgcolor, bgcolor) + off)
36
45
  }
37
46
 
38
- let set = 'trax' // Which color set should we draw with?
39
-
40
- // a little magic to display the game in progress
47
+ // A little magic to display the game in progress
41
48
  const colorSets = {
42
49
  // Colors for terminal sequence: <Esc>[38;5;<color_num>m
43
50
  // b, B, w, W: color of line for each player, capital for a winning line
@@ -48,6 +55,8 @@ const colorSets = {
48
55
  tentative: { b: 235, B: 18, e: 19, t: 244, w: 252, W: 217 },
49
56
  }
50
57
 
58
+ let set = colorSets.trax // Which color set should we draw with?
59
+
51
60
  export const name = (rules) => {
52
61
  if (Trax.variants.has(rules)) {
53
62
  return color(Trax.names[rules], colorSets[rules].t)
@@ -67,7 +76,12 @@ export const bubble = (clr, text) => {
67
76
  }
68
77
 
69
78
  const c = colors[clr]
70
- return color('', c.bg) + color(text, c.fg, c.bg) + off + color('', c.bg)
79
+ return (
80
+ color(leftBubble, c.bg) +
81
+ color(text, c.fg, c.bg) +
82
+ off +
83
+ color(rightBubble, c.bg)
84
+ )
71
85
  }
72
86
 
73
87
  const ln = 'm\u2584' // Lower half block -- with the leading m to terminate ansi color escape
@@ -124,7 +138,7 @@ const tileTty = (tile, top) => {
124
138
  }
125
139
  }
126
140
 
127
- let value = false
141
+ let value = ''
128
142
  if (tile in tiles) {
129
143
  tile = tiles[tile]
130
144
  if (top) {
@@ -154,7 +168,7 @@ const mergeTentative = () => {
154
168
 
155
169
  const printIcon = (icon, setName) => {
156
170
  set = colorSets[setName] || colorSets.trax
157
- mergeTentative(set)
171
+ mergeTentative()
158
172
  const on = esc + '38;5;' + String(set.t) + ';48;5;' + String(set.e) + 'm'
159
173
  let o1 = ' '
160
174
  let o2 = '1 '
@@ -208,10 +222,11 @@ const printIcon = (icon, setName) => {
208
222
  }
209
223
  }
210
224
 
211
- // Flexible way to get a Trax game object
212
- // game = existing Trax instance, or a string indicating the variant name
213
- // moves = optional list or string of moves to be made on the game
214
- // returns a Trax instance
225
+ /** Flexible way to get a Trax game object.
226
+ * @arg {Trax|TraxVariant} [game] - existing Trax instance or a string indicating the variant name
227
+ * @arg {string|string[]} [moves] - optional list or string of moves to be made on the game
228
+ * @returns {Trax} a Trax instance
229
+ */
215
230
  export const traxGame = (game = 'trax', moves = '') => {
216
231
  let trax
217
232
  if (game instanceof Trax) {
@@ -253,7 +268,7 @@ const printNotation = (trax, tentative, show = Number.POSITIVE_INFINITY) => {
253
268
  export const display = (
254
269
  game,
255
270
  players,
256
- tentative = false,
271
+ tentative = '',
257
272
  showMove = Number.POSITIVE_INFINITY,
258
273
  ) => {
259
274
  const trax = traxGame(game)
@@ -266,7 +281,7 @@ export const display = (
266
281
  if (drops.valid || drops.dropped.length > 0) {
267
282
  icon = trax.dropsIcon(drops)
268
283
  } else {
269
- tentative = false
284
+ tentative = ''
270
285
  }
271
286
  } else if (showMove <= trax.moves.length) {
272
287
  icon = trax.dropsIcon({
package/src/types.d.ts ADDED
@@ -0,0 +1,105 @@
1
+ /** Type information.
2
+ * @author Chad Transtrum <chad@transtrum.net>
3
+ * @license Apache-2.0
4
+ */
5
+
6
+ /** Color is a single character to represent white or black. */
7
+ type Color = 'w' | 'b'
8
+
9
+ /** Colorize is a fancy function for colorizing text. */
10
+ type Colorize = (text: string, def?: string | number) => string
11
+
12
+ /** Colorer is a little too fancy. Hence the gnarly typescript. */
13
+ type Colorer = {
14
+ (text: string, def?: string | number): string
15
+ black: Colorize
16
+ command: Colorize
17
+ default: Colorize
18
+ error: Colorize
19
+ fatal: Colorize
20
+ help: Colorize
21
+ id: Colorize
22
+ variable: Colorize
23
+ optional: Colorize
24
+ short: Colorize
25
+ white: Colorize
26
+ }
27
+
28
+ /** A point-like object has numerical x and y properties. */
29
+ type PointLike = {
30
+ x: number
31
+ y: number
32
+ }
33
+
34
+ /** A puzzle. */
35
+ type Puzzle = {
36
+ id: string
37
+ src: string
38
+ game: TraxVariant
39
+ notation: string
40
+ icon: string
41
+ level: number
42
+ max: number
43
+ player: number
44
+ title?: string
45
+ desc?: string
46
+ hint?: string
47
+ hints?: string[]
48
+ }
49
+
50
+ type PuzzleSource = {
51
+ name: string
52
+ copyright?: string
53
+ url?: string
54
+ }
55
+
56
+ /** Treat the save state as an opaque object,
57
+ * produced by save() and fed into restore().
58
+ */
59
+ type SaveState = {
60
+ id: string
61
+ move: number
62
+ turn: number
63
+ over: boolean
64
+ left: number
65
+ right: number
66
+ top: number
67
+ bottom: number
68
+ notation: string
69
+ tiles: string
70
+ path: string
71
+ invalid: boolean
72
+ }
73
+
74
+ /** Representations of the different ways a tile can curve.
75
+ * Matches the symbols used in Trax notation.
76
+ */
77
+ type Slash = '/' | '\\' | '+'
78
+
79
+ /** A single tile on the board. */
80
+ type Tile = {
81
+ id: TileId
82
+ loc: Point
83
+ type: TileType
84
+ move: number
85
+ seq: number
86
+ }
87
+
88
+ /** When a tile is dropped, this object represents the results. */
89
+ type TileDrop = {
90
+ dropped: Tile[]
91
+ notation: string
92
+ valid: boolean
93
+ }
94
+
95
+ /** A TileId is just a string. */
96
+ type TileId = string
97
+
98
+ /** Invalid tiles are represented by 'x'. */
99
+ type TileType = ValidTiles | 'x'
100
+
101
+ /** All of the variants supported by the engine. */
102
+ type TraxVariant = 'trax' | 'traxloop' | 'trax8'
103
+
104
+ /** Tile types are represented by one of the following single characters. */
105
+ type ValidTiles = 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.12.0'
2
+ export const version = '0.14.0'