@slugbugblue/trax 0.7.0 → 0.10.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/.c8rc.json +7 -0
- package/.gitattributes +2 -0
- package/CHANGELOG.md +27 -0
- package/README.md +87 -12
- package/docs/engine.md +18 -3
- package/docs/point.md +7 -0
- package/package.json +8 -4
- package/src/cli.js +623 -0
- package/src/cmds/delete.js +71 -0
- package/src/cmds/help.js +81 -0
- package/src/cmds/import-export.js +250 -0
- package/src/cmds/list.js +155 -0
- package/src/cmds/new.js +131 -0
- package/src/cmds/play-try.js +129 -0
- package/src/cmds/select.js +47 -0
- package/src/cmds/undo.js +40 -0
- package/src/cmds/view.js +53 -0
- package/{engine.js → src/engine.js} +20 -13
- package/{point.js → src/point.js} +12 -0
- package/{tty.js → src/tty.js} +88 -15
- package/src/version.js +2 -0
|
@@ -0,0 +1,129 @@
|
|
|
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.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// CLI play/try commands
|
|
17
|
+
|
|
18
|
+
const moveRx = /^\d+\.?$/
|
|
19
|
+
const notationRx = /^[@a-z]+\d+[bps/\\+]$/i
|
|
20
|
+
|
|
21
|
+
export const playCmd = {
|
|
22
|
+
name: 'play',
|
|
23
|
+
alt: ['move', 'mv'],
|
|
24
|
+
args: '<move> [...]',
|
|
25
|
+
comp: '<play>',
|
|
26
|
+
desc: 'play a move',
|
|
27
|
+
rx: [moveRx, notationRx],
|
|
28
|
+
help: [
|
|
29
|
+
'Enter the move in standard Trax notation. The final character of the',
|
|
30
|
+
'notation can be replaced with a letter for ease of entering at the',
|
|
31
|
+
'command line. Use <s> for "/", <b> for "\\", and <p> for "+".',
|
|
32
|
+
'Multiple moves can be submitted at once. Move numbers are optional,',
|
|
33
|
+
'but if included, they will be checked for accuracy.',
|
|
34
|
+
],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const tryCmd = {
|
|
38
|
+
name: 'try',
|
|
39
|
+
alt: 'see',
|
|
40
|
+
args: '<move>',
|
|
41
|
+
comp: '<play>',
|
|
42
|
+
desc: 'see the effects of a move',
|
|
43
|
+
help: [
|
|
44
|
+
'View a move without committing to it. This is useful for seeing the',
|
|
45
|
+
'effects of forced tiles.',
|
|
46
|
+
],
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const fixNotation = (move) => {
|
|
50
|
+
if (move.endsWith('b')) return move.slice(0, -1) + '\\'
|
|
51
|
+
if (move.endsWith('s')) return move.slice(0, -1) + '/'
|
|
52
|
+
if (move.endsWith('p')) return move.slice(0, -1) + '+'
|
|
53
|
+
return move
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
playCmd.fn = (CLI, ...moves) => {
|
|
57
|
+
if (moves.length === 0 || !moves[0]) {
|
|
58
|
+
CLI.error('You must provide a move.')
|
|
59
|
+
return CLI.do('help', 'play')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!CLI.GAME?.id) {
|
|
63
|
+
CLI.do('new')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (CLI.TRAX.over) {
|
|
67
|
+
return CLI.error('The game is over.')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const start = CLI.TRAX.move
|
|
71
|
+
let checkMove
|
|
72
|
+
|
|
73
|
+
for (let move of moves) {
|
|
74
|
+
if (notationRx.test(move)) {
|
|
75
|
+
const moveNumber = CLI.TRAX.move
|
|
76
|
+
move = fixNotation(move)
|
|
77
|
+
if (checkMove) {
|
|
78
|
+
CLI.TRAX.playMove(checkMove, move)
|
|
79
|
+
} else {
|
|
80
|
+
CLI.TRAX.play(move)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (CLI.TRAX.move === moveNumber) {
|
|
84
|
+
CLI.error(
|
|
85
|
+
'Move "' +
|
|
86
|
+
(checkMove ? checkMove + '. ' : '') +
|
|
87
|
+
move +
|
|
88
|
+
'" is invalid.',
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
} else if (moveRx.test(move)) {
|
|
92
|
+
checkMove = Number(move)
|
|
93
|
+
} else if (move) {
|
|
94
|
+
CLI.error('Move "' + move + '" is not in the correct notation.')
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (CLI.TRAX.move !== start) {
|
|
99
|
+
CLI.updateGameData()
|
|
100
|
+
CLI.do('view')
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
tryCmd.fn = (CLI, move) => {
|
|
105
|
+
if (move.length === 0) {
|
|
106
|
+
CLI.error('You must provide a move.')
|
|
107
|
+
return CLI.do('help', 'try')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!CLI.TRAX) {
|
|
111
|
+
CLI.do('new')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (CLI.TRAX.over) {
|
|
115
|
+
return CLI.error('The game is over.')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (notationRx.test(move)) {
|
|
119
|
+
move = fixNotation(move)
|
|
120
|
+
const play = CLI.TRAX.dropTile(move, undefined, 'tentative')
|
|
121
|
+
if (play.valid || play.dropped.length > 0) {
|
|
122
|
+
CLI.display(CLI.TRAX, CLI.GAME.players, move)
|
|
123
|
+
} else {
|
|
124
|
+
CLI.error('Move is invalid.')
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
CLI.error('Move is invalid.')
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
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.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// CLI select command
|
|
17
|
+
|
|
18
|
+
export const selectCmd = {
|
|
19
|
+
name: 'select',
|
|
20
|
+
alt: ['choose', '#'],
|
|
21
|
+
args: '#id [command]',
|
|
22
|
+
comp: '<id> <cmd>',
|
|
23
|
+
desc: 'select a different game',
|
|
24
|
+
rx: /^#\d+$/,
|
|
25
|
+
help: [
|
|
26
|
+
'Make another game the default game of future commands. If another',
|
|
27
|
+
'command is included, that command will be run immediately.',
|
|
28
|
+
],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
selectCmd.fn = (CLI, id, ...cmds) => {
|
|
32
|
+
if (!id) {
|
|
33
|
+
CLI.error('Missing id.')
|
|
34
|
+
return CLI.do('help', 'select')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (id.startsWith('#')) id = id.slice(1)
|
|
38
|
+
CLI.load(id)
|
|
39
|
+
if (String(CLI.GAME?.id) === id) {
|
|
40
|
+
CLI.out(CLI.color(`Game #${id} selected.`))
|
|
41
|
+
if (cmds.length > 0) {
|
|
42
|
+
CLI.doNext(cmds.join(' '))
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
CLI.error('Invalid id. Type "list" to see available games.')
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/cmds/undo.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
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.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// CLI undo command
|
|
17
|
+
|
|
18
|
+
export const undoCmd = {
|
|
19
|
+
name: 'undo',
|
|
20
|
+
args: '[n]',
|
|
21
|
+
comp: '<move>',
|
|
22
|
+
desc: 'undo the last move in a game',
|
|
23
|
+
help: [
|
|
24
|
+
'Backtrack one or more moves in a game. Type "undo" to take back the last',
|
|
25
|
+
'move, or "undo 2" to undo the last two moves.',
|
|
26
|
+
],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
undoCmd.fn = (CLI, n) => {
|
|
30
|
+
n = Math.max(1, Math.floor(Number(n) || 1)) // Get an integer 1 or higher
|
|
31
|
+
|
|
32
|
+
const { moves } = CLI.TRAX
|
|
33
|
+
const undo = moves.slice(0, 0 - n)
|
|
34
|
+
if (moves.length === undo.length) {
|
|
35
|
+
CLI.error('Unable to undo.')
|
|
36
|
+
} else {
|
|
37
|
+
CLI.setGame(undo)
|
|
38
|
+
CLI.do('view')
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/cmds/view.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
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.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// CLI view command
|
|
17
|
+
|
|
18
|
+
export const viewCmd = {
|
|
19
|
+
name: 'view',
|
|
20
|
+
alt: ['show', 'display'],
|
|
21
|
+
args: '[move number]',
|
|
22
|
+
comp: '<move>',
|
|
23
|
+
desc: 'show the game board',
|
|
24
|
+
help: [
|
|
25
|
+
'Show the current game. Include a move number to see the board as it was at',
|
|
26
|
+
'that move.',
|
|
27
|
+
],
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
viewCmd.fn = (CLI, move) => {
|
|
31
|
+
if (!CLI.GAME?.id) {
|
|
32
|
+
return CLI.error('No active game. Type "new" to start a game.')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const trax = CLI.TRAX
|
|
36
|
+
|
|
37
|
+
let show = Number.POSITIVE_INFINITY
|
|
38
|
+
if (move && Number(move) >= 0 && Number(move) <= trax.moves.length) {
|
|
39
|
+
show = Number(move)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const players = CLI.GAME.players || ['white', 'black']
|
|
43
|
+
|
|
44
|
+
CLI.out(
|
|
45
|
+
CLI.color('#' + CLI.ID + ' ') +
|
|
46
|
+
CLI.name(trax.rules) +
|
|
47
|
+
' ' +
|
|
48
|
+
CLI.bubble('w' + (trax.turn === 1 ? 'h' : ''), players[0]) +
|
|
49
|
+
CLI.color(' vs ') +
|
|
50
|
+
CLI.bubble('b' + (trax.turn === 2 ? 'h' : ''), players[1]),
|
|
51
|
+
)
|
|
52
|
+
CLI.display(trax, players, false, show)
|
|
53
|
+
}
|
|
@@ -24,7 +24,7 @@ import { Point } from './point.js'
|
|
|
24
24
|
// Fun trax helper constants
|
|
25
25
|
|
|
26
26
|
const zero = new Point(0, 0)
|
|
27
|
-
const notationRegex = /^
|
|
27
|
+
const notationRegex = /^([@a-z]+)(\d+)([/\\+])$/i
|
|
28
28
|
|
|
29
29
|
const slash = { a: '\\', b: '+', c: '/', d: '/', e: '+', f: '\\' }
|
|
30
30
|
|
|
@@ -72,7 +72,13 @@ const codeRowLength = (row) =>
|
|
|
72
72
|
// This is where the magic happens
|
|
73
73
|
export const Trax = class {
|
|
74
74
|
// Static class properties
|
|
75
|
-
static
|
|
75
|
+
static names = {
|
|
76
|
+
trax: 'Trax',
|
|
77
|
+
traxloop: 'Loop Trax',
|
|
78
|
+
trax8: '8x8 Trax',
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static variants = new Set(Object.keys(this.names)) // Known variants
|
|
76
82
|
|
|
77
83
|
// Static class methods
|
|
78
84
|
static point = (x, y) => new Point(x, y)
|
|
@@ -122,13 +128,9 @@ export const Trax = class {
|
|
|
122
128
|
// Moves might be a notation string
|
|
123
129
|
if (moves) {
|
|
124
130
|
if (typeof moves === 'string') moves = moves.split(/\s+/)
|
|
125
|
-
for (
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (notationRegex.test(move.notation)) {
|
|
131
|
-
this.playMove(move.move, move.notation)
|
|
131
|
+
for (const move of moves) {
|
|
132
|
+
if (notationRegex.test(move)) {
|
|
133
|
+
this.dropTile(move)
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
}
|
|
@@ -170,6 +172,11 @@ export const Trax = class {
|
|
|
170
172
|
}
|
|
171
173
|
}
|
|
172
174
|
|
|
175
|
+
get name() {
|
|
176
|
+
// Get the name of this variant
|
|
177
|
+
return Trax.names[this.rules]
|
|
178
|
+
}
|
|
179
|
+
|
|
173
180
|
get count() {
|
|
174
181
|
// Get the number of tiles currently in play
|
|
175
182
|
return Object.keys(this.tiles).length
|
|
@@ -433,9 +440,9 @@ export const Trax = class {
|
|
|
433
440
|
return this.dropTile(notation)
|
|
434
441
|
}
|
|
435
442
|
|
|
436
|
-
playMove(
|
|
437
|
-
// Generic API call for
|
|
438
|
-
if (
|
|
443
|
+
playMove(moveNumber, notation) {
|
|
444
|
+
// Generic API call for playing a move with move number safety
|
|
445
|
+
if (moveNumber === this.move + 1) return this.dropTile(notation)
|
|
439
446
|
return { valid: false }
|
|
440
447
|
}
|
|
441
448
|
|
|
@@ -482,7 +489,7 @@ export const Trax = class {
|
|
|
482
489
|
loc = decoded.loc
|
|
483
490
|
}
|
|
484
491
|
|
|
485
|
-
if (this.validLocation(loc) && this.validTile(type, loc)) {
|
|
492
|
+
if (loc && this.validLocation(loc) && this.validTile(type, loc)) {
|
|
486
493
|
valid = true
|
|
487
494
|
const saved = this.save() // Just in case we need to roll back
|
|
488
495
|
notation = this.notate(type, loc) // Determine notation BEFORE we play the move
|
|
@@ -23,6 +23,8 @@ export const Point = class {
|
|
|
23
23
|
#x = 0
|
|
24
24
|
#y = 0
|
|
25
25
|
|
|
26
|
+
static dirs = ['up', 'down', 'left', 'right']
|
|
27
|
+
|
|
26
28
|
constructor(x, y) {
|
|
27
29
|
if (is(x, 'obj') && is(x.x, 'num') && is(x.y, 'num')) {
|
|
28
30
|
// You can call this with new Point(point) or new Point({x,y})
|
|
@@ -76,6 +78,7 @@ export const Point = class {
|
|
|
76
78
|
dir(direction) {
|
|
77
79
|
const error = 'direction must be one of: up, down, left, right, u, d, l, r'
|
|
78
80
|
if (!is(direction, 'str')) throw new SyntaxError(error)
|
|
81
|
+
direction = direction.toLowerCase()
|
|
79
82
|
if (direction[0] === 'u') return this.up
|
|
80
83
|
if (direction[0] === 'd') return this.down
|
|
81
84
|
if (direction[0] === 'l') return this.left
|
|
@@ -99,6 +102,15 @@ export const Point = class {
|
|
|
99
102
|
return this.#x === that.x && this.#y === that.y
|
|
100
103
|
}
|
|
101
104
|
|
|
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
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
102
114
|
// Play nicely with the rest of the world
|
|
103
115
|
|
|
104
116
|
toString() {
|
package/{tty.js → src/tty.js}
RENAMED
|
@@ -22,13 +22,17 @@ import { Trax } from './engine.js'
|
|
|
22
22
|
const esc = '\u001B[' // Start of console color escape code
|
|
23
23
|
const off = esc + 'm' // Color reset code
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// Colorize a string
|
|
26
|
+
export const color = (contents = '', fgcolor = '', bgcolor = '') => {
|
|
27
|
+
const c = []
|
|
28
|
+
if (fgcolor) c.push('38;5;' + fgcolor)
|
|
29
|
+
if (bgcolor) c.push('48;5;' + bgcolor)
|
|
30
|
+
return esc + c.join(';') + 'm' + contents
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Display a message with optional color
|
|
26
34
|
export const outty = (message, fgcolor, bgcolor) => {
|
|
27
|
-
|
|
28
|
-
if (fgcolor) ansi += '38;5;' + fgcolor
|
|
29
|
-
if (bgcolor) ansi += '48;5;' + bgcolor
|
|
30
|
-
if (ansi) ansi += 'm'
|
|
31
|
-
console.log(ansi + message + off)
|
|
35
|
+
console.log(color(message, fgcolor, bgcolor) + off)
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
let set = 'trax' // Which color set should we draw with?
|
|
@@ -44,6 +48,28 @@ const colorSets = {
|
|
|
44
48
|
tentative: { b: 235, B: 18, e: 19, t: 244, w: 252, W: 217 },
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
export const name = (rules) => {
|
|
52
|
+
if (Trax.variants.has(rules)) {
|
|
53
|
+
return color(Trax.names[rules], colorSets[rules].t)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return rules
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const bubble = (clr, text) => {
|
|
60
|
+
const colors = {
|
|
61
|
+
w: { fg: 236, bg: 153 },
|
|
62
|
+
b: { fg: 250, bg: 18 },
|
|
63
|
+
wd: { fg: 236, bg: 248 },
|
|
64
|
+
bd: { fg: 248, bg: 236 },
|
|
65
|
+
wh: { fg: 63, bg: 123 },
|
|
66
|
+
bh: { fg: 220, bg: 20 },
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const c = colors[clr]
|
|
70
|
+
return color('', c.bg) + color(text, c.fg, c.bg) + off + color('', c.bg)
|
|
71
|
+
}
|
|
72
|
+
|
|
47
73
|
const ln = 'm\u2584' // Lower half block -- with the leading m to terminate ansi color escape
|
|
48
74
|
const up = 'm\u2588' // Full block
|
|
49
75
|
const lt = 'm\u2599' // Lower half and left half block
|
|
@@ -201,29 +227,76 @@ export const traxGame = (game = 'trax', moves = '') => {
|
|
|
201
227
|
return trax
|
|
202
228
|
}
|
|
203
229
|
|
|
204
|
-
|
|
205
|
-
|
|
230
|
+
const printNotation = (trax, tentative, show = Number.POSITIVE_INFINITY) => {
|
|
231
|
+
let b = false
|
|
232
|
+
let n = 1
|
|
233
|
+
let out = ''
|
|
234
|
+
let length = 0
|
|
235
|
+
const t = tentative ? trax.moves.length - 1 : show
|
|
236
|
+
for (const move of trax.moves) {
|
|
237
|
+
const c = (b ? 'b' : 'w') + (t < n ? 'd' : show === n ? 'h' : '')
|
|
238
|
+
const notation = String(n) + ' ' + move
|
|
239
|
+
if (length + notation.length + 3 > process.stdout.columns) {
|
|
240
|
+
out += '\n'
|
|
241
|
+
length = 0
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
out += bubble(c, notation) + ' '
|
|
245
|
+
length += notation.length + 3
|
|
246
|
+
b = !b
|
|
247
|
+
n++
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
outty(out)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export const display = (
|
|
254
|
+
game,
|
|
255
|
+
players,
|
|
256
|
+
tentative = false,
|
|
257
|
+
showMove = Number.POSITIVE_INFINITY,
|
|
258
|
+
) => {
|
|
259
|
+
const trax = traxGame(game)
|
|
260
|
+
const saved = trax.save()
|
|
261
|
+
const colors = ['white', 'black']
|
|
262
|
+
if (!Array.isArray(players)) players = colors
|
|
206
263
|
let { icon } = trax
|
|
207
264
|
if (tentative) {
|
|
208
265
|
const drops = trax.dropTile(tentative)
|
|
209
|
-
if (drops.valid) {
|
|
266
|
+
if (drops.valid || drops.dropped.length > 0) {
|
|
210
267
|
icon = trax.dropsIcon(drops)
|
|
211
268
|
} else {
|
|
212
269
|
tentative = false
|
|
213
270
|
}
|
|
271
|
+
} else if (showMove <= trax.moves.length) {
|
|
272
|
+
icon = trax.dropsIcon({
|
|
273
|
+
dropped: Object.values(trax.tiles).filter((t) => t.move > showMove),
|
|
274
|
+
})
|
|
214
275
|
}
|
|
215
276
|
|
|
216
|
-
|
|
277
|
+
printNotation(trax, tentative, showMove)
|
|
217
278
|
printIcon(icon, trax.rules)
|
|
218
|
-
|
|
219
|
-
|
|
279
|
+
const nameColor = { 1: 'w', 2: 'b' }[trax.turn]
|
|
280
|
+
if (tentative) {
|
|
281
|
+
const otherColor = { w: 'bd', b: 'wd' }[nameColor] || 'wd'
|
|
282
|
+
outty('---' + bubble(otherColor, 'tentative position') + off + '---')
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const name = players[trax.turn - 1] || colors[trax.turn - 1]
|
|
220
286
|
if (trax.over) {
|
|
221
287
|
if (trax.turn) {
|
|
222
|
-
|
|
288
|
+
outty(bubble(nameColor + 'h', name) + off + ' wins')
|
|
223
289
|
} else {
|
|
224
|
-
|
|
290
|
+
outty('game end' + (tentative ? 's' : 'ed') + ' in a draw')
|
|
225
291
|
}
|
|
226
292
|
} else if (trax.turn) {
|
|
227
|
-
|
|
293
|
+
outty(
|
|
294
|
+
(tentative ? 'leaving ' : '') +
|
|
295
|
+
bubble(nameColor, name) +
|
|
296
|
+
off +
|
|
297
|
+
' to play',
|
|
298
|
+
)
|
|
228
299
|
}
|
|
300
|
+
|
|
301
|
+
if (tentative) trax.restore(saved)
|
|
229
302
|
}
|
package/src/version.js
ADDED