schachnovelle 1.0.1 → 1.1.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/ui/Game.js CHANGED
@@ -1,17 +1,17 @@
1
- const readline = require('readline')
2
- const Side = require('./Side')
3
- const Board = require('./Board')
4
- const Printer = require('./Printer')
5
- const Utils = require('./Utils')
1
+ const readline = require("readline")
2
+ const Side = require("./Side")
3
+ const Board = require("./Board")
4
+ const Printer = require("./Printer")
5
+ const Utils = require("./Utils")
6
6
 
7
- const Enquirer = require('enquirer');
7
+ const Enquirer = require("enquirer")
8
8
 
9
9
  class Game {
10
- constructor (options) {
11
- this.white = new Side('white')
12
- this.black = new Side('black')
10
+ constructor(options) {
11
+ this.white = new Side("white")
12
+ this.black = new Side("black")
13
13
  this.playingAs = options.playingAs
14
- this.toMove = 'white'
14
+ this.toMove = "white"
15
15
 
16
16
  this.moveIndex = 0
17
17
  this.history = []
@@ -20,10 +20,10 @@ class Game {
20
20
 
21
21
  // Initialize the board
22
22
  this.board = new Board(this.white, this.black, {
23
- mode: 'pieces', // possibly add text mode
23
+ mode: "pieces", // possibly add text mode
24
24
  orientation: this.playingAs,
25
25
  spacing: options.spacing,
26
- mode: options.mode
26
+ mode: options.mode,
27
27
  })
28
28
 
29
29
  this.enquirer = new Enquirer()
@@ -36,19 +36,21 @@ class Game {
36
36
  * 2. Sets that as the selected piece
37
37
  */
38
38
  async promptPieceSelection() {
39
- const response = await this.enquirer.prompt([
40
- {
41
- type: 'select',
42
- name: 'piece',
43
- message: 'Piece',
44
- choices: this.listAvailablePieces(),
45
- result: (result) => {
46
- this.setSelectedPiece(result)
47
- return result
48
- }
49
- }
50
- ]).catch(console.error);
51
-
39
+ const response = await this.enquirer
40
+ .prompt([
41
+ {
42
+ type: "select",
43
+ name: "piece",
44
+ message: "Piece",
45
+ choices: this.listAvailablePieces(),
46
+ result: result => {
47
+ this.setSelectedPiece(result)
48
+ return result
49
+ },
50
+ },
51
+ ])
52
+ .catch(console.error)
53
+
52
54
  /**
53
55
  * Only do something if the await has been fulfilled
54
56
  */
@@ -62,29 +64,33 @@ class Game {
62
64
  * 3. Asks for the new coordinates of that piece
63
65
  */
64
66
  async promptFileSelection() {
65
- const backToPiece = '... piece selection'
66
- const response = await this.enquirer.prompt([
67
- {
68
- type: 'select',
69
- name: 'file',
70
- message: 'File:',
71
- choices: this.possibleFiles().concat(backToPiece),
72
- result: (result) => {
73
- if (result === backToPiece) {
74
- return result
75
- } else {
76
- this.setSelectedFile(result)
77
- return result
78
- }
79
- }
80
- }
81
- ]).catch(console.error);
82
-
67
+ const backToPiece = "... piece selection"
68
+ const response = await this.enquirer
69
+ .prompt([
70
+ {
71
+ type: "select",
72
+ name: "file",
73
+ message: "File:",
74
+ choices: this.possibleFiles().concat(backToPiece),
75
+ result: result => {
76
+ if (result === backToPiece) {
77
+ return result
78
+ } else {
79
+ this.setSelectedFile(result)
80
+ return result
81
+ }
82
+ },
83
+ },
84
+ ])
85
+ .catch(console.error)
86
+
83
87
  /**
84
88
  * Only do something if the await has been fulfilled
85
89
  */
86
90
  if (response) {
87
- if (response.file === backToPiece) { this.promptPieceSelection() } else {
91
+ if (response.file === backToPiece) {
92
+ this.promptPieceSelection()
93
+ } else {
88
94
  this.promptRankSelection()
89
95
  }
90
96
  }
@@ -95,156 +101,188 @@ class Game {
95
101
  * 3. Asks for the new coordinates of that piece
96
102
  */
97
103
  async promptRankSelection() {
98
- const backToFile = '... file selection'
99
- const response = await this.enquirer.prompt([
100
- {
101
- type: 'select',
102
- name: 'rank',
103
- message: 'Rank:',
104
- choices: this.possibleRanks().concat(backToFile),
105
- result: (result) => {
106
- if (result === backToFile) {
107
- return result
108
- } else {
109
- this.setSelectedRank(Number(result))
110
- return Number(result)
111
- }
112
- }
113
- }
114
- ]).catch(console.error);
115
-
104
+ const backToFile = "... file selection"
105
+ const response = await this.enquirer
106
+ .prompt([
107
+ {
108
+ type: "select",
109
+ name: "rank",
110
+ message: "Rank:",
111
+ choices: this.possibleRanks().concat(backToFile),
112
+ result: result => {
113
+ if (result === backToFile) {
114
+ return result
115
+ } else {
116
+ this.setSelectedRank(Number(result))
117
+ return Number(result)
118
+ }
119
+ },
120
+ },
121
+ ])
122
+ .catch(console.error)
123
+
116
124
  /**
117
125
  * Only do something if the await has been fulfilled
118
126
  */
119
127
  if (response) {
120
- if (response.rank === backToFile) { this.promptFileSelection() } else {
128
+ if (response.rank === backToFile) {
129
+ this.promptFileSelection()
130
+ } else {
121
131
  this.validateMove()
122
132
  }
123
133
  }
124
134
  }
125
135
 
136
+ /**
137
+ * For the given color, return all possible moves
138
+ */
139
+ allPossibleMoves(color) {
140
+ let all = []
141
+ this.board[color].pieces
142
+ .filter(p => !p.captured)
143
+ .forEach(p => {
144
+ this.possibleMoves(p, color)
145
+ .forEach(m => all.push(m))
146
+ })
147
+ return all
148
+ }
149
+
150
+ /**
151
+ * Return all possible checks
152
+ * @param {*} color
153
+ * @returns
154
+ */
155
+ allPossibleChecks(color) {
156
+ // Moves
157
+ const moves = this.allPossibleMoves(color)
158
+
159
+ const allChecks = moves.filter(m => m[2] === 'check')
160
+
161
+ return allChecks
162
+ }
163
+
126
164
  /**
127
165
  * Return the possible moves for the selected piece
128
166
  * Returns an object with all possible moves split into file, and rank
129
167
  */
130
- possibleMoves (piece) {
168
+ possibleMoves(piece, color) {
169
+ // TODO: If the current color is in check,
170
+ // then limit the possible moves to the ones that get you out of it
171
+
131
172
  // Start with the selected piece. First of all, it can't move to it's own square
132
173
  switch (piece.type) {
133
- case ('Pawn'):
134
- return this.possiblePawnMoves(piece)
135
- case ('Knight'):
136
- return this.possibleKnightMoves(piece)
137
- case ('Bishop'):
138
- return this.possibleBishopMoves(piece)
139
- case ('Rook'):
140
- return this.possibleRookMoves(piece)
141
- case ('Queen'):
142
- return this.possibleQueenMoves(piece)
143
- case ('King'):
144
- return this.possibleKingMoves(piece)
174
+ case "Pawn":
175
+ return this.possiblePawnMoves(piece, color)
176
+ case "Knight":
177
+ return this.possibleKnightMoves(piece, color)
178
+ case "Bishop":
179
+ return this.possibleBishopMoves(piece, color)
180
+ case "Rook":
181
+ return this.possibleRookMoves(piece, color)
182
+ case "Queen":
183
+ return this.possibleQueenMoves(piece, color)
184
+ case "King":
185
+ return this.possibleKingMoves(piece, color)
145
186
  }
146
187
  }
147
188
 
148
189
  /**
149
190
  * Returns all possible moves for a pawn
150
191
  */
151
- possiblePawnMoves (pawn) {
192
+ possiblePawnMoves (pawn, color) {
152
193
  const possible = []
153
194
  const positions = []
154
- const nextRank = this.toMove === 'white' ? pawn.pos[1] + 1 : pawn.pos[1] - 1
155
- const secondNextRank = this.toMove === 'white' ? pawn.pos[1] + 2 : pawn.pos[1] - 2
195
+ const direction = this.playingAs === color ? 1 : -1
196
+
197
+ const nextRank = pawn.pos[1] + direction
198
+ const secondNextRank = pawn.pos[1] + (direction * 2)
156
199
  // If it's on the starting rank, allow two moves
157
- if (pawn.pos[1] === 2 && this.toMove === 'white') {
158
- positions.push(
159
- [pawn.pos[0], nextRank],
160
- [pawn.pos[0], secondNextRank]
161
- )
162
- } else if (pawn.pos[1] === 7 && this.toMove === 'black') {
163
- positions.push(
164
- [pawn.pos[0], nextRank],
165
- [pawn.pos[0], secondNextRank]
166
- )
167
- } else if (this.toMove === 'white') {
168
- positions.push(
169
- [pawn.pos[0], nextRank]
170
- )
200
+ if (pawn.pos[1] === 2 && color === "white") {
201
+ positions.push([pawn.pos[0], nextRank], [pawn.pos[0], secondNextRank])
202
+ } else if (pawn.pos[1] === 7 && color === "black") {
203
+ positions.push([pawn.pos[0], nextRank], [pawn.pos[0], secondNextRank])
204
+ } else if (color === "white") {
205
+ positions.push([pawn.pos[0], nextRank])
171
206
  } else {
172
- positions.push(
173
- [pawn.pos[0], nextRank]
174
- )
207
+ positions.push([pawn.pos[0], nextRank])
175
208
  }
176
209
  // Captures
177
210
  const fileIndex = this.board.files.indexOf(pawn.pos[0])
178
211
  const positionsToCapture = []
179
212
  switch (fileIndex) {
180
- case (0):
181
- const posXL = [ this.board.files[fileIndex + 1], nextRank]
213
+ case 0:
214
+ const posXL = [this.board.files[fileIndex + 1], nextRank]
182
215
  positionsToCapture.push(posXL)
183
216
  break
184
- case (7):
185
- const posXR = [ this.board.files[fileIndex - 1], nextRank ]
217
+ case 7:
218
+ const posXR = [this.board.files[fileIndex - 1], nextRank]
186
219
  positionsToCapture.push(posXR)
187
220
  break
188
221
  default:
189
- const posL = [ this.board.files[fileIndex + 1], nextRank ]
190
- const posR = [ this.board.files[fileIndex - 1], nextRank ]
222
+ const posL = [this.board.files[fileIndex + 1], nextRank]
223
+ const posR = [this.board.files[fileIndex - 1], nextRank]
191
224
  positionsToCapture.push(posL, posR)
192
225
  break
193
226
  }
194
227
  for (const pos of positions) {
195
- if (this.movePotential(pos, false) !== 'blocked') {
228
+ if (this.movePotential(pos, color, false) !== "blocked") {
196
229
  possible.push(pos)
197
230
  }
198
231
  }
199
232
  for (const pos of positionsToCapture) {
200
- if (this.movePotential(pos, true) === 'capture') {
233
+ if (this.movePotential(pos, color, true) === "capture") {
234
+ possible.push(pos)
235
+ } else if (this.movePotential(pos, color, true) === "check") {
236
+ pos.push('check')
201
237
  possible.push(pos)
202
238
  }
203
239
  }
204
240
  // TODO: en passant (depends on a history)
205
- // TODO: Promotion. convert to queen / rook / bishop / knight
206
241
  return possible
207
242
  }
208
243
 
209
244
  /**
210
245
  * Just returns the candidate positions a knight could move to
211
- * @param {Integer} fileIndex
212
- * @param {Integer} rankIndex
246
+ * @param {Integer} fileIndex
247
+ * @param {Integer} rankIndex
213
248
  */
214
- knightCandidateMoves (fileIndex, rankIndex) {
249
+ knightCandidateMoves(fileIndex, rankIndex) {
215
250
  return [
216
251
  // Two up, one left
217
- [this.board.files[fileIndex - 1], this.board.ranks[rankIndex + 2]],
252
+ [this.board.files[fileIndex - 1], this.board.ranks[rankIndex + 2]],
218
253
  // Two up, one right
219
- [this.board.files[fileIndex + 1], this.board.ranks[rankIndex + 2]],
254
+ [this.board.files[fileIndex + 1], this.board.ranks[rankIndex + 2]],
220
255
  // Two right, one up
221
- [this.board.files[fileIndex + 2], this.board.ranks[rankIndex + 1]],
256
+ [this.board.files[fileIndex + 2], this.board.ranks[rankIndex + 1]],
222
257
  // Two right, one down
223
- [this.board.files[fileIndex + 2], this.board.ranks[rankIndex - 1]],
258
+ [this.board.files[fileIndex + 2], this.board.ranks[rankIndex - 1]],
224
259
  // Two down, one right
225
- [this.board.files[fileIndex + 1], this.board.ranks[rankIndex - 2]],
260
+ [this.board.files[fileIndex + 1], this.board.ranks[rankIndex - 2]],
226
261
  // Two down, one left
227
- [this.board.files[fileIndex - 1], this.board.ranks[rankIndex - 2]],
262
+ [this.board.files[fileIndex - 1], this.board.ranks[rankIndex - 2]],
228
263
  // Two left, one down
229
- [this.board.files[fileIndex - 2], this.board.ranks[rankIndex - 1]],
264
+ [this.board.files[fileIndex - 2], this.board.ranks[rankIndex - 1]],
230
265
  // Two left, one up
231
- [this.board.files[fileIndex - 2], this.board.ranks[rankIndex + 1]],
266
+ [this.board.files[fileIndex - 2], this.board.ranks[rankIndex + 1]],
232
267
  ]
233
268
  }
234
269
 
235
270
  /**
236
271
  * Returns the possible moves for a knight
237
272
  */
238
- possibleKnightMoves (knight) {
273
+ possibleKnightMoves(knight, color) {
239
274
  const fileIndex = this.board.files.indexOf(knight.pos[0])
240
275
  const rankIndex = this.board.ranks.indexOf(knight.pos[1])
241
276
  // From the starting square, draw a plus in each direction and then go one diagonal from there
242
277
  // Take the starting file and go left and right
243
278
  const candidates = this.knightCandidateMoves(fileIndex, rankIndex)
244
279
 
245
- const possible = candidates.filter((move) => {
280
+ const possible = candidates.filter(move => {
246
281
  if (move[0] !== undefined && move[1] !== undefined) {
247
- if (this.movePotential(move, true) !== 'blocked' && this.movePotential(move, true !== 'check')) {
282
+ if (this.movePotential(move, color, true) === "check") {
283
+ move.push('check')
284
+ return move
285
+ } else if (this.movePotential(move, color, true) !== "blocked") {
248
286
  return move
249
287
  }
250
288
  }
@@ -255,15 +293,15 @@ class Game {
255
293
  /**
256
294
  * Returns the possible moves for a bishop
257
295
  */
258
- possibleBishopMoves (bishop) {
296
+ possibleBishopMoves(bishop, color) {
259
297
  // go into all diagonal directions until there's no place or there's a capture or there's a block
260
298
  const fileIndex = this.board.files.indexOf(bishop.pos[0])
261
299
  const rankIndex = this.board.ranks.indexOf(bishop.pos[1])
262
-
263
- const fU = this.forwardAndUp(fileIndex, rankIndex)
264
- const fD = this.forwardAndDown(fileIndex, rankIndex, false)
265
- const bU = this.backwardAndUp(fileIndex, rankIndex, false)
266
- const bD = this.backwardAndDown(fileIndex, rankIndex, false)
300
+
301
+ const fU = this.forwardAndUp(fileIndex, rankIndex, color)
302
+ const fD = this.forwardAndDown(fileIndex, rankIndex, color, false)
303
+ const bU = this.backwardAndUp(fileIndex, rankIndex, color, false)
304
+ const bD = this.backwardAndDown(fileIndex, rankIndex, color, false)
267
305
 
268
306
  return [...fU, ...fD, ...bU, ...bD]
269
307
  }
@@ -271,15 +309,15 @@ class Game {
271
309
  /**
272
310
  * Returns the possible moves for a rook
273
311
  */
274
- possibleRookMoves (rook) {
312
+ possibleRookMoves(rook, color) {
275
313
  // go into all diagonal directions until there's no place or there's a capture or there's a block
276
314
  const fileIndex = this.board.files.indexOf(rook.pos[0])
277
315
  const rankIndex = this.board.ranks.indexOf(rook.pos[1])
278
-
279
- const u = this.up(fileIndex, rankIndex, false)
280
- const d = this.down(fileIndex, rankIndex, false)
281
- const l = this.left(fileIndex, rankIndex, false)
282
- const r = this.right(fileIndex, rankIndex, false)
316
+
317
+ const u = this.up(fileIndex, rankIndex, color, false)
318
+ const d = this.down(fileIndex, rankIndex, color, false)
319
+ const l = this.left(fileIndex, rankIndex, color, false)
320
+ const r = this.right(fileIndex, rankIndex, color, false)
283
321
 
284
322
  return [...u, ...d, ...l, ...r]
285
323
  }
@@ -287,19 +325,19 @@ class Game {
287
325
  /**
288
326
  * Returns the possible moves for a queen
289
327
  */
290
- possibleQueenMoves (queen) {
328
+ possibleQueenMoves(queen, color) {
291
329
  // go into all diagonal directions until there's no place or there's a capture or there's a block
292
330
  const fileIndex = this.board.files.indexOf(queen.pos[0])
293
331
  const rankIndex = this.board.ranks.indexOf(queen.pos[1])
294
-
295
- const u = this.up(fileIndex, rankIndex, false)
296
- const d = this.down(fileIndex, rankIndex, false)
297
- const l = this.left(fileIndex, rankIndex, false)
298
- const r = this.right(fileIndex, rankIndex, false)
299
- const fU = this.forwardAndUp(fileIndex, rankIndex, false)
300
- const fD = this.forwardAndDown(fileIndex, rankIndex, false)
301
- const bU = this.backwardAndUp(fileIndex, rankIndex, false)
302
- const bD = this.backwardAndDown(fileIndex, rankIndex, false)
332
+
333
+ const u = this.up(fileIndex, rankIndex, color, false)
334
+ const d = this.down(fileIndex, rankIndex, color, false)
335
+ const l = this.left(fileIndex, rankIndex, color, false)
336
+ const r = this.right(fileIndex, rankIndex, color, false)
337
+ const fU = this.forwardAndUp(fileIndex, rankIndex, color, false)
338
+ const fD = this.forwardAndDown(fileIndex, rankIndex, color, false)
339
+ const bU = this.backwardAndUp(fileIndex, rankIndex, color, false)
340
+ const bD = this.backwardAndDown(fileIndex, rankIndex, color, false)
303
341
 
304
342
  return [...u, ...d, ...l, ...r, ...fU, ...fD, ...bU, ...bD]
305
343
  }
@@ -307,19 +345,19 @@ class Game {
307
345
  /**
308
346
  * Returns the possible moves for a queen
309
347
  */
310
- possibleKingMoves (king) {
348
+ possibleKingMoves(king, color) {
311
349
  // go into all diagonal directions until there's no place or there's a capture or there's a block
312
350
  const fileIndex = this.board.files.indexOf(king.pos[0])
313
351
  const rankIndex = this.board.ranks.indexOf(king.pos[1])
314
-
315
- const u = this.up(fileIndex, rankIndex, true)
316
- const d = this.down(fileIndex, rankIndex, true)
317
- const l = this.left(fileIndex, rankIndex, true)
318
- const r = this.right(fileIndex, rankIndex, true)
319
- const fU = this.forwardAndUp(fileIndex, rankIndex, true)
320
- const fD = this.forwardAndDown(fileIndex, rankIndex, true)
321
- const bU = this.backwardAndUp(fileIndex, rankIndex, true)
322
- const bD = this.backwardAndDown(fileIndex, rankIndex, true)
352
+
353
+ const u = this.up(fileIndex, rankIndex, color, true)
354
+ const d = this.down(fileIndex, rankIndex, color, true)
355
+ const l = this.left(fileIndex, rankIndex, color, true)
356
+ const r = this.right(fileIndex, rankIndex, color, true)
357
+ const fU = this.forwardAndUp(fileIndex, rankIndex, color, true)
358
+ const fD = this.forwardAndDown(fileIndex, rankIndex, color, true)
359
+ const bU = this.backwardAndUp(fileIndex, rankIndex, color, true)
360
+ const bD = this.backwardAndDown(fileIndex, rankIndex, color, true)
323
361
 
324
362
  return [...u, ...d, ...l, ...r, ...fU, ...fD, ...bU, ...bD]
325
363
  }
@@ -330,17 +368,24 @@ class Game {
330
368
  * 1. To determine the next moves for a person playing
331
369
  * 2. To check if the next move will be checking the opponent's king
332
370
  */
333
- forwardAndUp (fileIndex, rankIndex, king) {
371
+ forwardAndUp(fileIndex, rankIndex, color, king) {
334
372
  const possible = []
335
373
  let fi = fileIndex + 1
336
374
  let ri = rankIndex + 1
337
375
 
338
376
  let nextPosition = [this.board.files[fi], this.board.ranks[ri]]
339
-
340
- while (this.movePotential(nextPosition, true) !== 'blocked' && this.movePotential(nextPosition, true) !== 'impossible') {
341
- if (this.movePotential(nextPosition, true) === 'check') { break }
377
+
378
+ while (
379
+ this.movePotential(nextPosition, color, true) !== "blocked" &&
380
+ this.movePotential(nextPosition, color, true) !== "impossible"
381
+ ) {
382
+ if (this.movePotential(nextPosition, color, true) === "check") {
383
+ nextPosition.push('check')
384
+ possible.push(nextPosition)
385
+ break
386
+ }
342
387
  possible.push(nextPosition)
343
- if (this.movePotential(nextPosition, true) === 'capture' || king) break
388
+ if (this.movePotential(nextPosition, color, true) === "capture" || king) break
344
389
  fi++
345
390
  ri++
346
391
  nextPosition = [this.board.files[fi], this.board.ranks[ri]]
@@ -352,17 +397,24 @@ class Game {
352
397
  /**
353
398
  * Returns the possible moves on a diagonal going right and down
354
399
  */
355
- forwardAndDown (fileIndex, rankIndex, king) {
400
+ forwardAndDown(fileIndex, rankIndex, color, king) {
356
401
  const possible = []
357
402
  let fi = fileIndex + 1
358
403
  let ri = rankIndex + -1
359
-
404
+
360
405
  let nextPosition = [this.board.files[fi], this.board.ranks[ri]]
361
-
362
- while (this.movePotential(nextPosition, true) !== 'blocked' && this.movePotential(nextPosition, true) !== 'impossible') {
363
- if (this.movePotential(nextPosition, true) === 'check') { break }
406
+
407
+ while (
408
+ this.movePotential(nextPosition, color, true) !== "blocked" &&
409
+ this.movePotential(nextPosition, color, true) !== "impossible"
410
+ ) {
411
+ if (this.movePotential(nextPosition, color, true) === "check") {
412
+ nextPosition.push('check')
413
+ possible.push(nextPosition)
414
+ break
415
+ }
364
416
  possible.push(nextPosition)
365
- if (this.movePotential(nextPosition, true) === 'capture' || king) break
417
+ if (this.movePotential(nextPosition, color, true) === "capture" || king) break
366
418
  fi++
367
419
  ri--
368
420
  nextPosition = [this.board.files[fi], this.board.ranks[ri]]
@@ -373,17 +425,24 @@ class Game {
373
425
  /**
374
426
  * Returns the possible moves on a diagonal going left and up
375
427
  */
376
- backwardAndUp (fileIndex, rankIndex, king) {
428
+ backwardAndUp(fileIndex, rankIndex, color, king) {
377
429
  const possible = []
378
430
  let fi = fileIndex - 1
379
431
  let ri = rankIndex + 1
380
432
 
381
433
  let nextPosition = [this.board.files[fi], this.board.ranks[ri]]
382
-
383
- while (this.movePotential(nextPosition, true) !== 'blocked' && this.movePotential(nextPosition, true) !== 'impossible') {
384
- if (this.movePotential(nextPosition, true) === 'check') { break }
434
+
435
+ while (
436
+ this.movePotential(nextPosition, color, true) !== "blocked" &&
437
+ this.movePotential(nextPosition, color, true) !== "impossible"
438
+ ) {
439
+ if (this.movePotential(nextPosition, color, true) === "check") {
440
+ nextPosition.push('check')
441
+ possible.push(nextPosition)
442
+ break
443
+ }
385
444
  possible.push(nextPosition)
386
- if (this.movePotential(nextPosition, true) === 'capture' || king) break
445
+ if (this.movePotential(nextPosition, color, true) === "capture" || king) break
387
446
  fi--
388
447
  ri++
389
448
  nextPosition = [this.board.files[fi], this.board.ranks[ri]]
@@ -394,17 +453,24 @@ class Game {
394
453
  /**
395
454
  * Returns the possible moves on a diagonal going left and down
396
455
  */
397
- backwardAndDown (fileIndex, rankIndex, king) {
456
+ backwardAndDown(fileIndex, rankIndex, color, king) {
398
457
  const possible = []
399
458
  let fi = fileIndex - 1
400
459
  let ri = rankIndex - 1
401
460
 
402
461
  let nextPosition = [this.board.files[fi], this.board.ranks[ri]]
403
-
404
- while (this.movePotential(nextPosition, true) !== 'blocked' && this.movePotential(nextPosition, true) !== 'impossible' ) {
405
- if (this.movePotential(nextPosition, true) === 'check') { break }
462
+
463
+ while (
464
+ this.movePotential(nextPosition, color, true) !== "blocked" &&
465
+ this.movePotential(nextPosition, color, true) !== "impossible"
466
+ ) {
467
+ if (this.movePotential(nextPosition, color, true) === "check") {
468
+ nextPosition.push('check')
469
+ possible.push(nextPosition)
470
+ break
471
+ }
406
472
  possible.push(nextPosition)
407
- if (this.movePotential(nextPosition, true) === 'capture' || king) break
473
+ if (this.movePotential(nextPosition, color, true) === "capture" || king) break
408
474
  fi--
409
475
  ri--
410
476
  nextPosition = [this.board.files[fi], this.board.ranks[ri]]
@@ -415,16 +481,23 @@ class Game {
415
481
  /**
416
482
  * Returns the possible moves on a file going up
417
483
  */
418
- up (fileIndex, rankIndex, king) {
484
+ up(fileIndex, rankIndex, color, king) {
419
485
  const possible = []
420
486
  let ri = rankIndex + 1
421
487
 
422
488
  let nextPosition = [this.board.files[fileIndex], this.board.ranks[ri]]
423
-
424
- while (this.movePotential(nextPosition, true) !== 'blocked' && this.movePotential(nextPosition, true) !== 'impossible') {
425
- if (this.movePotential(nextPosition, true) === 'check') { break }
489
+
490
+ while (
491
+ this.movePotential(nextPosition, color, true) !== "blocked" &&
492
+ this.movePotential(nextPosition, color, true) !== "impossible"
493
+ ) {
494
+ if (this.movePotential(nextPosition, color, true) === "check") {
495
+ nextPosition.push('check')
496
+ possible.push(nextPosition)
497
+ break
498
+ }
426
499
  possible.push(nextPosition)
427
- if (this.movePotential(nextPosition, true) === 'capture' || king) break
500
+ if (this.movePotential(nextPosition, color, true) === "capture" || king) break
428
501
  ri++
429
502
  nextPosition = [this.board.files[fileIndex], this.board.ranks[ri]]
430
503
  }
@@ -434,16 +507,23 @@ class Game {
434
507
  /**
435
508
  * Returns the possible moves on a file going down
436
509
  */
437
- down (fileIndex, rankIndex, king) {
510
+ down(fileIndex, rankIndex, color, king) {
438
511
  const possible = []
439
512
  let ri = rankIndex - 1
440
513
 
441
514
  let nextPosition = [this.board.files[fileIndex], this.board.ranks[ri]]
442
-
443
- while (this.movePotential(nextPosition, true) !== 'blocked' && this.movePotential(nextPosition, true) !== 'impossible') {
444
- if (this.movePotential(nextPosition, true) === 'check') { break }
515
+
516
+ while (
517
+ this.movePotential(nextPosition, color, true) !== "blocked" &&
518
+ this.movePotential(nextPosition, color, true) !== "impossible"
519
+ ) {
520
+ if (this.movePotential(nextPosition, color, true) === "check") {
521
+ nextPosition.push('check')
522
+ possible.push(nextPosition)
523
+ break
524
+ }
445
525
  possible.push(nextPosition)
446
- if (this.movePotential(nextPosition, true) === 'capture' || king) break
526
+ if (this.movePotential(nextPosition, color, true) === "capture" || king) break
447
527
  ri--
448
528
  nextPosition = [this.board.files[fileIndex], this.board.ranks[ri]]
449
529
  }
@@ -453,16 +533,23 @@ class Game {
453
533
  /**
454
534
  * Returns the possible moves on a rank going left
455
535
  */
456
- left (fileIndex, rankIndex, king) {
536
+ left(fileIndex, rankIndex, color, king) {
457
537
  const possible = []
458
538
  let fi = fileIndex - 1
459
539
 
460
540
  let nextPosition = [this.board.files[fi], this.board.ranks[rankIndex]]
461
-
462
- while (this.movePotential(nextPosition, true) !== 'blocked' && this.movePotential(nextPosition, true) !== 'impossible') {
463
- if (this.movePotential(nextPosition, true) === 'check') { break }
541
+
542
+ while (
543
+ this.movePotential(nextPosition, color, true) !== "blocked" &&
544
+ this.movePotential(nextPosition, color, true) !== "impossible"
545
+ ) {
546
+ if (this.movePotential(nextPosition, color, true) === "check") {
547
+ nextPosition.push('check')
548
+ possible.push(nextPosition)
549
+ break
550
+ }
464
551
  possible.push(nextPosition)
465
- if (this.movePotential(nextPosition, true) === 'capture' || king) break
552
+ if (this.movePotential(nextPosition, color, true) === "capture" || king) break
466
553
  fi--
467
554
  nextPosition = [this.board.files[fi], this.board.ranks[rankIndex]]
468
555
  }
@@ -472,16 +559,23 @@ class Game {
472
559
  /**
473
560
  * Returns the possible moves on a rank going right
474
561
  */
475
- right (fileIndex, rankIndex, king) {
562
+ right(fileIndex, rankIndex, color, king) {
476
563
  const possible = []
477
564
  let fi = fileIndex + 1
478
565
 
479
566
  let nextPosition = [this.board.files[fi], this.board.ranks[rankIndex]]
480
-
481
- while (this.movePotential(nextPosition, true) !== 'blocked' && this.movePotential(nextPosition, true) !== 'impossible') {
482
- if (this.movePotential(nextPosition, true) === 'check') { break }
567
+
568
+ while (
569
+ this.movePotential(nextPosition, color, true) !== "blocked" &&
570
+ this.movePotential(nextPosition, color, true) !== "impossible"
571
+ ) {
572
+ if (this.movePotential(nextPosition, color, true) === "check") {
573
+ nextPosition.push('check')
574
+ possible.push(nextPosition)
575
+ break
576
+ }
483
577
  possible.push(nextPosition)
484
- if (this.movePotential(nextPosition, true) === 'capture' || king) break
578
+ if (this.movePotential(nextPosition, color, true) === "capture" || king) break
485
579
  fi++
486
580
  nextPosition = [this.board.files[fi], this.board.ranks[rankIndex]]
487
581
  }
@@ -491,8 +585,10 @@ class Game {
491
585
  /**
492
586
  * Returns the possible ranks from the possiblemoves function
493
587
  */
494
- possibleFiles () {
495
- const possible = this.possibleMoves(this.selectedPiece).map((pos) => { return pos[0] })
588
+ possibleFiles() {
589
+ const possible = this.possibleMoves(this.selectedPiece, this.toMove).map(pos => {
590
+ return pos[0]
591
+ })
496
592
  /** Filter unique */
497
593
  return [...new Set(possible)]
498
594
  }
@@ -500,12 +596,16 @@ class Game {
500
596
  /**
501
597
  * Returns the possible ranks from the possiblemoves function
502
598
  */
503
- possibleRanks () {
504
- const possible = this.possibleMoves(this.selectedPiece).filter((move) => {
505
- if (move[0] === this.selectedFile) { return move[1] }
506
- }).map((pos) => {
507
- return pos[1].toString()
508
- })
599
+ possibleRanks() {
600
+ const possible = this.possibleMoves(this.selectedPiece, this.toMove)
601
+ .filter(move => {
602
+ if (move[0] === this.selectedFile) {
603
+ return move[1]
604
+ }
605
+ })
606
+ .map(pos => {
607
+ return pos[1].toString()
608
+ })
509
609
  /** Filter unique */
510
610
  return [...new Set(possible)]
511
611
  }
@@ -515,7 +615,7 @@ class Game {
515
615
  * 2. TODO: Check check
516
616
  * 3. TODO: Check checkmate
517
617
  */
518
- async validateMove () {
618
+ async validateMove() {
519
619
  // Commit the move to update the board
520
620
  this.processCapture()
521
621
  await this.promptPromotion()
@@ -524,85 +624,56 @@ class Game {
524
624
 
525
625
  /**
526
626
  * Allowed configures the check to consider if a capture may be made
527
- * If it is true,
627
+ * If it is true,
528
628
  * Returns 'capture' if there is an opposing piece at the given position
529
629
  * Returns 'check' if the opposing piece at the given position is a king
530
630
  * Returns 'blocked' if there is an own piece or 'possible' for no piece at the given position
531
631
  */
532
- movePotential (pos, allowed) {
533
- if (pos[0] === undefined || pos[1] === undefined) { return 'impossible' }
632
+ movePotential(pos, color, allowed) {
633
+ if (pos[0] === undefined || pos[1] === undefined) {
634
+ return "impossible"
635
+ }
534
636
 
535
- const opponentsPieces = this.board[this.utils.opponentColor(this.toMove)].pieces
637
+ const opponentsPieces =
638
+ this.board[this.utils.opposingColor(color)].pieces
536
639
 
537
- const ownPiece = this.board[this.toMove].pieces.find((piece) => {
640
+ const ownPiece = this.board[color].pieces.find(piece => {
538
641
  return piece.pos[0] === pos[0] && piece.pos[1] === pos[1]
539
642
  })
540
- const opponentsPiece = opponentsPieces.find((piece) => {
643
+ const opponentsPiece = opponentsPieces.find(piece => {
541
644
  return piece.pos[0] === pos[0] && piece.pos[1] === pos[1]
542
645
  })
543
646
 
544
647
  if (opponentsPiece && allowed) {
545
- if (opponentsPiece.type === 'King') {
546
- return 'check'
648
+ if (opponentsPiece.type === "King") {
649
+ return "check"
547
650
  } else {
548
- return 'capture'
651
+ return "capture"
549
652
  }
550
653
  }
551
- if (ownPiece && allowed) { return 'blocked' }
552
- if (ownPiece || opponentsPiece && !allowed) { return 'blocked' }
553
- if (!ownPiece && !opponentsPiece) { return 'possible' }
554
- }
555
-
556
- // /**
557
- // * For this position and direction, return the potential for a check
558
- // * Returns impossible for non existing pos
559
- // * Returns blocked if there is an own piece or a non checking piece
560
- // * Returns empty if there is no piece
561
- // * Returns check if the opponents piece in the way gives a check
562
- // */
563
- // checkPotential (pos, direction) {
564
- // if (pos[0] === undefined || pos[1] === undefined) { return 'impossible' }
565
-
566
- // const ownPiece = this.board[this.toMove].pieces.find((piece) => {
567
- // return piece.pos[0] === pos[0] && piece.pos[1] === pos[1]
568
- // })
569
- // const opponentsPiece = opponentsPieces.find((piece) => {
570
- // return piece.pos[0] === pos[0] && piece.pos[1] === pos[1]
571
- // })
572
-
573
- // if (ownPiece) { return 'blocked' }
574
- // if (!ownPiece && !opponentsPiece) { return 'empty' }
575
- // if (opponentsPiece) {
576
- // // There is an opposing piece on this square.
577
- // switch (direction) {
578
- // case ('up'):
579
- // switch (opponentsPiece.type) {
580
- // case ('Pawn'):
581
- // return 'blocked'
582
- // case ('Knight'):
583
- // return 'blocked'
584
- // case ('Bishop'):
585
- // return
586
- // case ('Rook'):
587
- // return
588
- // case ('Queen'):
589
- // return
590
- // case ('King'):
591
- // return 'impossible'
592
- // }
593
- // }
594
- // }
595
- // }
654
+ if (ownPiece && allowed) {
655
+ return "blocked"
656
+ }
657
+ if (ownPiece || (opponentsPiece && !allowed)) {
658
+ return "blocked"
659
+ }
660
+ if (!ownPiece && !opponentsPiece) {
661
+ return "possible"
662
+ }
663
+ }
596
664
 
597
665
  /**
598
666
  * Processes a capture.
599
667
  * 1. Checks for capture
600
668
  * 2. If capture, set the piece status to captured
601
669
  */
602
- processCapture () {
603
- const opponentsPieces = this.board[this.utils.opponentColor(this.toMove)].pieces
604
- const capturedPiece = opponentsPieces.find((piece) => {
605
- return piece.pos[0] === this.selectedFile && piece.pos[1] === this.selectedRank
670
+ processCapture() {
671
+ const opponentsPieces =
672
+ this.board[this.utils.opposingColor(this.toMove)].pieces
673
+ const capturedPiece = opponentsPieces.find(piece => {
674
+ return (
675
+ piece.pos[0] === this.selectedFile && piece.pos[1] === this.selectedRank
676
+ )
606
677
  })
607
678
  if (capturedPiece) {
608
679
  capturedPiece.captured = true
@@ -614,70 +685,63 @@ class Game {
614
685
  * 1. Asks user which piece to promote to
615
686
  * 2. Sets said piece to its new values
616
687
  */
617
- async promptPromotion () {
618
- if ((this.selectedPiece.type === 'Pawn' && this.toMove === 'white' && this.selectedRank === 8) || (this.selectedPiece.type === 'Pawn' && this.toMove === 'black' && this.selectedRank === 1)) {
619
- await this.enquirer.prompt({
620
- type: 'select',
621
- name: 'promotion',
622
- message: 'Promote to:',
623
- choices: this.board[this.toMove].promotionChoices.map((choice) => {
624
- return choice.piece
625
- }),
626
- result: (piece) => {
627
- const promotionChoice = this.board[this.toMove].promotionChoices.find((choice) => { return choice.piece === piece })
628
- const selectedPiece = this.getSelectedPiece()
629
- selectedPiece.piece = piece
630
- selectedPiece.type = promotionChoice.type
631
- selectedPiece.prefix = promotionChoice.prefix
632
- selectedPiece.value = promotionChoice.piece
633
- return selectedPiece
634
- }
635
- }).catch(console.error)
688
+ async promptPromotion() {
689
+ if (
690
+ (this.selectedPiece.type === "Pawn" &&
691
+ this.toMove === "white" &&
692
+ this.selectedRank === 8) ||
693
+ (this.selectedPiece.type === "Pawn" &&
694
+ this.toMove === "black" &&
695
+ this.selectedRank === 1)
696
+ ) {
697
+ await this.enquirer
698
+ .prompt({
699
+ type: "select",
700
+ name: "promotion",
701
+ message: "Promote to:",
702
+ choices: this.board[this.toMove].promotionChoices.map(choice => {
703
+ return choice.piece
704
+ }),
705
+ result: piece => {
706
+ const promotionChoice = this.board[
707
+ this.toMove
708
+ ].promotionChoices.find(choice => {
709
+ return choice.piece === piece
710
+ })
711
+ const selectedPiece = this.getSelectedPiece()
712
+ selectedPiece.piece = piece
713
+ selectedPiece.type = promotionChoice.type
714
+ selectedPiece.prefix = promotionChoice.prefix
715
+ selectedPiece.value = promotionChoice.piece
716
+ return selectedPiece
717
+ },
718
+ })
719
+ .catch(console.error)
636
720
  }
637
721
  }
638
722
 
639
-
640
723
  /**
641
- * Looks up from the given position and returns any checks that are delivered
724
+ * Check master function. Is run right before passTurn and board.render
642
725
  */
643
- checksUp (fileIndex, rankIndex) {
644
- const checks = []
645
- let ri = rankIndex + 1
726
+ checkForCheck() {
727
+ // If the opponent's checks length is > 0, that means you're in check
728
+ const opponent = this.utils.opposingColor(this.toMove)
729
+ const opponentsChecks = this.allPossibleChecks(opponent)
646
730
 
647
- let nextPosition = [this.board.files[fileIndex], this.board.ranks[ri]]
648
-
649
- while (this.checkPotential(nextPosition, 'up') !== 'blocked' && this.checkPotential(nextPosition, 'up') !== 'impossible') {
650
- if (this.checkPotential(nextPosition, 'up') === 'check') {
651
- checks.push(nextPosition)
652
- }
653
- ri++
654
- nextPosition = [this.board.files[fileIndex], this.board.ranks[ri]]
731
+ if (opponentsChecks.length > 0) {
732
+ this.board[this.toMove].inCheck = true
733
+ } else {
734
+ this.board[this.toMove].inCheck = false
735
+ this.board[opponent].inCheck = false
655
736
  }
656
- return checks
657
-
658
- }
659
-
660
- /**
661
- * Looks from the perspective of the king if he is being seen by any of the other player's pieces.
662
- */
663
- getChecks () {
664
- // return [...u]
665
- }
666
-
667
- /**
668
- * Check master function. Is run right before passTurn and board.render
669
- */
670
- setCheck () {
671
- const checks = this.getChecks()
672
- // List the possible moves that put the king in check. If the array remains length 0, do nothing. Otherwise, set the king in check
673
737
  }
674
738
 
675
739
  /**
676
740
  * Passes the turn to the other player by setting the toMove var and prompting the next move
677
741
  */
678
- passTurn () {
679
- this.toMove === 'white' ? this.toMove = 'black' : this.toMove = 'white'
680
- this.setCheck()
742
+ passTurn() {
743
+ this.toMove = this.toMove === "white" ? "black" : "white"
744
+ this.checkForCheck()
681
745
  this.promptPieceSelection()
682
746
  }
683
747
 
@@ -688,7 +752,7 @@ class Game {
688
752
  * 3. Passes the turn to the other player
689
753
  * 4. Renders the board
690
754
  */
691
- commitMove () {
755
+ commitMove() {
692
756
  this.getSelectedPiece().pos = [this.selectedFile, this.selectedRank]
693
757
  this.commitToHistory()
694
758
  this.passTurn()
@@ -699,25 +763,26 @@ class Game {
699
763
  /**
700
764
  * Pushes move to history
701
765
  */
702
- commitToHistory () {
703
- }
766
+ commitToHistory() {}
704
767
 
705
768
  /**
706
769
  * Returns a list of available pieces for the prompt
707
770
  */
708
- listAvailablePieces () {
709
- return this.board[this.toMove].pieces.filter((piece) => {
710
- return !piece.captured && this.possibleMoves(piece).length > 0
711
- }).map((piece) => {
712
- return `${piece.piece} (${piece.pos})`
713
- })
771
+ listAvailablePieces() {
772
+ return this.board[this.toMove].pieces
773
+ .filter(piece => {
774
+ return !piece.captured && this.possibleMoves(piece, this.toMove).length > 0
775
+ })
776
+ .map(piece => {
777
+ return `${piece.piece} (${piece.pos})`
778
+ })
714
779
  }
715
780
 
716
781
  /**
717
782
  * Sets the given piece as selected for this move
718
783
  */
719
- setSelectedPiece (piece) {
720
- const selection = this.board[this.toMove].pieces.find((p) => {
784
+ setSelectedPiece(piece) {
785
+ const selection = this.board[this.toMove].pieces.find(p => {
721
786
  return p.pos[0] === piece[3] && p.pos[1] === Number(piece[5])
722
787
  })
723
788
  this.selectedPiece = selection
@@ -726,46 +791,54 @@ class Game {
726
791
  /**
727
792
  * Sets the given file as selected for this move
728
793
  */
729
- setSelectedFile (file) {
794
+ setSelectedFile(file) {
730
795
  this.selectedFile = file
731
796
  }
732
797
 
733
798
  /**
734
799
  * Sets the given rank as selected for this move
735
800
  */
736
- setSelectedRank (rank) {
801
+ setSelectedRank(rank) {
737
802
  this.selectedRank = rank
738
803
  }
739
804
 
740
805
  /**
741
806
  * Get the current player's piece
742
807
  */
743
- getPiece (type) {
744
- return this.board[this.toMove].pieces.find((piece) => { return piece.type === type })
808
+ getPiece(type) {
809
+ return this.board[this.toMove].pieces.find(piece => {
810
+ return piece.type === type
811
+ })
745
812
  }
746
813
 
747
814
  /**
748
815
  * Get the opponent's piece
749
816
  */
750
- getOpponentsPiece (type) {
751
- return this.board[this.utils.opponentColor(this.toMove)].pieces.find((piece) => { return piece.type === type })
817
+ getOpponentsPiece(type) {
818
+ return this.board[this.utils.opposingColor(this.toMove)].pieces.find(
819
+ piece => {
820
+ return piece.type === type
821
+ }
822
+ )
752
823
  }
753
824
 
754
825
  /**
755
826
  * returns the selected piece for this move
756
827
  */
757
- getSelectedPiece () {
758
- return this.board[this.toMove].pieces.find(piece => { return piece === this.selectedPiece })
828
+ getSelectedPiece() {
829
+ return this.board[this.toMove].pieces.find(piece => {
830
+ return piece === this.selectedPiece
831
+ })
759
832
  }
760
833
 
761
834
  /**
762
835
  * Um Gottes Wille, nicht!
763
836
  */
764
- init () {
837
+ init() {
765
838
  this.board.clear()
766
839
  this.board.render()
767
840
  this.promptPieceSelection()
768
841
  }
769
842
  }
770
843
 
771
- module.exports = Game
844
+ module.exports = Game