@slugbugblue/trax 0.4.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slugbugblue/trax",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Trax game engine and friends",
5
5
  "keywords": [
6
6
  "trax",
@@ -14,17 +14,29 @@
14
14
  "exports": "./engine.js",
15
15
  "repository": "gitlab:slugbugblue/trax",
16
16
  "scripts": {
17
- "test": "c8 tap --no-coverage",
17
+ "test": "xo && c8 ava",
18
18
  "preversion": "npm test",
19
19
  "postversion": "git push && git push --tags"
20
20
  },
21
21
  "devDependencies": {
22
+ "ava": "^4.0.1",
22
23
  "c8": "^7.11.0",
23
- "eslint": "^8.7.0",
24
- "tap": "^15.1.6"
24
+ "prettier": "^2.5.1",
25
+ "xo": "^0.47.0"
25
26
  },
26
27
  "type": "module",
27
28
  "engines": {
28
29
  "node": "^14.14.0 || >=16.0.0"
30
+ },
31
+ "prettier": {
32
+ "bracketSpacing": true,
33
+ "proseWrap": "always",
34
+ "semi": false,
35
+ "singleQuote": true,
36
+ "trailingComma": "all",
37
+ "useTabs": false
38
+ },
39
+ "xo": {
40
+ "prettier": true
29
41
  }
30
42
  }
