schachnovelle 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -4,8 +4,8 @@ _»Um Gottes Willen! Nicht!«_
4
4
  [![Find the code on GitLab](https://upload.wikimedia.org/wikipedia/commons/d/d9/Chess_clt45.svg)](https://gitlab.com/manegame/schachnovelle)
5
5
 
6
6
 
7
+ _[manusnijhoff.nl](https://manusnijhoff.nl)_,
7
8
  _[Read the book](https://en.wikipedia.org/wiki/The_Royal_Game)_, _[Play me on lichess](https://lichess.org/@/manegame)_,
8
- _[Check out my website](https://manusnijhoff.nl)_
9
9
 
10
10
 
11
11
  ## Installation
@@ -23,13 +23,11 @@ or npm
23
23
  ...And play!
24
24
 
25
25
  ## Features
26
- There is no check detection yet. Play with your head! For now...
27
-
28
26
  You can play against yourself or a friend on the same keyboard.
29
27
 
30
28
  ## Roadmap
31
29
 
32
- I'm slowly working on connecting this to the [lichess](https://lichess.org/) API, so you can play others from the comfort of the command line!
30
+ I'd love to connect this to the [lichess](https://lichess.org/) API, so you can play others from the comfort of the command line!
33
31
 
34
32
  ## Changelog
35
33
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schachnovelle",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Chess to play on the command line",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -25,8 +25,10 @@
25
25
  "express": "^4.17.1",
26
26
  "express-session": "^1.17.1",
27
27
  "memorystore": "^1.6.4",
28
+ "ndjson": "^2.0.0",
28
29
  "node-fetch": "^2.6.0",
29
30
  "open": "^7.3.0",
31
+ "pug": "^3.0.0",
30
32
  "simple-oauth2": "^4.1.0"
31
33
  }
32
34
  }
package/server/api.js CHANGED
@@ -1,24 +1,132 @@
1
1
  const axios = require('axios')
2
+ const https = require('https')
3
+ const ndjson = require('ndjson')
2
4
 
3
5
  module.exports = {
6
+
7
+ /**
8
+ * get the user's information
9
+ * @param {*} token
10
+ */
4
11
  getUserInfo (token) {
5
- return axios.get('/api/account', {
6
- baseURL: 'https://lichess.org/',
7
- headers: { 'Authorization': 'Bearer ' + token.access_token }
12
+ return new Promise((resolve, reject) => {
13
+ axios.get('/api/account', {
14
+ baseURL: 'https://lichess.org/',
15
+ headers: { 'Authorization': 'Bearer ' + token.access_token }
16
+ })
17
+ .then((response) => {
18
+ resolve(response)
19
+ })
20
+ .catch((error) => {
21
+ reject(error)
22
+ })
8
23
  })
9
24
  },
10
25
 
11
- streamEvents (token) {
12
- return axios.get('/api/stream/event', {
13
- baseURL: 'https://lichess.org/',
14
- headers: { 'Authorization': 'Bearer ' + token.access_token }
26
+ /**
27
+ * Create an event stream
28
+ * @param {*} token
29
+ */
30
+ createEventStream (token) {
31
+ const options = {
32
+ headers: {
33
+ 'Authorization': 'Bearer ' + token.access_token
34
+ }
35
+ }
36
+
37
+ return new Promise((resolve, reject) => {
38
+ const req = https.get('https://lichess.org/api/stream/event', options)
39
+
40
+ req
41
+ .on('response', (stream) => {
42
+ const { statusCode } = stream
43
+
44
+ if (statusCode >= 200 && statusCode < 300) {
45
+ resolve(stream)
46
+ }
47
+ })
48
+ .on('error', (error) => {
49
+ reject(error)
50
+ })
15
51
  })
16
52
  },
53
+
54
+ createGameStream (token, gameId) {
55
+ const options = {
56
+ headers: {
57
+ 'Authorization': 'Bearer ' + token.access_token
58
+ }
59
+ }
60
+
61
+ return new Promise((resolve, reject) => {
62
+ const req = https.get(`https://lichess.org/api/board/game/stream/${gameId}`, options)
17
63
 
18
- createSeek (token) {
19
- return axios.post('/api/board/seek', {
20
- baseURL: 'https://lichess.org',
21
- headers: { 'Authorization': 'Bearer ' + token.access_token }
64
+ req
65
+ .on('response', (stream) => {
66
+ const { statusCode } = stream
67
+
68
+ if (statusCode >= 200 && statusCode < 300) {
69
+ resolve(stream)
70
+ }
71
+ })
72
+ .on('error', (error) => {
73
+ reject(error)
74
+ })
75
+ })
76
+ },
77
+
78
+ // Commence play after the user has been identified
79
+ startPlay (req, res) {
80
+ return new Promise(async (resolve, reject) => {
81
+ try {
82
+ const stream = await this.createEventStream(req.session.accessToken)
83
+ stream
84
+ .pipe(ndjson.parse())
85
+ .on('data', (data) => {
86
+ // Always write the data type to the stream
87
+ process.stdout.write(data.type)
88
+
89
+ // Hook into events from event stream!
90
+ if (data.type === 'gameStart') {
91
+ req.session.currentGame = data.game
92
+ this.openGameStream(req)
93
+ } else {
94
+ console.log('!!! unhandled data type !!!')
95
+ console.log(data)
96
+ console.log('!!! end unhandled data type !!!')
97
+ }
98
+ })
99
+ } catch (error) {
100
+ reject(error)
101
+ } finally {
102
+ resolve({ status: 200 })
103
+ }
22
104
  })
105
+ },
106
+
107
+ // Tryout to follow a game
108
+ async openGameStream (req) {
109
+ try {
110
+ const stream = await this.createGameStream(req.session.accessToken, req.session.currentGame.id)
111
+
112
+ stream
113
+ .pipe(ndjson.parse())
114
+ .on('data', (data) => {
115
+ // Write data type to stdout
116
+ process.stdout.write(data.type)
117
+
118
+ if (data.type === 'gameFull') {
119
+ req.session.currentGame.data = data
120
+ } else if (data.type === 'gameState') {
121
+ req.session.currentGame.data.gameState = data
122
+ } else {
123
+ console.log('UNUSED DATA TYPE', data.type)
124
+ }
125
+ })
126
+ } catch (error) {
127
+ console.error(error)
128
+ }
23
129
  }
130
+
131
+
24
132
  }
package/server/index.js CHANGED
@@ -1,30 +1,41 @@
1
1
  require('dotenv').config()
2
2
 
3
+ //
4
+ //
5
+ // Imports
3
6
  const express = require('express')
4
- const session = require('express-session')
7
+ // Storage
5
8
  const open = require('open')
9
+ const session = require('express-session')
6
10
  const MemoryStore = require('memorystore')(session)
7
- const simpleOauth = require('simple-oauth2')
8
- const api = require('./api')
11
+ // Lichess
12
+ const lichess = require('./api')
9
13
 
10
14
  /* Create your lichess OAuth app on https://lichess.org/account/oauth/app/create
11
15
  * Homepage URL: http://localhost:3000
12
16
  * Callback URL: http://localhost:3000/callback
13
17
  */
14
18
 
19
+
20
+
15
21
  //
16
22
  //
17
23
  // Server + Auth config
18
24
  const port = 3000
25
+ const host = process.env.NODE_ENV === 'production' ? 'https://schachnovelle.online' : 'http://localhost'
19
26
  const clientId = process.env.LICHESS_ID
20
27
  const clientSecret = process.env.LICHESS_SECRET
21
28
  const redirectUri = `http://localhost:${port}/callback`
22
29
 
30
+ // Scopes
23
31
  const scopes = [
24
32
  'preference:read',
25
33
  'challenge:read',
26
- 'board:play'
27
- ];
34
+ 'board:play',
35
+ 'bot:play'
36
+ ];
37
+
38
+
28
39
 
29
40
  //
30
41
  //
@@ -45,13 +56,25 @@ const oAuthConfig = {
45
56
  }
46
57
  }
47
58
 
48
- const { ClientCredentials, ResourceOwnerPassword, AuthorizationCode } = require('simple-oauth2')
59
+ const { AuthorizationCode } = require('simple-oauth2')
49
60
  const client = new AuthorizationCode(oAuthConfig)
50
61
 
62
+
63
+
51
64
  //
52
65
  //
53
66
  // Initialize the server
54
67
  const app = express();
68
+
69
+
70
+
71
+ //
72
+ //
73
+ // Set things
74
+ app.set('view engine', 'pug')
75
+
76
+
77
+
55
78
  //
56
79
  //
57
80
  // Session
@@ -70,7 +93,15 @@ app.use(session({
70
93
  //
71
94
  //
72
95
  // Show the "log in with lichess" button
73
- app.get('/', (req, res) => res.send('Hello<br><a href="/auth">Log in with lichess</a>'));
96
+ app.get('/', (req, res) => {
97
+ if (req.session.userInfo) {
98
+ res.redirect('/welcome')
99
+ } else {
100
+ res.render('../views/index')
101
+ }
102
+ })
103
+
104
+
74
105
 
75
106
  //
76
107
  //
@@ -85,7 +116,8 @@ app.get('/auth', (req, res) => {
85
116
  })
86
117
 
87
118
  res.redirect(authorizationUri)
88
- });
119
+ })
120
+
89
121
 
90
122
  //
91
123
  //
@@ -98,10 +130,10 @@ app.get('/callback', async (req, res) => {
98
130
  }
99
131
 
100
132
  try {
101
- console.log('creating token')
102
133
  const result = await client.getToken(tokenParams)
103
134
  const accessToken = client.createToken(result)
104
-
135
+
136
+ // Save token to the session
105
137
  req.session.accessToken = accessToken.token.token
106
138
 
107
139
  res.redirect('/welcome')
@@ -110,35 +142,303 @@ app.get('/callback', async (req, res) => {
110
142
  }
111
143
  })
112
144
 
145
+
146
+
113
147
  //
114
148
  //
115
149
  // Welcome page
116
150
  app.get('/welcome', async (req, res) => {
117
- if (req.session.accessToken) {
118
- console.log('Getting user information')
119
- const userInfo = await api.getUserInfo(req.session.accessToken);
120
-
121
- // open a stream for events
122
- // const stream = await api.streamEvents(accessToken.token.token)
123
-
124
- // console.log(stream)
125
- // stream.on('data', (data) => {
126
- // console.log(data)
127
- // })
128
-
129
- // seek a game
130
- // const seek = api.createSeek(accessToken.token.token)
131
- // console.log(seek)
132
-
133
- // send the webpage response
134
- res.send(`<h1>Success!</h1>Your lichess user info: <pre>${JSON.stringify(userInfo.data)}</pre>`)
135
- } else {
136
- res.redirect('/')
151
+ if (req.session.accessToken) {
152
+ try {
153
+ const userInfo = await lichess.getUserInfo(req.session.accessToken)
154
+ req.session.userInfo = userInfo.data
155
+ } catch (error) {
156
+ console.error(error)
157
+ } finally {
158
+ if (req.session.userInfo) {
159
+ lichess.startPlay(req, res)
160
+ .then((response) => {
161
+ if (response.status === 200) {
162
+ // This goes to the UI
163
+ process.stdout.write('streamReady')
164
+ // This to the browser
165
+ res.render('../views/welcome', { user: req.session.userInfo })
166
+ } else {
167
+ res.render('../views/error', response)
168
+ }
169
+ })
170
+ .catch((error) => {
171
+ console.error(error)
172
+ res.render('../views/error', error)
173
+ })
174
+ } else {
175
+ res.render('../views/error')
176
+ }
137
177
  }
178
+
179
+ } else {
180
+ res.redirect('/')
181
+ }
138
182
  })
139
183
 
140
184
  app.listen(port, async () => {
141
- await open(`http://localhost:${port}`)
185
+ await open(`${host}:${port}`)
142
186
  });
143
187
 
144
188
 
189
+
190
+ // SAMPLE DATA
191
+
192
+ // { type: 'gameStart', game: { id: 'Wmv3QOtD' } }
193
+ // {
194
+ // id: 'Wmv3QOtD',
195
+ // variant: { key: 'standard', name: 'Standard', short: 'Std' },
196
+ // clock: { initial: 600000, increment: 3000 },
197
+ // speed: 'rapid',
198
+ // perf: { name: 'Rapid' },
199
+ // rated: false,
200
+ // createdAt: 1612916752568,
201
+ // white: { id: 'manegame', name: 'manegame', title: null, rating: 1155 },
202
+ // black: {
203
+ // id: 'schachnovelle-online',
204
+ // name: 'schachnovelle-online',
205
+ // title: null,
206
+ // rating: 1500,
207
+ // provisional: true
208
+ // },
209
+ // initialFen: 'startpos',
210
+ // type: 'gameFull',
211
+ // state: {
212
+ // type: 'gameState',
213
+ // moves: '',
214
+ // wtime: 600000,
215
+ // btime: 600000,
216
+ // winc: 3000,
217
+ // binc: 3000,
218
+ // wdraw: false,
219
+ // bdraw: false,
220
+ // status: 'started'
221
+ // }
222
+ // }
223
+ // {
224
+ // type: 'gameState',
225
+ // moves: 'e2e4',
226
+ // wtime: 600000,
227
+ // btime: 600000,
228
+ // winc: 3000,
229
+ // binc: 3000,
230
+ // wdraw: false,
231
+ // bdraw: false,
232
+ // status: 'started'
233
+ // }
234
+ // {
235
+ // type: 'gameState',
236
+ // moves: 'e2e4 e7e5',
237
+ // wtime: 600000,
238
+ // btime: 600000,
239
+ // winc: 3000,
240
+ // binc: 3000,
241
+ // wdraw: false,
242
+ // bdraw: false,
243
+ // status: 'started'
244
+ // }
245
+ // {
246
+ // type: 'gameState',
247
+ // moves: 'e2e4 e7e5 g1f3',
248
+ // wtime: 599590,
249
+ // btime: 600000,
250
+ // winc: 3000,
251
+ // binc: 3000,
252
+ // wdraw: false,
253
+ // bdraw: false,
254
+ // status: 'started'
255
+ // }
256
+ // {
257
+ // type: 'gameState',
258
+ // moves: 'e2e4 e7e5 g1f3 c7c6',
259
+ // wtime: 599590,
260
+ // btime: 597200,
261
+ // winc: 3000,
262
+ // binc: 3000,
263
+ // wdraw: false,
264
+ // bdraw: false,
265
+ // status: 'started'
266
+ // }
267
+ // {
268
+ // type: 'gameState',
269
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4',
270
+ // wtime: 598580,
271
+ // btime: 597200,
272
+ // winc: 3000,
273
+ // binc: 3000,
274
+ // wdraw: false,
275
+ // bdraw: false,
276
+ // status: 'started'
277
+ // }
278
+ // {
279
+ // type: 'gameState',
280
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5',
281
+ // wtime: 598580,
282
+ // btime: 595560,
283
+ // winc: 3000,
284
+ // binc: 3000,
285
+ // wdraw: false,
286
+ // bdraw: false,
287
+ // status: 'started'
288
+ // }
289
+ // {
290
+ // type: 'gameState',
291
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5',
292
+ // wtime: 592540,
293
+ // btime: 595560,
294
+ // winc: 3000,
295
+ // binc: 3000,
296
+ // wdraw: false,
297
+ // bdraw: false,
298
+ // status: 'started'
299
+ // }
300
+ // {
301
+ // type: 'gameState',
302
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5',
303
+ // wtime: 592540,
304
+ // btime: 595370,
305
+ // winc: 3000,
306
+ // binc: 3000,
307
+ // wdraw: false,
308
+ // bdraw: false,
309
+ // status: 'started'
310
+ // }
311
+ // {
312
+ // type: 'gameState',
313
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5',
314
+ // wtime: 591460,
315
+ // btime: 595370,
316
+ // winc: 3000,
317
+ // binc: 3000,
318
+ // wdraw: false,
319
+ // bdraw: false,
320
+ // status: 'started'
321
+ // }
322
+ // {
323
+ // type: 'gameState',
324
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4',
325
+ // wtime: 591460,
326
+ // btime: 594720,
327
+ // winc: 3000,
328
+ // binc: 3000,
329
+ // wdraw: false,
330
+ // bdraw: false,
331
+ // status: 'started'
332
+ // }
333
+ // {
334
+ // type: 'gameState',
335
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3',
336
+ // wtime: 578720,
337
+ // btime: 594720,
338
+ // winc: 3000,
339
+ // binc: 3000,
340
+ // wdraw: false,
341
+ // bdraw: false,
342
+ // status: 'started'
343
+ // }
344
+ // {
345
+ // type: 'gameState',
346
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3 d8d2',
347
+ // wtime: 578720,
348
+ // btime: 597720,
349
+ // winc: 3000,
350
+ // binc: 3000,
351
+ // wdraw: false,
352
+ // bdraw: false,
353
+ // status: 'started'
354
+ // }
355
+ // {
356
+ // type: 'gameState',
357
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3 d8d2 e1f1',
358
+ // wtime: 565650,
359
+ // btime: 597720,
360
+ // winc: 3000,
361
+ // binc: 3000,
362
+ // wdraw: false,
363
+ // bdraw: false,
364
+ // status: 'started'
365
+ // }
366
+ // {
367
+ // type: 'gameState',
368
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3 d8d2 e1f1 e4e3',
369
+ // wtime: 565650,
370
+ // btime: 595360,
371
+ // winc: 3000,
372
+ // binc: 3000,
373
+ // wdraw: false,
374
+ // bdraw: false,
375
+ // status: 'started'
376
+ // }
377
+ // {
378
+ // type: 'gameState',
379
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3 d8d2 e1f1 e4e3 c2c3',
380
+ // wtime: 556810,
381
+ // btime: 595360,
382
+ // winc: 3000,
383
+ // binc: 3000,
384
+ // wdraw: false,
385
+ // bdraw: false,
386
+ // status: 'started'
387
+ // }
388
+ // {
389
+ // type: 'gameState',
390
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3 d8d2 e1f1 e4e3 c2c3 c8h3',
391
+ // wtime: 556810,
392
+ // btime: 587450,
393
+ // winc: 3000,
394
+ // binc: 3000,
395
+ // wdraw: false,
396
+ // bdraw: false,
397
+ // status: 'started'
398
+ // }
399
+ // {
400
+ // type: 'gameState',
401
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3 d8d2 e1f1 e4e3 c2c3 c8h3 f1g1',
402
+ // wtime: 547270,
403
+ // btime: 587450,
404
+ // winc: 3000,
405
+ // binc: 3000,
406
+ // wdraw: false,
407
+ // bdraw: false,
408
+ // status: 'started'
409
+ // }
410
+ // {
411
+ // type: 'gameState',
412
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3 d8d2 e1f1 e4e3 c2c3 c8h3 f1g1 h3g2',
413
+ // wtime: 547270,
414
+ // btime: 581970,
415
+ // winc: 3000,
416
+ // binc: 3000,
417
+ // wdraw: false,
418
+ // bdraw: false,
419
+ // status: 'started'
420
+ // }
421
+ // {
422
+ // type: 'gameState',
423
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3 d8d2 e1f1 e4e3 c2c3 c8h3 f1g1 h3g2 b2b3',
424
+ // wtime: 543850,
425
+ // btime: 581970,
426
+ // winc: 3000,
427
+ // binc: 3000,
428
+ // wdraw: false,
429
+ // bdraw: false,
430
+ // status: 'started'
431
+ // }
432
+ // { type: 'gameFinish', game: { id: 'Wmv3QOtD' } }
433
+ // {
434
+ // type: 'gameState',
435
+ // moves: 'e2e4 e7e5 g1f3 c7c6 f1c4 d7d5 c4d5 c6d5 f3e5 d5e4 g2g3 d8d2 e1f1 e4e3 c2c3 c8h3 f1g1 h3g2 b2b3 d2f2',
436
+ // wtime: 543850,
437
+ // btime: 574250,
438
+ // winc: 3000,
439
+ // binc: 3000,
440
+ // wdraw: false,
441
+ // bdraw: false,
442
+ // status: 'mate',
443
+ // winner: 'black'
444
+ // }
package/ui/Board.js CHANGED
@@ -29,9 +29,11 @@ class Board {
29
29
  }
30
30
 
31
31
  render () {
32
+ // this.topCheck()
32
33
  this.topRank()
33
34
  this.board()
34
35
  this.bottomRank()
36
+ // this.bottomCheck()
35
37
  }
36
38
 
37
39
  getPiece (file, rank) {
@@ -53,13 +55,13 @@ class Board {
53
55
  * Pieces
54
56
  */
55
57
  if (activeWhitePiece) {
56
- if (activeWhitePiece.inCheck === true) {
58
+ if (activeWhitePiece.type === 'King' && this.white.inCheck) {
57
59
  return chalk`{red ${activeWhitePiece.piece}} `
58
60
  } else {
59
61
  return chalk`{black ${activeWhitePiece.piece}} `
60
62
  }
61
63
  } else if (activeBlackPiece) {
62
- if (activeBlackPiece.inCheck === true) {
64
+ if (activeBlackPiece.type === 'King' && this.black.inCheck) {
63
65
  return chalk`{red ${activeBlackPiece.piece}} `
64
66
  } else {
65
67
  return chalk`{black ${activeBlackPiece.piece}} `
@@ -92,7 +94,7 @@ class Board {
92
94
  scoreBoard (side) {
93
95
  let scoreBoard = ''
94
96
  let score = 0
95
- const opponent = this.utils.opponentColor(side)
97
+ const opponent = this.utils.opposingColor(side)
96
98
  const pieces = this.capturedPieces(opponent)
97
99
 
98
100
  if (pieces.length > 0) {
@@ -110,11 +112,30 @@ class Board {
110
112
  this.printer.addSpace('y')
111
113
  }
112
114
 
115
+ /**
116
+ * For development only. Display if the king is in check
117
+ */
118
+ topCheck () {
119
+ const side = this.utils.opposingColor(this.options.orientation)
120
+ this.printer.print(side + ' king in check: ' + this[side].inCheck)
121
+ this.printer.addSpace('y')
122
+ }
123
+
124
+ /**
125
+ * For development only. Display if the king is in check
126
+ */
127
+ bottomCheck () {
128
+ const side = this.options.orientation
129
+ this.printer.print(side + ' king in check: ' + this[side].inCheck)
130
+ this.printer.addSpace('y')
131
+ }
132
+
113
133
  /**
114
134
  * Top signage
115
135
  */
116
136
  topRank () {
117
- this.scoreBoard(this.utils.opponentColor(this.options.orientation))
137
+ const side = this.utils.opposingColor(this.options.orientation)
138
+ this.scoreBoard(side)
118
139
  let renderTopRank = this.printer.tab
119
140
  renderTopRank = this.printer.addSpace('x', renderTopRank)
120
141
  for (let file in this.files) {
package/ui/Fen.js ADDED
@@ -0,0 +1,5 @@
1
+ export default {
2
+ currentPosition: null,
3
+ startPos: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
4
+ moves: []
5
+ }