@slugbugblue/trax 0.4.0 → 0.8.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/CHANGELOG.md +68 -17
- package/README.md +123 -69
- package/docs/engine.md +414 -0
- package/docs/point.md +134 -0
- package/package.json +33 -6
- package/src/cli.js +994 -0
- package/src/engine.js +657 -0
- package/src/point.js +116 -0
- package/src/tty.js +295 -0
- package/src/version.js +2 -0
- package/.eslintrc.yaml +0 -68
- package/engine.js +0 -535
- package/jsconfig.json +0 -8
- package/tty.js +0 -202
package/tty.js
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
/* Copyright 2021 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
|
-
// Ever wanted to draw Trax boards on the console? I have.
|
|
17
|
-
|
|
18
|
-
import Trax from './engine.js'
|
|
19
|
-
|
|
20
|
-
const esc = '\x1b[' // start of console color escape code
|
|
21
|
-
const off = esc + 'm' // color reset code
|
|
22
|
-
|
|
23
|
-
// display a message with optional color
|
|
24
|
-
const out = (message, fgcolor, bgcolor) => {
|
|
25
|
-
let ansi = fgcolor || bgcolor ? esc : ''
|
|
26
|
-
if (fgcolor) ansi += '38;5;' + fgcolor
|
|
27
|
-
if (bgcolor) ansi += '48;5;' + bgcolor
|
|
28
|
-
if (ansi) ansi += 'm'
|
|
29
|
-
console.log(ansi + message + off)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
let set = 'trax' // which color set should we draw with?
|
|
33
|
-
|
|
34
|
-
// a little magic to display the game in progress
|
|
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
|
|
38
|
-
trax: { b: 238, B: 57, e: 19, t: 74, w: 255, W: 184 },
|
|
39
|
-
trax8: { b: 236, B: 21, e: 58, t: 137, w: 254, W: 117 },
|
|
40
|
-
traxloop: { b: 235, B: 20, e: 17, t: 61, w: 253, W: 222 },
|
|
41
|
-
tentative: { b: 235, B: 18, e: 19, t: 244, w: 252, W: 217 },
|
|
42
|
-
}
|
|
43
|
-
|
|
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
|
|
51
|
-
// tiles are displayed on two lines; the top half of the first line is empty
|
|
52
|
-
// 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
|
-
// 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' }
|
|
61
|
-
|
|
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
|
|
64
|
-
// 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
|
|
69
|
-
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)
|
|
72
|
-
if (tile in tiles) {
|
|
73
|
-
colors.w = 'W'
|
|
74
|
-
colors.B = 'W'
|
|
75
|
-
} else { // and another six for a black win
|
|
76
|
-
tile = String.fromCharCode(tile.charCodeAt(0) - 6)
|
|
77
|
-
colors.b = 'B'
|
|
78
|
-
colors.W = 'B'
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
let val = false
|
|
82
|
-
if (tile in tiles) {
|
|
83
|
-
tile = tiles[tile]
|
|
84
|
-
if (top) {
|
|
85
|
-
val = uptty(colors[tile[0]])
|
|
86
|
-
} 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()
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
set = save
|
|
93
|
-
return val || (top ? '??? ' : '\'\'\' ')
|
|
94
|
-
}
|
|
95
|
-
|
|
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
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const print_icon = (icon, set_name) => {
|
|
103
|
-
set = color_sets[set_name] || color_sets.trax
|
|
104
|
-
merge_tentative(set)
|
|
105
|
-
const on = esc + '38;5;' + String(set.t) + ';48;5;' + String(set.e) + 'm'
|
|
106
|
-
let o1 = ' '
|
|
107
|
-
let o2 = '1 '
|
|
108
|
-
let row = 1
|
|
109
|
-
let width = 1
|
|
110
|
-
let info = []
|
|
111
|
-
let line_width = 0
|
|
112
|
-
while (icon.length) {
|
|
113
|
-
let c = icon[0]
|
|
114
|
-
icon = icon.slice(1)
|
|
115
|
-
if (c === ':') {
|
|
116
|
-
info.push({o: o1, w: line_width})
|
|
117
|
-
info.push({o: o2, w: line_width})
|
|
118
|
-
row++
|
|
119
|
-
width = Math.max(width, line_width)
|
|
120
|
-
line_width = 0
|
|
121
|
-
o1 = ' '
|
|
122
|
-
o2 = String(row).padEnd(3)
|
|
123
|
-
} else if (c >= '0' && c <= '9') {
|
|
124
|
-
let empty = Number(c) + 1
|
|
125
|
-
o1 += ' '.repeat(empty * 4)
|
|
126
|
-
o2 += ' '.repeat(empty * 4)
|
|
127
|
-
line_width += empty
|
|
128
|
-
} else {
|
|
129
|
-
o1 += tile_tty(c, 'top')
|
|
130
|
-
o2 += tile_tty(c)
|
|
131
|
-
line_width++
|
|
132
|
-
}
|
|
133
|
-
}
|
|
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)
|
|
138
|
-
}
|
|
139
|
-
let columns = process.stdout.columns || 0
|
|
140
|
-
let rows = process.stdout.rows || 0
|
|
141
|
-
if (width * 4 < columns - 4 && info.length < rows - 2) {
|
|
142
|
-
let col_out = ' '
|
|
143
|
-
for (let col = 1; col <= width; col++) {
|
|
144
|
-
col_out += ' ' + Trax.column(col).padEnd(3)
|
|
145
|
-
}
|
|
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))
|
|
148
|
-
console.info(on + ''.padEnd((width + 1) * 4) + off)
|
|
149
|
-
} else {
|
|
150
|
-
console.warn('Console is too small to display this game')
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// flexible way to get a Trax game object
|
|
155
|
-
// game = existing Trax instance, or a string indicating the variant name
|
|
156
|
-
// moves = optional list or string of moves to be made on the game
|
|
157
|
-
// returns a Trax instance
|
|
158
|
-
const trax_game = (game='trax', moves) => {
|
|
159
|
-
let trax
|
|
160
|
-
if (game instanceof Trax) {
|
|
161
|
-
trax = game
|
|
162
|
-
if (moves) {
|
|
163
|
-
if (typeof moves === 'string') moves = moves.split(' ')
|
|
164
|
-
moves.forEach(move => trax.drop_tile(move))
|
|
165
|
-
}
|
|
166
|
-
} else {
|
|
167
|
-
trax = new Trax(game, moves, 'cli')
|
|
168
|
-
}
|
|
169
|
-
return trax
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const display_game = (game='trax', moves='', tentative) => {
|
|
173
|
-
let trax = trax_game(game, moves)
|
|
174
|
-
let icon = trax.icon
|
|
175
|
-
if (tentative) {
|
|
176
|
-
let drops = trax.drop_tile(tentative)
|
|
177
|
-
if (drops.valid) {
|
|
178
|
-
icon = trax.tentative_icon(drops)
|
|
179
|
-
} else {
|
|
180
|
-
tentative = false
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
console.info(trax.notation)
|
|
184
|
-
print_icon(icon, trax.rules)
|
|
185
|
-
if (tentative) console.info('--- tentative position ---')
|
|
186
|
-
let name = {1: 'white', 2: 'black'}[trax.turn]
|
|
187
|
-
if (trax.over) {
|
|
188
|
-
if (trax.turn) {
|
|
189
|
-
console.info(name + ' wins')
|
|
190
|
-
} else {
|
|
191
|
-
console.info('game end' + (tentative ? 's' : 'ed') + ' in a draw')
|
|
192
|
-
}
|
|
193
|
-
} else if (trax.turn) {
|
|
194
|
-
console.info((tentative ? 'leaving ' : '') + name +' to play')
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export default {
|
|
199
|
-
display: display_game,
|
|
200
|
-
game: trax_game,
|
|
201
|
-
out,
|
|
202
|
-
}
|