fuzzyworld

dao-game / peerjs-browser-bug / files / src / local-game.ts

Branch peerjs-browser-bug · default branch · all branches

local-game.ts

Blame: the commit that last changed each line · file · history

47dfe48a 2022-03-20 Pavel Safronov1import { Game, IGame } from './game';
2import { Coord, MoveResult } from './types';
3import { ListenerManager } from './listeners';
b3c7b964 2022-03-22 Pavel Safronov4import { showModal } from './modal';
e46456c2 2022-03-22 Pavel Safronov5import { poly_animate } from './animate';
4918da41 2022-03-17 Pavel Safronov6
7const classNames = {
47dfe48a 2022-03-20 Pavel Safronov8 selectedPiece: 'selected-piece',
9 selectedPlayerText: 'selected-player-text',
10 activePlayerPiece: 'active-player-piece',
11 possibleMove: 'possible-move',
2909d324 2022-03-16 Pavel Safronov12};
13
1e41e9b4 2022-03-18 Pavel Safronov14export class LocalGame extends Game {
47dfe48a 2022-03-20 Pavel Safronov15 selectedPiece: Element | null = null;
16 listenerManager: ListenerManager = new ListenerManager();
17
18 // DOM references
9e157243 2022-03-21 Pavel Safronov19 readonly dom = {
47dfe48a 2022-03-20 Pavel Safronov20 defaultP1Piece: document.getElementById('default-p1-piece'),
21 defaultP2Piece: document.getElementById('default-p2-piece'),
22 resetButton: document.getElementById('reset-button') as HTMLInputElement,
23 undoButton: document.getElementById('undo-button') as HTMLInputElement,
24 cells: () => Array.from(document.querySelectorAll('td')),
25 allPieces: () => Array.from(document.querySelectorAll('.piece')),
9e157243 2022-03-21 Pavel Safronov26 p1TurnText: () => Array.from(document.querySelectorAll('.p1-turn-text'))[0],
27 p2TurnText: () => Array.from(document.querySelectorAll('.p2-turn-text'))[0],
47dfe48a 2022-03-20 Pavel Safronov28 };
29
30 private getPiecesAndCoords(playerId: string) {
31 const allCells = this.dom.cells();
32 const playerPositions = this.getPlayerPositions(playerId);
33 const piecesAndPositions = playerPositions.map((coord) => {
34 const index = this.coordToBoardIndex(coord);
35 const cell = allCells[index];
36 const piece = cell.children[0];
37 return { coord, piece };
38 });
39 return piecesAndPositions;
40 }
41
42 /**
43 * Animates the motion of piece moving from source to dest
44 * @param source Starting Coord
45 * @param dest Ending Coord
46 * @param playerId Player id
47 */
48 private async movePiece(source: Coord, dest: Coord) {
49 const { dom } = this;
50 const sourceIndex = this.coordToBoardIndex(source);
51 const destIndex = this.coordToBoardIndex(dest);
52
53 // find current piece
54 const piece = dom.cells()[sourceIndex].children[0];
55 // unselect piece
56 piece.classList.remove(classNames.selectedPiece);
57
58 // create clone for new position
59 const destPiece = piece.cloneNode(true);
60 // add to destination cell
61 dom.cells()[destIndex].appendChild(destPiece);
62
63 // start animations
b3c7b964 2022-03-22 Pavel Safronov64 const animDurationMs = 500;
e46456c2 2022-03-22 Pavel Safronov65 const destElement = dom.cells()[destIndex];
66 const destAnimation = poly_animate(destElement,[{ opacity: 0 }, { opacity: 1 }], { duration: animDurationMs, iterations: 1 });
67 const sourceAnimation = poly_animate(piece,
47dfe48a 2022-03-20 Pavel Safronov68 [
69 {
70 opacity: 1,
71 width: '45px',
72 height: '45px',
73 },
74 {
75 opacity: 0,
76 width: '5px',
77 height: '5px',
78 },
79 ],
b3c7b964 2022-03-22 Pavel Safronov80 { duration: animDurationMs, iterations: 1 }
47dfe48a 2022-03-20 Pavel Safronov81 );
82
83 // await animations
84 await Promise.all([sourceAnimation.finished, destAnimation.finished]);
85
86 // remove old piece
87 piece.parentNode.removeChild(piece);
88 }
89
5805825c 2022-03-21 Pavel Safronov90 public constructor(player1Id: string, player1Name: string, player2Id: string, player2Name: string) {
91 super(player1Id, player1Name, player2Id, player2Name);
47dfe48a 2022-03-20 Pavel Safronov92 this.init();
93 }
94
95 public override async makeMove(source: Coord, dest: Coord): Promise<MoveResult> {
96 this.listenerManager.removeListeners();
97 await this.movePiece(source, dest);
98 const result = await super.makeMove(source, dest);
99 this.handleMoveResult(result);
100 return result;
101 }
102
103 public async handleMoveResult(result: MoveResult) {
b3c7b964 2022-03-22 Pavel Safronov104 const { isTerminal, message } = result;
105 if (isTerminal) {
e46456c2 2022-03-22 Pavel Safronov106 await showModal({ text: message});
47dfe48a 2022-03-20 Pavel Safronov107 document.location.reload();
108 } else {
109 this.resetUi();
110 this.setActiveUi();
1e41e9b4 2022-03-18 Pavel Safronov111 }
47dfe48a 2022-03-20 Pavel Safronov112 }
2909d324 2022-03-16 Pavel Safronov113
47dfe48a 2022-03-20 Pavel Safronov114 public resetUi() {
115 const { dom, currentPlayerId, player1Id, canUndoMove, moveHistory, listenerManager, selectedPiece } = this;
2909d324 2022-03-16 Pavel Safronov116
47dfe48a 2022-03-20 Pavel Safronov117 // reset text
9e157243 2022-03-21 Pavel Safronov118 dom.p1TurnText().classList.remove(classNames.selectedPlayerText);
119 dom.p2TurnText().classList.remove(classNames.selectedPlayerText);
2909d324 2022-03-16 Pavel Safronov120
47dfe48a 2022-03-20 Pavel Safronov121 const isP1Turn = currentPlayerId === player1Id;
122 if (isP1Turn) {
9e157243 2022-03-21 Pavel Safronov123 dom.p1TurnText().classList.add(classNames.selectedPlayerText);
47dfe48a 2022-03-20 Pavel Safronov124 } else {
9e157243 2022-03-21 Pavel Safronov125 dom.p2TurnText().classList.add(classNames.selectedPlayerText);
47dfe48a 2022-03-20 Pavel Safronov126 }
2909d324 2022-03-16 Pavel Safronov127
f2c94061 2022-03-21 Pavel Safronov128 if (dom.undoButton || dom.resetButton) {
129 dom.undoButton.disabled = canUndoMove;
130 if (moveHistory.length === 0) {
131 dom.undoButton.disabled = true;
132 dom.resetButton.disabled = true;
133 } else {
134 dom.undoButton.disabled = false;
135 dom.resetButton.disabled = false;
136 }
2909d324 2022-03-16 Pavel Safronov137 }
138
47dfe48a 2022-03-20 Pavel Safronov139 this.selectedPiece = null;
140
141 // reset Cells
142 dom.cells().forEach((cell) => cell.classList.remove(classNames.possibleMove));
143 // reset pieces
144 dom.allPieces().forEach((piece) => {
145 piece.classList.remove(classNames.activePlayerPiece);
146 piece.classList.remove(classNames.selectedPiece);
147 });
148 // remove all listeners
149 listenerManager.removeListeners();
150 }
151 public setActiveUi() {
b3c7b964 2022-03-22 Pavel Safronov152 const { dom, currentPlayerId, listenerManager } = this;
153
154 const clearPossibleMovesBlink = () =>
155 dom.cells().forEach((cell) => cell.classList.remove(classNames.possibleMove));
156 const activePieceListener = (piece: Element, source: Coord) => {
157 // clear current selection
158 if (this.selectedPiece !== null) {
159 this.selectedPiece.classList.remove(classNames.selectedPiece);
160 clearPossibleMovesBlink();
161 listenerManager.removeListeners();
162 hookupActivePieces();
163 }
4918da41 2022-03-17 Pavel Safronov164
b3c7b964 2022-03-22 Pavel Safronov165 // set as selected
166 this.selectedPiece = piece;
167 piece.classList.add(classNames.selectedPiece);
168
169 // hook up allowed moves for:
170 // 1. blink
171 // 2. complete the move
172 const allowedMoves = this.getValidMoves(source);
173 allowedMoves.forEach((dest: Coord) => {
174 const cell = dom.cells()[this.coordToBoardIndex(dest)];
175 cell.classList.add(classNames.possibleMove);
176 listenerManager.addListener(cell, 'click', async () => {
177 clearPossibleMovesBlink();
178 await this.makeMove(source, dest);
1e41e9b4 2022-03-18 Pavel Safronov179 });
47dfe48a 2022-03-20 Pavel Safronov180 });
b3c7b964 2022-03-22 Pavel Safronov181 };
182
183 const hookupActivePieces = () => {
184 // mark active pieces
185 const piecesAndPositions = this.getPiecesAndCoords(currentPlayerId);
186 piecesAndPositions.forEach(({ coord: source, piece }) => {
187 piece.classList.add(classNames.activePlayerPiece);
188 listenerManager.addListener(piece, 'click', () => activePieceListener(piece, source));
189 });
190 }
191
192 hookupActivePieces();
47dfe48a 2022-03-20 Pavel Safronov193 }
194
195 private init() {
5805825c 2022-03-21 Pavel Safronov196 const { dom, player1Id, player1Name, player2Id, player2Name, size } = this;
47dfe48a 2022-03-20 Pavel Safronov197
198 // create board and pieces
199 let isWhite = true;
200 const table = document.getElementById('table');
201 for (let r = 0; r < size.row; r++) {
202 const tr = document.createElement('tr');
203 table.appendChild(tr);
204 for (let c = 0; c < size.col; c++) {
205 const td = document.createElement('td');
206 tr.appendChild(td);
b3c7b964 2022-03-22 Pavel Safronov207 td.classList.add('tile');
208 td.classList.add(isWhite ? 'white' : 'black');
47dfe48a 2022-03-20 Pavel Safronov209 isWhite = !isWhite;
210 }
211 isWhite = !isWhite;
1e41e9b4 2022-03-18 Pavel Safronov212 }
47dfe48a 2022-03-20 Pavel Safronov213 const createPiece = (isP1Turn: boolean, pieceId: string, coord: Coord) => {
214 const defaultPiece = isP1Turn ? dom.defaultP1Piece : dom.defaultP2Piece;
215 const newPiece = defaultPiece.cloneNode(true) as HTMLParagraphElement;
216 newPiece.id = pieceId;
217 const index = this.coordToBoardIndex(coord);
218 dom.cells()[index].appendChild(newPiece);
219 };
220 this.getPlayerPositions(player1Id).forEach((coord) => createPiece(true, player1Id, coord));
221 this.getPlayerPositions(player2Id).forEach((coord) => createPiece(false, player2Id, coord));
222
b3c7b964 2022-03-22 Pavel Safronov223 dom.p1TurnText().textContent = `${player1Name}`
224 dom.p2TurnText().textContent = `${player2Name}`
5805825c 2022-03-21 Pavel Safronov225
47dfe48a 2022-03-20 Pavel Safronov226 // hook up buttons
227 let blockActions = false;
f2c94061 2022-03-21 Pavel Safronov228 if (dom.resetButton) {
229 dom.resetButton.addEventListener('click', () => {
230 if (blockActions) {
231 return;
232 }
47dfe48a 2022-03-20 Pavel Safronov233
f2c94061 2022-03-21 Pavel Safronov234 blockActions = true;
235 if (confirm('Reset?')) {
236 window.location.reload();
237 }
238 blockActions = false;
239 });
240 }
47dfe48a 2022-03-20 Pavel Safronov241
f2c94061 2022-03-21 Pavel Safronov242 if (dom.undoButton) {
243 dom.undoButton.addEventListener('click', async () => {
244 if (blockActions) {
245 return;
246 }
47dfe48a 2022-03-20 Pavel Safronov247
f2c94061 2022-03-21 Pavel Safronov248 blockActions = true;
47dfe48a 2022-03-20 Pavel Safronov249
f2c94061 2022-03-21 Pavel Safronov250 const move = this.undoMove();
251 await this.movePiece(move.dest, move.source);
252 this.resetUi();
253 this.setActiveUi();
254
255 blockActions = false;
256 });
257 }
47dfe48a 2022-03-20 Pavel Safronov258 }
1e41e9b4 2022-03-18 Pavel Safronov259}
2909d324 2022-03-16 Pavel Safronov260
261// start!
4918da41 2022-03-17 Pavel Safronov262export const playGame = () => {
5805825c 2022-03-21 Pavel Safronov263 const lg = new LocalGame('red-player', 'Red', 'blue-player', 'Blue');
47dfe48a 2022-03-20 Pavel Safronov264 lg.resetUi();
265 lg.setActiveUi();
2909d324 2022-03-16 Pavel Safronov266};