knightstour 1.0.2 → 1.0.4

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.
Files changed (3) hide show
  1. package/README.md +115 -0
  2. package/index.js +6 -25
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,115 @@
1
+ ```
2
+ # KnightsTour
3
+
4
+ A flexible **Knight's Tour** game engine in JavaScript/TypeScript.
5
+ Supports single-player (Open/Closed tours) and multiplayer (Co-op/Versus) modes, with built-in move validation, undo, hints, and automated tour generation.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install knightstour
13
+ ```
14
+
15
+ or with Yarn:
16
+
17
+ ```bash
18
+ yarn add knightstour
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Usage
24
+
25
+ ```ts
26
+ import KnightsTour from "knightstour";
27
+
28
+ // Create a new game
29
+ const game = new KnightsTour({
30
+ rows: 8,
31
+ cols: 8,
32
+ players: [
33
+ { name: "Alice", color: "red", moveIndexes: [] },
34
+ { name: "Bob", color: "blue", moveIndexes: [] }
35
+ ],
36
+ gameMode: "Co-op", // Open, Closed, Versus, Co-op
37
+ disabledIndexes: [0, 7] // optional blocked cells
38
+ });
39
+
40
+ // Make a move
41
+ game.move(10);
42
+
43
+ // Undo last move
44
+ game.undo();
45
+
46
+ // Get the next available moves for current player
47
+ const nextMoves = game.getAvailableMovesFor(game.turnIndex);
48
+ console.log(nextMoves);
49
+
50
+ // Reset the game
51
+ game.reset();
52
+ ```
53
+
54
+ ---
55
+
56
+ ## API
57
+
58
+ ### `new KnightsTour(config?: KnightsTourConfig)`
59
+
60
+ - `rows` – number of rows (default: 6)
61
+ - `cols` – number of columns (default: 5)
62
+ - `players` – array of player objects:
63
+ ```ts
64
+ {
65
+ name: string;
66
+ color: string;
67
+ moveIndexes: number[];
68
+ CPU?: boolean; // optional
69
+ }
70
+ ```
71
+ - `gameMode` – `"Open" | "Closed" | "Co-op" | "Versus"` (default depends on player count)
72
+ - `disabledIndexes` – array of blocked cell indices
73
+
74
+ ---
75
+
76
+ ### Methods
77
+
78
+ - `move(index: number): boolean` – Move the current player to the given index
79
+ - `undo(): void` – Undo the last move
80
+ - `getAvailableMovesFor(playerIndex: number): number[]` – Get valid moves for a player
81
+ - `reset(): void` – Reset the game to initial configuration
82
+ - `updateBoard(autoMove?: boolean): void` – Refresh the board and calculate next moves
83
+ - `hint(): void` – Suggest and execute the best move for the current player
84
+ - `solve(increment?: number): void` – Attempt to solve the tour (single-player)
85
+ - `generate(increment?: number): void` – Attempt to generate a valid tour
86
+
87
+ ---
88
+
89
+ ### Properties
90
+
91
+ - `rows`, `cols` – board dimensions
92
+ - `players` – current players with their moves
93
+ - `turnIndex` – index of current player
94
+ - `cells` – array of cell objects for rendering
95
+ - `state` – current game state (e.g., `"Game Over"`, `"Alice's Turn"`)
96
+ - `solved` – boolean indicating if a tour is completed
97
+ - `generated` – boolean indicating if a tour was generated
98
+ - `allMoveIndexes` – flattened move history of all players
99
+
100
+ ---
101
+
102
+ ## TypeScript Support
103
+
104
+ Type definitions are included. Importing in TypeScript will give full IntelliSense support.
105
+
106
+ ```ts
107
+ import KnightsTour from "knightstour";
108
+ ```
109
+
110
+ ---
111
+
112
+ ## License
113
+
114
+ MIT © Puzzookie
115
+ ```
package/index.js CHANGED
@@ -45,14 +45,11 @@ class KnightsTour {
45
45
 
46
46
  this.players = players.map(p => ({
47
47
  ...p,
48
- // Ensures each player gets a fresh copy of their original moves
49
48
  moveIndexes: [...p.moveIndexes]
50
49
  }));
51
50
 
52
- //need to fix allMoveIndexes
53
51
  this.allMoveIndexes = [];
54
52
 
55
-
56
53
  const maxMoves = Math.max(...this.players.map(p => p.moveIndexes.length));
57
54
  for (let i = 0; i < maxMoves; i++) {
58
55
  for(let j = 0; j < this.players.length; j++)
@@ -72,7 +69,6 @@ class KnightsTour {
72
69
 
73
70
  this.cells = [];
74
71
 
75
- // Set Game Mode logic
76
72
  if (this.players.length === 1) {
77
73
  this.gameMode = (gameMode === "Open" || gameMode === "Closed") ? gameMode : "Open";
78
74
  } else {
@@ -100,28 +96,23 @@ class KnightsTour {
100
96
  isDisabled: this.disabledIndexes.includes(index),
101
97
  isHighlighted: false,
102
98
  isClosed: false,
103
- value: null // Stores the move number or exit count
99
+ value: null
104
100
  };
105
101
  })
106
102
  );
107
103
  }
