@slugbugblue/trax 0.1.0 → 0.2.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/.eslintrc.yaml +1 -1
- package/engine.js +32 -16
- package/jsconfig.json +1 -1
- package/package.json +7 -4
- package/tty.js +35 -35
package/.eslintrc.yaml
CHANGED
package/engine.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* Copyright 2021 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.
|
|
5
5
|
* You may obtain a copy of the License at
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
* Unless required by applicable law or agreed to in writing, software
|
|
10
10
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
11
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
// and then sorted alphabetically and given a single letter name, so 'bbww' becomes 'a'
|
|
20
20
|
// which gives us six different tile names: a-f
|
|
21
21
|
|
|
22
|
+
const variants = ['trax', 'traxloop', 'trax8'] // known variants
|
|
22
23
|
|
|
23
24
|
// fun trax helper constants
|
|
24
25
|
|
|
@@ -33,7 +34,9 @@ const symmetry = { // convert tile to different views
|
|
|
33
34
|
|
|
34
35
|
const notation_regex = /^ *([@a-z]+) *(\d+) *([/\\+]) *$/i
|
|
35
36
|
|
|
36
|
-
const
|
|
37
|
+
const color_of = { 1: 'w', 2: 'b' }
|
|
38
|
+
const player_num = { b: 2, w: 1 }
|
|
39
|
+
const other = { b: 'w', w: 'b' }
|
|
37
40
|
|
|
38
41
|
// fun trax helper functions
|
|
39
42
|
|
|
@@ -128,6 +131,10 @@ const Trax = class {
|
|
|
128
131
|
this.invalid = saved.invalid
|
|
129
132
|
}
|
|
130
133
|
|
|
134
|
+
get color () { // get the color of the current player (w or b)
|
|
135
|
+
return color_of[this.turn]
|
|
136
|
+
}
|
|
137
|
+
|
|
131
138
|
get game_over () { // returns true if the game is over
|
|
132
139
|
return this.over
|
|
133
140
|
}
|
|
@@ -194,7 +201,10 @@ const Trax = class {
|
|
|
194
201
|
if (this.width > 7 && (x < this.left || x > this.right)) return false
|
|
195
202
|
if (this.height > 7 && (y < this.top || y > this.bottom)) return false
|
|
196
203
|
}
|
|
197
|
-
return
|
|
204
|
+
return Boolean(
|
|
205
|
+
this.tile_at(x-1, y) || this.tile_at(x+1, y) ||
|
|
206
|
+
this.tile_at(x, y-1) || this.tile_at(x, y+1)
|
|
207
|
+
)
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
possible_locations () {
|
|
@@ -293,7 +303,7 @@ const Trax = class {
|
|
|
293
303
|
|
|
294
304
|
check_win (tiles) {
|
|
295
305
|
let winner = false
|
|
296
|
-
let colors = this.
|
|
306
|
+
let colors = [ this.color, other[this.color] ] // check win for current player first
|
|
297
307
|
colors.forEach(color => {
|
|
298
308
|
if (winner) return // skip the second player if the first has won
|
|
299
309
|
tiles.forEach(tile => {
|
|
@@ -314,12 +324,14 @@ const Trax = class {
|
|
|
314
324
|
})
|
|
315
325
|
if (winner) {
|
|
316
326
|
this.over = true
|
|
317
|
-
this.turn = winner
|
|
327
|
+
this.turn = player_num[winner]
|
|
318
328
|
} else {
|
|
319
329
|
// 8x8 Trax can end in a tie once there are no valid moves left
|
|
320
|
-
if (this.rules === 'trax8' && this.
|
|
321
|
-
this.
|
|
322
|
-
|
|
330
|
+
if (this.rules === 'trax8' && this.width > 7 && this.height > 7) {
|
|
331
|
+
if (this.possible_moves().length < 1) {
|
|
332
|
+
this.over = true
|
|
333
|
+
this.turn = 0 // tie
|
|
334
|
+
}
|
|
323
335
|
}
|
|
324
336
|
}
|
|
325
337
|
}
|
|
@@ -371,12 +383,12 @@ const Trax = class {
|
|
|
371
383
|
this.update_notation(notation)
|
|
372
384
|
dropped.push(this.add_tile(type, x, y)) // play the tile
|
|
373
385
|
dropped = dropped.concat(this.forced_moves(x, y)) // play all forced moves
|
|
374
|
-
if (!tentative) this.check_win(dropped, tentative)
|
|
375
386
|
if (this.invalid || tentative) {
|
|
376
387
|
valid = false
|
|
377
388
|
this.restore(saved) // abort! abort!
|
|
378
|
-
} else
|
|
379
|
-
this.
|
|
389
|
+
} else {
|
|
390
|
+
this.check_win(dropped)
|
|
391
|
+
if (!this.over) this.turn = this.turn === 1 ? 2 : 1
|
|
380
392
|
}
|
|
381
393
|
}
|
|
382
394
|
}
|
|
@@ -481,7 +493,7 @@ const Trax = class {
|
|
|
481
493
|
})
|
|
482
494
|
})
|
|
483
495
|
})
|
|
484
|
-
return norm
|
|
496
|
+
return (this.color || 't').toUpperCase() + norm
|
|
485
497
|
}
|
|
486
498
|
|
|
487
499
|
get normalized () {
|
|
@@ -490,7 +502,11 @@ const Trax = class {
|
|
|
490
502
|
}
|
|
491
503
|
}
|
|
492
504
|
|
|
493
|
-
|
|
505
|
+
// add class methods
|
|
506
|
+
Trax.column = encode_col
|
|
494
507
|
Trax.variants = variants
|
|
508
|
+
Trax.color_of = color_of
|
|
509
|
+
Trax.player_num = player_num
|
|
510
|
+
Trax.other = other
|
|
495
511
|
|
|
496
|
-
|
|
512
|
+
export default Trax
|
package/jsconfig.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slugbugblue/trax",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Trax game engine and friends",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"trax",
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
"bugs": "https://gitlab.com/slugbugblue/trax/issues",
|
|
12
12
|
"license": "Apache-2.0",
|
|
13
13
|
"author": "Chad Transtrum <chad@transtrum.net>",
|
|
14
|
-
"
|
|
14
|
+
"exports": "./engine.js",
|
|
15
15
|
"repository": "gitlab:slugbugblue/trax",
|
|
16
16
|
"scripts": {
|
|
17
|
-
"test": "tape tests.js"
|
|
17
|
+
"test": "tape tests.js",
|
|
18
|
+
"preversion": "npm test",
|
|
19
|
+
"postversion": "git push && git push --tags"
|
|
18
20
|
},
|
|
19
21
|
"devDependencies": {
|
|
20
22
|
"eslint": "8.x",
|
|
@@ -22,7 +24,8 @@
|
|
|
22
24
|
"typescript": "^4.5.4",
|
|
23
25
|
"typescript-language-server": "^0.8.1"
|
|
24
26
|
},
|
|
27
|
+
"type": "module",
|
|
25
28
|
"engines": {
|
|
26
|
-
"node": ">=16.0.0"
|
|
29
|
+
"node": "^14.14.0 || >=16.0.0"
|
|
27
30
|
}
|
|
28
31
|
}
|
package/tty.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* Copyright 2021 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.
|
|
5
5
|
* You may obtain a copy of the License at
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
* Unless required by applicable law or agreed to in writing, software
|
|
10
10
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
11
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
@@ -13,17 +13,12 @@
|
|
|
13
13
|
* limitations under the License.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
//
|
|
16
|
+
// Ever wanted to draw Trax boards on the console? I have.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const Trax = require('./engine')
|
|
21
|
-
|
|
22
|
-
// fix Javascript's broken typeof
|
|
23
|
-
const TypeOf = o => ({}).toString.call(o).slice(8,-1)
|
|
18
|
+
import Trax from './engine.js'
|
|
24
19
|
|
|
25
20
|
const esc = '\x1b[' // start of console color escape code
|
|
26
|
-
const off = esc + 'm'
|
|
21
|
+
const off = esc + 'm' // color reset code
|
|
27
22
|
|
|
28
23
|
// display a message with optional color
|
|
29
24
|
const out = (message, fgcolor, bgcolor) => {
|
|
@@ -34,34 +29,39 @@ const out = (message, fgcolor, bgcolor) => {
|
|
|
34
29
|
console.log(ansi + message + off)
|
|
35
30
|
}
|
|
36
31
|
|
|
32
|
+
let set = 'trax' // which color set should we draw with?
|
|
37
33
|
|
|
38
34
|
// a little magic to display the game in progress
|
|
39
35
|
const color_sets = { // Colors for terminal sequence: <Esc>[38;5;<color_num>m
|
|
36
|
+
// b, B, w, W: color of line for each player, capital for a winning line
|
|
37
|
+
// e: empty (ie, board) color, t: tile color
|
|
40
38
|
trax: { b: 238, B: 57, e: 19, t: 74, w: 255, W: 184 },
|
|
41
39
|
trax8: { b: 236, B: 21, e: 58, t: 137, w: 254, W: 117 },
|
|
42
40
|
traxloop: { b: 235, B: 20, e: 17, t: 61, w: 253, W: 222 },
|
|
43
41
|
}
|
|
44
42
|
|
|
43
|
+
const ln = 'm\u2584' // lower half block -- with the leading m to terminate ansi color escape
|
|
44
|
+
const up = 'm\u2588' // full block
|
|
45
|
+
const lt = 'm\u2599' // lower half and left half block
|
|
46
|
+
const rt = 'm\u259f' // lower half and right half block
|
|
47
|
+
const fg = n => '38;5;' + String(set[n]) // foreground color_num
|
|
48
|
+
const bg = n => '48;5;' + String(set[n]) // background color_num
|
|
49
|
+
const sp = () => esc + bg('e') + 'm ' // lower space
|
|
50
|
+
// tiles are displayed on two lines; the top half of the first line is empty
|
|
51
|
+
// and the second half is composed of three characters, up color surrounded by tile color
|
|
52
|
+
const uptty = clr => esc + fg('t') + ';' + bg('e') + ln
|
|
53
|
+
+ esc + fg(clr) + ln + esc + fg('t') + ln + ' '
|
|
54
|
+
// the top half of the second line has both horizontal lines with a center transition
|
|
55
|
+
// while the bottom half is the tile background color surrounding the down color
|
|
56
|
+
const horiztty = clr => esc + fg('t') + ';' + bg(clr) + ln
|
|
57
|
+
const dntty = (clr, oppclr, char) => esc + fg(clr) + (char === up ? '' : ';' + bg(oppclr)) + char
|
|
58
|
+
// list of icon_char => tile line colors for easy reference in order up, right, down, left
|
|
59
|
+
const tiles = { a: 'bbww', b: 'bwbw', c: 'bwwb', d: 'wbbw', e: 'wbwb', f: 'wwbb' }
|
|
60
|
+
|
|
45
61
|
// return the ANSI codes to display a 3x1.5 block tile, one line at a time
|
|
46
|
-
const tile_tty = (
|
|
47
|
-
const ln = 'm\u2584' // lower half block -- with the leading m to terminate a terminal color escape sequence
|
|
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
|
|
54
|
-
// tiles are displayed on two lines; the top half of the first line is empty
|
|
55
|
-
// and the second half is composed of three characters, up color surrounded by tile color
|
|
56
|
-
const uptty = clr => esc + fg('t') + ';' + bg('e') + ln + esc + fg(clr) + ln + esc + fg('t') + ln + ' '
|
|
57
|
-
// the top half of the second line has both horizontal lines with a center transition
|
|
58
|
-
// while the bottom half is the tile background color surrounding the down color
|
|
59
|
-
const horiztty = clr => esc + fg('t') + ';' + bg(clr) + ln
|
|
60
|
-
const dntty = (clr, oppclr, char) => esc + fg(clr) + (char === up ? '' : ';' + bg(oppclr)) + char
|
|
61
|
-
// list of icon_char => tile line colors for easy reference in order up, right, down, left
|
|
62
|
-
const tiles = { a: 'bbww', b: 'bwbw', c: 'bwwb', d: 'wbbw', e: 'wbwb', f: 'wwbb' }
|
|
62
|
+
const tile_tty = (tile, top) => { // top: true for first line, false for second
|
|
63
63
|
// quick access to tile color and its opposite in upper case
|
|
64
|
-
|
|
64
|
+
let colors = { b: 'b', w: 'w', B: 'w', W: 'b' }
|
|
65
65
|
if (!(tile in tiles)) { // winning paths are represented by adding 6 to the tile char code
|
|
66
66
|
tile = String.fromCharCode(tile.charCodeAt(0) - 6)
|
|
67
67
|
if (tile in tiles) {
|
|
@@ -78,14 +78,14 @@ const tile_tty = (set, tile, top) => { // top: true for first line, false for se
|
|
|
78
78
|
if (top) return uptty(colors[tile[0]])
|
|
79
79
|
let center_char = tile[1] === tile[2] ? rt : tile[2] === tile[3] ? lt : up
|
|
80
80
|
let centertty = dntty(colors[tile[2]], colors[tile[2].toUpperCase()], center_char)
|
|
81
|
-
return horiztty(colors[tile[3]]) + centertty + horiztty(colors[tile[1]]) + sp
|
|
81
|
+
return horiztty(colors[tile[3]]) + centertty + horiztty(colors[tile[1]]) + sp()
|
|
82
82
|
}
|
|
83
83
|
if (top) return '??? '
|
|
84
84
|
return '\'\'\' '
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const print_icon = (icon, set_name) => {
|
|
88
|
-
|
|
88
|
+
set = color_sets[set_name] || color_sets.trax
|
|
89
89
|
const on = esc + '38;5;' + String(set.t) + ';48;5;' + String(set.e) + 'm'
|
|
90
90
|
let o1 = ' '
|
|
91
91
|
let o2 = '1 '
|
|
@@ -110,8 +110,8 @@ const print_icon = (icon, set_name) => {
|
|
|
110
110
|
o2 += ' '.repeat(empty * 4)
|
|
111
111
|
line_width += empty
|
|
112
112
|
} else {
|
|
113
|
-
o1 += tile_tty(
|
|
114
|
-
o2 += tile_tty(
|
|
113
|
+
o1 += tile_tty(c, 'top')
|
|
114
|
+
o2 += tile_tty(c)
|
|
115
115
|
line_width++
|
|
116
116
|
}
|
|
117
117
|
}
|
|
@@ -144,7 +144,7 @@ const trax_game = (game='trax', moves) => {
|
|
|
144
144
|
if (game instanceof Trax) {
|
|
145
145
|
trax = game
|
|
146
146
|
if (moves) {
|
|
147
|
-
if (
|
|
147
|
+
if (typeof moves === 'string') moves = moves.split(' ')
|
|
148
148
|
moves.forEach(move => trax.drop_tile(move))
|
|
149
149
|
}
|
|
150
150
|
} else {
|
|
@@ -169,7 +169,7 @@ const display_game = (game='trax', moves='') => {
|
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
|
|
172
|
+
export default {
|
|
173
173
|
display: display_game,
|
|
174
174
|
game: trax_game,
|
|
175
175
|
out,
|