forked from web-proyectos/tic-tac-toe
-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.js
335 lines (298 loc) · 9.83 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
//////////////////////////////////
///
/// code by lhernandezcruz
/// uses socket.io and express
///
//////////////////////////////////
"use strict";
// Set up server
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const port = (process.env.PORT || 8000);
server.listen(port);
// Send index file when there is a connection
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res) {
res.sendFile('index.html');
});
/**
* Player Class
* @class This class holds information about a player
*/
class Player {
/**
* Constructs a Player
* @param {String} playerID The id of the player
* @param {Boolean} turn Whether it is the players turn to move
* @param {String} type "X" or "O"
*/
constructor(playerID, turn, type) {
/// Id of the player
this.id = playerID;
/// Whether it is the players turn
this.turn = turn;
/// The type of the player "X" or "O"
this.type = type;
}
/**
* Checks whether two players are equal
* @param {Object} rhs The other player
*/
equals(rhs) {
return (this.id == rhs.id
&& this.turn == rhs.turn
&& this.type == rhs.type);
}
}
/**
* Game Class
* @class This class holds information about the game
*/
class Game {
/**
* Constructs a Game
* @param {String} gameID Id of the game
*/
constructor(gameID) {
/// Id of the game
this.id = gameID;
/// Information about player1
this.player1 = null;
/// Information about player2
this.player2 = null;
/// Tic-tac-toe gameboard
this.gameboard = {
0: ""
, 1: ""
, 2: ""
, 3: ""
, 4: ""
, 5: ""
, 6: ""
, 7: ""
, 8: ""
};
}
/**
* Resets the gameboard and the turn of the players
*/
reset() {
// Reset gameboard
this.gameboard = {
0: ""
, 1: ""
, 2: ""
, 3: ""
, 4: ""
, 5: ""
, 6: ""
, 7: ""
, 8: ""
};
// Player1 starts the games
this.player1.turn = true;
this.player2.turn = false;
}
/**
* Adds a player to the game
* @param {String} playerID Id of the player joining
*/
addPlayer(playerID) {
// Check which player to add (only two players per game)
if (this.player1 == null) {
this.player1 = new Player(playerID, true, "X");
return "player1";
} else {
this.player2 = new Player(playerID, false, "O");
return "player2";
}
}
/**
* Checks if a move is valid. Valid if it is the players turn
* and the cell is empty.
* @param {Object} player Player that made move
* @param {String} cell The cell where move was made
*/
checkValid(player, cell) {
// Must check that player is one of the players
if (player.turn && (this.player1.equals(player) || this.player2.equals(player))) {
return this.gameboard[cell] == "";
}
return false;
}
/**
* Updates gameboard and the turns of the players.
* @param {String} cell Cell where move was made
* @param {String} type Type of player that made move
*/
updateBoard(cell, type) {
this.gameboard[cell] = type;
this.updateTurns();
}
/**
* Updates the turns of the players
*/
updateTurns() {
this.player1.turn = !this.player1.turn;
this.player2.turn = !this.player2.turn;
}
/**
* Checks the status of the game
* @return Returns "win" or "tie" or "ongoing"
*/
checkStatus() {
let board = this.gameboard;
// Check row 1
if ((board[0] != "") && ((board[0] == board[1]) && (board[1] == board[2]))) {
return "win";
}
// Check row 2
if ((board[3] != "") && ((board[3] == board[4]) && (board[4] == board[5]))) {
return "win";
}
// Check row 3
if ((board[6] != "") && ((board[6] == board[7]) && (board[7] == board[8]))) {
return "win";
}
// Check col 1
if ((board[0] != "") && ((board[0] == board[3]) && (board[3] == board[6]))) {
return "win";
}
// Check col 2
if ((board[1] != "") && ((board[1] == board[4]) && (board[4] == board[7]))) {
return "win";
}
// Check col 3
if ((board[2] != "") && ((board[2] === board[5]) && (board[5] == board[8]))) {
return "win";
}
// Check diag 1
if ((board[0] != "") && ((board[0] === board[4]) && (board[4] == board[8]))) {
return "win";
}
// Check diag 2
if ((board[2] != "") && ((board[2] === board[4]) && (board[4] == board[6]))) {
return "win";
}
// Check board full with no winner
if ((board[0] != "") && (board[1] != "") && (board[2] != "") && (board[3] != "") &&
(board[4] != "") && (board[5] != "") && (board[6] != "") && (board[7] != "") && (board[8] != "")) {
return "tie";
}
// Game is ongoing
return "ongoing";
}
}
/// Contains the ids of games being played
var games = {};
// Called when socket connects.
io.sockets.on('connection', function (socket) {
// Called when user creates a game
socket.on("create", function () {
// Create lobby id
let done = false;
let gameID = Math.floor((Math.random() * 100)).toString();
while (!done) {
if (games[gameID] == null) {
done = true;
} else {
gameID = Math.floor((Math.random() * 100)).toString();
}
}
// Create game and add player
games[gameID] = new Game(gameID);
games[gameID].addPlayer(socket.id);
// Add socket to lobby and emit the gameID to the socket
socket.join(gameID);
socket.lobby = gameID;
socket.emit('created', {
id: gameID
});
});
// Called when person attempts to join game
socket.on('join', function (data) {
// Check if the game exists
let gameID = data.gameID.toString();
if (games[gameID] != null) {
// Add player to the game
games[gameID].addPlayer(socket.id);
// Join lobby
socket.join(gameID);
socket.lobby = gameID;
// Emit data to first player.
socket.in(gameID).emit('start', {
id: gameID, gameboard: games[gameID].gameboard,
player: games[gameID].player1
});
// Emit data to second player.
socket.emit("start", {
id: gameID, gameboard: games[gameID].gameboard,
player: games[gameID].player2
});
} else {
// Game does not exist. Emit a failure to socket.
socket.emit("failed");
}
});
// Called when a move is made
socket.on("move", function (data) {
// Make sure it is a valid move
let gameID = data.id;
let valid = games[gameID].checkValid(data.player, data.cell);
if (valid) {
// Update board based on move
games[gameID].updateBoard(data.cell, data.player.type);
// Check status of the game
let status = games[gameID].checkStatus();
if (status == "ongoing") {
// Game is still continuing. Update both players
socket.in(gameID).emit('updateGame', {
id: gameID
, gameboard: games[gameID].gameboard
});
socket.emit("updateGame", {
id: gameID
, gameboard: games[gameID].gameboard
});
} else if (status == "win") {
// Game is won. Emit win and loss
socket.emit("win", games[gameID].gameboard);
socket.in(gameID).emit("loss", games[gameID].gameboard);
} else {
// Game is a tie. Emit a tie to both players.
socket.emit("tie", { gameboard: games[gameID].gameboard });
socket.in(gameID).emit("tie", {
gameboard: games[gameID].gameboard
});
}
} else {
// Emit invalid move
socket.emit("invalid");
}
});
// Called when person calls for restart of game.
socket.on("restart", function (data) {
// Reset the game board
let gameID = data.id;
games[gameID].reset();
// Emit data to first player.
socket.emit("start", {
id: gameID, gameboard: games[gameID].gameboard, player: games[gameID].player1
});
// Emit data to second player.
socket.in(gameID).emit('start', {
id: gameID, gameboard: games[gameID].gameboard, player: games[gameID].player2
});
});
// Called when connection is lost
socket.on("disconnect", function () {
// Remove the lobby and emit 'quit'
if (socket.lobby != null) {
socket.emit("quit");
socket.in(socket.lobby.toString()).emit("quit");
delete games[socket.lobby];
}
});
});