108
104
 
109
- // Helper to get cell by index without repetitive math
110
105
  getCell(index) {
111
106
  const r = Math.floor(index / this.cols);
112
107
  const c = index % this.cols;
113
108
  return this.matrix[r] ? this.matrix[r][c] : null;
114
109
  }
115
110
 
116
- /**
117
- * Core logic: Identifies where the current player can move.
118
- */
119
111
  getAvailableMovesFor(playerIndex) {
120
112
  const player = this.players[playerIndex];
121
113
  const lastMove = player.moveIndexes[player.moveIndexes.length - 1];
122
114
 
123
115
  if (lastMove === undefined) {
124
- // First move: any non-disabled, non-visited cell
125
116
  return this.matrix.flat()
126
117
  .filter(cell => this.isAvailable(cell.index))
127
118
  .map(cell => cell.index);
@@ -134,15 +125,12 @@ class KnightsTour {
134
125
 
135
126
  const currentPlayer = this.players[this.turnIndex];
136
127
 
137
- // Record move
138
128
  currentPlayer.moveIndexes.push(index);
139
129
  this.allMoveIndexes.push(this.turnIndex);
140
130
 
141
- // Visually mark the cell with the move number
142
131
  const cell = this.getCell(index);
143
132
  cell.value = currentPlayer.moveIndexes.length;
144
133
 
145
- // Switch turn and refresh board
146
134
  this.turnIndex = (this.turnIndex + 1) % this.players.length;
147
135
  this.updateBoard();
148
136
  return true;
@@ -162,11 +150,10 @@ class KnightsTour {
162
150
 
163
151
  this.generated = false;
164
152
  this.solved = false;
165
- // Reset the cell value completely
153
+
166
154
  const cell = this.getCell(lastMoveIndex);
167
155
  if (cell) cell.value = null;
168
156
 
169
- // Return turn to the player who was undone
170
157
  this.turnIndex = lastPlayerIndex;
171
158
  this.updateBoard(false);
172
159
 
@@ -182,7 +169,6 @@ class KnightsTour {
182
169
 
183
170
  let nextMoves = this.getAvailableMovesFor(startIndex);
184
171
 
185
- // 3. If current player is stuck, try skipping to next player (Co-op/Versus)
186
172
  if (nextMoves.length === 0 && this.players.length > 1) {
187
173
  const nextIdx = (startIndex + 1) % this.players.length;
188
174
  const nextMoves = this.getAvailableMovesFor(nextIdx);
@@ -206,10 +192,8 @@ class KnightsTour {
206
192
  }
207
193
  this.clear();
208
194
 
209
- // 2. Calculate next moves for the current player
210
195
  let nextMoves = this.getNextMoves(this.turnIndex);
211
196
 
212
- // 4. Update Board State
213
197
  if (nextMoves.length === 0) {
214
198
  this.state = "Game Over";
215
199
  if (this.gameMode === "Open") {
@@ -353,7 +337,6 @@ class KnightsTour {
353
337
  isClosed(lastMove = this.players[0].moveIndexes[this.players[0].moveIndexes.length - 1]) {
354
338
  const firstMove = this.players[0].moveIndexes[0];
355
339
  const knightMoves = this.getKnightMovesAt(firstMove, false);
356
- //const lastMove = this.players[0].moveIndexes[this.players[0].moveIndexes.length - 1];
357
340
  return knightMoves.includes(lastMove);
358
341
  }
359
342
 
@@ -421,6 +404,10 @@ class KnightsTour {
421
404
  {
422
405
  nextCell = this.cells[Math.floor(Math.random() * this.cells.length)];
423
406
  }
407
+ if(this.players[this.turnIndex].moveIndexes.length === 0)
408
+ {
409
+ nextCell = this.cells[Math.floor(Math.random() * this.cells.length)];
410
+ }
424
411
  this.move(nextCell.index);
425
412
  }
426
413
 
@@ -428,13 +415,11 @@ class KnightsTour {
428
415
  {
429
416
  if(this.gameMode === "Versus")
430
417
  {
431
- //console.log("Cannot solve in Versus mode");
432
418
  return;
433
419
  }
434
420
  this.solving = true;
435
421
  if(increment === 1000)
436
422
  {
437
- //console.log("No solution");
438
423
  this.solving = false;
439
424
  }
440
425
  else
@@ -446,7 +431,6 @@ class KnightsTour {
446
431
  }
447
432
  else
448
433
  {
449
- //console.log("Solved in " + (increment + 1) + " attempt(s)");
450
434
  this.solving = false;
451
435
  }
452
436
  }
@@ -457,9 +441,7 @@ class KnightsTour {
457
441
  this.generating = true;
458
442
  if(increment === 1000)
459
443
  {
460
- //console.log("No generation");
461
444
  this.generating = false;
462
-
463
445
  }
464
446
  else
465
447
  {
@@ -471,7 +453,6 @@ class KnightsTour {
471
453
  }
472
454
  else
473
455
  {
474
- //console.log("Generated in " + (increment + 1) + " attempt(s)");
475
456
  this.generating = false;
476
457
  }
477
458
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knightstour",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",