package/tty.js CHANGED
@@ -1,4 +1,4 @@
1
- /* Copyright 2021 Chad Transtrum
1
+ /* Copyright 2021-2022 Chad Transtrum
2
2
  *
3
3
  * Licensed under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License.
@@ -15,13 +15,15 @@
15
15
 
16
16
  // Ever wanted to draw Trax boards on the console? I have.
17
17
 
18
+ import process from 'node:process'
19
+
18
20
  import Trax from './engine.js'
19
21
 
20
- const esc = '\x1b[' // start of console color escape code
21
- const off = esc + 'm' // color reset code
22
+ const esc = '\u001B[' // Start of console color escape code
23
+ const off = esc + 'm' // Color reset code
22
24
 
23
25
  // display a message with optional color
24
- const out = (message, fgcolor, bgcolor) => {
26
+ export const outty = (message, fgcolor, bgcolor) => {
25
27
  let ansi = fgcolor || bgcolor ? esc : ''
26
28
  if (fgcolor) ansi += '38;5;' + fgcolor
27
29
  if (bgcolor) ansi += '48;5;' + bgcolor
@@ -29,10 +31,11 @@ const out = (message, fgcolor, bgcolor) => {
29
31
  console.log(ansi + message + off)
30
32
  }
31
33
 
32
- let set = 'trax' // which color set should we draw with?
34
+ let set = 'trax' // Which color set should we draw with?
33
35
 
34
36
  // a little magic to display the game in progress
35
- const color_sets = { // Colors for terminal sequence: <Esc>[38;5;<color_num>m
37
+ const colorSets = {
38
+ // Colors for terminal sequence: <Esc>[38;5;<color_num>m
36
39
  // b, B, w, W: color of line for each player, capital for a winning line
37
40
  // e: empty (ie, board) color, t: tile color
38
41
  trax: { b: 238, B: 57, e: 19, t: 74, w: 255, W: 184 },
@@ -41,149 +44,183 @@ const color_sets = { // Colors for terminal sequence: <Esc>[38;5;<color_num>m
41
44
  tentative: { b: 235, B: 18, e: 19, t: 244, w: 252, W: 217 },
42
45
  }
43
46
 
44
- const ln = 'm\u2584' // lower half block -- with the leading m to terminate ansi color escape
45
- const up = 'm\u2588' // full block
46
- const lt = 'm\u2599' // lower half and left half block
47
- const rt = 'm\u259f' // lower half and right half block
48
- const fg = n => '38;5;' + String(set[n]) // foreground color_num
49
- const bg = n => '48;5;' + String(set[n]) // background color_num
50
- const sp = () => esc + bg('e') + 'm ' // lower space
47
+ const ln = 'm\u2584' // Lower half block -- with the leading m to terminate ansi color escape
48
+ const up = 'm\u2588' // Full block
49
+ const lt = 'm\u2599' // Lower half and left half block
50
+ const rt = 'm\u259F' // Lower half and right half block
51
+ const fg = (n) => '38;5;' + String(set[n]) // Foreground color_num
52
+ const bg = (n) => '48;5;' + String(set[n]) // Background color_num
53
+ const sp = () => esc + bg('e') + 'm ' // Lower space
51
54
  // tiles are displayed on two lines; the top half of the first line is empty
52
55
  // and the second half is composed of three characters, up color surrounded by tile color
53
- const uptty = clr => esc + fg('t') + ';' + bg('e') + ln
54
- + esc + fg(clr) + ln + esc + fg('t') + ln + ' '
55
- // the top half of the second line has both horizontal lines with a center transition
56
+ const uptty = (clr) =>
57
+ esc +
58
+ fg('t') +
59
+ ';' +
60
+ bg('e') +
61
+ ln +
62
+ esc +
63
+ fg(clr) +
64
+ ln +
65
+ esc +
66
+ fg('t') +
67
+ ln +
68
+ ' '
69
+ // The top half of the second line has both horizontal lines with a center transition
56
70
  // while the bottom half is the tile background color surrounding the down color
57
- const horiztty = clr => esc + fg('t') + ';' + bg(clr) + ln
58
- const dntty = (clr, oppclr, char) => esc + fg(clr) + (char === up ? '' : ';' + bg(oppclr)) + char
59
- // list of icon_char => tile line colors for easy reference in order up, right, down, left
60
- const tiles = { a: 'bbww', b: 'bwbw', c: 'bwwb', d: 'wbbw', e: 'wbwb', f: 'wwbb' }
71
+ const horiztty = (clr) => esc + fg('t') + ';' + bg(clr) + ln
72
+ const dntty = (clr, oppclr, char) =>
73
+ esc + fg(clr) + (char === up ? '' : ';' + bg(oppclr)) + char
74
+ // List of icon_char => tile line colors for easy reference in order up, right, down, left
75
+ const tiles = {
76
+ a: 'bbww',
77
+ b: 'bwbw',
78
+ c: 'bwwb',
79
+ d: 'wbbw',
80
+ e: 'wbwb',
81
+ f: 'wwbb',
82
+ }
61
83
 
62
- // return the ANSI codes to display a 3x1.5 block tile, one line at a time
63
- const tile_tty = (tile, top) => { // top: true for first line, false for second
84
+ // Return the ANSI codes to display a 3x1.5 block tile, one line at a time
85
+ const tileTty = (tile, top) => {
86
+ // Top: true for first line, false for second
64
87
  // quick access to tile color and its opposite in upper case
65
- let colors = { b: 'b', w: 'w', B: 'w', W: 'b' }
66
- let save = set
67
- let tentative = tile !== tile.toLowerCase()
68
- if (tentative) set = color_sets.tentative
88
+ const colors = { b: 'b', w: 'w', B: 'w', W: 'b' }
89
+ const save = set
90
+ const tentative = tile !== tile.toLowerCase()
91
+ if (tentative) set = colorSets.tentative
69
92
  tile = tile.toLowerCase()
70
- if (!(tile in tiles)) { // winning paths are represented by adding 6 to the tile char code
71
- tile = String.fromCharCode(tile.charCodeAt(0) - 6)
93
+ if (!(tile in tiles)) {
94
+ // Winning paths are represented by adding 6 to the tile char code
95
+ tile = String.fromCodePoint(tile.codePointAt(0) - 6)
72
96
  if (tile in tiles) {
73
97
  colors.w = 'W'
74
98
  colors.B = 'W'
75
- } else { // and another six for a black win
76
- tile = String.fromCharCode(tile.charCodeAt(0) - 6)
99
+ } else {
100
+ // And another six for a black win
101
+ tile = String.fromCodePoint(tile.codePointAt(0) - 6)
77
102
  colors.b = 'B'
78
103
  colors.W = 'B'
79
104
  }
80
105
  }
81
- let val = false
106
+
107
+ let value = false
82
108
  if (tile in tiles) {
83
109
  tile = tiles[tile]
84
110
  if (top) {
85
- val = uptty(colors[tile[0]])
111
+ value = uptty(colors[tile[0]])
86
112
  } else {
87
- let center_char = tile[1] === tile[2] ? rt : tile[2] === tile[3] ? lt : up
88
- let centertty = dntty(colors[tile[2]], colors[tile[2].toUpperCase()], center_char)
89
- val = horiztty(colors[tile[3]]) + centertty + horiztty(colors[tile[1]]) + sp()
113
+ const centerChar =
114
+ tile[1] === tile[2] ? rt : tile[2] === tile[3] ? lt : up
115
+ const centertty = dntty(
116
+ colors[tile[2]],
117
+ colors[tile[2].toUpperCase()],
118
+ centerChar,
119
+ )
120
+ value =
121
+ horiztty(colors[tile[3]]) + centertty + horiztty(colors[tile[1]]) + sp()
90
122
  }
91
123
  }
124
+
92
125
  set = save
93
- return val || (top ? '??? ' : '\'\'\' ')
126
+ return value || (top ? '??? ' : "''' ")
94
127
  }
95
128
 
96
- const merge_tentative = () => {
97
- color_sets.tentative.e = set.e // copy empty color to tentative set
98
- color_sets.tentative.W = set.W
99
- color_sets.tentative.B = set.B
129
+ const mergeTentative = () => {
130
+ colorSets.tentative.e = set.e // Copy empty color to tentative set
131
+ colorSets.tentative.W = set.W
132
+ colorSets.tentative.B = set.B
100
133
  }
101
134
 
102
- const print_icon = (icon, set_name) => {
103
- set = color_sets[set_name] || color_sets.trax
104
- merge_tentative(set)
135
+ const printIcon = (icon, setName) => {
136
+ set = colorSets[setName] || colorSets.trax
137
+ mergeTentative(set)
105
138
  const on = esc + '38;5;' + String(set.t) + ';48;5;' + String(set.e) + 'm'
106
139
  let o1 = ' '
107
140
  let o2 = '1 '
108
141
  let row = 1
109
142
  let width = 1
110
- let info = []
111
- let line_width = 0
112
- while (icon.length) {
113
- let c = icon[0]
143
+ const info = []
144
+ let lineWidth = 0
145
+ while (icon.length > 0) {
146
+ const c = icon[0]
114
147
  icon = icon.slice(1)
115
148
  if (c === ':') {
116
- info.push({o: o1, w: line_width})
117
- info.push({o: o2, w: line_width})
149
+ info.push({ o: o1, w: lineWidth }, { o: o2, w: lineWidth })
118
150
  row++
119
- width = Math.max(width, line_width)
120
- line_width = 0
151
+ width = Math.max(width, lineWidth)
152
+ lineWidth = 0
121
153
  o1 = ' '
122
154
  o2 = String(row).padEnd(3)
123
155
  } else if (c >= '0' && c <= '9') {
124
- let empty = Number(c) + 1
156
+ const empty = Number(c) + 1
125
157
  o1 += ' '.repeat(empty * 4)
126
158
  o2 += ' '.repeat(empty * 4)
127
- line_width += empty
159
+ lineWidth += empty
128
160
  } else {
129
- o1 += tile_tty(c, 'top')
130
- o2 += tile_tty(c)
131
- line_width++
161
+ o1 += tileTty(c, 'top')
162
+ o2 += tileTty(c)
163
+ lineWidth++
132
164
  }
133
165
  }
134
- if (o1.length) {
135
- info.push({o: o1, w: line_width})
136
- info.push({o: o2, w: line_width})
137
- width = Math.max(width, line_width)
166
+
167
+ if (o1.length > 0) {
168
+ info.push({ o: o1, w: lineWidth }, { o: o2, w: lineWidth })
169
+ width = Math.max(width, lineWidth)
138
170
  }
139
- let columns = process.stdout.columns || 0
140
- let rows = process.stdout.rows || 0
171
+
172
+ const columns = process.stdout.columns || 0
173
+ const rows = process.stdout.rows || 0
141
174
  if (width * 4 < columns - 4 && info.length < rows - 2) {
142
- let col_out = ' '
175
+ let colOut = ' '
143
176
  for (let col = 1; col <= width; col++) {
144
- col_out += ' ' + Trax.column(col).padEnd(3)
177
+ colOut += ' ' + Trax.column(col).padEnd(3)
145
178
  }
146
- console.info(on + col_out.padEnd((width + 1) * 4) + off)
147
- info.forEach(line => console.info(on + line.o + ''.padEnd((width - line.w) * 4) + ' ' + off))
179
+
180
+ console.info(on + colOut.padEnd((width + 1) * 4) + off)
181
+ for (const line of info)
182
+ console.info(on + line.o + ''.padEnd((width - line.w) * 4) + ' ' + off)
148
183
  console.info(on + ''.padEnd((width + 1) * 4) + off)
149
184
  } else {
150
185
  console.warn('Console is too small to display this game')
151
186
  }
152
187
  }
153
188
 
154
- // flexible way to get a Trax game object
189
+ // Flexible way to get a Trax game object
155
190
  // game = existing Trax instance, or a string indicating the variant name
156
191
  // moves = optional list or string of moves to be made on the game
157
192
  // returns a Trax instance
158
- const trax_game = (game='trax', moves) => {
193
+ export const traxGame = (game = 'trax', moves = '') => {
159
194
  let trax
160
195
  if (game instanceof Trax) {
161
196
  trax = game
162
197
  if (moves) {
163
198
  if (typeof moves === 'string') moves = moves.split(' ')
164
- moves.forEach(move => trax.drop_tile(move))
199
+ for (const move of moves) trax.dropTile(move)
165
200
  }
166
201
  } else {
167
202
  trax = new Trax(game, moves, 'cli')
168
203
  }
204
+
169
205
  return trax
170
206
  }
171
207
 
172
- const display_game = (game='trax', moves='', tentative) => {
173
- let trax = trax_game(game, moves)
174
- let icon = trax.icon
208
+ export const displayGame = (game = 'trax', moves = '', tentative = false) => {
209
+ const trax = traxGame(game, moves)
210
+ let { icon } = trax
175
211
  if (tentative) {
176
- let drops = trax.drop_tile(tentative)
212
+ const drops = trax.dropTile(tentative)
177
213
  if (drops.valid) {
178
- icon = trax.tentative_icon(drops)
214
+ icon = trax.tentativeIcon(drops)
179
215
  } else {
180
216
  tentative = false
181
217
  }
182
218
  }
219
+
183
220
  console.info(trax.notation)
184
- print_icon(icon, trax.rules)
221
+ printIcon(icon, trax.rules)
185
222
  if (tentative) console.info('--- tentative position ---')
186
- let name = {1: 'white', 2: 'black'}[trax.turn]
223
+ const name = { 1: 'white', 2: 'black' }[trax.turn]
187
224
  if (trax.over) {
188
225
  if (trax.turn) {
189
226
  console.info(name + ' wins')
@@ -191,12 +228,6 @@ const display_game = (game='trax', moves='', tentative) => {
191
228
  console.info('game end' + (tentative ? 's' : 'ed') + ' in a draw')
192
229
  }
193
230
  } else if (trax.turn) {
194
- console.info((tentative ? 'leaving ' : '') + name +' to play')
231
+ console.info((tentative ? 'leaving ' : '') + name + ' to play')
195
232
  }
196
233
  }
197
-
198
- export default {
199
- display: display_game,
200
- game: trax_game,
201
- out,
202
- }