@oathompsonjones/mini-games 1.0.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/.eslintrc +6 -0
- package/.gitattributes +1 -0
- package/LICENSE +20 -0
- package/README.md +6 -0
- package/build/base/board.d.ts +171 -0
- package/build/base/board.js +330 -0
- package/build/base/controller.d.ts +124 -0
- package/build/base/controller.js +185 -0
- package/build/bitBoard/bitBoard.d.ts +133 -0
- package/build/bitBoard/bitBoard.js +56 -0
- package/build/bitBoard/intBitBoard.d.ts +27 -0
- package/build/bitBoard/intBitBoard.js +65 -0
- package/build/bitBoard/longInt.d.ts +206 -0
- package/build/bitBoard/longInt.js +310 -0
- package/build/bitBoard/longIntBitBoard.d.ts +46 -0
- package/build/bitBoard/longIntBitBoard.js +85 -0
- package/build/console.d.ts +23 -0
- package/build/console.js +30 -0
- package/build/games/connect4/board.d.ts +15 -0
- package/build/games/connect4/board.js +88 -0
- package/build/games/connect4/controller.d.ts +11 -0
- package/build/games/connect4/controller.js +49 -0
- package/build/games/tictactoe/board.d.ts +7 -0
- package/build/games/tictactoe/board.js +18 -0
- package/build/games/tictactoe/controller.d.ts +11 -0
- package/build/games/tictactoe/controller.js +49 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +3 -0
- package/package.json +32 -0
- package/patches/eventemitter3+^5.0.1.patch +49 -0
- package/tsconfig.build.json +8 -0
package/.eslintrc
ADDED
package/.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.js linguist-language=TypeScript
|
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright © 2023 Oliver Jones
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# MiniGames
|
|
2
|
+
This repository contains a selection of MiniGames.
|
|
3
|
+
You will be able to play these games on [my website](https://oathompsonjones.co.uk/arcade) in the future.
|
|
4
|
+
|
|
5
|
+
For now, enjoy this clip of what happens when a computer plays TicTacToe against itself 1000 times.
|
|
6
|
+

|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type BitBoard from "../bitBoard/bitBoard.js";
|
|
2
|
+
export interface Position {
|
|
3
|
+
y: number;
|
|
4
|
+
x: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Defines the characters used to draw a grid.
|
|
8
|
+
*/
|
|
9
|
+
export declare const enum GridLines {
|
|
10
|
+
Horizontal = "\u2500",
|
|
11
|
+
Vertical = "\u2502",
|
|
12
|
+
TopLeft = "\u250C",
|
|
13
|
+
TopRight = "\u2510",
|
|
14
|
+
BottomLeft = "\u2514",
|
|
15
|
+
BottomRight = "\u2518",
|
|
16
|
+
TLeft = "\u251C",
|
|
17
|
+
TRight = "\u2524",
|
|
18
|
+
TTop = "\u252C",
|
|
19
|
+
TBottom = "\u2534",
|
|
20
|
+
Cross = "\u253C"
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Represents a game board.
|
|
24
|
+
*/
|
|
25
|
+
export default abstract class Board<T extends BitBoard = BitBoard> {
|
|
26
|
+
/**
|
|
27
|
+
* Contains the ID of the game.
|
|
28
|
+
*/
|
|
29
|
+
gameID: string;
|
|
30
|
+
/**
|
|
31
|
+
* Contains the data stored in a BitBoard.
|
|
32
|
+
*/
|
|
33
|
+
protected readonly bitBoard: T;
|
|
34
|
+
/**
|
|
35
|
+
* The width of the board.
|
|
36
|
+
|
|
37
|
+
*/
|
|
38
|
+
protected readonly boardWidth: number;
|
|
39
|
+
/**
|
|
40
|
+
* The height of the board.
|
|
41
|
+
*/
|
|
42
|
+
protected readonly boardHeight: number;
|
|
43
|
+
/**
|
|
44
|
+
* A stack of moves.
|
|
45
|
+
*/
|
|
46
|
+
protected readonly moves: number[];
|
|
47
|
+
/**
|
|
48
|
+
* How many boards there are representing player positions (most likely 2).
|
|
49
|
+
*/
|
|
50
|
+
private readonly numberOfPlayerBoards;
|
|
51
|
+
/**
|
|
52
|
+
* Number of boards in total (most likely also 2).
|
|
53
|
+
*/
|
|
54
|
+
private readonly numberOfBoards;
|
|
55
|
+
/**
|
|
56
|
+
* The board states which represent a winning state.
|
|
57
|
+
*/
|
|
58
|
+
protected abstract readonly winningStates: T[];
|
|
59
|
+
/**
|
|
60
|
+
* Creates an instance of Board.
|
|
61
|
+
*
|
|
62
|
+
* @param width The width of the board.
|
|
63
|
+
* @param height The height of the board.
|
|
64
|
+
* @param playerBoardCount How many boards there are representing player positions (most likely 2).
|
|
65
|
+
* @param extraBoardCount Number of extra boards (most likely 0).
|
|
66
|
+
*/
|
|
67
|
+
protected constructor(width: number, height: number, playerBoardCount?: number, extraBoardCount?: number);
|
|
68
|
+
/**
|
|
69
|
+
* Calculates whether or not the board is full.
|
|
70
|
+
*/
|
|
71
|
+
get isFull(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Calculates whether or not the board is empty.
|
|
74
|
+
*/
|
|
75
|
+
get isEmpty(): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Calculates who the winner is.
|
|
78
|
+
* If the game is not over, the output is false.
|
|
79
|
+
* If there is a winner, the output is their ID.
|
|
80
|
+
* If there is a draw, the output is null.
|
|
81
|
+
*/
|
|
82
|
+
get winner(): 0 | 1 | false | null;
|
|
83
|
+
/**
|
|
84
|
+
* Calculates which cells are empty.
|
|
85
|
+
*/
|
|
86
|
+
get emptyCells(): Position[];
|
|
87
|
+
/**
|
|
88
|
+
* Calculates the heuristic score for a given board state.
|
|
89
|
+
*/
|
|
90
|
+
abstract get heuristic(): number;
|
|
91
|
+
/**
|
|
92
|
+
* Sets the game ID.
|
|
93
|
+
* @param id The ID of the game.
|
|
94
|
+
*/
|
|
95
|
+
setGameID(id: string): void;
|
|
96
|
+
/**
|
|
97
|
+
* Makes a move on the board.
|
|
98
|
+
*
|
|
99
|
+
* @param move The position of the move.
|
|
100
|
+
* @param playerId The player who's making the move.
|
|
101
|
+
*/
|
|
102
|
+
makeMove(move: Position, playerId: number): void;
|
|
103
|
+
/**
|
|
104
|
+
* Reverses the last move.
|
|
105
|
+
*/
|
|
106
|
+
undoLastMove(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Checks if a move is valid.
|
|
109
|
+
*
|
|
110
|
+
* @param move The move.
|
|
111
|
+
* @returns Whether or not it's valid.
|
|
112
|
+
*/
|
|
113
|
+
moveIsValid(move: Position): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Checks which player is occupying a given cell.
|
|
116
|
+
*
|
|
117
|
+
* @param cell The cell to check.
|
|
118
|
+
* @returns If the cell is empty, the output is null, otherwise the output is the player's ID.
|
|
119
|
+
*/
|
|
120
|
+
cellOccupier(cell: Position): number | null;
|
|
121
|
+
/**
|
|
122
|
+
* Creates a string representation of the board.
|
|
123
|
+
*
|
|
124
|
+
* @param wrap Whether or not to provide a border for the board.
|
|
125
|
+
* @param labelX Whether or not to label x.
|
|
126
|
+
* @param labelY Whether or not to label y.
|
|
127
|
+
* @param symbols The symbols to use as board pieces.
|
|
128
|
+
* @param colour Whether or not to colour the pieces.
|
|
129
|
+
* @returns The string representation.
|
|
130
|
+
*/
|
|
131
|
+
toString(wrap?: boolean, labelX?: boolean, labelY?: boolean, symbols?: string[], colour?: boolean): string;
|
|
132
|
+
/**
|
|
133
|
+
* Determines if a given player has a line of pieces on the board.
|
|
134
|
+
*
|
|
135
|
+
* @param playerId The ID of the player.
|
|
136
|
+
* @param length The number of pieces needed.
|
|
137
|
+
* @param maxGaps The number of gaps allowed for a line to be valid. Defaults to 0.
|
|
138
|
+
* @returns How many lines exist.
|
|
139
|
+
*/
|
|
140
|
+
hasLine(playerId: number, length: number, maxGaps?: number): number;
|
|
141
|
+
/**
|
|
142
|
+
* Gets a bit index from its coordinates and player ID.
|
|
143
|
+
*
|
|
144
|
+
* @param move The coordinates.
|
|
145
|
+
* @param playerId The player ID.
|
|
146
|
+
* @returns The bit index.
|
|
147
|
+
*/
|
|
148
|
+
protected getBitIndex(move: Position, playerId: number): number;
|
|
149
|
+
/**
|
|
150
|
+
* A BitBoard containing only the player's bits.
|
|
151
|
+
*
|
|
152
|
+
* @param playerId The player's ID.
|
|
153
|
+
* @returns The player's bits.
|
|
154
|
+
*/
|
|
155
|
+
protected getPlayerBoard(playerId: number): T;
|
|
156
|
+
/**
|
|
157
|
+
* Gets a bit index from its coordinates.
|
|
158
|
+
*
|
|
159
|
+
* @param move The coordinates.
|
|
160
|
+
* @returns The bit index.
|
|
161
|
+
*/
|
|
162
|
+
private getIndex;
|
|
163
|
+
/**
|
|
164
|
+
* Checks if a move is valid for the given board.
|
|
165
|
+
* Does not check if that cell is already occupied.
|
|
166
|
+
*
|
|
167
|
+
* @param position The position to check.
|
|
168
|
+
* @returns Whether or not that cell exists on the board.
|
|
169
|
+
*/
|
|
170
|
+
private isValidPosition;
|
|
171
|
+
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import IntBitBoard from "../bitBoard/intBitBoard.js";
|
|
2
|
+
import LongInt from "../bitBoard/longInt.js";
|
|
3
|
+
import LongIntBitBoard from "../bitBoard/longIntBitBoard.js";
|
|
4
|
+
/**
|
|
5
|
+
* Defines the characters used to draw a grid.
|
|
6
|
+
*/
|
|
7
|
+
export var GridLines;
|
|
8
|
+
(function (GridLines) {
|
|
9
|
+
GridLines["Horizontal"] = "\u2500";
|
|
10
|
+
GridLines["Vertical"] = "\u2502";
|
|
11
|
+
GridLines["TopLeft"] = "\u250C";
|
|
12
|
+
GridLines["TopRight"] = "\u2510";
|
|
13
|
+
GridLines["BottomLeft"] = "\u2514";
|
|
14
|
+
GridLines["BottomRight"] = "\u2518";
|
|
15
|
+
GridLines["TLeft"] = "\u251C";
|
|
16
|
+
GridLines["TRight"] = "\u2524";
|
|
17
|
+
GridLines["TTop"] = "\u252C";
|
|
18
|
+
GridLines["TBottom"] = "\u2534";
|
|
19
|
+
GridLines["Cross"] = "\u253C";
|
|
20
|
+
})(GridLines || (GridLines = {}));
|
|
21
|
+
/**
|
|
22
|
+
* Represents a game board.
|
|
23
|
+
*/
|
|
24
|
+
export default class Board {
|
|
25
|
+
/**
|
|
26
|
+
* Contains the ID of the game.
|
|
27
|
+
*/
|
|
28
|
+
gameID = "";
|
|
29
|
+
/**
|
|
30
|
+
* Contains the data stored in a BitBoard.
|
|
31
|
+
*/
|
|
32
|
+
bitBoard;
|
|
33
|
+
/**
|
|
34
|
+
* The width of the board.
|
|
35
|
+
|
|
36
|
+
*/
|
|
37
|
+
boardWidth;
|
|
38
|
+
/**
|
|
39
|
+
* The height of the board.
|
|
40
|
+
*/
|
|
41
|
+
boardHeight;
|
|
42
|
+
/**
|
|
43
|
+
* A stack of moves.
|
|
44
|
+
*/
|
|
45
|
+
moves = [];
|
|
46
|
+
/**
|
|
47
|
+
* How many boards there are representing player positions (most likely 2).
|
|
48
|
+
*/
|
|
49
|
+
numberOfPlayerBoards;
|
|
50
|
+
/**
|
|
51
|
+
* Number of boards in total (most likely also 2).
|
|
52
|
+
*/
|
|
53
|
+
numberOfBoards;
|
|
54
|
+
/**
|
|
55
|
+
* Creates an instance of Board.
|
|
56
|
+
*
|
|
57
|
+
* @param width The width of the board.
|
|
58
|
+
* @param height The height of the board.
|
|
59
|
+
* @param playerBoardCount How many boards there are representing player positions (most likely 2).
|
|
60
|
+
* @param extraBoardCount Number of extra boards (most likely 0).
|
|
61
|
+
*/
|
|
62
|
+
constructor(width, height, playerBoardCount = 2, extraBoardCount = 0) {
|
|
63
|
+
this.boardWidth = width;
|
|
64
|
+
this.boardHeight = height;
|
|
65
|
+
this.numberOfPlayerBoards = playerBoardCount;
|
|
66
|
+
this.numberOfBoards = this.numberOfPlayerBoards + extraBoardCount;
|
|
67
|
+
const totalBits = this.boardWidth * this.boardHeight * this.numberOfBoards;
|
|
68
|
+
this.bitBoard = (totalBits > 32 ? new LongIntBitBoard(Math.ceil(totalBits / 32)) : new IntBitBoard());
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Calculates whether or not the board is full.
|
|
72
|
+
*/
|
|
73
|
+
get isFull() {
|
|
74
|
+
let isFull = (this.bitBoard instanceof LongIntBitBoard
|
|
75
|
+
? new LongIntBitBoard(Math.ceil(this.boardWidth * this.boardHeight / 32))
|
|
76
|
+
: new IntBitBoard());
|
|
77
|
+
for (let i = 0; i < this.numberOfPlayerBoards; i++)
|
|
78
|
+
isFull = isFull.or(this.getPlayerBoard(i));
|
|
79
|
+
const fullValue = this.bitBoard instanceof LongIntBitBoard
|
|
80
|
+
? new LongInt([
|
|
81
|
+
...Array(Math.ceil(this.boardWidth * this.boardHeight / 32) - 1).fill(~0 >>> 0),
|
|
82
|
+
2 ** (this.boardWidth * this.boardHeight - (Math.ceil(this.boardWidth * this.boardHeight / 32) - 1) * 32) - 1
|
|
83
|
+
])
|
|
84
|
+
: 2 ** (this.boardWidth * this.boardHeight) - 1;
|
|
85
|
+
return isFull.equals(fullValue);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Calculates whether or not the board is empty.
|
|
89
|
+
*/
|
|
90
|
+
get isEmpty() {
|
|
91
|
+
let isEmpty = (this.bitBoard instanceof LongIntBitBoard
|
|
92
|
+
? new LongIntBitBoard(Math.ceil(this.boardWidth * this.boardHeight / 32))
|
|
93
|
+
: new IntBitBoard());
|
|
94
|
+
for (let i = 0; i < this.numberOfPlayerBoards; i++)
|
|
95
|
+
isEmpty = isEmpty.or(this.getPlayerBoard(i));
|
|
96
|
+
return isEmpty.equals(0);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Calculates who the winner is.
|
|
100
|
+
* If the game is not over, the output is false.
|
|
101
|
+
* If there is a winner, the output is their ID.
|
|
102
|
+
* If there is a draw, the output is null.
|
|
103
|
+
*/
|
|
104
|
+
get winner() {
|
|
105
|
+
const playerOneBoard = this.getPlayerBoard(0);
|
|
106
|
+
const playerTwoBoard = this.getPlayerBoard(1);
|
|
107
|
+
for (const state of this.winningStates) {
|
|
108
|
+
if (playerOneBoard.and(state).equals(state))
|
|
109
|
+
return 0;
|
|
110
|
+
if (playerTwoBoard.and(state).equals(state))
|
|
111
|
+
return 1;
|
|
112
|
+
}
|
|
113
|
+
if (this.isFull)
|
|
114
|
+
return null;
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Calculates which cells are empty.
|
|
119
|
+
*/
|
|
120
|
+
get emptyCells() {
|
|
121
|
+
const emptyCells = [];
|
|
122
|
+
for (let y = 0; y < this.boardHeight; y++) {
|
|
123
|
+
for (let x = 0; x < this.boardWidth; x++) {
|
|
124
|
+
if (this.cellOccupier({ x, y }) === null)
|
|
125
|
+
emptyCells.push({ x, y });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return emptyCells;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Sets the game ID.
|
|
132
|
+
* @param id The ID of the game.
|
|
133
|
+
*/
|
|
134
|
+
setGameID(id) {
|
|
135
|
+
this.gameID = id;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Makes a move on the board.
|
|
139
|
+
*
|
|
140
|
+
* @param move The position of the move.
|
|
141
|
+
* @param playerId The player who's making the move.
|
|
142
|
+
*/
|
|
143
|
+
makeMove(move, playerId) {
|
|
144
|
+
const bit = this.getBitIndex(move, playerId);
|
|
145
|
+
this.moves.push(bit);
|
|
146
|
+
this.bitBoard.setBit(bit);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Reverses the last move.
|
|
150
|
+
*/
|
|
151
|
+
undoLastMove() {
|
|
152
|
+
const lastMove = this.moves.pop();
|
|
153
|
+
if (lastMove === undefined)
|
|
154
|
+
throw new Error("No move to undo.");
|
|
155
|
+
this.bitBoard.clearBit(lastMove);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Checks if a move is valid.
|
|
159
|
+
*
|
|
160
|
+
* @param move The move.
|
|
161
|
+
* @returns Whether or not it's valid.
|
|
162
|
+
*/
|
|
163
|
+
moveIsValid(move) {
|
|
164
|
+
return this.isValidPosition(move) && this.cellOccupier(move) === null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Checks which player is occupying a given cell.
|
|
168
|
+
*
|
|
169
|
+
* @param cell The cell to check.
|
|
170
|
+
* @returns If the cell is empty, the output is null, otherwise the output is the player's ID.
|
|
171
|
+
*/
|
|
172
|
+
cellOccupier(cell) {
|
|
173
|
+
for (let i = 0; i < this.numberOfPlayerBoards; i++) {
|
|
174
|
+
if (this.bitBoard.getBit(this.getBitIndex(cell, i)) === 1)
|
|
175
|
+
return i;
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Creates a string representation of the board.
|
|
181
|
+
*
|
|
182
|
+
* @param wrap Whether or not to provide a border for the board.
|
|
183
|
+
* @param labelX Whether or not to label x.
|
|
184
|
+
* @param labelY Whether or not to label y.
|
|
185
|
+
* @param symbols The symbols to use as board pieces.
|
|
186
|
+
* @param colour Whether or not to colour the pieces.
|
|
187
|
+
* @returns The string representation.
|
|
188
|
+
*/
|
|
189
|
+
toString(wrap = true, labelX = true, labelY = true, symbols = ["X", "O"], colour = true) {
|
|
190
|
+
if (symbols.length !== this.numberOfPlayerBoards)
|
|
191
|
+
throw new Error("Too many symbols.");
|
|
192
|
+
const symbolLength = symbols[0].length;
|
|
193
|
+
if (symbols.some((s) => s.length !== symbolLength))
|
|
194
|
+
throw new Error("Symbols must be the same length.");
|
|
195
|
+
const matchCellSpace = new RegExp(`.{${symbolLength + 2}}`, "gu");
|
|
196
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
197
|
+
const xLabels = labelX
|
|
198
|
+
? `${alphabet.slice(0, this.boardWidth)
|
|
199
|
+
.split("")
|
|
200
|
+
.map((letter) => ` ${letter.padStart(symbolLength)} `)
|
|
201
|
+
.join("")
|
|
202
|
+
.match(matchCellSpace)
|
|
203
|
+
.join(" ")
|
|
204
|
+
.padStart(4 * this.boardWidth - 1 + Number(wrap) + Number(labelY))}\n`
|
|
205
|
+
: "";
|
|
206
|
+
const topBorder = wrap
|
|
207
|
+
? `${labelY ? " " : ""}${GridLines.TopLeft}${GridLines.Horizontal
|
|
208
|
+
.repeat(this.boardWidth * (symbolLength + 2))
|
|
209
|
+
.match(matchCellSpace)
|
|
210
|
+
.join(GridLines.TTop)}${GridLines.TopRight}\n`
|
|
211
|
+
: "";
|
|
212
|
+
const bottomBorder = wrap
|
|
213
|
+
? `${labelY ? " " : ""}${GridLines.BottomLeft}${GridLines.Horizontal
|
|
214
|
+
.repeat(this.boardWidth * (symbolLength + 2))
|
|
215
|
+
.match(matchCellSpace)
|
|
216
|
+
.join(GridLines.TBottom)}${GridLines.BottomRight}`
|
|
217
|
+
: "";
|
|
218
|
+
const rowSeparator = `${labelY ? " " : ""}${wrap ? GridLines.TLeft : ""}${GridLines.Horizontal
|
|
219
|
+
.repeat(this.boardWidth * (symbolLength + 2))
|
|
220
|
+
.match(matchCellSpace)
|
|
221
|
+
.join(GridLines.Cross)}${wrap ? GridLines.TRight : ""}\n`;
|
|
222
|
+
const rows = [];
|
|
223
|
+
for (let y = 0; y < this.boardHeight; y++) {
|
|
224
|
+
const yLabel = labelY ? `${y + 1}` : "";
|
|
225
|
+
const leftBorder = wrap ? GridLines.Vertical : "";
|
|
226
|
+
const rightBorder = wrap ? GridLines.Vertical : "";
|
|
227
|
+
let row = `${yLabel}${leftBorder}`;
|
|
228
|
+
for (let x = 0; x < this.boardWidth; x++) {
|
|
229
|
+
const cell = { x, y };
|
|
230
|
+
const bar = x === this.boardWidth - 1 ? "" : GridLines.Vertical;
|
|
231
|
+
const cellOccupier = this.cellOccupier(cell);
|
|
232
|
+
if (cellOccupier === null) {
|
|
233
|
+
row += ` ${" ".repeat(symbolLength)} ${bar}`;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
row += ` ${colour ? `\x1b[${[91, 93][cellOccupier]}m` : ""}` +
|
|
237
|
+
`${symbols[cellOccupier]}` +
|
|
238
|
+
`${colour ? "\x1b[0m" : ""} ${bar}`;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
rows.push(`${row}${rightBorder}\n`);
|
|
242
|
+
}
|
|
243
|
+
return `${xLabels}${topBorder}${rows.join(rowSeparator)}${bottomBorder}`;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Determines if a given player has a line of pieces on the board.
|
|
247
|
+
*
|
|
248
|
+
* @param playerId The ID of the player.
|
|
249
|
+
* @param length The number of pieces needed.
|
|
250
|
+
* @param maxGaps The number of gaps allowed for a line to be valid. Defaults to 0.
|
|
251
|
+
* @returns How many lines exist.
|
|
252
|
+
*/
|
|
253
|
+
hasLine(playerId, length, maxGaps = 0) {
|
|
254
|
+
if (length > Math.max(this.boardWidth, this.boardHeight))
|
|
255
|
+
return 0;
|
|
256
|
+
const DIRECTIONS = [{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 1, y: -1 }];
|
|
257
|
+
let lineCount = 0;
|
|
258
|
+
let gaps = [0, 0, 0, 0];
|
|
259
|
+
let lengths = [0, 0, 0, 0];
|
|
260
|
+
const checkCell = (x, y, direction) => {
|
|
261
|
+
const cell = { x, y };
|
|
262
|
+
if (this.isValidPosition(cell)) {
|
|
263
|
+
const cellOccupier = this.cellOccupier(cell);
|
|
264
|
+
if (cellOccupier === null)
|
|
265
|
+
gaps[direction]++;
|
|
266
|
+
else if (cellOccupier === playerId)
|
|
267
|
+
lengths[direction]++;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
for (let x = 0; x < this.boardWidth; x++) {
|
|
271
|
+
for (let y = 0; y < this.boardHeight; y++) {
|
|
272
|
+
gaps = [0, 0, 0, 0];
|
|
273
|
+
lengths = [0, 0, 0, 0];
|
|
274
|
+
for (let i = 0; i < length; i++) {
|
|
275
|
+
for (let j = 0; j < 4; j++) {
|
|
276
|
+
if (gaps[j] > maxGaps)
|
|
277
|
+
continue;
|
|
278
|
+
checkCell(x + i * DIRECTIONS[j].x, y + i * DIRECTIONS[j].y, j);
|
|
279
|
+
if (lengths[j] === length)
|
|
280
|
+
lineCount++;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return lineCount;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Gets a bit index from its coordinates and player ID.
|
|
289
|
+
*
|
|
290
|
+
* @param move The coordinates.
|
|
291
|
+
* @param playerId The player ID.
|
|
292
|
+
* @returns The bit index.
|
|
293
|
+
*/
|
|
294
|
+
getBitIndex(move, playerId) {
|
|
295
|
+
const moveIndex = this.getIndex(move);
|
|
296
|
+
const bitBoardMoveIndex = moveIndex + this.boardWidth * this.boardHeight * playerId;
|
|
297
|
+
return bitBoardMoveIndex;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* A BitBoard containing only the player's bits.
|
|
301
|
+
*
|
|
302
|
+
* @param playerId The player's ID.
|
|
303
|
+
* @returns The player's bits.
|
|
304
|
+
*/
|
|
305
|
+
getPlayerBoard(playerId) {
|
|
306
|
+
const totalBits = (this.bitBoard instanceof LongIntBitBoard ? this.bitBoard.data.wordCount : 1) * 32;
|
|
307
|
+
const boardSize = this.boardWidth * this.boardHeight;
|
|
308
|
+
return this.bitBoard.leftShift(totalBits - boardSize * (playerId + 1)).rightShift(totalBits - boardSize);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Gets a bit index from its coordinates.
|
|
312
|
+
*
|
|
313
|
+
* @param move The coordinates.
|
|
314
|
+
* @returns The bit index.
|
|
315
|
+
*/
|
|
316
|
+
getIndex(move) {
|
|
317
|
+
return this.boardWidth * move.y + move.x;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Checks if a move is valid for the given board.
|
|
321
|
+
* Does not check if that cell is already occupied.
|
|
322
|
+
*
|
|
323
|
+
* @param position The position to check.
|
|
324
|
+
* @returns Whether or not that cell exists on the board.
|
|
325
|
+
*/
|
|
326
|
+
isValidPosition(position) {
|
|
327
|
+
return position.x >= 0 && position.x < this.boardWidth && position.y >= 0 && position.y < this.boardHeight;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYm9hcmQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYmFzZS9ib2FyZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLFdBQVcsTUFBTSw0QkFBNEIsQ0FBQztBQUNyRCxPQUFPLE9BQU8sTUFBTSx3QkFBd0IsQ0FBQztBQUM3QyxPQUFPLGVBQWUsTUFBTSxnQ0FBZ0MsQ0FBQztBQU83RDs7R0FFRztBQUNILE1BQU0sQ0FBTixJQUFrQixTQVlqQjtBQVpELFdBQWtCLFNBQVM7SUFDdkIsa0NBQXFCLENBQUE7SUFDckIsZ0NBQW1CLENBQUE7SUFDbkIsK0JBQWtCLENBQUE7SUFDbEIsZ0NBQW1CLENBQUE7SUFDbkIsa0NBQXFCLENBQUE7SUFDckIsbUNBQXNCLENBQUE7SUFDdEIsNkJBQWdCLENBQUE7SUFDaEIsOEJBQWlCLENBQUE7SUFDakIsNEJBQWUsQ0FBQTtJQUNmLCtCQUFrQixDQUFBO0lBQ2xCLDZCQUFnQixDQUFBO0FBQ3BCLENBQUMsRUFaaUIsU0FBUyxLQUFULFNBQVMsUUFZMUI7QUFFRDs7R0FFRztBQUNILE1BQU0sQ0FBQyxPQUFPLE9BQWdCLEtBQUs7SUFDL0I7O09BRUc7SUFDSSxNQUFNLEdBQVcsRUFBRSxDQUFDO0lBRTNCOztPQUVHO0lBQ2dCLFFBQVEsQ0FBSTtJQUUvQjs7O09BR0c7SUFDZ0IsVUFBVSxDQUFTO0lBRXRDOztPQUVHO0lBQ2dCLFdBQVcsQ0FBUztJQUV2Qzs7T0FFRztJQUNnQixLQUFLLEdBQWEsRUFBRSxDQUFDO0lBRXhDOztPQUVHO0lBQ2Msb0JBQW9CLENBQVM7SUFFOUM7O09BRUc7SUFDYyxjQUFjLENBQVM7SUFPeEM7Ozs7Ozs7T0FPRztJQUNILFlBQXNCLEtBQWEsRUFBRSxNQUFjLEVBQUUsbUJBQTJCLENBQUMsRUFBRSxrQkFBMEIsQ0FBQztRQUMxRyxJQUFJLENBQUMsVUFBVSxHQUFHLEtBQUssQ0FBQztRQUN4QixJQUFJLENBQUMsV0FBVyxHQUFHLE1BQU0sQ0FB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QUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxTQUFTLENBQUMsT0FBTyxHQUFHLFNBQVMsQ0FBQyxVQUFVO2lCQUM1RCxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsR0FBRyxDQUFDLFlBQVksR0FBRyxDQUFDLENBQUMsQ0FBQztpQkFDNUMsS0FBSyxDQUFDLGNBQWMsQ0FBRTtpQkFDdEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsUUFBUSxJQUFJO1lBQ2xELENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDVCxNQUFNLFlBQVksR0FBRyxJQUFJO1lBQ3JCLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsU0FBUyxDQUFDLFVBQVUsR0FBRyxTQUFTLENBQUMsVUFBVTtpQkFDL0QsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLEdBQUcsQ0FBQyxZQUFZLEdBQUcsQ0FBQyxDQUFDLENBQUM7aUJBQzVDLEtBQUssQ0FBQyxjQUFjLENBQUU7aUJBQ3RCLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLEdBQUcsU0FBUyxDQUFDLFdBQVcsRUFBRTtZQUN0RCxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ1QsTUFBTSxZQUFZLEdBQUcsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLFNBQVMsQ0FBQyxVQUFVO2FBQ3pGLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxHQUFHLENBQUMsWUFBWSxHQUFHLENBQUMsQ0FBQyxDQUFDO2FBQzVDLEtBQUssQ0FBQyxjQUFjLENBQUU7YUFDdEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FB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QyxDQUFDLENBQUMsVUFBVSxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUMsQ0FBQztJQUM3RyxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSyxRQUFRLENBQUMsSUFBYztRQUMzQixPQUFPLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQzdDLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSyxlQUFlLENBQUMsUUFBa0I7UUFDdEMsT0FBTyxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxRQUFRLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxVQUFVLElBQUksUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksUUFBUSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDO0lBQy9HLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIEJpdEJvYXJkIGZyb20gXCIuLi9iaXRCb2FyZC9iaXRCb2FyZC5qc1wiO1xuaW1wb3J0IEludEJpdEJvYXJkIGZyb20gXCIuLi9iaXRCb2FyZC9pbnRCaXRCb2FyZC5qc1wiO1xuaW1wb3J0IExvbmdJbnQgZnJvbSBcIi4uL2JpdEJvYXJkL2xvbmdJbnQuanNcIjtcbmltcG9ydCBMb25nSW50Qml0Qm9hcmQgZnJvbSBcIi4uL2JpdEJvYXJkL2xvbmdJbnRCaXRCb2FyZC5qc1wiO1xuXG5leHBvcnQgaW50ZXJmYWNlIFBvc2l0aW9uIHtcbiAgICB5OiBudW1iZXI7XG4gICAgeDogbnVtYmVyO1xufVxuXG4vKipcbiAqIERlZmluZXMgdGhlIGNoYXJhY3RlcnMgdXNlZCB0byBkcmF3IGEgZ3JpZC5cbiAqL1xuZXhwb3J0IGNvbnN0IGVudW0gR3JpZExpbmVzIHtcbiAgICBIb3Jpem9udGFsID0gXCJcXHUyNTAwXCIsXG4gICAgVmVydGljYWwgPSBcIlxcdTI1MDJcIixcbiAgICBUb3BMZWZ0ID0gXCJcXHUyNTBDXCIsXG4gICAgVG9wUmlnaHQgPSBcIlxcdTI1MTBcIixcbiAgICBCb3R0b21MZWZ0ID0gXCJcXHUyNTE0XCIsXG4gICAgQm90dG9tUmlnaHQgPSBcIlxcdTI1MThcIixcbiAgICBUTGVmdCA9IFwiXFx1MjUxQ1wiLFxuICAgIFRSaWdodCA9IFwiXFx1MjUyNFwiLFxuICAgIFRUb3AgPSBcIlxcdTI1MkNcIixcbiAgICBUQm90dG9tID0gXCJcXHUyNTM0XCIsXG4gICAgQ3Jvc3MgPSBcIlxcdTI1M0NcIlxufVxuXG4vKipcbiAqIFJlcHJlc2VudHMgYSBnYW1lIGJvYXJkLlxuICovXG5leHBvcnQgZGVmYXVsdCBhYnN0cmFjdCBjbGFzcyBCb2FyZDxUIGV4dGVuZHMgQml0Qm9hcmQgPSBCaXRCb2FyZD4ge1xuICAgIC8qKlxuICAgICAqIENvbnRhaW5zIHRoZSBJRCBvZiB0aGUgZ2FtZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2FtZUlEOiBzdHJpbmcgPSBcIlwiO1xuXG4gICAgLyoqXG4gICAgICogQ29udGFpbnMgdGhlIGRhdGEgc3RvcmVkIGluIGEgQml0Qm9hcmQuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IGJpdEJvYXJkOiBUO1xuXG4gICAgLyoqXG4gICAgICogVGhlIHdpZHRoIG9mIHRoZSBib2FyZC5cblxuICAgICAqL1xuICAgIHByb3RlY3RlZCByZWFkb25seSBib2FyZFdpZHRoOiBudW1iZXI7XG5cbiAgICAvKipcbiAgICAgKiBUaGUgaGVpZ2h0IG9mIHRoZSBib2FyZC5cbiAgICAgKi9cbiAgICBwcm90ZWN0ZWQgcmVhZG9ubHkgYm9hcmRIZWlnaHQ6IG51bWJlcjtcblxuICAgIC8qKlxuICAgICAqIEEgc3RhY2sgb2YgbW92ZXMuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IG1vdmVzOiBudW1iZXJbXSA9IFtdO1xuXG4gICAgLyoqXG4gICAgICogSG93IG1hbnkgYm9hcmRzIHRoZXJlIGFyZSByZXByZXNlbnRpbmcgcGxheWVyIHBvc2l0aW9ucyAobW9zdCBsaWtlbHkgMikuXG4gICAgICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBudW1iZXJPZlBsYXllckJvYXJkczogbnVtYmVyO1xuXG4gICAgLyoqXG4gICAgICogTnVtYmVyIG9mIGJvYXJkcyBpbiB0b3RhbCAobW9zdCBsaWtlbHkgYWxzbyAyKS5cbiAgICAgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IG51bWJlck9mQm9hcmRzOiBudW1iZXI7XG5cbiAgICAvKipcbiAgICAgKiBUaGUgYm9hcmQgc3RhdGVzIHdoaWNoIHJlcHJlc2VudCBhIHdpbm5pbmcgc3RhdGUuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIGFic3RyYWN0IHJlYWRvbmx5IHdpbm5pbmdTdGF0ZXM6IFRbXTtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgQm9hcmQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gd2lkdGggVGhlIHdpZHRoIG9mIHRoZSBib2FyZC5cbiAgICAgKiBAcGFyYW0gaGVpZ2h0IFRoZSBoZWlnaHQgb2YgdGhlIGJvYXJkLlxuICAgICAqIEBwYXJhbSBwbGF5ZXJCb2FyZENvdW50IEhvdyBtYW55IGJvYXJkcyB0aGVyZSBhcmUgcmVwcmVzZW50aW5nIHBsYXllciBwb3NpdGlvbnMgKG1vc3QgbGlrZWx5IDIpLlxuICAgICAqIEBwYXJhbSBleHRyYUJvYXJkQ291bnQgTnVtYmVyIG9mIGV4dHJhIGJvYXJkcyAobW9zdCBsaWtlbHkgMCkuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyLCBwbGF5ZXJCb2FyZENvdW50OiBudW1iZXIgPSAyLCBleHRyYUJvYXJkQ291bnQ6IG51bWJlciA9IDApIHtcbiAgICAgICAgdGhpcy5ib2FyZFdpZHRoID0gd2lkdGg7XG4gICAgICAgIHRoaXMuYm9hcmRIZWlnaHQgPSBoZWlnaHQ7XG4gICAgICAgIHRoaXMubnVtYmVyT2ZQbGF5ZXJCb2FyZHMgPSBwbGF5ZXJCb2FyZENvdW50O1xuICAgICAgICB0aGlzLm51bWJlck9mQm9hcmRzID0gdGhpcy5udW1iZXJPZlBsYXllckJvYXJkcyArIGV4dHJhQm9hcmRDb3VudDtcbiAgICAgICAgY29uc3QgdG90YWxCaXRzID0gdGhpcy5ib2FyZFdpZHRoICogdGhpcy5ib2FyZEhlaWdodCAqIHRoaXMubnVtYmVyT2ZCb2FyZHM7XG4gICAgICAgIHRoaXMuYml0Qm9hcmQgPSAodG90YWxCaXRzID4gMzIgPyBuZXcgTG9uZ0ludEJpdEJvYXJkKE1hdGguY2VpbCh0b3RhbEJpdHMgLyAzMikpIDogbmV3IEludEJpdEJvYXJkKCkpIGFzIFQ7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2FsY3VsYXRlcyB3aGV0aGVyIG9yIG5vdCB0aGUgYm9hcmQgaXMgZnVsbC5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0IGlzRnVsbCgpOiBib29sZWFuIHtcbiAgICAgICAgbGV0IGlzRnVsbCA9ICh0aGlzLmJpdEJvYXJkIGluc3RhbmNlb2YgTG9uZ0ludEJpdEJvYXJkXG4gICAgICAgICAgICA/IG5ldyBMb25nSW50Qml0Qm9hcmQoTWF0aC5jZWlsKHRoaXMuYm9hcmRXaWR0aCAqIHRoaXMuYm9hcmRIZWlnaHQgLyAzMikpXG4gICAgICAgICAgICA6IG5ldyBJbnRCaXRCb2FyZCgpKSBhcyBUO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubnVtYmVyT2ZQbGF5ZXJCb2FyZHM7IGkrKylcbiAgICAgICAgICAgIGlzRnVsbCA9IGlzRnVsbC5vcih0aGlzLmdldFBsYXllckJvYXJkKGkpKTtcbiAgICAgICAgY29uc3QgZnVsbFZhbHVlID0gdGhpcy5iaXRCb2FyZCBpbnN0YW5jZW9mIExvbmdJbnRCaXRCb2FyZFxuICAgICAgICAgICAgPyBuZXcgTG9uZ0ludChbXG4gICAgICAgICAgICAgICAgLi4uQXJyYXk8bnVtYmVyPihNYXRoLmNlaWwodGhpcy5ib2FyZFdpZHRoICogdGhpcy5ib2FyZEhlaWdodCAvIDMyKSAtIDEpLmZpbGwofjAgPj4+IDApLFxuICAgICAgICAgICAgICAgIDIgKiogKHRoaXMuYm9hcmRXaWR0aCAqIHRoaXMuYm9hcmRIZWlnaHQgLSAoTWF0aC5jZWlsKHRoaXMuYm9hcmRXaWR0aCAqIHRoaXMuYm9hcmRIZWlnaHQgLyAzMikgLSAxKSAqIDMyKSAtIDFcbiAgICAgICAgICAgIF0pXG4gICAgICAgICAgICA6IDIgKiogKHRoaXMuYm9hcmRXaWR0aCAqIHRoaXMuYm9hcmRIZWlnaHQpIC0gMTtcbiAgICAgICAgcmV0dXJuIGlzRnVsbC5lcXVhbHMoZnVsbFZhbHVlKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGVzIHdoZXRoZXIgb3Igbm90IHRoZSBib2FyZCBpcyBlbXB0eS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0IGlzRW1wdHkoKTogYm9vbGVhbiB7XG4gICAgICAgIGxldCBpc0VtcHR5ID0gKHRoaXMuYml0Qm9hcmQgaW5zdGFuY2VvZiBMb25nSW50Qml0Qm9hcmRcbiAgICAgICAgICAgID8gbmV3IExvbmdJbnRCaXRCb2FyZChNYXRoLmNlaWwodGhpcy5ib2FyZFdpZHRoICogdGhpcy5ib2FyZEhlaWdodCAvIDMyKSlcbiAgICAgICAgICAgIDogbmV3IEludEJpdEJvYXJkKCkpIGFzIFQ7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5udW1iZXJPZlBsYXllckJvYXJkczsgaSsrKVxuICAgICAgICAgICAgaXNFbXB0eSA9IGlzRW1wdHkub3IodGhpcy5nZXRQbGF5ZXJCb2FyZChpKSk7XG4gICAgICAgIHJldHVybiBpc0VtcHR5LmVxdWFscygwKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGVzIHdobyB0aGUgd2lubmVyIGlzLlxuICAgICAqIElmIHRoZSBnYW1lIGlzIG5vdCBvdmVyLCB0aGUgb3V0cHV0IGlzIGZhbHNlLlxuICAgICAqIElmIHRoZXJlIGlzIGEgd2lubmVyLCB0aGUgb3V0cHV0IGlzIHRoZWlyIElELlxuICAgICAqIElmIHRoZXJlIGlzIGEgZHJhdywgdGhlIG91dHB1dCBpcyBudWxsLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXQgd2lubmVyKCk6IDAgfCAxIHwgZmFsc2UgfCBudWxsIHtcbiAgICAgICAgY29uc3QgcGxheWVyT25lQm9hcmQgPSB0aGlzLmdldFBsYXllckJvYXJkKDApO1xuICAgICAgICBjb25zdCBwbGF5ZXJUd29Cb2FyZCA9IHRoaXMuZ2V0UGxheWVyQm9hcmQoMSk7XG4gICAgICAgIGZvciAoY29uc3Qgc3RhdGUgb2YgdGhpcy53aW5uaW5nU3RhdGVzKSB7XG4gICAgICAgICAgICBpZiAocGxheWVyT25lQm9hcmQuYW5kKHN0YXRlKS5lcXVhbHMoc3RhdGUpKVxuICAgICAgICAgICAgICAgIHJldHVybiAwO1xuICAgICAgICAgICAgaWYgKHBsYXllclR3b0JvYXJkLmFuZChzdGF0ZSkuZXF1YWxzKHN0YXRlKSlcbiAgICAgICAgICAgICAgICByZXR1cm4gMTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5pc0Z1bGwpXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZXMgd2hpY2ggY2VsbHMgYXJlIGVtcHR5LlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXQgZW1wdHlDZWxscygpOiBQb3NpdGlvbltdIHtcbiAgICAgICAgY29uc3QgZW1wdHlDZWxsczogUG9zaXRpb25bXSA9IFtdO1xuICAgICAgICBmb3IgKGxldCB5ID0gMDsgeSA8IHRoaXMuYm9hcmRIZWlnaHQ7IHkrKykge1xuICAgICAgICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCB0aGlzLmJvYXJkV2lkdGg7IHgrKykge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmNlbGxPY2N1cGllcih7IHgsIHkgfSkgPT09IG51bGwpXG4gICAgICAgICAgICAgICAgICAgIGVtcHR5Q2VsbHMucHVzaCh7IHgsIHkgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGVtcHR5Q2VsbHM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2FsY3VsYXRlcyB0aGUgaGV1cmlzdGljIHNjb3JlIGZvciBhIGdpdmVuIGJvYXJkIHN0YXRlLlxuICAgICAqL1xuICAgIHB1YmxpYyBhYnN0cmFjdCBnZXQgaGV1cmlzdGljKCk6IG51bWJlcjtcblxuICAgIC8qKlxuICAgICAqIFNldHMgdGhlIGdhbWUgSUQuXG4gICAgICogQHBhcmFtIGlkIFRoZSBJRCBvZiB0aGUgZ2FtZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgc2V0R2FtZUlEKGlkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5nYW1lSUQgPSBpZDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBNYWtlcyBhIG1vdmUgb24gdGhlIGJvYXJkLlxuICAgICAqXG4gICAgICogQHBhcmFtIG1vdmUgVGhlIHBvc2l0aW9uIG9mIHRoZSBtb3ZlLlxuICAgICAqIEBwYXJhbSBwbGF5ZXJJZCBUaGUgcGxheWVyIHdobydzIG1ha2luZyB0aGUgbW92ZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgbWFrZU1vdmUobW92ZTogUG9zaXRpb24sIHBsYXllcklkOiBudW1iZXIpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgYml0ID0gdGhpcy5nZXRCaXRJbmRleChtb3ZlLCBwbGF5ZXJJZCk7XG4gICAgICAgIHRoaXMubW92ZXMucHVzaChiaXQpO1xuICAgICAgICB0aGlzLmJpdEJvYXJkLnNldEJpdChiaXQpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldmVyc2VzIHRoZSBsYXN0IG1vdmUuXG4gICAgICovXG4gICAgcHVibGljIHVuZG9MYXN0TW92ZSgpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgbGFzdE1vdmUgPSB0aGlzLm1vdmVzLnBvcCgpO1xuICAgICAgICBpZiAobGFzdE1vdmUgPT09IHVuZGVmaW5lZClcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIk5vIG1vdmUgdG8gdW5kby5cIik7XG4gICAgICAgIHRoaXMuYml0Qm9hcmQuY2xlYXJCaXQobGFzdE1vdmUpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrcyBpZiBhIG1vdmUgaXMgdmFsaWQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gbW92ZSBUaGUgbW92ZS5cbiAgICAgKiBAcmV0dXJucyBXaGV0aGVyIG9yIG5vdCBpdCdzIHZhbGlkLlxuICAgICAqL1xuICAgIHB1YmxpYyBtb3ZlSXNWYWxpZChtb3ZlOiBQb3NpdGlvbik6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkUG9zaXRpb24obW92ZSkgJiYgdGhpcy5jZWxsT2NjdXBpZXIobW92ZSkgPT09IG51bGw7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2hlY2tzIHdoaWNoIHBsYXllciBpcyBvY2N1cHlpbmcgYSBnaXZlbiBjZWxsLlxuICAgICAqXG4gICAgICogQHBhcmFtIGNlbGwgVGhlIGNlbGwgdG8gY2hlY2suXG4gICAgICogQHJldHVybnMgSWYgdGhlIGNlbGwgaXMgZW1wdHksIHRoZSBvdXRwdXQgaXMgbnVsbCwgb3RoZXJ3aXNlIHRoZSBvdXRwdXQgaXMgdGhlIHBsYXllcidzIElELlxuICAgICAqL1xuICAgIHB1YmxpYyBjZWxsT2NjdXBpZXIoY2VsbDogUG9zaXRpb24pOiBudW1iZXIgfCBudWxsIHtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm51bWJlck9mUGxheWVyQm9hcmRzOyBpKyspIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmJpdEJvYXJkLmdldEJpdCh0aGlzLmdldEJpdEluZGV4KGNlbGwsIGkpKSA9PT0gMSlcbiAgICAgICAgICAgICAgICByZXR1cm4gaTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBib2FyZC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB3cmFwIFdoZXRoZXIgb3Igbm90IHRvIHByb3ZpZGUgYSBib3JkZXIgZm9yIHRoZSBib2FyZC5cbiAgICAgKiBAcGFyYW0gbGFiZWxYIFdoZXRoZXIgb3Igbm90IHRvIGxhYmVsIHguXG4gICAgICogQHBhcmFtIGxhYmVsWSBXaGV0aGVyIG9yIG5vdCB0byBsYWJlbCB5LlxuICAgICAqIEBwYXJhbSBzeW1ib2xzIFRoZSBzeW1ib2xzIHRvIHVzZSBhcyBib2FyZCBwaWVjZXMuXG4gICAgICogQHBhcmFtIGNvbG91ciBXaGV0aGVyIG9yIG5vdCB0byBjb2xvdXIgdGhlIHBpZWNlcy5cbiAgICAgKiBAcmV0dXJucyBUaGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uLlxuICAgICAqL1xuICAgIHB1YmxpYyB0b1N0cmluZyhcbiAgICAgICAgd3JhcDogYm9vbGVhbiA9IHRydWUsXG4gICAgICAgIGxhYmVsWDogYm9vbGVhbiA9IHRydWUsXG4gICAgICAgIGxhYmVsWTogYm9vbGVhbiA9IHRydWUsXG4gICAgICAgIHN5bWJvbHM6IHN0cmluZ1tdID0gW1wiWFwiLCBcIk9cIl0sXG4gICAgICAgIGNvbG91cjogYm9vbGVhbiA9IHRydWVcbiAgICApOiBzdHJpbmcge1xuICAgICAgICBpZiAoc3ltYm9scy5sZW5ndGggIT09IHRoaXMubnVtYmVyT2ZQbGF5ZXJCb2FyZHMpXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJUb28gbWFueSBzeW1ib2xzLlwiKTtcbiAgICAgICAgY29uc3Qgc3ltYm9sTGVuZ3RoID0gc3ltYm9sc1swXSEubGVuZ3RoO1xuICAgICAgICBpZiAoc3ltYm9scy5zb21lKChzKSA9PiBzLmxlbmd0aCAhPT0gc3ltYm9sTGVuZ3RoKSlcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIlN5bWJvbHMgbXVzdCBiZSB0aGUgc2FtZSBsZW5ndGguXCIpO1xuICAgICAgICBjb25zdCBtYXRjaENlbGxTcGFjZSA9IG5ldyBSZWdFeHAoYC57JHtzeW1ib2xMZW5ndGggKyAyfX1gLCBcImd1XCIpO1xuICAgICAgICBjb25zdCBhbHBoYWJldCA9IFwiQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5elwiO1xuICAgICAgICBjb25zdCB4TGFiZWxzID0gbGFiZWxYXG4gICAgICAgICAgICA/IGAke2FscGhhYmV0LnNsaWNlKDAsIHRoaXMuYm9hcmRXaWR0aClcbiAgICAgICAgICAgICAgICAuc3BsaXQoXCJcIilcbiAgICAgICAgICAgICAgICAubWFwKChsZXR0ZXIpID0+IGAgJHtsZXR0ZXIucGFkU3RhcnQoc3ltYm9sTGVuZ3RoKX0gYClcbiAgICAgICAgICAgICAgICAuam9pbihcIlwiKVxuICAgICAgICAgICAgICAgIC5tYXRjaChtYXRjaENlbGxTcGFjZSkhXG4gICAgICAgICAgICAgICAgLmpvaW4oXCIgXCIpXG4gICAgICAgICAgICAgICAgLnBhZFN0YXJ0KDQgKiB0aGlzLmJvYXJkV2lkdGggLSAxICsgTnVtYmVyKHdyYXApICsgTnVtYmVyKGxhYmVsWSkpXG4gICAgICAgICAgICB9XFxuYFxuICAgICAgICAgICAgOiBcIlwiO1xuICAgICAgICBjb25zdCB0b3BCb3JkZXIgPSB3cmFwXG4gICAgICAgICAgICA/IGAke2xhYmVsWSA/IFwiIFwiIDogXCJcIn0ke0dyaWRMaW5lcy5Ub3BMZWZ0fSR7R3JpZExpbmVzLkhvcml6b250YWxcbiAgICAgICAgICAgICAgICAucmVwZWF0KHRoaXMuYm9hcmRXaWR0aCAqIChzeW1ib2xMZW5ndGggKyAyKSlcbiAgICAgICAgICAgICAgICAubWF0Y2gobWF0Y2hDZWxsU3BhY2UpIVxuICAgICAgICAgICAgICAgIC5qb2luKEdyaWRMaW5lcy5UVG9wKX0ke0dyaWRMaW5lcy5Ub3BSaWdodH1cXG5gXG4gICAgICAgICAgICA6IFwiXCI7XG4gICAgICAgIGNvbnN0IGJvdHRvbUJvcmRlciA9IHdyYXBcbiAgICAgICAgICAgID8gYCR7bGFiZWxZID8gXCIgXCIgOiBcIlwifSR7R3JpZExpbmVzLkJvdHRvbUxlZnR9JHtHcmlkTGluZXMuSG9yaXpvbnRhbFxuICAgICAgICAgICAgICAgIC5yZXBlYXQodGhpcy5ib2FyZFdpZHRoICogKHN5bWJvbExlbmd0aCArIDIpKVxuICAgICAgICAgICAgICAgIC5tYXRjaChtYXRjaENlbGxTcGFjZSkhXG4gICAgICAgICAgICAgICAgLmpvaW4oR3JpZExpbmVzLlRCb3R0b20pfSR7R3JpZExpbmVzLkJvdHRvbVJpZ2h0fWBcbiAgICAgICAgICAgIDogXCJcIjtcbiAgICAgICAgY29uc3Qgcm93U2VwYXJhdG9yID0gYCR7bGFiZWxZID8gXCIgXCIgOiBcIlwifSR7d3JhcCA/IEdyaWRMaW5lcy5UTGVmdCA6IFwiXCJ9JHtHcmlkTGluZXMuSG9yaXpvbnRhbFxuICAgICAgICAgICAgLnJlcGVhdCh0aGlzLmJvYXJkV2lkdGggKiAoc3ltYm9sTGVuZ3RoICsgMikpXG4gICAgICAgICAgICAubWF0Y2gobWF0Y2hDZWxsU3BhY2UpIVxuICAgICAgICAgICAgLmpvaW4oR3JpZExpbmVzLkNyb3NzKX0ke3dyYXAgPyBHcmlkTGluZXMuVFJpZ2h0IDogXCJcIn1cXG5gO1xuICAgICAgICBjb25zdCByb3dzOiBzdHJpbmdbXSA9IFtdO1xuICAgICAgICBmb3IgKGxldCB5ID0gMDsgeSA8IHRoaXMuYm9hcmRIZWlnaHQ7IHkrKykge1xuICAgICAgICAgICAgY29uc3QgeUxhYmVsID0gbGFiZWxZID8gYCR7eSArIDF9YCA6IFwiXCI7XG4gICAgICAgICAgICBjb25zdCBsZWZ0Qm9yZGVyID0gd3JhcCA/IEdyaWRMaW5lcy5WZXJ0aWNhbCA6IFwiXCI7XG4gICAgICAgICAgICBjb25zdCByaWdodEJvcmRlciA9IHdyYXAgPyBHcmlkTGluZXMuVmVydGljYWwgOiBcIlwiO1xuICAgICAgICAgICAgbGV0IHJvdyA9IGAke3lMYWJlbH0ke2xlZnRCb3JkZXJ9YDtcbiAgICAgICAgICAgIGZvciAobGV0IHggPSAwOyB4IDwgdGhpcy5ib2FyZFdpZHRoOyB4KyspIHtcbiAgICAgICAgICAgICAgICBjb25zdCBjZWxsID0geyB4LCB5IH07XG4gICAgICAgICAgICAgICAgY29uc3QgYmFyID0geCA9PT0gdGhpcy5ib2FyZFdpZHRoIC0gMSA/IFwiXCIgOiBHcmlkTGluZXMuVmVydGljYWw7XG4gICAgICAgICAgICAgICAgY29uc3QgY2VsbE9jY3VwaWVyID0gdGhpcy5jZWxsT2NjdXBpZXIoY2VsbCk7XG4gICAgICAgICAgICAgICAgaWYgKGNlbGxPY2N1cGllciA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICByb3cgKz0gYCAke1wiIFwiLnJlcGVhdChzeW1ib2xMZW5ndGgpfSAke2Jhcn1gO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJvdyArPSBgICR7Y29sb3VyID8gYFxceDFiWyR7WzkxLCA5M11bY2VsbE9jY3VwaWVyXSF9bWAgOiBcIlwifWAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgYCR7c3ltYm9sc1tjZWxsT2NjdXBpZXJdfWAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgYCR7Y29sb3VyID8gXCJcXHgxYlswbVwiIDogXCJcIn0gJHtiYXJ9YDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByb3dzLnB1c2goYCR7cm93fSR7cmlnaHRCb3JkZXJ9XFxuYCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGAke3hMYWJlbHN9JHt0b3BCb3JkZXJ9JHtyb3dzLmpvaW4ocm93U2VwYXJhdG9yKX0ke2JvdHRvbUJvcmRlcn1gO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIERldGVybWluZXMgaWYgYSBnaXZlbiBwbGF5ZXIgaGFzIGEgbGluZSBvZiBwaWVjZXMgb24gdGhlIGJvYXJkLlxuICAgICAqXG4gICAgICogQHBhcmFtIHBsYXllcklkIFRoZSBJRCBvZiB0aGUgcGxheWVyLlxuICAgICAqIEBwYXJhbSBsZW5ndGggVGhlIG51bWJlciBvZiBwaWVjZXMgbmVlZGVkLlxuICAgICAqIEBwYXJhbSBtYXhHYXBzIFRoZSBudW1iZXIgb2YgZ2FwcyBhbGxvd2VkIGZvciBhIGxpbmUgdG8gYmUgdmFsaWQuIERlZmF1bHRzIHRvIDAuXG4gICAgICogQHJldHVybnMgSG93IG1hbnkgbGluZXMgZXhpc3QuXG4gICAgICovXG4gICAgcHVibGljIGhhc0xpbmUocGxheWVySWQ6IG51bWJlciwgbGVuZ3RoOiBudW1iZXIsIG1heEdhcHM6IG51bWJlciA9IDApOiBudW1iZXIge1xuICAgICAgICBpZiAobGVuZ3RoID4gTWF0aC5tYXgodGhpcy5ib2FyZFdpZHRoLCB0aGlzLmJvYXJkSGVpZ2h0KSlcbiAgICAgICAgICAgIHJldHVybiAwO1xuICAgICAgICBjb25zdCBESVJFQ1RJT05TOiBbUG9zaXRpb24sIFBvc2l0aW9uLCBQb3NpdGlvbiwgUG9zaXRpb25dID0gW3sgeDogMSwgeTogMCB9LCB7IHg6IDAsIHk6IDEgfSwgeyB4OiAxLCB5OiAxIH0sIHsgeDogMSwgeTogLTEgfV07XG4gICAgICAgIGxldCBsaW5lQ291bnQgPSAwO1xuICAgICAgICBsZXQgZ2FwczogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0gPSBbMCwgMCwgMCwgMF07XG4gICAgICAgIGxldCBsZW5ndGhzOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSA9IFswLCAwLCAwLCAwXTtcbiAgICAgICAgY29uc3QgY2hlY2tDZWxsID0gKHg6IG51bWJlciwgeTogbnVtYmVyLCBkaXJlY3Rpb246IDAgfCAxIHwgMiB8IDMpOiB2b2lkID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGNlbGwgPSB7IHgsIHkgfSBhcyBQb3NpdGlvbjtcbiAgICAgICAgICAgIGlmICh0aGlzLmlzVmFsaWRQb3NpdGlvbihjZWxsKSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGNlbGxPY2N1cGllciA9IHRoaXMuY2VsbE9jY3VwaWVyKGNlbGwpO1xuICAgICAgICAgICAgICAgIGlmIChjZWxsT2NjdXBpZXIgPT09IG51bGwpXG4gICAgICAgICAgICAgICAgICAgIGdhcHNbZGlyZWN0aW9uXSsrO1xuICAgICAgICAgICAgICAgIGVsc2UgaWYgKGNlbGxPY2N1cGllciA9PT0gcGxheWVySWQpXG4gICAgICAgICAgICAgICAgICAgIGxlbmd0aHNbZGlyZWN0aW9uXSsrO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICBmb3IgKGxldCB4ID0gMDsgeCA8IHRoaXMuYm9hcmRXaWR0aDsgeCsrKSB7XG4gICAgICAgICAgICBmb3IgKGxldCB5ID0gMDsgeSA8IHRoaXMuYm9hcmRIZWlnaHQ7IHkrKykge1xuICAgICAgICAgICAgICAgIGdhcHMgPSBbMCwgMCwgMCwgMF07XG4gICAgICAgICAgICAgICAgbGVuZ3RocyA9IFswLCAwLCAwLCAwXTtcbiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvciAobGV0IGogPSAwIGFzIDAgfCAxIHwgMiB8IDM7IGogPCA0OyBqKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChnYXBzW2pdID4gbWF4R2FwcylcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrQ2VsbCh4ICsgaSAqIERJUkVDVElPTlNbal0ueCwgeSArIGkgKiBESVJFQ1RJT05TW2pdLnksIGopO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGxlbmd0aHNbal0gPT09IGxlbmd0aClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5lQ291bnQrKztcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbGluZUNvdW50O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldHMgYSBiaXQgaW5kZXggZnJvbSBpdHMgY29vcmRpbmF0ZXMgYW5kIHBsYXllciBJRC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBtb3ZlIFRoZSBjb29yZGluYXRlcy5cbiAgICAgKiBAcGFyYW0gcGxheWVySWQgVGhlIHBsYXllciBJRC5cbiAgICAgKiBAcmV0dXJucyBUaGUgYml0IGluZGV4LlxuICAgICAqL1xuICAgIHByb3RlY3RlZCBnZXRCaXRJbmRleChtb3ZlOiBQb3NpdGlvbiwgcGxheWVySWQ6IG51bWJlcik6IG51bWJlciB7XG4gICAgICAgIGNvbnN0IG1vdmVJbmRleCA9IHRoaXMuZ2V0SW5kZXgobW92ZSk7XG4gICAgICAgIGNvbnN0IGJpdEJvYXJkTW92ZUluZGV4ID0gbW92ZUluZGV4ICsgdGhpcy5ib2FyZFdpZHRoICogdGhpcy5ib2FyZEhlaWdodCAqIHBsYXllcklkO1xuICAgICAgICByZXR1cm4gYml0Qm9hcmRNb3ZlSW5kZXg7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQSBCaXRCb2FyZCBjb250YWluaW5nIG9ubHkgdGhlIHBsYXllcidzIGJpdHMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gcGxheWVySWQgVGhlIHBsYXllcidzIElELlxuICAgICAqIEByZXR1cm5zIFRoZSBwbGF5ZXIncyBiaXRzLlxuICAgICAqL1xuICAgIHByb3RlY3RlZCBnZXRQbGF5ZXJCb2FyZChwbGF5ZXJJZDogbnVtYmVyKTogVCB7XG4gICAgICAgIGNvbnN0IHRvdGFsQml0cyA9ICh0aGlzLmJpdEJvYXJkIGluc3RhbmNlb2YgTG9uZ0ludEJpdEJvYXJkID8gdGhpcy5iaXRCb2FyZC5kYXRhLndvcmRDb3VudCA6IDEpICogMzI7XG4gICAgICAgIGNvbnN0IGJvYXJkU2l6ZSA9IHRoaXMuYm9hcmRXaWR0aCAqIHRoaXMuYm9hcmRIZWlnaHQ7XG4gICAgICAgIHJldHVybiB0aGlzLmJpdEJvYXJkLmxlZnRTaGlmdCh0b3RhbEJpdHMgLSBib2FyZFNpemUgKiAocGxheWVySWQgKyAxKSkucmlnaHRTaGlmdCh0b3RhbEJpdHMgLSBib2FyZFNpemUpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldHMgYSBiaXQgaW5kZXggZnJvbSBpdHMgY29vcmRpbmF0ZXMuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gbW92ZSBUaGUgY29vcmRpbmF0ZXMuXG4gICAgICogQHJldHVybnMgVGhlIGJpdCBpbmRleC5cbiAgICAgKi9cbiAgICBwcml2YXRlIGdldEluZGV4KG1vdmU6IFBvc2l0aW9uKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYm9hcmRXaWR0aCAqIG1vdmUueSArIG1vdmUueDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVja3MgaWYgYSBtb3ZlIGlzIHZhbGlkIGZvciB0aGUgZ2l2ZW4gYm9hcmQuXG4gICAgICogRG9lcyBub3QgY2hlY2sgaWYgdGhhdCBjZWxsIGlzIGFscmVhZHkgb2NjdXBpZWQuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gcG9zaXRpb24gVGhlIHBvc2l0aW9uIHRvIGNoZWNrLlxuICAgICAqIEByZXR1cm5zIFdoZXRoZXIgb3Igbm90IHRoYXQgY2VsbCBleGlzdHMgb24gdGhlIGJvYXJkLlxuICAgICAqL1xuICAgIHByaXZhdGUgaXNWYWxpZFBvc2l0aW9uKHBvc2l0aW9uOiBQb3NpdGlvbik6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gcG9zaXRpb24ueCA+PSAwICYmIHBvc2l0aW9uLnggPCB0aGlzLmJvYXJkV2lkdGggJiYgcG9zaXRpb24ueSA+PSAwICYmIHBvc2l0aW9uLnkgPCB0aGlzLmJvYXJkSGVpZ2h0O1xuICAgIH1cbn1cbiJdfQ==
|